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

#9254 Add parametric image parsing to template viewer #9260

Merged
merged 13 commits into from
Jul 5, 2023
143 changes: 71 additions & 72 deletions web/client/components/TOC/fragments/settings/FeatureInfoEditor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,97 +6,96 @@
* LICENSE file in the root directory of this source tree.
*/

import React, { useState } from 'react';
import PropTypes from 'prop-types';
import React from 'react';
import ReactQuill from '../../../../libs/quill/react-quill-suspense';

import Message from '../../../I18N/Message';
import Portal from '../../../misc/Portal';
import ResizableModal from '../../../misc/ResizableModal';
import CompactRichTextEditor from '../../../mapviews/settings/CompactRichTextEditor';
import withDebounceOnCallback from '../../../misc/enhancers/withDebounceOnCallback';
import { htmlToDraftJSEditorState, draftJSEditorStateToHtml } from '../../../../utils/EditorUtils';

const DescriptionEditor = withDebounceOnCallback('onEditorStateChange', 'editorState')(CompactRichTextEditor);
/**
* Component for rendering FeatureInfoEditor a modal editor to modify format template
* @memberof components.TOC.fragments.settings
* @name FeatureInfoEditor
* @class
* @prop {object} element data of the current selected node
* @prop {bool} showEditor show/hide modal
* @prop {funciotn} onShowEditor called when click on close buttons
* @prop {boolean} showEditor show/hide modal
* @prop {function} onShowEditor called when click on close buttons
* @prop {function} onChange called when text in editor has been changed
* @prop {bool} enableIFrameModule enable iframe in editor, default true
* @prop {boolean} enableIFrameModule enable iframe in editor, default true
*/

class FeatureInfoEditor extends React.Component {
const FeatureInfoEditor = ({
element,
showEditor,
onShowEditor,
onChange,
enableIFrameModule
}) => {

static propTypes = {
showEditor: PropTypes.bool,
element: PropTypes.object,
onChange: PropTypes.func,
onShowEditor: PropTypes.func,
enableIFrameModule: PropTypes.bool,
onReady: PropTypes.func
};

static defaultProps = {
showEditor: false,
element: {},
enableIFrameModule: false,
onChange: () => {},
onShowEditor: () => {}
};

state = {
template: ' '
const [editorState, setEditorState] = useState(htmlToDraftJSEditorState(element?.featureInfo?.template || ''));
const onClose = () => {
onShowEditor(!showEditor);
onChange('featureInfo', {
...(element && element.featureInfo || {}),
template: draftJSEditorStateToHtml(editorState)
});
};
return (
<Portal>
<ResizableModal
fade
show={showEditor}
title={<Message msgId="layerProperties.editCustomFormat"/>}
size="lg"
showFullscreen
clickOutEnabled={false}
onClose={onClose}
buttons={[
{
bsStyle: 'primary',
text: <Message msgId="close"/>,
onClick: onClose
}
]}>
<div id="ms-template-editor" className="ms-editor">
<DescriptionEditor
toolbarOptions={['fontFamily', 'blockType', 'inline', 'textAlign', 'list', 'link', 'colorPicker', 'remove', 'image'].concat(enableIFrameModule ? ['embedded'] : [])}
editorState={editorState}
onEditorStateChange={(newEditorState) => {
const previousHTML = draftJSEditorStateToHtml(editorState);
const newHTML = draftJSEditorStateToHtml(newEditorState);
if (newHTML !== previousHTML) {
onChange({ template: draftJSEditorStateToHtml(newEditorState) });
setEditorState(newEditorState);
}
}}
/>
</div>
</ResizableModal>
</Portal>
);
};

UNSAFE_componentWillMount() {
this.setState({
template: this.props.element && this.props.element.featureInfo && this.props.element.featureInfo.template || ' '
});
}
FeatureInfoEditor.propTypes = {
showEditor: PropTypes.bool,
element: PropTypes.object,
onChange: PropTypes.func,
onShowEditor: PropTypes.func,
enableIFrameModule: PropTypes.bool
};

render() {
const { showEditor, enableIFrameModule = true, onReady = () => {} } = this.props;
return (
<Portal>
<ResizableModal
fade
show={showEditor}
title={<Message msgId="layerProperties.editCustomFormat"/>}
size="lg"
showFullscreen
clickOutEnabled={false}
onClose={() => this.close()}
buttons={[
{
bsStyle: 'primary',
text: <Message msgId="close"/>,
onClick: () => this.close()
}
]}>
<div id="ms-template-editor" className="ms-editor">
<ReactQuill
bounds="#ms-template-editor"
ref={(quill) => { if (quill) { this.quill = quill; onReady(quill); } } }
modules={(toolbarConfig) => enableIFrameModule ? {
resizeModule: {},
toolbar: toolbarConfig
} : {}}
defaultValue={this.state.template}
onChange={template => this.setState({ template })}/>
</div>
</ResizableModal>
</Portal>
);
}
FeatureInfoEditor.defaultProps = {
showEditor: false,
element: {},
enableIFrameModule: false,
onChange: () => {},
onShowEditor: () => {}
};

close = () => {
this.props.onShowEditor(!this.props.showEditor);
this.props.onChange('featureInfo', {
...(this.props.element && this.props.element.featureInfo || {}),
template: this.state.template
});
};
}

export default FeatureInfoEditor;
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import expect from 'expect';
import React from 'react';
import ReactDOM from 'react-dom';
import TestUtils from 'react-dom/test-utils';

import FeatureInfoEditor from '../FeatureInfoEditor';

Expand All @@ -31,71 +30,4 @@ describe("test FeatureInfoEditor", () => {
expect(modalEditor.length).toBe(1);
});

it('test rendering close x', (done) => {

const template = '<p>html</p>';

ReactDOM.render(<FeatureInfoEditor
onReady={(quill) => {
try {
// edit template
const editor = quill.getEditor();
editor.clipboard.dangerouslyPasteHTML(template);
const btns = document.getElementsByClassName('ms-header-btn');
expect(btns.length).toBe(2);
TestUtils.Simulate.click(btns[1]);
} catch (e) {
done(e);
}
}}
onShowEditor={(value) => {
expect(value).toBe(false);
}}
onChange={(key, value) => {
if (value.template === template) {
expect(key).toBe('featureInfo');
expect(value).toEqual({ template });
done();
}
}}
showEditor/>, document.getElementById("container"));

const modalEditor = document.getElementsByClassName('ms-resizable-modal');
expect(modalEditor.length).toBe(1);

});

it('test rendering close button', (done) => {

const template = '<p>html</p>';

ReactDOM.render(<FeatureInfoEditor
onReady={(quill) => {
try {
// edit template
const editor = quill.getEditor();
editor.clipboard.dangerouslyPasteHTML(template);
const btns = document.getElementsByClassName('btn');
expect(btns.length).toBe(1);
TestUtils.Simulate.click(btns[0]);
} catch (e) {
done(e);
}
}}
onShowEditor={(value) => {
expect(value).toBe(false);
}}
onChange={(key, value) => {
if (value.template === template) {
expect(key).toBe('featureInfo');
expect(value).toEqual({ template });
done();
}
}}
showEditor
/>, document.getElementById("container"));
const modalEditor = document.getElementsByClassName('ms-resizable-modal');
expect(modalEditor.length).toBe(1);
});

});
13 changes: 11 additions & 2 deletions web/client/components/data/identify/viewers/TemplateViewer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
*/

