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

[Backport 2024.02.xx] - #10545: Option to disable identify popup in case of no results #10624

Merged
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
4 changes: 4 additions & 0 deletions web/client/actions/__tests__/mapPopups-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,9 @@ describe('test map popups action creators', () => {
const action = POPUP.cleanPopups();
expect(action.type).toEqual(POPUP.CLEAN_MAP_POPUPS);
});
it('enable hide empty popup option', () => {
const action = POPUP.enableHideEmptyPopupOption();
expect(action.type).toEqual(POPUP.ENABLE_HIDE_EMPTY_POPUP);
});
});

5 changes: 5 additions & 0 deletions web/client/actions/mapPopups.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
export const ADD_MAP_POPUP = 'MAP:ADD_POPUP';
export const REMOVE_MAP_POPUP = 'MAP:REMOVE_POPUP';
export const CLEAN_MAP_POPUPS = 'MAP:CLEAN_POPUPS';
export const ENABLE_HIDE_EMPTY_POPUP = 'MAP:ENABLE_HIDE_EMPTY_POPUP';

export const addPopup = (id, options, single = true) => ({
type: ADD_MAP_POPUP,
Expand All @@ -26,3 +27,7 @@ export const removePopup = (id) => ({
export const cleanPopups = () => ({
type: CLEAN_MAP_POPUPS
});

export const enableHideEmptyPopupOption = () => ({
type: ENABLE_HIDE_EMPTY_POPUP
});
9 changes: 7 additions & 2 deletions web/client/components/data/identify/DefaultViewer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ class DefaultViewer extends React.Component {
renderValidOnly: PropTypes.bool,
loaded: PropTypes.bool,
isMobile: PropTypes.bool,
disableInfoAlert: PropTypes.bool
disableInfoAlert: PropTypes.bool,
hidePopupIfNoResults: PropTypes.bool
};

static defaultProps = {
Expand All @@ -64,7 +65,8 @@ class DefaultViewer extends React.Component {
onPrevious: () => {},
setIndex: () => {},
isMobile: false,
disableInfoAlert: false
disableInfoAlert: false,
hidePopupIfNoResults: false
};

shouldComponentUpdate(nextProps) {
Expand Down Expand Up @@ -147,6 +149,9 @@ class DefaultViewer extends React.Component {
renderEmptyPages = () => {
const {emptyResponses} = this.getResponseProperties();
if (this.props.missingResponses === 0 && emptyResponses) {
if (this.props.hidePopupIfNoResults) {
return <span className="hidePopupIfNoResults"/>;
}
return (
<Alert bsStyle={"danger"}>
<h4><HTML msgId="noFeatureInfo"/></h4>
Expand Down
8 changes: 5 additions & 3 deletions web/client/components/data/identify/PopupViewer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import Viewer from './DefaultViewer';
import {isArray, isUndefined} from 'lodash';
import SwipeHeader from './SwipeHeader';
import { identifyFloatingToolSelector } from '../../../selectors/map';
import { hideEmptyPopupSelector } from '../../../selectors/mapPopups';

/**
* Container that render only the selected result
Expand Down Expand Up @@ -46,16 +47,17 @@ const selector = createSelector([
generalInfoFormatSelector,
showEmptyMessageGFISelector,
identifyFloatingToolSelector,
isLoadedResponseSelector],
(responses, validResponses, requests, format, showEmptyMessageGFI, renderValidOnly, loaded) => ({
isLoadedResponseSelector, hideEmptyPopupSelector],
(responses, validResponses, requests, format, showEmptyMessageGFI, renderValidOnly, loaded, hidePopupIfNoResults ) => ({
responses,
validResponses,
requests,
format,
showEmptyMessageGFI,
missingResponses: (requests || []).length - (responses || []).length,
renderValidOnly,
loaded
loaded,
hidePopupIfNoResults
}));


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,4 +269,18 @@ describe('DefaultViewer', () => {
expect(gfiViewer.childNodes[1].childNodes.length).toBe(1);

});
it('test DefaultViewer component with hover identify if hidePopupIfNoResults = true', () => {
const responses = [];
ReactDOM.render(
<DefaultViewer hidePopupIfNoResults responses={responses} header={SwipeHeader}/>,
document.getElementById("container")
);

const container = document.getElementById('container');
let gfiViewer = container.querySelector('.mapstore-identify-viewer');
expect(gfiViewer).toBeTruthy();
expect(gfiViewer.childNodes.length).toBe(1);
expect(document.querySelector(".hidePopupIfNoResults")).toBeTruthy();
expect(document.querySelector(".hidePopupIfNoResults").innerHTML).toBeFalsy();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,18 @@ describe("test identify enhancers", () => {
);
expect(spyIdentifyIsMounted.calls.length).toEqual(1);
});
it("test identifyLifecycle component for call enableHideEmptyPopupOption if hidePopupIfNoResults prop = true", () => {
const Component = identifyLifecycle(() => <div id="test-component"></div>);
const testHandlers = {
enableHideEmptyPopupOption: () => {}
};
const spyEnableHideEmptyPopupOption = expect.spyOn(testHandlers, 'enableHideEmptyPopupOption');
ReactDOM.render(
<Component enabled responses={[{}]} hidePopupIfNoResults enableHideEmptyPopupOption={testHandlers.enableHideEmptyPopupOption}/>,
document.getElementById("container")
);
expect(spyEnableHideEmptyPopupOption.calls.length).toEqual(1);
});
it("Identify should run when enabled prop is true and showInMapPopup prop is false", () => {
let run = sampleComponentDidMount({enabled: true, showInMapPopup: false});
expect(run.checkIdentifyIsMounted).toBe(true);
Expand Down
7 changes: 6 additions & 1 deletion web/client/components/data/identify/enhancers/identify.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ export const identifyLifecycle = compose(
setShowInMapPopup = () => {},
checkIdentifyIsMounted = () => {},
onInitPlugin = () => {},
pluginCfg = {}
pluginCfg = {},
enableHideEmptyPopupOption = () => {},
hidePopupIfNoResults = false
} = this.props;

// Initialize plugin configuration
Expand All @@ -91,6 +93,9 @@ export const identifyLifecycle = compose(
showAllResponses,
highlight: pluginCfg?.highlightEnabledFromTheStart || false
});
if (hidePopupIfNoResults) {
enableHideEmptyPopupOption(true);
}
if (enabled || showInMapPopup) {
changeMousePointer('pointer');
checkIdentifyIsMounted(true);
Expand Down
48 changes: 48 additions & 0 deletions web/client/epics/__tests__/identify-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,54 @@ describe('identify Epics', () => {

testEpic(zoomToVisibleAreaEpic, 3, sentActions, expectedAction, state);
});
it('test zoomToVisibleAreaEpic remove shown marker of identify if no results + existing hideEmptyPopupOption flag = true', (done) => {
// remove previous hook
registerHook('RESOLUTION_HOOK', undefined);

const state = {
mapInfo: {
centerToMarker: true
},
mapPopups: {
hideEmptyPopupOption: true
},
map: {present: {...TEST_MAP_STATE.present, eventListeners: {mousemove: ["identifyFloatingTool"]}}},
maplayout: {
boundingMapRect: {
left: 500,
bottom: 250
}
}
};

const sentActions = [
featureInfoClick({ latlng: { lat: 36.95, lng: -79.84 } }),
loadFeatureInfo(1, "no features were found")
];

const expectedAction = actions => {
try {
expect(actions.length).toBe(2);
actions.map((action) => {
switch (action.type) {
case HIDE_MAPINFO_MARKER:
done();
break;
case UPDATE_CENTER_TO_MARKER:
expect(action.status).toBe('disabled');
break;
default:
expect(true).toBe(false);
}
});
} catch (ex) {
done(ex);
}
done();
};

testEpic(zoomToVisibleAreaEpic, 2, sentActions, expectedAction, state);
});

it('onMapClick triggers featureinfo when selected', done => {
registerHook(GET_COORDINATES_FROM_PIXEL_HOOK, undefined);
Expand Down
10 changes: 9 additions & 1 deletion web/client/epics/identify.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import { floatingIdentifyDelaySelector } from '../selectors/localConfig';
import { createControlEnabledSelector, measureSelector } from '../selectors/controls';
import { localizedLayerStylesEnvSelector } from '../selectors/localizedLayerStyles';
import { mouseOutSelector } from '../selectors/mousePosition';
import { hideEmptyPopupSelector } from '../selectors/mapPopups';
import {getBbox, getCurrentResolution, parseLayoutValue} from '../utils/MapUtils';
import {buildIdentifyRequest, defaultQueryableFilter, filterRequestParams} from '../utils/MapInfoUtils';
import { IDENTIFY_POPUP } from '../components/map/popups';
Expand Down Expand Up @@ -250,8 +251,15 @@ export const zoomToVisibleAreaEpic = (action$, store) =>
.filter(() => centerToMarkerSelector(store.getState()))
.switchMap((action) =>
action$.ofType(LOAD_FEATURE_INFO, ERROR_FEATURE_INFO)
.mergeMap(() => {
.mergeMap((loadFeatInfoAction) => {
const state = store.getState();
const hideIdentifyPopupIfNoResults = hideEmptyPopupSelector(state);
const hoverIdentifyActive = isMouseMoveIdentifyActiveSelector(state);
const noResultFeatures = loadFeatInfoAction.type === LOAD_FEATURE_INFO && loadFeatInfoAction?.data?.includes("no features were found");
// remove marker in case activated identify hover mode and no fetched results plus existing hideIdentifyPopupIfNoResults = true
if (noResultFeatures && hideIdentifyPopupIfNoResults && hoverIdentifyActive) {
return Rx.Observable.from([updateCenterToMarker('disabled'), hideMapinfoMarker()]);
}
const map = mapSelector(state);
const mapProjection = projectionSelector(state);
const projectionDefs = projectionDefsSelector(state);
Expand Down
5 changes: 4 additions & 1 deletion web/client/plugins/Identify.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
checkIdentifyIsMounted,
onInitPlugin
} from '../actions/mapInfo';
import { enableHideEmptyPopupOption } from '../actions/mapPopups';
import DefaultViewerComp from '../components/data/identify/DefaultViewer';
import { defaultViewerDefaultProps, defaultViewerHandlers } from '../components/data/identify/enhancers/defaultViewer';
import { identifyLifecycle } from '../components/data/identify/enhancers/identify';
Expand Down Expand Up @@ -196,6 +197,7 @@ const identifyDefaultProps = defaultProps({
* @prop cfg.dock {bool} true shows dock panel, false shows modal
* @prop cfg.draggable {boolean} draggable info window, when modal
* @prop cfg.showHighlightFeatureButton {boolean} show the highlight feature button if the interrogation returned valid features (openlayers only)
* @prop cfg.hidePopupIfNoResults {boolean} hide/show the identify popup in case of no results
* @prop cfg.highlightEnabledFromTheStart {boolean} the highlight feature button will be activated by default if true
* @prop cfg.viewerOptions.container {expression} the container of the viewer, expression from the context
* @prop cfg.viewerOptions.header {expression} the header of the viewer, expression from the context{expression}
Expand Down Expand Up @@ -265,7 +267,8 @@ const IdentifyPlugin = compose(
identifyIndex,
defaultViewerHandlers,
connect(() => ({}), {
setShowInMapPopup
setShowInMapPopup,
enableHideEmptyPopupOption
}),
identifyLifecycle
)(IdentifyContainer);
Expand Down
9 changes: 4 additions & 5 deletions web/client/plugins/map/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { projectionDefsSelector, isMouseMoveActiveSelector } from '../../selecto
import {
snappingLayerSelector
} from "../../selectors/draw";
import { mapPopupsSelector } from '../../selectors/mapPopups';

const Empty = () => { return <span/>; };

Expand Down Expand Up @@ -101,13 +102,11 @@ const pluginsCreator = (mapType, actions) => {

const LLayer = connect(null, {onWarning: warning})( components.Layer || Empty);

const EMPTY_POPUPS = [];
const PopupSupport = connect(
createSelector(
(state) => state.mapPopups && state.mapPopups.popups || EMPTY_POPUPS,
(popups) => ({
popups
})), {
mapPopupsSelector,
(popups) => ({popups})
), {
onPopupClose: removePopup
}
)(components.PopupSupport || Empty);
Expand Down
6 changes: 5 additions & 1 deletion web/client/reducers/__tests__/mapPopups-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,9 @@ describe('mapPopups reducer', () => {
expect(state.popups).toExist();
expect(state.popups.length).toBe(0);
});

it('ENABLE_HIDE_EMPTY_POPUP ', () => {
const state = reducer(initialState, ACTIONS.enableHideEmptyPopupOption());
expect(state.popups).toExist();
expect(state.hideEmptyPopupOption).toEqual(true);
});
});
7 changes: 6 additions & 1 deletion web/client/reducers/mapPopups.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree.
*/

import {ADD_MAP_POPUP, REMOVE_MAP_POPUP, CLEAN_MAP_POPUPS} from '../actions/mapPopups';
import {ADD_MAP_POPUP, REMOVE_MAP_POPUP, CLEAN_MAP_POPUPS, ENABLE_HIDE_EMPTY_POPUP} from '../actions/mapPopups';
import {arrayDelete} from '../utils/ImmutableUtils';

const initialState = {popups: []};
Expand All @@ -23,6 +23,11 @@ export default function(state = initialState, action) {
case CLEAN_MAP_POPUPS: {
return {...state, popups: []};
}
case ENABLE_HIDE_EMPTY_POPUP: {
return {
...state, hideEmptyPopupOption: true
};
}
default:
return state;
}
Expand Down
36 changes: 36 additions & 0 deletions web/client/selectors/__tests__/mapPopups-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2024, 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.
*/


import expect from 'expect';

import {
mapPopupsSelector,
hideEmptyPopupSelector
} from '../mapPopups';

describe('Test mapPopups', () => {
it('test mapPopupsSelector', () => {
const popups = mapPopupsSelector({mapPopups: {popups: [{"key": "value"}]}});

expect(popups).toExist();
expect(popups).toEqual([{key: "value"}]);
});

it('test hideEmptyPopupSelector true', () => {
const hideEmptyPopupOption = hideEmptyPopupSelector({mapPopups: {popups: [{"key": "value"}], hideEmptyPopupOption: true}});

expect(hideEmptyPopupOption).toExist();
expect(hideEmptyPopupOption).toBe(true);
});
it('test hideEmptyPopupSelector false', () => {
const hideEmptyPopupOption = hideEmptyPopupSelector({mapPopups: {popups: [{"key": "value"}], hideEmptyPopupOption: false}});

expect(hideEmptyPopupOption).toBeFalsy();
});
});
18 changes: 18 additions & 0 deletions web/client/selectors/mapPopups.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright 2024, 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.
*/


/**
* selects mapPopups state
* @name mapPopups
* @memberof selectors
* @static
*/

export const mapPopupsSelector = (state) => state?.mapPopups && state?.mapPopups?.popups || [];
export const hideEmptyPopupSelector = (state) => state?.mapPopups && state?.mapPopups?.hideEmptyPopupOption || false;
4 changes: 3 additions & 1 deletion web/client/themes/default/less/map-popup.less
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@
.map-popup-ol:before {
.border-top-color-var(@theme-vars[main-border-color]);
}

.map-popup-ol:has(.hidePopupIfNoResults), .ms-leaflet-popup:has(.hidePopupIfNoResults) {
display: none;
}
.ol-popup-closer {
.ms-popup-close-button();
}
Expand Down
Loading