Skip to content

Commit 9d75993

Browse files
committed
Add localization and unit tests
1 parent 85ea069 commit 9d75993

21 files changed

Lines changed: 375 additions & 27 deletions

File tree

web/client/actions/__tests__/widgets-test.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const {
2222
OPEN_FILTER_EDITOR,
2323
EXPORT_CSV,
2424
EXPORT_IMAGE,
25+
DEPENDENCY_SELECTOR_KEY,
2526
exportCSV,
2627
exportImage,
2728
openFilterEditor,
@@ -35,7 +36,9 @@ const {
3536
editNewWidget,
3637
onEditorChange,
3738
changeEditorSetting,
38-
setPage
39+
setPage,
40+
setupDependencySelector,
41+
toggleDependencySelector
3942
} = require('../widgets');
4043

4144
describe('Test correctness of the widgets actions', () => {
@@ -156,5 +159,22 @@ describe('Test correctness of the widgets actions', () => {
156159
expect(retval.key).toBe("step");
157160
expect(retval.value).toBe(1);
158161
});
162+
it('setupDependencySelector', () => {
163+
const value = { active: true, setup: "setup" };
164+
const retval = setupDependencySelector(value);
165+
expect(retval).toExist();
166+
expect(retval.type).toBe(EDITOR_SETTING_CHANGE);
167+
expect(retval.key).toBe(`${DEPENDENCY_SELECTOR_KEY}`);
168+
expect(retval.value).toBe(value);
169+
});
170+
it('toggleDependencySelector', () => {
171+
const value = { setup: "setup" };
172+
const retval = toggleDependencySelector(true, value);
173+
expect(retval).toExist();
174+
expect(retval.type).toBe(EDITOR_SETTING_CHANGE);
175+
expect(retval.key).toBe(`${DEPENDENCY_SELECTOR_KEY}`);
176+
expect(retval.value.setup).toBe("setup");
177+
expect(retval.value.active).toBe(true);
178+
});
159179

160180
});

web/client/actions/widgets.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ const loadDependencies = (dependencies) => ({
179179
* Action triggered to start the connection flow. Typically starts the connection flow
180180
* @param {array} availableDependencies Array of available dependency keys
181181
* @param {object} options a map of connections to apply when the dependencies has been resolved
182-
* @param {string} target target of the connection. If not present we assume is the current editing widget
182+
* @param {string} target target of the connection. If not present we assume is the current editing widget (not yet supported)
183183
*/
184184
const toggleConnection = (active, availableDependencies, options, target) => ({
185185
type: TOGGLE_CONNECTION,
@@ -214,9 +214,26 @@ const exportImage = ({widgetDivId}) => ({
214214
type: EXPORT_IMAGE,
215215
widgetDivId
216216
});
217+
/**
218+
* Triggers the filter editor opening
219+
*/
217220
const openFilterEditor = () => ({type: OPEN_FILTER_EDITOR});
221+
/**
222+
* Changes the setup of the dependency selector, that allow to select a dependent widget
223+
* @param {object} setup the initial setup of the dependency selector
224+
*/
218225
const setupDependencySelector = (setup) => changeEditorSetting(`${DEPENDENCY_SELECTOR_KEY}`, setup);
226+
/**
227+
* Sets a value in the configuration of the dependency selector
228+
* @param {string} key the configuration of the dependency selector to change
229+
* @param {any} value the value to assign to the key
230+
*/
219231
const changeDependencySelector = (key, value) => changeEditorSetting(`${DEPENDENCY_SELECTOR_KEY}[${key}]`, value);
232+
/**
233+
* Activate/deactivate the dependency selector with an initial setup
234+
* @param {boolean} active active flag of the dependency selector
235+
* @param {object} settings initial setup
236+
*/
220237
const toggleDependencySelector = (active, settings) => setupDependencySelector({
221238
active,
222239
...settings
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright 2018, GeoSolutions Sas.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
const React = require('react');
9+
const ReactDOM = require('react-dom');
10+
const expect = require('expect');
11+
const withMask = require('../withMask');
12+
13+
describe('withMask enhancer', () => {
14+
beforeEach((done) => {
15+
document.body.innerHTML = '<div id="container"></div>';
16+
setTimeout(done);
17+
});
18+
afterEach((done) => {
19+
ReactDOM.unmountComponentAtNode(document.getElementById("container"));
20+
document.body.innerHTML = '';
21+
setTimeout(done);
22+
});
23+
it('withMask rendering with defaults', () => {
24+
const Sink = withMask()(() => <div id="test">test</div>);
25+
ReactDOM.render(<Sink />, document.getElementById("container"));
26+
expect(document.querySelector('#test')).toExist();
27+
expect(document.querySelector('.ms2-mask-container')).toExist();
28+
expect(document.querySelector('.ms2-mask')).toNotExist();
29+
});
30+
it('withMask rendering mask', () => {
31+
const Sink = withMask(({test}) => test, () => <div id="mask"></div>)(() => <div id="test">test</div>);
32+
ReactDOM.render(<Sink test/>, document.getElementById("container"));
33+
expect(document.querySelector('#test')).toExist();
34+
expect(document.querySelector('.ms2-mask-container')).toExist();
35+
expect(document.querySelector('.ms2-mask')).toExist();
36+
expect(document.querySelector('#mask')).toExist();
37+
ReactDOM.render(<Sink test={false} />, document.getElementById("container"));
38+
expect(document.querySelector('.ms2-mask-container')).toExist();
39+
expect(document.querySelector('.ms2-mask')).toNotExist();
40+
expect(document.querySelector('#mask')).toNotExist();
41+
});
42+
it('withMask alwaysWrap option', () => {
43+
const Sink = withMask(
44+
({ test }) => test,
45+
() => <div id="mask"></div>,
46+
{
47+
alwaysWrap: false
48+
})(() => <div id="test">test</div>);
49+
ReactDOM.render(<Sink test />, document.getElementById("container"));
50+
expect(document.querySelector('#test')).toExist();
51+
expect(document.querySelector('.ms2-mask-container')).toExist();
52+
expect(document.querySelector('.ms2-mask')).toExist();
53+
expect(document.querySelector('#mask')).toExist();
54+
ReactDOM.render(<Sink test={false} />, document.getElementById("container"));
55+
expect(document.querySelector('#test')).toExist();
56+
expect(document.querySelector('.ms2-mask-container')).toNotExist();
57+
expect(document.querySelector('.ms2-mask')).toNotExist();
58+
expect(document.querySelector('#mask')).toNotExist();
59+
});
60+
});

web/client/components/misc/enhancers/withMask.js

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,31 @@ const {branch, nest} = require('recompose');
1212
* @param {function} showMask gets props as argument and returns true if the enhancer should be applied
1313
* @param {*} maskContent the content of the mask
1414
*/
15-
const maskEnhancer = (visible, maskContent, { maskContainerStyle, maskStyle }) => (A) => nest(
16-
(props) => (<div style={maskContainerStyle} >
15+
const maskEnhancer = (showMask, maskContent, { maskContainerStyle, maskStyle }) => (A) => nest(
16+
(props) => (<div className="ms2-mask-container" style={maskContainerStyle} >
1717
{props.children}
18-
<div style={{ ...maskStyle, visibility: visible(props) ? 'visible' : 'hidden'}} >
18+
{showMask(props) ? <div className="ms2-mask" style={maskStyle} >
1919
{maskContent(props)}
20-
</div>
20+
</div> : null}
2121
</div>),
2222
A);
23+
/**
24+
* Enhancer to gray out a component with a mask. The enhancer always adds a wrapper div with className="ms2-mask-container". if you want to avoid this use the option alwaysWrap = false.
25+
*
26+
* @param {function} showMask function that returns true if the mask have to be shown. Gets props as argument
27+
* @param {function} maskContent returns the content of the mask, gets props as argument TODO: allow any component
28+
* @param {object} options options for the mask:
29+
* - alwaysWrap: true by default. if false, apply the enhancer only when showMask is true.
30+
* This will cause a complete remount and re-render of the wrapped component, that may be a problem if you're using lifecycle methods, so by default is false
31+
* -
32+
*/
2333
module.exports = (
2434
showMask = () => {},
2535
maskContent = () => {},
2636
{
27-
alwaysWrap = false,
28-
maskContainerStyle = { width: "100%", height: "100%", position: "relative" },
29-
maskStyle = { backgroundColor: "rgba(0, 0, 0, 0.7)", color: "#fff", display: 'flex', zIndex: 1000, width: "100%", height: "100%", position: 'absolute', top: 0 }
37+
alwaysWrap = true,
38+
maskContainerStyle = {},
39+
maskStyle = {}
3040
} = {}
3141
) => alwaysWrap
3242
? maskEnhancer(showMask, maskContent, { maskContainerStyle, maskStyle })
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright 2018, GeoSolutions Sas.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
10+
const { mapPropsStreamWithConfig } = require('recompose');
11+
12+
const rxjsConfig = require('recompose/rxjsObservableConfig').default;
13+
const Rx = require('rxjs');
14+
const mapPropsStream = mapPropsStreamWithConfig(rxjsConfig);
15+
/**
16+
* implements the stream version of withProps
17+
* as the mapPropsStream implements mapProps with stream.
18+
* For the moment the argument is only a function, the stream returns the props
19+
* that have be merged with upcoming props.
20+
* You can also return falsy value to use the props stream only as a trigger for actions
21+
* @param {function} propStreamFactory a function that gets the stream of props and returns the stream of props to add to the enhanced component
22+
*/
23+
24+
module.exports = propStreamFactory => mapPropsStream(props$ => {
25+
const newProps$ = propStreamFactory(props$) || Rx.Observable.empty();
26+
return newProps$.startWith({}).combineLatest(props$, (overrides = {}, props = {}) => ({
27+
...props,
28+
...overrides
29+
}));
30+
});
31+

web/client/components/widgets/view/enhancers/withSelection.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ module.exports = branch(
2626
(w) => getWidgetClass(w)
2727
? getWidgetClass(w) + (isWidgetSelectable(w) ? undefined : ' disabled')
2828
: isWidgetSelectable(w) ? undefined : ' disabled',
29-
onWidgetClick: ({ onWidgetSelected = () => { } }) => (...args) => onWidgetSelected(...args)
29+
onWidgetClick: ({ onWidgetSelected = () => { }, isWidgetSelectable = () => true }) => (w, ...args) => isWidgetSelectable(w) ? onWidgetSelected(w, ...args) : null
3030
}))
3131
)
3232
);

web/client/epics/__tests__/widgets-test.js

Lines changed: 127 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,18 @@ const { testEpic, addTimeoutEpic, TEST_TIMEOUT } = require('./epicTestUtils');
1010

1111
const {
1212
clearWidgetsOnLocationChange,
13-
alignDependenciesToWidgets
13+
alignDependenciesToWidgets,
14+
toggleWidgetConnectFlow
1415
} = require('../widgets');
1516
const {
1617
CLEAR_WIDGETS,
1718
insertWidget,
18-
LOAD_DEPENDENCIES
19+
toggleConnection,
20+
selectWidget,
21+
EDITOR_CHANGE,
22+
EDITOR_SETTING_CHANGE,
23+
LOAD_DEPENDENCIES,
24+
DEPENDENCY_SELECTOR_KEY
1925
} = require('../../actions/widgets');
2026
const {
2127
savingMap,
@@ -147,4 +153,123 @@ describe('widgets Epics', () => {
147153
{});
148154
});
149155

156+
it('toggleWidgetConnectFlow with only map', (done) => {
157+
const checkActions = actions => {
158+
expect(actions.length).toBe(2);
159+
expect(actions[0].type).toBe(EDITOR_CHANGE);
160+
expect(actions[0].key).toBe("mapSync");
161+
expect(actions[0].value).toBe(true);
162+
const action = actions[1];
163+
expect(action.type).toBe(EDITOR_CHANGE);
164+
expect(action.key).toExist();
165+
expect(action.key).toBe("dependenciesMap");
166+
expect(action.value.center).toBe("center");
167+
expect(action.value.zoom).toBe("zoom");
168+
done();
169+
};
170+
testEpic(toggleWidgetConnectFlow,
171+
2,
172+
[toggleConnection(
173+
true,
174+
["map"],
175+
{ mappings: { zoom: "zoom", center: "center" } }
176+
)],
177+
checkActions,
178+
{});
179+
});
180+
it('toggleWidgetConnectFlow for widgets', (done) => {
181+
const checkActions = actions => {
182+
expect(actions.length).toBe(2);
183+
expect(actions[0].type).toBe(EDITOR_CHANGE);
184+
expect(actions[0].key).toBe("mapSync");
185+
expect(actions[0].value).toBe(true);
186+
const action = actions[1];
187+
expect(action.type).toBe(EDITOR_CHANGE);
188+
expect(action.key).toExist();
189+
expect(action.key).toBe("dependenciesMap");
190+
expect(action.value.center).toBe("widgets[a].map.center");
191+
expect(action.value.zoom).toBe("widgets[a].map.zoom");
192+
done();
193+
};
194+
testEpic(toggleWidgetConnectFlow,
195+
2,
196+
[toggleConnection(
197+
true,
198+
["widgets[a].map"],
199+
{ mappings: { "center": "center", "zoom": "zoom" } }
200+
)],
201+
checkActions,
202+
{});
203+
});
204+
it('toggleWidgetConnectFlow for multiple widgets', (done) => {
205+
const checkActions = actions => {
206+
expect(actions.length).toBe(4);
207+
expect(actions[0].type).toBe(EDITOR_SETTING_CHANGE);
208+
expect(actions[0].key).toBe(DEPENDENCY_SELECTOR_KEY);
209+
expect(actions[0].value.active).toBe(true);
210+
expect(actions[0].value.availableDependencies.length).toBe(2);
211+
expect(actions[1].type).toBe(EDITOR_CHANGE);
212+
expect(actions[1].key).toBe("mapSync");
213+
expect(actions[1].value).toBe(true);
214+
const action = actions[2];
215+
expect(action.type).toBe(EDITOR_CHANGE);
216+
expect(action.key).toExist();
217+
expect(action.key).toBe("dependenciesMap");
218+
expect(action.value.center).toBe("widgets[w1].map.center");
219+
expect(action.value.zoom).toBe("widgets[w1].map.zoom");
220+
expect(actions[3].type).toBe(EDITOR_SETTING_CHANGE);
221+
expect(actions[3].key).toBe(DEPENDENCY_SELECTOR_KEY);
222+
expect(actions[3].value.active).toBe(false);
223+
done();
224+
};
225+
testEpic(toggleWidgetConnectFlow,
226+
4,
227+
[toggleConnection(
228+
true,
229+
["w1", "w2"],
230+
{ mappings: { "center": "center", "zoom": "zoom" } }
231+
), selectWidget({
232+
id: "w1",
233+
widgetType: "map"
234+
})],
235+
checkActions,
236+
{
237+
widgets: {
238+
builder: {
239+
settings: {
240+
[DEPENDENCY_SELECTOR_KEY]: { active: true,
241+
availableDependencies: [
242+
"map.zoom",
243+
"widgets[w1].map",
244+
"widgets[w2].map"
245+
] }
246+
}
247+
}
248+
}
249+
});
250+
});
251+
it('toggleWidgetConnectFlow deactivate widgets', (done) => {
252+
const checkActions = actions => {
253+
expect(actions.length).toBe(2);
254+
expect(actions[0].type).toBe(EDITOR_CHANGE);
255+
expect(actions[0].key).toBe("mapSync");
256+
expect(actions[0].value).toBe(false);
257+
const action = actions[1];
258+
expect(action.type).toBe(EDITOR_CHANGE);
259+
expect(action.key).toExist();
260+
expect(action.key).toBe("dependenciesMap");
261+
expect(action.value.center).toNotExist();
262+
expect(action.value.zoom).toNotExist();
263+
done();
264+
};
265+
testEpic(toggleWidgetConnectFlow,
266+
2,
267+
[toggleConnection(
268+
false,
269+
["map"],
270+
{ mappings: { "center": "widgets[a].map.center", "zoom": "widgets[a].map.zoom" } }
271+
)],
272+
checkActions,
273+
{});
274+
});
150275
});

0 commit comments

Comments
 (0)