Refactor Draftail JS plugin API to be more similar to the rich text features registry API

pull/4261/head
Thibaud Colas 2018-02-08 00:01:32 +02:00 zatwierdzone przez Matt Westcott
rodzic f31bbba3e6
commit e59863f903
12 zmienionych plików z 73 dodań i 107 usunięć

Wyświetl plik

@ -16,9 +16,8 @@ Object {
"entityTypes": Array [ "entityTypes": Array [
Object { Object {
"block": [Function], "block": [Function],
"decorator": undefined,
"source": [Function], "source": [Function],
"type": "Image", "type": "IMAGE",
}, },
], ],
"inlineStyles": Array [], "inlineStyles": Array [],

Wyświetl plik

@ -46,13 +46,12 @@ export const initEditor = (fieldName, options) => {
const inlineStyles = options.inlineStyles || []; const inlineStyles = options.inlineStyles || [];
let entityTypes = options.entityTypes || []; let entityTypes = options.entityTypes || [];
entityTypes = entityTypes.map(wrapWagtailIcon).map(type => entityTypes = entityTypes.map(wrapWagtailIcon).map((type) => {
Object.assign(type, { const plugin = registry.getPlugin(type.type);
source: registry.getSource(type.source), // Override the properties defined in the JS plugin: Python should be the source of truth.
decorator: registry.getDecorator(type.decorator),
block: registry.getBlock(type.block), return Object.assign({}, plugin, type);
}) });
);
const enableHorizontalRule = options.enableHorizontalRule ? { const enableHorizontalRule = options.enableHorizontalRule ? {
description: STRINGS.HORIZONTAL_LINE, description: STRINGS.HORIZONTAL_LINE,

Wyświetl plik

@ -45,17 +45,15 @@ describe('Draftail', () => {
field.value = 'null'; field.value = 'null';
document.body.appendChild(field); document.body.appendChild(field);
registry.registerSources({ registry.registerPlugin({
ModalWorkflowSource: () => {}, type: 'IMAGE',
}); source: () => {},
block: () => {},
registry.registerBlocks({
ImageBlock: () => {},
}); });
initEditor('test', { initEditor('test', {
entityTypes: [ entityTypes: [
{ type: 'Image', source: 'ModalWorkflowSource', block: 'ImageBlock' }, { type: 'IMAGE' },
], ],
enableHorizontalRule: true, enableHorizontalRule: true,
}); });

Wyświetl plik

@ -1,22 +1,14 @@
const registry = { const plugins = {};
decorators: {},
blocks: {}, const registerPlugin = (plugin) => {
sources: {}, plugins[plugin.type] = plugin;
return plugins;
}; };
const registerDecorators = (decorators) => Object.assign(registry.decorators, decorators); const getPlugin = (type) => plugins[type];
const registerBlocks = (blocks) => Object.assign(registry.blocks, blocks);
const registerSources = (sources) => Object.assign(registry.sources, sources);
const getDecorator = name => registry.decorators[name];
const getBlock = name => registry.blocks[name];
const getSource = name => registry.sources[name];
export default { export default {
registerDecorators, registerPlugin,
registerBlocks, getPlugin,
registerSources,
getDecorator,
getBlock,
getSource,
}; };

Wyświetl plik

@ -1,39 +1,15 @@
import registry from './registry'; import registry from './registry';
describe('registry', () => { describe('registry', () => {
describe('sources', () => { it('works', () => {
it('works', () => { const plugin = {
expect(registry.getSource('UndefinedSource')).not.toBeDefined(); type: 'TEST',
source: null,
decorator: null,
};
registry.registerSources({ expect(registry.getPlugin('TEST')).not.toBeDefined();
TestSource: null, registry.registerPlugin(plugin);
}); expect(registry.getPlugin('TEST')).toBe(plugin);
expect(registry.getSource('TestSource')).toBe(null);
});
});
describe('decorators', () => {
it('works', () => {
expect(registry.getDecorator('UndefinedDecorator')).not.toBeDefined();
registry.registerDecorators({
TestDecorator: null,
});
expect(registry.getDecorator('TestDecorator')).toBe(null);
});
});
describe('blocks', () => {
it('works', () => {
expect(registry.getBlock('UndefinedBlock')).not.toBeDefined();
registry.registerBlocks({
TestBlock: null,
});
expect(registry.getBlock('TestBlock')).toBe(null);
});
}); });
}); });

Wyświetl plik

@ -175,8 +175,6 @@ In order to achieve this, we start with registering the rich text feature like f
'type': type_, 'type': type_,
'label': '$', 'label': '$',
'description': 'Stock', 'description': 'Stock',
'source': 'StockSource',
'decorator': 'Stock'
} }
features.register_editor_plugin( features.register_editor_plugin(
@ -282,14 +280,8 @@ We define the source component:
} }
} }
window.draftail.registerSources({
StockSource: StockSource,
});
This source component uses data and callbacks provided by `Draftail <https://github.com/springload/draftail>`_. This source component uses data and callbacks provided by `Draftail <https://github.com/springload/draftail>`_.
It also uses dependencies from global variables – see :ref:`extending_clientside_components`. It also uses dependencies from global variables – see :ref:`extending_clientside_components`.
Note how after the source declaration it is then registered for Draftail to know about it.
We then create the decorator component: We then create the decorator component:
@ -307,12 +299,17 @@ We then create the decorator component:
}, props.children); }, props.children);
}; };
window.draftail.registerDecorators({ This is a straightforward React component. It does not use JSX since we do not want to have to use a build step for our JavaScript. It uses ES6 syntax – this would not work in IE11 unless it was converted back to ES5 with a build step.
Stock: Stock,
});
This is a straightforward React component. It does not use JSX since we do not want to have to use a build step for our JavaScript. It uses ES6 syntax – this would not work in IE11 unless it was converted back to ES5. Finally, we register the JS components of our plugin:
We also have to register this with Draftail.
.. code-block:: javascript
window.draftail.registerPlugin({
type: 'STOCK',
source: StockSource,
decorator: Stock,
});
And thats it! All of this setup will finally produce the following HTML on the sites front-end: And thats it! All of this setup will finally produce the following HTML on the sites front-end:

