Skip to content

Commit 5a71d96

Browse files
committed
Enhanced customization for highlight styles of selected/clicked features in FeatureEditor and Identify - enabled defining highlightStyle object under FeatureEditor and Identify Plugin in localConfig.json to apply costume highlight styles for features. - updated and added relevant tests - updated jsdoc for both plugins On behalf of DB Systel GmbH
1 parent 4638aac commit 5a71d96

23 files changed

Lines changed: 347 additions & 87 deletions

web/client/actions/__tests__/highlight-test.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,27 @@ describe('Test correctness of the highlight actions', () => {
2828
expect(retval.type).toBe(HIGHLIGHT_STATUS);
2929
expect(retval.status).toBe("enabled");
3030
});
31-
it('highlightStatus', () => {
31+
it('setHighlightFeaturesPath', () => {
3232
let path = "my.path";
3333

3434
let retval = setHighlightFeaturesPath(path);
3535

3636
expect(retval).toExist();
3737
expect(retval.type).toBe(SET_HIGHLIGHT_FEATURES_PATH);
3838
expect(retval.featuresPath).toBe(path);
39+
expect(retval.highlightStyle).toEqual({});
40+
});
41+
42+
it('setHighlightFeaturesPath with customized highlight style', () => {
43+
let path = "my.path";
44+
let style = {color: 'yellow'};
45+
46+
let retval = setHighlightFeaturesPath(path, style);
47+
48+
expect(retval).toExist();
49+
expect(retval.type).toBe(SET_HIGHLIGHT_FEATURES_PATH);
50+
expect(retval.featuresPath).toBe(path);
51+
expect(retval.highlightStyle).toBe(style);
3952
});
4053

4154
it('updateHighlighted', () => {

web/client/actions/highlight.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@ export function highlightStatus(status) {
1616
status
1717
};
1818
}
19-
export function setHighlightFeaturesPath(featuresPath) {
19+
export function setHighlightFeaturesPath(featuresPath, highlightStyle = {}) {
2020
return {
2121
type: SET_HIGHLIGHT_FEATURES_PATH,
22-
featuresPath
22+
featuresPath,
23+
highlightStyle
2324
};
2425
}
2526
export function updateHighlighted(features, status) {

web/client/components/data/identify/enhancers/identify.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ export const identifyLifecycle = compose(
9191
},
9292
maxItems,
9393
showAllResponses,
94-
highlight: pluginCfg?.highlightEnabledFromTheStart || false
94+
highlight: pluginCfg?.highlightEnabledFromTheStart || false,
95+
highlightStyle: pluginCfg?.highlightStyle || {}
9596
});
9697
if (hidePopupIfNoResults) {
9798
enableHideEmptyPopupOption(true);

web/client/epics/__tests__/featuregrid-test.js

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,7 @@ describe('featuregrid Epics', () => {
716716
switch (action.type) {
717717
case SET_HIGHLIGHT_FEATURES_PATH:
718718
expect(action.featuresPath).toBe('featuregrid.select');
719+
expect(action.highlightStyle).toEqual({});
719720
break;
720721
case CHANGE_DRAWING_STATUS:
721722
expect(action.status).toBe("clean");
@@ -737,6 +738,37 @@ describe('featuregrid Epics', () => {
737738
testEpic(setHighlightFeaturesPath, 3, toggleEditMode(), epicResult, stateWithGmlGeometry);
738739
});
739740

741+
it('set highlight feature path with geometry not supported EDIT MODE and costume highligth style', (done) => {
742+
const highlightStyle = {color: 'red'};
743+
const stateWithCustomHighlightStyle = set("featuregrid.highlightStyle", highlightStyle, stateWithGmlGeometry);
744+
const epicResult = actions => {
745+
expect(actions.length).toBe(3);
746+
actions.map((action) => {
747+
switch (action.type) {
748+
case SET_HIGHLIGHT_FEATURES_PATH:
749+
expect(action.featuresPath).toBe('featuregrid.select');
750+
expect(action.highlightStyle).toBe(highlightStyle);
751+
break;
752+
case CHANGE_DRAWING_STATUS:
753+
expect(action.status).toBe("clean");
754+
expect(action.method).toBe("");
755+
expect(action.features).toEqual([]);
756+
expect(action.options).toEqual({});
757+
expect(action.style).toBe(undefined);
758+
break;
759+
case SHOW_NOTIFICATION:
760+
expect(action.uid).toBe("notSupportedGeometryWarning");
761+
break;
762+
default:
763+
expect(true).toBe(false);
764+
}
765+
});
766+
done();
767+
};
768+
769+
testEpic(setHighlightFeaturesPath, 3, toggleEditMode(), epicResult, stateWithCustomHighlightStyle);
770+
});
771+
740772
it('set highlight feature path VIEW MODE', (done) => {
741773
const epicResult = actions => {
742774
try {
@@ -745,6 +777,7 @@ describe('featuregrid Epics', () => {
745777
switch (action.type) {
746778
case SET_HIGHLIGHT_FEATURES_PATH:
747779
expect(action.featuresPath).toBe('featuregrid.select');
780+
expect(action.highlightStyle).toEqual({});
748781
break;
749782
case CHANGE_DRAWING_STATUS:
750783
expect(action.status).toBe("clean");
@@ -766,6 +799,38 @@ describe('featuregrid Epics', () => {
766799
testEpic(setHighlightFeaturesPath, 2, toggleViewMode(), epicResult, state);
767800
});
768801

802+
it('set highlight feature path VIEW MODE with costume highlight style', (done) => {
803+
const highlightStyle = {color: 'red'};
804+
const stateWithCustomHighlightStyle = set("featuregrid.highlightStyle", highlightStyle, state);
805+
const epicResult = actions => {
806+
try {
807+
expect(actions.length).toBe(2);
808+
actions.map((action) => {
809+
switch (action.type) {
810+
case SET_HIGHLIGHT_FEATURES_PATH:
811+
expect(action.featuresPath).toBe('featuregrid.select');
812+
expect(action.highlightStyle).toBe(highlightStyle);
813+
break;
814+
case CHANGE_DRAWING_STATUS:
815+
expect(action.status).toBe("clean");
816+
expect(action.method).toBe("");
817+
expect(action.features).toEqual([]);
818+
expect(action.options).toEqual({});
819+
expect(action.style).toBe(undefined);
820+
break;
821+
default:
822+
expect(true).toBe(false);
823+
}
824+
});
825+
} catch (e) {
826+
done(e);
827+
}
828+
done();
829+
};
830+
831+
testEpic(setHighlightFeaturesPath, 2, toggleViewMode(), epicResult, stateWithCustomHighlightStyle);
832+
});
833+
769834
it('set highlight feature path EDIT MODE', (done) => {
770835
const epicResult = actions => {
771836
try {

web/client/epics/featuregrid.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ import {
142142
getAttributeFilters,
143143
selectedLayerSelector,
144144
multiSelect,
145-
paginationSelector, isViewportFilterActive, viewportFilter
145+
paginationSelector, isViewportFilterActive, viewportFilter, highlightStyleSelector
146146
} from '../selectors/featuregrid';
147147

148148
import { error, warning } from '../actions/notifications';
@@ -897,11 +897,12 @@ export const onFeatureGridCreateNewFeature = (action$) => action$.ofType(CREATE_
897897
*/
898898
export const setHighlightFeaturesPath = (action$, store) => action$.ofType(TOGGLE_MODE)
899899
.switchMap( (a) => {
900+
const highlightStyle = highlightStyleSelector(store.getState());
900901
if (a.mode === MODES.VIEW) {
901-
return Rx.Observable.of(drawSupportReset(), setHighlightFeaturesPathAction("featuregrid.select"));
902+
return Rx.Observable.of(drawSupportReset(), setHighlightFeaturesPathAction("featuregrid.select", highlightStyle));
902903
}
903904
if (a.mode === MODES.EDIT && !hasSupportedGeometry(store.getState())) {
904-
return Rx.Observable.of(drawSupportReset(), setHighlightFeaturesPathAction("featuregrid.select"), warning({
905+
return Rx.Observable.of(drawSupportReset(), setHighlightFeaturesPathAction("featuregrid.select", highlightStyle), warning({
905906
title: "featuregrid.notSupportedGeometryTitle",
906907
message: "featuregrid.notSupportedGeometry",
907908
uid: "notSupportedGeometryWarning",

web/client/plugins/FeatureEditor.jsx

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ import {isViewportFilterActive} from "../selectors/featuregrid";
104104
* @prop {boolean} cfg.useUTCOffset avoid using UTC dates in attribute table and datetime editor, should be kept consistent with dateFormats
105105
* @prop {object} cfg.dateFormats Allows to specify custom date formats ( in [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format) to use to display dates in the table. `date` `date-time` and `time` are the supported entries for the date format. Example:
106106
* @prop {boolean} cfg.showPopoverSync default false. Hide the popup of map sync if false, shows the popup of map sync if true
107+
* @prop {object} cfg.highlightStyle default empty. Custom style for highlighted features.
107108
* ```
108109
* "dateFormats": {
109110
* "date-time": "MM DD YYYY - HH:mm:ss",
@@ -151,7 +152,12 @@ import {isViewportFilterActive} from "../selectors/featuregrid";
151152
* },
152153
* "filterByViewport": true,
153154
* "showFilterByViewportTool": true
154-
* }
155+
* },
156+
* "highlightStyle": {
157+
* "color": "#00ffff",
158+
* "width": 4,
159+
* "lineColor": "#00ffff",
160+
* "fillOpacity": 0.3
155161
* }
156162
* ```
157163
*
@@ -200,24 +206,27 @@ const EditorPlugin = connect(
200206
virtualScroll: this.props.virtualScroll ?? true,
201207
editingAllowedRoles: this.props.editingAllowedRoles,
202208
editingAllowedGroups: this.props.editingAllowedGroups,
203-
maxStoredPages: this.props.maxStoredPages
209+
maxStoredPages: this.props.maxStoredPages,
210+
highlightStyle: this.props.highlightStyle
204211
});
205212
},
206213
componentDidUpdate(prevProps) {
207214
// Re-Initialize configurations
208215
!this.props.viewportFilterInitialized && this.props.filterByViewport && this.props.setViewportFilter(true);
209216

210-
const {virtualScroll, editingAllowedRoles, editingAllowedGroups, maxStoredPages} = this.props ?? {};
217+
const {virtualScroll, editingAllowedRoles, editingAllowedGroups, maxStoredPages, highlightStyle} = this.props ?? {};
211218
if (prevProps.virtualScroll !== virtualScroll
212219
|| !isEqual(prevProps.editingAllowedRoles, editingAllowedRoles)
213220
|| !isEqual(prevProps.editingAllowedGroups, editingAllowedGroups)
214221
|| prevProps.maxStoredPages !== maxStoredPages
222+
|| !isEqual(prevProps.highlightStyle, highlightStyle)
215223
) {
216224
this.props.initPlugin({
217225
virtualScroll: virtualScroll ?? true,
218226
editingAllowedRoles,
219227
editingAllowedGroups,
220-
maxStoredPages
228+
maxStoredPages,
229+
highlightStyle
221230
});
222231
}
223232
}

web/client/plugins/Identify.jsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ const identifyDefaultProps = defaultProps({
199199
* @prop cfg.showHighlightFeatureButton {boolean} show the highlight feature button if the interrogation returned valid features (openlayers only)
200200
* @prop cfg.hidePopupIfNoResults {boolean} hide/show the identify popup in case of no results
201201
* @prop cfg.highlightEnabledFromTheStart {boolean} the highlight feature button will be activated by default if true
202+
* @prop cfg.highlightSytle {object} the style to apply to highlight features, when not defined a default style is applied
202203
* @prop cfg.viewerOptions.container {expression} the container of the viewer, expression from the context
203204
* @prop cfg.viewerOptions.header {expression} the header of the viewer, expression from the context{expression}
204205
* @prop cfg.disableCenterToMarker {bool} disable zoom to marker action
@@ -221,6 +222,12 @@ const identifyDefaultProps = defaultProps({
221222
* "viewerOptions": {
222223
* "container": "{context.ReactSwipe}",
223224
* "header": "{context.SwipeHeader}"
225+
* },
226+
* "highlightStyle": {
227+
* "color": "#00ffff",
228+
* "width": 4,
229+
* "lineColor": "#00ffff",
230+
* "fillOpacity": 0.3
224231
* }
225232
* }
226233
* }

web/client/plugins/Map.jsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,8 @@ class MapPlugin extends React.Component {
212212
onLoadingMapPlugins: PropTypes.func,
213213
onMapTypeLoaded: PropTypes.func,
214214
pluginsCreator: PropTypes.func,
215-
mapTitle: PropTypes.string
215+
mapTitle: PropTypes.string,
216+
highlightStyle: PropTypes.object
216217
};
217218

218219
static defaultProps = {
@@ -288,7 +289,7 @@ class MapPlugin extends React.Component {
288289

289290
getHighlightLayer = (projection, index, env) => {
290291
const plugins = this.state.plugins;
291-
const {features, ...options} = getHighlightLayerOptions({features: this.props.features});
292+
const {features, ...options} = getHighlightLayerOptions({features: this.props.features}, this.props?.highlightStyle);
292293
return (<plugins.Layer type="vector"
293294
srs={projection}
294295
position={index}

web/client/plugins/__tests__/FeatureEditor-test.jsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,11 @@ describe('FeatureEditor Plugin', () => {
3434
const props = {
3535
virtualScroll: false,
3636
editingAllowedRoles: ['USER', 'ADMIN'],
37-
maxStoredPages: 5
37+
maxStoredPages: 5,
38+
highlightStyle: {
39+
color: 'red',
40+
weight: 3
41+
}
3842
};
3943
const {Plugin, store} = getPluginForTest(FeatureEditor, { featuregrid: { open: false } });
4044
ReactDOM.render(<Plugin {...props}/>, document.getElementById("container"));
@@ -43,13 +47,18 @@ describe('FeatureEditor Plugin', () => {
4347
expect(state.virtualScroll).toBeFalsy();
4448
expect(state.editingAllowedRoles).toEqual(props.editingAllowedRoles);
4549
expect(state.maxStoredPages).toBe(props.maxStoredPages);
50+
expect(state.highlightStyle).toEqual(props.highlightStyle);
4651
});
4752
it('onInit FeatureEditor plugin be-recalled when props change', () => {
4853
const props = {
4954
virtualScroll: false,
5055
editingAllowedRoles: ['ADMIN'],
5156
editingAllowedGroups: ['GROUP1'],
52-
maxStoredPages: 5
57+
maxStoredPages: 5,
58+
highlightStyle: {
59+
color: 'red',
60+
weight: 3
61+
}
5362
};
5463
const props2 = {
5564
editingAllowedRoles: ['USER', 'ADMIN'],
@@ -68,15 +77,18 @@ describe('FeatureEditor Plugin', () => {
6877
expect(state.editingAllowedRoles).toEqual(props.editingAllowedRoles);
6978
expect(state.editingAllowedGroups).toEqual(props.editingAllowedGroups);
7079
expect(state.maxStoredPages).toBe(props.maxStoredPages);
80+
expect(state.highlightStyle).toEqual(props.highlightStyle);
7181
ReactDOM.render(<Plugin {...props2}/>, document.getElementById("container"));
7282
const state2 = store.getState().featuregrid;
7383
expect(state2.virtualScroll).toBeTruthy(); // the default
7484
expect(state2.editingAllowedRoles).toEqual(props2.editingAllowedRoles); // changed
7585
expect(state2.editingAllowedGroups).toEqual(props2.editingAllowedGroups); // changed
7686
expect(state2.maxStoredPages).toBe(props2.maxStoredPages);
87+
expect(state2.highlightStyle).toEqual({});
7788
ReactDOM.render(<Plugin {...props3}/>, document.getElementById("container"));
7889
const state3 = store.getState().featuregrid;
7990
expect(isEqual(state2.editingAllowedRoles, state3.editingAllowedRoles)).toBeTruthy(); // no double call
8091
expect(isEqual(state2.editingAllowedGroups, state3.editingAllowedGroups)).toBeTruthy(); // no double call
92+
expect(state2).toBe(state3); // no double call
8193
});
8294
});

web/client/plugins/map/__tests__/selector-test.jsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,19 @@ import { registerEventListener } from '../../../actions/map';
1818

1919
const stateMocker = createStateMocker({ map, layers, additionallayers});
2020
describe('Map plugin state to pros selector', () => {
21+
it('test getting highlightStyle from selector', () => {
22+
const stateWithHighlightStyle = selector({highlight: {
23+
highlightStyle: {
24+
color: 'red',
25+
weight: 3
26+
}
27+
}});
28+
expect(stateWithHighlightStyle.highlightStyle).toBeTruthy();
29+
expect(stateWithHighlightStyle.highlightStyle).toEqual({
30+
color: 'red',
31+
weight: 3
32+
});
33+
});
2134
it('test getting mapTitle from selector', () => {
2235
const stateWithMapTitle = selector({map: {
2336
info: {

0 commit comments

Comments
 (0)