From 73ed313a9faef949926a57d314795a781042c9d3 Mon Sep 17 00:00:00 2001 From: Thibaud Colas Date: Thu, 11 Jan 2018 00:51:11 +0200 Subject: [PATCH] Update Link and Document decorators to Draftail 0.10 API --- client/src/components/Draftail/Draftail.scss | 2 + .../Draftail/decorators/Document.js | 20 ++-- .../Draftail/decorators/Document.test.js | 25 ----- .../components/Draftail/decorators/Link.js | 19 ++-- .../Draftail/decorators/Link.test.js | 40 ------- .../Draftail/decorators/TooltipEntity.js | 100 ++++++++++++++++++ .../Draftail/decorators/TooltipEntity.scss | 12 +++ .../__snapshots__/Document.test.js.snap | 18 ---- .../__snapshots__/Link.test.js.snap | 33 ------ .../components/Draftail/decorators/index.js | 10 -- .../Draftail/decorators/index.test.js | 7 -- client/src/components/Draftail/index.js | 15 +-- 12 files changed, 147 insertions(+), 154 deletions(-) delete mode 100644 client/src/components/Draftail/decorators/Document.test.js delete mode 100644 client/src/components/Draftail/decorators/Link.test.js create mode 100644 client/src/components/Draftail/decorators/TooltipEntity.js create mode 100644 client/src/components/Draftail/decorators/TooltipEntity.scss delete mode 100644 client/src/components/Draftail/decorators/__snapshots__/Document.test.js.snap delete mode 100644 client/src/components/Draftail/decorators/__snapshots__/Link.test.js.snap delete mode 100644 client/src/components/Draftail/decorators/index.js delete mode 100644 client/src/components/Draftail/decorators/index.test.js diff --git a/client/src/components/Draftail/Draftail.scss b/client/src/components/Draftail/Draftail.scss index d063eb135..958368dc9 100644 --- a/client/src/components/Draftail/Draftail.scss +++ b/client/src/components/Draftail/Draftail.scss @@ -37,6 +37,8 @@ $color-editor-chrome-accent: lighten($color-editor-chrome, 20%); @import './Tooltip/Tooltip'; +@import './decorators/TooltipEntity'; + @import './blocks/MediaBlock'; @import './blocks/ImageBlock'; @import './blocks/EmbedBlock'; diff --git a/client/src/components/Draftail/decorators/Document.js b/client/src/components/Draftail/decorators/Document.js index a30b557fb..beb46c6cf 100644 --- a/client/src/components/Draftail/decorators/Document.js +++ b/client/src/components/Draftail/decorators/Document.js @@ -1,21 +1,25 @@ import PropTypes from 'prop-types'; import React from 'react'; -import { Icon } from 'draftail'; -const Document = ({ entityKey, contentState, children }) => { - const { title } = contentState.getEntity(entityKey).getData(); +import Icon from '../../Icon/Icon'; + +import TooltipEntity from '../decorators/TooltipEntity'; + +const Document = props => { + const { entityKey, contentState } = props; + const { url } = contentState.getEntity(entityKey).getData(); return ( - - - {children} - + } + label={url.replace(/(^\w+:|^)\/\//, '').split('/')[0]} + /> ); }; Document.propTypes = { entityKey: PropTypes.string.isRequired, contentState: PropTypes.object.isRequired, - children: PropTypes.node.isRequired, }; export default Document; diff --git a/client/src/components/Draftail/decorators/Document.test.js b/client/src/components/Draftail/decorators/Document.test.js deleted file mode 100644 index ebd22fffa..000000000 --- a/client/src/components/Draftail/decorators/Document.test.js +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react'; -import { shallow } from 'enzyme'; -import { convertFromHTML, ContentState } from 'draft-js'; -import Document from './Document'; - -describe('Document', () => { - it('exists', () => { - expect(Document).toBeDefined(); - }); - - it('renders', () => { - const contentBlocks = convertFromHTML('

aaaaaaaaaa

'); - const contentState = ContentState.createFromBlockArray(contentBlocks); - const contentStateWithEntity = contentState.createEntity('DOCUMENT', 'MUTABLE', { title: 'Test title' }); - const entityKey = contentStateWithEntity.getLastCreatedEntityKey(); - expect(shallow(( - - Test children - - ))).toMatchSnapshot(); - }); -}); diff --git a/client/src/components/Draftail/decorators/Link.js b/client/src/components/Draftail/decorators/Link.js index 7056a07eb..7f9652cbe 100644 --- a/client/src/components/Draftail/decorators/Link.js +++ b/client/src/components/Draftail/decorators/Link.js @@ -1,22 +1,27 @@ import PropTypes from 'prop-types'; import React from 'react'; -import { Icon } from 'draftail'; -const Link = ({ entityKey, contentState, children }) => { +import Icon from '../../Icon/Icon'; + +import TooltipEntity from '../decorators/TooltipEntity'; + +const Link = props => { + const { entityKey, contentState } = props; const { url } = contentState.getEntity(entityKey).getData(); + const icon = url.startsWith('mailto:') ? 'mail' : 'link'; return ( - - - {children} - + } + label={url.replace(/(^\w+:|^)\/\//, '').split('/')[0]} + /> ); }; Link.propTypes = { entityKey: PropTypes.string.isRequired, contentState: PropTypes.object.isRequired, - children: PropTypes.node.isRequired, }; export default Link; diff --git a/client/src/components/Draftail/decorators/Link.test.js b/client/src/components/Draftail/decorators/Link.test.js deleted file mode 100644 index 5bc391c46..000000000 --- a/client/src/components/Draftail/decorators/Link.test.js +++ /dev/null @@ -1,40 +0,0 @@ -import React from 'react'; -import { shallow } from 'enzyme'; -import { convertFromHTML, ContentState } from 'draft-js'; -import Link from './Link'; - -describe('Link', () => { - it('exists', () => { - expect(Link).toBeDefined(); - }); - - it('renders', () => { - const contentBlocks = convertFromHTML('

aaaaaaaaaa

'); - const contentState = ContentState.createFromBlockArray(contentBlocks); - const contentStateWithEntity = contentState.createEntity('LINK', 'MUTABLE', { url: 'http://example.com/' }); - const entityKey = contentStateWithEntity.getLastCreatedEntityKey(); - expect(shallow(( - - Test children - - ))).toMatchSnapshot(); - }); - - it('renders email', () => { - const contentBlocks = convertFromHTML('

aaaaaaaaaa

'); - const contentState = ContentState.createFromBlockArray(contentBlocks); - const contentStateWithEntity = contentState.createEntity('LINK', 'MUTABLE', { url: 'mailto:test@example.com' }); - const entityKey = contentStateWithEntity.getLastCreatedEntityKey(); - expect(shallow(( - - Test children - - ))).toMatchSnapshot(); - }); -}); diff --git a/client/src/components/Draftail/decorators/TooltipEntity.js b/client/src/components/Draftail/decorators/TooltipEntity.js new file mode 100644 index 000000000..14738df32 --- /dev/null +++ b/client/src/components/Draftail/decorators/TooltipEntity.js @@ -0,0 +1,100 @@ +import PropTypes from 'prop-types'; +import React, { Component } from 'react'; +import { Icon } from 'draftail'; + +import Tooltip from '../Tooltip/Tooltip'; +import Portal from '../../Portal/Portal'; + +class TooltipEntity extends Component { + constructor(props) { + super(props); + + this.state = { + showTooltipAt: null, + }; + + this.openTooltip = this.openTooltip.bind(this); + this.closeTooltip = this.closeTooltip.bind(this); + } + + openTooltip(e) { + const trigger = e.target; + this.setState({ showTooltipAt: trigger.getBoundingClientRect() }); + } + + closeTooltip() { + this.setState({ showTooltipAt: null }); + } + + render() { + const { + entityKey, + contentState, + children, + onEdit, + onRemove, + icon, + label, + } = this.props; + const { showTooltipAt } = this.state; + const { url } = contentState.getEntity(entityKey).getData(); + + // Contrary to what JSX A11Y says, this should be a button but it shouldn't be focusable. + /* eslint-disable springload/jsx-a11y/interactive-supports-focus */ + return ( + + + {children} + {showTooltipAt && ( + + + + {label} + + + + + + + + )} + + ); + } +} + +TooltipEntity.propTypes = { + entityKey: PropTypes.string.isRequired, + contentState: PropTypes.object.isRequired, + children: PropTypes.node.isRequired, + onEdit: PropTypes.func.isRequired, + onRemove: PropTypes.func.isRequired, + icon: PropTypes.oneOfType([ + PropTypes.string.isRequired, + PropTypes.object.isRequired, + ]).isRequired, + label: PropTypes.string.isRequired, +}; + +export default TooltipEntity; diff --git a/client/src/components/Draftail/decorators/TooltipEntity.scss b/client/src/components/Draftail/decorators/TooltipEntity.scss new file mode 100644 index 000000000..abda1d6d4 --- /dev/null +++ b/client/src/components/Draftail/decorators/TooltipEntity.scss @@ -0,0 +1,12 @@ +.TooltipEntity { + background: $color-light-blue; + border-bottom: 1px dotted $color-teal; + cursor: pointer; + + &__icon { + color: $color-teal; + margin-right: 0.2em; + width: 1em; + height: 1em; + } +} diff --git a/client/src/components/Draftail/decorators/__snapshots__/Document.test.js.snap b/client/src/components/Draftail/decorators/__snapshots__/Document.test.js.snap deleted file mode 100644 index ba3267df8..000000000 --- a/client/src/components/Draftail/decorators/__snapshots__/Document.test.js.snap +++ /dev/null @@ -1,18 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Document renders 1`] = ` - - - - Test children - - -`; diff --git a/client/src/components/Draftail/decorators/__snapshots__/Link.test.js.snap b/client/src/components/Draftail/decorators/__snapshots__/Link.test.js.snap deleted file mode 100644 index 59631823e..000000000 --- a/client/src/components/Draftail/decorators/__snapshots__/Link.test.js.snap +++ /dev/null @@ -1,33 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Link renders 1`] = ` - - - - Test children - - -`; - -exports[`Link renders email 1`] = ` - - - - Test children - - -`; diff --git a/client/src/components/Draftail/decorators/index.js b/client/src/components/Draftail/decorators/index.js deleted file mode 100644 index 0997c8d40..000000000 --- a/client/src/components/Draftail/decorators/index.js +++ /dev/null @@ -1,10 +0,0 @@ -import Link from './Link'; -import Document from './Document'; - -/** - * Mapping object from name to component. - */ -export default { - Link, - Document, -}; diff --git a/client/src/components/Draftail/decorators/index.test.js b/client/src/components/Draftail/decorators/index.test.js deleted file mode 100644 index 521c0f419..000000000 --- a/client/src/components/Draftail/decorators/index.test.js +++ /dev/null @@ -1,7 +0,0 @@ -import entities from './index'; - -describe('entities', () => { - it('exists', () => { - expect(entities).toBeInstanceOf(Object); - }); -}); diff --git a/client/src/components/Draftail/index.js b/client/src/components/Draftail/index.js index 89dbef03a..a103327c8 100644 --- a/client/src/components/Draftail/index.js +++ b/client/src/components/Draftail/index.js @@ -4,8 +4,9 @@ import DraftailEditor from 'draftail'; import Icon from '../Icon/Icon'; -import decorators from './decorators'; import sources from './sources'; +import Link from './decorators/Link'; +import Document from './decorators/Document'; import ImageBlock from './blocks/ImageBlock'; import EmbedBlock from './blocks/EmbedBlock'; @@ -19,7 +20,7 @@ const wrapWagtailIcon = type => { } return type; -} +}; export const initEditor = (fieldName, options = {}) => { const field = document.querySelector(`[name="${fieldName}"]`); @@ -50,7 +51,7 @@ export const initEditor = (fieldName, options = {}) => { strategy: registry.getStrategy(type.type) || null, decorator: registry.getDecorator(type.decorator), block: registry.getBlock(type.block), - }), + }) ); } @@ -77,9 +78,11 @@ export const initEditor = (fieldName, options = {}) => { ReactDOM.render(editor, editorWrapper); }; -// Register default Decorators and Sources -registry.registerDecorators(decorators); registry.registerSources(sources); +registry.registerDecorators({ + Link, + Document, +}); registry.registerBlocks({ ImageBlock, EmbedBlock, @@ -93,7 +96,7 @@ const draftail = Object.assign( // createClass: React.createClass, // createElement: React.createElement, }, - registry, + registry ); export default draftail;