From 20c08cf3ce631cd2551efe7df4c3707b27fd0c95 Mon Sep 17 00:00:00 2001
From: mahmoud adel <58145645+mahmoudadel54@users.noreply.github.com>
Date: Mon, 10 Feb 2025 17:47:31 +0200
Subject: [PATCH] #10736: fix issue of not hiding the enabled/disabled filter
icon in case there is no filters after reseting the interactive legend
(#10797)
---
.../epics/__tests__/styleeditor-test.js | 395 ++++++++++++------
.../plugins/TOC/components/FilterNodeTool.jsx | 3 +-
.../__tests__/DefaultLayer-test.jsx | 39 +-
web/client/utils/FilterUtils.js | 28 +-
.../utils/__tests__/FilterUtils-test.js | 15 +-
5 files changed, 329 insertions(+), 151 deletions(-)
diff --git a/web/client/epics/__tests__/styleeditor-test.js b/web/client/epics/__tests__/styleeditor-test.js
index 11eea235cc..64e83ba501 100644
--- a/web/client/epics/__tests__/styleeditor-test.js
+++ b/web/client/epics/__tests__/styleeditor-test.js
@@ -57,6 +57,7 @@ import { testEpic } from './epicTestUtils';
import MockAdapter from 'axios-mock-adapter';
import axios from '../../libs/ajax';
import { INTERACTIVE_LEGEND_ID } from '../../utils/LegendUtils';
+import { setConfigProp } from '../../utils/ConfigUtils';
let mockAxios;
@@ -466,141 +467,283 @@ describe('Test styleeditor epics', () => {
state);
});
- it('test createStyleEpic', (done) => {
- const state = {
- layers: {
- flat: [
- {
- id: 'layerId',
- name: 'layerName',
- url: 'base/web/client/test-resources/geoserver/',
- describeFeatureType: {},
- style: 'test_style',
- layerFilter: {
- filters: [{id: INTERACTIVE_LEGEND_ID, "test": "test"}]
- },
- enableInteractiveLegend: true
+ describe("tests for createStyleEpic", () => {
+ beforeEach(done => {
+ setConfigProp('miscSettings', { experimentalInteractiveLegend: true });
+ setTimeout(done);
+ });
+ afterEach(done => {
+ setConfigProp('miscSettings', { });
+ setTimeout(done);
+ });
+ it('test createStyleEpic', (done) => {
+ const state = {
+ layers: {
+ flat: [
+ {
+ id: 'layerId',
+ name: 'layerName',
+ url: 'base/web/client/test-resources/geoserver/',
+ describeFeatureType: {},
+ style: 'test_style',
+ layerFilter: {
+ filters: [{"format": "cql", "body": "test"}]
+ }
+ }
+ ],
+ selected: [
+ 'layerId'
+ ]
+ },
+ styleeditor: {
+ service: {
+ baseUrl: 'base/web/client/test-resources/geoserver/'
}
- ],
- selected: [
- 'layerId'
- ]
- },
- styleeditor: {
- service: {
- baseUrl: 'base/web/client/test-resources/geoserver/'
}
- }
- };
- const NUMBER_OF_ACTIONS = 5;
- const results = (actions) => {
- expect(actions.length).toBe(NUMBER_OF_ACTIONS);
- try {
- actions.map((action) => {
- switch (action.type) {
- case LOADING_STYLE:
- expect(action.status).toBe('');
- break;
- case UPDATE_OPTIONS_BY_OWNER:
- expect(action.owner).toBe('styleeditor');
- expect(action.options).toEqual([{}]);
- break;
- case UPDATE_SETTINGS_PARAMS:
- const styleName = action.newParams.style.split('___');
- expect(styleName[0]).toBe('style_title');
- expect(action.newParams.layerFilter).toBeTruthy();
- expect(action.update).toBe(true);
- break;
- case UPDATE_STATUS:
- expect(action.status).toBe('');
- break;
- case LOADED_STYLE:
- expect(action).toExist();
- break;
- default:
- expect(action).toBe(false);
- }
- });
- } catch (e) {
- done(e);
- }
- done();
- };
+ };
+ const NUMBER_OF_ACTIONS = 5;
+ const results = (actions) => {
+ expect(actions.length).toBe(NUMBER_OF_ACTIONS);
+ try {
+ actions.map((action) => {
+ switch (action.type) {
+ case LOADING_STYLE:
+ expect(action.status).toBe('');
+ break;
+ case UPDATE_OPTIONS_BY_OWNER:
+ expect(action.owner).toBe('styleeditor');
+ expect(action.options).toEqual([{}]);
+ break;
+ case UPDATE_SETTINGS_PARAMS:
+ const styleName = action.newParams.style.split('___');
+ expect(styleName[0]).toBe('style_title');
+ expect(action.update).toBe(true);
+ break;
+ case UPDATE_STATUS:
+ expect(action.status).toBe('');
+ break;
+ case LOADED_STYLE:
+ expect(action).toExist();
+ break;
+ default:
+ expect(action).toBe(false);
+ }
+ });
+ } catch (e) {
+ done(e);
+ }
+ done();
+ };
- testEpic(
- createStyleEpic,
- NUMBER_OF_ACTIONS,
- createStyle({title: 'style TitLe'}),
- results,
- state);
- });
+ testEpic(
+ createStyleEpic,
+ NUMBER_OF_ACTIONS,
+ createStyle({title: 'style TitLe'}),
+ results,
+ state);
+ });
+ it('test createStyleEpic if only interactive legend filter was applied', (done) => {
+ const state = {
+ layers: {
+ flat: [
+ {
+ id: 'layerId',
+ name: 'layerName',
+ url: 'base/web/client/test-resources/geoserver/',
+ describeFeatureType: {},
+ style: 'test_style',
+ layerFilter: {
+ filters: [{id: INTERACTIVE_LEGEND_ID, "test": "test"}]
+ },
+ enableInteractiveLegend: true
+ }
+ ],
+ selected: [
+ 'layerId'
+ ]
+ },
+ styleeditor: {
+ service: {
+ baseUrl: 'base/web/client/test-resources/geoserver/'
+ }
+ }
+ };
+ const NUMBER_OF_ACTIONS = 5;
+ const results = (actions) => {
+ expect(actions.length).toBe(NUMBER_OF_ACTIONS);
+ try {
+ actions.map((action) => {
+ switch (action.type) {
+ case LOADING_STYLE:
+ expect(action.status).toBe('');
+ break;
+ case UPDATE_OPTIONS_BY_OWNER:
+ expect(action.owner).toBe('styleeditor');
+ expect(action.options).toEqual([{}]);
+ break;
+ case UPDATE_SETTINGS_PARAMS:
+ const styleName = action.newParams.style.split('___');
+ expect(styleName[0]).toBe('style_title');
+ expect(action.newParams.layerFilter).toBeFalsy();
+ expect(action.update).toBe(true);
+ break;
+ case UPDATE_STATUS:
+ expect(action.status).toBe('');
+ break;
+ case LOADED_STYLE:
+ expect(action).toExist();
+ break;
+ default:
+ expect(action).toBe(false);
+ }
+ });
+ } catch (e) {
+ done(e);
+ }
+ done();
+ };
- it('test createStyleEpic with workspace', (done) => {
- const workspace = 'test';
- const state = {
- layers: {
- flat: [
- {
- id: 'layerId',
- name: `${workspace}:layerName`,
- url: 'base/web/client/test-resources/geoserver/',
- describeFeatureType: {},
- style: 'test_style'
+ testEpic(
+ createStyleEpic,
+ NUMBER_OF_ACTIONS,
+ createStyle({title: 'style TitLe'}),
+ results,
+ state);
+ });
+ it('test createStyleEpic if a interactive legend filter [with enabled experimentalInteractiveLegend = true + enableInteractiveLegend = true] was applied plus another filter', (done) => {
+ const state = {
+ layers: {
+ flat: [
+ {
+ id: 'layerId',
+ name: 'layerName',
+ url: 'base/web/client/test-resources/geoserver/',
+ describeFeatureType: {},
+ style: 'test_style',
+ layerFilter: {
+ filters: [{id: INTERACTIVE_LEGEND_ID, "test": "test"}, {"format": "cql", "body": "test"}]
+ },
+ enableInteractiveLegend: true
+ }
+ ],
+ selected: [
+ 'layerId'
+ ]
+ },
+ styleeditor: {
+ service: {
+ baseUrl: 'base/web/client/test-resources/geoserver/'
}
- ],
- selected: [
- 'layerId'
- ]
- },
- styleeditor: {
- service: {
- baseUrl: 'base/web/client/test-resources/geoserver/'
}
- }
- };
- const NUMBER_OF_ACTIONS = 4;
- const results = (actions) => {
- expect(actions.length).toBe(NUMBER_OF_ACTIONS);
- try {
- actions.map((action) => {
- switch (action.type) {
- case LOADING_STYLE:
- expect(action.status).toBe('');
- break;
- case UPDATE_OPTIONS_BY_OWNER:
- expect(action.owner).toBe('styleeditor');
- expect(action.options).toEqual([{}]);
- break;
- case UPDATE_SETTINGS_PARAMS:
- const styleName = action.newParams.style.split('___');
- expect(styleName[0]).toBe(`${workspace}:style_title`);
- expect(action.newParams.layerFilter).toBeFalsy();
- expect(action.update).toBe(true);
- break;
- case UPDATE_STATUS:
- expect(action.status).toBe('');
- break;
- case LOADED_STYLE:
- expect(action).toExist();
- break;
- default:
- expect(action).toBe(false);
+ };
+ const NUMBER_OF_ACTIONS = 5;
+ const results = (actions) => {
+ expect(actions.length).toBe(NUMBER_OF_ACTIONS);
+ try {
+ actions.map((action) => {
+ switch (action.type) {
+ case LOADING_STYLE:
+ expect(action.status).toBe('');
+ break;
+ case UPDATE_OPTIONS_BY_OWNER:
+ expect(action.owner).toBe('styleeditor');
+ expect(action.options).toEqual([{}]);
+ break;
+ case UPDATE_SETTINGS_PARAMS:
+ const styleName = action.newParams.style.split('___');
+ expect(styleName[0]).toBe('style_title');
+ expect(action.newParams.layerFilter).toBeTruthy();
+ expect(action.update).toBe(true);
+ break;
+ case UPDATE_STATUS:
+ expect(action.status).toBe('');
+ break;
+ case LOADED_STYLE:
+ expect(action).toExist();
+ break;
+ default:
+ expect(action).toBe(false);
+ }
+ });
+ } catch (e) {
+ done(e);
+ }
+ done();
+ };
+
+ testEpic(
+ createStyleEpic,
+ NUMBER_OF_ACTIONS,
+ createStyle({title: 'style TitLe'}),
+ results,
+ state);
+ });
+ it('test createStyleEpic with workspace', (done) => {
+ const workspace = 'test';
+ const state = {
+ layers: {
+ flat: [
+ {
+ id: 'layerId',
+ name: `${workspace}:layerName`,
+ url: 'base/web/client/test-resources/geoserver/',
+ describeFeatureType: {},
+ style: 'test_style'
+ }
+ ],
+ selected: [
+ 'layerId'
+ ]
+ },
+ styleeditor: {
+ service: {
+ baseUrl: 'base/web/client/test-resources/geoserver/'
}
- });
- } catch (e) {
- done(e);
- }
- done();
- };
+ }
+ };
+ const NUMBER_OF_ACTIONS = 4;
+ const results = (actions) => {
+ expect(actions.length).toBe(NUMBER_OF_ACTIONS);
+ try {
+ actions.map((action) => {
+ switch (action.type) {
+ case LOADING_STYLE:
+ expect(action.status).toBe('');
+ break;
+ case UPDATE_OPTIONS_BY_OWNER:
+ expect(action.owner).toBe('styleeditor');
+ expect(action.options).toEqual([{}]);
+ break;
+ case UPDATE_SETTINGS_PARAMS:
+ const styleName = action.newParams.style.split('___');
+ expect(styleName[0]).toBe(`${workspace}:style_title`);
+ expect(action.newParams.layerFilter).toBeFalsy();
+ expect(action.update).toBe(true);
+ break;
+ case UPDATE_STATUS:
+ expect(action.status).toBe('');
+ break;
+ case LOADED_STYLE:
+ expect(action).toExist();
+ break;
+ default:
+ expect(action).toBe(false);
+ }
+ });
+ } catch (e) {
+ done(e);
+ }
+ done();
+ };
- testEpic(
- createStyleEpic,
- NUMBER_OF_ACTIONS,
- createStyle({title: 'style TitLe'}),
- results,
- state);
+ testEpic(
+ createStyleEpic,
+ NUMBER_OF_ACTIONS,
+ createStyle({title: 'style TitLe'}),
+ results,
+ state);
+ });
});
-
it('test updateStyleCodeEpic', (done) => {
const state = {
layers: {
diff --git a/web/client/plugins/TOC/components/FilterNodeTool.jsx b/web/client/plugins/TOC/components/FilterNodeTool.jsx
index 4fd44b9401..e75381666d 100644
--- a/web/client/plugins/TOC/components/FilterNodeTool.jsx
+++ b/web/client/plugins/TOC/components/FilterNodeTool.jsx
@@ -6,6 +6,7 @@
* LICENSE file in the root directory of this source tree.
*/
import React from 'react';
+import { isFilterEmpty } from '../../../utils/FilterUtils';
const FilterNodeTool = ({
node,
@@ -14,7 +15,7 @@ const FilterNodeTool = ({
}) => {
const ItemComponent = itemComponent;
const { layerFilter } = node || {};
- if (!layerFilter || !ItemComponent) {
+ if (isFilterEmpty(layerFilter) || !ItemComponent) {
return null;
}
const { disabled } = layerFilter || {};
diff --git a/web/client/plugins/TOC/components/__tests__/DefaultLayer-test.jsx b/web/client/plugins/TOC/components/__tests__/DefaultLayer-test.jsx
index 7aab1562cc..0322a4d1f9 100644
--- a/web/client/plugins/TOC/components/__tests__/DefaultLayer-test.jsx
+++ b/web/client/plugins/TOC/components/__tests__/DefaultLayer-test.jsx
@@ -413,20 +413,55 @@ describe('test DefaultLayer module component', () => {
expect(errorTooltip).toBeFalsy();
});
- it('should display the layer filter button', () => {
+ it('should display the layer filter button if there is filters in layerFilter like filterFields/spatialFilter/legendFilter', () => {
const layer = {
id: 'layer00',
name: 'layer00',
title: 'Layer',
visibility: false,
opacity: 0.5,
- layerFilter: {}
+ layerFilter: {
+ "filterFields": [
+ {
+ "rowId": 1,
+ "groupId": 1,
+ "attribute": "FIELD_01",
+ "operator": "=",
+ "value": "value01",
+ "type": "string",
+ "fieldOptions": {
+ "valuesCount": 0,
+ "currentPage": 1
+ },
+ "exception": null,
+ "loading": false,
+ "openAutocompleteMenu": false,
+ "options": {
+ "FIELD_01": []
+ }
+ }
+ ]
+ }
};
ReactDOM.render(, document.getElementById("container"));
const filter = document.querySelector('.glyphicon-filter');
expect(filter).toBeTruthy();
});
+ it('should hide the layer filter button if layerFilter is empty from any kind filters', () => {
+ const layer = {
+ id: 'layer00',
+ name: 'layer00',
+ title: 'Layer',
+ visibility: false,
+ opacity: 0.5,
+ layerFilter: {}
+ };
+
+ ReactDOM.render(, document.getElementById("container"));
+ const filter = document.querySelector('.glyphicon-filter');
+ expect(filter).toBeFalsy();
+ });
it('should not display the layer filter button when hideFilter is true', () => {
const layer = {
diff --git a/web/client/utils/FilterUtils.js b/web/client/utils/FilterUtils.js
index e08dbd5ea4..70d11bb484 100644
--- a/web/client/utils/FilterUtils.js
+++ b/web/client/utils/FilterUtils.js
@@ -31,6 +31,7 @@ export const cqlToOgc = (cqlFilter, fOpts) => {
import { get, isNil, isArray, find, findIndex, isString, flatten } from 'lodash';
import { INTERACTIVE_LEGEND_ID } from './LegendUtils';
+import { getMiscSetting } from './ConfigUtils';
let FilterUtils;
const wrapValueWithWildcard = (value, condition) => {
@@ -1332,7 +1333,7 @@ export const updateLayerLegendFilter = (layerFilterObj, legendFilter) => {
...filterObj, filters: filterObj?.filters?.filter(f => f.id !== INTERACTIVE_LEGEND_ID)
};
}
- let newFilter = filterObj ? filterObj : undefined;
+ let newFilter = !isFilterEmpty(filterObj) ? filterObj : undefined;
return newFilter;
}
let interactiveLegendFilters = isLegendFilterExist ? isLegendFilterExist.filters || [] : [];
@@ -1355,7 +1356,7 @@ export const updateLayerLegendFilter = (layerFilterObj, legendFilter) => {
}
let newFilter = {
...(filterObj || {}), filters: [
- ...(filterObj?.filters?.filter(f => f.id !== INTERACTIVE_LEGEND_ID) || []), ...[
+ ...(filterObj?.filters?.filter(f => f.id !== INTERACTIVE_LEGEND_ID) || []), ...(interactiveLegendFilters?.length ? [
{
"id": INTERACTIVE_LEGEND_ID,
"format": "logic",
@@ -1363,10 +1364,10 @@ export const updateLayerLegendFilter = (layerFilterObj, legendFilter) => {
"logic": "OR",
"filters": [...interactiveLegendFilters]
}
- ]
+ ] : [])
]
};
- return newFilter;
+ return !isFilterEmpty(newFilter) ? newFilter : undefined;
};
/**
@@ -1407,7 +1408,7 @@ export const updateLayerWFSVectorLegendFilter = (layerFilterObj, legendGeostyler
...filterObj, filters: filterObj?.filters?.filter(f => f.id !== INTERACTIVE_LEGEND_ID)
};
}
- let newFilter = filterObj ? filterObj : undefined;
+ let newFilter = !isFilterEmpty(filterObj) ? filterObj : undefined;
return newFilter;
}
let interactiveLegendFilters = isLegendFilterExist ? isLegendFilterExist.filters || [] : [];
@@ -1425,7 +1426,7 @@ export const updateLayerWFSVectorLegendFilter = (layerFilterObj, legendGeostyler
}
let newFilter = {
...(filterObj || {}), filters: [
- ...(filterObj?.filters?.filter(f => f.id !== INTERACTIVE_LEGEND_ID) || []), ...[
+ ...(filterObj?.filters?.filter(f => f.id !== INTERACTIVE_LEGEND_ID) || []), ...(interactiveLegendFilters?.length ? [
{
"id": INTERACTIVE_LEGEND_ID,
"format": "logic",
@@ -1433,31 +1434,32 @@ export const updateLayerWFSVectorLegendFilter = (layerFilterObj, legendGeostyler
"logic": "OR",
"filters": [...interactiveLegendFilters]
}
- ]
+ ] : [])
]
};
- return newFilter;
+ return !isFilterEmpty(newFilter) ? newFilter : undefined;
};
export function resetLayerLegendFilter(layer, reason, value) {
+ const experimentalInteractiveLegend = getMiscSetting('experimentalInteractiveLegend', false);
const isResetForStyle = reason === 'style'; // here the reason for reset is change 'style' or change the enable/disable interactive legend config 'disableEnableInteractiveLegend'
let needReset = false;
if (isResetForStyle) {
needReset = isResetForStyle && value !== layer.style;
}
// check if the layer has interactive legend or not, if not cancel the epic
- const isLayerWithJSONLegend = layer?.enableInteractiveLegend;
- let filterObj = layer.layerFilter ? layer.layerFilter : undefined;
- if (!needReset || !isLayerWithJSONLegend || !filterObj) return false;
+ const isLayerHasInterActiveLegend = experimentalInteractiveLegend && layer?.enableInteractiveLegend;
+ let filterObj = !isFilterEmpty(layer.layerFilter) ? layer.layerFilter : undefined;
+ if (!needReset || !isLayerHasInterActiveLegend || !filterObj) return false;
// reset thte filter if legendCQLFilter is empty
const isLegendFilterExist = filterObj?.filters?.find(f => f.id === INTERACTIVE_LEGEND_ID);
if (isLegendFilterExist) {
filterObj = {
...filterObj, filters: filterObj?.filters?.filter(f => f.id !== INTERACTIVE_LEGEND_ID)
};
- return filterObj;
+ return !isFilterEmpty(filterObj) ? filterObj : undefined;
}
- return filterObj;
+ return !isFilterEmpty(filterObj) ? filterObj : undefined;
}
FilterUtils = {
diff --git a/web/client/utils/__tests__/FilterUtils-test.js b/web/client/utils/__tests__/FilterUtils-test.js
index 019bb0d779..170bd87f9d 100644
--- a/web/client/utils/__tests__/FilterUtils-test.js
+++ b/web/client/utils/__tests__/FilterUtils-test.js
@@ -2436,9 +2436,8 @@ describe('FilterUtils', () => {
]
};
const updatedFilterObj = updateLayerLegendFilter(layerFilterObj);
- expect(updatedFilterObj).toBeTruthy();
- expect(updatedFilterObj.filters.length).toEqual(0);
- expect(updatedFilterObj.filters.find(i => i.id === INTERACTIVE_LEGEND_ID)).toBeFalsy();
+ // check if there is no any filters --> updatedFilterObj will be undefined
+ expect(updatedFilterObj).toBeFalsy();
});
it('test resetLayerLegendFilter in case change wms style', () => {
const layerFilterObj = {
@@ -2491,9 +2490,8 @@ describe('FilterUtils', () => {
style: "style_01"
};
const updatedFilterObj = resetLayerLegendFilter(layer, 'style', 'style_02');
- expect(updatedFilterObj).toBeTruthy();
- expect(updatedFilterObj.filters.length).toEqual(0);
- expect(updatedFilterObj.filters.find(i => i.id === INTERACTIVE_LEGEND_ID)).toBeFalsy();
+ // check if there is no any filters --> updatedFilterObj will be undefined
+ expect(updatedFilterObj).toBeFalsy();
});
// for WFS
it('test updateLayerWFSVectorLegendFilter for wfs, simple filter', () => {
@@ -2596,8 +2594,7 @@ describe('FilterUtils', () => {
]
};
const updatedFilterObj = updateLayerWFSVectorLegendFilter(layerFilterObj);
- expect(updatedFilterObj).toBeTruthy();
- expect(updatedFilterObj.filters.length).toEqual(0);
- expect(updatedFilterObj.filters.find(i => i.id === INTERACTIVE_LEGEND_ID)).toBeFalsy();
+ // check if there is no any filters --> updatedFilterObj will be undefined
+ expect(updatedFilterObj).toBeFalsy();
});
});