Skip to content

Commit

Permalink
Add support for map sync (#2783)
Browse files Browse the repository at this point in the history
  • Loading branch information
offtherailz authored Mar 29, 2018
1 parent ea9e64c commit 6825a35
Show file tree
Hide file tree
Showing 16 changed files with 218 additions and 23 deletions.
4 changes: 2 additions & 2 deletions web/client/components/widgets/builder/wizard/map/Toolbar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const getSaveTooltipId = (step, { id } = {}) => {
return "widgets.builder.wizard.addToTheMap";
};

module.exports = ({ step = 0, buttons, tocButtons = [], editorData = {}, setPage = () => { }, onFinish = () => { }, toggleLayerSelector = () => { } } = {}) => (<Toolbar btnDefaultProps={{
module.exports = ({ step = 0, buttons, tocButtons = [], stepButtons = [], editorData = {}, setPage = () => { }, onFinish = () => { }, toggleLayerSelector = () => { } } = {}) => (<Toolbar btnDefaultProps={{
bsStyle: "primary",
bsSize: "sm"
}}
Expand All @@ -31,7 +31,7 @@ module.exports = ({ step = 0, buttons, tocButtons = [], editorData = {}, setPage
visible: step === 1,
glyph: "arrow-left",
tooltipId: "widgets.builder.wizard.configureMapOptions"
}, {
}, ...stepButtons, {
onClick: () => setPage(Math.min(step + 1, 2)),
visible: step === 0,
glyph: "arrow-right",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright 2018, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
const React = require('react');
const ReactDOM = require('react-dom');
const {createSink} = require('recompose');
const expect = require('expect');
const dependenciesToMapProp = require('../dependenciesToMapProp');

describe('dependenciesToMapProp enhancer', () => {
beforeEach((done) => {
document.body.innerHTML = '<div id="container"></div>';
setTimeout(done);
});
afterEach((done) => {
ReactDOM.unmountComponentAtNode(document.getElementById("container"));
document.body.innerHTML = '';
setTimeout(done);
});
it('dependenciesToMapProp rendering with defaults', (done) => {
const Sink = dependenciesToMapProp('center')(createSink( props => {
expect(props.map.center.x).toBe(1);
expect(props.map.center.y).toBe(1);
done();
}));
ReactDOM.render(<Sink map={{center: {x: 1, y: 1}}} dependencies={{center: {x: 2, y: 2}}}/>, document.getElementById("container"));
});
it('dependenciesToMapProp rendering with mapSync', (done) => {
const Sink = dependenciesToMapProp('center')(createSink(props => {
expect(props.map.center.x).toBe(2);
expect(props.map.center.y).toBe(2);
done();
}));
ReactDOM.render(<Sink mapSync map={{ center: { x: 1, y: 1 } }} dependencies={{ center: { x: 2, y: 2 } }} />, document.getElementById("container"));
});
});
24 changes: 24 additions & 0 deletions web/client/components/widgets/enhancers/dependenciesToMapProp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright 2018, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
const {set} = require('../../../utils/ImmutableUtils');
const {shallowEqual, branch, withPropsOnChange} = require('recompose');
/**
* Syncs map center
*/
module.exports = (prop) => branch(
(({mapSync} = {}) => mapSync),
withPropsOnChange(
({ mapSync, dependencies = {} } = {}, { mapSync: newMapSync, dependencies: newDependencies }) =>
newDependencies && shallowEqual(dependencies[prop], newDependencies[prop])
|| mapSync === newMapSync,
({ map, mapSync, dependencies = {} }) => ({
mapStateSource: "__dependency_system__",
map: dependencies[prop] && mapSync ? set(prop, dependencies[prop], map) : map
})
)
);
8 changes: 7 additions & 1 deletion web/client/components/widgets/widget/DefaultWidget.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,20 @@ const wpsChart = require('../enhancers/wpsChart');
const {compose} = require('recompose');
const dependenciesToFilter = require('../enhancers/dependenciesToFilter');
const dependenciesToWidget = require('../enhancers/dependenciesToWidget');
const dependenciesToMapProp = require('../enhancers/dependenciesToMapProp');
const ChartWidget = compose(
dependenciesToWidget,
dependenciesToFilter,
wpsChart,
enhanceChartWidget
)(require('./ChartWidget'));
const TextWidget = deleteWidget(require('./TextWidget'));
const MapWidget = deleteWidget(require('./MapWidget'));
const MapWidget = compose(
dependenciesToWidget,
dependenciesToMapProp('center'),
dependenciesToMapProp('zoom'),
deleteWidget
)(require('./MapWidget'));
const TableWidget = compose(
dependenciesToWidget,
dependenciesToFilter,
Expand Down
5 changes: 3 additions & 2 deletions web/client/components/widgets/widget/MapWidget.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
const React = require('react');
const WidgetContainer = require('./WidgetContainer');
const InfoPopover = require('./InfoPopover');

const { omit } = require('lodash');
const Message = require('../../I18N/Message');
const {withHandlers} = require('recompose');
const MapView = withHandlers({
Expand All @@ -33,6 +33,7 @@ module.exports = ({
toggleDeleteConfirm = () => { },
id, title, loading, description,
map,
mapStateSource,
confirmDelete = false,
onDelete = () => {}
} = {}) =>
Expand All @@ -45,5 +46,5 @@ module.exports = ({
</DropdownButton>
</ButtonToolbar>}
>
<MapView updateProperty={updateProperty} id={id} map={map} layers={map && map.layers} options={{ style: { margin: 10, height: 'calc(100% - 20px)' }}}/>
<MapView updateProperty={updateProperty} id={id} map={omit(map, 'mapStateSource')} mapStateSource={mapStateSource} layers={map && map.layers} options={{ style: { margin: 10, height: 'calc(100% - 20px)' }}}/>
</WidgetContainer>);
25 changes: 23 additions & 2 deletions web/client/epics/__tests__/widgets-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ var expect = require('expect');
const { testEpic, addTimeoutEpic, TEST_TIMEOUT } = require('./epicTestUtils');

const {
clearWidgetsOnLocationChange
clearWidgetsOnLocationChange,
alignDependenciesToWidgets
} = require('../widgets');
const {
CLEAR_WIDGETS
CLEAR_WIDGETS,
insertWidget,
LOAD_DEPENDENCIES
} = require('../../actions/widgets');
const {
savingMap,
Expand Down Expand Up @@ -126,4 +129,22 @@ describe('widgets Epics', () => {
};
});
});
it('alignDependenciesToWidgets triggered on insertWidget', (done) => {
const checkActions = actions => {
expect(actions.length).toBe(1);
const action = actions[0];
expect(action.type).toBe(LOAD_DEPENDENCIES);
expect(action.dependencies).toExist();
expect(action.dependencies.center).toBe("map.center");
expect(action.dependencies.viewport).toBe("map.bbox");
expect(action.dependencies.zoom).toBe("map.zoom");
done();
};
testEpic(alignDependenciesToWidgets,
1,
[insertWidget({id: 'test'})],
checkActions,
{});
});

});
3 changes: 2 additions & 1 deletion web/client/epics/widgets.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ module.exports = {
.map((maps=[]) => loadDependencies(maps.reduce( (deps, m) => ({
...deps,
[m === "map" ? "viewport" : `${m}.viewport`]: `${m}.bbox`, // {viewport: "map.bbox"} or {"widgets[ID_W].viewport": "widgets[ID_W].bbox"}
[m === "map" ? "center" : `${m}.center`]: `${m}.center` // {center: "map.center"} or {"widgets[ID_W].center": "widgets[ID_W].center"}
[m === "map" ? "center" : `${m}.center`]: `${m}.center`, // {center: "map.center"} or {"widgets[ID_W].center": "widgets[ID_W].center"}
[m === "map" ? "zoom" : `${m}.zoom`]: `${m}.zoom`
}), {}))
),
clearWidgetsOnLocationChange: (action$, {getState = () => {}} = {}) =>
Expand Down
2 changes: 1 addition & 1 deletion web/client/plugins/widgetbuilder/ChartBuilder.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ const chooseLayerEnhancer = compose(
)
);

module.exports = chooseLayerEnhancer(({ enabled, onClose = () => { }, availableDependencies = {}, dependencies, ...props} = {}) =>
module.exports = chooseLayerEnhancer(({ enabled, onClose = () => { }, availableDependencies = [], dependencies, ...props} = {}) =>

(<BorderLayout
header={<BuilderHeader onClose={onClose}><Toolbar availableDependencies={availableDependencies} onClose={onClose}/></BuilderHeader>}
Expand Down
2 changes: 1 addition & 1 deletion web/client/plugins/widgetbuilder/CounterBuilder.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ const chooseLayerEnhancer = compose(
)
);

module.exports = chooseLayerEnhancer(({ enabled, onClose = () => { }, availableDependencies={}, dependencies, ...props } = {}) =>
module.exports = chooseLayerEnhancer(({ enabled, onClose = () => { }, availableDependencies=[], dependencies, ...props } = {}) =>

(<BorderLayout
header={<BuilderHeader onClose={onClose}><Toolbar availableDependencies={availableDependencies} onClose={onClose} /></BuilderHeader>}
Expand Down
15 changes: 7 additions & 8 deletions web/client/plugins/widgetbuilder/MapBuilder.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
const React = require('react');
const {connect} = require('react-redux');
const {onEditorChange} = require('../../actions/widgets');
const { wizardSelector, wizardStateToProps, availableDependenciesSelector} = require('./commons');
const { wizardSelector, wizardStateToProps} = require('./commons');
const layerSelector = require('./enhancers/layerSelector');
const manageLayers = require('./enhancers/manageLayers');
const mapToolbar = require('./enhancers/mapToolbar');
Expand Down Expand Up @@ -63,17 +63,16 @@ const mapBuilder = compose(
map: editorData.map
})),
handleNodeSelection,
handleNodeEditing,
connect(availableDependenciesSelector)
handleNodeEditing
);


module.exports = mapBuilder(({
enabled, onClose = () => {},
toggleLayerSelector = () => {},
editNode, setEditNode, closeNodeEditor, selectedGroups=[], selectedLayers=[], selectedNodes, onNodeSelect = () => {} } = {},
availableDependencies = []
) =>
enabled, onClose = () => {},
toggleLayerSelector = () => {},
editNode, setEditNode, closeNodeEditor, selectedGroups=[], selectedLayers=[], selectedNodes, onNodeSelect = () => {},
availableDependencies =[]
} = {}) =>
(<BorderLayout
className = "map-selector"
header={(<BuilderHeader onClose={onClose}>
Expand Down
2 changes: 1 addition & 1 deletion web/client/plugins/widgetbuilder/TableBuilder.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ const chooseLayerEnhancer = compose(
)
);

module.exports = chooseLayerEnhancer(({ enabled, onClose = () => { }, editorData = {}, availableDependencies = {}, dependencies, ...props } = {}) =>
module.exports = chooseLayerEnhancer(({ enabled, onClose = () => { }, editorData = {}, availableDependencies = [], dependencies, ...props } = {}) =>

(<BorderLayout
header={
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright 2018, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
const { withHandlers, withProps, compose } = require('recompose');
const { omit } = require('lodash');

/**
* Provides proper handlers and variables to connect a widget to a map viewport in the toolbar or other builders
* requires:
* - editorData object
* - onChange: function to change widget properties
* - toggleDependencySelector function in case of multiple maps
*
*/
module.exports = compose(
withProps(({ availableDependencies = [], editorData = {}} = {}) => ({
availableDependencies: availableDependencies.filter(d => !(editorData.id && d.indexOf(editorData.id) >= 0))
})),
withProps(({ editorData = {} }) => ({
canConnect: true,
connected: editorData.mapSync
})),
withHandlers({
toggleConnection: ({ onChange = () => { }, editorData = {} }) => (widget, id) => {
onChange('mapSync', !editorData.mapSync);
const center =
!editorData.mapSync
? id === 'map'
? 'center'
: `${id}.center`
: undefined;
const zoom =
!editorData.mapSync
? id === 'map'
? 'zoom'
: `${id}.zoom`
: undefined;
const { dependenciesMap = {} } = editorData;
onChange('dependenciesMap', !editorData.mapSync && center && zoom !== undefined
? { ...dependenciesMap, center, zoom } :
omit(dependenciesMap, ['center', 'zoom']));


}
}),
withHandlers({
toggleConnection: ({ toggleConnection = () => { }, toggleDependencySelector = () => { }, widget, editorData = {} }) =>
(available = []) => available.length === 1 || editorData.mapSync
? toggleConnection(widget, available[0])
: toggleDependencySelector()
})
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2018, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
const { withProps, compose } = require('recompose');
/**
* Returns an enhancer that add `stepButtons` for viewport connection to a wizard toolbar
* @param {function} showCondition parses props to allow visualization of the buttons (if other connect condition are satisfied)
*/
module.exports = (showCondition = () => true) => compose(
withProps(({
stepButtons = [],
toggleConnection = () => { },
availableDependencies = [],
canConnect,
connected,
...props
}) => ({
stepButtons: [{
onClick: () => toggleConnection(availableDependencies),
disabled: availableDependencies.length > 1, // TODO: remove when support multi map
visible: showCondition(props) && canConnect && availableDependencies.length > 0,
bsStyle: connected ? "success" : "primary",
glyph: connected ? "plug" : "unplug",
tooltipId: connected
? "widgets.builder.wizard.clearConnection"
: availableDependencies.length === 1
? "widgets.builder.wizard.connectToTheMap"
: "connection to multiple maps not supported yet" // TODO: "widgets.builder.wizard.connectToAMap"
}, ...stepButtons
]
}))
);
10 changes: 7 additions & 3 deletions web/client/plugins/widgetbuilder/enhancers/mapToolbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@
*/
const { compose, branch, withProps, withHandlers} = require('recompose');
const {connect} = require('react-redux');
const { insertWidget, setPage} = require('../../../actions/widgets');
const { insertWidget, setPage, onEditorChange} = require('../../../actions/widgets');
const manageLayers = require('./manageLayers');
const handleNodeEditing = require('./handleNodeEditing');
const { wizardSelector, wizardStateToProps } = require('../commons');

const withConnectButton = require('./connection/withConnectButton');
const mapPositionConnect = require('./connection/mapPositionConnect');
module.exports = compose(
connect(wizardSelector, {
setPage,
onChange: onEditorChange,
insertWidget
},
wizardStateToProps
Expand Down Expand Up @@ -49,6 +51,8 @@ module.exports = compose(
tooltipId: "toc.toolTrashLayerTooltip"
}]
}))
)
),
mapPositionConnect,
withConnectButton(({step}) => step === 0)

);
6 changes: 6 additions & 0 deletions web/client/reducers/__tests__/widgets-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ describe('Test the widgets reducer', () => {
it('initial state', () => {
const state = widgets(undefined, {type: "START"});
expect(state.containers).toExist();
expect(state.dependencies.key).toBeFalsy();
expect(state.dependencies.viewport).toBe("map.bbox");
expect(state.dependencies.center).toBe("map.center");
expect(state.dependencies.zoom).toBe("map.zoom");
});
it('editNewWidget', () => {
const state = widgets(undefined, editNewWidget({a: "A"}, {step: 0}));
Expand Down Expand Up @@ -121,5 +125,7 @@ describe('Test the widgets reducer', () => {
expect(state).toExist();
expect(state.dependencies.key).toBeFalsy();
expect(state.dependencies.viewport).toBe("map.bbox");
expect(state.dependencies.center).toBe("map.center");
expect(state.dependencies.zoom).toBe("map.zoom");
});
});
3 changes: 2 additions & 1 deletion web/client/reducers/widgets.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ const {arrayUpsert, arrayDelete} = require('../utils/ImmutableUtils');
const emptyState = {
dependencies: {
viewport: "map.bbox",
center: "map.center"
center: "map.center",
zoom: "map.zoom"
},
containers: {
floating: {
Expand Down

0 comments on commit 6825a35

Please sign in to comment.