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 other widget (geosolutions-it#9804)
  • Loading branch information
dsuren1 authored Dec 18, 2023
1 parent 488cec0 commit 6810da8
Show file tree
Hide file tree
Showing 12 changed files with 205 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ const renderPreview = ({ data = {}, layer, dependencies = {}, setValid = () => {
setValid(false);
setErrors({...errors, [layer.name]: true});
}}
id={data.id}
isAnimationActive={false}
dependencies={dependencies}
dependenciesMap={data.dependenciesMap}
Expand Down
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
12 changes: 6 additions & 6 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 Expand Up @@ -106,11 +106,11 @@ const getValidLocationChange = action$ =>
* @param {string} dependency the dependency element id to add
* @param {object} options dependency mapping options. Must contain `mappings` object
*/
const configureDependency = (active, dependency, options, targetDependenciesMap) =>
const configureDependency = (active, dependency, options) =>
Rx.Observable.of(
onEditorChange("mapSync", active),
onEditorChange('dependenciesMap',
updateDependencyMap(active, dependency, options, targetDependenciesMap)
updateDependencyMap(active, dependency, options)
)
);

Expand Down Expand Up @@ -184,7 +184,7 @@ export const toggleWidgetConnectFlow = (action$, {getState = () => {}} = {}) =>
if (widget.widgetType === 'map') {
deps = deps.filter(d => (WIDGETS_MAPS_REGEX.exec(d) || [])[2] === widget.selectedMapId);
}
return configureDependency(active, deps[0], options, widget.dependeciesMap).concat(Rx.Observable.of(toggleDependencySelector(false, {})));
return configureDependency(active, deps[0], options).concat(Rx.Observable.of(toggleDependencySelector(false, {})));
}).takeUntil(
action$.ofType(LOCATION_CHANGE)
.merge(action$.filter(({ type, key } = {}) => type === EDITOR_SETTING_CHANGE && key === DEPENDENCY_SELECTOR_KEY))
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
Loading

0 comments on commit 6810da8

Please sign in to comment.