diff --git a/blocks/hooks/anchor.js b/blocks/hooks/anchor.js
index 65371a4088c433..a61b194f144a6c 100644
--- a/blocks/hooks/anchor.js
+++ b/blocks/hooks/anchor.js
@@ -6,7 +6,7 @@ import { assign } from 'lodash';
/**
* WordPress dependencies
*/
-import { createHigherOrderComponent } from '@wordpress/element';
+import { createHigherOrderComponent, Fragment } from '@wordpress/element';
import { addFilter } from '@wordpress/hooks';
import { TextControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
@@ -15,7 +15,7 @@ import { __ } from '@wordpress/i18n';
* Internal dependencies
*/
import { hasBlockSupport } from '../api';
-import InspectorControls from '../inspector-controls';
+import InspectorAdvancedControls from '../inspector-advanced-controls';
/**
* Regular expression matching invalid anchor characters for replacement.
@@ -58,22 +58,29 @@ export function addAttribute( settings ) {
*/
export const withInspectorControl = createHigherOrderComponent( ( BlockEdit ) => {
return ( props ) => {
- const hasAnchor = hasBlockSupport( props.name, 'anchor' ) && props.isSelected;
- return [
- ,
- hasAnchor &&
- {
- nextValue = nextValue.replace( ANCHOR_REGEX, '-' );
- props.setAttributes( {
- anchor: nextValue,
- } );
- } } />
- ,
- ];
+ const hasAnchor = hasBlockSupport( props.name, 'anchor' );
+
+ if ( hasAnchor && props.isSelected ) {
+ return (
+
+
+
+ {
+ nextValue = nextValue.replace( ANCHOR_REGEX, '-' );
+ props.setAttributes( {
+ anchor: nextValue,
+ } );
+ } } />
+
+
+ );
+ }
+
+ return ;
};
}, 'withInspectorControl' );
diff --git a/blocks/hooks/custom-class-name.js b/blocks/hooks/custom-class-name.js
index a8a8fa07591e76..bd58312857fcbc 100644
--- a/blocks/hooks/custom-class-name.js
+++ b/blocks/hooks/custom-class-name.js
@@ -7,7 +7,7 @@ import classnames from 'classnames';
/**
* WordPress dependencies
*/
-import { createHigherOrderComponent } from '@wordpress/element';
+import { createHigherOrderComponent, Fragment } from '@wordpress/element';
import { addFilter } from '@wordpress/hooks';
import { TextControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
@@ -16,7 +16,7 @@ import { __ } from '@wordpress/i18n';
* Internal dependencies
*/
import { hasBlockSupport } from '../api';
-import InspectorControls from '../inspector-controls';
+import InspectorAdvancedControls from '../inspector-advanced-controls';
/**
* Filters registered block settings, extending attributes with anchor using ID
@@ -49,22 +49,28 @@ export function addAttribute( settings ) {
*/
export const withInspectorControl = createHigherOrderComponent( ( BlockEdit ) => {
return ( props ) => {
- const hasCustomClassName = hasBlockSupport( props.name, 'customClassName', true ) && props.isSelected;
+ const hasCustomClassName = hasBlockSupport( props.name, 'customClassName', true );
- return [
- ,
- hasCustomClassName &&
- {
- props.setAttributes( {
- className: nextValue,
- } );
- } }
- />
- ,
- ];
+ if ( hasCustomClassName && props.isSelected ) {
+ return (
+
+
+
+ {
+ props.setAttributes( {
+ className: nextValue,
+ } );
+ } }
+ />
+
+
+ );
+ }
+
+ return ;
};
}, 'withInspectorControl' );
diff --git a/blocks/index.js b/blocks/index.js
index c157fc834c928f..9adcc900ddb508 100644
--- a/blocks/index.js
+++ b/blocks/index.js
@@ -24,6 +24,7 @@ export { default as Editable } from './rich-text/editable';
export { default as ImagePlaceholder } from './image-placeholder';
export { default as InnerBlocks } from './inner-blocks';
export { default as InspectorControls } from './inspector-controls';
+export { default as InspectorAdvancedControls } from './inspector-advanced-controls';
export { default as PlainText } from './plain-text';
export { default as MediaUpload } from './media-upload';
export { default as RichText } from './rich-text';
diff --git a/blocks/inspector-advanced-controls/index.js b/blocks/inspector-advanced-controls/index.js
new file mode 100644
index 00000000000000..b725f38ee72bf4
--- /dev/null
+++ b/blocks/inspector-advanced-controls/index.js
@@ -0,0 +1,6 @@
+/**
+ * WordPress dependencies
+ */
+import { createSlotFill } from '@wordpress/components';
+
+export default createSlotFill( 'InspectorAdvancedControls' );
diff --git a/blocks/inspector-controls/index.js b/blocks/inspector-controls/index.js
index 05c1dea42d5592..4a91e43d798fee 100644
--- a/blocks/inspector-controls/index.js
+++ b/blocks/inspector-controls/index.js
@@ -1,12 +1,6 @@
/**
* WordPress dependencies
*/
-import { Fill } from '@wordpress/components';
+import { createSlotFill } from '@wordpress/components';
-export default function InspectorControls( { children } ) {
- return (
-
- { children }
-
- );
-}
+export default createSlotFill( 'InspectorControls' );
diff --git a/components/index.js b/components/index.js
index 789c92124067d5..c50b14168b770f 100644
--- a/components/index.js
+++ b/components/index.js
@@ -49,7 +49,7 @@ export { default as ToggleControl } from './toggle-control';
export { default as Toolbar } from './toolbar';
export { default as Tooltip } from './tooltip';
export { default as TreeSelect } from './tree-select';
-export { Slot, Fill, Provider as SlotFillProvider } from './slot-fill';
+export { createSlotFill, Slot, Fill, Provider as SlotFillProvider } from './slot-fill';
// Higher-Order Components
export { default as ifCondition } from './higher-order/if-condition';
diff --git a/components/slot-fill/README.md b/components/slot-fill/README.md
index 9584b47d493d6b..45d4bd24491f88 100644
--- a/components/slot-fill/README.md
+++ b/components/slot-fill/README.md
@@ -42,13 +42,48 @@ Any Fill will automatically occupy this Slot space, even if rendered elsewhere i
You can either use the Fill component directly, or a wrapper component type as in the above example to abstract the slot name from consumer awareness.
+There is also `createSlotFill` helper method which was created to simplify the process of matching the corresponding `Slot` and `Fill` components:
+
+```jsx
+const Toolbar = createSlotFill( 'Toolbar' );
+
+const MyToolbarItem = () => (
+
+ My item
+
+);
+
+const MyToolbar = () => (
+
+
+
+);
+```
+
## Props
The `SlotFillProvider` component does not accept any props.
Both `Slot` and `Fill` accept a `name` string prop, where a `Slot` with a given `name` will render the `children` of any associated `Fill`s.
-`Slot` also accepts a `bubblesVirtually` prop which changes the event bubbling behaviour:
+`Slot` accepts a `bubblesVirtually` prop which changes the event bubbling behaviour:
- By default, events will bubble to their parents on the DOM hierarchy (native event bubbling)
- If `bubblesVirtually` is set to true, events will bubble to their virtual parent in the React elements hierarchy instead.
+
+`Slot` also accepts optional `children` function prop, which takes `fills` as a param. It allows to perform additional processing and wrap `fills` conditionally.
+
+_Example_:
+```jsx
+const Toolbar = ( { isMobile } ) => (
+
+
+ { ( fills ) => {
+ return isMobile && fills.length > 3 ?
+ { fills }
:
+ fills;
+ } }
+
+
+);
+```
diff --git a/components/slot-fill/index.js b/components/slot-fill/index.js
index 6f4e9f2141e5be..c81d29eafd2e3a 100644
--- a/components/slot-fill/index.js
+++ b/components/slot-fill/index.js
@@ -9,4 +9,19 @@ export { Slot };
export { Fill };
export { Provider };
-export default { Slot, Fill, Provider };
+export function createSlotFill( name ) {
+ const Component = ( { children, ...props } ) => (
+
+ { children }
+
+ );
+ Component.displayName = name;
+
+ Component.Slot = ( { children, ...props } ) => (
+
+ { children }
+
+ );
+
+ return Component;
+}
diff --git a/components/slot-fill/slot.js b/components/slot-fill/slot.js
index 60df7a4987344d..4785e6e0837d1a 100644
--- a/components/slot-fill/slot.js
+++ b/components/slot-fill/slot.js
@@ -45,32 +45,34 @@ class Slot extends Component {
}
render() {
- const { name, bubblesVirtually = false, fillProps = {} } = this.props;
+ const { children, name, bubblesVirtually = false, fillProps = {} } = this.props;
const { getFills = noop } = this.context;
if ( bubblesVirtually ) {
return ;
}
+ const fills = map( getFills( name ), ( fill ) => {
+ const fillKey = fill.occurrence;
+
+ // If a function is passed as a child, render it with the fillProps.
+ if ( isFunction( fill.props.children ) ) {
+ return cloneElement( fill.props.children( fillProps ), { key: fillKey } );
+ }
+
+ return Children.map( fill.props.children, ( child, childIndex ) => {
+ if ( ! child || isString( child ) ) {
+ return child;
+ }
+
+ const childKey = `${ fillKey }---${ child.key || childIndex }`;
+ return cloneElement( child, { key: childKey } );
+ } );
+ } );
+
return (
- { map( getFills( name ), ( fill ) => {
- const fillKey = fill.occurrence;
-
- // If a function is passed as a child, render it with the fillProps.
- if ( isFunction( fill.props.children ) ) {
- return cloneElement( fill.props.children( fillProps ), { key: fillKey } );
- }
-
- return Children.map( fill.props.children, ( child, childIndex ) => {
- if ( ! child || isString( child ) ) {
- return child;
- }
-
- const childKey = `${ fillKey }---${ child.key || childIndex }`;
- return cloneElement( child, { key: childKey } );
- } );
- } ) }
+ { isFunction( children ) ? children( fills.filter( Boolean ) ) : fills }
);
}
diff --git a/components/slot-fill/test/index.js b/components/slot-fill/test/index.js
new file mode 100644
index 00000000000000..041c8337a34b7a
--- /dev/null
+++ b/components/slot-fill/test/index.js
@@ -0,0 +1,28 @@
+/**
+ * External dependecies
+ */
+import { shallow } from 'enzyme';
+
+/**
+ * Internal dependencies
+ */
+import { createSlotFill, Fill, Slot } from '../';
+
+describe( 'createSlotFill', () => {
+ const SLOT_NAME = 'MyFill';
+ const MyFill = createSlotFill( SLOT_NAME );
+
+ test( 'should match snapshot for Fill', () => {
+ const wrapper = shallow( );
+
+ expect( wrapper.type() ).toBe( Fill );
+ expect( wrapper ).toHaveProp( 'name', SLOT_NAME );
+ } );
+
+ test( 'should match snapshot for Slot', () => {
+ const wrapper = shallow( );
+
+ expect( wrapper.type() ).toBe( Slot );
+ expect( wrapper ).toHaveProp( 'name', SLOT_NAME );
+ } );
+} );
diff --git a/components/slot-fill/test/slot.js b/components/slot-fill/test/slot.js
index 0fe7112f22bf91..3d84e9d5f0289b 100644
--- a/components/slot-fill/test/slot.js
+++ b/components/slot-fill/test/slot.js
@@ -2,6 +2,7 @@
* External dependencies
*/
import { mount } from 'enzyme';
+import { isEmpty } from 'lodash';
/**
* Internal dependencies
@@ -103,6 +104,42 @@ describe( 'Slot', () => {
expect( onClose ).toHaveBeenCalledTimes( 1 );
} );
+ it( 'should render empty Fills without HTML wrapper when render props used', () => {
+ const element = mount(
+
+
+ { ( fills ) => ( ! isEmpty( fills ) && (
+
+ { fills }
+
+ ) ) }
+
+
+
+ );
+
+ expect( element.find( 'Slot > div' ).html() ).toBe( '' );
+ } );
+
+ it( 'should render a string Fill with HTML wrapper when render props used', () => {
+ const element = mount(
+
+
+ { ( fills ) => ( fills && (
+
+ { fills }
+
+ ) ) }
+
+
+ content
+
+
+ );
+
+ expect( element.find( 'Slot > div' ).html() ).toBe( '' );
+ } );
+
it( 'should re-render Slot when not bubbling virtually', () => {
const element = mount(
diff --git a/edit-post/components/sidebar/block-inspector-panel/style.scss b/edit-post/components/sidebar/block-inspector-panel/style.scss
index 636e0721425299..3e59bcb43fe535 100644
--- a/edit-post/components/sidebar/block-inspector-panel/style.scss
+++ b/edit-post/components/sidebar/block-inspector-panel/style.scss
@@ -9,4 +9,9 @@
.components-panel__body-toggle {
color: $dark-gray-500;
}
+
+ &.editor-block-inspector__advanced {
+ border-top: 1px solid $light-gray-500;
+ margin-bottom: -16px;
+ }
}
diff --git a/editor/components/block-inspector/index.js b/editor/components/block-inspector/index.js
index 6d9b0545430776..940311a2ee15ed 100644
--- a/editor/components/block-inspector/index.js
+++ b/editor/components/block-inspector/index.js
@@ -1,14 +1,20 @@
/**
* External dependencies
*/
+import { isEmpty } from 'lodash';
import { connect } from 'react-redux';
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
-import { Slot } from '@wordpress/components';
-import { getBlockType, BlockIcon } from '@wordpress/blocks';
+import {
+ BlockIcon,
+ getBlockType,
+ InspectorControls,
+ InspectorAdvancedControls,
+} from '@wordpress/blocks';
+import { PanelBody } from '@wordpress/components';
/**
* Internal Dependencies
@@ -37,7 +43,17 @@ const BlockInspector = ( { selectedBlock, count } ) => {
{ blockType.description }
,
- ,
+ ,
+
+ { ( fills ) => ! isEmpty( fills ) && (
+
+ { fills }
+
+ ) }
+ ,
];
};