Wyświetl plik

@ -9,22 +9,36 @@ import {
} from '../../../../../client/src/components/Draftail/index'; } from '../../../../../client/src/components/Draftail/index';
/** /**
* Expose as a global, and register the built-in entities. * Entry point loaded when the Draftail editor is in use.
*/ */
const draftail = registry;
window.draftail = registry; // Expose Draftail as a global, with the initEditor function.
window.draftail.initEditor = initEditor; draftail.initEditor = initEditor;
window.draftail = draftail;
window.draftail.registerSources({ // Plugins for the built-in entities.
ModalWorkflowSource, const plugins = [
}); {
type: 'DOCUMENT',
source: ModalWorkflowSource,
decorator: Document,
},
{
type: 'LINK',
source: ModalWorkflowSource,
decorator: Link,
},
{
type: 'IMAGE',
source: ModalWorkflowSource,
block: ImageBlock,
},
{
type: 'EMBED',
source: ModalWorkflowSource,
block: EmbedBlock,
},
];
window.draftail.registerDecorators({ plugins.forEach(draftail.registerPlugin);
Link,
Document,
});
window.draftail.registerBlocks({
ImageBlock,
EmbedBlock,
});

Wyświetl plik

@ -6,10 +6,9 @@ describe('draftail.entry', () => {
}); });
it('has defaults registered', () => { it('has defaults registered', () => {
expect(window.draftail.getSource('ModalWorkflowSource')).toBeDefined(); expect(window.draftail.getPlugin('LINK')).toBeDefined();
expect(window.draftail.getDecorator('Link')).toBeDefined(); expect(window.draftail.getPlugin('DOCUMENT')).toBeDefined();
expect(window.draftail.getDecorator('Document')).toBeDefined(); expect(window.draftail.getPlugin('IMAGE')).toBeDefined();
expect(window.draftail.getBlock('ImageBlock')).toBeDefined(); expect(window.draftail.getPlugin('EMBED')).toBeDefined();
expect(window.draftail.getBlock('EmbedBlock')).toBeDefined();
}); });
}); });

Wyświetl plik

@ -430,8 +430,6 @@ def register_core_features(features):
'type': 'LINK', 'type': 'LINK',
'icon': 'link', 'icon': 'link',
'description': ugettext('Link'), 'description': ugettext('Link'),
'source': 'ModalWorkflowSource',
'decorator': 'Link',
# We want to enforce constraints on which links can be pasted into rich text. # We want to enforce constraints on which links can be pasted into rich text.
# Keep only the attributes Wagtail needs. # Keep only the attributes Wagtail needs.
'attributes': ['url', 'id', 'parentId'], 'attributes': ['url', 'id', 'parentId'],

Wyświetl plik

@ -90,8 +90,6 @@ def register_document_feature(features):
'type': 'DOCUMENT', 'type': 'DOCUMENT',
'icon': 'doc-full', 'icon': 'doc-full',
'description': ugettext('Document'), 'description': ugettext('Document'),
'source': 'ModalWorkflowSource',
'decorator': 'Document',
}) })
) )

Wyświetl plik

@ -54,8 +54,6 @@ def register_embed_feature(features):
'type': 'EMBED', 'type': 'EMBED',
'icon': 'media', 'icon': 'media',
'description': _('Embed'), 'description': _('Embed'),
'source': 'ModalWorkflowSource',
'block': 'EmbedBlock',
}) })
) )

Wyświetl plik

@ -89,8 +89,6 @@ def register_image_feature(features):
'type': 'IMAGE', 'type': 'IMAGE',
'icon': 'image', 'icon': 'image',
'description': ugettext('Image'), 'description': ugettext('Image'),
'source': 'ModalWorkflowSource',
'block': 'ImageBlock',
# We do not want users to be able to copy-paste hotlinked images into rich text. # We do not want users to be able to copy-paste hotlinked images into rich text.
# Keep only the attributes Wagtail needs. # Keep only the attributes Wagtail needs.
'attributes': ['id', 'src', 'alt', 'format'], 'attributes': ['id', 'src', 'alt', 'format'],