-
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
Merged
Merged
Changes from 1 commit
Commits
Show all changes
88 commits
Select commit
Hold shift + click to select a range
142f7b1
Initial implementation of slot-fill sidebar API
65af7e6
Attempt to implement createContext api for plugin slot fill API
0e921fe
Fixed error on start up
2b022f7
Implemented way to pass props from plugin API via context to PluginSi…
78623ad
Finalize first pass of registerPlugin API
0beaa4b
Remove create-react-context
cbfe6dc
Merge branch 'master' into add/slot-fill-sidebar-api
75e35ba
Removed old sidebar and refactored the way plugin context is handled
1ae8632
Improved withPluginContext HOC to prevent usage of cloneElement
0d3d03d
Implemented tests for registerPlugin
789e675
Added test for plugin/context components, revamped naming of passed c…
08f6e7b
Fixed eslint issues
ffe0870
Refactored plugin-sidebar to use withSelect/withDispatch and created …
c55eaaf
Removed PropTypes
a3a08de
Used enzyme instead of react-test-renderer in plugin context tests
37a41d8
Hidden internal Slot and Fill components in layout
ab5df97
Moved plugin-sidebar error-boundary to separate file
8fe79d0
Moved sidebar error boundary around the component only so the error c…
8c2930c
Updated documentation
a3fada7
Updated documentation
83a4dd3
Updated documentation
908112d
Refactored editpost/api/context component to reparate folder
913415e
Moved plugin components to api folder and made API experimental
2dc4a78
Updated docs to reflect experimental status
a4ce7d2
Updated documentation
1432bc4
Fixed failing test
ca63d62
Docs: Add a few tweaks to the Extensibility doc
gziolo 4ac2152
Initial implementation of slot-fill sidebar API
fe93e2d
Attempt to implement createContext api for plugin slot fill API
0a6ee55
Fixed error on start up
499cb71
Implemented way to pass props from plugin API via context to PluginSi…
fde16b5
Finalize first pass of registerPlugin API
a268de4
Remove create-react-context
8bfcbee
Removed old sidebar and refactored the way plugin context is handled
ef02a0c
Improved withPluginContext HOC to prevent usage of cloneElement
c2e0f20
Implemented tests for registerPlugin
528bcdc
Added test for plugin/context components, revamped naming of passed c…
893dff8
Fixed eslint issues
86049a9
Refactored plugin-sidebar to use withSelect/withDispatch and created …
8d00bf4
Removed PropTypes
5b22af7
Used enzyme instead of react-test-renderer in plugin context tests
bb552ef
Hidden internal Slot and Fill components in layout
d4da2c5
Moved plugin-sidebar error-boundary to separate file
b110d70
Moved sidebar error boundary around the component only so the error c…
b4dfc8e
Updated documentation
c173637
Updated documentation
47ee199
Updated documentation
d56a58d
Refactored editpost/api/context component to reparate folder
646f72c
Moved plugin components to api folder and made API experimental
60d174c
Updated docs to reflect experimental status
42652a5
Updated documentation
210d0ab
Fixed failing test
f082bf0
Docs: Add a few tweaks to the Extensibility doc
gziolo b629a85
Rebase master
c871e8b
Merged remote changes
a960db8
Merge branch 'master' of https://github.com/WordPress/gutenberg into …
1f6b5f5
Register UI components in plugin.js
295d410
Updated tests
474ff55
created PluginRegistry singleton
b809684
Updated JSDoc
20eb71a
Made name first parameter of registerPlugin function instead of being…
688280c
Small documentation tweaks
d25b07d
Moved and documented plugin context provider and higher order component
4d5209a
Updated test
db3c98c
Fixed sass class names
b2526da
Made scss specific for plugin0-sidebar which nog longer depends on ed…
d3e038f
Moved PLuginsArea to separate file
eed00e2
Updated test
995d7de
Moved plugin api to plugins module
e706a7a
Updated documentation
dc717f8
Converted spaces to tabs in style.scss file
644b7e8
Comply to coding style conventions regarding css in plugin-sidebar
02a5924
Added slot to pluginsidebar fill for consistency
687e25c
Moved styles.scss under Internal dependencies
5f8beaa
removed unnecessary id from hidden div for plugin fills
b435d4b
Removed getPluginUIComponent API function in favor of 'plugin-sidebar…
90d0af8
Don't throw error on invalid PluginSidebar props
6540b05
Plugins: Refactor plugin UI registration as Slot/Fill
aduth f127727
Plugins: Remove unused code
aduth cdc51c5
Plugins: Represent plugin render as the component it is
aduth 5488d06
Plugins: Revert plugin sidebar slot name to PluginSidebar
aduth 9ee20f1
Plugins: Remove unused error boundary styles
aduth ce9b374
Plugin: Remove unused sidebar settings
aduth a322b70
Fix plugin sidebar with on mobile
atimmer d751d50
Merge branch 'master' into add/slot-fill-sidebar-api
atimmer 10df23c
Merge branch 'master' into add/slot-fill-sidebar-api
atimmer 853655c
Documentation: Remove experimental flag from plugins
aduth a1de97b
Testing: Include plugins folder in ESLint import rule
aduth File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,110 +1,57 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { Component, Children, cloneElement, compose } from '@wordpress/element'; | ||
import { Slot, Fill, withFocusReturn } from '@wordpress/components'; | ||
import { withSelect, withDispatch } from '@wordpress/data'; | ||
import { withPluginContext } from '@wordpress/plugins'; | ||
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'; | ||
import ErrorBoundary from './sidebar-error-boundary'; | ||
|
||
/** | ||
* Name of slot in which the sidebar should fill. | ||
* | ||
* @type {String} | ||
*/ | ||
const SLOT_NAME = 'PluginSidebar'; | ||
const SLOT_NAME = 'plugin-sidebar'; | ||
|
||
/** | ||
* The plugin sidebar fill. | ||
* Renders the plugin sidebar fill. | ||
* | ||
* @return {WPElement} Plugin sidebar fill. | ||
*/ | ||
class PluginSidebar extends Component { | ||
constructor( props ) { | ||
super( props ); | ||
|
||
this.state = { invalid: false }; | ||
|
||
if ( typeof props.name !== 'string' ) { | ||
// eslint-disable-next-line no-console | ||
console.error( 'PluginSidebar name should be of type string' ); | ||
this.state.invalid = true; | ||
} | ||
if ( ! /^[a-z][a-z0-9-]*$/.test( props.name ) ) { | ||
// eslint-disable-next-line no-console | ||
console.error( 'Sidebar names must include only lowercase alphanumeric characters or dashes,' + | ||
' and start with a letter. Example: "my-sidebar".' ); | ||
this.state.invalid = true; | ||
} | ||
} | ||
|
||
/** | ||
* Generates the UI plugin identifier by combining the plugin name and the sidebar name. | ||
* | ||
* Also registers the plugin in the plugin registry. | ||
*/ | ||
componentWillMount() { | ||
if ( ! this.namespacedName ) { | ||
this.namespacedName = `plugin-sidebar/${ this.props.pluginContext.namespace }/${ this.props.name }`; | ||
} | ||
} | ||
|
||
/** | ||
* Renders the PluginSidebar component. | ||
* | ||
* @return {ReactElement} The rendered component. | ||
*/ | ||
render() { | ||
if ( this.props.activePlugin !== this.namespacedName || this.state.invalid ) { | ||
return null; | ||
} | ||
|
||
const { children, ...props } = this.props; | ||
const newProps = { | ||
...props, | ||
namespacedName: this.namespacedName, | ||
}; | ||
|
||
return ( | ||
<Fill name={ SLOT_NAME }> | ||
<SidebarLayout | ||
title={ props.title } | ||
onClose={ props.onClose } > | ||
<ErrorBoundary pluginName={ this.namespacedName }> | ||
{ typeof children === 'string' ? children : cloneElement( Children.only( children ), newProps ) } | ||
</ErrorBoundary> | ||
</SidebarLayout> | ||
</Fill> | ||
); | ||
} | ||
function PluginSidebar( { pluginName, name, title, onClose, children } ) { | ||
return ( | ||
<Fill name={ [ SLOT_NAME, pluginName, name ].join( '/' ) }> | ||
<SidebarLayout | ||
title={ title } | ||
onClose={ onClose } > | ||
{ children } | ||
</SidebarLayout> | ||
</Fill> | ||
); | ||
} | ||
|
||
const WrappedPluginSidebar = compose( [ | ||
withSelect( select => { | ||
return { | ||
activePlugin: select( 'core/edit-post' ).getActiveGeneralSidebarName(), | ||
}; | ||
} ), | ||
PluginSidebar = compose( [ | ||
withDispatch( dispatch => { | ||
return { | ||
onClose: dispatch( 'core/edit-post' ).closeGeneralSidebar, | ||
}; | ||
} ), | ||
withFocusReturn, | ||
withPluginContext, | ||
withContext( 'pluginName' )(), | ||
] )( PluginSidebar ); | ||
|
||
/** | ||
* The plugin sidebar slot. | ||
* | ||
* @return {ReactElement} The plugin sidebar slot. | ||
* @return {WPElement} The plugin sidebar slot. | ||
*/ | ||
WrappedPluginSidebar.Slot = () => ( | ||
<Slot name={ SLOT_NAME } /> | ||
PluginSidebar.Slot = ( { name } ) => ( | ||
<Slot name={ [ SLOT_NAME, name ].join( '/' ) } /> | ||
); | ||
|
||
export default WrappedPluginSidebar; | ||
export default PluginSidebar; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,106 @@ | ||
/* eslint no-console: [ 'error', { allow: [ 'error' ] } ] */ | ||
|
||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { applyFilters } from '@wordpress/hooks'; | ||
|
||
/** | ||
* External dependencies | ||
*/ | ||
import { isFunction } from 'lodash'; | ||
|
||
/** | ||
* Plugin definitions keyed by plugin name. | ||
* | ||
* @type {Object.<string,WPPlugin>} | ||
*/ | ||
const plugins = {}; | ||
|
||
/** | ||
* Internal dependencies | ||
* Registers a plugin to the editor. | ||
* | ||
* @param {string} name The name of the plugin. | ||
* @param {Object} settings The settings for this plugin. | ||
* @param {Function} settings.render The function that renders the plugin. | ||
* | ||
* @return {Object} The final plugin settings object. | ||
*/ | ||
import PluginRegistry from './plugin'; | ||
export function registerPlugin( name, settings ) { | ||
if ( typeof settings !== 'object' ) { | ||
console.error( | ||
'No settings object provided!' | ||
); | ||
return null; | ||
} | ||
if ( typeof name !== 'string' ) { | ||
console.error( | ||
'Plugin names must be strings.' | ||
); | ||
return null; | ||
} | ||
if ( ! /^[a-z][a-z0-9-]*$/.test( name ) ) { | ||
console.error( | ||
'Plugin names must include only lowercase alphanumeric characters or dashes, and start with a letter. Example: "my-plugin".' | ||
); | ||
return null; | ||
} | ||
if ( plugins[ name ] ) { | ||
console.error( | ||
`Plugin "${ name }" is already registered.` | ||
); | ||
} | ||
if ( ! isFunction( settings.render ) ) { | ||
console.error( | ||
'The "render" property must be specified and must be a valid function.' | ||
); | ||
return null; | ||
} | ||
|
||
const registry = PluginRegistry.getInstance(); | ||
settings.name = name; | ||
settings.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. This doesn't do anything |
||
|
||
const __experimental = { | ||
registerPlugin: registry.registerPlugin, | ||
getRegisteredUIComponent: registry.getRegisteredUIComponent, | ||
}; | ||
settings = applyFilters( 'plugins.registerPlugin', settings, name ); | ||
|
||
export { | ||
__experimental, | ||
}; | ||
return plugins[ settings.name ] = settings; | ||
} | ||
|
||
/** | ||
* Unregisters a plugin by name. | ||
* | ||
* @param {string} name Plugin name. | ||
* | ||
* @return {?WPPlugin} The previous plugin settings object, if it has been | ||
* successfully unregistered; otherwise `undefined`. | ||
*/ | ||
export function unregisterPlugin( name ) { | ||
if ( ! plugins[ name ] ) { | ||
console.error( | ||
'Plugin "' + name + '" is not registered.' | ||
); | ||
return; | ||
} | ||
const oldPlugin = plugins[ name ]; | ||
delete plugins[ name ]; | ||
return oldPlugin; | ||
} | ||
|
||
/** | ||
* Returns a registered plugin settings. | ||
* | ||
* @param {string} name Plugin name. | ||
* | ||
* @return {?Object} Plugin setting. | ||
*/ | ||
export function getPlugin( name ) { | ||
return plugins[ name ]; | ||
} | ||
|
||
/** | ||
* Returns all registered plugins. | ||
* | ||
* @return {Array} Plugin settings. | ||
*/ | ||
export function getPlugins() { | ||
return Object.values( plugins ); | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
This was actually what I was trying to prevent because it doesn't allow any more sidebar types. But I guess that's a concern for later.
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.
It's the nice thing about a selector is that it abstracts away these underlying implementation-detail concerns.