Skip to content

Commit

Permalink
Counter widget (#2645)
Browse files Browse the repository at this point in the history
  • Loading branch information
offtherailz authored Feb 27, 2018
1 parent 6549901 commit d54c857
Show file tree
Hide file tree
Showing 35 changed files with 911 additions and 72 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@
"react-sortable-items": "https://github.com/geosolutions-it/react-sortable-items/tarball/react15",
"react-spinkit": "2.1.2",
"react-swipeable-views": "0.12.2",
"react-textfit": "1.1.0",
"react-twitter-widgets": "1.3.0",
"react-widgets": "3.4.8",
"recharts": "0.22.4",
Expand Down
12 changes: 6 additions & 6 deletions web/client/actions/widgets.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
*/
const uuid = require('uuid/v1');
const INSERT = "WIDGETS:INSERT";
const NEW = "WIGETS:NEW";
const NEW = "WIDGETS:NEW";
const EDIT = "WIDGETS:EDIT";
const EDIT_NEW = "WIDGETS:EDIT_NEW";
const EDITOR_CHANGE = "WIDGETS:EDITOR_CHANGE";
const EDITOR_SETTING_CHANGE = "WIGETS:EDITOR_SETTING_CHANGE";
const EDITOR_SETTING_CHANGE = "WIDGETS:EDITOR_SETTING_CHANGE";
const UPDATE = "WIDGETS:UPDATE";
const UPDATE_PROPERTY = "WIDGETS:UPDATE_PROPERTY";
const CHANGE_LAYOUT = "WIDGETS:CHANGE_LAYOUT";
Expand Down Expand Up @@ -115,7 +115,7 @@ const editWidget = (widget) => ({
* Edit new widget. Initializes the widget builder properly
* @param {object} widget The widget template
* @param {object} settings The settings for the template
* @return {object} the action of type `WIGETS:EDIT_NEW`
* @return {object} the action of type `WIDGETS:EDIT_NEW`
*/
const editNewWidget = (widget, settings) => ({
type: EDIT_NEW,
Expand All @@ -127,7 +127,7 @@ const editNewWidget = (widget, settings) => ({
* Changes an entry in the widget editor
* @param {string} key the key of the value to set. even a path is allowed
* @param {any} value the new value
* @return {object} The action of type `WIGETS:EDITOR_CHANGE` with key and value
* @return {object} The action of type `WIDGETS:EDITOR_CHANGE` with key and value
*/
const onEditorChange = (key, value) => ({
type: EDITOR_CHANGE,
Expand All @@ -139,7 +139,7 @@ const onEditorChange = (key, value) => ({
* Changes a setting of the editor (e.g. the page)
* @param {string} key the key of the value to set. even a path is allowed
* @param {any} value the new value
* @return {object} The action of type `WIGETS:EDITOR_SETTING_CHANGE` with key and value
* @return {object} The action of type `WIDGETS:EDITOR_SETTING_CHANGE` with key and value
*/
const changeEditorSetting = (key, value) => ({
type: EDITOR_SETTING_CHANGE,
Expand All @@ -149,7 +149,7 @@ const changeEditorSetting = (key, value) => ({
/**
* Change the page setting of the editor
* @param {number} step the page number
* @return {object} action of type `WIGETS:EDITOR_SETTING_CHANGE` with the step
* @return {object} action of type `WIDGETS:EDITOR_SETTING_CHANGE` with the step
*/
const setPage = (step) => changeEditorSetting("step", step);

Expand Down
2 changes: 1 addition & 1 deletion web/client/components/I18N/Number.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ var {FormattedNumber} = require('react-intl');

class NumberFormat extends React.Component {
static propTypes = {
value: PropTypes.object,
value: PropTypes.oneOf(PropTypes.object, PropTypes.number),
numberParams: PropTypes.object
};

Expand Down
7 changes: 6 additions & 1 deletion web/client/components/widgets/builder/WidgetTypeSelector.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,13 @@ const DEFAULT_TYPES = [{
}, {
title: <Message msgId={"widgets.types.table.title"} />,
type: "table",
glyph: "features-grid",
glyph: "features-grid",
caption: <Message msgId={"widgets.types.table.caption"} />
}, {
title: <Message msgId={"widgets.types.counter.title"} />,
type: "counter",
glyph: "counter",
caption: <Message msgId={"widgets.types.counter.caption"} />
}];

module.exports = ({widgetTypes = DEFAULT_TYPES, typeFilter = () => true, onSelect= () => {}}) =>
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 @@ -11,8 +11,8 @@ const {wizardHandlers} = require('../../../misc/wizard/enhancers');
const loadingState = require('../../../misc/enhancers/loadingState')(({loading, data}) => loading || !data, {width: 500, height: 200});

const ChartType = require('./chart/ChartType');
const wfsChartOptions = require('./chart/wfsChartOptions');
const ChartOptions = wfsChartOptions(require('./chart/ChartOptions'));
const wfsChartOptions = require('./common/wfsChartOptions');
const ChartOptions = wfsChartOptions(require('./common/WPSWidgetOptions'));
const WidgetOptions = require('./common/WidgetOptions');
const sampleData = require('../../enhancers/sampleChartData');
const wpsChart = require('../../enhancers/wpsChart');
Expand Down
116 changes: 116 additions & 0 deletions web/client/components/widgets/builder/wizard/CounterWizard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/**
* 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 {isNil} = require('lodash');
const { compose, lifecycle } = require('recompose');

const {wizardHandlers} = require('../../../misc/wizard/enhancers');
const loadingState = require('../../../misc/enhancers/loadingState')(({loading, data}) => loading || !data, {width: 500, height: 200});

const wfsChartOptions = require('./common/wfsChartOptions');
const CounterOptions = wfsChartOptions(require('./common/WPSWidgetOptions'));
const WidgetOptions = require('./common/WidgetOptions');

const wpsCounter = require('../../enhancers/wpsCounter');
const dependenciesToFilter = require('../../enhancers/dependenciesToFilter');
const emptyChartState = require('../../enhancers/emptyChartState');
const errorChartState = require('../../enhancers/errorChartState');

const isCounterOptionsValid = (options = {}) => options.aggregateFunction && options.aggregationAttribute;
const triggerSetValid = compose(
lifecycle({
componentWillReceiveProps: ({ valid, data = [], options={}, setValid = () => { }, error } = {}) => {
const isNowValid = !isNil(data[0]) && !error;
if (!!valid !== !!isNowValid && isCounterOptionsValid(options)) {
setValid(isNowValid);
}
}
}));

const enhanchePreview = compose(

dependenciesToFilter,
wpsCounter,
triggerSetValid,
loadingState,
errorChartState,
emptyChartState
);

const sampleProps = {
width: 430,
height: 200
};

const Wizard = wizardHandlers(require('../../../misc/wizard/WizardContainer'));


const Counter = require('../../widget/CounterView');
const Preview = enhanchePreview(Counter);
const CounterPreview = ({ data = {}, layer, dependencies = {}, valid, setValid = () => { } }) =>
!isCounterOptionsValid(data.options)
? <Counter
{...sampleProps}
data={[{data: 42}]}
options={data.options}
series={[{dataKey: "data"}]} />
: <Preview
{...sampleProps}
valid={valid}
dependencies={dependencies}
setValid={setValid}
type={data.type}
legend={data.legend}
layer={data.layer || layer}
filter={data.filter}
geomProp={data.geomProp}
mapSync={data.mapSync}
options={data.options} />;

const enhanceWizard = compose(lifecycle({
componentWillReceiveProps: ({data = {}, valid, setValid = () => {}} = {}) => {
if (valid && !isCounterOptionsValid(data.options)) {
setValid(false);
}
}})
);
module.exports = enhanceWizard(({ onChange = () => { }, onFinish = () => { }, setPage = () => { }, setValid = () => { }, valid, formOptions, data = {}, layer ={}, step=0, types, featureTypeProperties, dependencies}) =>
(<Wizard
step={step}
setPage={setPage}
onFinish={onFinish}
isStepValid={n => n === 1 ? isCounterOptionsValid(data.options) : true} hideButtons>
<CounterOptions
dependencies={dependencies}
key="chart-options"
formOptions={formOptions}
featureTypeProperties={featureTypeProperties}
types={types}
data={data}
onChange={onChange}
layer={data.layer || layer}
sampleChart={<CounterPreview
data={data}
valid={valid}
layer={data.layer || layer}
dependencies={dependencies}
setValid={v => setValid(v && isCounterOptionsValid(data.options))} />}
/>
<WidgetOptions
key="widget-options"
data={data}
onChange={onChange}
layer={data.layer || layer}
sampleChart={<CounterPreview
data={data}
valid={valid}
layer={data.layer || layer}
dependencies={dependencies}
setValid={v => setValid(v && isCounterOptionsValid(data.options))} />}
/>
</Wizard>));
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2017, 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 expect = require('expect');
const CounterWizard = require('../CounterWizard');
describe('CounterWizard component', () => {
beforeEach((done) => {
document.body.innerHTML = '<div id="container"></div>';
setTimeout(done);
});
afterEach((done) => {
ReactDOM.unmountComponentAtNode(document.getElementById("container"));
document.body.innerHTML = '';
setTimeout(done);
});
it('CounterWizard rendering with defaults', () => {
ReactDOM.render(<CounterWizard />, document.getElementById("container"));
const container = document.getElementById('container');
const el = container.querySelector('.ms-wizard');
expect(el).toExist();
expect(container.querySelector('.chart-options-form')).toExist();
});
it('CounterWizard rendering widget options', () => {
ReactDOM.render(<CounterWizard step={1}/>, document.getElementById("container"));
const container = document.getElementById('container');
const el = container.querySelector('.chart-options-form');
expect(el).toNotExist();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
* LICENSE file in the root directory of this source tree.
*/
const React = require('react');
const {head} = require('lodash');
const {Row, Col, Form, FormGroup, ControlLabel} = require('react-bootstrap');
const { head, get} = require('lodash');
const { Row, Col, Form, FormGroup, FormControl, ControlLabel} = require('react-bootstrap');
const Message = require('../../../../I18N/Message');
const Select = require('react-select');
const ColorRangeSelector = require('../../../../style/ColorRangeSelector');
Expand Down Expand Up @@ -46,8 +46,20 @@ const getColorRangeItems = (type) => {
}
return COLORS;
};

module.exports = ({data = {options: {}}, onChange = () => {}, options=[], dependencies, aggregationOptions = [], sampleChart}) => (<Row>
const getLabelMessageId = (field, data = {}) => `widgets.${field}.${data.type || data.widgetType || "default"}`;
module.exports = ({
data = { options: {} },
onChange = () => { },
options = [],
formOptions = {
showGroupBy: true,
showUom: false,
showColorRampSelector: true,
showLegend: true
},
dependencies,
aggregationOptions = [],
sampleChart}) => (<Row>
<StepHeader title={<Message msgId={`widgets.chartOptionsTitle`} />} />
<Col xs={12}>
<div style={{marginBottom: "30px"}}>
Expand All @@ -56,9 +68,9 @@ module.exports = ({data = {options: {}}, onChange = () => {}, options=[], depend
</Col>
<Col xs={12}>
<Form className="chart-options-form" horizontal>
<FormGroup controlId="groupByAttributes" className="mapstore-block-width">
{formOptions.showGroupBy ? (<FormGroup controlId="groupByAttributes" className="mapstore-block-width">
<Col componentClass={ControlLabel} sm={6}>
<Message msgId={`widgets.groupByAttributes.${data.type}`} />
<Message msgId={getLabelMessageId("groupByAttributes", data)} />
</Col>
<Col sm={6}>
<Select
Expand All @@ -70,11 +82,10 @@ module.exports = ({data = {options: {}}, onChange = () => {}, options=[], depend
}}
/>
</Col>
</FormGroup>

</FormGroup>) : null}
<FormGroup controlId="aggregationAttribute" className="mapstore-block-width">
<Col componentClass={ControlLabel} sm={6}>
<Message msgId={`widgets.aggregationAttribute.${data.type}`} />
<Message msgId={getLabelMessageId("aggregationAttribute", data)} />
</Col>
<Col sm={6}>
<Select
Expand All @@ -89,7 +100,7 @@ module.exports = ({data = {options: {}}, onChange = () => {}, options=[], depend
</FormGroup>
<FormGroup controlId="aggregateFunction" className="mapstore-block-width">
<Col componentClass={ControlLabel} sm={6}>
<Message msgId={`widgets.aggregateFunction.${data.type}`} />
<Message msgId={getLabelMessageId("aggregateFunction", data)} />
</Col>
<Col sm={6}>
<Select
Expand All @@ -102,9 +113,17 @@ module.exports = ({data = {options: {}}, onChange = () => {}, options=[], depend
/>
</Col>
</FormGroup>
<FormGroup controlId="colorRamp" className="mapstore-block-width">
{formOptions.showUom ? <FormGroup controlId="uom">
<Col componentClass={ControlLabel} sm={6}>
<Message msgId={getLabelMessageId("uom", data)} />
</Col>
<Col sm={6}>
<FormControl value={get(data, `options.seriesOptions[0].uom`)} type="text" onChange={e => onChange("options.seriesOptions.[0].uom", e.target.value)} />
</Col>
</FormGroup> : null}
{formOptions.showColorRampSelector ? <FormGroup controlId="colorRamp" className="mapstore-block-width">
<Col componentClass={ControlLabel} sm={6}>
<Message msgId={`widgets.colorRamp.${data.type}`} />
<Message msgId={getLabelMessageId("colorRamp", data)} />
</Col>
<Col sm={6}>
<ColorRangeSelector
Expand All @@ -113,7 +132,7 @@ module.exports = ({data = {options: {}}, onChange = () => {}, options=[], depend
samples={data.type === "pie" ? 5 : 1}
onChange={v => {onChange("autoColorOptions", {...v.options, name: v.name}); }}/>
</Col>
</FormGroup>
</FormGroup> : null}
{dependencies && dependencies.viewport
? (<FormGroup controlId="mapSync" className="mapstore-block-width">
<Col componentClass={ControlLabel} sm={6}>
Expand All @@ -129,9 +148,9 @@ module.exports = ({data = {options: {}}, onChange = () => {}, options=[], depend
</Col>
</FormGroup>)
: null}
<FormGroup controlId="displayLegend">
{formOptions.showLegend ? <FormGroup controlId="displayLegend">
<Col componentClass={ControlLabel} sm={6}>
<Message msgId={`widgets.displayLegend.${data.type}`} />
<Message msgId={getLabelMessageId("displayLegend", data)} />
</Col>
<Col sm={6}>
<SwitchButton
Expand All @@ -141,7 +160,7 @@ module.exports = ({data = {options: {}}, onChange = () => {}, options=[], depend
}}
/>
</Col>
</FormGroup>
</FormGroup> : null}
</Form>


Expand Down
Loading

0 comments on commit d54c857

Please sign in to comment.