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

Site Editor - add basic plugin support #34460

Merged
merged 5 commits into from
Sep 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* WordPress dependencies
*/
import { ActionItem } from '@wordpress/interface';
import { compose } from '@wordpress/compose';
import { withPluginContext } from '@wordpress/plugins';

/**
* Renders a menu item in `Plugins` group in `More Menu` drop down, and can be used to as a button or link depending on the props provided.
* The text within the component appears as the menu item label.
*
* @param {Object} props Component properties.
* @param {string} [props.href] When `href` is provided then the menu item is represented as an anchor rather than button. It corresponds to the `href` attribute of the anchor.
* @param {WPBlockTypeIconRender} [props.icon=inherits from the plugin] The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug string, or an SVG WP element, to be rendered to the left of the menu item label.
* @param {Function} [props.onClick=noop] The callback function to be executed when the user clicks the menu item.
* @param {...*} [props.other] Any additional props are passed through to the underlying [Button](/packages/components/src/button/README.md) component.
*
* @example
* ```js
* // Using ES5 syntax
* var __ = wp.i18n.__;
* var PluginMoreMenuItem = wp.editSite.PluginMoreMenuItem;
* var moreIcon = wp.element.createElement( 'svg' ); //... svg element.
*
* function onButtonClick() {
* alert( 'Button clicked.' );
* }
*
* function MyButtonMoreMenuItem() {
* return wp.element.createElement(
* PluginMoreMenuItem,
* {
* icon: moreIcon,
* onClick: onButtonClick,
* },
* __( 'My button title' )
* );
* }
* ```
*
* @example
* ```jsx
* // Using ESNext syntax
* import { __ } from '@wordpress/i18n';
* import { PluginMoreMenuItem } from '@wordpress/edit-site';
* import { more } from '@wordpress/icons';
*
* function onButtonClick() {
* alert( 'Button clicked.' );
* }
*
* const MyButtonMoreMenuItem = () => (
* <PluginMoreMenuItem
* icon={ more }
* onClick={ onButtonClick }
* >
* { __( 'My button title' ) }
* </PluginMoreMenuItem>
* );
* ```
*
* @return {WPComponent} The component to be rendered.
*/
export default compose(
withPluginContext( ( context, ownProps ) => {
return {
icon: ownProps.icon || context.icon,
name: 'core/edit-site/plugin-more-menu',
};
} )
)( ActionItem );
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* WordPress dependencies
*/
import { ComplementaryAreaMoreMenuItem } from '@wordpress/interface';

/**
* Renders a menu item in `Plugins` group in `More Menu` drop down,
* and can be used to activate the corresponding `PluginSidebar` component.
* The text within the component appears as the menu item label.
*
* @param {Object} props Component props.
* @param {string} props.target A string identifying the target sidebar you wish to be activated by this menu item. Must be the same as the `name` prop you have given to that sidebar.
* @param {WPBlockTypeIconRender} [props.icon=inherits from the plugin] The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug string, or an SVG WP element, to be rendered to the left of the menu item label.
*
* @example
* ```js
* // Using ES5 syntax
* var __ = wp.i18n.__;
* var PluginSidebarMoreMenuItem = wp.editSite.PluginSidebarMoreMenuItem;
* var moreIcon = wp.element.createElement( 'svg' ); //... svg element.
*
* function MySidebarMoreMenuItem() {
* return wp.element.createElement(
* PluginSidebarMoreMenuItem,
* {
* target: 'my-sidebar',
* icon: moreIcon,
* },
* __( 'My sidebar title' )
* )
* }
* ```
*
* @example
* ```jsx
* // Using ESNext syntax
* import { __ } from '@wordpress/i18n';
* import { PluginSidebarMoreMenuItem } from '@wordpress/edit-site';
* import { more } from '@wordpress/icons';
*
* const MySidebarMoreMenuItem = () => (
* <PluginSidebarMoreMenuItem
* target="my-sidebar"
* icon={ more }
* >
* { __( 'My sidebar title' ) }
* </PluginSidebarMoreMenuItem>
* );
* ```
*
* @return {WPComponent} The component to be rendered.
*/

export default function PluginSidebarMoreMenuItem( props ) {
return (
<ComplementaryAreaMoreMenuItem
// Menu item is marked with unstable prop for backward compatibility.
// @see https://github.com/WordPress/gutenberg/issues/14457
__unstableExplicitMenuItem
Copy link
Contributor

Choose a reason for hiding this comment

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

Just a note that I looked into the implications of taking __unstableExplicitMenuItem out, since there won't be a concern with backwards compatibility, as we're just adding this to the Site Editor now.

If this attribute was removed, PluginSidebar components can't register a companion PluginSidebarMoreMenuItem when isPinnable={ true }. The PluginSidebarMoreMenuItem is generated automatically in that case, and is duplicated if it's also declared manually.

I think it's best to leave __unstableExplicitMenuItem in place:

  • Keeps the API consistent between the Post and Site editors
  • If PluginSidebarMoreMenuItem is declared, it is used as the PluginSidebar's menu item whether isPinnable is set to true or false, which seems less confusing, overall

scope="core/edit-site"
{ ...props }
/>
);
}
80 changes: 80 additions & 0 deletions packages/edit-site/src/components/sidebar/plugin-sidebar/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* WordPress dependencies
*/
import { ComplementaryArea } from '@wordpress/interface';

