Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extensibility: Plugin sidebar api #5430

Merged
merged 88 commits into from
Mar 19, 2018
Merged
Show file tree
Hide file tree
Changes from 86 commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
142f7b1
Initial implementation of slot-fill sidebar API
Mar 2, 2018
65af7e6
Attempt to implement createContext api for plugin slot fill API
Mar 6, 2018
0e921fe
Fixed error on start up
Mar 6, 2018
2b022f7
Implemented way to pass props from plugin API via context to PluginSi…
Mar 6, 2018
78623ad
Finalize first pass of registerPlugin API
Mar 6, 2018
0beaa4b
Remove create-react-context
Mar 6, 2018
cbfe6dc
Merge branch 'master' into add/slot-fill-sidebar-api
Mar 7, 2018
75e35ba
Removed old sidebar and refactored the way plugin context is handled
Mar 7, 2018
1ae8632
Improved withPluginContext HOC to prevent usage of cloneElement
Mar 7, 2018
0d3d03d
Implemented tests for registerPlugin
Mar 7, 2018
789e675
Added test for plugin/context components, revamped naming of passed c…
Mar 7, 2018
08f6e7b
Fixed eslint issues
Mar 7, 2018
ffe0870
Refactored plugin-sidebar to use withSelect/withDispatch and created …
Mar 7, 2018
c55eaaf
Removed PropTypes
Mar 7, 2018
a3a08de
Used enzyme instead of react-test-renderer in plugin context tests
Mar 7, 2018
37a41d8
Hidden internal Slot and Fill components in layout
Mar 7, 2018
ab5df97
Moved plugin-sidebar error-boundary to separate file
Mar 7, 2018
8fe79d0
Moved sidebar error boundary around the component only so the error c…
Mar 7, 2018
8c2930c
Updated documentation
Mar 7, 2018
a3fada7
Updated documentation
Mar 7, 2018
83a4dd3
Updated documentation
Mar 8, 2018
908112d
Refactored editpost/api/context component to reparate folder
Mar 8, 2018
913415e
Moved plugin components to api folder and made API experimental
Mar 8, 2018
2dc4a78
Updated docs to reflect experimental status
Mar 8, 2018
a4ce7d2
Updated documentation
Mar 8, 2018
1432bc4
Fixed failing test
Mar 8, 2018
ca63d62
Docs: Add a few tweaks to the Extensibility doc
gziolo Mar 9, 2018
4ac2152
Initial implementation of slot-fill sidebar API
Mar 2, 2018
fe93e2d
Attempt to implement createContext api for plugin slot fill API
Mar 6, 2018
0a6ee55
Fixed error on start up
Mar 6, 2018
499cb71
Implemented way to pass props from plugin API via context to PluginSi…
Mar 6, 2018
fde16b5
Finalize first pass of registerPlugin API
Mar 6, 2018
a268de4
Remove create-react-context
Mar 6, 2018
8bfcbee
Removed old sidebar and refactored the way plugin context is handled
Mar 7, 2018
ef02a0c
Improved withPluginContext HOC to prevent usage of cloneElement
Mar 7, 2018
c2e0f20
Implemented tests for registerPlugin
Mar 7, 2018
528bcdc
Added test for plugin/context components, revamped naming of passed c…
Mar 7, 2018
893dff8
Fixed eslint issues
Mar 7, 2018
86049a9
Refactored plugin-sidebar to use withSelect/withDispatch and created …
Mar 7, 2018
8d00bf4
Removed PropTypes
Mar 7, 2018
5b22af7
Used enzyme instead of react-test-renderer in plugin context tests
Mar 7, 2018
bb552ef
Hidden internal Slot and Fill components in layout
Mar 7, 2018
d4da2c5
Moved plugin-sidebar error-boundary to separate file
Mar 7, 2018
b110d70
Moved sidebar error boundary around the component only so the error c…
Mar 7, 2018
b4dfc8e
Updated documentation
Mar 7, 2018
c173637
Updated documentation
Mar 7, 2018
47ee199
Updated documentation
Mar 8, 2018
d56a58d
Refactored editpost/api/context component to reparate folder
Mar 8, 2018
646f72c
Moved plugin components to api folder and made API experimental
Mar 8, 2018
60d174c
Updated docs to reflect experimental status
Mar 8, 2018
42652a5
Updated documentation
Mar 8, 2018
210d0ab
Fixed failing test
Mar 8, 2018
f082bf0
Docs: Add a few tweaks to the Extensibility doc
gziolo Mar 9, 2018
b629a85
Rebase master
Mar 12, 2018
c871e8b
Merged remote changes
Mar 12, 2018
a960db8
Merge branch 'master' of https://github.com/WordPress/gutenberg into …
Mar 12, 2018
1f6b5f5
Register UI components in plugin.js
Mar 13, 2018
295d410
Updated tests
Mar 13, 2018
474ff55
created PluginRegistry singleton
Mar 13, 2018
b809684
Updated JSDoc
Mar 13, 2018
20eb71a
Made name first parameter of registerPlugin function instead of being…
Mar 13, 2018
688280c
Small documentation tweaks
Mar 13, 2018
d25b07d
Moved and documented plugin context provider and higher order component
Mar 14, 2018
4d5209a
Updated test
Mar 14, 2018
db3c98c
Fixed sass class names
Mar 14, 2018
b2526da
Made scss specific for plugin0-sidebar which nog longer depends on ed…
Mar 14, 2018
d3e038f
Moved PLuginsArea to separate file
Mar 14, 2018
eed00e2
Updated test
Mar 14, 2018
995d7de
Moved plugin api to plugins module
Mar 14, 2018
e706a7a
Updated documentation
Mar 14, 2018
dc717f8
Converted spaces to tabs in style.scss file
Mar 14, 2018
644b7e8
Comply to coding style conventions regarding css in plugin-sidebar
Mar 14, 2018
02a5924
Added slot to pluginsidebar fill for consistency
Mar 14, 2018
687e25c
Moved styles.scss under Internal dependencies
Mar 14, 2018
5f8beaa
removed unnecessary id from hidden div for plugin fills
Mar 14, 2018
b435d4b
Removed getPluginUIComponent API function in favor of 'plugin-sidebar…
Mar 15, 2018
90d0af8
Don't throw error on invalid PluginSidebar props
Mar 15, 2018
6540b05
Plugins: Refactor plugin UI registration as Slot/Fill
aduth Mar 15, 2018
f127727
Plugins: Remove unused code
aduth Mar 15, 2018
cdc51c5
Plugins: Represent plugin render as the component it is
aduth Mar 15, 2018
5488d06
Plugins: Revert plugin sidebar slot name to PluginSidebar
aduth Mar 15, 2018
9ee20f1
Plugins: Remove unused error boundary styles
aduth Mar 16, 2018
ce9b374
Plugin: Remove unused sidebar settings
aduth Mar 16, 2018
a322b70
Fix plugin sidebar with on mobile
atimmer Mar 16, 2018
d751d50
Merge branch 'master' into add/slot-fill-sidebar-api
atimmer Mar 16, 2018
10df23c
Merge branch 'master' into add/slot-fill-sidebar-api
atimmer Mar 16, 2018
853655c
Documentation: Remove experimental flag from plugins
aduth Mar 16, 2018
a1de97b
Testing: Include plugins folder in ESLint import rule
aduth Mar 16, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bin/build-plugin-zip.sh
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ zip -r gutenberg.zip \
blocks/library/*/*.php \
post-content.js \
$vendor_scripts \
{blocks,components,date,editor,element,hooks,i18n,data,utils,edit-post,viewport}/build/*.{js,map} \
{blocks,components,date,editor,element,hooks,i18n,data,utils,edit-post,viewport,plugins}/build/*.{js,map} \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tangent: Perhaps by reading webpack.config.js, it'd be nice if we didn't have to manually keep the build script, the ESLint config, etc. in sync.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tangent: Perhaps by reading webpack.config.js, it'd be nice if we didn't have to manually keep the build script, the ESLint config, etc. in sync.

Yes, this has become an increasingly frequent source of friction.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--> #5664, for starters

{blocks,components,editor,edit-post}/build/*.css \
languages/gutenberg.pot \
languages/gutenberg-translations.php \
Expand Down
66 changes: 44 additions & 22 deletions docs/extensibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,38 +120,60 @@ wp.hooks.addFilter(

_Note:_ This filter must always be run on every page load, and not in your browser's developer tools console. Otherwise, a [block validation](https://wordpress.org/gutenberg/handbook/block-api/block-edit-save/#validation) error will occur the next time the post is edited. This is due to the fact that block validation occurs by verifying that the saved output matches what is stored in the post's content during editor initialization. So, if this filter does not exist when the editor loads, the block will be marked as invalid.

## Adding a sidebar
## Extending the editor's UI (Experimental)

**Warning:** This is an experimental API, and is subject to change or even removal.
Extending the editor's UI is done with in `registerPlugin` API, and allows you to define all your plugin's UI elements in one place.

### Registering a sidebar

`wp.editPost.__experimentalRegisterSidebar( name: string, settings: { title: string, render: function } )`

This method takes a sidebar `name` and a `settings` object, containing a title and a render function. The name should contain a namespace prefix (Example: my-plugin/my-custom-sidebar).
### `wp.plugins.__experimental.registerPlugin( name: string, { render: function } )`

This method takes two arguments:
1. `name`: A string identifying the plugin. Must be unique across all registered plugins.
2. `settings`: An object containing the following data:
- `render`: A component containing the UI elements to be rendered. See the list below for all available UI elements.

**Example**

```js
wp.editPost.__experimentalRegisterSidebar( 'my-plugin/my-custom-sidebar', {
render: function mySidebar() {
return <p>This is an example</p>;
},
```jsx
const { Fragment } = wp.element;
const { PluginSidebar } = wp.editPost.__experimental;

const Component = () => (
<Fragment>
<PluginSidebar name="first-sidebar-name" title="My Sidebar">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some issues with the spacing here when you check the preview:

screen shot 2018-03-08 at 15 01 48

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is also confusing that we use h1 in here, it might give an impression that it should be a header of the plugin, but it isn't.

<p>Content of the first sidebar</p>
</PluginSidebar>
<PluginSidebar name="second-sidebar-name" title="My Second Sidebar">
<p>Content of the second sidebar</p>
</PluginSidebar>
</Fragment>
);

wp.plugins.__experimental.registerPlugin( 'plugin-names', {
render: Component,
} );
```

### Activating a sidebar

`wp.editPost.__experimentalActivateSidebar( name: string )`

This method takes the `name` of the sidebar you'd like to open. That sidebar should have been registered beforehand using the `registerSidebar` method.

**Example**
You can activate the sidebars using the following lines:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can remove lines 157-160 as we have a similar thing put later in a better-scoped context.


```js
wp.editPost.__experimentalActivateSidebar( 'my-plugin/my-custom-sidebar' );
wp.data.dispatch( 'core/edit-post' ).openGeneralSidebar( 'plugin-name/first-sidebar-name' );
wp.data.dispatch( 'core/edit-post' ).openGeneralSidebar( 'plugin-name/second-sidebar-name' );
```

### Available UI components

The available UI components are found in the global variable `wp.editPost.__experimental`, and are React components.

## Extending the editor's UI (Slot and Fill)
#### PluginSidebar

Coming soon.
Renders a sidebar when activated.
```jsx
<PluginSidebar name="sidebar-name" title="Sidebar title">
<MySidebar />
</PluginSidebar>
```
- Props
- `name`: A string identifying the sidebar. Must be unique for every sidebar registered within the scope of your plugin.
- `title`: Title displayed at the top of the sidebar. Must be a string.

The contents you render within the `PluginSidebar` will show up as content within the sidebar.
37 changes: 0 additions & 37 deletions edit-post/api/README.md

This file was deleted.

4 changes: 0 additions & 4 deletions edit-post/api/index.js

This file was deleted.

100 changes: 0 additions & 100 deletions edit-post/api/sidebar.js

This file was deleted.

8 changes: 6 additions & 2 deletions edit-post/components/layout/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
} from '@wordpress/editor';
import { withDispatch, withSelect } from '@wordpress/data';
import { compose } from '@wordpress/element';
import { PluginArea } from '@wordpress/plugins';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add an entry to .eslintrc.js?

gutenberg/.eslintrc.js

Lines 77 to 80 in 10df23c

{
selector: 'ImportDeclaration[source.value=/^viewport$/]',
message: 'Use @wordpress/viewport as import path instead.',
},

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add an entry to .eslintrc.js?

Added in a1de97b.


/**
* Internal dependencies
Expand All @@ -31,12 +32,13 @@ import VisualEditor from '../visual-editor';
import EditorModeKeyboardShortcuts from '../keyboard-shortcuts';
import MetaBoxes from '../meta-boxes';
import { getMetaBoxContainer } from '../../utils/meta-boxes';
import PluginsPanel from '../../components/plugins-panel/index.js';
import PluginSidebar from '../plugin-sidebar';

function Layout( {
mode,
editorSidebarOpened,
pluginSidebarOpened,
sidebarName,
publishSidebarOpened,
hasFixedToolbar,
closePublishSidebar,
Expand Down Expand Up @@ -81,8 +83,9 @@ function Layout( {
/>
) }
{ editorSidebarOpened && <Sidebar /> }
{ pluginSidebarOpened && <PluginsPanel /> }
{ pluginSidebarOpened && <PluginSidebar.Slot name={ sidebarName } /> }
<Popover.Slot />
<PluginArea />
</div>
);
}
Expand All @@ -92,6 +95,7 @@ export default compose(
mode: select( 'core/edit-post' ).getEditorMode(),
editorSidebarOpened: select( 'core/edit-post' ).isEditorSidebarOpened(),
pluginSidebarOpened: select( 'core/edit-post' ).isPluginSidebarOpened(),
sidebarName: select( 'core/edit-post' ).getActiveGeneralSidebarName(),
publishSidebarOpened: select( 'core/edit-post' ).isPublishSidebarOpened(),
hasFixedToolbar: select( 'core/edit-post' ).isFeatureActive( 'fixedToolbar' ),
metaBoxes: select( 'core/edit-post' ).getMetaBoxes(),
Expand Down
57 changes: 57 additions & 0 deletions edit-post/components/plugin-sidebar/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* WordPress dependencies
*/
import { compose } from '@wordpress/element';
import { Slot, Fill, withFocusReturn, withContext } from '@wordpress/components';
import { withDispatch } from '@wordpress/data';

