From 429ee3f5eaa7c43d8ed944045701f579fb907a60 Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Thu, 2 Nov 2017 16:21:55 -0400 Subject: [PATCH 1/3] Extensibility: Enable hook-based Slot/Fill registration --- components/higher-order/with-hooks/index.js | 24 ++++++++ .../higher-order/with-hooks/provider.js | 31 ++++++++++ components/index.js | 2 + editor/components/index.js | 1 + editor/components/plugin-fills/index.js | 58 +++++++++++++++++++ editor/components/provider/index.js | 12 ++++ editor/index.js | 1 + editor/layout/index.js | 8 ++- 8 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 components/higher-order/with-hooks/index.js create mode 100644 components/higher-order/with-hooks/provider.js create mode 100644 editor/components/plugin-fills/index.js diff --git a/components/higher-order/with-hooks/index.js b/components/higher-order/with-hooks/index.js new file mode 100644 index 0000000000000..7aae2646a92d8 --- /dev/null +++ b/components/higher-order/with-hooks/index.js @@ -0,0 +1,24 @@ +/** + * External dependencies + */ +import { noop } from 'lodash'; + +export default ( WrappedComponent ) => { + function HooksComponent( props, context ) { + return ( + + ); + } + + // Derive display name from original component + const { displayName = WrappedComponent.name || 'Component' } = WrappedComponent; + HooksComponent.displayName = `hooks(${ displayName })`; + + HooksComponent.contextTypes = { + hooks: noop, + }; + + return HooksComponent; +}; diff --git a/components/higher-order/with-hooks/provider.js b/components/higher-order/with-hooks/provider.js new file mode 100644 index 0000000000000..1355cc1b0d73e --- /dev/null +++ b/components/higher-order/with-hooks/provider.js @@ -0,0 +1,31 @@ +/** + * External dependencies + */ +import { noop } from 'lodash'; + +/** + * WordPress dependencies + */ +import createHooks from '@wordpress/hooks'; +import { Component } from '@wordpress/element'; + +export default class HooksProvider extends Component { + constructor() { + super( ...arguments ); + + this.hooks = createHooks(); + } + + getChildContext() { + const { hooks } = this; + return { hooks }; + } + + render() { + return this.props.children; + } +} + +HooksProvider.childContextTypes = { + hooks: noop, +}; diff --git a/components/index.js b/components/index.js index dabaab6b78688..2aa7fb3eb99ef 100644 --- a/components/index.js +++ b/components/index.js @@ -12,6 +12,7 @@ export { default as ExternalLink } from './external-link'; export { default as FormFileUpload } from './form-file-upload'; export { default as FormToggle } from './form-toggle'; export { default as FormTokenField } from './form-token-field'; +export { default as HooksProvider } from './higher-order/with-hooks/provider'; export { default as IconButton } from './icon-button'; export { default as KeyboardShortcuts } from './keyboard-shortcuts'; export { NavigableMenu, TabbableContainer } from './navigable-container'; @@ -35,5 +36,6 @@ export { default as navigateRegions } from './higher-order/navigate-regions'; export { default as withAPIData } from './higher-order/with-api-data'; export { default as withFocusOutside } from './higher-order/with-focus-outside'; export { default as withFocusReturn } from './higher-order/with-focus-return'; +export { default as withHooks } from './higher-order/with-hooks'; export { default as withInstanceId } from './higher-order/with-instance-id'; export { default as withSpokenMessages } from './higher-order/with-spoken-messages'; diff --git a/editor/components/index.js b/editor/components/index.js index adbb8903c8966..3ac1fbcee8e91 100644 --- a/editor/components/index.js +++ b/editor/components/index.js @@ -41,3 +41,4 @@ export { default as Warning } from './warning'; // State Related Components export { default as EditorProvider } from './provider'; +export { default as PluginFills } from './plugin-fills'; diff --git a/editor/components/plugin-fills/index.js b/editor/components/plugin-fills/index.js new file mode 100644 index 0000000000000..aca46ea30c457 --- /dev/null +++ b/editor/components/plugin-fills/index.js @@ -0,0 +1,58 @@ +/** + * External dependencies + */ +import { map } from 'lodash'; + +/** + * WordPress dependencies + */ +import { Fill, withHooks } from '@wordpress/components'; +import { Component } from '@wordpress/element'; + +class PluginFills extends Component { + constructor() { + super( ...arguments ); + + this.registerFill = this.registerFill.bind( this ); + + this.state = { + fills: {}, + }; + } + + componentDidMount() { + this.props.hooks.addAction( 'registerFill', 'core\plugin-fills-register', this.registerFill ); + } + + componentWillUnmount() { + this.props.hooks.removeAction( 'registerFill', 'core\plugin-fills-register' ); + } + + registerFill( name, FillComponent ) { + this.setState( ( prevState ) => { + const { fills } = prevState; + + return { + fills: { + ...fills, + [ name ]: [ + ...( fills[ name ] || [] ), + FillComponent, + ], + }, + }; + } ); + } + + render() { + return map( this.state.fills, ( components, name ) => ( + + { map( components, ( FillComponent, index ) => ( + + ) ) } + + ) ); + } +} + +export default withHooks( PluginFills ); diff --git a/editor/components/provider/index.js b/editor/components/provider/index.js index b9b0b3e99816c..68375d1f2803e 100644 --- a/editor/components/provider/index.js +++ b/editor/components/provider/index.js @@ -14,6 +14,7 @@ import { APIProvider, DropZoneProvider, SlotFillProvider, + HooksProvider, } from '@wordpress/components'; /** @@ -42,6 +43,8 @@ class EditorProvider extends Component { constructor( props ) { super( ...arguments ); + this.setHooks = this.setHooks.bind( this ); + const store = createReduxStore( props.initialState ); // If initial state is passed, assume that we don't need to initialize, @@ -73,6 +76,10 @@ class EditorProvider extends Component { } } + setHooks( node ) { + this.hooks = node.hooks; + } + render() { const { children } = this.props; const providers = [ @@ -103,6 +110,11 @@ class EditorProvider extends Component { SlotFillProvider, ], + [ + HooksProvider, + { ref: this.setHooks }, + ], + // APIProvider // // - context.getAPISchema diff --git a/editor/index.js b/editor/index.js index 4091f164981cf..b2822e9b151a4 100644 --- a/editor/index.js +++ b/editor/index.js @@ -96,5 +96,6 @@ export function createEditorInstance( id, post, settings ) { initializeMetaBoxes( metaBoxes ) { provider.store.dispatch( initializeMetaBoxState( metaBoxes ) ); }, + ...provider.hooks, }; } diff --git a/editor/layout/index.js b/editor/layout/index.js index ed030d25f2162..890fdf1bf8156 100644 --- a/editor/layout/index.js +++ b/editor/layout/index.js @@ -20,7 +20,12 @@ import TextEditor from '../modes/text-editor'; import VisualEditor from '../modes/visual-editor'; import DocumentTitle from '../document-title'; import { removeNotice } from '../actions'; -import { MetaBoxes, AutosaveMonitor, UnsavedChangesWarning } from '../components'; +import { + MetaBoxes, + AutosaveMonitor, + UnsavedChangesWarning, + PluginFills, +} from '../components'; import { getEditorMode, isEditorSidebarOpened, @@ -50,6 +55,7 @@ function Layout( { mode, isSidebarOpened, notices, ...props } ) { { isSidebarOpened && } + ); } From 7fddb4fa5a2faa4f1f586c275011fa841c1b05df Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Tue, 14 Nov 2017 10:34:51 -0500 Subject: [PATCH 2/3] Publish: Add slot for publish dropdown Extensible via slot hooks --- editor/header/publish-dropdown/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/editor/header/publish-dropdown/index.js b/editor/header/publish-dropdown/index.js index 38b9cd0a056d9..0750586c8aeb3 100644 --- a/editor/header/publish-dropdown/index.js +++ b/editor/header/publish-dropdown/index.js @@ -2,7 +2,7 @@ * WordPress Dependencies */ import { __ } from '@wordpress/i18n'; -import { withAPIData, PanelBody } from '@wordpress/components'; +import { withAPIData, PanelBody, Slot } from '@wordpress/components'; /** * Internal Dependencies @@ -44,6 +44,7 @@ function PublishDropdown( { user, onSubmit } ) { } +
From 2e89a58e660285453ae11df0c5ada8252746224b Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Tue, 14 Nov 2017 10:37:56 -0500 Subject: [PATCH 3/3] Framework: Expose abstracted fill registration API Not yet considering to expose internal hooks registry yet, to see whether fill registration is sufficient --- editor/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/editor/index.js b/editor/index.js index b2822e9b151a4..f06daef75d0fe 100644 --- a/editor/index.js +++ b/editor/index.js @@ -96,6 +96,8 @@ export function createEditorInstance( id, post, settings ) { initializeMetaBoxes( metaBoxes ) { provider.store.dispatch( initializeMetaBoxState( metaBoxes ) ); }, - ...provider.hooks, + registerFill( fill, component ) { + provider.hooks.doAction( 'registerFill', fill, component ); + }, }; }