Skip to content

Commit e84e47f

Browse files
authored
fix: layerFilter structure (#11914)
1 parent 1dee3e8 commit e84e47f

3 files changed

Lines changed: 84 additions & 55 deletions

File tree

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

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { testEpic } from './epicTestUtils';
1111
import { applyFilterWidgetInteractionsEpic, cleanupAndReapplyFilterWidgetInteractionsEpic } from '../interactions';
1212
import { applyFilterWidgetInteractions } from '../../actions/interactions';
1313
import { UPDATE_PROPERTY, DELETE } from '../../actions/widgets';
14+
import { CHANGE_LAYER_PROPERTIES } from '../../actions/layers';
1415

1516
const FILTER_ID = 'filter-1';
1617
const FILTER_WIDGET_ID = 'filter-widget-1';
@@ -67,15 +68,21 @@ const makeTableWidget = (overrides = {}) => ({
6768
...overrides
6869
});
6970

70-
const makeState = (widgets) => ({
71-
widgets: {
72-
containers: {
73-
floating: {
74-
widgets
71+
const makeState = (widgets, layersState = null) => {
72+
const state = {
73+
widgets: {
74+
containers: {
75+
floating: {
76+
widgets
77+
}
7578
}
7679
}
80+
};
81+
if (layersState) {
82+
state.layers = layersState;
7783
}
78-
});
84+
return state;
85+
};
7986

8087
describe('interactions epics', () => {
8188
describe('applyFilterWidgetInteractionsEpic', () => {
@@ -109,6 +116,43 @@ describe('interactions epics', () => {
109116
);
110117
});
111118

119+
it('dispatches changeLayerProperties with full default layerFilter when layer has null layerFilter (map layer target)', (done) => {
120+
const LAYER_ID = 'layer-1';
121+
const filterWidget = makeFilterWidget({
122+
interactions: [{
123+
id: 'int-map-layer',
124+
plugged: true,
125+
targetType: 'applyFilter',
126+
source: { nodePath: `widgets[${FILTER_WIDGET_ID}].filters[${FILTER_ID}]` },
127+
target: { nodePath: `map.layers[${LAYER_ID}]` }
128+
}]
129+
});
130+
const layersState = { flat: [{ id: LAYER_ID, name: 'test:layer' }] };
131+
const state = makeState([filterWidget], layersState);
132+
133+
testEpic(
134+
applyFilterWidgetInteractionsEpic,
135+
1,
136+
[applyFilterWidgetInteractions(FILTER_WIDGET_ID, 'floating', FILTER_ID)],
137+
(actions) => {
138+
expect(actions.length).toBe(1);
139+
expect(actions[0].type).toBe(CHANGE_LAYER_PROPERTIES);
140+
expect(actions[0].layer).toBe(LAYER_ID);
141+
const layerFilter = actions[0].newProperties?.layerFilter;
142+
expect(layerFilter).toBeTruthy();
143+
expect(Array.isArray(layerFilter.groupFields)).toBe(true);
144+
expect(layerFilter.groupFields.length).toBeGreaterThan(0);
145+
expect(Array.isArray(layerFilter.filterFields)).toBe(true);
146+
expect(layerFilter.attributePanelExpanded).toBe(true);
147+
expect(layerFilter.spatialField).toBeTruthy();
148+
expect(Array.isArray(layerFilter.filters)).toBe(true);
149+
expect(layerFilter.filters.length).toBeGreaterThan(0);
150+
},
151+
state,
152+
done
153+
);
154+
});
155+
112156
it('dispatches updateWidgetProperty with interactionFilters for table target', (done) => {
113157
const filterWidget = makeFilterWidget({
114158
interactions: [{

web/client/epics/interactions.js

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { extractTraceFromWidgetByNodePath, TARGET_TYPES } from '../utils/Interac
1313
import { updateWidgetProperty, INSERT, UPDATE, DELETE } from '../actions/widgets';
1414
import { getLayerFromId, layersSelector } from '../selectors/layers';
1515
import { changeLayerProperties } from '../actions/layers';
16+
import { defaultLayerFilter } from '../utils/FilterUtils';
1617
import { processFilterToCQL, buildDefaultCQLFilter } from '../utils/FilterEventUtils';
1718
import { APPLY_FILTER_WIDGET_INTERACTIONS, applyFilterWidgetInteractions } from '../actions/interactions';
1819

@@ -73,16 +74,21 @@ function extractLayerIdFromNodePath(nodePath) {
7374
// ============================================================================
7475

7576
/**
76-
* Ensures layerFilter structure exists on a layer object
77+
* Ensures layerFilter structure exists on a layer object.
78+
* When layerFilter is null/undefined, uses full default (groupFields, filterFields, etc.).
79+
* When it exists but is partial, merges with default and normalizes .filters.
7780
* @param {object} layer - The layer object to ensure structure on
7881
* @returns {object} The layer with ensured layerFilter structure
7982
*/
8083
function ensureLayerFilterStructure(layer) {
8184
if (!layer.layerFilter) {
82-
layer.layerFilter = {};
83-
}
84-
if (!layer.layerFilter.filters) {
85-
layer.layerFilter.filters = [];
85+
layer.layerFilter = { ...defaultLayerFilter, filters: [] };
86+
} else {
87+
layer.layerFilter = {
88+
...defaultLayerFilter,
89+
...layer.layerFilter,
90+
filters: Array.isArray(layer.layerFilter.filters) ? layer.layerFilter.filters : []
91+
};
8692
}
8793
return layer;
8894
}

web/client/utils/FilterUtils.js

Lines changed: 23 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1334,29 +1334,30 @@ export const mergeFiltersToOGC = (opts = {}, ...filters) => {
13341334
return filterString;
13351335
};
13361336

1337-
export const updateLayerLegendFilter = (layerFilterObj, legendFilter) => {
1338-
const defaultLayerFilter = {
1339-
groupFields: [
1340-
{
1341-
id: 1,
1342-
logic: 'OR',
1343-
index: 0
1344-
}
1345-
],
1346-
filterFields: [],
1347-
attributePanelExpanded: true,
1348-
spatialPanelExpanded: true,
1349-
crossLayerExpanded: true,
1350-
crossLayerFilter: {
1351-
attribute: 'the_geom'
1352-
},
1353-
spatialField: {
1354-
method: null,
1355-
operation: 'INTERSECTS',
1356-
geometry: null,
1357-
attribute: 'the_geom'
1337+
export const defaultLayerFilter = {
1338+
groupFields: [
1339+
{
1340+
id: 1,
1341+
logic: 'OR',
1342+
index: 0
13581343
}
1359-
};
1344+
],
1345+
filterFields: [],
1346+
attributePanelExpanded: true,
1347+
spatialPanelExpanded: true,
1348+
crossLayerExpanded: true,
1349+
crossLayerFilter: {
1350+
attribute: 'the_geom'
1351+
},
1352+
spatialField: {
1353+
method: null,
1354+
operation: 'INTERSECTS',
1355+
geometry: null,
1356+
attribute: 'the_geom'
1357+
}
1358+
};
1359+
1360+
export const updateLayerLegendFilter = (layerFilterObj, legendFilter) => {
13601361
let filterObj = {...defaultLayerFilter, ...layerFilterObj};
13611362
const isLegendFilterExist = filterObj?.filters?.find(f => f.id === INTERACTIVE_LEGEND_ID);
13621363
if (!legendFilter) {
@@ -1410,28 +1411,6 @@ export const updateLayerLegendFilter = (layerFilterObj, legendFilter) => {
14101411
* @return {object} layerFilterObj updated the layer filter object
14111412
*/
14121413
export const updateLayerWFSVectorLegendFilter = (layerFilterObj, legendGeostylerFilter) => {
1413-
const defaultLayerFilter = {
1414-
groupFields: [
1415-
{
1416-
id: 1,
1417-
logic: 'OR',
1418-
index: 0
1419-
}
1420-
],
1421-
filterFields: [],
1422-
attributePanelExpanded: true,
1423-
spatialPanelExpanded: true,
1424-
crossLayerExpanded: true,
1425-
crossLayerFilter: {
1426-
attribute: 'the_geom'
1427-
},
1428-
spatialField: {
1429-
method: null,
1430-
operation: 'INTERSECTS',
1431-
geometry: null,
1432-
attribute: 'the_geom'
1433-
}
1434-
};
14351414
let filterObj = {...defaultLayerFilter, ...layerFilterObj};
14361415
const isLegendFilterExist = filterObj?.filters?.find(f => f.id === INTERACTIVE_LEGEND_ID);
14371416
if (!legendGeostylerFilter) {

0 commit comments

Comments
 (0)