/**
* Internal dependencies
*/
import './style.scss';
import SidebarLayout from './sidebar-layout';

/**
* Name of slot in which the sidebar should fill.
*
* @type {String}
*/
const SLOT_NAME = 'PluginSidebar';

/**
* Renders the plugin sidebar fill.
*
* @return {WPElement} Plugin sidebar fill.
*/
function PluginSidebar( { pluginName, name, title, onClose, children } ) {
return (
<Fill name={ [ SLOT_NAME, pluginName, name ].join( '/' ) }>
<SidebarLayout
title={ title }
onClose={ onClose } >
{ children }
</SidebarLayout>
</Fill>
);
}

PluginSidebar = compose( [
withDispatch( dispatch => {
return {
onClose: dispatch( 'core/edit-post' ).closeGeneralSidebar,
};
} ),
withFocusReturn,
withContext( 'pluginName' )(),
] )( PluginSidebar );

/**
* The plugin sidebar slot.
*
* @return {WPElement} The plugin sidebar slot.
*/
PluginSidebar.Slot = ( { name } ) => (
<Slot name={ [ SLOT_NAME, name ].join( '/' ) } />
);

export default PluginSidebar;
34 changes: 34 additions & 0 deletions edit-post/components/plugin-sidebar/sidebar-layout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* WordPress dependencies
*/
import { IconButton } from '@wordpress/components';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import './style.scss';

const SidebarLayout = ( { onClose, title, children } ) => {
return (
<div
className="edit-post-plugin-sidebar__sidebar-layout"
role="region"
aria-label={ __( 'Editor plugins' ) }
tabIndex="-1">
<div className="edit-post-plugin-sidebar__sidebar-layout__header">
<h3 className="edit-post-plugin-sidebar__sidebar-layout__title">{ title }</h3>
<IconButton
onClick={ onClose }
icon="no-alt"
label={ __( 'Close settings' ) }
/>
</div>
<div className="edit-post-plugin-sidebar__sidebar-layout__content">
{ children }
</div>
</div>
);
};

export default SidebarLayout;
Loading