-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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 1 commit
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,4 @@ | ||
export { | ||
registerSidebar, | ||
activateSidebar, | ||
} from './sidebar'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
/* eslint no-console: [ 'error', { allow: [ 'error' ] } ] */ | ||
|
||
/* External dependencies */ | ||
import { isFunction } from 'lodash'; | ||
|
||
/* Internal dependencies */ | ||
import store from '../store'; | ||
import { setActivePanel } from '../store/actions'; | ||
import { applyFilters } from '@wordpress/hooks'; | ||
|
||
const sidebars = {}; | ||
|
||
/** | ||
* Registers a sidebar with 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; | ||
} | ||
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; | ||
} | ||
if ( ! settings || ! isFunction( settings.render ) ) { | ||
console.error( | ||
'The "render" property must be specified and must be a valid function.' | ||
); | ||
return; | ||
} | ||
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 ( ! ( 'title' in settings ) || settings.title === '' ) { | ||
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. Could this be simplified to |
||
console.error( | ||
'The sidebar "' + name + '" must have a title.' | ||
); | ||
return; | ||
} | ||
if ( typeof settings.title !== 'string' ) { | ||
console.error( | ||
'Sidebar titles must be strings.' | ||
); | ||
return; | ||
} | ||
|
||
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. | ||
* | ||
* @returns {false|Object} The settings object of the sidebar. Or false if the | ||
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 "return Aside: If we did return false, the return value type would be Boolean. |
||
* sidebar doesn't exist. | ||
*/ | ||
export function getSidebar( name ) { | ||
if ( ! sidebars.hasOwnProperty( name ) ) { | ||
return false; | ||
} | ||
|
||
return sidebars[ name ]; | ||
} | ||
|
||
/** | ||
* Retrieves all sidebars that are registered. | ||
* | ||
* @returns {Object[]} Registered sidebars. | ||
*/ | ||
export function getSidebars() { | ||
return sidebars; | ||
} | ||
|
||
/** | ||
* Activates the gives 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. the given sidebar |
||
* | ||
* @param {string} name The name of the sidebar to activate. | ||
*/ | ||
export function activateSidebar( name ) { | ||
store.dispatch( setActivePanel( name ) ); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,7 @@ import { IconButton, Dropdown } from '@wordpress/components'; | |
import './style.scss'; | ||
import ModeSwitcher from '../mode-switcher'; | ||
import FixedToolbarToggle from '../fixed-toolbar-toggle'; | ||
import Plugins from '../plugins'; | ||
|
||
const element = ( | ||
<Dropdown | ||
|
@@ -28,6 +29,8 @@ const element = ( | |
<ModeSwitcher onSelect={ onClose } /> | ||
<div className="editor-ellipsis-menu__separator" /> | ||
<FixedToolbarToggle onToggle={ onClose } /> | ||
<div className="editor-ellipsis-menu__separator" /> | ||
<Plugins onToggle={ onClose } /> | ||
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. |
||
</div> | ||
) } | ||
/> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { map } from 'lodash'; | ||
|
||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { __ } from '@wordpress/i18n'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { getSidebars, activateSidebar } from '../../../api/sidebar'; | ||
import { MenuItemsGroup } from '../../../../components'; | ||
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. We should not be reaching into other top-level directories. This should be |
||
import { getActivePanel, isEditorSidebarOpened } from '../../../store/selectors'; | ||
import { connect } from 'react-redux'; | ||
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 should be moved under "External dependencies" |
||
import { toggleSidebar } from '../../../store/actions'; | ||
|
||
/** | ||
* Renders a list of plugins that will activate different UI elements. | ||
* | ||
* @param {Object} props Props. | ||
* @param {Function} props.onSwitch Function to call when a plugin is | ||
* switched to. | ||
* @param {string} props.activePanel The currently active panel. | ||
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. Even though the JSDoc documentation differentiates with lower and uppercase differences, and I'd previously made an attempt to respect them, I think we ought to just standardize on always uppercasing, to avoid needing to consistently consult with the docs (or more likely, do it wrong). 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 could be a good discussion to have in #core-js. Tentatively I would say that keeping primitives lower case has value for a parser. This is consistent with PHP, where primitives are always lowercase and objects are always spelled the way they are defined in the Doesn't block this PR, I think. |
||
* @param {boolean} props.isSidebarOpened Whether the sidebar is currently open. | ||
* @param {Function} props.onToggleSidebar Function to call when the sidebar | ||
* should be toggled. | ||
* | ||
* @returns {Object} The rendered list of menu items. | ||
*/ | ||
function Plugins( { activePanel, onSwitch, isSidebarOpened, onToggleSidebar } ) { | ||
const sidebars = getSidebars(); | ||
|
||
// This makes sure no check mark is before a plugin if the sidebar is closed. | ||
if ( ! isSidebarOpened ) { | ||
activePanel = ''; | ||
} | ||
|
||
/** | ||
* Handles the user clicking on one of the plugins in the menu | ||
* | ||
* @param {string} panelToActivate The sidebar panel to activate. | ||
*/ | ||
function onSelect( panelToActivate ) { | ||
onSwitch( panelToActivate ); | ||
|
||
if ( ! isSidebarOpened ) { | ||
onToggleSidebar(); | ||
} | ||
} | ||
|
||
const plugins = map( sidebars, ( sidebar ) => { | ||
return { | ||
value: sidebar.name, | ||
label: sidebar.title, | ||
}; | ||
} ); | ||
|
||
return ( | ||
<MenuItemsGroup | ||
label={ __( 'Plugins' ) } | ||
choices={ plugins } | ||
value={ activePanel } | ||
onSelect={ onSelect } | ||
/> | ||
); | ||
} | ||
|
||
export default connect( | ||
( state ) => { | ||
return { | ||
activePanel: getActivePanel( state ), | ||
isSidebarOpened: isEditorSidebarOpened( state ), | ||
}; | ||
}, | ||
( dispatch, ownProps ) => { | ||
return { | ||
onSwitch: ( value ) => { | ||
activateSidebar( value ); | ||
ownProps.onToggle( value ); | ||
}, | ||
onToggleSidebar: () => { | ||
dispatch( toggleSidebar() ); | ||
}, | ||
}; | ||
} | ||
)( Plugins ); |
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
.