Skip to content

Commit

Permalink
Implement new Draftail customisation APIs
Browse files Browse the repository at this point in the history
- Fixes wagtail#5580
- Remove TSLA/TWTR/BTC references and replace with clean energy FSLR / NEE stocks
  • Loading branch information
thibaudcolas authored and lb- committed Jul 6, 2023
1 parent 4f012d7 commit f4ea015
Show file tree
Hide file tree
Showing 9 changed files with 426 additions and 108 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Changelog
* Optimise queries in collection permission policies using cache on the user object (Sage Abdullah)
* Phone numbers entered via a link chooser will now have any spaces stripped out, ensuring a valid href="tel:..." attribute (Sahil Jangra)
* Auto-select the `StreamField` block when only one block type is declared (Sébastien Corbin)
* Add support for more advanced Draftail customisation APIs (Thibaud Colas)
* Fix: Prevent choosers from failing when initial value is an unrecognised ID, e.g. when moving a page from a location where `parent_page_types` would disallow it (Dan Braghis)
* Fix: Move comment notifications toggle to the comments side panel (Sage Abdullah)
* Fix: Remove comment button on InlinePanel fields (Sage Abdullah)
Expand Down
22 changes: 19 additions & 3 deletions client/src/components/Draftail/__snapshots__/index.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,19 @@ Object {
"bottomToolbar": [Function],
"commandToolbar": [Function],
"commands": true,
"controls": Array [],
"decorators": Array [],
"controls": Array [
Object {
"meta": [Function],
"type": "sentences",
},
],
"decorators": Array [
Object {
"component": [Function],
"strategy": [Function],
"type": "punctuation",
},
],
"editorState": null,
"enableHorizontalRule": Object {
"description": "Horizontal line",
Expand All @@ -43,7 +54,12 @@ Object {
"onFocus": null,
"onSave": [Function],
"placeholder": "Write something or type ‘/’ to insert a block",
"plugins": Array [],
"plugins": Array [
Object {
"handlePastedText": [Function],
"type": "anchorify",
},
],
"rawContentState": null,
"readOnly": false,
"showRedoControl": false,
Expand Down
43 changes: 34 additions & 9 deletions client/src/components/Draftail/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,21 @@ const onSetToolbar = (choice, callback) => {
/**
* Registry for client-side code of Draftail plugins.
*/
const PLUGINS = {};
const PLUGINS = {
entityTypes: {},
plugins: {},
controls: {},
decorators: {},
};

const registerPlugin = (plugin) => {
PLUGINS[plugin.type] = plugin;
return PLUGINS;
/**
* Client-side editor-specific equivalent to register_editor_plugin.
* `optionName` defaults to entityTypes for backwards-compatibility with
* previous function signature only allowing registering entities.
*/
const registerPlugin = (type, optionName = 'entityTypes') => {
PLUGINS[optionName][type.type] = type;
return PLUGINS[optionName];
};

/**
Expand Down Expand Up @@ -157,15 +167,28 @@ const initEditor = (selector, originalOptions, currentScript) => {
const blockTypes = newOptions.blockTypes || [];
const inlineStyles = newOptions.inlineStyles || [];
let controls = newOptions.controls || [];
let decorators = newOptions.decorators || [];
let plugins = newOptions.plugins || [];
const commands = newOptions.commands || true;
let entityTypes = newOptions.entityTypes || [];

entityTypes = entityTypes.map(wrapWagtailIcon).map((type) => {
const plugin = PLUGINS[type.type];

entityTypes = entityTypes
.map(wrapWagtailIcon)
// Override the properties defined in the JS plugin: Python should be the source of truth.
return { ...plugin, ...type };
});
.map((type) => ({ ...PLUGINS.entityTypes[type.type], ...type }));

controls = controls.map((type) => ({
...PLUGINS.controls[type.type],
...type,
}));
decorators = decorators.map((type) => ({
...PLUGINS.decorators[type.type],
...type,
}));
plugins = plugins.map((type) => ({
...PLUGINS.plugins[type.type],
...type,
}));

// Only initialise the character count / max length on fields explicitly requiring it.
if (field.hasAttribute('maxlength')) {
Expand Down Expand Up @@ -228,6 +251,8 @@ const initEditor = (selector, originalOptions, currentScript) => {
inlineStyles: inlineStyles.map(wrapWagtailIcon),
entityTypes,
controls,
decorators,
plugins,
commands,
enableHorizontalRule,
};
Expand Down
61 changes: 54 additions & 7 deletions client/src/components/Draftail/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,45 @@ describe('Draftail', () => {
document.body.innerHTML = '<input id="test" value="null" />';
const field = document.querySelector('#test');

draftail.registerPlugin({
type: 'IMAGE',
source: () => {},
block: () => {},
});
draftail.registerPlugin(
{
type: 'IMAGE',
source: () => {},
block: () => null,
},
'entityTypes',
);

draftail.registerPlugin(
{
type: 'sentences',
meta: () => null,
},
'controls',
);

draftail.registerPlugin(
{
type: 'punctuation',
strategy: () => {},
component: () => null,
},
'decorators',
);

draftail.registerPlugin(
{
type: 'anchorify',
handlePastedText: () => 'not-handled',
},
'plugins',
);

draftail.initEditor('#test', {
entityTypes: [{ type: 'IMAGE' }],
controls: [{ type: 'sentences' }],
decorators: [{ type: 'punctuation' }],
plugins: [{ type: 'anchorify' }],
enableHorizontalRule: true,
});

Expand Down Expand Up @@ -153,14 +184,30 @@ describe('Draftail', () => {

describe('#registerPlugin', () => {
it('works', () => {
const plugin = { type: 'TEST' };
expect(draftail.registerPlugin(plugin, 'entityTypes')).toMatchObject({
TEST: plugin,
});
expect(draftail.registerPlugin(plugin, 'controls')).toMatchObject({
TEST: plugin,
});
expect(draftail.registerPlugin(plugin, 'decorators')).toMatchObject({
TEST: plugin,
});
expect(draftail.registerPlugin(plugin, 'plugins')).toMatchObject({
TEST: plugin,
});
});

it('supports legacy entityTypes registration', () => {
const plugin = {
type: 'TEST',
type: 'TEST_ENTITY',
source: null,
decorator: null,
};

expect(draftail.registerPlugin(plugin)).toMatchObject({
TEST: plugin,
TEST_ENTITY: plugin,
});
});
});
Expand Down
4 changes: 2 additions & 2 deletions client/src/entrypoints/admin/draftail.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ window.Draftail = Draftail;
window.draftail = draftail;

// Plugins for the built-in entities.
const plugins = [
const entityTypes = [
{
type: 'DOCUMENT',
source: draftail.DocumentModalWorkflowSource,
Expand All @@ -41,4 +41,4 @@ const plugins = [
},
];

plugins.forEach(draftail.registerPlugin);
entityTypes.forEach((type) => draftail.registerPlugin(type, 'entityTypes'));
48 changes: 38 additions & 10 deletions client/src/entrypoints/admin/draftail.test.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,49 @@
require('./draftail');

describe('draftail', () => {
it('exposes module as global', () => {
expect(window.draftail).toBeDefined();
it('exposes a stable API', () => {
expect(window.draftail).toMatchInlineSnapshot(`
Object {
"DocumentModalWorkflowSource": [Function],
"DraftUtils": Object {
"addHorizontalRuleRemovingSelection": [Function],
"addLineBreak": [Function],
"applyMarkdownStyle": [Function],
"getCommandPalettePrompt": [Function],
"getEntitySelection": [Function],
"getEntityTypeStrategy": [Function],
"getSelectedBlock": [Function],
"getSelectionEntity": [Function],
"handleDeleteAtomic": [Function],
"handleHardNewline": [Function],
"handleNewLine": [Function],
"insertNewUnstyledBlock": [Function],
"removeBlock": [Function],
"removeBlockEntity": [Function],
"removeCommandPalettePrompt": [Function],
"resetBlockWithType": [Function],
"updateBlockEntity": [Function],
},
"EmbedModalWorkflowSource": [Function],
"ImageModalWorkflowSource": [Function],
"LinkModalWorkflowSource": [Function],
"ModalWorkflowSource": [Function],
"Tooltip": [Function],
"TooltipEntity": [Function],
"initEditor": [Function],
"registerPlugin": [Function],
"splitState": [Function],
}
`);
});

it('exposes package as global', () => {
expect(window.Draftail).toBeDefined();
});

it('has defaults registered', () => {
expect(Object.keys(window.draftail.registerPlugin({}))).toEqual([
'DOCUMENT',
'LINK',
'IMAGE',
'EMBED',
'undefined',
]);
it('has default entities registered', () => {
expect(
Object.keys(window.draftail.registerPlugin({}, 'entityTypes')),
).toEqual(['DOCUMENT', 'LINK', 'IMAGE', 'EMBED', 'undefined']);
});
});
Loading

0 comments on commit f4ea015

Please sign in to comment.