Skip to content

Commit

Permalink
Disable toolbar buttons when editing (#2080)
Browse files Browse the repository at this point in the history
  • Loading branch information
offtherailz authored Aug 4, 2017
1 parent 86fa732 commit f546340
Show file tree
Hide file tree
Showing 13 changed files with 253 additions and 53 deletions.
9 changes: 9 additions & 0 deletions web/client/actions/featuregrid.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const CLOSE_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';
const DISABLE_TOOLBAR = 'FEATUREGRID:DISABLE_TOOLBAR';
const MODES = {
EDIT: "EDIT",
VIEW: "VIEW"
Expand Down Expand Up @@ -224,6 +225,13 @@ function closeFeatureGrid() {
type: CLOSE_GRID
};
}

function disableToolbar(disabled) {
return {
type: DISABLE_TOOLBAR,
disabled
};
}
function setPermission(permission) {
return {
type: SET_PERMISSION,
Expand Down Expand Up @@ -264,6 +272,7 @@ module.exports = {
CLEAR_CHANGES_CONFIRMED, clearChangeConfirmed,
CLOSE_GRID, closeFeatureGrid,
FEATURE_GRID_CLOSE_CONFIRMED, closeFeatureGridConfirmed,
DISABLE_TOOLBAR, disableToolbar,
setLayer,
selectFeatures,
deselectFeatures,
Expand Down
25 changes: 25 additions & 0 deletions web/client/components/data/featuregrid/editors/AttributeEditor.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const PropTypes = require('prop-types');
const { editors } = require('react-data-grid');

class AttributeEditor extends editors.SimpleTextEditor {
static propTypes = {
onTemporaryChanges: PropTypes.func
};
static defaultProps = {
onTemporaryChanges: () => {}
};
componentDidMount() {
if (this.props.onTemporaryChanges) {
this.props.onTemporaryChanges(true);
}
}
componentWillUnmount() {
// needs to be detouched.
// Otherwise this will trigger before other events out of the editors
// and so the tempChanges seems to be not present.
if (this.props.onTemporaryChanges) {
setTimeout( () => this.props.onTemporaryChanges(false), 300);
}
}
}
module.exports = AttributeEditor;
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const React = require('react');
const PropTypes = require('prop-types');
const { editors } = require('react-data-grid');
const AttributeEditor = require('./AttributeEditor');
const nanToNull = v => isNaN(v) ? null : v;
const processValue = (obj, func) => Object.keys(obj).reduce((acc, curr) => ({
...acc,
Expand All @@ -10,7 +10,7 @@ const parsers = {
"int": v => parseInt(v, 10),
"number": v => parseFloat(v, 10)
};
class NumberEditor extends editors.EditorBase {
class NumberEditor extends AttributeEditor {
static propTypes = {
value: PropTypes.oneOfType([
PropTypes.string,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const React = require('react');
const ReactDOM = require('react-dom');
const AttributeEditor = require('../AttributeEditor');
var expect = require('expect');

let testColumn = {
key: 'columnKey'
};


describe('FeatureGrid AttributeEditor 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('AttributeEditor', () => {

const cmp = ReactDOM.render(<AttributeEditor
value={"A"}
rowIdx={1}
column={testColumn}/>, document.getElementById("container"));
expect(cmp.getValue().columnKey).toBe("A");
});
it('Test onTemporaryChanges triggered on render', (done) => {
const onTemporaryChanges = () => done();
ReactDOM.render(<AttributeEditor
onTemporaryChanges={onTemporaryChanges}
dataType="int"
value={1.1}
rowIdx={2}
column={testColumn}/>, document.getElementById("container"));
});
});
12 changes: 8 additions & 4 deletions web/client/components/data/featuregrid/editors/index.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
const React = require('react');
const Editor = require('./AttributeEditor');
const NumberEditor = require('./NumberEditor');
module.exports = {
"int": <NumberEditor dataType="int" inputProps={{step: 1}}/>,
"number": <NumberEditor dataType="number" />
};

const types = {
"defaultEditor": (props) => <Editor {...props}/>,
"int": (props) => <NumberEditor dataType="int" inputProps={{step: 1, type: "number"}} {...props}/>,
"number": (props) => <NumberEditor dataType="number" inputProps={{step: 1, type: "number"}} {...props}/>
};
module.exports = (type, props) => types[type] ? types[type](props) : types.defaultEditor(props);
5 changes: 4 additions & 1 deletion web/client/components/data/featuregrid/enhancers/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ const featuresToGrid = compose(
editable: props.mode === "EDIT",
sortable: !props.isFocused
}, {
getEditor: ({localType=""} = {}) => props.editors[localType]
getEditor: ({localType=""} = {}) => props.editors(localType, {
onTemporaryChanges: props.gridEvents && props.gridEvents.onTemporaryChanges
})
}))
})
),
Expand All @@ -60,6 +62,7 @@ const featuresToGrid = compose(
onRowsSelected = () => {},
onRowsDeselected = () => {},
onRowsToggled = () => {},
hasTemporaryChanges = () => {},
...gridEvents} = getGridEvents(props.gridEvents, props.rowGetter, props.describeFeatureType, props.actionOpts);

// setup gridOpts setting app selection events binded
Expand Down
20 changes: 20 additions & 0 deletions web/client/components/data/featuregrid/toolbars/TButton.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const React = require('react');
const {Button, Glyphicon, Tooltip, OverlayTrigger} = require('react-bootstrap');
const hideStyle = {
width: 0,
padding: 0,
borderWidth: 0
};
const normalStyle = {
};

const getStyle = (visible) => visible ? normalStyle : hideStyle;
module.exports = ({disabled, id, tooltip="", visible, onClick, glyph, active}) =>
(<OverlayTrigger placement="top" overlay={<Tooltip id={`fe-${id}`}>{tooltip}</Tooltip>}>
<Button key={id} bsStyle={active ? "success" : "primary"} disabled={disabled} id={`fg-${id}`}
style={getStyle(visible)}
className="square-button"
onClick={() => !disabled && onClick()}>
<Glyphicon glyph={glyph}/>
</Button>
</OverlayTrigger>);
119 changes: 77 additions & 42 deletions web/client/components/data/featuregrid/toolbars/Toolbar.jsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
const React = require('react');
const {Button, ButtonGroup, Glyphicon, Tooltip, OverlayTrigger} = require('react-bootstrap');
const {ButtonGroup} = require('react-bootstrap');
require("./toolbar.css");
const hideStyle = {
width: 0,
padding: 0,
borderWidth: 0
};
const normalStyle = {
};
const Message = require('../../../I18N/Message');

const getStyle = (visible) => visible ? normalStyle : hideStyle;
const TButton = require("./TButton");
const getDrawFeatureTooltip = (isDrawing, isSimpleGeom) => {
if (isDrawing) {
return "featuregrid.toolbar.stopDrawGeom";
Expand All @@ -24,37 +16,80 @@ const getSaveMessageId = ({saving, saved}) => {
return "featuregrid.toolbar.saveChanges";
};

module.exports = ({events = {}, mode = "VIEW", selectedCount, hasChanges, hasGeometry, hasNewFeatures, isSimpleGeom, isDrawing = false, isEditingAllowed, saving = false, saved = false, isDownloadOpen, isColumnsOpen} = {}) =>
module.exports = ({events = {}, mode = "VIEW", selectedCount, hasChanges, hasGeometry, hasNewFeatures, isSimpleGeom, isDrawing = false, isEditingAllowed, saving = false, saved = false, isDownloadOpen, isColumnsOpen, disableToolbar} = {}) =>
(<ButtonGroup id="featuregrid-toolbar" className="featuregrid-toolbar featuregrid-toolbar-margin">
<OverlayTrigger placement="top" overlay={<Tooltip id="fe-edit-mode"><Message msgId="featuregrid.toolbar.editMode"/></Tooltip>}>
<Button key="edit-mode" bsStyle="primary" id="fg-edit-mode" style={getStyle(mode === "VIEW" && isEditingAllowed)} className="square-button" onClick={events.switchEditMode}><Glyphicon glyph="pencil"/></Button>
</OverlayTrigger>
<OverlayTrigger placement="top" overlay={<Tooltip id="fe-back-view"><Message msgId="featuregrid.toolbar.quitEditMode"/></Tooltip>}>
<Button key="back-view" bsStyle="primary" id="fg-back-view" style={getStyle(mode === "EDIT" && !hasChanges && !hasNewFeatures)} className="square-button" onClick={events.switchViewMode}><Glyphicon glyph="arrow-left"/></Button>
</OverlayTrigger>
<OverlayTrigger placement="top" overlay={<Tooltip id="fe-add-feature"><Message msgId="featuregrid.toolbar.addNewFeatures"/></Tooltip>}>
<Button key="add-feature" bsStyle="primary" id="fg-add-feature" style={getStyle(mode === "EDIT" && !hasNewFeatures && !hasChanges)} className="square-button" onClick={events.createFeature}><Glyphicon glyph="row-add"/></Button>
</OverlayTrigger>
<OverlayTrigger placement="top" overlay={<Tooltip id="fe-draw-feature"><Message msgId={getDrawFeatureTooltip(isDrawing, isSimpleGeom)}/></Tooltip>}>
<Button key="draw-feature" bsStyle="primary" id="fg-draw-feature" style={getStyle(mode === "EDIT" && selectedCount === 1 && (!hasGeometry || hasGeometry && !isSimpleGeom))} className={ isDrawing ? "square-button btn-success" : "square-button"} onClick={events.startDrawingFeature}><Glyphicon glyph="pencil-add"/></Button>
</OverlayTrigger>
<OverlayTrigger placement="top" overlay={<Tooltip id="fe-remove-features"><Message msgId="featuregrid.toolbar.deleteSelectedFeatures"/></Tooltip>}>
<Button key="remove-features" bsStyle="primary" id="fg-remove-features" style={getStyle(mode === "EDIT" && selectedCount > 0 && !hasChanges && !hasNewFeatures)} className="square-button" onClick={events.deleteFeatures}><Glyphicon glyph="trash-square"/></Button>
</OverlayTrigger>
<OverlayTrigger placement="top" overlay={<Tooltip id="fe-save-features"><Message msgId={getSaveMessageId({saving, saved})}/></Tooltip>}>
<Button key="save-feature" bsStyle="primary" disabled={saving || saved} bsStyle={saved ? "success" : "primary"} id="fg-save-features" style={getStyle(mode === "EDIT" && hasChanges || hasNewFeatures)} className="square-button" onClick={events.saveChanges}><Glyphicon glyph="floppy-disk"/></Button>
</OverlayTrigger>
<OverlayTrigger placement="top" overlay={<Tooltip id="fe-cancel-editing"><Message msgId="featuregrid.toolbar.cancelChanges"/></Tooltip>}>
<Button key="cancel-editing" bsStyle="primary" id="fg-cancel-editing" style={getStyle(mode === "EDIT" && hasChanges || hasNewFeatures)}
className="square-button" onClick={events.clearFeatureEditing}><Glyphicon glyph="remove-square"/></Button>
</OverlayTrigger>
<OverlayTrigger placement="top" overlay={<Tooltip id="fe-delete-geometry"><Message msgId="featuregrid.toolbar.deleteGeometry"/></Tooltip>}>
<Button key="delete-geometry" bsStyle="primary" id="fg-delete-geometry" style={getStyle(mode === "EDIT" && hasGeometry && selectedCount === 1)} className="square-button" onClick={events.deleteGeometry}><Glyphicon glyph="polygon-trash"/></Button>
</OverlayTrigger>
<OverlayTrigger placement="top" overlay={<Tooltip id="fe-download-grid"><Message msgId="featuregrid.toolbar.downloadGridData"/></Tooltip>}>
<Button key="download-grid" bsStyle="primary" id="fg-download-grid" bsStyle={isDownloadOpen ? "success" : "primary"} style={getStyle(mode === "VIEW")} className="square-button" onClick={events.download}><Glyphicon glyph="features-grid-download"/></Button>
</OverlayTrigger>
<OverlayTrigger placement="top" overlay={<Tooltip id="fe-grid-settings"><Message msgId="featuregrid.toolbar.hideShowColumns"/></Tooltip>}>
<Button key="grid-settings" bsStyle="primary" id="fg-grid-settings" bsStyle={isColumnsOpen ? "success" : "primary"} className="square-button" style={getStyle(selectedCount <= 1 && mode === "VIEW")} onClick={events.settings}><Glyphicon glyph="features-grid-set"/></Button>
</OverlayTrigger>
<TButton
id="edit-mode"
tooltip={<Message msgId="featuregrid.toolbar.editMode"/>}
disabled={disableToolbar}
visible={mode === "VIEW" && isEditingAllowed}
onClick={events.switchEditMode}
glyph="pencil"/>
<TButton
id="back-view"
tooltip={<Message msgId="featuregrid.toolbar.quitEditMode"/>}
disabled={disableToolbar}
visible={mode === "EDIT" && !hasChanges && !hasNewFeatures}
onClick={events.switchViewMode}
glyph="arrow-left"/>
<TButton
id="add-feature"
tooltip={<Message msgId="featuregrid.toolbar.addNewFeatures"/>}
disabled={disableToolbar}
visible={mode === "EDIT" && !hasNewFeatures && !hasChanges}
onClick={events.createFeature}
glyph="row-add"/>
<TButton
id="draw-feature"
tooltip={<Message msgId={getDrawFeatureTooltip(isDrawing, isSimpleGeom)}/>}
disabled={disableToolbar}
visible={mode === "EDIT" && selectedCount === 1 && (!hasGeometry || hasGeometry && !isSimpleGeom)}
onClick={events.startDrawingFeature}
active={isDrawing}
glyph="pencil-add"/>
<TButton
id="remove-features"
tooltip={<Message msgId="featuregrid.toolbar.deleteSelectedFeatures"/>}
disabled={disableToolbar}
visible={mode === "EDIT" && selectedCount > 0 && !hasChanges && !hasNewFeatures}
onClick={events.deleteFeatures}
glyph="trash-square"/>
<TButton
id="save-feature"
tooltip={<Message msgId={getSaveMessageId({saving, saved})}/>}
disabled={saving || saved || disableToolbar}
visible={mode === "EDIT" && hasChanges || hasNewFeatures}
active={saved}
onClick={events.saveChanges}
glyph="floppy-disk"/>
<TButton
id="cancel-editing"
tooltip={<Message msgId="featuregrid.toolbar.cancelChanges"/>}
disabled={disableToolbar}
visible={mode === "EDIT" && hasChanges || hasNewFeatures}
onClick={events.clearFeatureEditing}
glyph="remove-square"/>
<TButton
id="delete-geometry"
tooltip={<Message msgId="featuregrid.toolbar.deleteGeometry"/>}
disabled={disableToolbar}
visible={mode === "EDIT" && hasGeometry && selectedCount === 1}
onClick={events.deleteGeometry}
glyph="polygon-trash"/>
<TButton
id="download-grid"
tooltip={<Message msgId="featuregrid.toolbar.downloadGridData"/>}
disabled={disableToolbar}
active={isDownloadOpen}
visible={mode === "VIEW"}
onClick={events.download}
glyph="features-grid-download"/>
<TButton
id="grid-settings"
tooltip={<Message msgId="featuregrid.toolbar.hideShowColumns"/>}
disabled={disableToolbar}
active={isColumnsOpen}
visible={selectedCount <= 1 && mode === "VIEW"}
onClick={events.settings}
glyph="features-grid-set"/>
</ButtonGroup>);
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* 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.
*/
var React = require('react');
var ReactDOM = require('react-dom');
var TButton = require('../TButton');
var expect = require('expect');
const spyOn = expect.spyOn;

const isVisibleButton = (el) => {
return el.style.width !== 0 && el.style.width !== "0" && el.style.width !== "0px";
};

describe('Featuregrid TButton', () => {
beforeEach((done) => {
document.body.innerHTML = '<div id="container"></div>';
setTimeout(done);
});

afterEach((done) => {
ReactDOM.unmountComponentAtNode(document.getElementById("container"));
document.body.innerHTML = '';
setTimeout(done);
});
it('TButton', () => {
const events = {
onClick: () => {}
};
spyOn(events, "onClick");
ReactDOM.render(<TButton id="TEST_BUTTON" onClick={events.onClick} visible />, document.getElementById("container"));
let editButton = document.getElementById("fg-TEST_BUTTON");
expect(editButton).toExist();
expect(isVisibleButton(editButton)).toBe(true);
editButton.click();
expect(events.onClick).toHaveBeenCalled();
ReactDOM.render(<TButton id="TEST_BUTTON" onClick={events.onClick} visible active/>, document.getElementById("container"));
editButton = document.getElementById("fg-TEST_BUTTON");
expect(editButton.className.indexOf("success") >= 0).toBe(true);
ReactDOM.render(<TButton id="TEST_BUTTON" onClick={events.onClick} />, document.getElementById("container"));
editButton = document.getElementById("fg-TEST_BUTTON");
expect(isVisibleButton(editButton)).toBe(false);
});

});
3 changes: 2 additions & 1 deletion web/client/plugins/featuregrid/gridEvents.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
const {sort, selectFeatures, deselectFeatures, featureModified} = require('../../actions/featuregrid');
const {sort, selectFeatures, deselectFeatures, featureModified, disableToolbar} = require('../../actions/featuregrid');

const range = (start, end) => Array.from({length: (end + 1 - start)}, (v, k) => k + start);
module.exports = {
onGridSort: (sortBy, sortOrder) => sort(sortBy, sortOrder),
onTemporaryChanges: (v) => disableToolbar(v),
onGridRowsUpdated: ({fromRow, toRow, updated}, rowGetter) => {

let features = range(fromRow, toRow).map(r => rowGetter(r)).filter(f =>
Expand Down
1 change: 1 addition & 0 deletions web/client/plugins/featuregrid/panels/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const Toolbar = connect(
isDrawing: isDrawingSelector,
isSimpleGeom: isSimpleGeomSelector,
selectedCount: selectedFeaturesCount,
disableToolbar: state => state && state.featuregrid && state.featuregrid.disableToolbar,
isDownloadOpen: state => state && state.controls && state.controls.wfsdownload && state.controls.wfsdownload.enabled,
isColumnsOpen: state => state && state.featuregrid && state.featuregrid.tools && state.featuregrid.tools.settings,
isEditingAllowed: (state) => isAdminUserSelector(state) || canEditSelector(state)
Expand Down
12 changes: 11 additions & 1 deletion web/client/reducers/__tests__/featuregrid-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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} = require('../../actions/featuregrid');
deleteGeometryFeature, geometryChanged, setSelectionOptions, changePage, featureModified, setPermission, disableToolbar} = require('../../actions/featuregrid');
const {featureTypeLoaded, featureClose} = require('../../actions/wfsquery');
const {changeDrawingStatus} = require('../../actions/draw');

Expand Down Expand Up @@ -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", {
Expand Down
Loading

0 comments on commit f546340

Please sign in to comment.