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

Block settings extensibility #7895

Merged
merged 6 commits into from
Aug 7, 2018
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
82 changes: 82 additions & 0 deletions edit-post/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,88 @@ Refer to [the plugins module documentation](../plugins/) for more information.
The following components can be used with the `registerPlugin` ([see documentation](../packages/plugins)) API.
They can be found in the global variable `wp.editPost` when defining `wp-edit-post` as a script dependency.

### `PluginBlockSettingsMenuItem`

Renders a new item in the block settings menu.

Example:

{% codetabs %}

{% ES5 %}
```js
var __ = wp.i18n.__;
var PluginBlockSettingsMenuItem = wp.editPost.PluginBlockSettingsMenuItem;

function doOnClick(){
// To be called when the user clicks the menu item.
}

function MyPluginBlockSettingsMenuItem() {
return el(
PluginBlockSettingsMenuItem,
{
allowedBlockNames: [ 'core/paragraph' ],
icon: 'dashicon-name',
label: __( 'Menu item text' ),
onClick: doOnClick,
}
);
}
```

{% ESNext %}
```jsx
import { __ } from wp.i18n;
import { PluginBlockSettingsMenuItem } from wp.editPost;

const doOnClick = ( ) => {
// To be called when the user clicks the menu item.
};

const MyPluginBlockSettingsMenuItem = () => (
<PluginBlockSettingsMenuItem
allowedBlockNames=[ 'core/paragraph' ]
icon='dashicon-name'
label=__( 'Menu item text' )
onClick={ doOnClick } />
);
```

{% end %}

#### Props

##### allowedBlockNames

An array containing a whitelist of block names for which the item should be shown. If this prop is not present the item will be rendered for any block. If multiple blocks are selected, it'll be shown if and only if all of them are in the whitelist.

- Type: `Array`
- Required: No
- Default: Menu item is shown for any block

##### icon

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.

- Type: `String` | `Element`
- Required: No
- Default: Menu item wil be rendered without icon

##### label

A string containing the menu item text.

- Type: `String`
- Required: Yes

##### onClick

The callback function to be executed when the user clicks the menu item.

- Type: `function`
- Required: Yes

### `PluginSidebar`

Renders a sidebar when activated. The contents within the `PluginSidebar` will appear as content within the sidebar.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* External dependencies
*/
import { isEmpty, map } from 'lodash';

/**
* WordPress dependencies
*/
import { createSlotFill } from '@wordpress/components';
import { Fragment } from '@wordpress/element';
import { withSelect } from '@wordpress/data';

const { Fill: PluginBlockSettingsMenuGroup, Slot } = createSlotFill( 'PluginBlockSettingsMenuGroup' );

const PluginBlockSettingsMenuGroupSlot = ( { fillProps, selectedBlocks } ) => {
selectedBlocks = map( selectedBlocks, ( block ) => block.name );
return (
<Slot fillProps={ { ...fillProps, selectedBlocks } } >
{ ( fills ) => ! isEmpty( fills ) && (
<Fragment>
<div className="editor-block-settings-menu__separator" />
{ fills }
</Fragment>
) }
</Slot>
);
};

PluginBlockSettingsMenuGroup.Slot = withSelect( ( select, { fillProps: { clientIds } } ) => ( {
selectedBlocks: select( 'core/editor' ).getBlocksByUID( clientIds ),
Copy link
Member

Choose a reason for hiding this comment

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

getBlocksByUID was deprecated in Gutenberg v3.3 and is slated to be removed in the next release. A warning would have been logged to your console on its use.

} ) )( PluginBlockSettingsMenuGroupSlot );

export default PluginBlockSettingsMenuGroup;
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* External dependencies
*/
import { difference } from 'lodash';

/**
* WordPress dependencies
*/
import { IconButton } from '@wordpress/components';
import { compose } from '@wordpress/element';
Copy link
Member

Choose a reason for hiding this comment

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

Similarly, wp.element.compose was deprecated in v3.3 and is slated to be removed in the next release. See #7948


/**
* Internal dependencies
*/
import PluginBlockSettingsMenuGroup from './plugin-block-settings-menu-group';

const isEverySelectedBlockAllowed = ( selected, allowed ) => difference( selected, allowed ).length === 0;

/**
* Plugins may want to add an item to the menu either for every block
* or only for the specific ones provided in the `allowedBlocks` component property.
*
* If there are multiple blocks selected the item will be rendered if every block
* is of one allowed type (not necesarily the same).
*
* @param {string[]} selectedBlockNames Array containing the names of the blocks selected
* @param {string[]} allowedBlockNames Array containing the names of the blocks allowed
* @return {boolean} Whether the item will be rendered or not.
*/
const shouldRenderItem = ( selectedBlockNames, allowedBlockNames ) => ! Array.isArray( allowedBlockNames ) ||
isEverySelectedBlockAllowed( selectedBlockNames, allowedBlockNames );

