Skip to content

Commit 50c18da

Browse files
authored
#2215: add group in TOC toolbar (#3797)
* #2215: add group in TOC toolbar * #2215: fixed some tooltips, fixed removing groups on layer removed, fixed problem with dot in group name
1 parent 410fb7d commit 50c18da

40 files changed

Lines changed: 757 additions & 81 deletions

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

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ var expect = require('expect');
1010
var {
1111
TOGGLE_CONTROL, toggleControl,
1212
SET_CONTROL_PROPERTY, setControlProperty,
13+
SET_CONTROL_PROPERTIES, setControlProperties,
1314
RESET_CONTROLS, resetControls,
1415
on
1516
} = require('../controls');
@@ -69,4 +70,38 @@ describe('Test correctness of the controls actions', () => {
6970
expect(retval.property).toBe(testProperty);
7071
expect(retval.value).toBe(testValue);
7172
});
73+
74+
it('setControlProperties', () => {
75+
const testControl = 'test';
76+
const testProperty1 = 'prop1';
77+
const testValue1 = 'val1';
78+
const testProperty2 = 'prop2';
79+
const testValue2 = 'val2';
80+
var retval = setControlProperties(testControl, testProperty1, testValue1, testProperty2, testValue2);
81+
82+
expect(retval).toExist();
83+
expect(retval.type).toBe(SET_CONTROL_PROPERTIES);
84+
expect(retval.control).toBe(testControl);
85+
expect(retval.properties).toExist();
86+
expect(retval.properties[testProperty1]).toBe(testValue1);
87+
expect(retval.properties[testProperty2]).toBe(testValue2);
88+
});
89+
90+
it('setControlProperties wrong params ignored', () => {
91+
const testControl = 'test';
92+
const testProperty1 = 'prop1';
93+
const testValue1 = 'val1';
94+
const testProperty2 = 'prop2';
95+
const testValue2 = 'val2';
96+
const testProperty3 = 'prop3';
97+
var retval = setControlProperties(testControl, testProperty1, testValue1, testProperty2, testValue2, testProperty3);
98+
99+
expect(retval).toExist();
100+
expect(retval.type).toBe(SET_CONTROL_PROPERTIES);
101+
expect(retval.control).toBe(testControl);
102+
expect(retval.properties).toExist();
103+
expect(retval.properties[testProperty1]).toBe(testValue1);
104+
expect(retval.properties[testProperty2]).toBe(testValue2);
105+
expect(retval.properties[testProperty3]).toNotExist();
106+
});
72107
});

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

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ var {
3232
SHOW_LAYER_METADATA,
3333
HIDE_LAYER_METADATA,
3434
UPDATE_SETTINGS_PARAMS,
35+
ADD_GROUP,
3536
changeLayerProperties,
3637
toggleNode,
3738
sortNode,
@@ -55,7 +56,8 @@ var {
5556
filterLayers,
5657
showLayerMetadata,
5758
hideLayerMetadata,
58-
updateSettingsParams
59+
updateSettingsParams,
60+
addGroup
5961
} = require('../layers');
6062
var {getLayerCapabilities} = require('../layerCapabilities');
6163

@@ -140,6 +142,17 @@ describe('Test correctness of the layers actions', () => {
140142
expect(retval.type).toBe(REMOVE_NODE);
141143
expect(retval.node).toBe('sampleNode');
142144
expect(retval.nodeType).toBe('sampleType');
145+
expect(retval.removeEmpty).toBe(false);
146+
});
147+
148+
it('removeNode with removeEmpty', () => {
149+
var retval = removeNode('sampleNode', 'sampleType', true);
150+
151+
expect(retval).toExist();
152+
expect(retval.type).toBe(REMOVE_NODE);
153+
expect(retval.node).toBe('sampleNode');
154+
expect(retval.nodeType).toBe('sampleType');
155+
expect(retval.removeEmpty).toBe(true);
143156
});
144157

145158
it('updateNode', () => {
@@ -304,4 +317,18 @@ describe('Test correctness of the layers actions', () => {
304317
expect(action.newParams).toBe(newParams);
305318
expect(action.update).toBe(update);
306319
});
320+
321+
it('add root group', () => {
322+
const action = addGroup('newgroup');
323+
expect(action.type).toBe(ADD_GROUP);
324+
expect(action.group).toBe('newgroup');
325+
expect(action.parent).toNotExist();
326+
});
327+
328+
it('add nested group', () => {
329+
const action = addGroup('newgroup', 'group1.group2');
330+
expect(action.type).toBe(ADD_GROUP);
331+
expect(action.group).toBe('newgroup');
332+
expect(action.parent).toBe('group1.group2');
333+
});
307334
});

web/client/actions/controls.js

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@
77
*/
88
const TOGGLE_CONTROL = 'TOGGLE_CONTROL';
99
const SET_CONTROL_PROPERTY = 'SET_CONTROL_PROPERTY';
10+
const SET_CONTROL_PROPERTIES = 'SET_CONTROL_PROPERTIES';
1011
const RESET_CONTROLS = 'RESET_CONTROLS';
1112

13+
const { fromPairs, chunk } = require('lodash');
14+
1215
/**
1316
* Toggle a control property
1417
* @memberof actions.controls
@@ -52,6 +55,24 @@ function setControlProperty(control, property, value, toggle) {
5255
};
5356
}
5457

58+
/**
59+
* Sets a list of properties at once
60+
* @memberof actions.controls
61+
* @param {string} control control name
62+
* @param {array} properties the properties to set, pairs of keys and related values
63+
* @return {object} of type `SET_CONTROL_PROPERTIES` with control and properties
64+
*
65+
* @example
66+
* setControlProperties('metadataexplorer', 'enabled', true, 'group', 'newgroup')
67+
*/
68+
function setControlProperties(control, ...properties) {
69+
return {
70+
type: SET_CONTROL_PROPERTIES,
71+
control,
72+
properties: fromPairs(chunk(properties, 2))
73+
};
74+
}
75+
5576
/**
5677
* Reset all the controls
5778
* @memberof actions.controls
@@ -69,5 +90,6 @@ function resetControls(skip = []) {
6990
* control property.
7091
* @name actions.controls
7192
*/
72-
module.exports = {TOGGLE_CONTROL, SET_CONTROL_PROPERTY, RESET_CONTROLS,
73-
toggleControl, on, setControlProperty, resetControls};
93+
module.exports = {
94+
TOGGLE_CONTROL, SET_CONTROL_PROPERTY, SET_CONTROL_PROPERTIES, RESET_CONTROLS,
95+
toggleControl, on, setControlProperty, setControlProperties, resetControls};

web/client/actions/layers.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const LAYER_LOADING = 'LAYER_LOADING';
1818
const LAYER_LOAD = 'LAYER_LOAD';
1919
const LAYER_ERROR = 'LAYER_ERROR';
2020
const ADD_LAYER = 'ADD_LAYER';
21+
const ADD_GROUP = 'ADD_GROUP';
2122
const REMOVE_LAYER = 'REMOVE_LAYER';
2223
const SHOW_SETTINGS = 'SHOW_SETTINGS';
2324
const HIDE_SETTINGS = 'HIDE_SETTINGS';
@@ -114,12 +115,12 @@ function sortNode(node, order, sortLayers = null) {
114115
};
115116
}
116117

