From 622357799d05c02f4033d5aa3af67210687dc972 Mon Sep 17 00:00:00 2001 From: Lorenzo Natali Date: Fri, 4 Aug 2017 15:14:53 +0200 Subject: [PATCH 1/8] Direct access to the grid from the toc --- tests.webpack.js | 2 +- web/client/actions/__tests__/wfsquery-test.js | 8 +- web/client/actions/featuregrid.js | 20 +++- web/client/actions/layers.js | 11 ++- web/client/actions/wfsquery.js | 11 +-- web/client/components/TOC/DefaultLayer.jsx | 14 ++- web/client/epics/featuregrid.js | 99 ++++++++++++++----- web/client/epics/wfsquery.js | 2 - .../examples/featuregrid/stores/store.js | 4 +- web/client/plugins/FeatureEditor.jsx | 2 +- web/client/plugins/TOC.jsx | 11 +-- .../plugins/featuregrid/panels/index.jsx | 5 +- .../plugins/featuregrid/toolbarEvents.js | 4 +- .../reducers/__tests__/featuregrid-test.js | 18 +++- web/client/reducers/featuregrid.js | 27 +++-- web/client/reducers/query.js | 10 +- web/client/selectors/query.js | 1 + 17 files changed, 159 insertions(+), 90 deletions(-) diff --git a/tests.webpack.js b/tests.webpack.js index 1fd499071c..bb80054b3f 100644 --- a/tests.webpack.js +++ b/tests.webpack.js @@ -1,3 +1,3 @@ -var context = require.context('./web', true, /(-test\.jsx?)|(-test-chrome\.jsx?)$/); +var context = require.context('./web/client/reducers', true, /(-test\.jsx?)|(-test-chrome\.jsx?)$/); context.keys().forEach(context); module.exports = context; diff --git a/web/client/actions/__tests__/wfsquery-test.js b/web/client/actions/__tests__/wfsquery-test.js index d6a6d474bd..b19610d1d0 100644 --- a/web/client/actions/__tests__/wfsquery-test.js +++ b/web/client/actions/__tests__/wfsquery-test.js @@ -19,7 +19,6 @@ var { QUERY_CREATE, QUERY, RESET_QUERY, - FEATURE_CLOSE, layerSelectedForSearch, featureTypeSelected, featureTypeError, @@ -30,8 +29,7 @@ var { queryError, createQuery, query, - resetQuery, - featureClose + resetQuery } = require('../wfsquery'); describe('wfsquery actions', () => { @@ -97,8 +95,4 @@ describe('wfsquery actions', () => { let {type} = resetQuery(); expect(type).toBe(RESET_QUERY); }); - it('featureClose', () => { - let {type} = featureClose(); - expect(type).toBe(FEATURE_CLOSE); - }); }); diff --git a/web/client/actions/featuregrid.js b/web/client/actions/featuregrid.js index fc55d0fd13..aaa17acb47 100644 --- a/web/client/actions/featuregrid.js +++ b/web/client/actions/featuregrid.js @@ -33,7 +33,9 @@ const GEOMETRY_CHANGED = 'FEATUREGRID:GEOMETRY_CHANGED'; const DOCK_SIZE_FEATURES = 'DOCK_SIZE_FEATURES'; const TOGGLE_TOOL = 'FEATUREGRID:TOGGLE_TOOL'; const CUSTOMIZE_ATTRIBUTE = 'FEATUREGRID:CUSTOMIZE_ATTRIBUTE'; -const CLOSE_GRID = 'FEATUREGRID:CLOSE_GRID'; +const CLOSE_FEATURE_GRID_CONFIRM = 'ASK_CLOSE_FEATURE_GRID_CONFIRM'; +const OPEN_FEATURE_GRID = 'FEATUREGRID:OPEN_GRID'; +const CLOSE_FEATURE_GRID = 'FEATUREGRID:CLOSE_GRID'; const CLEAR_CHANGES_CONFIRMED = 'FEATUREGRID:CLEAR_CHANGES_CONFIRMED'; const FEATURE_GRID_CLOSE_CONFIRMED = 'FEATUREGRID:FEATURE_GRID_CLOSE_CONFIRMED'; const SET_PERMISSION = 'FEATUREGRID:SET_PERMISSION'; @@ -219,9 +221,19 @@ function saveError() { type: SAVE_ERROR }; } +function closeFeatureGridConfirm() { + return { + type: CLOSE_FEATURE_GRID_CONFIRM + }; +} function closeFeatureGrid() { return { - type: CLOSE_GRID + type: CLOSE_FEATURE_GRID + }; +} +function openFeatureGrid() { + return { + type: OPEN_FEATURE_GRID }; } function setPermission(permission) { @@ -262,7 +274,9 @@ module.exports = { DELETE_GEOMETRY, deleteGeometry, DELETE_GEOMETRY_FEATURE, deleteGeometryFeature, CLEAR_CHANGES_CONFIRMED, clearChangeConfirmed, - CLOSE_GRID, closeFeatureGrid, + CLOSE_FEATURE_GRID, closeFeatureGrid, + OPEN_FEATURE_GRID, openFeatureGrid, + CLOSE_FEATURE_GRID_CONFIRM, closeFeatureGridConfirm, FEATURE_GRID_CLOSE_CONFIRMED, closeFeatureGridConfirmed, setLayer, selectFeatures, diff --git a/web/client/actions/layers.js b/web/client/actions/layers.js index 4ae0199c06..9933d0118d 100644 --- a/web/client/actions/layers.js +++ b/web/client/actions/layers.js @@ -24,6 +24,7 @@ const UPDATE_SETTINGS = 'UPDATE_SETTINGS'; const REFRESH_LAYERS = 'REFRESH_LAYERS'; const LAYERS_REFRESHED = 'LAYERS_REFRESHED'; const LAYERS_REFRESH_ERROR = 'LAYERS_REFRESH_ERROR'; +const BROWSE_DATA = 'LAYERS:BROWSE_DATA'; function showSettings(node, nodeType, options) { return { @@ -175,11 +176,17 @@ function layersRefreshError(layers, error) { error }; } +function browseData(layer) { + return { + type: BROWSE_DATA, + layer + }; +} module.exports = {changeLayerProperties, changeGroupProperties, toggleNode, sortNode, removeNode, contextNode, updateNode, layerLoading, layerLoad, layerError, addLayer, removeLayer, showSettings, hideSettings, updateSettings, refreshLayers, - layersRefreshed, layersRefreshError, refreshLayerVersion, + layersRefreshed, layersRefreshError, refreshLayerVersion, browseData, CHANGE_LAYER_PROPERTIES, CHANGE_GROUP_PROPERTIES, TOGGLE_NODE, SORT_NODE, REMOVE_NODE, UPDATE_NODE, LAYER_LOADING, LAYER_LOAD, LAYER_ERROR, ADD_LAYER, REMOVE_LAYER, - SHOW_SETTINGS, HIDE_SETTINGS, UPDATE_SETTINGS, CONTEXT_NODE, REFRESH_LAYERS, LAYERS_REFRESHED, LAYERS_REFRESH_ERROR + SHOW_SETTINGS, HIDE_SETTINGS, UPDATE_SETTINGS, CONTEXT_NODE, REFRESH_LAYERS, LAYERS_REFRESHED, LAYERS_REFRESH_ERROR, BROWSE_DATA }; diff --git a/web/client/actions/wfsquery.js b/web/client/actions/wfsquery.js index d3d674c987..1761ed3643 100644 --- a/web/client/actions/wfsquery.js +++ b/web/client/actions/wfsquery.js @@ -12,7 +12,6 @@ const FEATURE_LOADED = 'FEATURE_LOADED'; const FEATURE_LOADING = 'FEATURE_LOADING'; const FEATURE_TYPE_ERROR = 'FEATURE_TYPE_ERROR'; const FEATURE_ERROR = 'FEATURE_ERROR'; -const FEATURE_CLOSE = 'FEATURE_CLOSE'; const QUERY_CREATE = 'QUERY_CREATE'; const QUERY_RESULT = 'QUERY_RESULT'; const QUERY_ERROR = 'QUERY_ERROR'; @@ -132,7 +131,6 @@ function resetQuery() { }; } - function toggleQueryPanel(url, name, id) { return (dispatch, getState) => { if (getState().query.typeName !== name) { @@ -141,13 +139,9 @@ function toggleQueryPanel(url, name, id) { dispatch(changeDrawingStatus('clean', null, "queryform", [])); dispatch(featureTypeSelected(url, name)); dispatch(toggleControl('queryPanel', null)); - dispatch(layerSelectedForSearch(id)); dispatch(setControlProperty('drawer', 'width', getState().controls.queryPanel.enabled ? 700 : 300)); - }; -} -function featureClose() { - return { - type: FEATURE_CLOSE + dispatch(layerSelectedForSearch(id)); + }; } @@ -168,7 +162,6 @@ module.exports = { FEATURE_TYPE_LOADED, featureTypeLoaded, FEATURE_TYPE_ERROR, featureTypeError, FEATURE_ERROR, featureError, - FEATURE_CLOSE, featureClose, QUERY_CREATE, createQuery, QUERY_RESULT, querySearchResponse, QUERY_ERROR, queryError, diff --git a/web/client/components/TOC/DefaultLayer.jsx b/web/client/components/TOC/DefaultLayer.jsx index 8e47d6165f..2c19222472 100644 --- a/web/client/components/TOC/DefaultLayer.jsx +++ b/web/client/components/TOC/DefaultLayer.jsx @@ -26,7 +26,7 @@ class DefaultLayer extends React.Component { retrieveLayerData: PropTypes.func, onToggle: PropTypes.func, onContextMenu: PropTypes.func, - onToggleQuerypanel: PropTypes.func, + onBrowseData: PropTypes.func, onZoom: PropTypes.func, onSettings: PropTypes.func, onRefresh: PropTypes.func, @@ -71,7 +71,7 @@ class DefaultLayer extends React.Component { onSettings: () => {}, onRefresh: () => {}, retrieveLayerData: () => {}, - onToggleQuerypanel: () => {}, + onBrowseData: () => {}, activateRemoveLayer: false, activateLegendTool: false, activateSettingsTool: false, @@ -150,13 +150,17 @@ class DefaultLayer extends React.Component { if (this.props.activateQueryTool && this.props.node.search) { tools.push( this.props.onToggleQuerypanel(node.search.url || node.url, node.name, node.id)}/> + glyph="features-grid" + onClick={(node) => this.props.onBrowseData({ + url: node.search.url || node.url, + name: node.name, + id: node.id + })}/> ); } return (
diff --git a/web/client/epics/featuregrid.js b/web/client/epics/featuregrid.js index 9abea8b72d..4f9f19cc0f 100644 --- a/web/client/epics/featuregrid.js +++ b/web/client/epics/featuregrid.js @@ -17,14 +17,16 @@ const {changeDrawingStatus, GEOMETRY_CHANGED} = require('../actions/draw'); const requestBuilder = require('../utils/ogc/WFST/RequestBuilder'); const {findGeometryProperty} = require('../utils/ogc/WFS/base'); const {setControlProperty} = require('../actions/controls'); -const {query, QUERY_CREATE, QUERY_RESULT, LAYER_SELECTED_FOR_SEARCH, closeResponse} = require('../actions/wfsquery'); +const {query, QUERY_CREATE, QUERY_RESULT, LAYER_SELECTED_FOR_SEARCH, FEATURE_TYPE_LOADED, featureTypeSelected, createQuery} = require('../actions/wfsquery'); +const {BROWSE_DATA} = require('../actions/layers'); const {parseString} = require('xml2js'); const {stripPrefix} = require('xml2js/lib/processors'); const {SORT_BY, CHANGE_PAGE, SAVE_CHANGES, SAVE_SUCCESS, DELETE_SELECTED_FEATURES, featureSaving, changePage, saveSuccess, saveError, clearChanges, setLayer, clearSelection, toggleViewMode, toggleTool, CLEAR_CHANGES, START_EDITING_FEATURE, TOGGLE_MODE, MODES, geometryChanged, DELETE_GEOMETRY, deleteGeometryFeature, - SELECT_FEATURES, DESELECT_FEATURES, START_DRAWING_FEATURE, CREATE_NEW_FEATURE, CLOSE_GRID, closeFeatureGrid, - CLEAR_CHANGES_CONFIRMED, FEATURE_GRID_CLOSE_CONFIRMED} = require('../actions/featuregrid'); + SELECT_FEATURES, DESELECT_FEATURES, START_DRAWING_FEATURE, CREATE_NEW_FEATURE, + CLEAR_CHANGES_CONFIRMED, FEATURE_GRID_CLOSE_CONFIRMED, + openFeatureGrid, closeFeatureGrid, OPEN_FEATURE_GRID, CLOSE_FEATURE_GRID, CLOSE_FEATURE_GRID_CONFIRM} = require('../actions/featuregrid'); const {TOGGLE_CONTROL} = require('../actions/controls'); const {setHighlightFeaturesPath} = require('../actions/highlight'); @@ -34,7 +36,7 @@ const {selectedFeaturesSelector, changesMapSelector, newFeaturesSelector, hasCha isFeatureGridOpen} = require('../selectors/featuregrid'); const {error} = require('../actions/notifications'); -const {describeSelector, getFeatureById, wfsURL, wfsFilter} = require('../selectors/query'); +const {describeSelector, isDescribeLoaded, getFeatureById, wfsURL, wfsFilter} = require('../selectors/query'); const drawSupportReset = () => changeDrawingStatus("clean", "", "featureGrid", [], {}); /** * Intercept OGC Exception (200 response with exceptionReport) to throw error in the stream @@ -142,7 +144,44 @@ const createDeleteFlow = (features, describeFeatureType, url) => save( createDeleteTransaction(features, requestBuilder(describeFeatureType)) ); +const createLoadPageFlow = (store) => ({page, size} = {}) => { + const state = store.getState(); + return Rx.Observable.of( query( + wfsURL(state), + addPagination({ + ...wfsFilter(state) + }, + getPagination(state, {page, size}) + ) + )); +}; + +const createInitialQueryFlow = (action$, store, {url, name} = {}) => { + const createInitialQuery = () => createQuery(url, { + featureTypeName: name, + filterType: 'OGC', + ogcVersion: '1.1.0' + }); + + if (isDescribeLoaded(store.getState(), name)) { + return Rx.Observable.of(createInitialQuery()); + } + return Rx.Observable.of(featureTypeSelected(url, name)).merge( + action$.ofType(FEATURE_TYPE_LOADED).filter(({typeName} = {}) => typeName === name) + .map(createInitialQuery) + ); +}; module.exports = { + featureGridBrowseData: (action$, store) => + action$.ofType(BROWSE_DATA).switchMap( ({layer}) => + Rx.Observable.of( + setControlProperty('drawer', 'enabled', false), + setLayer(layer.id), + openFeatureGrid() + ).merge( + createInitialQueryFlow(action$, store, layer) + ) + ), /** * Intercepts layer selection to set it's id in the status and retrieve it later */ @@ -152,12 +191,10 @@ module.exports = { /** * Intercepts query creation to perform the real query, setting page to 0 */ - featureGridStartupQuery: (action$) => + featureGridStartupQuery: (action$, store) => action$.ofType(QUERY_CREATE) - .switchMap(() => Rx.Observable.of( - changePage(0), - toggleViewMode() - )), + .switchMap(() => Rx.Observable.of(changePage(0)) + .concat(modeSelector(store.getState()) === MODES.VIEW ? Rx.Observable.of(toggleViewMode()) : Rx.Observable.empty())), /** * Create sorted queries on sort action */ @@ -177,16 +214,7 @@ module.exports = { * perform paginated query on page change */ featureGridChangePage: (action$, store) => - action$.ofType(CHANGE_PAGE).switchMap( ({page, size} = {}) => - Rx.Observable.of( query( - wfsURL(store.getState()), - addPagination({ - ...wfsFilter(store.getState()) - }, - getPagination(store.getState(), {page, size}) - ) - )) - ), + action$.ofType(CHANGE_PAGE).switchMap(createLoadPageFlow(store)), /** * Reload the page on save success. * NOTE: The page is in the action. @@ -302,6 +330,16 @@ module.exports = { }; return Rx.Observable.of(changeDrawingStatus("drawOrEdit", geomType, "featureGrid", [feature], drawOptions)); }), + resetEditingOnFeatureGridClose: (action$, store) => action$.ofType(OPEN_FEATURE_GRID).switchMap( () => + action$.ofType(TOGGLE_MODE) + .filter(() => modeSelector(store.getState()) === MODES.EDIT) + .take(1) + .switchMap( () => + action$.ofType(LOCATION_CHANGE, CLOSE_FEATURE_GRID) + .take(1) + .switchMap(() => Rx.Observable.of(drawSupportReset()))) + + ), /** * intercept geomertry changed events in draw support to update current * modified geometry in featuregrid @@ -364,21 +402,28 @@ module.exports = { * This forces to view mode and turn all tools to initial state. */ resetGridOnLocationChange: action$ => - action$.ofType(LOCATION_CHANGE).switchMap( () =>Rx.Observable.of( - closeResponse() - )), + action$.ofType(OPEN_FEATURE_GRID).switchMap( () => + action$.ofType(LOCATION_CHANGE) + .take(1) + .map(() => toggleViewMode()) + .takeUntil(CLOSE_FEATURE_GRID) + ), /** * Closes the feature grid when the drawer menu button has been toggled */ - autoCloseFeatureGridEpicOnDrowerOpen: (action$, store) => action$.ofType(TOGGLE_CONTROL) - .filter(action => action.control && action.control === 'drawer' && isFeatureGridOpen(store.getState())) - .switchMap(() => Rx.Observable.of(closeFeatureGrid())), - askChangesConfirmOnFeatureGridClose: (action$, store) => action$.ofType(CLOSE_GRID).switchMap( () => { + autoCloseFeatureGridEpicOnDrowerOpen: (action$, store) => + action$.ofType(OPEN_FEATURE_GRID).switchMap(() => + action$.ofType(TOGGLE_CONTROL) + .filter(action => action.control && action.control === 'drawer' && isFeatureGridOpen(store.getState())) + .switchMap(() => Rx.Observable.of(closeFeatureGrid())) + .takeUntil(CLOSE_FEATURE_GRID, LOCATION_CHANGE) + ), + askChangesConfirmOnFeatureGridClose: (action$, store) => action$.ofType(CLOSE_FEATURE_GRID_CONFIRM).switchMap( () => { const state = store.getState(); if (hasChangesSelector(state) || hasNewFeaturesSelector(state)) { return Rx.Observable.of(toggleTool("featureCloseConfirm", true)); } - return Rx.Observable.of(closeResponse()); + return Rx.Observable.of(closeFeatureGrid()); }), onClearChangeConfirmedFeatureGrid: (action$) => action$.ofType(CLEAR_CHANGES_CONFIRMED) .switchMap( () => Rx.Observable.of(clearChanges(), toggleTool("clearConfirm", false))), diff --git a/web/client/epics/wfsquery.js b/web/client/epics/wfsquery.js index c790012597..a8fd8e127a 100644 --- a/web/client/epics/wfsquery.js +++ b/web/client/epics/wfsquery.js @@ -17,7 +17,6 @@ const FilterUtils = require('../utils/FilterUtils'); const assign = require('object-assign'); const {isString, isObject} = require('lodash'); -const {setControlProperty} = require('../actions/controls'); // this is a workaround for https://osgeo-org.atlassian.net/browse/GEOS-7233. can be removed when fixed const workaroundGEOS7233 = ({totalFeatures, features, ...rest}, {startIndex, maxFeatures}, originalSize) => { if (originalSize > totalFeatures && originalSize === startIndex + features.length && totalFeatures === features.length) { @@ -231,7 +230,6 @@ const wfsQueryEpic = (action$, store) => action$.ofType(QUERY) .switchMap(action => { return Rx.Observable.merge( - Rx.Observable.of(setControlProperty('drawer', 'enabled', false)), getWFSFeature(action.searchUrl, action.filterObj) .switchMap((response) => { // try to guess if it was a missing id error and try to search again with forced sortOptions diff --git a/web/client/examples/featuregrid/stores/store.js b/web/client/examples/featuregrid/stores/store.js index 5eeb41f679..380c5fa792 100644 --- a/web/client/examples/featuregrid/stores/store.js +++ b/web/client/examples/featuregrid/stores/store.js @@ -9,7 +9,7 @@ const Rx = require('rxjs'); const {featureTypeSelectedEpic, wfsQueryEpic, viewportSelectedEpic} = require('../../../epics/wfsquery'); const {getLayerFromId} = require('../../../selectors/layers'); -const {createQuery, featureTypeSelected, layerSelectedForSearch, LAYER_SELECTED_FOR_SEARCH, FEATURE_TYPE_LOADED, FEATURE_CLOSE} = require('../../../actions/wfsquery'); +const {createQuery, featureTypeSelected, layerSelectedForSearch, LAYER_SELECTED_FOR_SEARCH, FEATURE_TYPE_LOADED, CLOSE_FEATURE_GRID} = require('../../../actions/wfsquery'); const {clearChanges, setPermission, toggleTool} = require('../../../actions/featuregrid'); const {hasChangesSelector, hasNewFeaturesSelector} = require('../../../selectors/featuregrid'); module.exports = (plugins) => { @@ -29,7 +29,7 @@ module.exports = (plugins) => { const state = store.getState(); if (hasChangesSelector(state) || hasNewFeaturesSelector(state)) { return Rx.Observable.of(toggleTool("featureCloseConfirm", true)) - .merge(action$.ofType(FEATURE_CLOSE).switchMap( () => Rx.Observable.of( + .merge(action$.ofType(CLOSE_FEATURE_GRID).switchMap( () => Rx.Observable.of( layerSelectedForSearch(id), setPermission({canEdit: true}), featureTypeSelected( 'http://demo.geo-solutions.it:80/geoserver/wfs', id), diff --git a/web/client/plugins/FeatureEditor.jsx b/web/client/plugins/FeatureEditor.jsx index 89d224d3b7..be34447e4c 100644 --- a/web/client/plugins/FeatureEditor.jsx +++ b/web/client/plugins/FeatureEditor.jsx @@ -64,7 +64,7 @@ const FeatureDock = (props = { ); }; const selector = createSelector( - state => get(state, "query.open"), + state => get(state, "featuregrid.open"), resultsSelector, describeSelector, state => get(state, "featuregrid.attributes"), diff --git a/web/client/plugins/TOC.jsx b/web/client/plugins/TOC.jsx index 43a381d981..eaee68c5d2 100644 --- a/web/client/plugins/TOC.jsx +++ b/web/client/plugins/TOC.jsx @@ -13,7 +13,7 @@ const {Button, Glyphicon} = require('react-bootstrap'); const autocompleteEpics = require('../epics/autocomplete'); const {changeLayerProperties, changeGroupProperties, toggleNode, contextNode, - sortNode, showSettings, hideSettings, updateSettings, updateNode, removeNode} = require('../actions/layers'); + sortNode, showSettings, hideSettings, updateSettings, updateNode, removeNode, browseData} = require('../actions/layers'); const {getLayerCapabilities} = require('../actions/layerCapabilities'); const {zoomToExtent} = require('../actions/map'); const {groupsSelector, layersSelector} = require('../selectors/layers'); @@ -62,7 +62,7 @@ const {toggleControl, setControlProperty} = require('../actions/controls'); const {refreshLayers} = require('../actions/layers'); -const {createQuery, toggleQueryPanel} = require('../actions/wfsquery'); +const {createQuery} = require('../actions/wfsquery'); const { changeDrawingStatus, @@ -213,7 +213,7 @@ class LayerTree extends React.Component { onToggleGroup: PropTypes.func, onToggleLayer: PropTypes.func, onContextMenu: PropTypes.func, - onToggleQuery: PropTypes.func, + onBrowseData: PropTypes.func, onZoomToExtent: PropTypes.func, retrieveLayerData: PropTypes.func, onSort: PropTypes.func, @@ -313,7 +313,7 @@ class LayerTree extends React.Component { settingsOptions={this.props.settingsOptions} onToggle={this.props.onToggleLayer} onContextMenu={this.props.onContextMenu} - onToggleQuerypanel={this.props.onToggleQuery } + onBrowseData={this.props.onBrowseData} onZoom={this.props.onZoomToExtent} onSettings={this.props.onSettings} onRefresh={this.props.onRefreshLayer} @@ -354,7 +354,6 @@ class LayerTree extends React.Component { renderQueryPanel = () => { return (
- {} , { onClose: () => closeFeatureGridConfirmed(), - onConfirm: () => closeResponse() + onConfirm: () => closeFeatureGrid() })(require('../../../components/data/featuregrid/dialog/ConfirmFeatureClose')); const panels = { diff --git a/web/client/plugins/featuregrid/toolbarEvents.js b/web/client/plugins/featuregrid/toolbarEvents.js index fdf4c6ec61..4a889f5072 100644 --- a/web/client/plugins/featuregrid/toolbarEvents.js +++ b/web/client/plugins/featuregrid/toolbarEvents.js @@ -2,7 +2,7 @@ const {toggleControl} = require('../../actions/controls'); const {toggleTool, toggleEditMode, toggleViewMode, - closeFeatureGrid, + closeFeatureGridConfirm, saveChanges, createNewFeatures, startEditingFeature, @@ -22,5 +22,5 @@ module.exports = { startEditingFeature: () => startEditingFeature(), startDrawingFeature: () => startDrawingFeature(), switchViewMode: () => toggleViewMode(), - onClose: () => closeFeatureGrid() + onClose: () => closeFeatureGridConfirm() }; diff --git a/web/client/reducers/__tests__/featuregrid-test.js b/web/client/reducers/__tests__/featuregrid-test.js index 463f3745d4..1f72e26315 100644 --- a/web/client/reducers/__tests__/featuregrid-test.js +++ b/web/client/reducers/__tests__/featuregrid-test.js @@ -46,8 +46,8 @@ const expect = require('expect'); const featuregrid = require('../featuregrid'); const {setFeatures, dockSizeFeatures, setLayer, toggleTool, customizeAttribute, selectFeatures, deselectFeatures, createNewFeatures, featureSaving, toggleSelection, clearSelection, MODES, toggleEditMode, toggleViewMode, saveSuccess, clearChanges, saveError, startDrawingFeature, - deleteGeometryFeature, geometryChanged, setSelectionOptions, changePage, featureModified, setPermission} = require('../../actions/featuregrid'); -const {featureTypeLoaded, featureClose} = require('../../actions/wfsquery'); + deleteGeometryFeature, geometryChanged, setSelectionOptions, changePage, featureModified, setPermission, disableToolbar} = require('../../actions/featuregrid'); +const {featureTypeLoaded, closeFeatureGrid} = require('../../actions/wfsquery'); const {changeDrawingStatus} = require('../../actions/draw'); const museam = require('json-loader!../../test-resources/wfs/museam.json'); @@ -219,8 +219,8 @@ describe('Test the featuregrid reducer', () => { let state = featuregrid( {}, setPermission({canEdit: true})); expect(state.canEdit).toBe(true); }); - it('featureClose', () => { - let state = featuregrid( {pagination: {size: 3}}, featureClose()); + it('closeFeatureGrid', () => { + let state = featuregrid( {pagination: {size: 3}}, closeFeatureGrid()); expect(state.drawing).toBe(false); expect(state.deleteConfirm).toBe(false); expect(state.pagination.size).toBe(3); @@ -250,6 +250,16 @@ describe('Test the featuregrid reducer', () => { state = featuregrid( state, geometryChanged([feature1])); expect(state.changes.length).toBe(2); + }); + it('DISABLE_TOOLBAR', () => { + let state = featuregrid({}, {type: "UNKNOWN"}); + expect(state.disableToolbar).toBeFalsy(); + state = featuregrid({}, disableToolbar(true)); + expect(state.disableToolbar).toBe(true); + + state = featuregrid({}, disableToolbar(false)); + expect(state.disableToolbar).toBe(false); + }); it('featureTypeLoaded', () => { let state = featuregrid( {}, featureTypeLoaded("typeName", { diff --git a/web/client/reducers/featuregrid.js b/web/client/reducers/featuregrid.js index fcbad6b433..39fc08782e 100644 --- a/web/client/reducers/featuregrid.js +++ b/web/client/reducers/featuregrid.js @@ -6,7 +6,7 @@ * LICENSE file in the root directory of this source tree. */ const assign = require("object-assign"); -const {head} = require("lodash"); +const {head, get} = require("lodash"); const { SELECT_FEATURES, DESELECT_FEATURES, @@ -29,11 +29,13 @@ const { GEOMETRY_CHANGED, DELETE_GEOMETRY_FEATURE, START_DRAWING_FEATURE, - SET_PERMISSION + SET_PERMISSION, + DISABLE_TOOLBAR, + OPEN_FEATURE_GRID, + CLOSE_FEATURE_GRID } = require('../actions/featuregrid'); const{ - FEATURE_TYPE_LOADED, - FEATURE_CLOSE + FEATURE_TYPE_LOADED } = require('../actions/wfsquery'); const{ CHANGE_DRAWING_STATUS @@ -41,6 +43,7 @@ const{ const uuid = require('uuid'); const emptyResultsState = { + open: false, canEdit: false, focusOnEdit: true, mode: MODES.VIEW, @@ -220,7 +223,7 @@ function featuregrid(state = emptyResultsState, action) { } case FEATURE_TYPE_LOADED: { return assign({}, state, { - localType: action.featureType.original.featureTypes[0].properties[1].localType + localType: get(action, "featureType.original.featureTypes[0].properties[1].localType") }); } case START_DRAWING_FEATURE: { @@ -228,8 +231,14 @@ function featuregrid(state = emptyResultsState, action) { drawing: !state.drawing }); } - case FEATURE_CLOSE: { + case OPEN_FEATURE_GRID: { return assign({}, state, { + open: true + }); + } + case CLOSE_FEATURE_GRID: { + return assign({}, state, { + open: false, pagination: { page: 0, size: state.pagination.size @@ -244,6 +253,11 @@ function featuregrid(state = emptyResultsState, action) { select: [] }); } + case DISABLE_TOOLBAR: { + return assign({}, state, { + disableToolbar: action.disabled + }); + } case SET_PERMISSION: { return assign({}, state, { canEdit: action.permission.canEdit @@ -257,7 +271,6 @@ function featuregrid(state = emptyResultsState, action) { } return state; } - default: return state; } diff --git a/web/client/reducers/query.js b/web/client/reducers/query.js index 967ea57e29..f8549665f6 100644 --- a/web/client/reducers/query.js +++ b/web/client/reducers/query.js @@ -16,8 +16,7 @@ const { QUERY_CREATE, QUERY_RESULT, QUERY_ERROR, - RESET_QUERY, - FEATURE_CLOSE + RESET_QUERY } = require('../actions/wfsquery'); const {QUERY_FORM_RESET} = require('../actions/queryform'); @@ -88,7 +87,6 @@ function query(state = initialState, action) { } case QUERY_CREATE: { return assign({}, state, { - open: true, isNew: true, searchUrl: action.searchUrl, filterObj: action.filterObj @@ -113,7 +111,6 @@ function query(state = initialState, action) { case RESET_CONTROLS: case QUERY_FORM_RESET: return assign({}, state, { - open: false, isNew: false, result: null, filterObj: null, @@ -125,11 +122,6 @@ function query(state = initialState, action) { resultError: null }); } - case FEATURE_CLOSE: { - return assign({}, state, { - open: false - }); - } default: return state; } diff --git a/web/client/selectors/query.js b/web/client/selectors/query.js index 716039fe08..07cb0f8909 100644 --- a/web/client/selectors/query.js +++ b/web/client/selectors/query.js @@ -14,6 +14,7 @@ module.exports = { resultSize: (state) =>get(state, "query.result.features.length"), totalFeatures: (state) => get(state, "query.result.totalFeatures") }, + isDescribeLoaded: (state, name) => !!get(state, `query.featureTypes.${name}`), describeSelector: (state) => get(state, `query.featureTypes.${get(state, "query.filterObj.featureTypeName")}.original`), featureLoadingSelector: (state) => get(state, "query.featureLoading") }; From 6cef8fdea6fa2167c4b3ac2858a2eb502ef2dc92 Mon Sep 17 00:00:00 2001 From: Lorenzo Natali Date: Fri, 4 Aug 2017 18:28:15 +0200 Subject: [PATCH 2/8] Add advanced filter to featuregrid --- web/client/actions/featuregrid.js | 7 + web/client/actions/wfsquery.js | 3 +- web/client/components/TOC/DefaultLayer.jsx | 2 +- .../data/featuregrid/toolbars/Toolbar.jsx | 7 + web/client/epics/featuregrid.js | 35 ++++- web/client/localConfig.json | 21 ++- web/client/plugins/FeatureGrid.jsx | 4 +- web/client/plugins/QueryPanel.jsx | 30 ++++- web/client/plugins/TOC.jsx | 124 +----------------- .../plugins/featuregrid/toolbarEvents.js | 6 +- web/client/reducers/query.js | 4 +- web/client/selectors/featuregrid.js | 2 +- web/client/translations/data.de-DE | 2 + web/client/translations/data.en-US | 3 +- web/client/translations/data.es-ES | 2 + web/client/translations/data.fr-FR | 2 + web/client/translations/data.it-IT | 2 + 17 files changed, 110 insertions(+), 146 deletions(-) diff --git a/web/client/actions/featuregrid.js b/web/client/actions/featuregrid.js index 607c273339..c3e511c199 100644 --- a/web/client/actions/featuregrid.js +++ b/web/client/actions/featuregrid.js @@ -40,6 +40,7 @@ const CLEAR_CHANGES_CONFIRMED = 'FEATUREGRID:CLEAR_CHANGES_CONFIRMED'; const FEATURE_GRID_CLOSE_CONFIRMED = 'FEATUREGRID:FEATURE_GRID_CLOSE_CONFIRMED'; const SET_PERMISSION = 'FEATUREGRID:SET_PERMISSION'; const DISABLE_TOOLBAR = 'FEATUREGRID:DISABLE_TOOLBAR'; +const OPEN_ADVANCED_SEARCH = 'FEATUREGRID:ADVANCED_SEARCH'; const MODES = { EDIT: "EDIT", VIEW: "VIEW" @@ -250,6 +251,11 @@ function setPermission(permission) { permission }; } +function openAdvancedSearch() { + return { + type: OPEN_ADVANCED_SEARCH + }; +} module.exports = { SELECT_FEATURES, @@ -287,6 +293,7 @@ module.exports = { CLOSE_FEATURE_GRID_CONFIRM, closeFeatureGridConfirm, FEATURE_GRID_CLOSE_CONFIRMED, closeFeatureGridConfirmed, DISABLE_TOOLBAR, disableToolbar, + OPEN_ADVANCED_SEARCH, openAdvancedSearch, setLayer, selectFeatures, deselectFeatures, diff --git a/web/client/actions/wfsquery.js b/web/client/actions/wfsquery.js index 1761ed3643..69aaff3a59 100644 --- a/web/client/actions/wfsquery.js +++ b/web/client/actions/wfsquery.js @@ -144,10 +144,9 @@ function toggleQueryPanel(url, name, id) { }; } - function closeResponse() { return (dispatch, getState) => { - dispatch(featureClose()); + // dispatch(featureClose()); let state = getState(); if (state.controls && state.controls.queryPanel && state.controls.drawer && !state.controls.drawer.enabled) { dispatch(setControlProperty('drawer', 'enabled', true)); diff --git a/web/client/components/TOC/DefaultLayer.jsx b/web/client/components/TOC/DefaultLayer.jsx index 2c19222472..10ddb9de0f 100644 --- a/web/client/components/TOC/DefaultLayer.jsx +++ b/web/client/components/TOC/DefaultLayer.jsx @@ -150,7 +150,7 @@ class DefaultLayer extends React.Component { if (this.props.activateQueryTool && this.props.node.search) { tools.push( + } + disabled={disableToolbar} + visible={mode === "VIEW"} + onClick={events.showQueryPanel} + glyph="filter"/> } diff --git a/web/client/epics/featuregrid.js b/web/client/epics/featuregrid.js index 4f9f19cc0f..61a49a0256 100644 --- a/web/client/epics/featuregrid.js +++ b/web/client/epics/featuregrid.js @@ -18,6 +18,7 @@ const requestBuilder = require('../utils/ogc/WFST/RequestBuilder'); const {findGeometryProperty} = require('../utils/ogc/WFS/base'); const {setControlProperty} = require('../actions/controls'); const {query, QUERY_CREATE, QUERY_RESULT, LAYER_SELECTED_FOR_SEARCH, FEATURE_TYPE_LOADED, featureTypeSelected, createQuery} = require('../actions/wfsquery'); +const {reset} = require('../actions/queryform'); const {BROWSE_DATA} = require('../actions/layers'); const {parseString} = require('xml2js'); const {stripPrefix} = require('xml2js/lib/processors'); @@ -26,7 +27,7 @@ const {SORT_BY, CHANGE_PAGE, SAVE_CHANGES, SAVE_SUCCESS, DELETE_SELECTED_FEATURE CLEAR_CHANGES, START_EDITING_FEATURE, TOGGLE_MODE, MODES, geometryChanged, DELETE_GEOMETRY, deleteGeometryFeature, SELECT_FEATURES, DESELECT_FEATURES, START_DRAWING_FEATURE, CREATE_NEW_FEATURE, CLEAR_CHANGES_CONFIRMED, FEATURE_GRID_CLOSE_CONFIRMED, - openFeatureGrid, closeFeatureGrid, OPEN_FEATURE_GRID, CLOSE_FEATURE_GRID, CLOSE_FEATURE_GRID_CONFIRM} = require('../actions/featuregrid'); + openFeatureGrid, closeFeatureGrid, OPEN_FEATURE_GRID, CLOSE_FEATURE_GRID, CLOSE_FEATURE_GRID_CONFIRM, OPEN_ADVANCED_SEARCH} = require('../actions/featuregrid'); const {TOGGLE_CONTROL} = require('../actions/controls'); const {setHighlightFeaturesPath} = require('../actions/highlight'); @@ -180,6 +181,11 @@ module.exports = { openFeatureGrid() ).merge( createInitialQueryFlow(action$, store, layer) + ).merge( + get(store.getState(), "query.typeName") !== name + ? Rx.Observable.of(reset()) + : Rx.Observable.empty() + ) ), /** @@ -405,8 +411,13 @@ module.exports = { action$.ofType(OPEN_FEATURE_GRID).switchMap( () => action$.ofType(LOCATION_CHANGE) .take(1) - .map(() => toggleViewMode()) - .takeUntil(CLOSE_FEATURE_GRID) + .switchMap(() => + Rx.Observable.of( + toggleViewMode(), + closeFeatureGrid() + ) + ) + .takeUntil(action$.ofType(CLOSE_FEATURE_GRID)) ), /** * Closes the feature grid when the drawer menu button has been toggled @@ -416,7 +427,7 @@ module.exports = { action$.ofType(TOGGLE_CONTROL) .filter(action => action.control && action.control === 'drawer' && isFeatureGridOpen(store.getState())) .switchMap(() => Rx.Observable.of(closeFeatureGrid())) - .takeUntil(CLOSE_FEATURE_GRID, LOCATION_CHANGE) + .takeUntil(action$.ofType(CLOSE_FEATURE_GRID, LOCATION_CHANGE)) ), askChangesConfirmOnFeatureGridClose: (action$, store) => action$.ofType(CLOSE_FEATURE_GRID_CONFIRM).switchMap( () => { const state = store.getState(); @@ -430,5 +441,19 @@ module.exports = { onCloseFeatureGridConfirmed: (action$) => action$.ofType(FEATURE_GRID_CLOSE_CONFIRMED) .switchMap( () => { return Rx.Observable.of(setControlProperty("drawer", "enabled", false), toggleTool("featureCloseConfirm", false)); - }) + }), + onOpenAdvancedSearch: action$ => + action$.ofType(OPEN_ADVANCED_SEARCH).switchMap(() => + Rx.Observable.of( + setControlProperty('queryPanel', "enabled", true), + closeFeatureGrid() + ).merge( + action$.ofType(QUERY_CREATE).switchMap(() => + Rx.Observable.of( + setControlProperty('queryPanel', "enabled", false), + openFeatureGrid() + ) + ) + ) + ) }; diff --git a/web/client/localConfig.json b/web/client/localConfig.json index 452881788d..a12d1e59cd 100644 --- a/web/client/localConfig.json +++ b/web/client/localConfig.json @@ -158,7 +158,26 @@ "alwaysVisible": true } } - }, "Home", "FeatureEditor", "WFSDownload", { + }, "Home", "FeatureEditor", "WFSDownload", + { + "name": "QueryPanel", + "cfg": { + "activateQueryTool": true, + "spatialOperations": [ + {"id": "INTERSECTS", "name": "queryform.spatialfilter.operations.intersects"}, + {"id": "BBOX", "name": "queryform.spatialfilter.operations.bbox"}, + {"id": "CONTAINS", "name": "queryform.spatialfilter.operations.contains"}, + {"id": "WITHIN", "name": "queryform.spatialfilter.operations.within"} + ], + "spatialMethodOptions": [ + {"id": "Viewport", "name": "queryform.spatialfilter.methods.viewport"}, + {"id": "BBOX", "name": "queryform.spatialfilter.methods.box"}, + {"id": "Circle", "name": "queryform.spatialfilter.methods.circle"}, + {"id": "Polygon", "name": "queryform.spatialfilter.methods.poly"} + ] + } + + }, { "name": "TOC", "cfg": { "activateQueryTool": true, diff --git a/web/client/plugins/FeatureGrid.jsx b/web/client/plugins/FeatureGrid.jsx index d5a17cd28b..91a0063991 100644 --- a/web/client/plugins/FeatureGrid.jsx +++ b/web/client/plugins/FeatureGrid.jsx @@ -7,7 +7,7 @@ */ const {connect} = require('react-redux'); const {selectFeatures, dockSizeFeatures} = require('../actions/featuregrid'); -const {query, closeResponse} = require('../actions/wfsquery'); +const {query, queryClose} = require('../actions/wfsquery'); const {changeMapView} = require('../actions/map'); const {toggleControl} = require('../actions/controls'); @@ -39,7 +39,7 @@ module.exports = { exportAction: () => toggleControl("wfsdownload"), changeMapView, onQuery: query, - onBackToSearch: closeResponse, + onBackToSearch: queryClose, setDockSize: dockSizeFeatures })(require('../components/data/featuregrid_ag/DockedFeatureGrid')), reducers: { diff --git a/web/client/plugins/QueryPanel.jsx b/web/client/plugins/QueryPanel.jsx index 38a04db504..4429b7405b 100644 --- a/web/client/plugins/QueryPanel.jsx +++ b/web/client/plugins/QueryPanel.jsx @@ -12,6 +12,7 @@ const Sidebar = require('react-sidebar').default; const {createSelector} = require('reselect'); const {changeLayerProperties, changeGroupProperties, toggleNode, sortNode, showSettings, hideSettings, updateSettings, updateNode, removeNode} = require('../actions/layers'); +const Message = require('./locale/Message'); const {getLayerCapabilities} = require('../actions/layerCapabilities'); @@ -49,7 +50,8 @@ const { changeDwithinValue, zoneGetValues, zoneSearch, - zoneChange + zoneChange, + toggleMenu } = require('../actions/queryform'); const {createQuery} = require('../actions/wfsquery'); @@ -70,19 +72,29 @@ const SmartQueryForm = connect((state) => { groupLevels: state.queryform.groupLevels, groupFields: state.queryform.groupFields, filterFields: state.queryform.filterFields, + attributes: state.query && state.query.typeName && state.query.featureTypes && state.query.featureTypes[state.query.typeName] && state.query.featureTypes[state.query.typeName].attributes, + featureTypeError: state.query && state.query.typeName && state.query.featureTypes && state.query.featureTypes[state.query.typeName] && state.query.featureTypes[state.query.typeName].error, spatialField: state.queryform.spatialField, showDetailsPanel: state.queryform.showDetailsPanel, toolbarEnabled: state.queryform.toolbarEnabled, attributePanelExpanded: state.queryform.attributePanelExpanded, + autocompleteEnabled: state.queryform.autocompleteEnabled, + maxFeaturesWPS: state.queryform.maxFeaturesWPS, spatialPanelExpanded: state.queryform.spatialPanelExpanded, - searchUrl: "http://demo.geo-solutions.it/geoserver/ows?service=WFS", - featureTypeName: "topp:states", + featureTypeConfigUrl: state.query && state.query.url, + searchUrl: state.query && state.query.url, + featureTypeName: state.query && state.query.typeName, ogcVersion: "1.1.0", + params: {typeName: state.query && state.query.typeName}, resultTitle: "Query Result", - showGeneratedFilter: false + showGeneratedFilter: false, + allowEmptyFilter: true, + emptyFilterWarning: true, + maxHeight: state.map && state.map.present && state.map.present.size && state.map.present.size.height }; }, dispatch => { return { + attributeFilterActions: bindActionCreators({ onAddGroupField: addGroupField, onAddFilterField: addFilterField, @@ -92,6 +104,7 @@ const SmartQueryForm = connect((state) => { onUpdateLogicCombo: updateLogicCombo, onRemoveGroupField: removeGroupField, onChangeCascadingValue: changeCascadingValue, + toggleMenu: toggleMenu, onExpandAttributeFilterPanel: expandAttributeFilterPanel }, dispatch), spatialFilterActions: bindActionCreators({ @@ -130,7 +143,7 @@ const tocSelector = createSelector( }) ); -class LayerTree extends React.Component { +class QueryPanel extends React.Component { static propTypes = { id: PropTypes.number, buttonContent: PropTypes.node, @@ -212,7 +225,10 @@ class LayerTree extends React.Component { renderQueryPanel = () => { return (
- + }/>
); }; @@ -235,7 +251,7 @@ const QueryPanelPlugin = connect(tocSelector, { updateSettings, updateNode, removeNode -})(LayerTree); +})(QueryPanel); module.exports = { QueryPanelPlugin, diff --git a/web/client/plugins/TOC.jsx b/web/client/plugins/TOC.jsx index eaee68c5d2..a0c540b55e 100644 --- a/web/client/plugins/TOC.jsx +++ b/web/client/plugins/TOC.jsx @@ -27,116 +27,12 @@ const assign = require('object-assign'); const layersIcon = require('./toolbar/assets/img/layers.png'); -// include application component -const QueryBuilder = require('../components/data/query/QueryBuilder'); - const {isObject} = require('lodash'); -const {bindActionCreators} = require('redux'); -const { - // QueryBuilder action functions - addGroupField, - addFilterField, - removeFilterField, - updateFilterField, - updateExceptionField, - updateLogicCombo, - removeGroupField, - changeCascadingValue, - expandAttributeFilterPanel, - expandSpatialFilterPanel, - selectSpatialMethod, - selectViewportSpatialMethod, - selectSpatialOperation, - removeSpatialSelection, - showSpatialSelectionDetails, - reset, - changeDwithinValue, - zoneGetValues, - zoneSearch, - zoneChange, - toggleMenu -} = require('../actions/queryform'); - const {toggleControl, setControlProperty} = require('../actions/controls'); const {refreshLayers} = require('../actions/layers'); -const {createQuery} = require('../actions/wfsquery'); - -const { - changeDrawingStatus, - endDrawing -} = require('../actions/draw'); - -// connecting a Dumb component to the store -// makes it a smart component -// we both connect state => props -// and actions to event handlers -const SmartQueryForm = connect((state) => { - return { - // QueryBuilder props - useMapProjection: state.queryform.useMapProjection, - groupLevels: state.queryform.groupLevels, - groupFields: state.queryform.groupFields, - filterFields: state.queryform.filterFields, - attributes: state.query && state.query.typeName && state.query.featureTypes && state.query.featureTypes[state.query.typeName] && state.query.featureTypes[state.query.typeName].attributes, - featureTypeError: state.query && state.query.typeName && state.query.featureTypes && state.query.featureTypes[state.query.typeName] && state.query.featureTypes[state.query.typeName].error, - spatialField: state.queryform.spatialField, - showDetailsPanel: state.queryform.showDetailsPanel, - toolbarEnabled: state.queryform.toolbarEnabled, - attributePanelExpanded: state.queryform.attributePanelExpanded, - autocompleteEnabled: state.queryform.autocompleteEnabled, - maxFeaturesWPS: state.queryform.maxFeaturesWPS, - spatialPanelExpanded: state.queryform.spatialPanelExpanded, - featureTypeConfigUrl: state.query && state.query.url, - searchUrl: state.query && state.query.url, - featureTypeName: state.query && state.query.typeName, - ogcVersion: "1.1.0", - params: {typeName: state.query && state.query.typeName}, - resultTitle: "Query Result", - showGeneratedFilter: false, - allowEmptyFilter: true, - emptyFilterWarning: true, - maxHeight: state.map && state.map.present && state.map.present.size && state.map.present.size.height - }; -}, dispatch => { - return { - - attributeFilterActions: bindActionCreators({ - onAddGroupField: addGroupField, - onAddFilterField: addFilterField, - onRemoveFilterField: removeFilterField, - onUpdateFilterField: updateFilterField, - onUpdateExceptionField: updateExceptionField, - onUpdateLogicCombo: updateLogicCombo, - onRemoveGroupField: removeGroupField, - onChangeCascadingValue: changeCascadingValue, - toggleMenu: toggleMenu, - onExpandAttributeFilterPanel: expandAttributeFilterPanel - }, dispatch), - spatialFilterActions: bindActionCreators({ - onExpandSpatialFilterPanel: expandSpatialFilterPanel, - onSelectSpatialMethod: selectSpatialMethod, - onSelectViewportSpatialMethod: selectViewportSpatialMethod, - onSelectSpatialOperation: selectSpatialOperation, - onChangeDrawingStatus: changeDrawingStatus, - onRemoveSpatialSelection: removeSpatialSelection, - onShowSpatialSelectionDetails: showSpatialSelectionDetails, - onEndDrawing: endDrawing, - onChangeDwithinValue: changeDwithinValue, - zoneFilter: zoneGetValues, - zoneSearch, - zoneChange - }, dispatch), - queryToolbarActions: bindActionCreators({ - onQuery: createQuery, - onReset: reset, - onChangeDrawingStatus: changeDrawingStatus - }, dispatch) - }; -})(QueryBuilder); - const refreshSelector = createSelector([ (state) => state.controls && state.controls.RefreshLayers || {}, layersSelector, @@ -179,13 +75,11 @@ const tocSelector = createSelector( (state) => state.controls && state.controls.toolbar && state.controls.toolbar.active === 'toc', groupsSelector, (state) => state.layers && state.layers.settings || {expanded: false, options: {opacity: 1}}, - (state) => state.controls && state.controls.queryPanel && state.controls.queryPanel.enabled || false, mapSelector - ], (enabled, groups, settings, querypanelEnabled, map) => ({ + ], (enabled, groups, settings, map) => ({ enabled, groups, settings, - querypanelEnabled, currentZoomLvl: map && map.zoom, scales: mapUtils.getScales( map && map.projection || 'EPSG:3857', @@ -205,7 +99,6 @@ class LayerTree extends React.Component { buttonContent: PropTypes.node, groups: PropTypes.array, settings: PropTypes.object, - querypanelEnabled: PropTypes.bool, refreshMapEnabled: PropTypes.bool, groupStyle: PropTypes.object, groupPropertiesChangeHandler: PropTypes.func, @@ -267,7 +160,6 @@ class LayerTree extends React.Component { closeGlyph: "1-close", buttonSize: "small" }, - querypanelEnabled: false, refreshMapEnabled: false, layerOptions: {}, groupOptions: {}, @@ -351,24 +243,10 @@ class LayerTree extends React.Component { ); }; - renderQueryPanel = () => { - return ( -
- }/> -
- ); - }; - render() { if (!this.props.groups) { return
; } - if (this.props.querypanelEnabled) { - return this.renderQueryPanel(); - } return this.renderTOC(); } } diff --git a/web/client/plugins/featuregrid/toolbarEvents.js b/web/client/plugins/featuregrid/toolbarEvents.js index 4a889f5072..fada9952fa 100644 --- a/web/client/plugins/featuregrid/toolbarEvents.js +++ b/web/client/plugins/featuregrid/toolbarEvents.js @@ -7,7 +7,8 @@ const {toggleTool, createNewFeatures, startEditingFeature, startDrawingFeature, - deleteGeometry + deleteGeometry, + openAdvancedSearch } = require('../../actions/featuregrid'); module.exports = { @@ -22,5 +23,6 @@ module.exports = { startEditingFeature: () => startEditingFeature(), startDrawingFeature: () => startDrawingFeature(), switchViewMode: () => toggleViewMode(), - onClose: () => closeFeatureGridConfirm() + onClose: () => closeFeatureGridConfirm(), + showQueryPanel: () => openAdvancedSearch() }; diff --git a/web/client/reducers/query.js b/web/client/reducers/query.js index f8549665f6..facb35cc2c 100644 --- a/web/client/reducers/query.js +++ b/web/client/reducers/query.js @@ -16,7 +16,9 @@ const { QUERY_CREATE, QUERY_RESULT, QUERY_ERROR, - RESET_QUERY + RESET_QUERY, + QUERY_OPEN, + QUERY_CLOSE } = require('../actions/wfsquery'); const {QUERY_FORM_RESET} = require('../actions/queryform'); diff --git a/web/client/selectors/featuregrid.js b/web/client/selectors/featuregrid.js index be546e3c53..5810cc4092 100644 --- a/web/client/selectors/featuregrid.js +++ b/web/client/selectors/featuregrid.js @@ -46,7 +46,7 @@ const hasGeometrySelectedFeature = (state) => { const hasChangesSelector = state => changesSelector(state) && changesSelector(state).length > 0; const hasNewFeaturesSelector = state => newFeaturesSelector(state) && newFeaturesSelector(state).length > 0; module.exports = { - isFeatureGridOpen: state => state && state.query && state.query.open, // TODO move open in featuregrid from query reducer + isFeatureGridOpen: state => state && state.featuregrid && state.featuregrid.open, selectedLayerIdSelector, getTitleSelector: state => getTitle( getLayerById( diff --git a/web/client/translations/data.de-DE b/web/client/translations/data.de-DE index cf38c1e0a9..145025639a 100644 --- a/web/client/translations/data.de-DE +++ b/web/client/translations/data.de-DE @@ -216,6 +216,7 @@ "displayLegendAndTools": "Legende und Werkzeuge zeigen", "zoomToLayerExtent": "Zoome auf Ausdehung der Ebene", "editLayerProperties": "Ändere Ebenen Eigenschaften", + "browseData": "öffnen Sie die Attributtabelle", "searchFeatures": "Auf dieser Ebene suchen...", "removeLayer": "Ebene entfernen", "loadingerror": "Diese Ebene wurde nicht korrekt geladen", @@ -830,6 +831,7 @@ "missingGeometry": "Missing geometry", "toolbar": { "editMode": "Bearbeitungsmodus", + "advancedFilter": "Erweiterte Suche", "quitEditMode": "Beenden Sie den Bearbeitungsmodus", "addNewFeatures": "Neue Funktion hinzufügen", "editFeature": "Bearbeiten Sie die Funktion", diff --git a/web/client/translations/data.en-US b/web/client/translations/data.en-US index 325adac603..080b555489 100644 --- a/web/client/translations/data.en-US +++ b/web/client/translations/data.en-US @@ -216,7 +216,7 @@ "displayLegendAndTools": "Display legend and tools", "zoomToLayerExtent": "Zoom to layer extent", "editLayerProperties": "Edit layer properties", - "searchFeatures": "Search on this layer...", + "browseData": "Open Attribute Table", "removeLayer": "Remove layer", "loadingerror": "The layer has not been loaded correctly", "measure": "Measure", @@ -830,6 +830,7 @@ "missingGeometry": "Missing geometry", "toolbar": { "editMode": "Edit mode", + "advancedFilter": "Advanced Search", "quitEditMode": "Quit edit mode", "addNewFeatures": "Add New feature", "editFeature": "Edit feature", diff --git a/web/client/translations/data.es-ES b/web/client/translations/data.es-ES index c9a1d3eba4..e5c3214d62 100644 --- a/web/client/translations/data.es-ES +++ b/web/client/translations/data.es-ES @@ -218,6 +218,7 @@ "displayLegendAndTools": "Mostrar la leyenda y las herramientas", "zoomToLayerExtent": "Zoom sobre la extensión del mapa", "editLayerProperties": "Modificar las propiedades de la capa", + "browseData": "Abrir la tabla de atributos", "searchFeatures": "Buscar en el mapa ...", "removeLayer": "Borrar la capa", "loadingerror": "La capa se ha cargado correctamente", @@ -833,6 +834,7 @@ "missingGeometry": "Missing geometry", "toolbar": { "editMode": "Modo de edición", + "advancedFilter": "Búsqueda avanzada", "quitEditMode": "Salir del modo de edición", "addNewFeatures": "Añadir nueva función", "editFeature": "Función de edición", diff --git a/web/client/translations/data.fr-FR b/web/client/translations/data.fr-FR index bc4e0ceedb..81f299e454 100644 --- a/web/client/translations/data.fr-FR +++ b/web/client/translations/data.fr-FR @@ -218,6 +218,7 @@ "displayLegendAndTools": "Afficher la légende et les outils", "zoomToLayerExtent": "Zoomer sur l'étendue du calque", "editLayerProperties": "Modifier les propriétés de la couche", + "browseData": "Ouvrir la table d'attribut", "searchFeatures": "Recherche sur ce calque ...", "removeLayer": "Supprimer la couche", "loadingerror": "La couche n'a pas été chargée correctement", @@ -833,6 +834,7 @@ "missingGeometry": "Missing geometry", "toolbar": { "editMode": "Mode édition", + "advancedFilter": "Recherche Avancée", "quitEditMode": "Quitter le mode d'édition", "addNewFeatures": "Ajouter un nouvel élément", "editFeature": "Élément d'édition", diff --git a/web/client/translations/data.it-IT b/web/client/translations/data.it-IT index 5a8be1f4fc..cb321b7b5c 100644 --- a/web/client/translations/data.it-IT +++ b/web/client/translations/data.it-IT @@ -216,6 +216,7 @@ "displayLegendAndTools": "Visualizza legenda e strumenti aggiuntivi", "zoomToLayerExtent": "Zoom all' estensione del livello", "editLayerProperties": "Modifica proprietà dal livello", + "browseData": "Apri tabella degli attributi", "searchFeatures": "Effettua una ricerca su questo livello...", "removeLayer": "Rimuovi livello", "loadingerror": "Il livello non è stato caricato correttamente", @@ -830,6 +831,7 @@ "missingGeometry": "Geometria mancante", "toolbar": { "editMode": "Entra in modalità editing", + "advancedFilter": "Ricerca avanzata", "quitEditMode": "Esci dalla modalità editing", "addNewFeatures": "Aggiungi una nuova feature", "editFeature": "Modifica la feature", From 18adb04b3382aa0c4b004da1e2acd8837b484280 Mon Sep 17 00:00:00 2001 From: Lorenzo Natali Date: Fri, 4 Aug 2017 18:30:01 +0200 Subject: [PATCH 3/8] fixed query reducer eslint --- web/client/reducers/query.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/web/client/reducers/query.js b/web/client/reducers/query.js index facb35cc2c..f8549665f6 100644 --- a/web/client/reducers/query.js +++ b/web/client/reducers/query.js @@ -16,9 +16,7 @@ const { QUERY_CREATE, QUERY_RESULT, QUERY_ERROR, - RESET_QUERY, - QUERY_OPEN, - QUERY_CLOSE + RESET_QUERY } = require('../actions/wfsquery'); const {QUERY_FORM_RESET} = require('../actions/queryform'); From 3b3338eba37016259274d77fa8d3bee3b274e178 Mon Sep 17 00:00:00 2001 From: Lorenzo Natali Date: Fri, 4 Aug 2017 18:54:39 +0200 Subject: [PATCH 4/8] Fixed examples and tests --- .../examples/featuregrid/localConfig.json | 19 ++++++++++++ web/client/examples/featuregrid/plugins.js | 1 + .../examples/featuregrid/stores/store.js | 30 ++++++------------- .../reducers/__tests__/featuregrid-test.js | 12 ++------ .../selectors/__tests__/featuregrid-test.js | 2 +- 5 files changed, 32 insertions(+), 32 deletions(-) diff --git a/web/client/examples/featuregrid/localConfig.json b/web/client/examples/featuregrid/localConfig.json index 090d24b4cd..67477eb53c 100644 --- a/web/client/examples/featuregrid/localConfig.json +++ b/web/client/examples/featuregrid/localConfig.json @@ -7,12 +7,31 @@ "cfg": { "tools": ["draw", "highlight"] } + },{ + "name": "QueryPanel", + "cfg": { + "activateQueryTool": true, + "spatialOperations": [ + {"id": "INTERSECTS", "name": "queryform.spatialfilter.operations.intersects"}, + {"id": "BBOX", "name": "queryform.spatialfilter.operations.bbox"}, + {"id": "CONTAINS", "name": "queryform.spatialfilter.operations.contains"}, + {"id": "WITHIN", "name": "queryform.spatialfilter.operations.within"} + ], + "spatialMethodOptions": [ + {"id": "Viewport", "name": "queryform.spatialfilter.methods.viewport"}, + {"id": "BBOX", "name": "queryform.spatialfilter.methods.box"}, + {"id": "Circle", "name": "queryform.spatialfilter.methods.circle"}, + {"id": "Polygon", "name": "queryform.spatialfilter.methods.poly"} + ] + } + }, "FeatureEditor", "WFSDownload", "LayerSelector", "Notifications" ], + "mobile": [ { "cfg": { diff --git a/web/client/examples/featuregrid/plugins.js b/web/client/examples/featuregrid/plugins.js index 371b3fb6f9..84f87d9632 100644 --- a/web/client/examples/featuregrid/plugins.js +++ b/web/client/examples/featuregrid/plugins.js @@ -12,6 +12,7 @@ module.exports = { MapPlugin: require('../../plugins/Map'), WFSDownload: require('../../plugins/WFSDownload'), FeatureEditor: require('../../plugins/FeatureEditor'), + QueryPanel: require('../../plugins/QueryPanel'), Notifications: require('../../plugins/Notifications') }, requires: { diff --git a/web/client/examples/featuregrid/stores/store.js b/web/client/examples/featuregrid/stores/store.js index 380c5fa792..ff1b7629a4 100644 --- a/web/client/examples/featuregrid/stores/store.js +++ b/web/client/examples/featuregrid/stores/store.js @@ -9,7 +9,8 @@ const Rx = require('rxjs'); const {featureTypeSelectedEpic, wfsQueryEpic, viewportSelectedEpic} = require('../../../epics/wfsquery'); const {getLayerFromId} = require('../../../selectors/layers'); -const {createQuery, featureTypeSelected, layerSelectedForSearch, LAYER_SELECTED_FOR_SEARCH, FEATURE_TYPE_LOADED, CLOSE_FEATURE_GRID} = require('../../../actions/wfsquery'); +const {featureTypeSelected, layerSelectedForSearch, LAYER_SELECTED_FOR_SEARCH, CLOSE_FEATURE_GRID} = require('../../../actions/wfsquery'); +const {browseData} = require('../../../actions/layers'); const {clearChanges, setPermission, toggleTool} = require('../../../actions/featuregrid'); const {hasChangesSelector, hasNewFeaturesSelector} = require('../../../selectors/featuregrid'); module.exports = (plugins) => { @@ -42,26 +43,13 @@ module.exports = (plugins) => { ); }), createFeatureGridDemoQuery: (action$, store) => - Rx.Observable.zip( - action$.ofType(LAYER_SELECTED_FOR_SEARCH), - action$.ofType(FEATURE_TYPE_LOADED) - ).switchMap(([layer]) => Rx.Observable.of( - clearChanges(), - createQuery("http://demo.geo-solutions.it:80/geoserver/wfs", { - featureTypeName: getLayerFromId(store.getState(), layer.id).name, - groupFields: [ - - ], - filterFields: [], - pagination: { - maxFeatures: 20, - startIndex: 0 - }, - filterType: 'OGC', - ogcVersion: '1.1.0', - sortOptions: null, - hits: false - }) + action$.ofType(LAYER_SELECTED_FOR_SEARCH) + .switchMap((layer) => Rx.Observable.of( + clearChanges(), + browseData({ + ...getLayerFromId(store.getState(), layer.id), + url: 'http://demo.geo-solutions.it:80/geoserver/wfs' + }) )) }, plugins); }; diff --git a/web/client/reducers/__tests__/featuregrid-test.js b/web/client/reducers/__tests__/featuregrid-test.js index 1f72e26315..20db7e8331 100644 --- a/web/client/reducers/__tests__/featuregrid-test.js +++ b/web/client/reducers/__tests__/featuregrid-test.js @@ -47,7 +47,7 @@ const featuregrid = require('../featuregrid'); const {setFeatures, dockSizeFeatures, setLayer, toggleTool, customizeAttribute, selectFeatures, deselectFeatures, createNewFeatures, featureSaving, toggleSelection, clearSelection, MODES, toggleEditMode, toggleViewMode, saveSuccess, clearChanges, saveError, startDrawingFeature, deleteGeometryFeature, geometryChanged, setSelectionOptions, changePage, featureModified, setPermission, disableToolbar} = require('../../actions/featuregrid'); -const {featureTypeLoaded, closeFeatureGrid} = require('../../actions/wfsquery'); +const {featureTypeLoaded} = require('../../actions/wfsquery'); const {changeDrawingStatus} = require('../../actions/draw'); const museam = require('json-loader!../../test-resources/wfs/museam.json'); @@ -219,15 +219,7 @@ describe('Test the featuregrid reducer', () => { let state = featuregrid( {}, setPermission({canEdit: true})); expect(state.canEdit).toBe(true); }); - it('closeFeatureGrid', () => { - let state = featuregrid( {pagination: {size: 3}}, closeFeatureGrid()); - expect(state.drawing).toBe(false); - expect(state.deleteConfirm).toBe(false); - expect(state.pagination.size).toBe(3); - expect(state.newFeatures.length).toBe(0); - expect(state.changes.length).toBe(0); - expect(state.select.length).toBe(0); - }); + it('CHANGE_DRAWING_STATUS', () => { let state = featuregrid( {}, changeDrawingStatus("clean")); expect(state.drawing).toBe(false); diff --git a/web/client/selectors/__tests__/featuregrid-test.js b/web/client/selectors/__tests__/featuregrid-test.js index fcc136187b..7c6538a1b0 100644 --- a/web/client/selectors/__tests__/featuregrid-test.js +++ b/web/client/selectors/__tests__/featuregrid-test.js @@ -250,7 +250,6 @@ let initialState = { } }, resultError: null, - open: true, isNew: false, filterObj: { featureTypeName: 'editing:polygons', @@ -304,6 +303,7 @@ describe('Test featuregrid selectors', () => { afterEach(() => { initialState = assign({}, initialState, { featuregrid: { + open: true, saving: false, saved: false, selectedLayer: "TEST_LAYER", From d1d83215fd56a240c087e1af5b85a621f7b1fdcb Mon Sep 17 00:00:00 2001 From: Lorenzo Natali Date: Mon, 7 Aug 2017 09:45:56 +0200 Subject: [PATCH 5/8] fixed featuregrid selector test --- web/client/selectors/__tests__/featuregrid-test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/web/client/selectors/__tests__/featuregrid-test.js b/web/client/selectors/__tests__/featuregrid-test.js index 7c6538a1b0..e44fef435f 100644 --- a/web/client/selectors/__tests__/featuregrid-test.js +++ b/web/client/selectors/__tests__/featuregrid-test.js @@ -282,6 +282,7 @@ let initialState = { featureLoading: false }, featuregrid: { + open: true, selectedLayer: "TEST_LAYER", mode: modeEdit, select: [feature1, feature2], From 736310a2bc3b02dfe8c391d8871b446f3b1f618c Mon Sep 17 00:00:00 2001 From: Lorenzo Natali Date: Mon, 7 Aug 2017 09:54:20 +0200 Subject: [PATCH 6/8] restored test.webpack.js --- tests.webpack.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests.webpack.js b/tests.webpack.js index bb80054b3f..1fd499071c 100644 --- a/tests.webpack.js +++ b/tests.webpack.js @@ -1,3 +1,3 @@ -var context = require.context('./web/client/reducers', true, /(-test\.jsx?)|(-test-chrome\.jsx?)$/); +var context = require.context('./web', true, /(-test\.jsx?)|(-test-chrome\.jsx?)$/); context.keys().forEach(context); module.exports = context; From cad2db7a7f9b7727441b9e0d8496e13601ce993d Mon Sep 17 00:00:00 2001 From: Lorenzo Natali Date: Mon, 7 Aug 2017 12:28:19 +0200 Subject: [PATCH 7/8] general clean up and minor changes --- web/client/actions/wfsquery.js | 31 +---------------- web/client/epics/featuregrid.js | 33 ++++++++++++++----- web/client/epics/wfsquery.js | 10 ++++-- .../examples/featuregrid/stores/store.js | 6 ++-- .../queryform/components/SmartQueryForm.jsx | 12 +++++++ .../queryform/containers/QueryForm.jsx | 2 +- web/client/plugins/QueryPanel.jsx | 6 ++-- web/client/plugins/TOC.jsx | 3 +- 8 files changed, 54 insertions(+), 49 deletions(-) diff --git a/web/client/actions/wfsquery.js b/web/client/actions/wfsquery.js index 69aaff3a59..c158bd138c 100644 --- a/web/client/actions/wfsquery.js +++ b/web/client/actions/wfsquery.js @@ -19,9 +19,6 @@ const RESET_QUERY = 'RESET_QUERY'; const QUERY = 'QUERY'; const axios = require('../libs/ajax'); -const {toggleControl, setControlProperty} = require('./controls'); -const {changeDrawingStatus} = require('./draw'); -const {reset} = require('./queryform'); function layerSelectedForSearch(id) { return { @@ -131,30 +128,6 @@ function resetQuery() { }; } -function toggleQueryPanel(url, name, id) { - return (dispatch, getState) => { - if (getState().query.typeName !== name) { - dispatch(reset()); - } - dispatch(changeDrawingStatus('clean', null, "queryform", [])); - dispatch(featureTypeSelected(url, name)); - dispatch(toggleControl('queryPanel', null)); - dispatch(setControlProperty('drawer', 'width', getState().controls.queryPanel.enabled ? 700 : 300)); - dispatch(layerSelectedForSearch(id)); - - }; -} -function closeResponse() { - return (dispatch, getState) => { - // dispatch(featureClose()); - let state = getState(); - if (state.controls && state.controls.queryPanel && state.controls.drawer && !state.controls.drawer.enabled) { - dispatch(setControlProperty('drawer', 'enabled', true)); - dispatch(setControlProperty('drawer', 'disabled', false)); - } - }; -} - module.exports = { LAYER_SELECTED_FOR_SEARCH, layerSelectedForSearch, FEATURE_TYPE_SELECTED, featureTypeSelected, @@ -168,7 +141,5 @@ module.exports = { QUERY, query, FEATURE_LOADING, featureLoading, FEATURE_LOADED, featureLoaded, - loadFeature, - toggleQueryPanel, - closeResponse + loadFeature }; diff --git a/web/client/epics/featuregrid.js b/web/client/epics/featuregrid.js index 61a49a0256..6f81d110e0 100644 --- a/web/client/epics/featuregrid.js +++ b/web/client/epics/featuregrid.js @@ -18,7 +18,7 @@ const requestBuilder = require('../utils/ogc/WFST/RequestBuilder'); const {findGeometryProperty} = require('../utils/ogc/WFS/base'); const {setControlProperty} = require('../actions/controls'); const {query, QUERY_CREATE, QUERY_RESULT, LAYER_SELECTED_FOR_SEARCH, FEATURE_TYPE_LOADED, featureTypeSelected, createQuery} = require('../actions/wfsquery'); -const {reset} = require('../actions/queryform'); +const {reset, QUERY_FORM_RESET} = require('../actions/queryform'); const {BROWSE_DATA} = require('../actions/layers'); const {parseString} = require('xml2js'); const {stripPrefix} = require('xml2js/lib/processors'); @@ -165,7 +165,7 @@ const createInitialQueryFlow = (action$, store, {url, name} = {}) => { }); if (isDescribeLoaded(store.getState(), name)) { - return Rx.Observable.of(createInitialQuery()); + return Rx.Observable.of(createInitialQuery(), featureTypeSelected(url, name)); } return Rx.Observable.of(featureTypeSelected(url, name)).merge( action$.ofType(FEATURE_TYPE_LOADED).filter(({typeName} = {}) => typeName === name) @@ -442,18 +442,35 @@ module.exports = { .switchMap( () => { return Rx.Observable.of(setControlProperty("drawer", "enabled", false), toggleTool("featureCloseConfirm", false)); }), - onOpenAdvancedSearch: action$ => + onOpenAdvancedSearch: (action$, store) => action$.ofType(OPEN_ADVANCED_SEARCH).switchMap(() => Rx.Observable.of( setControlProperty('queryPanel', "enabled", true), closeFeatureGrid() ).merge( - action$.ofType(QUERY_CREATE).switchMap(() => - Rx.Observable.of( - setControlProperty('queryPanel', "enabled", false), - openFeatureGrid() + action$.ofType(QUERY_FORM_RESET) // ON RESET YOU HAVE TO PERFORM A SEARCH AGAIN WHEN BACK + .switchMap(() => action$.ofType(TOGGLE_CONTROL) + .filter(({control, property} = {}) => control === "queryPanel" && (!property || property === "enabled")) + .switchMap( () => createInitialQueryFlow(action$, store, { + url: get(store.getState(), "query.url"), + name: get(store.getState(), "query.typeName") + })))) + .merge( + Rx.Observable.race( + action$.ofType(QUERY_CREATE).mergeMap(() => + Rx.Observable.of( + setControlProperty('queryPanel', "enabled", false), + openFeatureGrid() + ) + ), + action$.ofType(TOGGLE_CONTROL) + .filter(({control, property} = {}) => control === "queryPanel" && (!property || property === "enabled")) + .mergeMap(() => + Rx.Observable.of( + openFeatureGrid() + ) ) - ) + ).takeUntil(action$.ofType(OPEN_FEATURE_GRID, LOCATION_CHANGE)) ) ) }; diff --git a/web/client/epics/wfsquery.js b/web/client/epics/wfsquery.js index a8fd8e127a..3b5c902721 100644 --- a/web/client/epics/wfsquery.js +++ b/web/client/epics/wfsquery.js @@ -12,7 +12,7 @@ const Url = require('url'); const {changeSpatialAttribute, SELECT_VIEWPORT_SPATIAL_METHOD, updateGeometrySpatialField} = require('../actions/queryform'); const {CHANGE_MAP_VIEW} = require('../actions/map'); const {FEATURE_TYPE_SELECTED, QUERY, featureLoading, featureTypeLoaded, featureTypeError, querySearchResponse, queryError} = require('../actions/wfsquery'); -const {paginationInfo} = require('../selectors/query'); +const {paginationInfo, isDescribeLoaded, describeSelector} = require('../selectors/query'); const FilterUtils = require('../utils/FilterUtils'); const assign = require('object-assign'); @@ -196,10 +196,16 @@ const retryWithForcedSortOptions = (action, store) => { * @return {external:Observable} */ -const featureTypeSelectedEpic = action$ => +const featureTypeSelectedEpic = (action$, store) => action$.ofType(FEATURE_TYPE_SELECTED) .filter(action => action.url && action.typeName) .switchMap(action => { + const state = store.getState(); + if (isDescribeLoaded(state, action.typeName)) { + const info = extractInfo(describeSelector(state)); + const geometry = info.geometry[0] && info.geometry[0].attribute ? info.geometry[0].attribute : 'the_geom'; + return Rx.Observable.of(changeSpatialAttribute(geometry)); + } return Rx.Observable.defer( () => axios.get(action.url + '?service=WFS&version=1.1.0&request=DescribeFeatureType&typeName=' + action.typeName + '&outputFormat=application/json')) .map((response) => { if (typeof response.data === 'object' && response.data.featureTypes && response.data.featureTypes[0]) { diff --git a/web/client/examples/featuregrid/stores/store.js b/web/client/examples/featuregrid/stores/store.js index ff1b7629a4..45e292b452 100644 --- a/web/client/examples/featuregrid/stores/store.js +++ b/web/client/examples/featuregrid/stores/store.js @@ -32,14 +32,12 @@ module.exports = (plugins) => { return Rx.Observable.of(toggleTool("featureCloseConfirm", true)) .merge(action$.ofType(CLOSE_FEATURE_GRID).switchMap( () => Rx.Observable.of( layerSelectedForSearch(id), - setPermission({canEdit: true}), - featureTypeSelected( 'http://demo.geo-solutions.it:80/geoserver/wfs', id), + setPermission({canEdit: true}) ))); } return Rx.Observable.of( layerSelectedForSearch(id), - setPermission({canEdit: true}), - featureTypeSelected( 'http://demo.geo-solutions.it:80/geoserver/wfs', id), + setPermission({canEdit: true}) ); }), createFeatureGridDemoQuery: (action$, store) => diff --git a/web/client/examples/queryform/components/SmartQueryForm.jsx b/web/client/examples/queryform/components/SmartQueryForm.jsx index 57e6700712..70cf58fb9c 100644 --- a/web/client/examples/queryform/components/SmartQueryForm.jsx +++ b/web/client/examples/queryform/components/SmartQueryForm.jsx @@ -68,6 +68,18 @@ const SmartQueryForm = connect((state) => { filterFields: state.queryform.filterFields, attributes: attributesSelector(state), spatialField: state.queryform.spatialField, + spatialOperations: [ + {"id": "INTERSECTS", "name": "queryform.spatialfilter.operations.intersects"}, + {"id": "BBOX", "name": "queryform.spatialfilter.operations.bbox"}, + {"id": "CONTAINS", "name": "queryform.spatialfilter.operations.contains"}, + {"id": "WITHIN", "name": "queryform.spatialfilter.operations.within"} + ], + spatialMethodOptions: [ + {"id": "Viewport", "name": "queryform.spatialfilter.methods.viewport"}, + {"id": "BBOX", "name": "queryform.spatialfilter.methods.box"}, + {"id": "Circle", "name": "queryform.spatialfilter.methods.circle"}, + {"id": "Polygon", "name": "queryform.spatialfilter.methods.poly"} + ], showDetailsPanel: state.queryform.showDetailsPanel, toolbarEnabled: state.queryform.toolbarEnabled, attributePanelExpanded: state.queryform.attributePanelExpanded, diff --git a/web/client/examples/queryform/containers/QueryForm.jsx b/web/client/examples/queryform/containers/QueryForm.jsx index d2d6972ec1..f4199dfdc8 100644 --- a/web/client/examples/queryform/containers/QueryForm.jsx +++ b/web/client/examples/queryform/containers/QueryForm.jsx @@ -44,7 +44,7 @@ class QueryForm extends React.Component {
- +
diff --git a/web/client/plugins/QueryPanel.jsx b/web/client/plugins/QueryPanel.jsx index 4429b7405b..ca97eb94ce 100644 --- a/web/client/plugins/QueryPanel.jsx +++ b/web/client/plugins/QueryPanel.jsx @@ -8,6 +8,7 @@ const PropTypes = require('prop-types'); */ const React = require('react'); const {connect} = require('react-redux'); +const {Button, Glyphicon} = require('react-bootstrap'); const Sidebar = require('react-sidebar').default; const {createSelector} = require('reselect'); const {changeLayerProperties, changeGroupProperties, toggleNode, @@ -27,7 +28,7 @@ const LayersUtils = require('../utils/LayersUtils'); const QueryBuilder = require('../components/data/query/QueryBuilder'); const {featureTypeSelectedEpic, wfsQueryEpic, viewportSelectedEpic} = require('../epics/wfsquery'); - +const autocompleteEpics = require('../epics/autocomplete'); const {bindActionCreators} = require('redux'); const { // QueryBuilder action functions @@ -225,6 +226,7 @@ class QueryPanel extends React.Component { renderQueryPanel = () => { return (
+ Date: Mon, 7 Aug 2017 12:55:53 +0200 Subject: [PATCH 8/8] updated tests and example --- .../actions/__tests__/featuregrid-test.js | 18 ++++++++++++++++++ web/client/actions/__tests__/layers-test.js | 10 +++++++++- .../examples/featuregrid/stores/store.js | 2 +- .../reducers/__tests__/featuregrid-test.js | 13 ++++++++++++- web/client/selectors/__tests__/query-test.js | 5 +++++ 5 files changed, 45 insertions(+), 3 deletions(-) diff --git a/web/client/actions/__tests__/featuregrid-test.js b/web/client/actions/__tests__/featuregrid-test.js index cb01e42339..27eb6321e5 100644 --- a/web/client/actions/__tests__/featuregrid-test.js +++ b/web/client/actions/__tests__/featuregrid-test.js @@ -28,6 +28,9 @@ const { deleteGeometryFeature, DELETE_GEOMETRY_FEATURE, clearChangeConfirmed, CLEAR_CHANGES_CONFIRMED, deleteFeaturesConfirm, DELETE_SELECTED_FEATURES_CONFIRM, + openFeatureGrid, OPEN_FEATURE_GRID, + closeFeatureGrid, CLOSE_FEATURE_GRID, + closeFeatureGridConfirm, CLOSE_FEATURE_GRID_CONFIRM, closeFeatureGridConfirmed, FEATURE_GRID_CLOSE_CONFIRMED } = require('../featuregrid'); @@ -175,6 +178,21 @@ describe('Test correctness of featurgrid actions', () => { expect(retval).toExist(); expect(retval.type).toBe(DELETE_SELECTED_FEATURES_CONFIRM); }); + it('Test openFeatureGrid', () => { + const retval = openFeatureGrid(); + expect(retval).toExist(); + expect(retval.type).toBe(OPEN_FEATURE_GRID); + }); + it('Test closeFeatureGrid', () => { + const retval = closeFeatureGrid(); + expect(retval).toExist(); + expect(retval.type).toBe(CLOSE_FEATURE_GRID); + }); + it('Test closeFeatureGridConfirm', () => { + const retval = closeFeatureGridConfirm(); + expect(retval).toExist(); + expect(retval.type).toBe(CLOSE_FEATURE_GRID_CONFIRM); + }); it('Test saveError', () => { const retval = saveError(); expect(retval).toExist(); diff --git a/web/client/actions/__tests__/layers-test.js b/web/client/actions/__tests__/layers-test.js index eb431be3f0..2f8c02d72a 100644 --- a/web/client/actions/__tests__/layers-test.js +++ b/web/client/actions/__tests__/layers-test.js @@ -24,6 +24,7 @@ var { REFRESH_LAYERS, LAYERS_REFRESHED, LAYERS_REFRESH_ERROR, + BROWSE_DATA, changeLayerProperties, toggleNode, sortNode, @@ -39,7 +40,8 @@ var { updateSettings, refreshLayers, layersRefreshed, - layersRefreshError + layersRefreshError, + browseData } = require('../layers'); var {getLayerCapabilities} = require('../layerCapabilities'); @@ -223,4 +225,10 @@ describe('Test correctness of the layers actions', () => { } }); }); + it('browseData', () => { + const layer = {id: "TEST", name: "test", url: "test"}; + const action = browseData(layer); + expect(action.type).toBe(BROWSE_DATA); + expect(action.layer).toBe(layer); + }); }); diff --git a/web/client/examples/featuregrid/stores/store.js b/web/client/examples/featuregrid/stores/store.js index 45e292b452..c91a37bb87 100644 --- a/web/client/examples/featuregrid/stores/store.js +++ b/web/client/examples/featuregrid/stores/store.js @@ -9,7 +9,7 @@ const Rx = require('rxjs'); const {featureTypeSelectedEpic, wfsQueryEpic, viewportSelectedEpic} = require('../../../epics/wfsquery'); const {getLayerFromId} = require('../../../selectors/layers'); -const {featureTypeSelected, layerSelectedForSearch, LAYER_SELECTED_FOR_SEARCH, CLOSE_FEATURE_GRID} = require('../../../actions/wfsquery'); +const {layerSelectedForSearch, LAYER_SELECTED_FOR_SEARCH, CLOSE_FEATURE_GRID} = require('../../../actions/wfsquery'); const {browseData} = require('../../../actions/layers'); const {clearChanges, setPermission, toggleTool} = require('../../../actions/featuregrid'); const {hasChangesSelector, hasNewFeaturesSelector} = require('../../../selectors/featuregrid'); diff --git a/web/client/reducers/__tests__/featuregrid-test.js b/web/client/reducers/__tests__/featuregrid-test.js index 20db7e8331..e1ddb91926 100644 --- a/web/client/reducers/__tests__/featuregrid-test.js +++ b/web/client/reducers/__tests__/featuregrid-test.js @@ -46,7 +46,7 @@ const expect = require('expect'); const featuregrid = require('../featuregrid'); const {setFeatures, dockSizeFeatures, setLayer, toggleTool, customizeAttribute, selectFeatures, deselectFeatures, createNewFeatures, featureSaving, toggleSelection, clearSelection, MODES, toggleEditMode, toggleViewMode, saveSuccess, clearChanges, saveError, startDrawingFeature, - deleteGeometryFeature, geometryChanged, setSelectionOptions, changePage, featureModified, setPermission, disableToolbar} = require('../../actions/featuregrid'); + deleteGeometryFeature, geometryChanged, setSelectionOptions, changePage, featureModified, setPermission, disableToolbar, openFeatureGrid, closeFeatureGrid} = require('../../actions/featuregrid'); const {featureTypeLoaded} = require('../../actions/wfsquery'); const {changeDrawingStatus} = require('../../actions/draw'); @@ -64,6 +64,17 @@ describe('Test the featuregrid reducer', () => { expect(state.select).toExist(); expect(state.features).toExist(); }); + it('openFeatureGrid', () => { + let state = featuregrid(undefined, openFeatureGrid()); + expect(state).toExist(); + expect(state.open).toBe(true); + }); + it('closeFeatureGrid', () => { + let state = featuregrid(undefined, closeFeatureGrid()); + expect(state).toExist(); + expect(state.open).toBe(false); + expect(state.mode).toBe(MODES.VIEW); + }); it('selectFeature', () => { // TODO FIX this test or the reducer diff --git a/web/client/selectors/__tests__/query-test.js b/web/client/selectors/__tests__/query-test.js index c80be10acd..6d50497e25 100644 --- a/web/client/selectors/__tests__/query-test.js +++ b/web/client/selectors/__tests__/query-test.js @@ -13,6 +13,7 @@ const { resultsSelector, paginationInfo, featureLoadingSelector, + isDescribeLoaded, describeSelector, getFeatureById, attributesSelector @@ -322,6 +323,10 @@ describe('Test query selectors', () => { const describe = describeSelector(initialState); expect(describe.elementFormDefault).toBe("qualified"); }); + it('test isDescribeLoaded', () => { + const isLoaded = isDescribeLoaded(initialState, "editing:polygons"); + expect(isLoaded).toBe(true); + }); it('test getFeatureById selector', () => { const ft = getFeatureById(initialState, "poligoni.7"); expect(ft).toExist();