Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#10736: Interactive legend for TOC layers [WFS Layer part] #10748

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions web/client/api/catalog/WFS.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,12 @@ const searchAndPaginate = (json = {}, startPosition, maxRecords, text) => {
};
};

const recordToLayer = (record) => {
const recordToLayer = (record, {
service
}) => {
const {
layerOptions
} = service || {};
return {
type: record.type || "wfs",
search: {
Expand All @@ -85,7 +90,8 @@ const recordToLayer = (record) => {
description: record.description || "",
bbox: record.boundingBox,
links: getRecordLinks(record),
...record.layerOptions
...record.layerOptions,
...layerOptions
};
};

Expand Down
37 changes: 36 additions & 1 deletion web/client/components/TOC/fragments/settings/Display.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import ThreeDTilesSettings from './ThreeDTilesSettings';
import ModelTransformation from './ModelTransformation';
import StyleBasedWMSJsonLegend from '../../../../plugins/TOC/components/StyleBasedWMSJsonLegend';
import { getMiscSetting } from '../../../../utils/ConfigUtils';
import VectorLegend from '../../../../plugins/TOC/components/VectorLegend';

export default class extends React.Component {
static propTypes = {
Expand Down Expand Up @@ -325,7 +326,6 @@ export default class extends React.Component {
<div style={this.setOverFlow() && this.state.containerStyle || {}} ref={this.containerRef} >
{ enableInteractiveLegend ?
<StyleBasedWMSJsonLegend
owner="legendPreview"
style={this.setOverFlow() && {} || undefined}
layer={this.props.element}
legendHeight={
Expand All @@ -351,6 +351,41 @@ export default class extends React.Component {
</Col>
</div>
</Row>}
{this.props.element.type === "wfs" && <Row>
<div className={"legend-options"}>
{experimentalInteractiveLegend && <Col xs={12} className={"legend-label"}>
<label key="legend-options-title" className="control-label"><Message msgId="layerProperties.legendOptions.title" /></label>
</Col>}
{ experimentalInteractiveLegend && !this.props?.hideInteractiveLegendOption &&
<Col xs={12} className="first-selectize">
<Checkbox
data-qa="display-interactive-legend-option"
value="enableInteractiveLegend"
key="enableInteractiveLegend"
onChange={(e) => {
if (!e.target.checked) {
const newLayerFilter = updateLayerLegendFilter(this.props.element.layerFilter);
this.props.onChange("layerFilter", newLayerFilter );
}
this.props.onChange("enableInteractiveLegend", e.target.checked);
}}
checked={enableInteractiveLegend} >
<Message msgId="layerProperties.enableInteractiveLegendInfo.label"/>
&nbsp;<InfoPopover text={<Message msgId="layerProperties.enableInteractiveLegendInfo.infoWithoutGSNote" />} />
</Checkbox>
</Col>
}
{enableInteractiveLegend && <Col xs={12} className="legend-preview">
<ControlLabel><Message msgId="layerProperties.legendOptions.legendPreview" /></ControlLabel>
<div style={this.setOverFlow() && this.state.containerStyle || {}} ref={this.containerRef} >
<VectorLegend
layer={this.props.element}
style={this.props.element.style || {}}
/>
</div>
</Col>}
</div>
</Row>}
</Grid>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ describe('test Layer Properties Display module component', () => {
expect(labels[6].innerText).toBe("layerProperties.legendOptions.legendHeight");
expect(labels[7].innerText).toBe("layerProperties.legendOptions.legendPreview");
});
it('tests Layer Properties Legend component events', () => {
it('tests wms Layer Properties Legend component events', () => {
const l = {
name: 'layer00',
title: 'Layer',
Expand Down Expand Up @@ -369,4 +369,43 @@ describe('test Layer Properties Display module component', () => {
expect(inputs[11].value).toBe("20");
expect(inputs[12].value).toBe("40");
});
it('tests wfs Layer Properties Legend component events', () => {
const l = {
name: 'layer00',
title: 'Layer',
visibility: true,
storeIndex: 9,
type: 'wfs',
url: 'fakeurl',
legendOptions: {
legendWidth: 15,
legendHeight: 15
},
enableInteractiveLegend: false
};
const settings = {
options: {
opacity: 1
}
};
const handlers = {
onChange() {}
};
let spy = expect.spyOn(handlers, "onChange");
const comp = ReactDOM.render(<Display element={l} settings={settings} onChange={handlers.onChange}/>, document.getElementById("container"));
expect(comp).toBeTruthy();
const inputs = ReactTestUtils.scryRenderedDOMComponentsWithTag( comp, "input" );
const legendPreview = ReactTestUtils.scryRenderedDOMComponentsWithClass( comp, "legend-preview" );
expect(legendPreview).toBeTruthy();
expect(inputs).toBeTruthy();
expect(inputs.length).toBe(6);
let interactiveLegendConfig = document.querySelector(".legend-options input[data-qa='display-interactive-legend-option']");
// change enableInteractiveLegend to enable interactive legend
interactiveLegendConfig.checked = true;
ReactTestUtils.Simulate.change(interactiveLegendConfig);
expect(spy).toHaveBeenCalled();
expect(spy.calls[0].arguments[0]).toEqual("enableInteractiveLegend");
expect(spy.calls[0].arguments[1]).toEqual(true);
});

});
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { FormGroup, Checkbox } from "react-bootstrap";

import Message from "../../../I18N/Message";
import InfoPopover from '../../../widgets/widget/InfoPopover';
import { getMiscSetting } from '../../../../utils/ConfigUtils';

/**
* Common Advanced settings form WMS/CSW/WMTS/WFS
Expand All @@ -24,30 +25,41 @@ export default ({
service,
onChangeServiceProperty = () => { },
onToggleThumbnail = () => { }
}) => (
<>
<FormGroup controlId="autoload" key="autoload">
{service.autoload !== undefined && <Checkbox value="autoload" onChange={(e) => onChangeServiceProperty("autoload", e.target.checked)}
checked={!isNil(service.autoload) ? service.autoload : false}>
<Message msgId="catalog.autoload" />
</Checkbox>}
</FormGroup>
<FormGroup controlId="thumbnail" key="thumbnail">
<Checkbox
onChange={() => onToggleThumbnail()}
checked={!isNil(service.hideThumbnail) ? !service.hideThumbnail : true}>
<Message msgId="catalog.showPreview" />
</Checkbox>
</FormGroup>
}) => {
const experimentalInteractiveLegend = getMiscSetting('experimentalInteractiveLegend', false);
return (
<>
<FormGroup controlId="autoload" key="autoload">
{service.autoload !== undefined && <Checkbox value="autoload" onChange={(e) => onChangeServiceProperty("autoload", e.target.checked)}
checked={!isNil(service.autoload) ? service.autoload : false}>
<Message msgId="catalog.autoload" />
</Checkbox>}
</FormGroup>
<FormGroup controlId="thumbnail" key="thumbnail">
<Checkbox
onChange={() => onToggleThumbnail()}
checked={!isNil(service.hideThumbnail) ? !service.hideThumbnail : true}>
<Message msgId="catalog.showPreview" />
</Checkbox>
</FormGroup>

{!isNil(service.type) && service.type === "cog" &&
{!isNil(service.type) && service.type === "cog" &&
<FormGroup controlId="fetchMetadata" key="fetchMetadata">
<Checkbox
onChange={(e) => onChangeServiceProperty("fetchMetadata", e.target.checked)}
checked={!isNil(service.fetchMetadata) ? service.fetchMetadata : true}>
<Message msgId="catalog.fetchMetadata.label" />&nbsp;<InfoPopover text={<Message msgId="catalog.fetchMetadata.tooltip" />} />
</Checkbox>
</FormGroup>}
{children}
</>
);
{experimentalInteractiveLegend && ['wfs'].includes(service.type) && <FormGroup className="wfs-interactive-legend" controlId="enableInteractiveLegend" key="enableInteractiveLegend">
<Checkbox data-qa="display-interactive-legend-option"
onChange={(e) => onChangeServiceProperty("layerOptions", { ...service.layerOptions, enableInteractiveLegend: e.target.checked})}
checked={!isNil(service.layerOptions?.enableInteractiveLegend) ? service.layerOptions?.enableInteractiveLegend : false}>
<Message msgId="layerProperties.enableInteractiveLegendInfo.label" />
&nbsp;<InfoPopover text={<Message msgId="layerProperties.enableInteractiveLegendInfo.infoWithoutGSNote" />} />
</Checkbox>
</FormGroup>}
{children}
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,18 @@ import ReactDOM from "react-dom";
import CommonAdvancedSettings from "../CommonAdvancedSettings";
import expect from "expect";
import TestUtils from "react-dom/test-utils";
import { setConfigProp } from '../../../../../utils/ConfigUtils';

describe('Test common advanced settings', () => {
beforeEach((done) => {
document.body.innerHTML = '<div id="container"></div>';
setConfigProp('miscSettings', { experimentalInteractiveLegend: true });
mahmoudadel54 marked this conversation as resolved.
Show resolved Hide resolved
setTimeout(done);
});
afterEach((done) => {
ReactDOM.unmountComponentAtNode(document.getElementById("container"));
document.body.innerHTML = '';
setConfigProp('miscSettings', { });
setTimeout(done);
});
it('creates the component with defaults', () => {
Expand All @@ -38,7 +41,7 @@ describe('Test common advanced settings', () => {
const advancedSettingPanel = document.getElementsByClassName("mapstore-switch-panel");
expect(advancedSettingPanel).toBeTruthy();
const fields = document.querySelectorAll(".form-group");
expect(fields.length).toBe(2);
expect(fields.length).toBe(3);
});
it('test wms advanced options onChangeServiceProperty autoreload', () => {
const action = {
Expand All @@ -52,7 +55,7 @@ describe('Test common advanced settings', () => {
const advancedSettingPanel = document.getElementsByClassName("mapstore-switch-panel");
expect(advancedSettingPanel).toBeTruthy();
const fields = document.querySelectorAll(".form-group");
expect(fields.length).toBe(2);
expect(fields.length).toBe(3);
const autoload = document.querySelectorAll('input[type="checkbox"]')[0];
const formGroup = document.querySelectorAll('.form-group')[0];
expect(formGroup.textContent.trim()).toBe('catalog.autoload');
Expand Down Expand Up @@ -85,4 +88,14 @@ describe('Test common advanced settings', () => {
expect(spyOn).toHaveBeenCalled();
expect(spyOn.calls[1].arguments).toEqual([ 'fetchMetadata', false ]);
});
it('test showing/hiding interactive legend checkbox', () => {
ReactDOM.render(<CommonAdvancedSettings
service={{type: "wfs"}}
/>, document.getElementById("container"));
const interactiveLegendCheckboxInput = document.querySelector(".wfs-interactive-legend .checkbox input[data-qa='display-interactive-legend-option']");
expect(interactiveLegendCheckboxInput).toBeTruthy();
const interactiveLegendLabel = document.querySelector(".wfs-interactive-legend .checkbox span");
expect(interactiveLegendLabel).toBeTruthy();
expect(interactiveLegendLabel.innerHTML).toEqual('layerProperties.enableInteractiveLegendInfo.label');
});
});
4 changes: 4 additions & 0 deletions web/client/plugins/TOC/components/DefaultLayer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ const NodeLegend = ({
<li>
{visible ? <VectorLegend
style={node?.style}
layer={node}
interactive
onChange={onChange}
/> : null}
</li>
</>
Expand All @@ -81,6 +84,7 @@ const NodeLegend = ({
language={config?.language}
{...config?.layerOptions?.legendOptions}
onChange={onChange}
interactive
/> : null}
</li>
</>
Expand Down
13 changes: 9 additions & 4 deletions web/client/plugins/TOC/components/StyleBasedWMSJsonLegend.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class StyleBasedWMSJsonLegend extends React.Component {
scaleDependent: PropTypes.bool,
language: PropTypes.string,
onChange: PropTypes.func,
owner: PropTypes.string,
interactive: PropTypes.bool, // the indicator flag that refers if this legend is interactive or not
projection: PropTypes.string,
mapSize: PropTypes.object,
mapBbox: PropTypes.object
Expand All @@ -56,7 +56,7 @@ class StyleBasedWMSJsonLegend extends React.Component {
style: {maxWidth: "100%"},
scaleDependent: true,
onChange: () => {},
owner: ''
interactive: false
};
state = {
error: false,
Expand Down Expand Up @@ -159,6 +159,7 @@ class StyleBasedWMSJsonLegend extends React.Component {
const interactiveLegendFilters = get(layerFilter, 'filters', []).find(f => f.id === INTERACTIVE_LEGEND_ID);
const legendFilters = get(interactiveLegendFilters, 'filters', []);
const showResetWarning = !this.checkPreviousFiltersAreValid(rules, legendFilters) && !layerFilter.disabled;
const isNotInteractiveLegend = !this.props.interactive;
return (
<>
{showResetWarning ? <Alert bsStyle="warning">
Expand All @@ -176,11 +177,15 @@ class StyleBasedWMSJsonLegend extends React.Component {
: rules.map((rule, idx) => {
const activeFilter = legendFilters?.some(f => f.id === rule.filter);
const isFilterDisabled = this.props?.layer?.layerFilter?.disabled;
const isLegendFilterNotApplicable = isFilterDisabled || isNotInteractiveLegend || !rule?.filter;
return (
<div
className={`wms-json-legend-rule ${isFilterDisabled || this.props.owner === 'legendPreview' || !rule?.filter ? "" : "filter-enabled "} ${activeFilter ? 'active' : ''}`}
className={`wms-json-legend-rule ${isLegendFilterNotApplicable ? "" : "filter-enabled "} ${activeFilter ? 'active' : ''}`}
key={`${rule.filter}-${idx}`}
onClick={() => this.filterWMSLayerHandler(rule.filter)}>
onClick={() => {
if (isLegendFilterNotApplicable) return;
this.filterWMSLayerHandler(rule.filter);
}}>
<WMSJsonLegendIcon rule={rule} />
<span>{rule.name || rule.title || ''}</span>
</div>
Expand Down
Loading
Loading