117-
function removeNode(node, type, properties) {
118+
function removeNode(node, type, removeEmpty = false) {
118119
return {
119120
type: REMOVE_NODE,
120121
node: node,
121122
nodeType: type,
122-
properties
123+
removeEmpty
123124
};
124125
}
125126

@@ -164,6 +165,14 @@ function addLayer(layer, foreground = true) {
164165
};
165166
}
166167

168+
function addGroup(group, parent) {
169+
return {
170+
type: ADD_GROUP,
171+
group,
172+
parent
173+
};
174+
}
175+
167176
function removeLayer(layerId) {
168177
return {
169178
type: REMOVE_LAYER,
@@ -270,9 +279,10 @@ module.exports = {
270279
changeLayerProperties, changeLayerParams, changeGroupProperties, toggleNode, sortNode, removeNode, contextNode,
271280
updateNode, layerLoading, layerLoad, layerError, addLayer, removeLayer, showSettings, hideSettings, updateSettings, refreshLayers,
272281
layersRefreshed, layersRefreshError, refreshLayerVersion, updateLayerDimension, browseData, clearLayers, selectNode, filterLayers, showLayerMetadata,
273-
hideLayerMetadata, download, updateSettingsParams,
282+
hideLayerMetadata, download, updateSettingsParams, addGroup,
274283
CHANGE_LAYER_PROPERTIES, CHANGE_LAYER_PARAMS, CHANGE_GROUP_PROPERTIES, TOGGLE_NODE, SORT_NODE,
275284
REMOVE_NODE, UPDATE_NODE, LAYER_LOADING, LAYER_LOAD, LAYER_ERROR, ADD_LAYER, REMOVE_LAYER,
285+
ADD_GROUP,
276286
SHOW_SETTINGS, HIDE_SETTINGS, UPDATE_SETTINGS, CONTEXT_NODE, REFRESH_LAYERS, LAYERS_REFRESHED, LAYERS_REFRESH_ERROR, UPDATE_LAYERS_DIMENSION, BROWSE_DATA, DOWNLOAD,
277287
CLEAR_LAYERS, SELECT_NODE, FILTER_LAYERS, SHOW_LAYER_METADATA, HIDE_LAYER_METADATA, UPDATE_SETTINGS_PARAMS
278288
};

web/client/components/TOC/DefaultGroup.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class DefaultGroup extends React.Component {
5656
(<VisibilityCheck
5757
node={this.props.node}
5858
key="visibility"
59+
tooltip="toc.toggleGroupVisibility"
5960
checkType={this.props.visibilityCheckType}
6061
propertiesChangeHandler={this.props.propertiesChangeHandler}/>)
6162
:

web/client/components/TOC/TOCItemsSettings.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ const TOCItemSettings = (props, context) => {
5252
dock = true,
5353
showFullscreen,
5454
draggable,
55-
position = 'left'
55+
position = 'left',
56+
tabsConfig = {}
5657
} = props;
5758

5859
const tabs = getTabs(props, context);
@@ -122,6 +123,7 @@ const TOCItemSettings = (props, context) => {
122123
{tabs.filter(tab => tab.id && tab.id === activeTab).filter(tab => tab.Component).map(tab => (
123124
<tab.Component
124125
{...props}
126+
{...tabsConfig[tab.id]}
125127
key={'ms-tab-settings-body-' + tab.id}
126128
containerWidth={width}
127129
element={element}

web/client/components/TOC/Toolbar.jsx

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class Toolbar extends React.Component {
3232
settings: PropTypes.object,
3333
layerMetadata: PropTypes.object,
3434
wfsdownload: PropTypes.object,
35+
maxDepth: PropTypes.number,
3536
metadataTemplate: PropTypes.oneOfType([PropTypes.string, PropTypes.array, PropTypes.object, PropTypes.func])
3637
};
3738

@@ -52,11 +53,13 @@ class Toolbar extends React.Component {
5253
onHideSettings: () => {},
5354
onReload: () => {},
5455
onAddLayer: () => {},
56+
onAddGroup: () => { },
5557
onDownload: () => {},
5658
onGetMetadataRecord: () => {},
5759
onHideLayerMetadata: () => {},
5860
onShow: () => {}
5961
},
62+
maxDepth: 3,
6063
text: {
6164
settingsText: '',
6265
opacityText: '',
@@ -67,6 +70,10 @@ class Toolbar extends React.Component {
6770
confirmDeleteMessage: '',
6871
confirmDeleteCancelText: '',
6972
createWidgetTooltip: '',
73+
addLayerTooltip: '',
74+
addLayerToGroupTooltip: '',
75+
addGroupTooltip: '',
76+
addSubGroupTooltip: '',
7077
zoomToTooltip: {
7178
LAYER: '',
7279
LAYERS: ''
@@ -96,6 +103,7 @@ class Toolbar extends React.Component {
96103
activateDownloadTool: true,
97104
activateSettingsTool: true,
98105
activateAddLayer: true,
106+
activateAddGroup: true,
99107
includeDeleteButtonInSettings: false,
100108
activateMetedataTool: true
101109
},
@@ -145,7 +153,23 @@ class Toolbar extends React.Component {
145153
status = this.props.selectedLayers.length > 0 && this.props.selectedLayers.filter(l => l.loadingError === 'Error').length === this.props.selectedLayers.length ? `${status}_LOAD_ERROR` : status;
146154
return status;
147155
}
148-
156+
getSelectedGroup = () => {
157+
return this.props.selectedGroups.length > 0 && this.props.selectedGroups[this.props.selectedGroups.length - 1];
158+
};
159+
getSelectedNodeDepth = () => {
160+
if (this.getStatus() === 'DESELECT') {
161+
return 0;
162+
}
163+
return this.getSelectedGroup().id.split('.').length + 1;
164+
};
165+
addLayer = () => {
166+
const group = this.getSelectedGroup();
167+
this.props.onToolsActions.onAddLayer(group && group.id);
168+
};
169+
addGroup = () => {
170+
const group = this.getSelectedGroup();
171+
this.props.onToolsActions.onAddGroup(group && group.id);
172+
};
149173
render() {
150174
const status = this.getStatus();
151175
const currentEPSG = this.checkBbox();
@@ -163,11 +187,26 @@ class Toolbar extends React.Component {
163187
transitionName="toc-toolbar-btn-transition"
164188
transitionEnterTimeout={300}
165189
transitionLeaveTimeout={300}>
166-
{this.props.activateTool.activateAddLayer && status === 'DESELECT' ?
167-
<Button key="addLayer" bsStyle="primary" bsSize="small" onClick={this.props.onToolsActions.onAddLayer}>
168-
{this.props.text.addLayer}
169-
</Button>
190+
{this.props.activateTool.activateAddLayer && (status === 'DESELECT' || status === 'GROUP') ?
191+
<OverlayTrigger
192+
key="addLayer"
193+
placement="top"
194+
overlay={<Tooltip id="toc-tooltip-addLayer">{status === 'GROUP' ? this.props.text.addLayerToGroupTooltip : this.props.text.addLayerTooltip}</Tooltip>}>
195+
<Button key="addLayer" bsStyle="primary" className="square-button-md" onClick={this.addLayer}>
196+
<Glyphicon glyph="add-layer" />
197+
</Button>
198+
</OverlayTrigger>
170199
: null}
200+
{this.props.activateTool.activateAddGroup && (status === 'DESELECT' || status === 'GROUP') && this.getSelectedNodeDepth() <= this.props.maxDepth ?
201+
<OverlayTrigger
202+
key="addGroup"
203+
placement="top"
204+
overlay={<Tooltip id="toc-tooltip-addGroup">{status === 'GROUP' ? this.props.text.addSubGroupTooltip : this.props.text.addGroupTooltip}</Tooltip>}>
205+
<Button key="addGroup" bsStyle="primary" className="square-button-md" onClick={this.addGroup}>
206+
<Glyphicon glyph="add-folder" />
207+
</Button>
208+
</OverlayTrigger>
209+
: null}
171210
{this.props.activateTool.activateZoomTool && (status === 'LAYER' || status === 'GROUP' || status === 'LAYERS' || status === 'GROUPS') && currentEPSG ?
172211
<OverlayTrigger
173212
key="zoomTo"
@@ -189,7 +228,7 @@ class Toolbar extends React.Component {
189228
<OverlayTrigger
190229
key="settings"
191230
placement="top"
192-
overlay={<Tooltip id="toc-tooltip-settings">{this.props.text.settingsTooltip[status ? 'LAYER_LOAD_ERROR' && 'LAYER' : status]}</Tooltip>}>
231+
overlay={<Tooltip id="toc-tooltip-settings">{this.props.text.settingsTooltip[status === 'LAYER_LOAD_ERROR' ? 'LAYER' : status]}</Tooltip>}>
193232
<Button active={this.props.settings.expanded} bsStyle={this.props.settings.expanded ? 'success' : 'primary'} className="square-button-md" onClick={() => { this.showSettings(status); }}>
194233
<Glyphicon glyph="wrench"/>
195234
</Button>
@@ -328,7 +367,7 @@ class Toolbar extends React.Component {
328367

329368
removeNodes = () => {
330369
this.props.selectedLayers.forEach((layer) => {
331-
this.props.onToolsActions.onRemove(layer.id, 'layers', layer);
370+
this.props.onToolsActions.onRemove(layer.id, 'layers');
332371
});
333372
this.props.onToolsActions.onClear();
334373
this.closeDeleteDialog();

0 commit comments

Comments
 (0)