diff --git a/web/client/components/TOC/fragments/settings/FeatureInfoEditor.jsx b/web/client/components/TOC/fragments/settings/FeatureInfoEditor.jsx index 00a5005a43..832e955fcf 100644 --- a/web/client/components/TOC/fragments/settings/FeatureInfoEditor.jsx +++ b/web/client/components/TOC/fragments/settings/FeatureInfoEditor.jsx @@ -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 ( + + } + size="lg" + showFullscreen + clickOutEnabled={false} + onClose={onClose} + buttons={[ + { + bsStyle: 'primary', + text: , + onClick: onClose + } + ]}> +
+ { + const previousHTML = draftJSEditorStateToHtml(editorState); + const newHTML = draftJSEditorStateToHtml(newEditorState); + if (newHTML !== previousHTML) { + onChange({ template: draftJSEditorStateToHtml(newEditorState) }); + setEditorState(newEditorState); + } + }} + /> +
+
+
+ ); +}; - 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 ( - - } - size="lg" - showFullscreen - clickOutEnabled={false} - onClose={() => this.close()} - buttons={[ - { - bsStyle: 'primary', - text: , - onClick: () => this.close() - } - ]}> -
- { if (quill) { this.quill = quill; onReady(quill); } } } - modules={(toolbarConfig) => enableIFrameModule ? { - resizeModule: {}, - toolbar: toolbarConfig - } : {}} - defaultValue={this.state.template} - onChange={template => this.setState({ template })}/> -
-
-
- ); - } +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; diff --git a/web/client/components/TOC/fragments/settings/__tests__/FeatureInfoEditor-test.jsx b/web/client/components/TOC/fragments/settings/__tests__/FeatureInfoEditor-test.jsx index 95bdd42b4f..5f4f2ecf17 100644 --- a/web/client/components/TOC/fragments/settings/__tests__/FeatureInfoEditor-test.jsx +++ b/web/client/components/TOC/fragments/settings/__tests__/FeatureInfoEditor-test.jsx @@ -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'; @@ -31,71 +30,4 @@ describe("test FeatureInfoEditor", () => { expect(modalEditor.length).toBe(1); }); - it('test rendering close x', (done) => { - - const template = '

html

'; - - ReactDOM.render( { - 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 = '

html

'; - - ReactDOM.render( { - 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); - }); - }); diff --git a/web/client/components/data/identify/viewers/TemplateViewer.jsx b/web/client/components/data/identify/viewers/TemplateViewer.jsx index eba8fa2db7..4aa249de90 100644 --- a/web/client/components/data/identify/viewers/TemplateViewer.jsx +++ b/web/client/components/data/identify/viewers/TemplateViewer.jsx @@ -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}) => (
{response.features.map((feature, i) => { const cleanTemplate = getCleanTemplate(layer.featureInfo && layer.featureInfo.template || '', feature, /\$\{.*?\}/g, 2, 1); @@ -33,3 +35,10 @@ export default ({layer = {}, response}) => ( )}
); + +export default TemplateViewer; + +TemplateViewer.propTypes = { + response: PropTypes.object, + layer: PropTypes.object +}; diff --git a/web/client/components/mapviews/settings/CompactRichTextEditor.jsx b/web/client/components/mapviews/settings/CompactRichTextEditor.jsx index c0d3ec9ab7..c862d8ea69 100644 --- a/web/client/components/mapviews/settings/CompactRichTextEditor.jsx +++ b/web/client/components/mapviews/settings/CompactRichTextEditor.jsx @@ -9,8 +9,8 @@ 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) => { @@ -43,6 +43,7 @@ export const resizeBase64Image = (src, options) => { function CompactRichTextEditor({ wrapperClassName = 'ms-compact-text-editor', + toolbarOptions, ...props }) { @@ -52,7 +53,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 diff --git a/web/client/themes/default/less/map-views.less b/web/client/themes/default/less/map-views.less index b354ba823f..364858160f 100644 --- a/web/client/themes/default/less/map-views.less +++ b/web/client/themes/default/less/map-views.less @@ -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]); @@ -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;