Skip to content

Commit

Permalink
#7585: refactor of the Print plugin as a plugins container
Browse files Browse the repository at this point in the history
  • Loading branch information
mbarto committed Nov 23, 2021
1 parent 3a9a1a2 commit 4dfd8db
Show file tree
Hide file tree
Showing 16 changed files with 739 additions and 244 deletions.
129 changes: 56 additions & 73 deletions web/client/plugins/Print.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { head } from 'lodash';
import assign from 'object-assign';
import PropTypes from 'prop-types';
import React from 'react';
import { Accordion, Col, Glyphicon, Grid, Panel, Row } from 'react-bootstrap';
import { PanelGroup, Col, Glyphicon, Grid, Panel, Row } from 'react-bootstrap';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';

Expand All @@ -30,6 +30,7 @@ import { reprojectBbox } from '../utils/CoordinatesUtils';
import { getMessageById } from '../utils/LocaleUtils';
import { defaultGetZoomForExtent, getResolutions, mapUpdated, dpi2dpu, DEFAULT_SCREEN_DPI } from '../utils/MapUtils';
import { isInsideResolutionsLimits } from '../utils/LayersUtils';
import { getPluginItems } from "../utils/PluginsUtils";

/**
* Print plugin. This plugin allows to print current map view. **note**: this plugin requires the **printing module** to work.
Expand Down Expand Up @@ -81,20 +82,7 @@ export default {
loadPlugin: (resolve) => {
require.ensure('./print/index', () => {
const {
Name,
Description,
Resolution,
DefaultBackgroundOption,
Sheet,
LegendOption,
MultiPageOption,
LandscapeOption,
ForceLabelsOption,
AntiAliasingOption,
IconSizeOption,
LegendDpiOption,
Font,
MapPreview,
defaultItems,
PrintSubmit,
PrintPreview
} = require('./print/index').default;
Expand Down Expand Up @@ -151,11 +139,14 @@ export default {
currentLocaleLanguage: PropTypes.string,
overrideOptions: PropTypes.object,
isLocalizedLayerStylesEnabled: PropTypes.bool,
localizedLayerStylesEnv: PropTypes.object
localizedLayerStylesEnv: PropTypes.object,
items: PropTypes.array
};

static contextTypes = {
messages: PropTypes.object
messages: PropTypes.object,
plugins: PropTypes.object,
loadedPlugins: PropTypes.object
};

static defaultProps = {
Expand All @@ -177,19 +168,6 @@ export default {
mapType: "leaflet",
minZoom: 1,
maxZoom: 23,
alternatives: [{
name: "legend",
component: LegendOption,
regex: /legend/
}, {
name: "2pages",
component: MultiPageOption,
regex: /2_pages/
}, {
name: "landscape",
component: LandscapeOption,
regex: /landscape/
}],
usePreview: true,
mapPreviewOptions: {
enableScalebox: false,
Expand All @@ -213,10 +191,19 @@ export default {
},
style: {},
currentLocale: 'en-US',
overrideOptions: {}
overrideOptions: {},
items: []
};

state = {
items: [],
activeAccordionPanel: 0
}

UNSAFE_componentWillMount() {
this.setState({
items: getPluginItems({}, this.context.plugins, defaultItems, "Print", "", true, this.context.loadedPlugins)
});
this.configurePrintMap();
}

Expand All @@ -228,7 +215,11 @@ export default {
this.configurePrintMap(nextProps);
}
}

getItems = (target) => {
return [...this.props.items, ...this.state.items]
.filter(i => !target || i.target === target)
.sort((i1, i2) => (i1.position ?? 0) - (i2.position ?? 0));
};
getMapSize = (layout) => {
const currentLayout = layout || this.getLayout();
return {
Expand All @@ -243,15 +234,6 @@ export default {
return head(capabilities.layouts.filter((l) => l.name === layoutName));
};

renderLayoutsAlternatives = () => {
return this.props.alternatives.map((alternative) =>
(<alternative.component key={"printoption_" + alternative.name}
label={getMessageById(this.context.messages, "print.alternatives." + alternative.name)}
enableRegex={alternative.regex}
/>)
);
};

renderPreviewPanel = () => {
return <PrintPreview {...this.props.previewOptions} role="body" prevPage={this.prevPage} nextPage={this.nextPage}/>;
};
Expand All @@ -269,48 +251,49 @@ export default {
}
return null;
};

renderItem = (item, options) => {
const Comp = item.plugin;
return <Comp {...this.props} {...item.cfg} {...options}/>;
};
renderItems = (target, options) => {
return this.getItems(target)
.map(item => this.renderItem(item, options));
};
renderAccordion = (target, options) => {
const items = this.getItems(target);
return (<PanelGroup accordion activeKey={this.state.activeAccordionPanel} onSelect={(key) => {
this.setState({
activeAccordionPanel: key
});
}}>
{items.map((item, pos) => (
<Panel header={getMessageById(this.context.messages, item.cfg.title)} eventKey={pos} collapsible>
{this.renderItem(item, options)}
</Panel>
))}
</PanelGroup>);
};
renderPrintPanel = () => {
const layout = this.getLayout();
const layoutName = this.props.getLayoutName(this.props.printSpec);
const mapSize = this.getMapSize(layout);
const options = {
layout,
layoutName: this.props.getLayoutName(this.props.printSpec),
mapSize: this.getMapSize(layout),
resolutions: getResolutions(),
onRefresh: () => this.configurePrintMap(),
notAllowedLayers: this.isBackgroundIgnored()
};
return (
<Grid fluid role="body">
{this.renderError()}
{this.renderWarning(layout)}
<Row>
<Col xs={12} md={6}>
<Name label={getMessageById(this.context.messages, 'print.title')} placeholder={getMessageById(this.context.messages, 'print.titleplaceholder')} />
<Description label={getMessageById(this.context.messages, 'print.description')} placeholder={getMessageById(this.context.messages, 'print.descriptionplaceholder')} />
<Accordion defaultActiveKey="1">
<Panel className="print-layout" header={getMessageById(this.context.messages, "print.layout")} eventKey="1" collapsible>
<Sheet key="sheetsize"
layouts={this.props.capabilities.layouts}
label={getMessageById(this.context.messages, "print.sheetsize")}
/>
{this.renderLayoutsAlternatives()}
</Panel>
<Panel className="print-legend-options" header={getMessageById(this.context.messages, "print.legendoptions")} eventKey="2" collapsible>
<Font label={getMessageById(this.context.messages, "print.legend.font")}/>
<ForceLabelsOption label={getMessageById(this.context.messages, "print.legend.forceLabels")}/>
<AntiAliasingOption label={getMessageById(this.context.messages, "print.legend.antiAliasing")}/>
<IconSizeOption label={getMessageById(this.context.messages, "print.legend.iconsSize")}/>
<LegendDpiOption label={getMessageById(this.context.messages, "print.legend.dpi")}/>
</Panel>
</Accordion>
{this.renderItems("left-panel", options)}
{this.renderAccordion("left-panel-accordion", options)}
</Col>
<Col xs={12} md={6} style={{textAlign: "center"}}>
<Resolution label={getMessageById(this.context.messages, "print.resolution")}/>
<MapPreview width={mapSize.width} height={mapSize.height} mapType={this.props.mapType}
onMapRefresh={() => this.configurePrintMap()}
layout={layoutName}
layoutSize={layout && layout.map || {width: 10, height: 10}}
resolutions={getResolutions()}
useFixedScales={this.props.useFixedScales}
env={this.props.localizedLayerStylesEnv}
{...this.props.mapPreviewOptions}
/>
{this.isBackgroundIgnored() ? <DefaultBackgroundOption label={getMessageById(this.context.messages, "print.defaultBackground")}/> : null}
{this.renderItems("right-panel", options)}
<PrintSubmit {...this.props.submitConfig} disabled={!layout} onPrint={this.print}/>
{this.renderDownload()}
</Col>
Expand Down
123 changes: 123 additions & 0 deletions web/client/plugins/__tests__/Print-test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import React from "react";
import ReactDOM from "react-dom";
import expect from "expect";

import Print from "../Print";
import { getLazyPluginForTest } from './pluginsTestUtils';
import TextInput from "../print/TextInput";
import Layout from "../print/Layout";
import LegendOptions from "../print/LegendOptions";
import Resolution from "../print/Resolution";
import MapPreview from "../print/MapPreview";
import Option from "../print/Option";

function getByXPath(xpath) {
return document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
}

const initialState = {
controls: {
print: {
enabled: true
}
},
print: {
spec: {},
capabilities: {
layouts: [],
dpis: [],
scales: []
}
}
};

const CustomComponent = () => {
return <div>print.mycustomplugin</div>;
};

function expectDefaultItems() {
expect(document.getElementById("mapstore-print-panel")).toExist();
expect(getByXPath("//*[text()='print.title']")).toExist();
expect(getByXPath("//*[text()='print.description']")).toExist();
expect(document.getElementById("print_preview")).toExist();
expect(getByXPath("//*[text()='print.sheetsize']")).toExist();
expect(getByXPath("//*[text()='print.alternatives.legend']")).toExist();
expect(getByXPath("//*[text()='print.alternatives.2pages']")).toExist();
expect(getByXPath("//*[text()='print.alternatives.landscape']")).toExist();
expect(getByXPath("//*[text()='print.alternatives.portrait']")).toExist();
expect(getByXPath("//*[text()='print.legend.font']")).toExist();
expect(getByXPath("//*[text()='print.legend.forceLabels']")).toExist();
expect(getByXPath("//*[text()='print.legend.iconsSize']")).toExist();
expect(getByXPath("//*[text()='print.legend.dpi']")).toExist();
expect(getByXPath("//*[text()='print.resolution']")).toExist();
}

function getPrintPlugin({items = [], layers = []} = {}) {
return getLazyPluginForTest({
plugin: Print,
storeState: {
...initialState,
layers,
print: {
...initialState.print,
map: {
center: {x: 0, y: 0},
layers
}
}
},
additionalPlugins: {
PrintTextInputPlugin: TextInput,
PrintLayoutPlugin: Layout,
PrintLegendOptionsPlugin: LegendOptions,
PrintResolution: Resolution,
PrintMapPreview: MapPreview,
PrintOption: Option
},
items
});
}

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

afterEach((done) => {
ReactDOM.unmountComponentAtNode(document.getElementById("container"));
document.body.innerHTML = '';
setTimeout(done);
});

it('default configuration', (done) => {
getPrintPlugin().then(({ Plugin }) => {
ReactDOM.render(<Plugin />, document.getElementById("container"));
expectDefaultItems();
done();
});
});

it('default configuration with not allowed layers', (done) => {
getPrintPlugin({
layers: [{visibility: true, type: "google"}]
}).then(({ Plugin }) => {
ReactDOM.render(<Plugin />, document.getElementById("container"));
expectDefaultItems();
expect(getByXPath("//*[text()='print.defaultBackground']")).toExist();
done();
});
});

it('custom plugin', (done) => {
getPrintPlugin({items: [{
plugin: CustomComponent,
target: "left-panel"
}]}).then(({ Plugin }) => {
ReactDOM.render(<Plugin />, document.getElementById("container"));
expectDefaultItems();
expect(getByXPath("//*[text()='print.mycustomplugin']")).toExist();
done();
});
});
});
Loading

0 comments on commit 4dfd8db

Please sign in to comment.