diff --git a/web/client/components/data/featuregrid/toolbars/Toolbar.jsx b/web/client/components/data/featuregrid/toolbars/Toolbar.jsx index 37fe00ab3b9..16f0e3521e7 100644 --- a/web/client/components/data/featuregrid/toolbars/Toolbar.jsx +++ b/web/client/components/data/featuregrid/toolbars/Toolbar.jsx @@ -48,12 +48,12 @@ const standardButtons = { visible={mode === "VIEW" && showAdvancedFilterButton} onClick={events.showQueryPanel} glyph="filter"/>), - zoomAll: ({disabled, disableZoomAll = false, mode, events = {}}) => ( (), backToViewMode: ({disabled, mode, hasChanges, hasNewFeatures, events = {}}) => (), - drawFeature: ({isDrawing = false, disabled, isSimpleGeom, mode, selectedCount, hasGeometry, hasSupportedGeometry = true, events = {}}) => ( (), @@ -111,12 +111,12 @@ const standardButtons = { visible={mode === "EDIT" && hasChanges || hasNewFeatures} onClick={events.clearFeatureEditing} glyph="remove-square"/>), - deleteGeometry: ({disabled, mode, hasGeometry, selectedCount, hasSupportedGeometry = true, events = {}}) => ( (), gridSettings: ({disabled, isColumnsOpen, selectedCount, mode, events = {}}) => (), - syncGridFilterToMap: ({disabled, isSyncActive = false, showSyncOnMapButton = true, events = {}, syncPopover = {}, showPopoverSync, hideSyncPopover}) => ( ( events.setTimeSync && events.setTimeSync(!timeSync)} glyph="time" />), - snapToFeature: ({snapping, availableSnappingLayers = [], isSnappingLoading, snappingConfig, mode, mapType, editorHeight, pluginCfg, events = {}}) => ( ( { events.toggleSnapping && events.toggleSnapping(!snapping); }} @@ -259,11 +259,11 @@ const standardButtons = { ), - viewportFilter: ({viewportFilter, isFilterByViewportSupported, pluginCfg, events = {}}) => ( ( { events.setViewportFilter && events.setViewportFilter(!viewportFilter); }} diff --git a/web/client/components/data/featuregrid/toolbars/__tests__/Toolbar-test.jsx b/web/client/components/data/featuregrid/toolbars/__tests__/Toolbar-test.jsx index b89bb5cb720..f15d6d736da 100644 --- a/web/client/components/data/featuregrid/toolbars/__tests__/Toolbar-test.jsx +++ b/web/client/components/data/featuregrid/toolbars/__tests__/Toolbar-test.jsx @@ -456,4 +456,70 @@ describe('Featuregrid toolbar component', () => { expect(el).toExist(); }); }); + describe('hideSpatialFunctionalityTools', () => { + it('hides all spatial tools in VIEW mode when hideSpatialFunctionalityTools is true', () => { + ReactDOM.render(, document.getElementById("container")); + const el = document.getElementsByClassName("featuregrid-toolbar")[0]; + expect(el).toExist(); + // Check zoomAll button is hidden + expect(isVisibleButton(document.getElementById("fg-zoom-all"))).toBe(false); + // Check syncGridFilterToMap button is hidden + expect(isVisibleButton(document.getElementById("fg-grid-map-filter"))).toBe(false); + // Check viewportFilter button is hidden + expect(isVisibleButton(document.getElementById("fg-viewportFilter-button"))).toBe(false); + }); + it('hides all spatial tools in EDIT mode when hideSpatialFunctionalityTools is true', () => { + ReactDOM.render(, document.getElementById("container")); + const el = document.getElementsByClassName("featuregrid-toolbar")[0]; + expect(el).toExist(); + // Check drawFeature button is hidden + expect(isVisibleButton(document.getElementById("fg-draw-feature"))).toBe(false); + // Check deleteGeometry button is hidden + expect(isVisibleButton(document.getElementById("fg-delete-geometry"))).toBe(false); + // Check snapToFeature button is hidden + expect(document.getElementById("snap-button")).toNotExist(); + // Check syncGridFilterToMap button is hidden + expect(isVisibleButton(document.getElementById("fg-grid-map-filter"))).toBe(false); + // Check viewportFilter button is hidden + expect(isVisibleButton(document.getElementById("fg-viewportFilter-button"))).toBe(false); + }); + it('shows non-spatial tools even when hideSpatialFunctionalityTools is true', () => { + const events = { + switchEditMode: () => {}, + showQueryPanel: () => {}, + settings: () => {} + }; + ReactDOM.render(, document.getElementById("container")); + const el = document.getElementsByClassName("featuregrid-toolbar")[0]; + expect(el).toExist(); + // Check editMode button is still visible + expect(isVisibleButton(document.getElementById("fg-edit-mode"))).toBe(true); + // Check filter button is still visible + expect(isVisibleButton(document.getElementById("fg-search"))).toBe(true); + // Check gridSettings button is still visible + expect(isVisibleButton(document.getElementById("fg-grid-settings"))).toBe(true); + // Verify spatial tools are hidden + expect(isVisibleButton(document.getElementById("fg-zoom-all"))).toBe(false); + expect(isVisibleButton(document.getElementById("fg-grid-map-filter"))).toBe(false); + }); + }); }); diff --git a/web/client/plugins/featuregrid/FeatureEditor.jsx b/web/client/plugins/featuregrid/FeatureEditor.jsx index e9df25d81ac..e87735b19b6 100644 --- a/web/client/plugins/featuregrid/FeatureEditor.jsx +++ b/web/client/plugins/featuregrid/FeatureEditor.jsx @@ -18,16 +18,20 @@ import BorderLayout from '../../components/layout/BorderLayout'; import { toChangesMap} from '../../utils/FeatureGridUtils'; import { setUp, setSyncTool } from '../../actions/featuregrid'; import {paginationInfo, describeSelector, attributesJSONSchemaSelector, wfsURLSelector, typeNameSelector, isSyncWmsActive} from '../../selectors/query'; -import {modeSelector, changesSelector, newFeaturesSelector, hasChangesSelector, selectedLayerFieldsSelector, selectedFeaturesSelector} from '../../selectors/featuregrid'; +import {modeSelector, changesSelector, newFeaturesSelector, hasChangesSelector, selectedLayerFieldsSelector, selectedFeaturesSelector, hasNoGeometry as hasNoGeometrySelector} from '../../selectors/featuregrid'; import {getPanels, getHeader, getFooter, getDialogs, getEmptyRowsView, getFilterRenderers} from './panels/index'; -import {gridTools, gridEvents, pageEvents, toolbarEvents} from './index'; +import {gridTools as defaultGridTools, gridEvents, pageEvents, toolbarEvents} from './index'; import useFeatureValidation from './hooks/useFeatureValidation'; import withResize from './hoc/withResize'; const EMPTY_ARR = []; const EMPTY_OBJ = {}; +const filterGeometryToolColumn = (tools = EMPTY_ARR, hide = false) => { + return hide ? tools.filter((t) => t?.key !== 'geometry') : tools; +}; + /** * @name FeatureEditor * @memberof plugins @@ -174,6 +178,12 @@ const Editor = (props = { return getFilterRenderers(props.describe, props.fields, props.isWithinAttrTbl); }, [props.describe, props.fields]); + // If the dataset has no geometry, hide the geometry tool column + const hideGeometryColumn = props?.hasNoGeometry; + const gridTools = useMemo(() => + filterGeometryToolColumn(props.gridTools, hideGeometryColumn), + [props.gridTools, hideGeometryColumn]); + // changes compute using useMemo to reduce the re-render of the component const changes = useMemo(() => toChangesMap(props.changes), [props.changes]); @@ -228,7 +238,7 @@ const Editor = (props = { describeFeatureType={props.describe} features={props.features} minHeight={600} - tools={props.gridTools} + tools={gridTools} pagination={props.pagination} pages={props.pages} virtualScroll={virtualScroll} @@ -273,7 +283,8 @@ export const selector = createStructuredSelector({ enableColumnFilters: state => get(state, 'featuregrid.enableColumnFilters'), pagination: createStructuredSelector(paginationInfo), pages: state => get(state, 'featuregrid.pages'), - size: state => get(state, 'featuregrid.pagination.size') + size: state => get(state, 'featuregrid.pagination.size'), + hasNoGeometry: hasNoGeometrySelector }); const EditorPlugin = compose( @@ -315,7 +326,7 @@ const EditorPlugin = compose( gridEvents: bindActionCreators(gridEvents, dispatch), pageEvents: bindActionCreators(pageEvents, dispatch), toolbarEvents: bindActionCreators(toolbarEvents, dispatch), - gridTools: gridTools.map((t) => ({ + gridTools: defaultGridTools.map((t) => ({ ...t, events: bindActionCreators(t.events, dispatch) })) diff --git a/web/client/plugins/featuregrid/__tests__/FeatureEditor-test.jsx b/web/client/plugins/featuregrid/__tests__/FeatureEditor-test.jsx index 8edea45a05f..a3287bb0b41 100644 --- a/web/client/plugins/featuregrid/__tests__/FeatureEditor-test.jsx +++ b/web/client/plugins/featuregrid/__tests__/FeatureEditor-test.jsx @@ -50,7 +50,8 @@ describe('FeatureEditor plugin component', () => { enableColumnFilters: true, pagination: { startIndex: undefined, maxFeatures: undefined, resultSize: undefined, totalFeatures: undefined }, pages: undefined, - size: 20 + size: 20, + hasNoGeometry: true }; it('base state', () => { expect(BASE_EXPECTED).toEqual(BASE_EXPECTED); diff --git a/web/client/plugins/featuregrid/panels/index.jsx b/web/client/plugins/featuregrid/panels/index.jsx index f0414fba343..d7e16810d45 100644 --- a/web/client/plugins/featuregrid/panels/index.jsx +++ b/web/client/plugins/featuregrid/panels/index.jsx @@ -46,7 +46,8 @@ import { timeSyncActive, isViewportFilterActive, isFilterByViewportSupported, - selectedLayerSelector + selectedLayerSelector, + hasNoGeometry } from '../../../selectors/featuregrid'; import {isCesium, mapTypeSelector} from '../../../selectors/maptype'; import { @@ -103,7 +104,8 @@ const Toolbar = connect( mapType: mapTypeSelector, viewportFilter: isViewportFilterActive, isFilterByViewportSupported, - layer: selectedLayerSelector + layer: selectedLayerSelector, + hideSpatialFunctionalityTools: hasNoGeometry }), (dispatch) => ({events: bindActionCreators(toolbarEvents, dispatch)}) )(ToolbarComp); diff --git a/web/client/selectors/__tests__/featuregrid-test.js b/web/client/selectors/__tests__/featuregrid-test.js index 6b69e8864fc..b2c534943df 100644 --- a/web/client/selectors/__tests__/featuregrid-test.js +++ b/web/client/selectors/__tests__/featuregrid-test.js @@ -39,7 +39,8 @@ import { isFilterByViewportSupported, selectedLayerFieldsSelector, editingAllowedGroupsSelector, - isEditingAllowedSelector + isEditingAllowedSelector, + hasNoGeometry } from '../featuregrid'; const idFt1 = "idFt1"; @@ -788,4 +789,34 @@ describe('Test featuregrid selectors', () => { })).toBeTruthy(); }); }); + describe('hasNoGeometry', () => { + it('returns false when describe metadata has geometry', () => { + expect(hasNoGeometry(initialState)).toBe(false); + }); + it('returns false when features contain geometry and no describe metadata', () => { + const state = { + featuregrid: { + features: [{ + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [0, 0] + } + }] + } + }; + expect(hasNoGeometry(state)).toBe(false); + }); + it('returns true when neither describe metadata nor features expose geometry', () => { + const state = { + featuregrid: { + features: [{ + type: 'Feature', + geometry: null + }] + } + }; + expect(hasNoGeometry(state)).toBe(true); + }); + }); }); diff --git a/web/client/selectors/featuregrid.js b/web/client/selectors/featuregrid.js index 67d9e5dc95c..106549cf83d 100644 --- a/web/client/selectors/featuregrid.js +++ b/web/client/selectors/featuregrid.js @@ -234,3 +234,16 @@ export const viewportFilter = createShallowSelectorCreator(isEqual)( } : {}; } ); + +/** + * Returns true when neither the DescribeFeatureType metadata nor the + * loaded features expose a geometry field. + */ +export const hasNoGeometry = (state) => { + const describe = describeSelector(state); + if (describe && findGeometryProperty(describe)) { + return false; + } + const features = get(state, "featuregrid.features", []); + return !(features || []).some(({ geometry }) => geometry); +};