import React from 'react';

import { template } from 'lodash';
import PropTypes from 'prop-types';

import { getCleanTemplate } from '../../../../utils/TemplateUtils';
import HtmlRenderer from '../../../misc/HtmlRenderer';
import Message from '../../../I18N/Message';

export default ({layer = {}, response}) => (

const TemplateViewer = ({layer = {}, response}) => (
<div className="ms-template-viewer">
{response.features.map((feature, i) => {
const cleanTemplate = getCleanTemplate(layer.featureInfo && layer.featureInfo.template || '', feature, /\$\{.*?\}/g, 2, 1);
Expand All @@ -33,3 +35,10 @@ export default ({layer = {}, response}) => (
)}
</div>
);

export default TemplateViewer;

TemplateViewer.propTypes = {
response: PropTypes.object,
layer: PropTypes.object
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
* LICENSE file in the root directory of this source tree.
*/

import PropTypes from 'prop-types';
MV88 marked this conversation as resolved.
Show resolved Hide resolved
import React from 'react';
import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css';
import { Editor } from 'react-draft-wysiwyg';
import { DEFAULT_FONT_FAMILIES } from '../../../utils/GeoStoryUtils';
import embed from 'embed-video';
import { DEFAULT_FONT_FAMILIES } from '../../../utils/GeoStoryUtils';

export const resizeBase64Image = (src, options) => {
return new Promise((resolve, reject) => {
Expand Down Expand Up @@ -43,6 +44,7 @@ export const resizeBase64Image = (src, options) => {

function CompactRichTextEditor({
wrapperClassName = 'ms-compact-text-editor',
toolbarOptions,
...props
}) {

Expand All @@ -52,7 +54,7 @@ function CompactRichTextEditor({
editorStyle={{ minHeight: 200 }}
wrapperClassName={wrapperClassName}
toolbar={{
options: ['fontFamily', 'blockType', 'inline', 'textAlign', 'list', 'link', 'colorPicker', 'remove', 'image', 'embedded'],
options: toolbarOptions || ['fontFamily', 'blockType', 'inline', 'textAlign', 'list', 'link', 'colorPicker', 'remove', 'image', 'embedded'],
image: {
urlEnabled: true,
// disable the upload at the moment
Expand Down
4 changes: 2 additions & 2 deletions web/client/themes/default/less/map-views.less
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
.border-color-var(@theme-vars[main-border-color]);
}
}

.ms-map-views {
.ms-map-views-wrapper {
.background-color-var(@theme-vars[main-bg]);
Expand Down Expand Up @@ -90,9 +90,9 @@
.rdw-editor-toolbar {
position: sticky;
z-index: 20;
top: 30px;
}
.rdw-editor-main {
height: auto;
padding: 0 4px;
iframe {
aspect-ratio: 16 / 9;
Expand Down