Skip to content

Commit

Permalink
geosolutions-it#9739: Fix - Incorrect connection of the parent table …
Browse files Browse the repository at this point in the history
…with the map and another widget
  • Loading branch information
dsuren1 committed Dec 7, 2023
1 parent 4493d8c commit ae10148
Show file tree
Hide file tree
Showing 11 changed files with 201 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ const MapCatalog = ({
: i)}
loading={loading}
onItemClick={({ map } = {}, props, event) => {
if (event.ctrlKey) {
if (event.ctrlKey || event.metaKey) {
return onSelected(isEmpty(selected)
? castArray(map)
: castArray(selected).concat(map));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,4 +262,143 @@ describe('dependenciesToWidget enhancer', () => {
const response = buildDependencies(null, deps, originalWidgetId);
expect(response).toBe(deps);
});
it('Return dependencies if there is a parent table', () => {
const dependencyMap = {
"viewport": "widgets[3d418c80-9030-11ee-b036-b16bb8d06c94].maps[3800a3a0-9030-11ee-b036-b16bb8d06c94].viewport",
"layers": "widgets[3d418c80-9030-11ee-b036-b16bb8d06c94].maps[3800a3a0-9030-11ee-b036-b16bb8d06c94].layers",
"filter": "widgets[3d418c80-9030-11ee-b036-b16bb8d06c94].maps[3800a3a0-9030-11ee-b036-b16bb8d06c94].filter",
"quickFilters": "widgets[3d418c80-9030-11ee-b036-b16bb8d06c94].maps[3800a3a0-9030-11ee-b036-b16bb8d06c94].quickFilters",
"layer": "widgets[3d418c80-9030-11ee-b036-b16bb8d06c94].maps[3800a3a0-9030-11ee-b036-b16bb8d06c94].layer",
"options": "widgets[3d418c80-9030-11ee-b036-b16bb8d06c94].maps[3800a3a0-9030-11ee-b036-b16bb8d06c94].options",
"mapSync": "widgets[3d418c80-9030-11ee-b036-b16bb8d06c94].mapSync",
"dependenciesMap": "widgets[3d418c80-9030-11ee-b036-b16bb8d06c94].dependenciesMap"
};
const deps = {
"widgets[3d418c80-9030-11ee-b036-b16bb8d06c94].dependenciesMap": {
"filter": "widgets[34792a90-9030-11ee-b036-b16bb8d06c94].filter",
"quickFilters": "widgets[34792a90-9030-11ee-b036-b16bb8d06c94].quickFilters",
"layer": "widgets[34792a90-9030-11ee-b036-b16bb8d06c94].layer",
"options": "widgets[34792a90-9030-11ee-b036-b16bb8d06c94].options",
"dependenciesMap": "widgets[34792a90-9030-11ee-b036-b16bb8d06c94].dependenciesMap",
"mapSync": "widgets[34792a90-9030-11ee-b036-b16bb8d06c94].mapSync"
},
"widgets[3d418c80-9030-11ee-b036-b16bb8d06c94].mapSync": true,
"widgets[3d418c80-9030-11ee-b036-b16bb8d06c94].maps[3800a3a0-9030-11ee-b036-b16bb8d06c94].viewport": {
"bounds": {
"minx": -10428653.241920743,
"miny": 2596212.3657196583,
"maxx": -8237050.76692817,
"maxy": 3907260.274867002
},
"crs": "EPSG:3857",
"rotation": 0
},
"widgets[3d418c80-9030-11ee-b036-b16bb8d06c94].maps[3800a3a0-9030-11ee-b036-b16bb8d06c94].center": {
"x": -83.83843600000002,
"y": 28.021909206829974,
"crs": "EPSG:4326"
},
"widgets[3d418c80-9030-11ee-b036-b16bb8d06c94].maps[3800a3a0-9030-11ee-b036-b16bb8d06c94].zoom": 5,
"widgets[3d418c80-9030-11ee-b036-b16bb8d06c94].maps[3800a3a0-9030-11ee-b036-b16bb8d06c94].layers": [
{
"type": "osm",
"title": "Open Street Map",
"name": "mapnik",
"source": "osm",
"group": "background",
"visibility": true,
"id": "mapnik__0"
},
{
"type": "wms",
"featureInfo": null,
"url": "https://gs-stable.geosolutionsgroup.com/geoserver/wms",
"visibility": true,
"dimensions": [

],
"name": "gs:us_states",
"title": "States of US",
"description": "",
"bbox": {
"crs": "EPSG:4326",
"bounds": {
"minx": -124.73142200000001,
"miny": 24.955967,
"maxx": -66.969849,
"maxy": 49.371735
}
},
"links": [],
"params": {},
"allowedSRS": {},
"imageFormats": [],
"infoFormats": [],
"search": {
"type": "wfs",
"url": "https://gs-stable.geosolutionsgroup.com/geoserver/wfs"
},
"id": "gs:us_states__3bb39980-9030-11ee-b036-b16bb8d06c94"
}
],
"layers": [],
"widgets[34792a90-9030-11ee-b036-b16bb8d06c94].quickFilters": {
"STATE_NAME": {
"rawValue": "florida",
"value": "florida",
"operator": "ilike",
"type": "string",
"attribute": "STATE_NAME"
}
},
"widgets[34792a90-9030-11ee-b036-b16bb8d06c94].mapSync": false,
"widgets[34792a90-9030-11ee-b036-b16bb8d06c94].layer": {
"type": "wms",
"featureInfo": null,
"url": "https://gs-stable.geosolutionsgroup.com/geoserver/wms",
"visibility": true,
"dimensions": [

],
"name": "gs:us_states",
"title": "States of US",
"description": "",
"bbox": {
"crs": "EPSG:4326",
"bounds": {
"minx": -124.73142200000001,
"miny": 24.955967,
"maxx": -66.969849,
"maxy": 49.371735
}
},
"links": [],
"params": {},
"allowedSRS": {},
"imageFormats": [],
"infoFormats": [],
"search": {
"type": "wfs",
"url": "https://gs-stable.geosolutionsgroup.com/geoserver/wfs"
}
},
"widgets[34792a90-9030-11ee-b036-b16bb8d06c94].options": {
"propertyName": [{"name": "STATE_NAME"}, {"name": "STATE_FIPS"}, {"name": "SUB_REGION"}, {"name": "STATE_ABBR"}, {"name": "LAND_KM"}, {"name": "WATER_KM"}, {"name": "PERSONS"}, {"name": "FAMILIES"}, {"name": "HOUSHOLD"}, {"name": "MALE"}, {"name": "FEMALE"}, {"name": "WORKERS"}, {"name": "DRVALONE"}, {"name": "CARPOOL"}, {"name": "PUBTRANS"}, {"name": "EMPLOYED"}, {"name": "UNEMPLOY"}, {"name": "SERVICE"}, {"name": "MANUAL"}, {"name": "P_MALE"}, {"name": "P_FEMALE"}, {"name": "SAMP_POP"}]
}
};
const response = buildDependencies(dependencyMap, deps, "e81849f0-9404-11ee-b8a8-2f8d898ee742");
expect(response).toEqual({
viewport: {bounds: {minx: -10428653.241920743, miny: 2596212.3657196583, maxx: -8237050.76692817, maxy: 3907260.274867002}, crs: 'EPSG:3857', rotation: 0},
layers: [
{type: 'osm', title: 'Open Street Map', name: 'mapnik', source: 'osm', group: 'background', visibility: true, id: 'mapnik__0'},
{type: 'wms', featureInfo: null, url: 'https://gs-stable.geosolutionsgroup.com/geoserver/wms', visibility: true, dimensions: [], name: 'gs:us_states', title: 'States of US', description: '', bbox: {crs: 'EPSG:4326', bounds: {minx: -124.73142200000001, miny: 24.955967, maxx: -66.969849, maxy: 49.371735}}, links: [], params: {}, allowedSRS: {}, imageFormats: [], infoFormats: [], search: {type: 'wfs', url: 'https://gs-stable.geosolutionsgroup.com/geoserver/wfs'}, id: 'gs:us_states__3bb39980-9030-11ee-b036-b16bb8d06c94'}
],
filter: undefined,
quickFilters: {STATE_NAME: {rawValue: 'florida', value: 'florida', operator: 'ilike', type: 'string', attribute: 'STATE_NAME'}},
layer: {type: 'wms', featureInfo: null, url: 'https://gs-stable.geosolutionsgroup.com/geoserver/wms', visibility: true, dimensions: [], name: 'gs:us_states', title: 'States of US', description: '', bbox: {crs: 'EPSG:4326', bounds: {minx: -124.73142200000001, miny: 24.955967, maxx: -66.969849, maxy: 49.371735}}, links: [], params: {}, allowedSRS: {}, imageFormats: [], infoFormats: [], search: {type: 'wfs', url: 'https://gs-stable.geosolutionsgroup.com/geoserver/wfs'}},
options: {propertyName: [{name: 'STATE_NAME'}, {name: 'STATE_FIPS'}, {name: 'SUB_REGION'}, {name: 'STATE_ABBR'}, {name: 'LAND_KM'}, {name: 'WATER_KM'}, {name: 'PERSONS'}, {name: 'FAMILIES'}, {name: 'HOUSHOLD'}, {name: 'MALE'}, {name: 'FEMALE'}, {name: 'WORKERS'}, {name: 'DRVALONE'}, {name: 'CARPOOL'}, {name: 'PUBTRANS'}, {name: 'EMPLOYED'}, {name: 'UNEMPLOY'}, {name: 'SERVICE'}, {name: 'MANUAL'}, {name: 'P_MALE'}, {name: 'P_FEMALE'}, {name: 'SAMP_POP'}]},
mapSync: true,
dependenciesMap: undefined
});
});
});
11 changes: 6 additions & 5 deletions web/client/components/widgets/enhancers/dependenciesToWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,17 +64,18 @@ import { pick } from 'lodash';
* // the enhancer will pass to the component of dependencies={counterDependencies}
*/

export const buildDependencies = (map, deps, originalWidgetId) => {
export const buildDependencies = (map, deps, originalWidgetId, updatedDependencyMap = []) => {
if (map) {
const dependenciesGenerated = Object.keys(map).reduce((ret, k) => {
if (k === "dependenciesMap" && deps[map[k]] && deps[map.mapSync] &&
deps[map[k]][k] && deps[map[k]][k].indexOf(originalWidgetId) === -1 // avoiding loop
&& !ret.mapSync
if (k === "dependenciesMap" && deps[map[k]] && deps[map.mapSync] && deps[map[k]][k]
&& originalWidgetId && deps[map[k]][k].indexOf(originalWidgetId) === -1
&& updatedDependencyMap.every(dep => deps[map[k]][k] !== dep)
) {
const _updatedDependencyMap = updatedDependencyMap.concat(deps[map[k]][k]);
// go recursively until we get the dependencies from table ancestors
return {
...ret,
...pick(buildDependencies(deps[map[k]], deps, originalWidgetId), ["options", "layer", "quickFilters", "filter", "dependenciesMap"])
...pick(buildDependencies(deps[map[k]], deps, originalWidgetId, _updatedDependencyMap), ["options", "layer", "quickFilters", "filter", "dependenciesMap"])
};
}
return {
Expand Down
6 changes: 3 additions & 3 deletions web/client/epics/widgets.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const updateDependencyMap = (active, targetId, { dependenciesMap, mappings}) =>
const id = (WIDGETS_REGEX.exec(targetId) || [])[1];
const cleanDependenciesMap = omitBy(dependenciesMap, i => i.indexOf(id) === -1);


const depToTheWidget = targetId.split(".maps")[0];
const overrides = Object.keys(mappings).filter(k => mappings[k] !== undefined).reduce( (ov, k) => {
if (!endsWith(targetId, "map") && includes(tableDependencies, k)) {
return {
Expand All @@ -66,12 +66,12 @@ const updateDependencyMap = (active, targetId, { dependenciesMap, mappings}) =>
}
return {
...ov,
[k]: `${targetId.replace(/.map$/, "")}.${mappings[k]}`
[k]: `${depToTheWidget}.${mappings[k]}`
};
}
return ov;
}, {});
const depToTheWidget = targetId.split(".maps")[0];

return active
? { ...cleanDependenciesMap, ...overrides, ["dependenciesMap"]: `${depToTheWidget}.dependenciesMap`, ["mapSync"]: `${depToTheWidget}.mapSync`}
: omit(cleanDependenciesMap, [Object.keys(mappings)]);
Expand Down
3 changes: 2 additions & 1 deletion web/client/plugins/Dashboard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {
import dashboardReducers from '../reducers/dashboard';
import dashboardEpics from '../epics/dashboard';
import widgetsEpics from '../epics/widgets';
import { isChartCompatibleWithTableWidget } from '../utils/WidgetsUtils';

const WidgetsView = compose(
connect(
Expand Down Expand Up @@ -112,7 +113,7 @@ const WidgetsView = compose(
target.widgetType === "table" &&
(editingWidget.widgetType !== "map" &&
editingWidget.widgetType === "chart"
? (target.layer && editingWidget && editingWidget?.charts?.map(c => c?.layer?.name)?.includes(target.layer.name))
? (target.layer && editingWidget && editingWidget?.charts?.map(c => c?.layer?.name)?.includes(target.layer.name) && isChartCompatibleWithTableWidget(editingWidget, target))
: (target.layer && editingWidget.layer && target.layer.name === editingWidget.layer.name)
|| editingWidget.widgetType === "map") && !target.mapSync
) && target.id !== editingWidget.id
Expand Down
4 changes: 2 additions & 2 deletions web/client/plugins/widgetbuilder/ChartBuilder.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import LayerSelector from './ChartLayerSelector';
import BuilderHeader from './BuilderHeader';
import Toolbar from '../../components/widgets/builder/wizard/chart/Toolbar';
import { catalogEditorEnhancer } from './enhancers/catalogEditorEnhancer';
import { getDependantWidget } from "../../utils/WidgetsUtils";
import { getDependantWidget, isChartCompatibleWithTableWidget } from "../../utils/WidgetsUtils";


const setMultiDependencySupport = ({editorData = {}, disableMultiDependencySupport: disableSupport, widgets = []} = {}) => {
Expand All @@ -40,7 +40,7 @@ const setMultiDependencySupport = ({editorData = {}, disableMultiDependencySuppo
if (dependantWidget?.widgetType === 'table') {
// Disable dependency support when some layers in multi chart
// doesn't match dependant table widget
disableMultiDependencySupport = disableMultiDependencySupport || editorData?.charts?.some(c => c.layer.name !== dependantWidget?.layer?.name);
disableMultiDependencySupport = disableMultiDependencySupport || !isChartCompatibleWithTableWidget(editorData, dependantWidget);
}
return { disableMultiDependencySupport };
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ const layerSelector = compose(
: i
),
onItemClick: ({record} = {}, props, event) => {
if (event.ctrlKey) {
if (event.ctrlKey || event.metaKey) {
const selectedArray = castArray(selected);
if (isEmpty(selected)) {
return setSelected(castArray(record));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* LICENSE file in the root directory of this source tree.
*/
import { withProps, compose } from 'recompose';
import isNil from 'lodash/isNil';

/**
* Returns an enhancer that add `stepButtons` for viewport connection to a wizard toolbar
Expand All @@ -20,21 +21,26 @@ export default (showCondition = () => true) => compose(
canConnect,
connected,
...props
}) => ({
stepButtons: [...stepButtons, {
onClick: () => toggleConnection(availableDependencies, props.widgets),
disabled: disableMultiDependencySupport,
visible: !!showCondition(props) && availableDependencies.length > 0,
bsStyle: (!disableMultiDependencySupport && connected) ? "success" : "primary",
glyph: connected ? "plug" : "unplug",
tooltipId: (disableMultiDependencySupport || !canConnect)
? "widgets.builder.wizard.disableConnectToMap"
: connected
? "widgets.builder.wizard.clearConnection"
: availableDependencies.length === 1
? "widgets.builder.wizard.connectToTheMap"
: "widgets.builder.wizard.connectToAMap"
}
]
}))
}) => {
const disableConnect = !isNil(disableMultiDependencySupport) ? disableMultiDependencySupport : !canConnect;
return {
stepButtons: [
...stepButtons,
{
onClick: () => toggleConnection(availableDependencies, props.widgets),
disabled: disableConnect,
visible: !!showCondition(props) && availableDependencies.length > 0,
bsStyle: (!disableMultiDependencySupport && connected) ? "success" : "primary",
glyph: connected ? "plug" : "unplug",
tooltipId: disableConnect
? "widgets.builder.wizard.disableConnectToMap"
: connected
? "widgets.builder.wizard.clearConnection"
: availableDependencies.length === 1
? "widgets.builder.wizard.connectToTheMap"
: "widgets.builder.wizard.connectToAMap"
}
]
};
})
);
4 changes: 2 additions & 2 deletions web/client/selectors/widgets.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { mapSelector } from './map';
import { getSelectedLayer } from './layers';
import { pathnameSelector } from './router';
import { DEFAULT_TARGET, DEPENDENCY_SELECTOR_KEY, WIDGETS_REGEX } from '../actions/widgets';
import { getWidgetsGroups, getWidgetDependency, getSelectedWidgetData } from '../utils/WidgetsUtils';
import { getWidgetsGroups, getWidgetDependency, getSelectedWidgetData, isChartCompatibleWithTableWidget } from '../utils/WidgetsUtils';
import { dashboardServicesSelector, isDashboardAvailable, isDashboardEditing } from './dashboard';
import { createSelector, createStructuredSelector } from 'reselect';
import { createShallowSelector } from '../utils/ReselectUtils';
Expand Down Expand Up @@ -109,7 +109,7 @@ export const availableDependenciesForEditingWidgetSelector = createSelector(
.concat(
castArray(tableWidgets)
.filter(() => pathname.indexOf("viewer") === -1)
.filter((w) => (!isChart && isArray(editingLayer)) || (isChart ? editingLayer.includes(w.layer.name) : editingLayer.name === w.layer.name))
.filter((w) => (!isChart && isArray(editingLayer)) || (isChart ? isChartCompatibleWithTableWidget(editingWidget, w) : editingLayer.name === w.layer.name))
.filter((w) => editingWidget && editingWidget.id !== w.id)
.map(({id}) => `widgets[${id}]`)
)
Expand Down
12 changes: 12 additions & 0 deletions web/client/utils/WidgetsUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -331,3 +331,15 @@ export const getSelectedWidgetData = (widget = {}) => {
}
return widget;
};

/**
* Check if chart widget layers are compatible with table widget layer
* @param {object} widget current widget object
* @param {object} tableWidget depedant table widget object
* @returns {boolean} flag determines if compatible
*/
export const isChartCompatibleWithTableWidget = (widget, tableWidget) => {
const tableLayerName = tableWidget?.layer?.name;
return tableLayerName && get(widget, 'charts', [])
.every(({ layer = {} } = {}) => layer.name === tableLayerName);
};
Loading

0 comments on commit ae10148

Please sign in to comment.