Update Link and Document decorators to Draftail 0.10 API

pull/4136/head
Thibaud Colas 2018-01-11 00:51:11 +02:00
rodzic 306bd8dd03
commit 73ed313a9f
12 zmienionych plików z 147 dodań i 154 usunięć

Wyświetl plik

@ -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';

Wyświetl plik

@ -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 (
<span data-tooltip={entityKey} className="RichEditor-link" title={title}>
<Icon name="icon-doc-full" />
{children}
</span>
<TooltipEntity
{...props}
icon={<Icon name="doc-full" />}
label={url.replace(/(^\w+:|^)\/\//, '').split('/')[0]}
/>
);
};
Document.propTypes = {
entityKey: PropTypes.string.isRequired,
contentState: PropTypes.object.isRequired,
children: PropTypes.node.isRequired,
};
export default Document;

Wyświetl plik

@ -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('<h1>aaaaaaaaaa</h1>');
const contentState = ContentState.createFromBlockArray(contentBlocks);
const contentStateWithEntity = contentState.createEntity('DOCUMENT', 'MUTABLE', { title: 'Test title' });
const entityKey = contentStateWithEntity.getLastCreatedEntityKey();
expect(shallow((
<Document
entityKey={entityKey}
contentState={contentStateWithEntity}
>
<span>Test children</span>
</Document>
))).toMatchSnapshot();
});
});

Wyświetl plik

@ -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 (
<span data-tooltip={entityKey} className="RichEditor-link">
<Icon name={`icon-${url.indexOf('mailto:') !== -1 ? 'mail' : 'link'}`} />
{children}
</span>
<TooltipEntity
{...props}
icon={<Icon name={icon} />}
label={url.replace(/(^\w+:|^)\/\//, '').split('/')[0]}
/>
);
};
Link.propTypes = {
entityKey: PropTypes.string.isRequired,
contentState: PropTypes.object.isRequired,
children: PropTypes.node.isRequired,
};
export default Link;

Wyświetl plik

@ -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('<h1>aaaaaaaaaa</h1>');
const contentState = ContentState.createFromBlockArray(contentBlocks);
const contentStateWithEntity = contentState.createEntity('LINK', 'MUTABLE', { url: 'http://example.com/' });
const entityKey = contentStateWithEntity.getLastCreatedEntityKey();
expect(shallow((
<Link
entityKey={entityKey}
contentState={contentStateWithEntity}
>
<span>Test children</span>
</Link>
))).toMatchSnapshot();
});
it('renders email', () => {
const contentBlocks = convertFromHTML('<h1>aaaaaaaaaa</h1>');
const contentState = ContentState.createFromBlockArray(contentBlocks);
const contentStateWithEntity = contentState.createEntity('LINK', 'MUTABLE', { url: 'mailto:test@example.com' });
const entityKey = contentStateWithEntity.getLastCreatedEntityKey();
expect(shallow((
<Link
entityKey={entityKey}
contentState={contentStateWithEntity}
>
<span>Test children</span>
</Link>
))).toMatchSnapshot();
});
});

Wyświetl plik

@ -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 (
<a role="button" onMouseUp={this.openTooltip} className="TooltipEntity">
<Icon icon={icon} className="TooltipEntity__icon" />
{children}
{showTooltipAt && (
<Portal
onClose={this.closeTooltip}
closeOnClick
closeOnType
closeOnResize
>
<Tooltip target={showTooltipAt} direction="top">
<a
href={url}
title={url}
target="_blank"
rel="noopener noreferrer"
className="Tooltip__link"
>
{label}
</a>
<button
className="Tooltip__button"
onClick={onEdit.bind(null, entityKey)}
>
Edit
</button>
<button
className="Tooltip__button"
onClick={onRemove.bind(null, entityKey)}
>
Remove
</button>
</Tooltip>
</Portal>
)}
</a>
);
}
}
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;

Wyświetl plik

@ -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;
}
}

Wyświetl plik

@ -1,18 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Document renders 1`] = `
<span
className="RichEditor-link"
data-tooltip="1"
title="Test title"
>
<Icon
className=""
name="icon-doc-full"
title={null}
/>
<span>
Test children
</span>
</span>
`;

Wyświetl plik

@ -1,33 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Link renders 1`] = `
<span
className="RichEditor-link"
data-tooltip="1"
>
<Icon
className=""
name="icon-link"
title={null}
/>
<span>
Test children
</span>
</span>
`;
exports[`Link renders email 1`] = `
<span
className="RichEditor-link"
data-tooltip="2"
>
<Icon
className=""
name="icon-mail"
title={null}
/>
<span>
Test children
</span>
</span>
`;

Wyświetl plik

@ -1,10 +0,0 @@
import Link from './Link';
import Document from './Document';
/**
* Mapping object from name to component.
*/
export default {
Link,
Document,
};

Wyświetl plik

@ -1,7 +0,0 @@
import entities from './index';
describe('entities', () => {
it('exists', () => {
expect(entities).toBeInstanceOf(Object);
});
});

Wyświetl plik

@ -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;