-
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
Add an API to add a plugin sidebar #4109
Changes from 16 commits
f3d3daf
2864974
b4fa154
ba05cd8
506fd15
9caaf8e
53579b3
9428aa9
7241609
8870121
cc0b124
b3aa610
c7b36b3
e4b9df3
693e5a8
04d6e2b
aa3c84b
ed6ea39
3af2240
10529c8
fd057e8
af1cc84
c7daa63
a5ffe6c
ec870c3
b34991d
06950bd
fce3ddd
02cc074
0072e66
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 |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export { | ||
registerSidebar, | ||
renderSidebar, | ||
activateSidebar, | ||
getSidebar | ||
} from './sidebar'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
/* eslint no-console: [ 'error', { allow: [ 'error' ] } ] */ | ||
|
||
/* External dependencies */ | ||
import { isFunction } from 'lodash'; | ||
|
||
/* Internal dependencies */ | ||
import store from '../store'; | ||
import { setGeneralSidebarActivePanel, openGeneralSidebar } from '../store/actions'; | ||
import { applyFilters } from '@wordpress/hooks'; | ||
|
||
const sidebars = {}; | ||
|
||
/** | ||
* Registers a sidebar to the editor. | ||
* | ||
* A button will be shown in the settings menu to open the sidebar. The sidebar | ||
* can be manually opened by calling the `activateSidebar` function. | ||
* | ||
* @param {string} name The name of the sidebar. Should be in | ||
* `[plugin]/[sidebar]` format. | ||
* @param {Object} settings The settings for this sidebar. | ||
* @param {string} settings.title The name to show in the settings menu. | ||
* @param {Function} settings.render The function that renders the sidebar. | ||
* | ||
* @returns {Object} The final sidebar settings object. | ||
*/ | ||
export function registerSidebar( name, settings ) { | ||
settings = { | ||
name, | ||
...settings, | ||
}; | ||
|
||
if ( typeof name !== 'string' ) { | ||
console.error( | ||
'Sidebar names must be strings.' | ||
); | ||
return null; | ||
} | ||
if ( ! /^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/.test( name ) ) { | ||
console.error( | ||
'Sidebar names must contain a namespace prefix, include only lowercase alphanumeric characters or dashes, and start with a letter. Example: my-plugin/my-custom-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. Missing period |
||
); | ||
return null; | ||
} | ||
if ( ! settings || ! isFunction( settings.render ) ) { | ||
console.error( | ||
'The "render" property must be specified and must be a valid function.' | ||
); | ||
return null; | ||
} | ||
if ( sidebars[ name ] ) { | ||
console.error( | ||
'Sidebar "' + name + '" is already registered.' | ||
); | ||
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. Missing return 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 might be intentional to overwrite a plugin? 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. Minor: Normally we avoid + operator to concatenate strings Maybe we can use |
||
} | ||
|
||
if ( ! settings.title ) { | ||
console.error( | ||
'The sidebar "' + name + '" must have a title.' | ||
); | ||
return null; | ||
} | ||
if ( typeof settings.title !== 'string' ) { | ||
console.error( | ||
'Sidebar titles must be strings.' | ||
); | ||
return null; | ||
} | ||
|
||
settings = applyFilters( 'editor.registerSidebar', settings, name ); | ||
|
||
return sidebars[ name ] = settings; | ||
} | ||
|
||
/** | ||
* Retrieves the sidebar settings object. | ||
* | ||
* @param {string} name The name of the sidebar to retrieve the settings for. | ||
* | ||
* @returns {Object} The settings object of the sidebar. Or null if the | ||
* sidebar doesn't exist. | ||
*/ | ||
export function getSidebarSettings( name ) { | ||
console.log("getSidebarSettings", sidebars) | ||
if ( ! sidebars.hasOwnProperty( name ) ) { | ||
return null; | ||
} | ||
return sidebars[ name ]; | ||
} | ||
|
||
/** | ||
* Renders a plugin sidebar. | ||
* | ||
* @param {string} name The name of the plugin sidebar. | ||
* @param {Object} settings The settings for this sidebar. | ||
* @param {string} settings.title The name to show in the settings menu. | ||
* @param {Function} settings.render The function that renders the sidebar. | ||
* | ||
* @returns {void} | ||
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. To my knowledge, there's no concept of a void type in JavaScript†. Would we need this JSDoc tag at all? More accurately this would have an † There is a void operator 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. The |
||
*/ | ||
export function renderSidebar( name, settings ) { | ||
registerSidebar( name, settings ); | ||
activateSidebar( name, settings ); | ||
/* | ||
let render = getSidebarSettings( name ).render; | ||
render();*/ | ||
} | ||
|
||
/** | ||
* Activates the given sidebar. | ||
* | ||
* @param {string} name The name of the sidebar to activate. | ||
* @return {void} | ||
*/ | ||
export function activateSidebar( name ) { | ||
if ( ! sidebars[ name ] ) { | ||
console.error( | ||
'Sidebar "' + name + '" is not registered yet.' | ||
); | ||
} | ||
store.dispatch( openGeneralSidebar( 'plugins' ) ); | ||
store.dispatch( setGeneralSidebarActivePanel( 'plugins', name ) ); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { connect } from 'react-redux'; | ||
|
||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { __, sprintf } from '@wordpress/i18n'; | ||
import { compose } from '@wordpress/element'; | ||
import { Panel, PanelBody, IconButton, withFocusReturn } from '@wordpress/components'; | ||
|
||
/** | ||
* Internal Dependencies | ||
*/ | ||
import './style.scss'; | ||
import { getSidebarSettings } from '../../api/sidebar'; | ||
import { getActivePlugin } from '../../store/selectors'; | ||
import { closeGeneralSidebar } from '../../store/actions'; | ||
|
||
/** | ||
* Returns the sidebar that should be rendered in the sidebar registered by | ||
* plugins. | ||
* | ||
* @param {string} plugin The currently active plugin. | ||
* | ||
* @returns {Object} The React element to render as a panel. | ||
*/ | ||
export function getPluginSidebar( plugin ) { | ||
const pluginSidebar = getSidebarSettings( plugin ); | ||
console.log( "pluginSidebar", pluginSidebar ); | ||
console.log( "plugin", plugin ); | ||
|
||
if ( ! pluginSidebar ) { | ||
return { | ||
title: __( 'Error: Unregistered plugin requested.' ), | ||
render: () => { | ||
return <Panel> | ||
<PanelBody> | ||
{ sprintf( __( 'No matching plugin sidebar found for plugin "%s"' ), plugin ) } | ||
</PanelBody> | ||
</Panel>; | ||
}, | ||
}; | ||
} | ||
return pluginSidebar; | ||
} | ||
|
||
function PluginsPanel( { onClose, plugin } ) { | ||
const { | ||
title, | ||
render, | ||
} = getPluginSidebar( plugin ); | ||
return ( | ||
<div | ||
className="editor-sidebar" | ||
role="region" | ||
aria-label={ __( 'Editor plugins' ) } | ||
tabIndex="-1"> | ||
<div className="components-panel__header editor-sidebar__panel-tabs"> | ||
<h3>{ title }</h3> | ||
<IconButton | ||
onClick={ onClose } | ||
icon="no-alt" | ||
label={ __( 'Close settings' ) } | ||
/> | ||
</div> | ||
<div className="editor-plugins-panel__content"> | ||
{ render() } | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
export default connect( | ||
( state ) => { | ||
return { | ||
plugin: getActivePlugin( state ), | ||
}; | ||
}, { | ||
onClose: closeGeneralSidebar, | ||
} | ||
)( withFocusReturn( PluginsPanel ) ); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
.editor-plugins-panel { | ||
position: fixed; | ||
top: 0; | ||
right: 0; | ||
bottom: 0; | ||
width: $sidebar-width; | ||
border-left: 1px solid $light-gray-500; | ||
background: $light-gray-300; | ||
color: $dark-gray-500; | ||
height: 100vh; | ||
overflow: hidden; | ||
|
||
@include break-small() { | ||
top: $admin-bar-height-big + $header-height; | ||
z-index: auto; | ||
height: auto; | ||
overflow: auto; | ||
-webkit-overflow-scrolling: touch; | ||
} | ||
|
||
@include break-medium() { | ||
top: $admin-bar-height + $header-height; | ||
} | ||
|
||
> .components-panel .components-panel__header { | ||
position: fixed; | ||
z-index: z-index( '.components-panel__header' ); | ||
top: 0; | ||
left: 0; | ||
right: 0; | ||
height: $panel-header-height; | ||
|
||
@include break-small() { | ||
position: inherit; | ||
top: auto; | ||
left: auto; | ||
right: auto; | ||
} | ||
} | ||
} | ||
|
||
.editor-plugins-panel__header { | ||
padding-left: 16px; | ||
height: $header-height; | ||
border-bottom: 1px solid $light-gray-500; | ||
display: flex; | ||
align-items: center; | ||
align-content: space-between; | ||
} |
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.
TIL
@returns
and@return
are equally valid and equivalent JSDoc tags. Noting that we've conventionally used@return
elsewhere.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.
In the WordPress JavaScript documentation standards we go with
@returns
.