Skip to content

Commit cd62de6

Browse files
MV88Tobia Di Pisa
authored andcommitted
Fix #3608 add TOC tooltip customization (#3609)
* Fix #3608 add TOC tooltip custmoization * Update web/client/utils/TOCUtils.js Co-Authored-By: MV88 <matteo.velludini.geosolutions@gmail.com> * Update web/client/utils/TOCUtils.js Co-Authored-By: MV88 <matteo.velludini.geosolutions@gmail.com>
1 parent 891f3db commit cd62de6

9 files changed

Lines changed: 168 additions & 27 deletions

File tree

web/client/components/TOC/DefaultGroup.jsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ class DefaultGroup extends React.Component {
2828
currentLocale: PropTypes.string,
2929
selectedNodes: PropTypes.array,
3030
onSelect: PropTypes.func,
31-
titleTooltip: PropTypes.bool
31+
titleTooltip: PropTypes.bool,
32+
tooltipOptions: PropTypes.object
3233
};
3334

3435
static defaultProps = {
@@ -74,7 +75,7 @@ class DefaultGroup extends React.Component {
7475
<div className="toc-default-group-head">
7576
{grab}
7677
{this.renderVisibility(error)}
77-
<GroupTitle tooltip={this.props.titleTooltip} node={this.props.node} currentLocale={this.props.currentLocale} onClick={this.props.onToggle} onSelect={this.props.onSelect}/>
78+
<GroupTitle tooltipOptions={this.props.tooltipOptions} tooltip={this.props.titleTooltip} node={this.props.node} currentLocale={this.props.currentLocale} onClick={this.props.onToggle} onSelect={this.props.onSelect}/>
7879
</div>
7980
<GroupChildren level={this.props.level + 1} onSort={this.props.onSort} position="collapsible">
8081
{this.props.children}

web/client/components/TOC/DefaultLayer.jsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ const localizedProps = require('../misc/enhancers/localizedProps');
2222

2323
const GlyphIndicator = localizedProps('tooltip')(withTooltip(Glyphicon));
2424

25+
/**
26+
* Default layer node for TOC
27+
*/
2528
class DefaultLayer extends React.Component {
2629
static propTypes = {
2730
node: PropTypes.object,
@@ -46,7 +49,8 @@ class DefaultLayer extends React.Component {
4649
titleTooltip: PropTypes.bool,
4750
filter: PropTypes.func,
4851
showFullTitleOnExpand: PropTypes.bool,
49-
hideOpacityTooltip: PropTypes.bool
52+
hideOpacityTooltip: PropTypes.bool,
53+
tooltipOptions: PropTypes.object
5054
};
5155

5256
static defaultProps = {
@@ -144,7 +148,7 @@ class DefaultLayer extends React.Component {
144148
<div className="toc-default-layer-head">
145149
{grab}
146150
{this.renderVisibility()}
147-
<Title tooltip={this.props.titleTooltip} filterText={this.props.filterText} node={this.props.node} currentLocale={this.props.currentLocale} onClick={this.props.onSelect} onContextMenu={this.props.onContextMenu} />
151+
<Title tooltipOptions={this.props.tooltipOptions} tooltip={this.props.titleTooltip} filterText={this.props.filterText} node={this.props.node} currentLocale={this.props.currentLocale} onClick={this.props.onSelect} onContextMenu={this.props.onContextMenu} />
148152
{this.props.node.loading ? <div className="toc-inline-loader"></div> : this.renderToolsLegend(isEmpty)}
149153
{this.props.indicators ? this.renderIndicators() : null}
150154
</div>

web/client/components/TOC/fragments/GroupTitle.jsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
const React = require('react');
1010
const PropTypes = require('prop-types');
1111
const StatusIcon = require('./StatusIcon');
12-
const {isObject} = require('lodash');
1312
const {Tooltip} = require('react-bootstrap');
1413
const OverlayTrigger = require('../../misc/OverlayTrigger');
14+
const {getTooltipText, getTooltip} = require('../../../utils/TOCUtils');
1515

1616
class GroupTitle extends React.Component {
1717
static propTypes = {
@@ -20,7 +20,8 @@ class GroupTitle extends React.Component {
2020
onSelect: PropTypes.func,
2121
style: PropTypes.object,
2222
currentLocale: PropTypes.string,
23-
tooltip: PropTypes.bool
23+
tooltip: PropTypes.bool,
24+
tooltipOptions: PropTypes.object
2425
};
2526

2627
static inheritedPropTypes = ['node'];
@@ -37,9 +38,10 @@ class GroupTitle extends React.Component {
3738

3839
render() {
3940
let expanded = this.props.node.expanded !== undefined ? this.props.node.expanded : true;
40-
const groupTitle = isObject(this.props.node.title) ? this.props.node.title[this.props.currentLocale] || this.props.node.title.default || this.props.node.name : this.props.node.title || this.props.node.name;
41+
const groupTitle = getTooltipText("title", this.props.node, this.props.currentLocale);
42+
const tooltipText = getTooltip(this.props.tooltipOptions, this.props.node, this.props.currentLocale);
4143
return this.props.tooltip ? (
42-
<OverlayTrigger placement="top" overlay={(<Tooltip id={"tooltip-layer-group"}>{groupTitle}</Tooltip>)}>
44+
<OverlayTrigger placement="top" overlay={(<Tooltip id={"tooltip-layer-group"}>{tooltipText}</Tooltip>)}>
4345
<div style={this.props.style}>
4446
<span className="toc-group-title" onClick={ this.props.onSelect ? (e) => this.props.onSelect(this.props.node.id, 'group', e.ctrlKey) : () => {}}>{groupTitle}</span><StatusIcon onClick={() => this.props.onClick(this.props.node.id, expanded)} expanded={expanded} node={this.props.node}/>
4547
</div>

web/client/components/TOC/fragments/Title.jsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88

99
const PropTypes = require('prop-types');
1010
const React = require('react');
11-
const {isObject} = require('lodash');
1211
const {Tooltip} = require('react-bootstrap');
1312
const OverlayTrigger = require('../../misc/OverlayTrigger');
13+
const {getTooltipText, getTooltip} = require('../../../utils/TOCUtils');
1414
require("./css/toctitle.css");
1515

1616
class Title extends React.Component {
@@ -20,15 +20,17 @@ class Title extends React.Component {
2020
onContextMenu: PropTypes.func,
2121
currentLocale: PropTypes.string,
2222
filterText: PropTypes.string,
23-
tooltip: PropTypes.bool
23+
tooltip: PropTypes.bool,
24+
tooltipOptions: PropTypes.object
2425
};
2526

2627
static defaultProps = {
2728
onClick: () => {},
2829
onContextMenu: () => {},
2930
currentLocale: 'en-US',
3031
filterText: '',
31-
tooltip: false
32+
tooltip: false,
33+
tooltipOptions: null
3234
};
3335

3436
getFilteredTitle = (title) => {
@@ -43,10 +45,10 @@ class Title extends React.Component {
4345
}
4446

4547
render() {
46-
const translation = isObject(this.props.node.title) ? this.props.node.title[this.props.currentLocale] || this.props.node.title.default : this.props.node.title;
47-
const title = translation || this.props.node.name;
48+
const title = getTooltipText("title", this.props.node, this.props.currentLocale);
49+
const tooltipText = getTooltip(this.props.tooltipOptions, this.props.node, this.props.currentLocale);
4850
return this.props.tooltip ? (
49-
<OverlayTrigger placement="top" overlay={(<Tooltip id={"tooltip-layer-title"}>{title}</Tooltip>)}>
51+
<OverlayTrigger placement="top" overlay={(<Tooltip id={"tooltip-layer-title"}>{tooltipText}</Tooltip>)}>
5052
<div className="toc-title" onClick={this.props.onClick ? (e) => this.props.onClick(this.props.node.id, 'layer', e.ctrlKey) : () => {}} onContextMenu={(e) => {e.preventDefault(); this.props.onContextMenu(this.props.node); }}>
5153
{this.getFilteredTitle(title)}
5254
</div>
@@ -61,4 +63,5 @@ class Title extends React.Component {
6163
}
6264
}
6365

66+
6467
module.exports = Title;

web/client/components/TOC/fragments/__tests__/GroupTitle-test.jsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const GroupTitle = require('../GroupTitle');
1212

1313
const expect = require('expect');
1414
const ReactTestUtils = require('react-dom/test-utils');
15+
const {getTooltip} = require('../../../../utils/TOCUtils');
1516

1617
describe('test GroupTitle module component', () => {
1718
beforeEach((done) => {
@@ -91,4 +92,24 @@ describe('test GroupTitle module component', () => {
9192
ReactTestUtils.Simulate.mouseOver(domNode);
9293
expect(ReactDOM.findDOMNode(comp).getAttribute('aria-describedby')).toBe(null);
9394
});
95+
96+
it('tests GroupTitle with customtooltip fragments', () => {
97+
const node = {
98+
name: 'group1',
99+
title: {
100+
'default': 'Group',
101+
'it-IT': 'Gruppo'
102+
},
103+
id: "group1",
104+
description: "desc"
105+
};
106+
const tooltipOptions = {"group1": ["title", "description"]};
107+
const currentLocale = "it-IT";
108+
const comp = ReactDOM.render(<GroupTitle node={node} tooltip tooltipOptions={tooltipOptions} currentLocale={currentLocale}/>, document.getElementById("container"));
109+
const domNode = ReactDOM.findDOMNode(comp);
110+
expect(domNode).toExist();
111+
ReactTestUtils.Simulate.mouseOver(domNode);
112+
expect(ReactDOM.findDOMNode(comp).getAttribute('aria-describedby')).toBe('tooltip-layer-group');
113+
expect(getTooltip(tooltipOptions, node, currentLocale)).toBe("Gruppo - desc");
114+
});
94115
});

web/client/components/TOC/fragments/__tests__/Title-test.jsx

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@
66
* LICENSE file in the root directory of this source tree.
77
*/
88

9-
var React = require('react');
10-
var ReactDOM = require('react-dom');
11-
var Title = require('../Title');
9+
const React = require('react');
10+
const ReactDOM = require('react-dom');
11+
const Title = require('../Title');
12+
const {getTooltip} = require('../../../../utils/TOCUtils');
1213

13-
var expect = require('expect');
14+
const expect = require('expect');
1415

15-
var ReactTestUtils = require('react-dom/test-utils');
16+
const ReactTestUtils = require('react-dom/test-utils');
1617

1718
describe('test Title module component', () => {
1819
beforeEach((done) => {
@@ -152,4 +153,29 @@ describe('test Title module component', () => {
152153
ReactTestUtils.Simulate.mouseOver(domNode);
153154
expect(ReactDOM.findDOMNode(comp).getAttribute('aria-describedby')).toBe(null);
154155
});
156+
157+
it('tests Title with customtooltip fragments', () => {
158+
const node = {
159+
name: 'layer00',
160+
title: {
161+
'default': 'Layer',
162+
'it-IT': 'Livello'
163+
},
164+
id: "layer00",
165+
description: "desc",
166+
visibility: true,
167+
storeIndex: 9,
168+
type: 'wms',
169+
url: 'fakeurl'
170+
};
171+
const tooltipOptions = {"layer00": ["title", "description"]};
172+
const currentLocale = "it-IT";
173+
const comp = ReactDOM.render(<Title node={node} tooltip tooltipOptions={tooltipOptions} currentLocale={currentLocale}/>, document.getElementById("container"));
174+
const domNode = ReactDOM.findDOMNode(comp);
175+
expect(domNode).toExist();
176+
ReactTestUtils.Simulate.mouseOver(domNode);
177+
expect(ReactDOM.findDOMNode(comp).getAttribute('aria-describedby')).toBe('tooltip-layer-title');
178+
expect(getTooltip(tooltipOptions, node, currentLocale)).toBe("Livello - desc");
179+
});
180+
155181
});

web/client/plugins/TOC.jsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,7 @@ class LayerTree extends React.Component {
457457
* @prop {object} cfg.layerOptions: options to pass to the layer.
458458
* @prop {boolean} cfg.showFullTitleOnExpand shows full length title in the legend. default `false`.
459459
* @prop {boolean} cfg.hideOpacityTooltip hide toolip on opacity sliders
460+
*
460461
* Some of the layerOptions are: `legendContainerStyle`, `legendStyle`. These 2 allow to customize the legend:
461462
* For instance you can pass some styling props to the legend.
462463
* this example is to make the legend scrollable horizontally
@@ -472,7 +473,7 @@ class LayerTree extends React.Component {
472473
* }
473474
* }
474475
* ```
475-
* Another layerOptionS entry can be `indicators`. `indicators` is an array of icons to add to the TOC. They must satisfy a condition to be shown in the TOC.
476+
* Another layerOptions entry can be `indicators`. `indicators` is an array of icons to add to the TOC. They must satisfy a condition to be shown in the TOC.
476477
* For the moment only indicators of type `dimension` are supported.
477478
* example :
478479
* ```
@@ -493,6 +494,16 @@ class LayerTree extends React.Component {
493494
* }
494495
* }]
495496
* ```
497+
*
498+
* Another layerOptions is `tooltipOptions` which contains the custom tooltips fragment for nodes in the TOC
499+
* structure is {"nodeID": ["prop1", "prop2"], "joinsStr": " - "}, it will display the fragment in the order are written
500+
* for example
501+
* ```
502+
* "tooltipOptions": {
503+
* "id_of_the_layer": ["title", "description"]
504+
* }
505+
* ```
506+
* it can contain also the "joinStr" property to be used for joining tooltips fragments, default is " - "
496507
*/
497508
const TOCPlugin = connect(tocSelector, {
498509
groupPropertiesChangeHandler: changeGroupProperties,

web/client/utils/TOCUtils.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* LICENSE file in the root directory of this source tree.
77
*/
88

9+
const {isObject, find, isNil} = require('lodash');
910

1011
const TOCUtils = {
1112
createFromSearch: function(options, search) {
@@ -22,6 +23,43 @@ const TOCUtils = {
2223

2324
const val = search.replace(/\./g, '${dot}').replace(/\//g, '.');
2425
return {label: search, value: val};
26+
},
27+
/**
28+
* gets and joins the fragments for tooltips of the node component in the TOC
29+
* @param {object} tooltipOptions
30+
* @param {object} node layer or group
31+
* @param {string} currentLocale
32+
* @return {string} tooltip text
33+
*/
34+
getTooltip: (tooltipOptions, node, currentLocale) => {
35+
// if this node is present in the tooltipOptions then use those keys to create the text for the tooltip
36+
let tooltips = tooltipOptions && find(Object.keys(tooltipOptions), id => id === node.id);
37+
if (tooltips) {
38+
// if you specify a joinsStr it uses it for concatenating the various fields
39+
return tooltipOptions[tooltips]
40+
.map(t => TOCUtils.getTooltipText(t, node, currentLocale))
41+
.filter(t => !isNil(t))
42+
.join(tooltipOptions.joinStr || " - ");
43+
}
44+
return TOCUtils.getTooltipText("title", node, currentLocale);
45+
},
46+
/**
47+
* gets the fragment for the tooltip.
48+
* @param {object} fragment in the node
49+
* @param {object} node layer or group
50+
* @param {string} currentLocale
51+
* @return {string} tooltip fragment
52+
*/
53+
getTooltipText: (fragment, node, currentLocale) => {
54+
switch (fragment) {
55+
case "title": {
56+
const translation = isObject(node.title) ? node.title[currentLocale] || node.title.default : node.title;
57+
const title = translation || node.name;
58+
return title;
59+
}
60+
// default is the name of the property passed
61+
default: return node[fragment];
62+
}
2563
}
2664
};
2765

web/client/utils/__tests__/TOCUtils-test.js

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* LICENSE file in the root directory of this source tree.
77
*/
88
const expect = require('expect');
9-
const {createFromSearch} = require('../TOCUtils');
9+
const {createFromSearch, getTooltip, getTooltipText} = require('../TOCUtils');
1010
let options = [{label: "lab1", value: "val1"}];
1111

1212
describe('TOCUtils', () => {
@@ -20,11 +20,46 @@ describe('TOCUtils', () => {
2020
});
2121

2222
it('test createFromSearch for General Fragment with new valid value', () => {
23-
let val = createFromSearch(options, "lab2");
24-
expect(val.label).toBe("lab2");
25-
expect(val.value).toBe("lab2");
26-
val = createFromSearch(options, "lab2/lab5");
27-
expect(val.label).toBe("lab2/lab5");
28-
expect(val.value).toBe("lab2.lab5");
23+
const node = {
24+
name: 'layer00',
25+
title: {
26+
'default': 'Layer',
27+
'it-IT': 'Livello'
28+
},
29+
id: "layer00",
30+
description: "desc",
31+
visibility: true,
32+
storeIndex: 9,
33+
type: 'wms',
34+
url: 'fakeurl'
35+
};
36+
const tooltipOptions = {"layer00": ["title", "description", "fakeFragment"]};
37+
const currentLocale = "it-IT";
38+
const tooltip = getTooltip(tooltipOptions, node, currentLocale);
39+
expect(tooltip).toBe("Livello - desc");
40+
});
41+
it('test getTooltipText', () => {
42+
const node = {
43+
name: 'layer00',
44+
title: {
45+
'default': 'Layer',
46+
'it-IT': 'Livello'
47+
},
48+
id: "layer00",
49+
description: "desc",
50+
visibility: true,
51+
storeIndex: 9,
52+
type: 'wms',
53+
url: 'fakeurl'
54+
};
55+
const currentLocale = "it-IT";
56+
let tooltip = getTooltipText("title", node, currentLocale);
57+
expect(tooltip).toBe("Livello");
58+
tooltip = getTooltipText("description", node, currentLocale);
59+
expect(tooltip).toBe("desc");
60+
61+
tooltip = getTooltipText("fakeFragment", node, currentLocale);
62+
expect(tooltip).toBe(undefined);
63+
2964
});
3065
});

0 commit comments

Comments
 (0)