Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 44 additions & 28 deletions web/client/components/mapcontrols/search/SearchBar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,42 @@ import CoordinatesSearch, {CoordinateOptions} from "../searchcoordinates/Coordin
import CurrentMapCRSCoordSearch from '../searchcoordinates/CurrentMapCRSCoordSearch';
import tooltip from '../../misc/enhancers/tooltip';

const TMenuItem = tooltip(MenuItem);
const TDiv = tooltip('div');

const SearchServicesContainer = ({activeTool, searchIcon, services = [], selectedService = -1, onServiceSelect = () => {}}) => {
return (
<>
<MenuItem className="trigger-item" active={activeTool === "addressSearch"} onClick={() => onServiceSelect(-1)}>
<Glyphicon glyph={searchIcon}/>
<Message msgId="search.addressSearch"/>
</MenuItem>
<div className="search-services-submenus">
<TDiv tooltipPosition="left" tooltipId="search.searchOnAllServices" className={`search-services-item all-services-item ${activeTool === "addressSearch" && selectedService === -1 ? "active" : ""}`} onClick={() => onServiceSelect(-1)}>
<Glyphicon glyph={searchIcon}/>
<Message msgId="search.addressSearch"/>
</TDiv>
{services.map((service, index) => {
const name = service.name || service.type;
return (
<TDiv
key={index}
tooltip={get(service, 'options.tooltip', `Search on ${name}`)}
tooltipPosition="left"
onClick={() => onServiceSelect(index)}
className={`search-services-item ${activeTool === "addressSearch" && selectedService === index ? "active" : ""}`}
>
<span className="search-services-item-icon">
<Glyphicon glyph={searchIcon}/>
{name}
</span>
</TDiv>
);
})}
</div>
</>
);
};

const SearchServicesSelectorMenu = ({activeTool, searchIcon, services = [], selectedService = -1, onServiceSelect = () => {}}) => {
if (services.length === 0) {
return null;
Expand All @@ -36,33 +71,14 @@ const SearchServicesSelectorMenu = ({activeTool, searchIcon, services = [], sele
</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/>
</>);

return (<SearchServicesContainer
activeTool={activeTool}
searchIcon={searchIcon}
services={services}
selectedService={selectedService}
onServiceSelect={onServiceSelect}
/>);
};

export default ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,32 +45,49 @@ describe("test the SearchBar", () => {
expect(search).toBeTruthy();
expect(search.length).toBe(2);
});
it("No Submenu container when only one service", () => {
ReactDOM.render(<SearchBar searchOptions={{services: [{type: "nominatim"}]}}/>, document.getElementById("container"));
let submenus = document.querySelectorAll(".search-services-submenus");
expect(submenus.length).toBe(0);
});
it("Submenu container when multiple services", () => {
ReactDOM.render(<SearchBar searchOptions={{services: [{type: "nominatim"}, {type: "wfs", name: "test"}]}}/>, document.getElementById("container"));
let submenus = document.querySelectorAll(".search-services-submenus");
expect(submenus.length).toBe(1);
const searchServicesSubMenus = document.querySelectorAll('.search-services-item');
expect(searchServicesSubMenus.length).toBe(3);
});
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"]');
const searchServicesSubMenus = document.querySelectorAll('.search-services-item');
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
expect(search.length).toBe(5);
expect(menuItems.length).toBe(2);
expect(searchServicesSubMenus.length).toBe(3);
expect(searchServicesSubMenus[1].innerHTML).toContain("nominatim");
expect(searchServicesSubMenus[2].innerHTML).toContain("test");
});
it('test onSearch with multiple services', () => {
const actions = {
onSearch: () => {}
onSearch: () => {},
onSearchReset: () => {}
};
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"));
ReactDOM.render(<SearchBar onSearch={actions.onSearch} searchText="test" searchOptions={{services}} onSearchReset={actions.onSearchReset}/>, document.getElementById("container"));
let search = document.getElementsByClassName("glyphicon-search");
let input = document.querySelector(".searchInput");
let menuItems = document.querySelectorAll('[role="menuitem"]');
const searchServicesSubMenus = document.querySelectorAll('.search-services-item');

expect(search).toBeTruthy();
expect(input).toBeTruthy();
expect(search.length).toBe(4);
expect(search.length).toBe(5);

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

TestUtils.Simulate.keyDown(input, { key: 'Enter', keyCode: 13 });
expect(spyOnSearch).toHaveBeenCalled();
Expand Down
48 changes: 48 additions & 0 deletions web/client/themes/default/less/searchbar.less
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@
.search-result-list .search-result:hover {
.background-color-var(@theme-vars[main-hover-bg]);
}

.search-services-submenus {
.search-services-item {
&:hover {
.background-color-var(@theme-vars[main-hover-bg]);
}
&.active {
.background-color-var(@theme-vars[primary]);
.color-var(@theme-vars[main-bg]);
}
}
}
}

// **************
Expand Down Expand Up @@ -163,3 +175,39 @@
max-width: 400px;
text-overflow: ellipsis;
}

/* Search services submenu */
.search-services-submenus{
position: absolute;
right: 100%;
top: 0;
min-width: 280px;
max-width: 300px;
background: white;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
opacity: 0;
visibility: hidden;
transition: opacity 0.2s ease, visibility 0.2s ease;
z-index: 1000;
max-height: 400px;
overflow: auto;
.search-services-item {
&.all-services-item{
padding-left: 5px;
}
cursor: pointer;
padding: 3px 5px;
&:not(.all-services-item) {
.search-services-item-icon{
margin-left: 20px;
}
}
}
}

.trigger-item:hover + .search-services-submenus,
.search-services-submenus:hover {
opacity: 1;
visibility: visible;
}