const PluginBlockSettingsMenuItem = ( { allowedBlocks, icon, label, onClick, small, role } ) => (
<PluginBlockSettingsMenuGroup>
{ ( { selectedBlocks, onClose } ) => {
if ( ! shouldRenderItem( selectedBlocks, allowedBlocks ) ) {
return null;
}
return ( <IconButton
className="editor-block-settings-menu__control"
onClick={ compose( onClick, onClose ) }
icon={ icon || 'admin-plugins' }
label={ small ? label : undefined }
role={ role }
>
{ ! small && label }
</IconButton> );
} }
</PluginBlockSettingsMenuGroup>
);

export default PluginBlockSettingsMenuItem;
5 changes: 5 additions & 0 deletions edit-post/components/visual-editor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ import {
BlockSelectionClearer,
MultiSelectScrollIntoView,
_BlockSettingsMenuFirstItem,
_BlockSettingsMenuPluginsExtension,
} from '@wordpress/editor';

/**
* Internal dependencies
*/
import './style.scss';
import BlockInspectorButton from './block-inspector-button';
import PluginBlockSettingsMenuGroup from '../block-settings-menu/plugin-block-settings-menu-group';

function VisualEditor() {
return (
Expand All @@ -34,6 +36,9 @@ function VisualEditor() {
<_BlockSettingsMenuFirstItem>
{ ( { onClose } ) => <BlockInspectorButton onClick={ onClose } role="menuitem" /> }
</_BlockSettingsMenuFirstItem>
<_BlockSettingsMenuPluginsExtension>
{ ( { clientIds, onClose } ) => <PluginBlockSettingsMenuGroup.Slot fillProps={ { clientIds, onClose } } /> }
</_BlockSettingsMenuPluginsExtension>
</BlockSelectionClearer>
);
}
Expand Down
1 change: 1 addition & 0 deletions edit-post/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export function initializeEditor( id, postType, postId, settings, overridePost )
};
}

export { default as PluginBlockSettingsMenuItem } from './components/block-settings-menu/plugin-block-settings-menu-item';
export { default as PluginPostPublishPanel } from './components/sidebar/plugin-post-publish-panel';
export { default as PluginPostStatusInfo } from './components/sidebar/plugin-post-status-info';
export { default as PluginPrePublishPanel } from './components/sidebar/plugin-pre-publish-panel';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* WordPress dependencies
*/
import { createSlotFill } from '@wordpress/components';

const { Fill: _BlockSettingsMenuPluginsExtension, Slot } = createSlotFill( '_BlockSettingsMenuPluginsExtension' );

_BlockSettingsMenuPluginsExtension.Slot = Slot;

export default _BlockSettingsMenuPluginsExtension;
2 changes: 2 additions & 0 deletions packages/editor/src/components/block-settings-menu/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import ReusableBlockDeleteButton from './reusable-block-delete-button';
import BlockHTMLConvertButton from './block-html-convert-button';
import BlockUnknownConvertButton from './block-unknown-convert-button';
import _BlockSettingsMenuFirstItem from './block-settings-menu-first-item';
import _BlockSettingsMenuPluginsExtension from './block-settings-menu-plugins-extension';
import withDeprecatedUniqueId from '../with-deprecated-unique-id';

export class BlockSettingsMenu extends Component {
Expand Down Expand Up @@ -126,6 +127,7 @@ export class BlockSettingsMenu extends Component {
itemsRole="menuitem"
/>
) }
<_BlockSettingsMenuPluginsExtension.Slot fillProps={ { clientIds, onClose } } />
<div className="editor-block-settings-menu__separator" />
{ count === 1 && (
<ReusableBlockDeleteButton
Expand Down
1 change: 1 addition & 0 deletions packages/editor/src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export { default as BlockMover } from './block-mover';
export { default as BlockSelectionClearer } from './block-selection-clearer';
export { default as BlockSettingsMenu } from './block-settings-menu';
export { default as _BlockSettingsMenuFirstItem } from './block-settings-menu/block-settings-menu-first-item';
export { default as _BlockSettingsMenuPluginsExtension } from './block-settings-menu/block-settings-menu-plugins-extension';
export { default as BlockTitle } from './block-title';
export { default as BlockToolbar } from './block-toolbar';
export { default as CopyHandler } from './copy-handler';
Expand Down