Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #2798 search clickable #2800

Merged
merged 3 commits into from
Apr 6, 2018
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
1 change: 1 addition & 0 deletions docma-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@
"web/client/plugins/GlobeViewSwitcher.jsx",
"web/client/plugins/GoFull.jsx",
"web/client/plugins/Map.jsx",
"web/client/plugins/MapSearch.jsx",
"web/client/plugins/Measure.jsx",
"web/client/plugins/MeasurePanel.jsx",
"web/client/plugins/MeasureResults.jsx",
Expand Down
48 changes: 33 additions & 15 deletions web/client/components/mapcontrols/search/SearchBar.jsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
const PropTypes = require('prop-types');
/*
* Copyright 2015, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
*/

var React = require('react');
var {FormControl, FormGroup, Glyphicon, Tooltip} = require('react-bootstrap');
const PropTypes = require('prop-types');
const React = require('react');
const {FormControl, FormGroup, Glyphicon, Tooltip} = require('react-bootstrap');
const OverlayTrigger = require('../../misc/OverlayTrigger');
var LocaleUtils = require('../../../utils/LocaleUtils');
var Spinner = require('react-spinkit');
const LocaleUtils = require('../../../utils/LocaleUtils');
const Spinner = require('react-spinkit');
const assign = require('object-assign');


