-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Changes from 78 commits
142f7b1
65af7e6
0e921fe
2b022f7
78623ad
0beaa4b
cbfe6dc
75e35ba
1ae8632
0d3d03d
789e675
08f6e7b
ffe0870
c55eaaf
a3a08de
37a41d8
ab5df97
8fe79d0
8c2930c
a3fada7
83a4dd3
908112d
913415e
2dc4a78
a4ce7d2
1432bc4
ca63d62
4ac2152
fe93e2d
0a6ee55
499cb71
fde16b5
a268de4
8bfcbee
ef02a0c
c2e0f20
528bcdc
893dff8
86049a9
8d00bf4
5b22af7
bb552ef
d4da2c5
b110d70
b4dfc8e
c173637
47ee199
d56a58d
646f72c
60d174c
42652a5
210d0ab
f082bf0
b629a85
c871e8b
a960db8
1f6b5f5
295d410
474ff55
b809684
20eb71a
688280c
d25b07d
4d5209a
db3c98c
b2526da
d3e038f
eed00e2
995d7de
e706a7a
dc717f8
644b7e8
02a5924
687e25c
5f8beaa
b435d4b
90d0af8
6540b05
f127727
cdc51c5
5488d06
9ee20f1
ce9b374
a322b70
d751d50
10df23c
853655c
a1de97b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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"> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
This file was deleted.
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -19,6 +19,7 @@ import { | |||||||||
} from '@wordpress/editor'; | ||||||||||
import { withDispatch, withSelect } from '@wordpress/data'; | ||||||||||
import { compose } from '@wordpress/element'; | ||||||||||
import { PluginArea } from '@wordpress/plugins'; | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we add an entry to Lines 77 to 80 in 10df23c
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Added in a1de97b. |
||||||||||
|
||||||||||
/** | ||||||||||
* Internal dependencies | ||||||||||
|
@@ -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, | ||||||||||
|
@@ -81,8 +83,9 @@ function Layout( { | |||||||||
/> | ||||||||||
) } | ||||||||||
{ editorSidebarOpened && <Sidebar /> } | ||||||||||
{ pluginSidebarOpened && <PluginsPanel /> } | ||||||||||
{ pluginSidebarOpened && <PluginSidebar.Slot name={ sidebarName } /> } | ||||||||||
<Popover.Slot /> | ||||||||||
<PluginArea /> | ||||||||||
</div> | ||||||||||
); | ||||||||||
} | ||||||||||
|
@@ -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(), | ||||||||||
|
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 = 'plugin-sidebar'; | ||
|
||
/** | ||
* 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; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { Component } from '@wordpress/element'; | ||
import { __, sprintf } from '@wordpress/i18n'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import './style.scss'; | ||
|
||
class SidebarErrorBoundary extends Component { | ||
constructor( props ) { | ||
super( props ); | ||
this.state = { hasError: false }; | ||
} | ||
|
||
componentDidCatch() { | ||
this.setState( { hasError: true } ); | ||
} | ||
|
||
render() { | ||
if ( this.state.hasError ) { | ||
return ( | ||
<p className="edit-post-plugin-sidebar__sidebar-error-boundary__message"> | ||
{ sprintf( __( 'An error occurred rendering the plugin sidebar with id "%s".' ), this.props.pluginName ) } | ||
</p> | ||
); | ||
} | ||
return this.props.children; | ||
} | ||
} | ||
|
||
export default SidebarErrorBoundary; |
There was a problem hiding this comment.
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.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this has become an increasingly frequent source of friction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
--> #5664, for starters