Skip to content

Commit

Permalink
Merge pull request #5 from geosolutions-it/2022.02.xx
Browse files Browse the repository at this point in the history
Catch up with upstream 2022.02.xx
  • Loading branch information
ridoo authored Jan 24, 2024
2 parents 68a63cd + ce0069d commit 72c461f
Show file tree
Hide file tree
Showing 23 changed files with 593 additions and 106 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@
"draft-js-side-toolbar-plugin": "3.0.1",
"draftjs-to-html": "0.8.4",
"element-closest": "2.0.2",
"embed-video": "2.0.4",
"es6-object-assign": "1.1.0",
"es6-promise": "2.3.0",
"eventlistener": "0.0.1",
Expand Down
5 changes: 3 additions & 2 deletions web/client/actions/geostory.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,11 +167,12 @@ export const toggleSettingsPanel = (withSave = false) => ({ type: TOGGLE_SETTING
* @param {object} element the object to update
* @param {string|object} [mode="replace"] "merge" or "replace", if "merge", the object passed as element will be merged with the original one (if present and if it is an object)
*/
export const update = (path, element, mode = "replace") => ({
export const update = (path, element, mode = "replace", options) => ({
type: UPDATE,
path,
element,
mode
mode,
options
});
/**
* updates the current page with current value of sectionId (future can be extended adding other info about current content).
Expand Down
99 changes: 87 additions & 12 deletions web/client/components/mapcontrols/search/SearchBar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
* LICENSE file in the root directory of this source tree.
*/

import React from 'react';
import React, { useEffect, useState } from 'react';
import {FormGroup, Glyphicon, MenuItem} from 'react-bootstrap';
import {isEmpty, isUndefined} from 'lodash';
import { isEmpty, isEqual, isUndefined, get } from 'lodash';

import Message from '../../I18N/Message';
import SearchBarMenu from './SearchBarMenu';
Expand All @@ -20,6 +20,49 @@ import SearchBarToolbar from '../../search/SearchBarToolbar';
import { defaultSearchWrapper } from '../../search/SearchBarUtils';
import BookmarkSelect, {BookmarkOptions} from "../searchbookmarkconfig/BookmarkSelect";
import CoordinatesSearch, {CoordinateOptions} from "../searchcoordinates/CoordinatesSearch";
import tooltip from '../../misc/enhancers/tooltip';

const TMenuItem = tooltip(MenuItem);
const SearchServicesSelectorMenu = ({activeTool, searchIcon, services = [], selectedService = -1, onServiceSelect = () => {}}) => {
if (services.length === 0) {
return null;
}
if (services.length === 1) {
return (
<MenuItem active={activeTool === "addressSearch"} onClick={() => onServiceSelect(-1)}>
<Glyphicon glyph={searchIcon}/>
<Message msgId="search.addressSearch"/>
</MenuItem>
);
}
return (<>
<TMenuItem
tooltipId="search.searchOnAllServices"
tooltipPosition="left"
active={activeTool === "addressSearch" && selectedService === -1}
onClick={() => onServiceSelect(-1)}
>
<Glyphicon glyph={searchIcon}/>
<Message msgId="search.addressSearch"/>
</TMenuItem>
{services.map((service, index) => {
const name = service.name || service.type;
return (<TMenuItem
tooltip={get(service, 'options.tooltip', `Search on ${name}`)}
tooltipPosition="left"
onClick={() => onServiceSelect(index)}
key={index}
active={activeTool === "addressSearch" && selectedService === index}
>
<span style={{marginLeft: 20}}>
<Glyphicon glyph={searchIcon}/>
{name}
</span>
</TMenuItem>);
})}
<MenuItem divider/>
</>);
};

export default ({
activeSearchTool: activeTool = 'addressSearch',
Expand Down Expand Up @@ -61,8 +104,24 @@ export default ({
items = [],
...props
}) => {
const [selectedSearchService, setSearchServiceSelected] = useState(-1);
useEffect(() => {
// Reset selected service, when service changes
if (!isEqual(searchOptions?.services, searchOptions?.services)) {
setSearchServiceSelected(-1);
}
}, [searchOptions?.services]);

const search = defaultSearchWrapper({searchText, selectedItems, searchOptions, maxResults, onSearch, onSearchReset});
const selectedServices = searchOptions?.services?.filter((_, index) => selectedSearchService >= 0 ? selectedSearchService === index : true) ?? [];
const search = defaultSearchWrapper({
searchText,
selectedItems,
searchOptions: {
...searchOptions,
services: selectedServices
},
maxResults, onSearch, onSearchReset
});

const clearSearch = () => {
onSearchReset();
Expand All @@ -78,14 +137,20 @@ export default ({
let searchMenuOptions = [];
if (showAddressSearchOption) {
searchMenuOptions.push(
<MenuItem active={activeTool === "addressSearch"} onClick={()=>{
onClearCoordinatesSearch({owner: "search"});
onClearBookmarkSearch("selected");
onChangeActiveSearchTool("addressSearch");
}}
>
<Glyphicon glyph={searchIcon}/> <Message msgId="search.addressSearch"/>
</MenuItem>);
<SearchServicesSelectorMenu
searchIcon={searchIcon}
activeTool={activeTool}
selectedService={selectedSearchService}
onServiceSelect={(index) => {
setSearchServiceSelected(index === -1 ? undefined : index);
onClearCoordinatesSearch({owner: "search"});
onClearBookmarkSearch("selected");
onChangeActiveSearchTool("addressSearch");
return;
}}
services={searchOptions?.services}
/>
);
}
if (showCoordinatesSearchOption) {
searchMenuOptions.push(
Expand Down Expand Up @@ -129,6 +194,16 @@ export default ({
return null;
};

const getPlaceholder = () => {
// when placeholder is present, nested service's placeholder is applied
if (!placeholder && selectedServices?.length === 1 && searchOptions?.services?.length > 1) {
const [service] = selectedServices;
const name = service.name || service.type;
return get(service, 'options.placeholder', `Search by ${name}`);
}
return placeholder;
};

return (<SearchBarBase>
<FormGroup>
<div className="input-group" style={{display: "flex"}}>
Expand All @@ -140,7 +215,7 @@ export default ({
delay={delay}
typeAhead={typeAhead}
blurResetDelay={blurResetDelay}
placeholder={placeholder}
placeholder={getPlaceholder()}
placeholderMsgId={placeholderMsgId}
searchText={searchText}
selectedItems={selectedItems}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,52 @@ describe("test the SearchBar", () => {
expect(rootDiv).toExist();
});

it('test search service menu', () => {
ReactDOM.render(<SearchBar searchOptions={{services: [{type: "nominatim"}]}}/>, document.getElementById("container"));
let search = document.getElementsByClassName("glyphicon-search");
expect(search).toBeTruthy();
expect(search.length).toBe(2);
});
it('test search with multiple services', () => {
ReactDOM.render(<SearchBar searchOptions={{services: [{type: "nominatim"}, {type: "wfs", name: "test"}]}}/>, document.getElementById("container"));
let search = document.getElementsByClassName("glyphicon-search");
let menuItems = document.querySelectorAll('[role="menuitem"]');
expect(search).toBeTruthy();
expect(search.length).toBe(4);
expect(menuItems.length).toBe(4);
expect(menuItems[1].innerText).toBe('nominatim');
expect(menuItems[2].innerText).toBe('test'); // Service name is menu name
});
it('test onSearch with multiple services', () => {
const actions = {
onSearch: () => {}
};
const services = [{type: "nominatim"}, {type: "wfs", name: "test"}];
const spyOnSearch = expect.spyOn(actions, 'onSearch');
ReactDOM.render(<SearchBar onSearch={actions.onSearch} searchText="test" searchOptions={{services}}/>, document.getElementById("container"));
let search = document.getElementsByClassName("glyphicon-search");
let input = document.querySelector(".searchInput");
let menuItems = document.querySelectorAll('[role="menuitem"]');
expect(search).toBeTruthy();
expect(input).toBeTruthy();
expect(search.length).toBe(4);

expect(menuItems.length).toBe(4);
TestUtils.Simulate.click(menuItems[1]); // Select single search

TestUtils.Simulate.keyDown(input, { key: 'Enter', keyCode: 13 });
expect(spyOnSearch).toHaveBeenCalled();
expect(spyOnSearch.calls[0].arguments[0]).toBe("test");
expect(spyOnSearch.calls[0].arguments[1]).toEqual({"services": [services[0]]});
expect(spyOnSearch.calls[0].arguments[2]).toBe(15);

TestUtils.Simulate.click(menuItems[0]); // Select all search
TestUtils.Simulate.keyDown(input, { key: 'Enter', keyCode: 13 });
expect(spyOnSearch).toHaveBeenCalled();
expect(spyOnSearch.calls[1].arguments[0]).toBe("test");
expect(spyOnSearch.calls[1].arguments[1]).toEqual({services});
expect(spyOnSearch.calls[1].arguments[2]).toBe(30);
});
it('test search and reset buttons', () => {
const renderSearchBar = (testHandlers, text) => {
return ReactDOM.render(
Expand Down Expand Up @@ -211,7 +257,7 @@ describe("test the SearchBar", () => {
let search = document.getElementsByClassName("glyphicon-search");
expect(reset).toExist();
expect(search).toExist();
expect(search.length).toBe(2);
expect(search.length).toBe(1);
});
it('test only search present, splitTools=false', () => {
ReactDOM.render(<SearchBar splitTools={false} searchText={""} delay={0} typeAhead={false} />, document.getElementById("container"));
Expand All @@ -237,7 +283,7 @@ describe("test the SearchBar", () => {
let reset = document.getElementsByClassName("glyphicon-1-close")[0];
let search = document.getElementsByClassName("glyphicon-search");
expect(reset).toExist();
expect(search.length).toBe(1);
expect(search.length).toBe(0);
});
it('test zoomToPoint, with search, with decimal, with reset', () => {
const store = {dispatch: () => {}, subscribe: () => {}, getState: () => ({search: {coordinate: {lat: 2, lon: 2}}})};
Expand All @@ -246,7 +292,7 @@ describe("test the SearchBar", () => {
let search = document.getElementsByClassName("glyphicon-search");
let cog = document.getElementsByClassName("glyphicon-cog");
expect(reset.length).toBe(1);
expect(search.length).toBe(2);
expect(search.length).toBe(1);
expect(cog.length).toBe(1);
});

Expand All @@ -258,7 +304,7 @@ describe("test the SearchBar", () => {
let cog = document.getElementsByClassName("glyphicon-cog");
let inputs = document.getElementsByTagName("input");
expect(reset.length).toBe(0);
expect(search.length).toBe(2);
expect(search.length).toBe(1);
expect(cog.length).toBe(1);
expect(inputs.length).toBe(6);
});
Expand Down Expand Up @@ -404,9 +450,9 @@ describe("test the SearchBar", () => {
TestUtils.Simulate.click(buttons[1]);
const links = container.querySelectorAll('a');
const bookmark = container.getElementsByClassName('glyphicon-bookmark');
expect(links.length).toBe(3);
expect(links.length).toBe(2);
expect(bookmark).toExist();
expect(links[2].innerText).toBe('Search by bookmark');
expect(links[1].innerText).toBe('Search by bookmark');
});
it('test searchByBookmark, search button disabled', () => {
const store = {dispatch: () => {}, subscribe: () => {}, getState: () => ({searchbookmarkconfig: {selected: {}}})};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,26 @@ class WFSOptionalProps extends React.Component {
/>
<Label key="maxZoomLevel-label" className="slider-label" >{options.maxZoomLevel || 21}</Label>
</FormGroup>
<FormGroup>
<ControlLabel>
<Message msgId="search.s_placeholder" />
</ControlLabel>
<FormControl
value={options.placeholder}
key="placeholder"
type="text"
onChange={this.updateProp.bind(null, "placeholder")}/>
</FormGroup>
<FormGroup>
<ControlLabel>
<Message msgId="search.s_tooltip" />
</ControlLabel>
<FormControl
value={options.tooltip}
key="tooltip"
type="text"
onChange={this.updateProp.bind(null, "tooltip")}/>
</FormGroup>
</form>);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ describe("test ResultProps component", () => {
const wfsOptionalProps = ReactDOM.render(<WFSOptionalProps.Element/>, document.getElementById("container"));
expect(wfsOptionalProps).toExist();
const labels = TestUtils.scryRenderedDOMComponentsWithClass(wfsOptionalProps, "control-label");
expect(labels.length).toBe(3);
expect(labels.length).toBe(5);
});

it('test WFSOptionalProps with preconfigured service', () => {
Expand All @@ -45,12 +45,14 @@ describe("test ResultProps component", () => {
let wfsOptionalProps = ReactDOM.render(<WFSOptionalProps.Element service={service}/>, document.getElementById("container"));
expect(wfsOptionalProps).toExist();
const labels = TestUtils.scryRenderedDOMComponentsWithClass(wfsOptionalProps, "control-label");
expect(labels.length).toBe(3);
expect(labels.length).toBe(5);
expect(labels[0].innerText).toBe('search.s_sort');
expect(labels[1].innerText).toBe('search.s_max_features');
expect(labels[2].innerText).toBe('search.s_max_zoom');
expect(labels[3].innerText).toBe('search.s_placeholder');
expect(labels[4].innerText).toBe('search.s_tooltip');
const inputs = TestUtils.scryRenderedDOMComponentsWithClass(wfsOptionalProps, 'form-control');
expect(inputs.length).toBe(1);
expect(inputs.length).toBe(3);
expect(inputs[0].value).toBe('NAME');
let sliders = TestUtils.scryRenderedDOMComponentsWithClass(wfsOptionalProps, 'slider-label');
expect(sliders.length).toBe(2);
Expand Down
Loading

0 comments on commit 72c461f

Please sign in to comment.