Skip to content

Commit

Permalink
Fix #9098 dashboard filter capabilities (#9514)
Browse files Browse the repository at this point in the history
  • Loading branch information
MV88 authored Oct 13, 2023
1 parent 608cd5c commit 6856256
Show file tree
Hide file tree
Showing 35 changed files with 628 additions and 83 deletions.
36 changes: 36 additions & 0 deletions docs/developer-guide/mapstore-migration-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,42 @@ This is a list of things to check if you want to update from a previous version

## Migration from 2023.02.xx to 2024.01.00

### Adding spatial filter to dashboard widgets

In order to enable the possibility to add in and the spatial filter to the widgets ( see [#9098](https://github.com/geosolutions-it/MapStore2/issues/9098) ) you have to edit the `QueryPanel` config in the `plugins.dashboard` array of the `localConfig.json` file by adding:

- **useEmbeddedMap**: flag to enable the embedded map
- **spatialOperations**: The list of spatial operations allowed for this plugin
- **spatialMethodOptions**: the list of spatial methods to use.

```json
...
"dashboard": [
...
{
"name": "QueryPanel",
"cfg": {
"toolsOptions": {
"hideCrossLayer": true,
"useEmbeddedMap": true
},
"spatialPanelExpanded": false,
"spatialOperations": [
{"id": "INTERSECTS", "name": "queryform.spatialfilter.operations.intersects"},
{"id": "CONTAINS", "name": "queryform.spatialfilter.operations.contains"},
{"id": "WITHIN", "name": "queryform.spatialfilter.operations.within"}
],
"spatialMethodOptions": [
{"id": "BBOX", "name": "queryform.spatialfilter.methods.box"},
{"id": "Circle", "name": "queryform.spatialfilter.methods.circle"},
{"id": "Polygon", "name": "queryform.spatialfilter.methods.poly"}
],
"containerPosition": "columns"
}
}

```

### MapFish Print update

The **MapFish Print** library has been updated to work with the latest GeoTools version and Java 11 as well as being aligned with the same dependency used by the official GeoServer printing extension (see this issue <https://github.com/geosolutions-it/mapfish-print/issues/65>)
Expand Down
11 changes: 10 additions & 1 deletion web/client/actions/__tests__/queryform-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,20 @@ import {
changeSpatialFilterValue,
updateCrossLayerFilterFieldOptions,
upsertFilters,
removeFilters
changeMapEditor,
removeFilters,
CHANGE_MAP_EDITOR
} from '../queryform';

describe('Test correctness of the queryform actions', () => {

it('changeMapEditor', () => {
var retval = changeMapEditor(null);

expect(retval).toExist();
expect(retval.type).toBe(CHANGE_MAP_EDITOR);
expect(retval.mapData).toBe(null);
});
it('addFilterField', () => {
let groupId = 1;

Expand Down
2 changes: 1 addition & 1 deletion web/client/actions/__tests__/widgets-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ import {
DEPENDENCY_SELECTOR_KEY,
TOGGLE_TRAY,
TOGGLE_MAXIMIZE,
createChart,
NEW_CHART,
createChart,
exportCSV,
exportImage,
openFilterEditor,
Expand Down
11 changes: 11 additions & 0 deletions web/client/actions/queryform.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,19 @@ export const LOAD_FILTER = 'QUERYFORM:LOAD_FILTER';
export const UPSERT_FILTERS = 'QUERYFORM:UPSERT_FILTERS';
export const REMOVE_FILTERS = 'QUERYFORM:REMOVE_FILTERS';

export const CHANGE_MAP_EDITOR = "QUERYFORM:CHANGE_MAP_EDITOR";

import axios from '../libs/ajax';

/**
* Changes the map config to be used by query form for creating spatial filters
* @param {object} mapData the new map data
*/
export const changeMapEditor = (mapData) => ({
type: CHANGE_MAP_EDITOR,
mapData
});

export function addFilterField(groupId) {
return {
type: ADD_FILTER_FIELD,
Expand Down
23 changes: 13 additions & 10 deletions web/client/components/data/query/QueryBuilder.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -207,16 +207,19 @@ class QueryBuilder extends React.Component {
/></div>);
const { spatialMethodOptions, toolsOptions, spatialOperations} = this.props;
return this.props.attributes.length > 0 ?
<BorderLayout header={header} className="mapstore-query-builder" id="query-form-panel">
{this.renderItems('start', { spatialOperations, spatialMethodOptions, ...toolsOptions })}
{this.renderItems('attributes', { spatialOperations, spatialMethodOptions, ...toolsOptions })}
{this.renderItems('afterAttributes', { spatialOperations, spatialMethodOptions, ...toolsOptions })}
{this.renderItems('spatial', { spatialOperations, spatialMethodOptions, ...toolsOptions })}
{this.renderItems('afterSpatial', { spatialOperations, spatialMethodOptions, ...toolsOptions })}
{this.renderItems('layers', { spatialOperations, spatialMethodOptions, ...toolsOptions })}
{this.renderItems('end', { spatialOperations, spatialMethodOptions, ...toolsOptions })}
</BorderLayout>
: <div style={{margin: "0 auto", width: "60px"}}><Spinner spinnerName="three-bounce" overrideSpinnerClassName="spinner"/></div>;
<>
<BorderLayout header={header} className="mapstore-query-builder" id="query-form-panel">
{this.renderItems('start', { spatialOperations, spatialMethodOptions, ...toolsOptions })}
{this.renderItems('attributes', { spatialOperations, spatialMethodOptions, ...toolsOptions })}
{this.renderItems('afterAttributes', { spatialOperations, spatialMethodOptions, ...toolsOptions })}
{this.renderItems('spatial', { spatialOperations, spatialMethodOptions, ...toolsOptions })}
{this.renderItems('afterSpatial', { spatialOperations, spatialMethodOptions, ...toolsOptions })}
{this.renderItems('layers', { spatialOperations, spatialMethodOptions, ...toolsOptions })}
{this.renderItems('end', { spatialOperations, spatialMethodOptions, ...toolsOptions })}
</BorderLayout>
{this.renderItems('map', { spatialOperations, spatialMethodOptions, ...toolsOptions })}
</>
: <div className="spinner-panel" style={{margin: "0 auto", width: "60px"}}><Spinner spinnerName="three-bounce" overrideSpinnerClassName="spinner"/></div>;
}

filterItem = (target, layerName) => (el) => {
Expand Down
4 changes: 2 additions & 2 deletions web/client/components/data/query/SpatialFilter.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,8 @@ class SpatialFilter extends React.Component {
{this.props.spatialMethodOptions.length > 1 ? this.renderSpatialHeader() : <span/>}
{this.renderZoneFields()}
{this.props.spatialField.method
&& this.getMethodFromId(this.props.spatialField.method)
&& this.getMethodFromId(this.props.spatialField.method).type === "wfsGeocoder"
&& this.getMethodFromId(this.props.spatialField.method)
&& this.getMethodFromId(this.props.spatialField.method).type === "wfsGeocoder"
? this.renderRoiPanel()
: null}
{this.props.spatialOperations.length > 1 ?
Expand Down
71 changes: 61 additions & 10 deletions web/client/components/data/query/__tests__/QueryBuilder-test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,19 @@ import expect from 'expect';
import React from 'react';
import ReactDOM from 'react-dom';
import TestUtils from 'react-dom/test-utils';

import {Provider} from 'react-redux';
import QueryBuilder from '../QueryBuilder';
import standardItemsReference from "../../../../plugins/querypanel/index";
import SwitchPanel from "../../../misc/switch/SwitchPanel";
import configureMockStore from 'redux-mock-store';
const mockStore = configureMockStore();

const standardItems = Object.keys(standardItemsReference).reduce((prev, cur) => {
return {...prev, [cur]: standardItemsReference[cur].map(el => ({
...el,
plugin: () => <SwitchPanel key={el.id} />
}))};
}, {});

describe('QueryBuilder', () => {

let store;
beforeEach((done) => {
store = mockStore({
queryform: {}
});
document.body.innerHTML = '<div id="container"></div>';
setTimeout(done);
});
Expand All @@ -34,7 +32,13 @@ describe('QueryBuilder', () => {
document.body.innerHTML = '';
setTimeout(done);
});

const standardItems = Object.keys(standardItemsReference).reduce((prev, cur) => {
return {...prev, [cur]: standardItemsReference[cur].map(el => ({
...el,
plugin: el.plugin ? (props) => <Provider store={store}><el.plugin {...props} /></Provider> : undefined,
component: el.component ? (props) => <Provider store={store}><el.component {...props} /></Provider> : undefined
}))};
}, {});
it('creates the QueryBuilder component with his default content', () => {
const querybuilder = ReactDOM.render(<QueryBuilder/>, document.getElementById("container"));
expect(querybuilder).toExist();
Expand Down Expand Up @@ -198,6 +202,53 @@ describe('QueryBuilder', () => {
// only attribute filter should be shown
expect(document.querySelectorAll('.mapstore-switch-panel').length).toBe(1);
});
it('useEmbeddedMap', () => {
const groupLevels = 5;

const groupFields = [];

const filterFields = [{
rowId: 100,
groupId: 1,
attribute: "",
operator: null,
value: null,
exception: null
}];

const attributes = [{
id: "Attribute",
type: "list",
values: [
"attribute1",
"attribute2",
"attribute3",
"attribute4",
"attribute5"
]
}];

const querybuilder = ReactDOM.render(
<QueryBuilder
queryPanelEnabled
toolsOptions={{
hideCrossLayer: true,
hideSpatialFilter: false,
useEmbeddedMap: true
}}
filterFields={filterFields}
attributes={attributes}
groupFields={groupFields}
groupLevels={groupLevels}
standardItems={standardItems}
/>,
document.getElementById("container")
);
expect(querybuilder).toExist();
// only attribute filter should be shown
expect(document.querySelectorAll('.mapstore-switch-panel').length).toBe(2);
expect(document.querySelectorAll('.mapstore-query-map').length).toBe(1);
});

it('creates the QueryBuilder component in error state', () => {

Expand Down
8 changes: 5 additions & 3 deletions web/client/components/map/BaseMap.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ class BaseMap extends React.Component {
plugins: PropTypes.any,
tools: PropTypes.array,
getLayerProps: PropTypes.func,
env: PropTypes.array
env: PropTypes.array,
zoomControl: PropTypes.bool
};

static defaultProps = {
Expand All @@ -60,7 +61,8 @@ class BaseMap extends React.Component {
onLayerLoading: () => {},
onLayerError: () => {}
},
env: []
env: [],
zoomControl: false
};

getTool = (tool) => {
Expand Down Expand Up @@ -143,7 +145,7 @@ class BaseMap extends React.Component {
projectionDefs={this.props.projectionDefs}
style={this.props.styleMap}
id={this.props.id}
zoomControl={false}
zoomControl={this.props.zoomControl}
center={{ x: 0, y: 0 }}
zoom={1}
hookRegister={this.props.hookRegister}
Expand Down
4 changes: 2 additions & 2 deletions web/client/components/widgets/builder/wizard/ChartWizard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ const ChartWizard = ({
hideButtons
className={"chart-options"}>
{[ChartOptions, WidgetOptions].map(component =>
<>
(<>
<StepHeader step={step}/>
<ChartSwitcher
key="chart-switcher"
Expand All @@ -244,7 +244,7 @@ const ChartWizard = ({
>
{component}
</ChartSwitcher>
</>
</>)
)}
</Wizard>);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,14 +140,14 @@ const ColorClassModal = ({

ColorClassModal.propTypes = {
modalClassName: PropTypes.string,
show: PropTypes.boolean,
show: PropTypes.bool,
onClose: PropTypes.func,
onSaveClassification: PropTypes.func,
onChangeClassAttribute: PropTypes.func,
classificationAttribute: PropTypes.string,
onUpdateClasses: PropTypes.func,
options: PropTypes.array,
placeHolder: PropTypes.string,
placeHolder: PropTypes.oneOfType(PropTypes.string, PropTypes.object),
classification: PropTypes.array,
rangeClassification: PropTypes.array,
defaultCustomColor: PropTypes.string,
Expand Down
13 changes: 12 additions & 1 deletion web/client/configs/localConfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -814,8 +814,19 @@
"cfg": {
"toolsOptions": {
"hideCrossLayer": true,
"hideSpatialFilter": true
"useEmbeddedMap": true
},
"spatialPanelExpanded": false,
"spatialOperations": [
{"id": "INTERSECTS", "name": "queryform.spatialfilter.operations.intersects"},
{"id": "CONTAINS", "name": "queryform.spatialfilter.operations.contains"},
{"id": "WITHIN", "name": "queryform.spatialfilter.operations.within"}
],
"spatialMethodOptions": [
{"id": "BBOX", "name": "queryform.spatialfilter.methods.box"},
{"id": "Circle", "name": "queryform.spatialfilter.methods.circle"},
{"id": "Polygon", "name": "queryform.spatialfilter.methods.poly"}
],
"containerPosition": "columns"
}
},
Expand Down
Loading

0 comments on commit 6856256

Please sign in to comment.