/**
* Renders a sidebar when activated. The contents within the `PluginSidebar` will appear as content within the sidebar.
* It also automatically renders a corresponding `PluginSidebarMenuItem` component when `isPinnable` flag is set to `true`.
* If you wish to display the sidebar, you can with use the `PluginSidebarMoreMenuItem` component or the `wp.data.dispatch` API:
*
* ```js
* wp.data.dispatch( 'core/edit-site' ).openGeneralSidebar( 'plugin-name/sidebar-name' );
* ```
*
* @see PluginSidebarMoreMenuItem
*
* @param {Object} props Element props.
* @param {string} props.name A string identifying the sidebar. Must be unique for every sidebar registered within the scope of your plugin.
* @param {string} [props.className] An optional class name added to the sidebar body.
* @param {string} props.title Title displayed at the top of the sidebar.
* @param {boolean} [props.isPinnable=true] Whether to allow to pin sidebar to the toolbar. When set to `true` it also automatically renders a corresponding menu item.
* @param {WPBlockTypeIconRender} [props.icon=inherits from the plugin] The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug string, or an SVG WP element, to be rendered when the sidebar is pinned to toolbar.
*
* @example
* ```js
* // Using ES5 syntax
* var __ = wp.i18n.__;
* var el = wp.element.createElement;
* var PanelBody = wp.components.PanelBody;
* var PluginSidebar = wp.editSite.PluginSidebar;
* var moreIcon = wp.element.createElement( 'svg' ); //... svg element.
*
* function MyPluginSidebar() {
* return el(
* PluginSidebar,
* {
* name: 'my-sidebar',
* title: 'My sidebar title',
* icon: moreIcon,
* },
* el(
* PanelBody,
* {},
* __( 'My sidebar content' )
* )
* );
* }
* ```
*
* @example
* ```jsx
* // Using ESNext syntax
* import { __ } from '@wordpress/i18n';
* import { PanelBody } from '@wordpress/components';
* import { PluginSidebar } from '@wordpress/edit-site';
* import { more } from '@wordpress/icons';
*
* const MyPluginSidebar = () => (
* <PluginSidebar
* name="my-sidebar"
* title="My sidebar title"
* icon={ more }
* >
* <PanelBody>
* { __( 'My sidebar content' ) }
* </PanelBody>
* </PluginSidebar>
* );
* ```
*/
export default function PluginSidebarEditSite( { className, ...props } ) {
return (
<ComplementaryArea
panelClassName={ className }
className="edit-site-sidebar"
scope="core/edit-site"
{ ...props }
/>
);
}
3 changes: 3 additions & 0 deletions packages/edit-site/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,6 @@ export function initialize( id, settings ) {

export { default as __experimentalMainDashboardButton } from './components/main-dashboard-button';
export { default as __experimentalNavigationToggle } from './components/navigation-sidebar/navigation-toggle';
export { default as PluginSidebar } from './components/sidebar/plugin-sidebar';
export { default as PluginSidebarMoreMenuItem } from './components/header/plugin-sidebar-more-menu-item';
export { default as PluginMoreMenuItem } from './components/header/plugin-more-menu-item';
29 changes: 29 additions & 0 deletions packages/edit-site/src/store/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { addQueryArgs, getPathAndQueryString } from '@wordpress/url';
import { __ } from '@wordpress/i18n';
import { store as noticesStore } from '@wordpress/notices';
import { store as coreStore } from '@wordpress/core-data';
import { store as interfaceStore } from '@wordpress/interface';

/**
* Internal dependencies
Expand Down Expand Up @@ -441,3 +442,31 @@ export function* revertTemplate( template ) {
);
}
}
/**
* Returns an action object used in signalling that the user opened an editor sidebar.
*
* @param {?string} name Sidebar name to be opened.
*
* @yield {Object} Action object.
*/
export function* openGeneralSidebar( name ) {
Copy link
Contributor

Choose a reason for hiding this comment

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

NB:

Does this need to be handled as a control effect because it's dispatching to a separate store?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, we use controls.dispatch to send this over to the interfaceStore as is done with the equivalent actions in the edit-post store. Or were you proposing a different approach here?

yield controls.dispatch(
interfaceStore,
'enableComplementaryArea',
editSiteStoreName,
name
);
}

/**
* Returns an action object signalling that the user closed the sidebar.
*
* @yield {Object} Action object.
*/
export function* closeGeneralSidebar() {
yield controls.dispatch(
interfaceStore,
'disableComplementaryArea',
editSiteStoreName
);
}