浏览代码

Expose reusable client-side code to build Draftail extensions (#4467)

* Expose Draftail package as global variable for reuse
* Expose Wagtail React components for reuse
* Expose Draftail-related React components for reuse
Thibaud Colas 7 年之前
父节点
当前提交
c309753378

+ 1 - 0
CHANGELOG.txt

@@ -23,6 +23,7 @@ Changelog
  * Added error handling to the Draftail editor (Thibaud Colas)
  * Add new `wagtail_icon` template tag to facilitate making admin icons accessible (Sander Tuit)
  * Set `ALLOWED_HOSTS` in the project template to allow any host in development (Tom Dyson)
+ * Expose reusable client-side code to build Draftail extensions (Thibaud Colas)
  * Fix: Status button on 'edit page' now links to the correct URL when live and draft slug differ (LB (Ben Johnston))
  * Fix: Image title text in the gallery and in the chooser now wraps for long filenames (LB (Ben Johnston), Luiz Boaretto)
  * Fix: Move image editor action buttons to the bottom of the form on mobile (Julian Gallo)

+ 7 - 2
client/src/components/Draftail/index.js

@@ -11,8 +11,9 @@ export { default as Document } from './decorators/Document';
 export { default as ImageBlock } from './blocks/ImageBlock';
 export { default as EmbedBlock } from './blocks/EmbedBlock';
 
-export { default as ModalWorkflowSource } from './sources/ModalWorkflowSource';
-
+import ModalWorkflowSource from './sources/ModalWorkflowSource';
+import Tooltip from './Tooltip/Tooltip';
+import TooltipEntity from './decorators/TooltipEntity';
 import EditorFallback from './EditorFallback/EditorFallback';
 
 // 1024x1024 SVG path rendering of the "↵" character, that renders badly in MS Edge.
@@ -122,4 +123,8 @@ const initEditor = (selector, options, currentScript) => {
 export default {
   initEditor,
   registerPlugin,
+  // Components exposed for third-party reuse.
+  ModalWorkflowSource,
+  Tooltip,
+  TooltipEntity,
 };

+ 4 - 2
client/src/components/Draftail/index.test.js

@@ -1,6 +1,5 @@
 import draftail, {
   wrapWagtailIcon,
-  ModalWorkflowSource,
   Link,
   Document,
   ImageBlock,
@@ -133,9 +132,12 @@ describe('Draftail', () => {
     });
   });
 
-  it('#ModalWorkflowSource', () => expect(ModalWorkflowSource).toBeDefined());
   it('#Link', () => expect(Link).toBeDefined());
   it('#Document', () => expect(Document).toBeDefined());
   it('#ImageBlock', () => expect(ImageBlock).toBeDefined());
   it('#EmbedBlock', () => expect(EmbedBlock).toBeDefined());
+
+  it('#ModalWorkflowSource', () => expect(draftail.ModalWorkflowSource).toBeDefined());
+  it('#Tooltip', () => expect(draftail.Tooltip).toBeDefined());
+  it('#TooltipEntity', () => expect(draftail.TooltipEntity).toBeDefined());
 });

+ 2 - 0
client/src/index.js

@@ -7,6 +7,7 @@ import Button from './components/Button/Button';
 import Icon from './components/Icon/Icon';
 import PublicationStatus from './components/PublicationStatus/PublicationStatus';
 import LoadingSpinner from './components/LoadingSpinner/LoadingSpinner';