var delay = (
const delay = (
function() {
var timer = 0;
return function(callback, ms) {
Expand All @@ -36,13 +37,16 @@ require('./searchbar.css');
* @prop {function} onCancelSelectedItem triggered when the user deletes the selected item (by hitting backspace) when text is empty
* @prop {string} placeholder string to use as placeholder when text is empty
* @prop {string} placeholderMsgId msgId for the placeholder. Used if placeholder is not defined
* @prop {string} removeIcon glyphicon used for reset button, default 1-close
* @prop {string} searchIcon glyphicon used for search button, default search
* @prop {number} delay milliseconds after trigger onSearch if typeAhead is true
* @prop {boolean} hideOnBlur if true, it triggers onPurgeResults on blur
* @prop {boolean} typeAhead if true, onSearch is triggered when users change the search text, after `delay` milliseconds
* @prop {number} blurResetDelay time to wait before to trigger onPurgeResults after blur event, if `hideOnBlur` is true
* @prop {searchText} the text to display in the component
* @prop {object[]} selectedItems the items selected. Must have `text` property to display
* @prop {boolean} autoFocusOnSelect if true, the component gets focus when items are added, or deleted but some item is still selected. Useful for continue writing after selecting an item (with nested services for instance)
* @prop {boolean} splitTools if false, the search and reset can appear both at the same time, otherwise the search appear only with empty text, the reset if a text is entered
* @prop {boolean} loading if true, shows the loading tool
* @prop {object} error if not null, an error icon will be display
* @prop {object} style css style to apply to the component
Expand All @@ -64,8 +68,11 @@ class SearchBar extends React.Component {
blurResetDelay: PropTypes.number,
typeAhead: PropTypes.bool,
searchText: PropTypes.string,
removeIcon: PropTypes.string,
searchIcon: PropTypes.string,
selectedItems: PropTypes.array,
autoFocusOnSelect: PropTypes.bool,
splitTools: PropTypes.bool,
loading: PropTypes.bool,
error: PropTypes.object,
style: PropTypes.object,
Expand All @@ -84,9 +91,12 @@ class SearchBar extends React.Component {
onCancelSelectedItem: () => {},
selectedItems: [],
placeholderMsgId: "search.placeholder",
removeIcon: "1-close",
searchIcon: "search",
delay: 1000,
blurResetDelay: 300,
autoFocusOnSelect: true,
splitTools: true,
hideOnBlur: true,
typeAhead: true,
searchText: ""
Expand Down Expand Up @@ -138,22 +148,30 @@ class SearchBar extends React.Component {
}
};

getSpinnerStyle = () => {
const nonSplittedStyle = {
right: "19px",
top: "7px"
};
const splittedStyle = {
right: "16px",
top: "12px"
};
return assign({}, {position: "absolute"}, this.props.splitTools ? {...splittedStyle} : {...nonSplittedStyle} );
}
renderAddonBefore = () => {
return this.props.selectedItems && this.props.selectedItems.map((item, index) =>
<span key={"selected-item" + index} className="input-group-addon"><div className="selectedItem-text">{item.text}</div></span>
);
};

renderAddonAfter = () => {
const remove = <Glyphicon className="searchclear" glyph="remove" onClick={this.clearSearch} key="searchbar_remove_glyphicon"/>;
var showRemove = this.props.searchText !== "" || this.props.selectedItems && this.props.selectedItems.length > 0;
let addonAfter = showRemove ? [remove] : [<Glyphicon glyph="search" key="searchbar_search_glyphicon"/>];
const remove = <Glyphicon className="searchclear" glyph={this.props.removeIcon} onClick={this.clearSearch} key="searchbar_remove_glyphicon"/>;
const search = <Glyphicon className="magnifying-glass" glyph={this.props.searchIcon} key="searchbar_search_glyphicon" onClick={this.search}/>;
const showRemove = this.props.searchText !== "" || this.props.selectedItems && this.props.selectedItems.length > 0;
let addonAfter = showRemove ? (this.props.splitTools ? [remove] : [remove, search]) : [search];
if (this.props.loading) {
addonAfter = [<Spinner style={{
position: "absolute",
right: "16px",
top: "12px"
}} spinnerName="pulse" noFadeIn/>, addonAfter];
addonAfter = [<Spinner style={this.getSpinnerStyle()} spinnerName="pulse" noFadeIn/>, addonAfter];
}
if (this.props.error) {
let tooltip = <Tooltip id="tooltip">{this.props.error && this.props.error.message || null}</Tooltip>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,4 +197,39 @@ describe("test the SearchBar", () => {
done();
}, 10);
});

it('test search and reset buttons both present, splitTools=false', () => {
const tb = ReactDOM.render(<SearchBar splitTools={false} searchText={"some val"} delay={0} typeAhead={false} />, document.getElementById("container"));
let reset = TestUtils.findRenderedDOMComponentWithClass(tb, "searchclear");
let search = TestUtils.findRenderedDOMComponentWithClass(tb, "magnifying-glass");
expect(reset).toExist();
expect(search).toExist();
});
it('test only search present, splitTools=false', () => {
const tb = ReactDOM.render(<SearchBar splitTools={false} searchText={""} delay={0} typeAhead={false} />, document.getElementById("container"));
let reset = TestUtils.scryRenderedDOMComponentsWithClass(tb, "searchclear");
expect(reset.length).toBe(0);

let search = TestUtils.findRenderedDOMComponentWithClass(tb, "magnifying-glass");
expect(search).toExist();
});


it('test only search present, splitTools=true', () => {
const tb = ReactDOM.render(<SearchBar splitTools searchText={""} delay={0} typeAhead={false} />, document.getElementById("container"));
let reset = TestUtils.scryRenderedDOMComponentsWithClass(tb, "searchclear");
expect(reset.length).toBe(0);

let search = TestUtils.findRenderedDOMComponentWithClass(tb, "magnifying-glass");
expect(search).toExist();
});

it('test only reset present, splitTools=true', () => {
const tb = ReactDOM.render(<SearchBar splitTools searchText={"va"} delay={0} typeAhead={false} />, document.getElementById("container"));
let reset = TestUtils.findRenderedDOMComponentWithClass(tb, "searchclear");
expect(reset).toExist();

let search = TestUtils.scryRenderedDOMComponentsWithClass(tb, "magnifying-glass");
expect(search.length).toBe(0);
});
});
2 changes: 1 addition & 1 deletion web/client/examples/3dviewer/containers/Viewer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ class Viewer extends React.Component {
}}>
<label>Graticule:&nbsp;&nbsp;<input type="checkbox" checked={this.props.showGraticule} onChange={this.props.toggleGraticule}/></label>
</div>
<SearchBar key="seachBar" searchText={this.props.searchText} onSearchTextChange={this.props.searchTextChanged} onSearch={this.props.textSearch} onSearchReset={this.props.resultsPurge} />
<SearchBar key="seachBar" removeIcon="remove" searchText={this.props.searchText} onSearchTextChange={this.props.searchTextChanged} onSearch={this.props.textSearch} onSearchReset={this.props.resultsPurge} />
<SearchResultList key="nominatimresults"
results={this.props.searchResults}
onItemClick={this.onSearchClick}
Expand Down
24 changes: 18 additions & 6 deletions web/client/plugins/MapSearch.jsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,27 @@
/**
/*
* Copyright 2016, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
*/
const {connect} = require('react-redux');


const {loadMaps, mapsSearchTextChanged} = require('../actions/maps');
const ConfigUtils = require('../utils/ConfigUtils');

/**
* MapSearch Plugin is a plugin that allows to make a search, reset its content, show a loading spinner while search is going on and can be
* used for different purpose (maps, wfs services)
* @name MapSearch
* @memberof plugins
* @class
* @example
* {
* "name": "MapSearch",
* "cfg": {
* "splitTools": true
* }
* }
*/
const SearchBar = connect((state) => ({
className: "maps-search",
hideOnBlur: false,
Expand All @@ -26,10 +37,11 @@ const SearchBar = connect((state) => ({
return loadMaps(ConfigUtils.getDefaults().geoStoreUrl, searchText, options);
},
onSearchReset: loadMaps.bind(null, ConfigUtils.getDefaults().geoStoreUrl, ConfigUtils.getDefaults().initialMapFilter || "*")
}, (stateProps, dispatchProps) => {
}, (stateProps, dispatchProps, ownProps) => {

return {
...stateProps,
...ownProps,
onSearch: (text) => {
let limit = stateProps.limit;
dispatchProps.onSearch(text, {start: 0, limit});
Expand Down
8 changes: 7 additions & 1 deletion web/client/themes/default/less/map-search-bar.less
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,16 @@ div.MapSearchBar .input-group-addon {
font-size: @icon-size;
}

.MapSearchBar .input-group-addon .magnifying-glass:hover,
.MapSearchBar .input-group-addon .searchclear:hover {
opacity: 0.75
}

.MapSearchBar .input-group-addon .magnifying-glass,
.MapSearchBar .input-group-addon .searchclear,
.MapSearchBar .input-group-addon .searcherror {
font-size: @icon-size-md;
margin-right: 4px;
cursor: pointer
}

.MapSearchBar .input-group-addon .searcherror {
Expand Down