kopia lustrzana https://github.com/wagtail/wagtail
Update Link and Document decorators to Draftail 0.10 API
rodzic
306bd8dd03
commit
73ed313a9f
|
@ -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';
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
`;
|
|
@ -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>
|
||||
`;
|
|
@ -1,10 +0,0 @@
|
|||
import Link from './Link';
|
||||
import Document from './Document';
|
||||
|
||||
/**
|
||||
* Mapping object from name to component.
|
||||
*/
|
||||
export default {
|
||||
Link,
|
||||
Document,
|
||||
};
|
|
@ -1,7 +0,0 @@
|
|||
import entities from './index';
|
||||
|
||||
describe('entities', () => {
|
||||
it('exists', () => {
|
||||
expect(entities).toBeInstanceOf(Object);
|
||||
});
|
||||
});
|
|
@ -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;
|
||||
|
|
Ładowanie…
Reference in New Issue