+import Portal from './components/Portal/Portal';
 import Transition from './components/Transition/Transition';
 import Explorer, {
   ExplorerToggle,
@@ -18,6 +19,7 @@ export {
   Icon,
   PublicationStatus,
   LoadingSpinner,
+  Portal,
   Transition,
   Explorer,
   ExplorerToggle,

+ 5 - 0
client/src/index.test.js

@@ -3,6 +3,7 @@ import {
   Icon,
   PublicationStatus,
   LoadingSpinner,
+  Portal,
   Transition,
   Explorer,
   ExplorerToggle,
@@ -26,6 +27,10 @@ describe('wagtail package API', () => {
     expect(LoadingSpinner).toBeDefined();
   });
 
+  it('has Portal', () => {
+    expect(Portal).toBeDefined();
+  });
+
   it('has Transition', () => {
     expect(Transition).toBeDefined();
   });

+ 2 - 0
client/tests/stubs.js

@@ -46,6 +46,8 @@ global.wagtailConfig = {
 
 global.wagtailVersion = '1.6a1';
 
+global.wagtail = {};
+
 global.chooserUrls = {
   documentChooser: '/admin/documents/chooser/',
   emailLinkChooser: '/admin/choose-email-link/',

+ 20 - 0
docs/advanced_topics/customisation/admin_templates.rst

@@ -188,5 +188,25 @@ To make this easier, Wagtail exposes its React-related dependencies as global va
     window.ReactDOM;
     // 'react-transition-group/CSSTransitionGroup'
     window.CSSTransitionGroup;
+
+Wagtail also exposes some of its own React components. You can reuse:
+
+.. code-block:: javascript
+
+    window.wagtail.components.Icon;
+    window.wagtail.components.Portal;
+
+Pages containing rich text editors also have access to:
+
+.. code-block:: javascript
+
     // 'draft-js'
     window.DraftJS;
+    // 'draftail'
+    window.Draftail;
+
+    // Wagtail’s Draftail-related APIs and components.
+    window.draftail;
+    window.draftail.ModalWorkflowSource;
+    window.draftail.Tooltip;
+    window.draftail.TooltipEntity;

+ 1 - 1
docs/advanced_topics/customisation/extending_draftail.rst

@@ -141,7 +141,7 @@ Here are the main requirements to create a new entity feature:
 * Like for inline styles and blocks, set up the to/from DB conversion.
 * The conversion usually is more involved, since entities contain data that needs to be serialised to HTML.
 
-To write the React components, Wagtail exposes its own React and Draft.js dependencies as global variables. Read more about this in :ref:`extending_clientside_components`.
+To write the React components, Wagtail exposes its own React, Draft.js and Draftail dependencies as global variables. Read more about this in :ref:`extending_clientside_components`.
 To go further, please look at the `Draftail documentation <https://github.com/springload/draftail#formatting-options>`_ as well as the `Draft.js exporter documentation <https://github.com/springload/draftjs_exporter>`_.
 
 Here is a detailed example to showcase how those tools are used in the context of Wagtail.

+ 1 - 0
docs/releases/2.1.rst

@@ -42,6 +42,7 @@ Other features
  * Added error handling to the Draftail editor (Thibaud Colas)
  * Add new `wagtail_icon` template tag to facilitate making admin icons accessible (Sander Tuit)
  * Set `ALLOWED_HOSTS` in the project template to allow any host in development (Tom Dyson)
+ * Expose reusable client-side code to build Draftail extensions (Thibaud Colas)
 
 Bug fixes
 ~~~~~~~~~

+ 7 - 5
wagtail/admin/static_src/wagtailadmin/app/draftail.entry.js

@@ -1,5 +1,5 @@
+import * as Draftail from 'draftail';
 import draftail, {
-  ModalWorkflowSource,
   Link,
   Document,
   ImageBlock,
@@ -10,6 +10,8 @@ import draftail, {
  * Entry point loaded when the Draftail editor is in use.
  */
 
+ // Expose Draftail package as a global.
+window.Draftail = Draftail;
 // Expose module as a global.
 window.draftail = draftail;
 
@@ -17,22 +19,22 @@ window.draftail = draftail;
 const plugins = [
   {
     type: 'DOCUMENT',
-    source: ModalWorkflowSource,
+    source: draftail.ModalWorkflowSource,
     decorator: Document,
   },
   {
     type: 'LINK',
-    source: ModalWorkflowSource,
+    source: draftail.ModalWorkflowSource,
     decorator: Link,
   },
   {
     type: 'IMAGE',
-    source: ModalWorkflowSource,
+    source: draftail.ModalWorkflowSource,
     block: ImageBlock,
   },
   {
     type: 'EMBED',
-    source: ModalWorkflowSource,
+    source: draftail.ModalWorkflowSource,
     block: EmbedBlock,
   },
 ];

+ 5 - 1
wagtail/admin/static_src/wagtailadmin/app/draftail.entry.test.js

@@ -1,10 +1,14 @@
 require('./draftail.entry');
 
 describe('draftail.entry', () => {
-  it('exposes global', () => {
+  it('exposes module as global', () => {
     expect(window.draftail).toBeDefined();
   });
 
+  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"]);
   });

+ 8 - 2
wagtail/admin/static_src/wagtailadmin/app/wagtailadmin.entry.js

@@ -1,7 +1,13 @@
-import { initExplorer } from 'wagtail-client';
+import { initExplorer, Icon, Portal } from 'wagtail-client';
+
+// Expose components as globals for third-party reuse.
+window.wagtail.components = {
+  Icon,
+  Portal,
+};
 
 /**
- * Admin JS entry point. Add in here code to run once the page is loaded.
+ * Add in here code to run once the page is loaded.
  */
 document.addEventListener('DOMContentLoaded', () => {
   const explorerNode = document.querySelector('[data-explorer-menu]');

+ 4 - 0
wagtail/admin/static_src/wagtailadmin/app/wagtailadmin.entry.test.js

@@ -8,6 +8,10 @@ require('./wagtailadmin.entry');
 describe('wagtailadmin.entry', () => {
   const [event, listener] = document.addEventListener.mock.calls[0];
 
+  it('exposes components for reuse', () => {
+    expect(Object.keys(window.wagtail.components)).toEqual(['Icon', 'Portal']);
+  });
+
   it('DOMContentLoaded', () => {
     expect(event).toBe('DOMContentLoaded');
   });