From 795fe998039d8c1e6519a1bdff80d57eef53b428 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Mon, 31 May 2021 19:16:04 +1000
Subject: [PATCH 01/50] Add utils to spacing supports to check or reset values
---
packages/block-editor/src/hooks/margin.js | 33 ++++++++++++++++++++++
packages/block-editor/src/hooks/padding.js | 33 ++++++++++++++++++++++
2 files changed, 66 insertions(+)
diff --git a/packages/block-editor/src/hooks/margin.js b/packages/block-editor/src/hooks/margin.js
index c0cb5c5d4ef5d..db9c6cb7ea5fa 100644
--- a/packages/block-editor/src/hooks/margin.js
+++ b/packages/block-editor/src/hooks/margin.js
@@ -28,6 +28,38 @@ export function hasMarginSupport( blockType ) {
return !! ( true === support || support?.margin );
}
+/**
+ * Checks if there is a current value in the margin block support attributes.
+ *
+ * @param {Object} props Block props.
+ * @return {boolean} Whether or not the block has a margin value set.
+ */
+export function hasMarginValue( props ) {
+ return props.attributes.style?.spacing?.margin !== undefined;
+}
+
+/**
+ * Resets the margin block support attributes. This can be used when disabling
+ * the margin support controls for a block via a progressive discovery panel.
+ *
+ * @param {Object} props Block props.
+ * @param {Object} props.attributes Block's attributes.
+ * @param {Object} props.setAttributes Function to set block's attributes.
+ */
+export function resetMargin( { attributes = {}, setAttributes } ) {
+ const { style } = attributes;
+
+ setAttributes( {
+ style: {
+ ...style,
+ spacing: {
+ ...style?.spacing,
+ margin: undefined,
+ },
+ },
+ } );
+}
+
/**
* Custom hook that checks if margin settings have been disabled.
*
@@ -106,6 +138,7 @@ export function MarginEdit( props ) {
label={ __( 'Margin' ) }
sides={ sides }
units={ units }
+ allowReset={ false }
/>
>
),
diff --git a/packages/block-editor/src/hooks/padding.js b/packages/block-editor/src/hooks/padding.js
index 6a29ad7fd7f00..b3fd9bb79d87d 100644
--- a/packages/block-editor/src/hooks/padding.js
+++ b/packages/block-editor/src/hooks/padding.js
@@ -28,6 +28,38 @@ export function hasPaddingSupport( blockType ) {
return !! ( true === support || support?.padding );
}
+/**
+ * Checks if there is a current value in the padding block support attributes.
+ *
+ * @param {Object} props Block props.
+ * @return {boolean} Whether or not the block has a padding value set.
+ */
+export function hasPaddingValue( props ) {
+ return props.attributes.style?.spacing?.padding !== undefined;
+}
+
+/**
+ * Resets the padding block support attributes. This can be used when disabling
+ * the padding support controls for a block via a progressive discovery panel.
+ *
+ * @param {Object} props Block props.
+ * @param {Object} props.attributes Block's attributes.
+ * @param {Object} props.setAttributes Function to set block's attributes.
+ */
+export function resetPadding( { attributes = {}, setAttributes } ) {
+ const { style } = attributes;
+
+ setAttributes( {
+ style: {
+ ...style,
+ spacing: {
+ ...style?.spacing,
+ padding: undefined,
+ },
+ },
+ } );
+}
+
/**
* Custom hook that checks if padding settings have been disabled.
*
@@ -106,6 +138,7 @@ export function PaddingEdit( props ) {
label={ __( 'Padding' ) }
sides={ sides }
units={ units }
+ allowReset={ false }
/>
>
),
From dfb1403e24d6108b7d77e08f720a9ea05fd5581f Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Mon, 31 May 2021 19:20:34 +1000
Subject: [PATCH 02/50] Draft block support panel
Adds a new component that handles progressive display of block support controls within an inspector controls panel. It also triggers resetting block support attributes when controls are toggled on/off.
---
docs/manifest.json | 6 ++
.../src/block-support-panel/README.md | 1 +
.../src/block-support-panel/index.js | 93 +++++++++++++++++++
.../src/block-support-panel/stories/index.js | 1 +
.../src/block-support-panel/style.scss | 45 +++++++++
.../src/block-support-panel/test/index.js | 1 +
.../src/block-support-panel/title.js | 64 +++++++++++++
packages/components/src/index.js | 1 +
packages/components/src/style.scss | 1 +
9 files changed, 213 insertions(+)
create mode 100644 packages/components/src/block-support-panel/README.md
create mode 100644 packages/components/src/block-support-panel/index.js
create mode 100644 packages/components/src/block-support-panel/stories/index.js
create mode 100644 packages/components/src/block-support-panel/style.scss
create mode 100644 packages/components/src/block-support-panel/test/index.js
create mode 100644 packages/components/src/block-support-panel/title.js
diff --git a/docs/manifest.json b/docs/manifest.json
index 654c6037674e9..ef6392b9be2b2 100644
--- a/docs/manifest.json
+++ b/docs/manifest.json
@@ -695,6 +695,12 @@
"markdown_source": "../packages/components/src/base-field/README.md",
"parent": "components"
},
+ {
+ "title": "BlockSupportPanel",
+ "slug": "block-support-panel",
+ "markdown_source": "../packages/components/src/block-support-panel/README.md",
+ "parent": "components"
+ },
{
"title": "BoxControl",
"slug": "box-control",
diff --git a/packages/components/src/block-support-panel/README.md b/packages/components/src/block-support-panel/README.md
new file mode 100644
index 0000000000000..75c21b40a4b37
--- /dev/null
+++ b/packages/components/src/block-support-panel/README.md
@@ -0,0 +1 @@
+
diff --git a/packages/components/src/block-support-panel/index.js b/packages/components/src/block-support-panel/index.js
new file mode 100644
index 0000000000000..69e109c0882ad
--- /dev/null
+++ b/packages/components/src/block-support-panel/index.js
@@ -0,0 +1,93 @@
+/**
+ * External dependencies
+ */
+import classnames from 'classnames';
+
+/**
+ * WordPress dependencies
+ */
+import { useEffect, useState } from '@wordpress/element';
+
+/**
+ * Internal dependencies
+ */
+import BlockSupportPanelTitle from './title';
+
+const BlockSupportPanel = ( props ) => {
+ const { children, className, label: menuLabel, resetAll, title } = props;
+ const [ menuItems, setMenuItems ] = useState( {} );
+ const classes = classnames( 'components-block-support-panel', className );
+
+ // Collect data to manage control visibility via the panel's dropdown menu.
+ useEffect( () => {
+ const items = {};
+
+ children.forEach( ( child ) => {
+ items[ child.props.label ] = child.props.hasValue( child.props );
+ } );
+
+ setMenuItems( items );
+ }, [] );
+
+ const getControlByMenuLabel = ( label ) => {
+ return children.find( ( child ) => child.props.label === label );
+ };
+
+ // Toggles display of a block support control resetting the attributes if
+ // being turned off.
+ const toggleControl = ( label ) => {
+ const isSelected = menuItems[ label ];
+
+ if ( isSelected ) {
+ const control = getControlByMenuLabel( label );
+ control.props.reset( control.props );
+ }
+
+ setMenuItems( {
+ ...menuItems,
+ [ label ]: ! isSelected,
+ } );
+ };
+
+ // Resets all block support attributes for controls represented by the
+ // menu items. Then turns off their display.
+ const resetAllControls = () => {
+ // Reset the block support attributes.
+ resetAll();
+
+ // Turn off all the controls in menu.
+ const resetMenuItems = {};
+
+ children.forEach( ( child ) => {
+ resetMenuItems[ child.props.label ] = false;
+ } );
+
+ setMenuItems( resetMenuItems );
+ };
+
+ return (
+
+
+ { children.map( ( child ) => {
+ const { label, hasValue } = child.props;
+
+ // Only display the block support controls if the support
+ // attributes have a value or the controls have be chosen for
+ // display by the user.
+ if ( menuItems[ label ] || hasValue( child.props ) ) {
+ return child;
+ }
+
+ return null;
+ } ) }
+
+ );
+};
+
+export default BlockSupportPanel;
diff --git a/packages/components/src/block-support-panel/stories/index.js b/packages/components/src/block-support-panel/stories/index.js
new file mode 100644
index 0000000000000..06daf7b01ae57
--- /dev/null
+++ b/packages/components/src/block-support-panel/stories/index.js
@@ -0,0 +1 @@
+// TODO: Add Storybook entries for block support panel.
diff --git a/packages/components/src/block-support-panel/style.scss b/packages/components/src/block-support-panel/style.scss
new file mode 100644
index 0000000000000..6644751aecc8e
--- /dev/null
+++ b/packages/components/src/block-support-panel/style.scss
@@ -0,0 +1,45 @@
+.components-block-support-panel {
+ border: none;
+ border-top: $border-width solid $gray-200;
+ padding: $grid-unit-20;
+
+ & + .components-block-support-panel {
+ margin-top: -1px;
+ }
+
+ .components-block-support-panel__title {
+ margin: 0;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ font-weight: 500;
+ line-height: normal;
+
+ .components-dropdown-menu {
+ height: $grid-unit-30;
+ margin-top: -4px;
+ margin-bottom: -4px;
+ }
+
+ .components-dropdown-menu__toggle {
+ padding: 0;
+ min-width: $grid-unit-30;
+ width: $grid-unit-30;
+ height: $grid-unit-30;
+ }
+
+ &:not(:last-child) {
+ padding-bottom: $grid-unit-30;
+ }
+ }
+
+ > div {
+ padding-bottom: 0;
+ margin-bottom: $grid-unit-30;
+ max-width: 100%;
+
+ &:last-child {
+ margin-bottom: $grid-unit-10;
+ }
+ }
+}
diff --git a/packages/components/src/block-support-panel/test/index.js b/packages/components/src/block-support-panel/test/index.js
new file mode 100644
index 0000000000000..b3f1c4ff19eb8
--- /dev/null
+++ b/packages/components/src/block-support-panel/test/index.js
@@ -0,0 +1 @@
+// TODO: Add automatic tests for block support panel.
diff --git a/packages/components/src/block-support-panel/title.js b/packages/components/src/block-support-panel/title.js
new file mode 100644
index 0000000000000..8383dc10897b7
--- /dev/null
+++ b/packages/components/src/block-support-panel/title.js
@@ -0,0 +1,64 @@
+/**
+ * WordPress dependencies
+ */
+import { check, moreHorizontal } from '@wordpress/icons';
+import { __ } from '@wordpress/i18n';
+
+/**
+ * Internal dependencies
+ */
+import MenuGroup from '../menu-group';
+import MenuItem from '../menu-item';
+import DropdownMenu from '../dropdown-menu';
+
+const BlockSupportPanelTitle = ( props ) => {
+ const { menuItems = {}, menuLabel, resetAll, title, toggleControl } = props;
+
+ if ( ! title ) {
+ return null;
+ }
+
+ return (
+
+ { title }
+
+ { ( { onClose } ) => (
+ <>
+
+ { Object.entries( menuItems ).map(
+ ( [ label, isSelected ] ) => {
+ return (
+
+ );
+ }
+ ) }
+
+
+
+
+ >
+ ) }
+
+
+ );
+};
+
+export default BlockSupportPanelTitle;
diff --git a/packages/components/src/index.js b/packages/components/src/index.js
index 86d40d1db38fb..8c2d93f252ec0 100644
--- a/packages/components/src/index.js
+++ b/packages/components/src/index.js
@@ -22,6 +22,7 @@ export {
useAutocompleteProps as __unstableUseAutocompleteProps,
} from './autocomplete';
export { default as BaseControl } from './base-control';
+export { default as __experimentalBlockSupportPanel } from './block-support-panel';
export { default as __experimentalBoxControl } from './box-control';
export { default as Button } from './button';
export { default as ButtonGroup } from './button-group';
diff --git a/packages/components/src/style.scss b/packages/components/src/style.scss
index 22ae2fb477995..1d1d252ef27bc 100644
--- a/packages/components/src/style.scss
+++ b/packages/components/src/style.scss
@@ -1,5 +1,6 @@
@import "./animate/style.scss";
@import "./autocomplete/style.scss";
+@import "./block-support-panel/style.scss";
@import "./button-group/style.scss";
@import "./button/style.scss";
@import "./checkbox-control/style.scss";
From 0cb9f82fcdf935b297b080cb83ff197a3405ceec Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Mon, 31 May 2021 19:23:01 +1000
Subject: [PATCH 03/50] Update spacing hook to use new block support panel
In addition to using the new block support panel with progressive display of controls, the panel has been renamed Dimensions with the view that future block support for height and width will be added within this panel.
---
packages/block-editor/src/hooks/spacing.js | 53 +++++++++++++++++++---
1 file changed, 46 insertions(+), 7 deletions(-)
diff --git a/packages/block-editor/src/hooks/spacing.js b/packages/block-editor/src/hooks/spacing.js
index f356a211248f7..2127739ac7587 100644
--- a/packages/block-editor/src/hooks/spacing.js
+++ b/packages/block-editor/src/hooks/spacing.js
@@ -1,7 +1,7 @@
/**
* WordPress dependencies
*/
-import { PanelBody } from '@wordpress/components';
+import { __experimentalBlockSupportPanel as BlockSupportPanel } from '@wordpress/components';
import { Platform } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { getBlockSupport } from '@wordpress/blocks';
@@ -10,10 +10,18 @@ import { getBlockSupport } from '@wordpress/blocks';
* Internal dependencies
*/
import InspectorControls from '../components/inspector-controls';
-import { MarginEdit, hasMarginSupport, useIsMarginDisabled } from './margin';
+import {
+ MarginEdit,
+ hasMarginSupport,
+ hasMarginValue,
+ resetMargin,
+ useIsMarginDisabled,
+} from './margin';
import {
PaddingEdit,
hasPaddingSupport,
+ hasPaddingValue,
+ resetPadding,
useIsPaddingDisabled,
} from './padding';
@@ -34,12 +42,43 @@ export function SpacingPanel( props ) {
return null;
}
+ // Callback to reset all block support attributes controlled via this panel.
+ const resetAll = () => {
+ const { style } = props.attributes;
+
+ props.setAttributes( {
+ style: {
+ ...style,
+ spacing: {
+ ...style?.spacing,
+ margin: undefined,
+ padding: undefined,
+ },
+ },
+ } );
+ };
+
return (
-
-
-
-
-
+
+
+
+
+
);
}
From 1b23faddfa85b730db06d35db5263172f42cf09b Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Tue, 1 Jun 2021 11:33:44 +1000
Subject: [PATCH 04/50] Update block support panel to handle false children
---
.../components/src/block-support-panel/index.js | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/packages/components/src/block-support-panel/index.js b/packages/components/src/block-support-panel/index.js
index 69e109c0882ad..da58dbc07b008 100644
--- a/packages/components/src/block-support-panel/index.js
+++ b/packages/components/src/block-support-panel/index.js
@@ -18,11 +18,16 @@ const BlockSupportPanel = ( props ) => {
const [ menuItems, setMenuItems ] = useState( {} );
const classes = classnames( 'components-block-support-panel', className );
+ // If a block support UI has been disabled via theme.json a boolean `false`
+ // will be passed as a child. This panel is only interested in the children
+ // to be displayed.
+ const filteredChildren = children.filter( Boolean );
+
// Collect data to manage control visibility via the panel's dropdown menu.
useEffect( () => {
const items = {};
- children.forEach( ( child ) => {
+ filteredChildren.forEach( ( child ) => {
items[ child.props.label ] = child.props.hasValue( child.props );
} );
@@ -30,7 +35,9 @@ const BlockSupportPanel = ( props ) => {
}, [] );
const getControlByMenuLabel = ( label ) => {
- return children.find( ( child ) => child.props.label === label );
+ return filteredChildren.find(
+ ( child ) => child.props.label === label
+ );
};
// Toggles display of a block support control resetting the attributes if
@@ -58,7 +65,7 @@ const BlockSupportPanel = ( props ) => {
// Turn off all the controls in menu.
const resetMenuItems = {};
- children.forEach( ( child ) => {
+ filteredChildren.forEach( ( child ) => {
resetMenuItems[ child.props.label ] = false;
} );
@@ -74,8 +81,8 @@ const BlockSupportPanel = ( props ) => {
toggleControl={ toggleControl }
resetAll={ resetAllControls }
/>
- { children.map( ( child ) => {
- const { label, hasValue } = child.props;
+ { filteredChildren.map( ( child ) => {
+ const { label, hasValue } = child?.props || {};
// Only display the block support controls if the support
// attributes have a value or the controls have be chosen for
From 2487805cf70bfef064489a4d4278611c362493cc Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Tue, 1 Jun 2021 11:35:17 +1000
Subject: [PATCH 05/50] Conditionally display spacing block support controls
This is to prevent the block support feature appearing within the new block support panel's menu.
---
packages/block-editor/src/hooks/spacing.js | 31 +++++++++++++---------
1 file changed, 18 insertions(+), 13 deletions(-)
diff --git a/packages/block-editor/src/hooks/spacing.js b/packages/block-editor/src/hooks/spacing.js
index 2127739ac7587..4d557ab463044 100644
--- a/packages/block-editor/src/hooks/spacing.js
+++ b/packages/block-editor/src/hooks/spacing.js
@@ -35,6 +35,8 @@ export const SPACING_SUPPORT_KEY = 'spacing';
* @return {WPElement} Inspector controls for spacing support features.
*/
export function SpacingPanel( props ) {
+ const isPaddingDisabled = useIsPaddingDisabled( props );
+ const isMarginDisabled = useIsMarginDisabled( props );
const isDisabled = useIsSpacingDisabled( props );
const isSupported = hasSpacingSupport( props.name );
@@ -65,19 +67,22 @@ export function SpacingPanel( props ) {
title={ __( 'Dimensions' ) }
resetAll={ resetAll }
>
-
-
+ { ! isPaddingDisabled && (
+
+ ) }
+ { ! isMarginDisabled && (
+
+ ) }
);
From 696070f6389aa4adfecebc4f6cd877a569562163 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Tue, 1 Jun 2021 13:03:18 +1000
Subject: [PATCH 06/50] Draft README for BlockSupportPanel
This will need updating with uploaded screenshots.
---
.../src/block-support-panel/README.md | 101 +++++++++++++++++-
1 file changed, 100 insertions(+), 1 deletion(-)
diff --git a/packages/components/src/block-support-panel/README.md b/packages/components/src/block-support-panel/README.md
index 75c21b40a4b37..63fca61e6d29f 100644
--- a/packages/components/src/block-support-panel/README.md
+++ b/packages/components/src/block-support-panel/README.md
@@ -1 +1,100 @@
-
+# Block Support Panel
+
+These panels provide progressive discovery options for multiple controls
+provided via block supports.
+
+## Development guidelines
+
+The `BlockSupportPanel` creates a container with a header including a dropdown
+menu. The menu is generated automatically from the panel's children. Each menu
+item allows for the display of the corresponding child control to be toggled
+on or off. When toggled off the control's reset callback is fired allowing for
+the block support provided attribtues to reset.
+
+Whether a child control is initially displayed or not is dependent upon
+whether there has previously been a value set. This is checked via the
+`hasValue` function provided through the child's props.
+
+### Usage
+
+```jsx
+import { __experimentalBlockSupportPanel as BlockSupportPanel } from '@wordpress/components';
+import {
+ PaddingEdit,
+ hasPaddingValue,
+ resetPadding,
+ useIsPaddingDisabled,
+} from './padding';
+
+
+export function DimensionPanel( props ) {
+ const isPaddingDisabled = useIsPaddingDisabled( props );
+
+ const resetAll = () => {
+ // Reset attributes for all block support features in this panel.
+ };
+
+ return (
+
+ { ! isPaddingDisabled && (
+
+ ) }
+
+ );
+}
+```
+
+### Sub-Components
+
+#### BlockSupportPanelTitle
+
+This is a simple component to display the panel title and house the dropdown
+menu for toggling control display. It is used by the `BlockSupportPanel`
+component under the hood, so it does not typically need to be used.
+
+##### Props
+###### resetAll
+
+A function to call when the `Reset all` menu option is selected.
+
+- Type: `function`
+- Required: Yes
+
+###### toggleControl
+
+Callback used to toggle display of an individual block support control and reset
+its value if being turned off.
+
+- Type: `function`
+- Required: Yes
+
+###### menuItems
+
+This object represents the child controls and their visibility state. It
+is built by the parent panel using its children.
+
+- Type: `Object`
+- Required: No
+
+###### menuLabel
+
+A label for the dropdown menu.
+
+- Type: `String`
+- Required: No
+
+###### title
+
+The panel title to display.
+
+- Type: `String`
+- Required: No
From 03c87c49c7f8362746320a18b2a948f1fc6027bd Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Tue, 1 Jun 2021 17:27:06 +1000
Subject: [PATCH 07/50] Make block supports panel handle filtered children
---
packages/components/src/block-support-panel/index.js | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/packages/components/src/block-support-panel/index.js b/packages/components/src/block-support-panel/index.js
index da58dbc07b008..a66742503b222 100644
--- a/packages/components/src/block-support-panel/index.js
+++ b/packages/components/src/block-support-panel/index.js
@@ -16,12 +16,13 @@ import BlockSupportPanelTitle from './title';
const BlockSupportPanel = ( props ) => {
const { children, className, label: menuLabel, resetAll, title } = props;
const [ menuItems, setMenuItems ] = useState( {} );
- const classes = classnames( 'components-block-support-panel', className );
// If a block support UI has been disabled via theme.json a boolean `false`
// will be passed as a child. This panel is only interested in the children
// to be displayed.
- const filteredChildren = children.filter( Boolean );
+ const filteredChildren = Array.isArray( children )
+ ? children.filter( Boolean )
+ : [];
// Collect data to manage control visibility via the panel's dropdown menu.
useEffect( () => {
@@ -34,6 +35,10 @@ const BlockSupportPanel = ( props ) => {
setMenuItems( items );
}, [] );
+ if ( filteredChildren.length === 0 ) {
+ return null;
+ }
+
const getControlByMenuLabel = ( label ) => {
return filteredChildren.find(
( child ) => child.props.label === label
@@ -72,6 +77,8 @@ const BlockSupportPanel = ( props ) => {
setMenuItems( resetMenuItems );
};
+ const classes = classnames( 'components-block-support-panel', className );
+
return (
Date: Tue, 1 Jun 2021 17:53:01 +1000
Subject: [PATCH 08/50] Add initial tests for block supports panel
---
.../src/block-support-panel/test/index.js | 204 +++++++++++++++++-
1 file changed, 203 insertions(+), 1 deletion(-)
diff --git a/packages/components/src/block-support-panel/test/index.js b/packages/components/src/block-support-panel/test/index.js
index b3f1c4ff19eb8..5412591a3c70f 100644
--- a/packages/components/src/block-support-panel/test/index.js
+++ b/packages/components/src/block-support-panel/test/index.js
@@ -1 +1,203 @@
-// TODO: Add automatic tests for block support panel.
+/**
+ * External dependencies
+ */
+import { render, screen, fireEvent } from '@testing-library/react';
+
+/**
+ * Internal dependencies
+ */
+import BlockSupportPanel from '../';
+
+// Used to represent the block support provided controls.
+const MockControl = ( { children } ) => { children }
;
+
+const resetAll = jest.fn();
+const hasValue = jest.fn().mockImplementation( ( props ) => {
+ // Use fudged attribute to determine existence of value for testing.
+ return props.attributes.value;
+} );
+
+// Default props for the block supports panel.
+const defaultProps = {
+ label: 'Display options',
+ title: 'Panel title',
+ resetAll,
+};
+
+// Default props for enabled block supports control to be rendered within panel.
+const controlProps = {
+ attributes: { value: true },
+ hasValue,
+ label: 'Example',
+ reset: jest.fn().mockImplementation( () => {
+ controlProps.attributes.value = undefined;
+ } ),
+};
+
+// Default props without a value for alternate block supports control to be
+// rendered within the panel.
+const altControlProps = {
+ attributes: { value: false },
+ hasValue,
+ label: 'Alt',
+ reset: jest.fn(),
+};
+
+// Attempts to find the block supports panel via its CSS class.
+const getPanel = ( container ) =>
+ container.querySelector( '.components-block-support-panel' );
+
+// Renders a default block supports panel including a control that simulates
+// block support being disabled for that particular control.
+const renderPanel = () => {
+ return render(
+
+ { false && Hidden
}
+ Example control
+ Alt control
+
+ );
+};
+
+// Helper to find the menu button and simulate a user click.
+const openDropdownMenu = () => {
+ const menuButton = screen.getByLabelText( defaultProps.label );
+ fireEvent.click( menuButton );
+};
+
+// Opens dropdown then selects the menu item by label before simulating a click.
+const selectMenuItem = async ( label ) => {
+ openDropdownMenu();
+ const menuItem = await screen.findByText( label );
+ fireEvent.click( menuItem );
+};
+
+describe( 'BlockSupportPanel', () => {
+ describe( 'basic rendering', () => {
+ it( 'should not render when no children provided', () => {
+ const { container } = render(
+
+ );
+
+ expect( getPanel( container ) ).not.toBeInTheDocument();
+ } );
+
+ it( 'should not render when only child has been filtered out', () => {
+ // This covers case where children prop is not an array.
+ const { container } = render(
+
+ { false && Should not show }
+
+ );
+
+ expect( getPanel( container ) ).not.toBeInTheDocument();
+ } );
+
+ it( 'should not render when all children have been filtered out', () => {
+ const { container } = render(
+
+ { false && Should not show }
+ { false && Not shown either }
+
+ );
+
+ expect( getPanel( container ) ).not.toBeInTheDocument();
+ } );
+
+ it( 'should render panel when at least one child', () => {
+ const { container } = renderPanel();
+
+ expect( getPanel( container ) ).toBeInTheDocument();
+ } );
+
+ it( 'should render display options menu', () => {
+ renderPanel();
+
+ const menuButton = screen.getByLabelText( defaultProps.label );
+ expect( menuButton ).toBeInTheDocument();
+ } );
+
+ it( 'should render reset all item in menu', async () => {
+ renderPanel();
+ openDropdownMenu();
+
+ const resetAllItem = await screen.findByRole( 'menuitem' );
+
+ expect( resetAllItem ).toBeInTheDocument();
+ } );
+
+ it( 'should render display options menu items correctly', async () => {
+ renderPanel();
+ openDropdownMenu();
+
+ const menuItems = await screen.findAllByRole( 'menuitemcheckbox' );
+
+ expect( menuItems.length ).toEqual( 2 );
+ expect( menuItems[ 0 ] ).toHaveAttribute( 'aria-checked', 'true' );
+ expect( menuItems[ 1 ] ).toHaveAttribute( 'aria-checked', 'false' );
+ } );
+
+ it( 'should render panel title', () => {
+ renderPanel();
+ const title = screen.getByText( defaultProps.title );
+
+ expect( title ).toBeInTheDocument();
+ } );
+ } );
+
+ describe( 'conditional rendering of inner controls', () => {
+ it( 'should render child control when it has a value', () => {
+ renderPanel();
+
+ const exampleControl = screen.getByText( 'Example control' );
+ const altControl = screen.queryByText( 'Alt control' );
+
+ expect( exampleControl ).toBeInTheDocument();
+ expect( altControl ).not.toBeInTheDocument();
+ } );
+
+ it( 'should render child control when corresponding menu is selected', async () => {
+ renderPanel();
+ await selectMenuItem( altControlProps.label );
+ const control = await screen.findByText( 'Alt control' );
+
+ expect( control ).toBeInTheDocument();
+ } );
+
+ it( 'should prevent child rendering when toggled off via menu', async () => {
+ renderPanel();
+ await selectMenuItem( controlProps.label );
+ const control = screen.queryByText( 'Example control' );
+
+ expect( control ).not.toBeInTheDocument();
+ } );
+ } );
+
+ describe( 'reset callbacks on menu item selection', () => {
+ beforeEach( () => {
+ jest.clearAllMocks();
+ controlProps.attributes.value = true;
+ } );
+
+ it( 'should call reset callback when menu item is toggled off', async () => {
+ renderPanel();
+ await selectMenuItem( controlProps.label );
+
+ expect( controlProps.reset ).toHaveBeenCalledTimes( 1 );
+ } );
+
+ it( 'should not call reset callback when menu item is toggled on', async () => {
+ renderPanel();
+ await selectMenuItem( altControlProps.label );
+
+ expect( altControlProps.reset ).not.toHaveBeenCalled();
+ } );
+
+ it( 'should call resetAll callback when its menu item is selected', async () => {
+ renderPanel();
+ await selectMenuItem( 'Reset all' );
+
+ expect( resetAll ).toHaveBeenCalledTimes( 1 );
+ } );
+ } );
+} );
From 7fb7e8a6f1b2bf41904c0e5149ef7cba30e995eb Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Tue, 1 Jun 2021 18:36:08 +1000
Subject: [PATCH 09/50] Add story for block support panel
---
.../src/block-support-panel/stories/index.js | 70 ++++++++++++++++++-
.../src/block-support-panel/style.scss | 5 ++
2 files changed, 74 insertions(+), 1 deletion(-)
diff --git a/packages/components/src/block-support-panel/stories/index.js b/packages/components/src/block-support-panel/stories/index.js
index 06daf7b01ae57..0b14422760375 100644
--- a/packages/components/src/block-support-panel/stories/index.js
+++ b/packages/components/src/block-support-panel/stories/index.js
@@ -1 +1,69 @@
-// TODO: Add Storybook entries for block support panel.
+/**
+ * External dependencies
+ */
+import styled from '@emotion/styled';
+
+/**
+ * WordPress dependencies
+ */
+import { useState } from '@wordpress/element';
+
+/**
+ * Internal dependencies
+ */
+import BlockSupportPanel from '../';
+import Panel from '../../panel';
+import UnitControl from '../../unit-control';
+
+export default {
+ title: 'Components/BlockSupportPanel',
+ component: BlockSupportPanel,
+};
+
+export const _default = () => {
+ const [ height, setHeight ] = useState();
+ const [ width, setWidth ] = useState();
+
+ const resetAll = () => {
+ setHeight( undefined );
+ setWidth( undefined );
+ };
+
+ return (
+
+
+
+ !! height }
+ label="Height"
+ reset={ () => setHeight( undefined ) }
+ value={ height }
+ onChange={ ( next ) => setHeight( next ) }
+ />
+ !! width }
+ label="Width"
+ reset={ () => setWidth( undefined ) }
+ value={ width }
+ onChange={ ( next ) => setWidth( next ) }
+ />
+
+
+
+ );
+};
+
+function PlaceholderControl( { label, value, onChange } ) {
+ return (
+
+ );
+}
+
+const PanelWrapperView = styled.div`
+ max-width: 232px;
+ font-size: 13px;
+`;
diff --git a/packages/components/src/block-support-panel/style.scss b/packages/components/src/block-support-panel/style.scss
index 6644751aecc8e..67cbd408a530c 100644
--- a/packages/components/src/block-support-panel/style.scss
+++ b/packages/components/src/block-support-panel/style.scss
@@ -7,6 +7,10 @@
margin-top: -1px;
}
+ &:first-child {
+ margin-top: -1px;
+ }
+
.components-block-support-panel__title {
margin: 0;
display: flex;
@@ -14,6 +18,7 @@
justify-content: space-between;
font-weight: 500;
line-height: normal;
+ font-size: inherit;
.components-dropdown-menu {
height: $grid-unit-30;
From 62d789508e3256a32eaf839eecc7a76a0080deea Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Wed, 2 Jun 2021 16:10:55 +1000
Subject: [PATCH 10/50] Rename spacing support to dimensions
This does NOT change the shape of the block attributes style object. Spacing related block support styles such as margin and padding remain under `style.spacing.padding` etc.
---
.../{spacing.php => dimensions.php} | 38 +++++++++++++++----
lib/load.php | 2 +-
.../src/hooks/{spacing.js => dimensions.js} | 16 ++++----
packages/block-editor/src/hooks/index.js | 2 +-
packages/block-editor/src/hooks/margin.js | 2 +-
packages/block-editor/src/hooks/padding.js | 2 +-
packages/block-editor/src/hooks/style.js | 4 +-
7 files changed, 44 insertions(+), 22 deletions(-)
rename lib/block-supports/{spacing.php => dimensions.php} (71%)
rename packages/block-editor/src/hooks/{spacing.js => dimensions.js} (87%)
diff --git a/lib/block-supports/spacing.php b/lib/block-supports/dimensions.php
similarity index 71%
rename from lib/block-supports/spacing.php
rename to lib/block-supports/dimensions.php
index 592949f0473b8..b51682a34e016 100644
--- a/lib/block-supports/spacing.php
+++ b/lib/block-supports/dimensions.php
@@ -1,6 +1,6 @@
attributes ) {
$block_type->attributes = array();
}
- if ( $has_spacing_support && ! array_key_exists( 'style', $block_type->attributes ) ) {
+ // Check for existing style attribute definition e.g. from block.json.
+ if ( array_key_exists( 'style', $block_type->attributes ) ) {
+ return;
+ }
+
+ $has_spacing_support = gutenberg_block_has_support( $block_type, array( 'spacing' ), false );
+ // Future block supports such as height & width will be added here.
+
+ if ( $has_spacing_support ) {
$block_type->attributes['style'] = array(
'type' => 'object',
);
}
}
+/**
+ * Add CSS classes for block dimensions to the incoming attributes array.
+ * This will be applied to the block markup in the front-end.
+ *
+ * @param WP_Block_Type $block_type Block Type.
+ * @param array $block_attributes Block attributes.
+ *
+ * @return array Block spacing CSS classes and inline styles.
+ */
+function gutenberg_apply_dimensions_support( $block_type, $block_attributes ) {
+ $spacing_styles = gutenberg_apply_spacing_support( $block_type, $block_attributes );
+ // Future block supports such as height and width will be added here.
+
+ return $spacing_styles;
+}
+
/**
* Add CSS classes for block spacing to the incoming attributes array.
* This will be applied to the block markup in the front-end.
@@ -88,9 +110,9 @@ function gutenberg_skip_spacing_serialization( $block_type ) {
// Register the block support.
WP_Block_Supports::get_instance()->register(
- 'spacing',
+ 'dimensions',
array(
- 'register_attribute' => 'gutenberg_register_spacing_support',
- 'apply' => 'gutenberg_apply_spacing_support',
+ 'register_attribute' => 'gutenberg_register_dimensions_support',
+ 'apply' => 'gutenberg_apply_dimensions_support',
)
);
diff --git a/lib/load.php b/lib/load.php
index d6cf4449b2168..3e9292efbf57b 100644
--- a/lib/load.php
+++ b/lib/load.php
@@ -128,5 +128,5 @@ function gutenberg_is_experiment_enabled( $name ) {
require __DIR__ . '/block-supports/custom-classname.php';
require __DIR__ . '/block-supports/border.php';
require __DIR__ . '/block-supports/layout.php';
-require __DIR__ . '/block-supports/spacing.php';
+require __DIR__ . '/block-supports/dimensions.php';
require __DIR__ . '/block-supports/duotone.php';
diff --git a/packages/block-editor/src/hooks/spacing.js b/packages/block-editor/src/hooks/dimensions.js
similarity index 87%
rename from packages/block-editor/src/hooks/spacing.js
rename to packages/block-editor/src/hooks/dimensions.js
index 4d557ab463044..14ed1663b51b8 100644
--- a/packages/block-editor/src/hooks/spacing.js
+++ b/packages/block-editor/src/hooks/dimensions.js
@@ -28,17 +28,17 @@ import {
export const SPACING_SUPPORT_KEY = 'spacing';
/**
- * Inspector controls for spacing support.
+ * Inspector controls for dimensions support.
*
* @param {Object} props Block props.
*
* @return {WPElement} Inspector controls for spacing support features.
*/
-export function SpacingPanel( props ) {
+export function DimensionsPanel( props ) {
const isPaddingDisabled = useIsPaddingDisabled( props );
const isMarginDisabled = useIsMarginDisabled( props );
- const isDisabled = useIsSpacingDisabled( props );
- const isSupported = hasSpacingSupport( props.name );
+ const isDisabled = useIsDimensionsDisabled( props );
+ const isSupported = hasDimensionsSupport( props.name );
if ( isDisabled || ! isSupported ) {
return null;
@@ -89,13 +89,13 @@ export function SpacingPanel( props ) {
}
/**
- * Determine whether there is block support for padding or margins.
+ * Determine whether there is dimensions related block support.
*
* @param {string} blockName Block name.
*
* @return {boolean} Whether there is support.
*/
-export function hasSpacingSupport( blockName ) {
+export function hasDimensionsSupport( blockName ) {
if ( Platform.OS !== 'web' ) {
return false;
}
@@ -104,13 +104,13 @@ export function hasSpacingSupport( blockName ) {
}
/**
- * Determines whether spacing support has been disabled.
+ * Determines whether dimensions support has been disabled.
*
* @param {Object} props Block properties.
*
* @return {boolean} If spacing support is completely disabled.
*/
-const useIsSpacingDisabled = ( props = {} ) => {
+const useIsDimensionsDisabled = ( props = {} ) => {
const paddingDisabled = useIsPaddingDisabled( props );
const marginDisabled = useIsMarginDisabled( props );
diff --git a/packages/block-editor/src/hooks/index.js b/packages/block-editor/src/hooks/index.js
index c0e5c1b5f8bb8..e8a976277f970 100644
--- a/packages/block-editor/src/hooks/index.js
+++ b/packages/block-editor/src/hooks/index.js
@@ -12,7 +12,7 @@ import './font-size';
import './border-color';
import './layout';
-export { useCustomSides } from './spacing';
+export { useCustomSides } from './dimensions';
export { getBorderClassesAndStyles, useBorderProps } from './use-border-props';
export { getColorClassesAndStyles, useColorProps } from './use-color-props';
export { getSpacingClassesAndStyles } from './use-spacing-props';
diff --git a/packages/block-editor/src/hooks/margin.js b/packages/block-editor/src/hooks/margin.js
index db9c6cb7ea5fa..b734191a35b12 100644
--- a/packages/block-editor/src/hooks/margin.js
+++ b/packages/block-editor/src/hooks/margin.js
@@ -13,7 +13,7 @@ import {
* Internal dependencies
*/
import useSetting from '../components/use-setting';
-import { SPACING_SUPPORT_KEY, useCustomSides } from './spacing';
+import { SPACING_SUPPORT_KEY, useCustomSides } from './dimensions';
import { cleanEmptyObject } from './utils';
/**
diff --git a/packages/block-editor/src/hooks/padding.js b/packages/block-editor/src/hooks/padding.js
index b3fd9bb79d87d..c8392f04ec7b7 100644
--- a/packages/block-editor/src/hooks/padding.js
+++ b/packages/block-editor/src/hooks/padding.js
@@ -13,7 +13,7 @@ import {
* Internal dependencies
*/
import useSetting from '../components/use-setting';
-import { SPACING_SUPPORT_KEY, useCustomSides } from './spacing';
+import { SPACING_SUPPORT_KEY, useCustomSides } from './dimensions';
import { cleanEmptyObject } from './utils';
/**
diff --git a/packages/block-editor/src/hooks/style.js b/packages/block-editor/src/hooks/style.js
index 8c5d6ce1872f9..87bf4685d50d4 100644
--- a/packages/block-editor/src/hooks/style.js
+++ b/packages/block-editor/src/hooks/style.js
@@ -37,7 +37,7 @@ import {
TYPOGRAPHY_SUPPORT_KEY,
TYPOGRAPHY_SUPPORT_KEYS,
} from './typography';
-import { SPACING_SUPPORT_KEY, SpacingPanel } from './spacing';
+import { SPACING_SUPPORT_KEY, DimensionsPanel } from './dimensions';
import useDisplayBlockControls from '../components/use-display-block-controls';
const styleSupportKeys = [
@@ -232,7 +232,7 @@ export const withBlockControls = createHigherOrderComponent(
-
+
>
) }
From 5f7b39fdd23d1b621628b746e73a5ee37ad6088d Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Wed, 2 Jun 2021 16:17:27 +1000
Subject: [PATCH 11/50] Rename spacing support to dimensions in FSE
---
.../{spacing-panel.js => dimensions-panel.js} | 6 +++---
.../src/components/sidebar/global-styles-sidebar.js | 13 ++++++++-----
2 files changed, 11 insertions(+), 8 deletions(-)
rename packages/edit-site/src/components/sidebar/{spacing-panel.js => dimensions-panel.js} (94%)
diff --git a/packages/edit-site/src/components/sidebar/spacing-panel.js b/packages/edit-site/src/components/sidebar/dimensions-panel.js
similarity index 94%
rename from packages/edit-site/src/components/sidebar/spacing-panel.js
rename to packages/edit-site/src/components/sidebar/dimensions-panel.js
index cf224ee63fc25..1eed695fde8a6 100644
--- a/packages/edit-site/src/components/sidebar/spacing-panel.js
+++ b/packages/edit-site/src/components/sidebar/dimensions-panel.js
@@ -14,7 +14,7 @@ import { __experimentalUseCustomSides as useCustomSides } from '@wordpress/block
*/
import { useSetting } from '../editor/utils';
-export function useHasSpacingPanel( context ) {
+export function useHasDimensionsPanel( context ) {
const hasPadding = useHasPadding( context );
const hasMargin = useHasMargin( context );
@@ -61,7 +61,7 @@ function splitStyleValue( value ) {
return value;
}
-export default function SpacingPanel( { context, getStyle, setStyle } ) {
+export default function DimensionsPanel( { context, getStyle, setStyle } ) {
const { name } = context;
const showPaddingControl = useHasPadding( context );
const showMarginControl = useHasMargin( context );
@@ -92,7 +92,7 @@ export default function SpacingPanel( { context, getStyle, setStyle } ) {
};
return (
-
+
{ showPaddingControl && (
) }
- { hasSpacingPanel && (
-
Date: Wed, 2 Jun 2021 17:29:31 +1000
Subject: [PATCH 12/50] Update GlobalStyles dimensions panel
---
.../src/block-support-panel/style.scss | 19 +++++++------
.../components/sidebar/dimensions-panel.js | 27 ++++++++++++++++---
2 files changed, 35 insertions(+), 11 deletions(-)
diff --git a/packages/components/src/block-support-panel/style.scss b/packages/components/src/block-support-panel/style.scss
index 67cbd408a530c..407825312cc65 100644
--- a/packages/components/src/block-support-panel/style.scss
+++ b/packages/components/src/block-support-panel/style.scss
@@ -2,14 +2,7 @@
border: none;
border-top: $border-width solid $gray-200;
padding: $grid-unit-20;
-
- & + .components-block-support-panel {
- margin-top: -1px;
- }
-
- &:first-child {
- margin-top: -1px;
- }
+ margin-top: -1px;
.components-block-support-panel__title {
margin: 0;
@@ -48,3 +41,13 @@
}
}
}
+
+.edit-site {
+ .components-block-support-panel {
+ border-bottom: $border-width solid $gray-200;
+ }
+
+ .components-block-support-panel + .components-panel__body {
+ margin-top: -1px;
+ }
+}
diff --git a/packages/edit-site/src/components/sidebar/dimensions-panel.js b/packages/edit-site/src/components/sidebar/dimensions-panel.js
index 1eed695fde8a6..c0c09cde6c5b2 100644
--- a/packages/edit-site/src/components/sidebar/dimensions-panel.js
+++ b/packages/edit-site/src/components/sidebar/dimensions-panel.js
@@ -3,8 +3,8 @@
*/
import { __ } from '@wordpress/i18n';
import {
+ __experimentalBlockSupportPanel as BlockSupportPanel,
__experimentalBoxControl as BoxControl,
- PanelBody,
__experimentalUseCustomUnits as useCustomUnits,
} from '@wordpress/components';
import { __experimentalUseCustomSides as useCustomSides } from '@wordpress/block-editor';
@@ -82,6 +82,9 @@ export default function DimensionsPanel( { context, getStyle, setStyle } ) {
const padding = filterValuesBySides( newPaddingValues, paddingSides );
setStyle( name, 'padding', padding );
};
+ const resetPaddingValue = () => setPaddingValues( {} );
+ const hasPaddingValue = () =>
+ paddingValues && Object.keys( paddingValues ).length;
const marginValues = splitStyleValue( getStyle( name, 'margin' ) );
const marginSides = useCustomSides( name, 'margin' );
@@ -90,9 +93,21 @@ export default function DimensionsPanel( { context, getStyle, setStyle } ) {
const margin = filterValuesBySides( newMarginValues, marginSides );
setStyle( name, 'margin', margin );
};
+ const resetMarginValue = () => setMarginValues( {} );
+ const hasMarginValue = () =>
+ marginValues && Object.keys( marginValues ).length;
+
+ const resetAll = () => {
+ resetPaddingValue();
+ resetMarginValue();
+ };
return (
-
+
{ showPaddingControl && (
) }
{ showMarginControl && (
@@ -109,8 +127,11 @@ export default function DimensionsPanel( { context, getStyle, setStyle } ) {
label={ __( 'Margin' ) }
sides={ marginSides }
units={ units }
+ hasValue={ hasMarginValue }
+ reset={ resetMarginValue }
+ allowReset={ false }
/>
) }
-
+
);
}
From dd015b00b6817ad5dc52a63ea1db15561d9d54b6 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Fri, 4 Jun 2021 20:51:48 +1000
Subject: [PATCH 13/50] Add means to handle default controls in block support
panel
---
packages/block-editor/src/hooks/dimensions.js | 7 +++
.../src/block-support-panel/index.js | 54 ++++++++++++++-----
.../components/sidebar/dimensions-panel.js | 2 +
3 files changed, 51 insertions(+), 12 deletions(-)
diff --git a/packages/block-editor/src/hooks/dimensions.js b/packages/block-editor/src/hooks/dimensions.js
index 14ed1663b51b8..dd28feb901959 100644
--- a/packages/block-editor/src/hooks/dimensions.js
+++ b/packages/block-editor/src/hooks/dimensions.js
@@ -44,6 +44,11 @@ export function DimensionsPanel( props ) {
return null;
}
+ const defaultSpacingControls = getBlockSupport( props.name, [
+ SPACING_SUPPORT_KEY,
+ '__experimentalDefaultControls',
+ ] );
+
// Callback to reset all block support attributes controlled via this panel.
const resetAll = () => {
const { style } = props.attributes;
@@ -73,6 +78,7 @@ export function DimensionsPanel( props ) {
hasValue={ hasPaddingValue }
label={ __( 'Padding' ) }
reset={ resetPadding }
+ isShownByDefault={ defaultSpacingControls?.padding }
/>
) }
{ ! isMarginDisabled && (
@@ -81,6 +87,7 @@ export function DimensionsPanel( props ) {
hasValue={ hasMarginValue }
label={ __( 'Margin' ) }
reset={ resetMargin }
+ isShownByDefault={ defaultSpacingControls?.margin }
/>
) }
diff --git a/packages/components/src/block-support-panel/index.js b/packages/components/src/block-support-panel/index.js
index a66742503b222..3ed4367c233a1 100644
--- a/packages/components/src/block-support-panel/index.js
+++ b/packages/components/src/block-support-panel/index.js
@@ -16,6 +16,7 @@ import BlockSupportPanelTitle from './title';
const BlockSupportPanel = ( props ) => {
const { children, className, label: menuLabel, resetAll, title } = props;
const [ menuItems, setMenuItems ] = useState( {} );
+ const [ defaultControls, setDefaultControls ] = useState( {} );
// If a block support UI has been disabled via theme.json a boolean `false`
// will be passed as a child. This panel is only interested in the children
@@ -24,17 +25,48 @@ const BlockSupportPanel = ( props ) => {
? children.filter( Boolean )
: [];
- // Collect data to manage control visibility via the panel's dropdown menu.
+ // Collect which controls have custom values. Used to update menu state to
+ // reflect customization for controls that display by default / always show.
+ const customizedChildren = filteredChildren.map( ( child ) =>
+ child.props.hasValue( child.props ) ? child.props.label : undefined
+ );
+
+ // On first render determine initial menu state and which controls should
+ // always display by default.
useEffect( () => {
const items = {};
+ const defaults = {};
filteredChildren.forEach( ( child ) => {
items[ child.props.label ] = child.props.hasValue( child.props );
+ defaults[ child.props.label ] = child.props.isShownByDefault;
} );
setMenuItems( items );
+ setDefaultControls( defaults );
}, [] );
+ // As the default controls are visible all the time. Reflect their
+ // customizations in the menu items' selected state.
+ useEffect( () => {
+ const menuLabels = Object.keys( menuItems );
+
+ // Skip if no children or menu state not initialized.
+ if ( menuLabels.length === 0 ) {
+ return;
+ }
+
+ const updatedItems = { ...menuItems };
+
+ menuLabels.forEach( ( label ) => {
+ if ( defaultControls[ label ] ) {
+ updatedItems[ label ] = customizedChildren.includes( label );
+ }
+ } );
+
+ setMenuItems( updatedItems );
+ }, customizedChildren );
+
if ( filteredChildren.length === 0 ) {
return null;
}
@@ -45,8 +77,9 @@ const BlockSupportPanel = ( props ) => {
);
};
- // Toggles display of a block support control resetting the attributes if
- // being turned off.
+ // Toggles the customized state of the block support control and its display
+ // if it isn't to be displayed by default. When toggling off a control its
+ // associated block attribute is reset.
const toggleControl = ( label ) => {
const isSelected = menuItems[ label ];
@@ -89,16 +122,13 @@ const BlockSupportPanel = ( props ) => {
resetAll={ resetAllControls }
/>
{ filteredChildren.map( ( child ) => {
- const { label, hasValue } = child?.props || {};
-
- // Only display the block support controls if the support
- // attributes have a value or the controls have be chosen for
- // display by the user.
- if ( menuItems[ label ] || hasValue( child.props ) ) {
- return child;
- }
+ // Only display the block support control if it is toggled
+ // on in the menu or is set to display by default.
+ const isShown =
+ menuItems[ child.props.label ] ||
+ defaultControls[ child.props.label ];
- return null;
+ return isShown ? child : null;
} ) }
);
diff --git a/packages/edit-site/src/components/sidebar/dimensions-panel.js b/packages/edit-site/src/components/sidebar/dimensions-panel.js
index c0c09cde6c5b2..d6df96c0a83ed 100644
--- a/packages/edit-site/src/components/sidebar/dimensions-panel.js
+++ b/packages/edit-site/src/components/sidebar/dimensions-panel.js
@@ -118,6 +118,7 @@ export default function DimensionsPanel( { context, getStyle, setStyle } ) {
hasValue={ hasPaddingValue }
reset={ resetPaddingValue }
allowReset={ false }
+ isShownByDefault={ true }
/>
) }
{ showMarginControl && (
@@ -130,6 +131,7 @@ export default function DimensionsPanel( { context, getStyle, setStyle } ) {
hasValue={ hasMarginValue }
reset={ resetMarginValue }
allowReset={ false }
+ isShownByDefault={ true }
/>
) }
From 659a79e67e97166bbe3913b20bedb96485a61d27 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Mon, 7 Jun 2021 11:05:26 +1000
Subject: [PATCH 14/50] Change default controls display in block supports panel
Default controls now show as checked when panel initially displayed. These controls can also now be toggled off from display.
---
.../src/block-support-panel/index.js | 52 +++----------------
1 file changed, 8 insertions(+), 44 deletions(-)
diff --git a/packages/components/src/block-support-panel/index.js b/packages/components/src/block-support-panel/index.js
index 3ed4367c233a1..b86b0118f19e3 100644
--- a/packages/components/src/block-support-panel/index.js
+++ b/packages/components/src/block-support-panel/index.js
@@ -16,7 +16,6 @@ import BlockSupportPanelTitle from './title';
const BlockSupportPanel = ( props ) => {
const { children, className, label: menuLabel, resetAll, title } = props;
const [ menuItems, setMenuItems ] = useState( {} );
- const [ defaultControls, setDefaultControls ] = useState( {} );
// If a block support UI has been disabled via theme.json a boolean `false`
// will be passed as a child. This panel is only interested in the children
@@ -25,48 +24,20 @@ const BlockSupportPanel = ( props ) => {
? children.filter( Boolean )
: [];
- // Collect which controls have custom values. Used to update menu state to
- // reflect customization for controls that display by default / always show.
- const customizedChildren = filteredChildren.map( ( child ) =>
- child.props.hasValue( child.props ) ? child.props.label : undefined
- );
-
- // On first render determine initial menu state and which controls should
- // always display by default.
+ // On first render determine initial menu state. Default controls will
+ // initially display and have a check mark beside their menu item regardless
+ // of whether they have a value.
useEffect( () => {
const items = {};
- const defaults = {};
filteredChildren.forEach( ( child ) => {
- items[ child.props.label ] = child.props.hasValue( child.props );
- defaults[ child.props.label ] = child.props.isShownByDefault;
+ const { hasValue, isShownByDefault, label } = child.props;
+ items[ label ] = isShownByDefault || hasValue( child.props );
} );
setMenuItems( items );
- setDefaultControls( defaults );
}, [] );
- // As the default controls are visible all the time. Reflect their
- // customizations in the menu items' selected state.
- useEffect( () => {
- const menuLabels = Object.keys( menuItems );
-
- // Skip if no children or menu state not initialized.
- if ( menuLabels.length === 0 ) {
- return;
- }
-
- const updatedItems = { ...menuItems };
-
- menuLabels.forEach( ( label ) => {
- if ( defaultControls[ label ] ) {
- updatedItems[ label ] = customizedChildren.includes( label );
- }
- } );
-
- setMenuItems( updatedItems );
- }, customizedChildren );
-
if ( filteredChildren.length === 0 ) {
return null;
}
@@ -77,9 +48,8 @@ const BlockSupportPanel = ( props ) => {
);
};
- // Toggles the customized state of the block support control and its display
- // if it isn't to be displayed by default. When toggling off a control its
- // associated block attribute is reset.
+ // Toggles the display of the block support control and resets its
+ // associated block attribute via the control's reset callback prop.
const toggleControl = ( label ) => {
const isSelected = menuItems[ label ];
@@ -122,13 +92,7 @@ const BlockSupportPanel = ( props ) => {
resetAll={ resetAllControls }
/>
{ filteredChildren.map( ( child ) => {
- // Only display the block support control if it is toggled
- // on in the menu or is set to display by default.
- const isShown =
- menuItems[ child.props.label ] ||
- defaultControls[ child.props.label ];
-
- return isShown ? child : null;
+ return menuItems[ child.props.label ] ? child : null;
} ) }
);
From 213d250e88e636ef542c0f103a5df3ab4e8122db Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Tue, 8 Jun 2021 09:32:24 +1000
Subject: [PATCH 15/50] Make default controls still show after reset all
---
packages/components/src/block-support-panel/index.js | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/packages/components/src/block-support-panel/index.js b/packages/components/src/block-support-panel/index.js
index b86b0118f19e3..789e28cd64ab5 100644
--- a/packages/components/src/block-support-panel/index.js
+++ b/packages/components/src/block-support-panel/index.js
@@ -16,6 +16,7 @@ import BlockSupportPanelTitle from './title';
const BlockSupportPanel = ( props ) => {
const { children, className, label: menuLabel, resetAll, title } = props;
const [ menuItems, setMenuItems ] = useState( {} );
+ const [ defaultControls, setDefaultControls ] = useState( {} );
// If a block support UI has been disabled via theme.json a boolean `false`
// will be passed as a child. This panel is only interested in the children
@@ -29,13 +30,16 @@ const BlockSupportPanel = ( props ) => {
// of whether they have a value.
useEffect( () => {
const items = {};
+ const defaults = {};
filteredChildren.forEach( ( child ) => {
const { hasValue, isShownByDefault, label } = child.props;
items[ label ] = isShownByDefault || hasValue( child.props );
+ defaults[ label ] = isShownByDefault;
} );
setMenuItems( items );
+ setDefaultControls( defaults );
}, [] );
if ( filteredChildren.length === 0 ) {
@@ -70,11 +74,11 @@ const BlockSupportPanel = ( props ) => {
// Reset the block support attributes.
resetAll();
- // Turn off all the controls in menu.
+ // Turn off menu items unless they are to display by default.
const resetMenuItems = {};
- filteredChildren.forEach( ( child ) => {
- resetMenuItems[ child.props.label ] = false;
+ filteredChildren.forEach( ( { props: { label } } ) => {
+ resetMenuItems[ label ] = defaultControls[ label ];
} );
setMenuItems( resetMenuItems );
From a96dc3563e1119ed74da1303362c9275f7157547 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Wed, 9 Jun 2021 10:39:07 +1000
Subject: [PATCH 16/50] Change back to default controls always displaying
---
.../src/block-support-panel/index.js | 53 +++++++++++++++----
1 file changed, 42 insertions(+), 11 deletions(-)
diff --git a/packages/components/src/block-support-panel/index.js b/packages/components/src/block-support-panel/index.js
index 789e28cd64ab5..53c4469396d89 100644
--- a/packages/components/src/block-support-panel/index.js
+++ b/packages/components/src/block-support-panel/index.js
@@ -25,23 +25,47 @@ const BlockSupportPanel = ( props ) => {
? children.filter( Boolean )
: [];
- // On first render determine initial menu state. Default controls will
- // initially display and have a check mark beside their menu item regardless
- // of whether they have a value.
+ // Collect which controls have custom values. Used to update menu state to
+ // reflect customization for controls that display by default / always show.
+ const customizedChildren = filteredChildren.map( ( child ) =>
+ child.props.hasValue( child.props ) ? child.props.label : undefined
+ );
+
+ // On first render determine initial menu state and which controls should
+ // always display by default.
useEffect( () => {
const items = {};
const defaults = {};
filteredChildren.forEach( ( child ) => {
- const { hasValue, isShownByDefault, label } = child.props;
- items[ label ] = isShownByDefault || hasValue( child.props );
- defaults[ label ] = isShownByDefault;
+ items[ child.props.label ] = child.props.hasValue( child.props );
+ defaults[ child.props.label ] = child.props.isShownByDefault;
} );
setMenuItems( items );
setDefaultControls( defaults );
}, [] );
+ // As the default controls are visible all the time. Reflect their
+ // customizations in the menu items' selected state.
+ useEffect( () => {
+ const menuLabels = Object.keys( menuItems );
+
+ // Skip if no children or menu state not initialized.
+ if ( menuLabels.length === 0 ) {
+ return;
+ }
+
+ const updatedItems = { ...menuItems };
+ menuLabels.forEach( ( label ) => {
+ if ( defaultControls[ label ] ) {
+ updatedItems[ label ] = customizedChildren.includes( label );
+ }
+ } );
+
+ setMenuItems( updatedItems );
+ }, customizedChildren );
+
if ( filteredChildren.length === 0 ) {
return null;
}
@@ -52,8 +76,9 @@ const BlockSupportPanel = ( props ) => {
);
};
- // Toggles the display of the block support control and resets its
- // associated block attribute via the control's reset callback prop.
+ // Toggles the customized state of the block support control and its display
+ // if it isn't to be displayed by default. When toggling off a control its
+ // associated block attribute is reset via the control's reset callback.
const toggleControl = ( label ) => {
const isSelected = menuItems[ label ];
@@ -77,8 +102,8 @@ const BlockSupportPanel = ( props ) => {
// Turn off menu items unless they are to display by default.
const resetMenuItems = {};
- filteredChildren.forEach( ( { props: { label } } ) => {
- resetMenuItems[ label ] = defaultControls[ label ];
+ filteredChildren.forEach( ( child ) => {
+ resetMenuItems[ child.props.label ] = false;
} );
setMenuItems( resetMenuItems );
@@ -96,7 +121,13 @@ const BlockSupportPanel = ( props ) => {
resetAll={ resetAllControls }
/>
{ filteredChildren.map( ( child ) => {
- return menuItems[ child.props.label ] ? child : null;
+ // Only display the block support control if it is toggled on
+ // in the menu or is set to display by default.
+ const isShown =
+ menuItems[ child.props.label ] ||
+ defaultControls[ child.props.label ];
+
+ return isShown ? child : null;
} ) }
);
From 481ea39718ba60a319c37621cac72d32366821cb Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Wed, 23 Jun 2021 16:58:02 +1000
Subject: [PATCH 17/50] Tweak panel to make it a little more generic
---
docs/manifest.json | 12 +-
packages/block-editor/src/hooks/dimensions.js | 10 +-
.../src/block-support-panel/README.md | 100 ---------------
packages/components/src/index.js | 2 +-
.../progressive-disclosure-panel/README.md | 117 ++++++++++++++++++
.../index.js | 74 +++++------
.../stories/index.js | 16 +--
.../style.scss | 8 +-
.../test/index.js | 62 +++++-----
.../title.js | 10 +-
packages/components/src/style.scss | 2 +-
11 files changed, 219 insertions(+), 194 deletions(-)
delete mode 100644 packages/components/src/block-support-panel/README.md
create mode 100644 packages/components/src/progressive-disclosure-panel/README.md
rename packages/components/src/{block-support-panel => progressive-disclosure-panel}/index.js (54%)
rename packages/components/src/{block-support-panel => progressive-disclosure-panel}/stories/index.js (77%)
rename packages/components/src/{block-support-panel => progressive-disclosure-panel}/style.scss (79%)
rename packages/components/src/{block-support-panel => progressive-disclosure-panel}/test/index.js (70%)
rename packages/components/src/{block-support-panel => progressive-disclosure-panel}/title.js (80%)
diff --git a/docs/manifest.json b/docs/manifest.json
index ef6392b9be2b2..a1e4afa816823 100644
--- a/docs/manifest.json
+++ b/docs/manifest.json
@@ -695,12 +695,6 @@
"markdown_source": "../packages/components/src/base-field/README.md",
"parent": "components"
},
- {
- "title": "BlockSupportPanel",
- "slug": "block-support-panel",
- "markdown_source": "../packages/components/src/block-support-panel/README.md",
- "parent": "components"
- },
{
"title": "BoxControl",
"slug": "box-control",
@@ -1097,6 +1091,12 @@
"markdown_source": "../packages/components/src/popover/README.md",
"parent": "components"
},
+ {
+ "title": "ProgressiveDisclosurePanel",
+ "slug": "progressive-disclosure-panel",
+ "markdown_source": "../packages/components/src/progressive-disclosure-panel/README.md",
+ "parent": "components"
+ },
{
"title": "QueryControls",
"slug": "query-controls",
diff --git a/packages/block-editor/src/hooks/dimensions.js b/packages/block-editor/src/hooks/dimensions.js
index dd28feb901959..290cb5ce227c8 100644
--- a/packages/block-editor/src/hooks/dimensions.js
+++ b/packages/block-editor/src/hooks/dimensions.js
@@ -1,7 +1,7 @@
/**
* WordPress dependencies
*/
-import { __experimentalBlockSupportPanel as BlockSupportPanel } from '@wordpress/components';
+import { __experimentalProgressiveDisclosurePanel as ProgressiveDisclosurePanel } from '@wordpress/components';
import { Platform } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { getBlockSupport } from '@wordpress/blocks';
@@ -67,7 +67,7 @@ export function DimensionsPanel( props ) {
return (
-
) }
@@ -86,11 +86,11 @@ export function DimensionsPanel( props ) {
{ ...props }
hasValue={ hasMarginValue }
label={ __( 'Margin' ) }
- reset={ resetMargin }
+ onDeselect={ resetMargin }
isShownByDefault={ defaultSpacingControls?.margin }
/>
) }
-
+
);
}
diff --git a/packages/components/src/block-support-panel/README.md b/packages/components/src/block-support-panel/README.md
deleted file mode 100644
index 63fca61e6d29f..0000000000000
--- a/packages/components/src/block-support-panel/README.md
+++ /dev/null
@@ -1,100 +0,0 @@
-# Block Support Panel
-
-These panels provide progressive discovery options for multiple controls
-provided via block supports.
-
-## Development guidelines
-
-The `BlockSupportPanel` creates a container with a header including a dropdown
-menu. The menu is generated automatically from the panel's children. Each menu
-item allows for the display of the corresponding child control to be toggled
-on or off. When toggled off the control's reset callback is fired allowing for
-the block support provided attribtues to reset.
-
-Whether a child control is initially displayed or not is dependent upon
-whether there has previously been a value set. This is checked via the
-`hasValue` function provided through the child's props.
-
-### Usage
-
-```jsx
-import { __experimentalBlockSupportPanel as BlockSupportPanel } from '@wordpress/components';
-import {
- PaddingEdit,
- hasPaddingValue,
- resetPadding,
- useIsPaddingDisabled,
-} from './padding';
-
-
-export function DimensionPanel( props ) {
- const isPaddingDisabled = useIsPaddingDisabled( props );
-
- const resetAll = () => {
- // Reset attributes for all block support features in this panel.
- };
-
- return (
-
- { ! isPaddingDisabled && (
-
- ) }
-
- );
-}
-```
-
-### Sub-Components
-
-#### BlockSupportPanelTitle
-
-This is a simple component to display the panel title and house the dropdown
-menu for toggling control display. It is used by the `BlockSupportPanel`
-component under the hood, so it does not typically need to be used.
-
-##### Props
-###### resetAll
-
-A function to call when the `Reset all` menu option is selected.
-
-- Type: `function`
-- Required: Yes
-
-###### toggleControl
-
-Callback used to toggle display of an individual block support control and reset
-its value if being turned off.
-
-- Type: `function`
-- Required: Yes
-
-###### menuItems
-
-This object represents the child controls and their visibility state. It
-is built by the parent panel using its children.
-
-- Type: `Object`
-- Required: No
-
-###### menuLabel
-
-A label for the dropdown menu.
-
-- Type: `String`
-- Required: No
-
-###### title
-
-The panel title to display.
-
-- Type: `String`
-- Required: No
diff --git a/packages/components/src/index.js b/packages/components/src/index.js
index 8c2d93f252ec0..540fbf2759518 100644
--- a/packages/components/src/index.js
+++ b/packages/components/src/index.js
@@ -22,7 +22,6 @@ export {
useAutocompleteProps as __unstableUseAutocompleteProps,
} from './autocomplete';
export { default as BaseControl } from './base-control';
-export { default as __experimentalBlockSupportPanel } from './block-support-panel';
export { default as __experimentalBoxControl } from './box-control';
export { default as Button } from './button';
export { default as ButtonGroup } from './button-group';
@@ -101,6 +100,7 @@ export { default as PanelHeader } from './panel/header';
export { default as PanelRow } from './panel/row';
export { default as Placeholder } from './placeholder';
export { default as Popover } from './popover';
+export { default as __experimentalProgressiveDisclosurePanel } from './progressive-disclosure-panel';
export { default as QueryControls } from './query-controls';
export { default as __experimentalRadio } from './radio';
export { default as __experimentalRadioGroup } from './radio-group';
diff --git a/packages/components/src/progressive-disclosure-panel/README.md b/packages/components/src/progressive-disclosure-panel/README.md
new file mode 100644
index 0000000000000..f56fd865ee285
--- /dev/null
+++ b/packages/components/src/progressive-disclosure-panel/README.md
@@ -0,0 +1,117 @@
+# Progressive Disclosure Panel
+
+These panels provide progressive discovery options for their children. For
+example the controls provided via block supports.
+
+## Development guidelines
+
+The `ProgressiveDisclosurePanel` creates a container with a header including a
+dropdown menu. The menu is generated automatically from the panel's children.
+Each menu item allows for the display of the corresponding child to be
+toggled on or off. The control's `onSelect` and `onDeselect` callbacks are fired
+allowing for greater control over the child e.g. resetting block attributes when
+a block support control is toggled off.
+
+Whether a child control is initially displayed or not is dependent upon
+if there has previously been a value set or the child has been flagged as
+displaying by default through the `isShownByDefault` prop. Determining whether a
+child has a value is done via the `hasValue` function provided through the
+child's props.
+
+### Usage
+
+```jsx
+import { __experimentalProgressiveDisclosurePanel as ProgressiveDisclosurePanel } from '@wordpress/components';
+import {
+ PaddingEdit,
+ hasPaddingValue,
+ resetPadding,
+ useIsPaddingDisabled,
+} from './padding';
+
+
+export function DimensionPanel( props ) {
+ const isPaddingDisabled = useIsPaddingDisabled( props );
+
+ const resetAll = () => {
+ // Reset attributes for all block support features in this panel.
+ };
+
+ return (
+
+ { ! isPaddingDisabled && (
+
+ ) }
+
+ );
+}
+```
+
+### Props
+
+#### label
+
+The label for the panel's dropdown menu.
+
+#### resetAll
+
+A function to call when the `Reset all` menu option is selected. This is passed
+through to the panel's title component.
+
+#### title
+
+Title to be displayed within the panel's title.
+
+### Sub-Components
+
+#### ProgressiveDisclosurePanelTitle
+
+This is a simple component to display the panel title and house the dropdown
+menu for toggling child display. It is used by the `ProgressiveDisclosurePanel`
+component under the hood, so it does not typically need to be used.
+
+##### Props
+###### resetAll
+
+A function to call when the `Reset all` menu option is selected.
+
+- Type: `function`
+- Required: Yes
+
+###### toggleChild
+
+Callback used to toggle display of an individual child component.
+
+- Type: `function`
+- Required: Yes
+
+###### menuItems
+
+This object represents the panel's children and their visibility state. It
+is built by the parent panel from its children prop.
+
+- Type: `Object`
+- Required: No
+
+###### menuLabel
+
+A label for the dropdown menu.
+
+- Type: `String`
+- Required: No
+
+###### title
+
+The panel title to display.
+
+- Type: `String`
+- Required: No
diff --git a/packages/components/src/block-support-panel/index.js b/packages/components/src/progressive-disclosure-panel/index.js
similarity index 54%
rename from packages/components/src/block-support-panel/index.js
rename to packages/components/src/progressive-disclosure-panel/index.js
index 53c4469396d89..371f8bcf58bab 100644
--- a/packages/components/src/block-support-panel/index.js
+++ b/packages/components/src/progressive-disclosure-panel/index.js
@@ -2,6 +2,7 @@
* External dependencies
*/
import classnames from 'classnames';
+import noop from 'lodash';
/**
* WordPress dependencies
@@ -11,27 +12,27 @@ import { useEffect, useState } from '@wordpress/element';
/**
* Internal dependencies
*/
-import BlockSupportPanelTitle from './title';
+import ProgressiveDisclosurePanelTitle from './title';
-const BlockSupportPanel = ( props ) => {
+const ProgressiveDisclosurePanel = ( props ) => {
const { children, className, label: menuLabel, resetAll, title } = props;
const [ menuItems, setMenuItems ] = useState( {} );
- const [ defaultControls, setDefaultControls ] = useState( {} );
+ const [ defaultChildren, setDefaultChildren ] = useState( {} );
- // If a block support UI has been disabled via theme.json a boolean `false`
- // will be passed as a child. This panel is only interested in the children
- // to be displayed.
+ // When conditionally including components e.g. { isShown && }
+ // a boolean `false` will be passed as a child if component is excluded.
+ // This panel is only interested in the children to be displayed.
const filteredChildren = Array.isArray( children )
? children.filter( Boolean )
: [];
- // Collect which controls have custom values. Used to update menu state to
- // reflect customization for controls that display by default / always show.
+ // Collect which children have custom values. Used to update menu state to
+ // reflect customization for children that display by default / always show.
const customizedChildren = filteredChildren.map( ( child ) =>
child.props.hasValue( child.props ) ? child.props.label : undefined
);
- // On first render determine initial menu state and which controls should
+ // On first render determine initial menu state and which children should
// always display by default.
useEffect( () => {
const items = {};
@@ -43,10 +44,10 @@ const BlockSupportPanel = ( props ) => {
} );
setMenuItems( items );
- setDefaultControls( defaults );
+ setDefaultChildren( defaults );
}, [] );
- // As the default controls are visible all the time. Reflect their
+ // As the default children are visible all the time. Reflect their
// customizations in the menu items' selected state.
useEffect( () => {
const menuLabels = Object.keys( menuItems );
@@ -58,7 +59,7 @@ const BlockSupportPanel = ( props ) => {
const updatedItems = { ...menuItems };
menuLabels.forEach( ( label ) => {
- if ( defaultControls[ label ] ) {
+ if ( defaultChildren[ label ] ) {
updatedItems[ label ] = customizedChildren.includes( label );
}
} );
@@ -70,33 +71,33 @@ const BlockSupportPanel = ( props ) => {
return null;
}
- const getControlByMenuLabel = ( label ) => {
+ const getChildByMenuLabel = ( label ) => {
return filteredChildren.find(
( child ) => child.props.label === label
);
};
- // Toggles the customized state of the block support control and its display
- // if it isn't to be displayed by default. When toggling off a control its
- // associated block attribute is reset via the control's reset callback.
- const toggleControl = ( label ) => {
- const isSelected = menuItems[ label ];
-
- if ( isSelected ) {
- const control = getControlByMenuLabel( label );
- control.props.reset( control.props );
+ // Toggles the customized state of the child and its display if it isn't to
+ // be displayed by default. When toggling a child it's callback is executed.
+ const toggleChild = ( label ) => {
+ const wasSelected = menuItems[ label ];
+ const child = getChildByMenuLabel( label );
+ const { onDeselect = noop, onSelect = noop } = child.props;
+
+ if ( wasSelected ) {
+ onDeselect( child.props );
+ } else {
+ onSelect( child.props );
}
setMenuItems( {
...menuItems,
- [ label ]: ! isSelected,
+ [ label ]: ! wasSelected,
} );
};
- // Resets all block support attributes for controls represented by the
- // menu items. Then turns off their display.
- const resetAllControls = () => {
- // Reset the block support attributes.
+ // Resets display of children and executes resetAll callback if available.
+ const resetAllChildren = () => {
resetAll();
// Turn off menu items unless they are to display by default.
@@ -109,23 +110,26 @@ const BlockSupportPanel = ( props ) => {
setMenuItems( resetMenuItems );
};
- const classes = classnames( 'components-block-support-panel', className );
+ const classes = classnames(
+ 'components-progressive-disclosure-panel',
+ className
+ );
return (
-
{ filteredChildren.map( ( child ) => {
- // Only display the block support control if it is toggled on
- // in the menu or is set to display by default.
+ // Only display the child if it is toggled on in the menu or is
+ // set to display by default.
const isShown =
menuItems[ child.props.label ] ||
- defaultControls[ child.props.label ];
+ defaultChildren[ child.props.label ];
return isShown ? child : null;
} ) }
@@ -133,4 +137,4 @@ const BlockSupportPanel = ( props ) => {
);
};
-export default BlockSupportPanel;
+export default ProgressiveDisclosurePanel;
diff --git a/packages/components/src/block-support-panel/stories/index.js b/packages/components/src/progressive-disclosure-panel/stories/index.js
similarity index 77%
rename from packages/components/src/block-support-panel/stories/index.js
rename to packages/components/src/progressive-disclosure-panel/stories/index.js
index 0b14422760375..a8525aa25caf5 100644
--- a/packages/components/src/block-support-panel/stories/index.js
+++ b/packages/components/src/progressive-disclosure-panel/stories/index.js
@@ -11,13 +11,13 @@ import { useState } from '@wordpress/element';
/**
* Internal dependencies
*/
-import BlockSupportPanel from '../';
+import ProgressiveDisclosurePanel from '../';
import Panel from '../../panel';
import UnitControl from '../../unit-control';
export default {
- title: 'Components/BlockSupportPanel',
- component: BlockSupportPanel,
+ title: 'Components/ProgressiveDisclosurePanel',
+ component: ProgressiveDisclosurePanel,
};
export const _default = () => {
@@ -32,26 +32,26 @@ export const _default = () => {
return (
-
!! height }
label="Height"
- reset={ () => setHeight( undefined ) }
+ onDeselect={ () => setHeight( undefined ) }
value={ height }
onChange={ ( next ) => setHeight( next ) }
/>
!! width }
label="Width"
- reset={ () => setWidth( undefined ) }
+ onDeselect={ () => setWidth( undefined ) }
value={ width }
onChange={ ( next ) => setWidth( next ) }
/>
-
+
);
diff --git a/packages/components/src/block-support-panel/style.scss b/packages/components/src/progressive-disclosure-panel/style.scss
similarity index 79%
rename from packages/components/src/block-support-panel/style.scss
rename to packages/components/src/progressive-disclosure-panel/style.scss
index 407825312cc65..d332fcd14c07d 100644
--- a/packages/components/src/block-support-panel/style.scss
+++ b/packages/components/src/progressive-disclosure-panel/style.scss
@@ -1,10 +1,10 @@
-.components-block-support-panel {
+.components-progressive-disclosure-panel {
border: none;
border-top: $border-width solid $gray-200;
padding: $grid-unit-20;
margin-top: -1px;
- .components-block-support-panel__title {
+ .components-progressive-disclosure-panel__title {
margin: 0;
display: flex;
align-items: center;
@@ -43,11 +43,11 @@
}
.edit-site {
- .components-block-support-panel {
+ .components-progressive-disclosure-panel {
border-bottom: $border-width solid $gray-200;
}
- .components-block-support-panel + .components-panel__body {
+ .components-progressive-disclosure-panel + .components-panel__body {
margin-top: -1px;
}
}
diff --git a/packages/components/src/block-support-panel/test/index.js b/packages/components/src/progressive-disclosure-panel/test/index.js
similarity index 70%
rename from packages/components/src/block-support-panel/test/index.js
rename to packages/components/src/progressive-disclosure-panel/test/index.js
index 5412591a3c70f..3fff561a758a8 100644
--- a/packages/components/src/block-support-panel/test/index.js
+++ b/packages/components/src/progressive-disclosure-panel/test/index.js
@@ -6,9 +6,9 @@ import { render, screen, fireEvent } from '@testing-library/react';
/**
* Internal dependencies
*/
-import BlockSupportPanel from '../';
+import ProgressiveDisclosurePanel from '../';
-// Used to represent the block support provided controls.
+// Represents a child provided to the panel such as a block support control.
const MockControl = ( { children } ) =>
{ children }
;
const resetAll = jest.fn();
@@ -17,45 +17,47 @@ const hasValue = jest.fn().mockImplementation( ( props ) => {
return props.attributes.value;
} );
-// Default props for the block supports panel.
+// Default props for the progressive disclosure panel.
const defaultProps = {
label: 'Display options',
title: 'Panel title',
resetAll,
};
-// Default props for enabled block supports control to be rendered within panel.
+// Default props for an enabled control to be rendered within panel.
const controlProps = {
attributes: { value: true },
hasValue,
label: 'Example',
- reset: jest.fn().mockImplementation( () => {
+ onDeselect: jest.fn().mockImplementation( () => {
controlProps.attributes.value = undefined;
} ),
+ onSelect: jest.fn(),
};
-// Default props without a value for alternate block supports control to be
-// rendered within the panel.
+// Default props without a value for an alternate control to be rendered within
+// the panel.
const altControlProps = {
attributes: { value: false },
hasValue,
label: 'Alt',
- reset: jest.fn(),
+ onDeselect: jest.fn(),
+ onSelect: jest.fn(),
};
-// Attempts to find the block supports panel via its CSS class.
+// Attempts to find the progressive disclosure panel via its CSS class.
const getPanel = ( container ) =>
- container.querySelector( '.components-block-support-panel' );
+ container.querySelector( '.components-progressive-disclosure-panel' );
-// Renders a default block supports panel including a control that simulates
-// block support being disabled for that particular control.
+// Renders a default progressive disclosure panel including a child that
+// is being conditionally disabled.
const renderPanel = () => {
return render(
-
+
{ false && Hidden
}
Example control
Alt control
-
+
);
};
@@ -72,11 +74,11 @@ const selectMenuItem = async ( label ) => {
fireEvent.click( menuItem );
};
-describe( 'BlockSupportPanel', () => {
+describe( 'ProgressiveDisclosurePanel', () => {
describe( 'basic rendering', () => {
it( 'should not render when no children provided', () => {
const { container } = render(
-
+
);
expect( getPanel( container ) ).not.toBeInTheDocument();
@@ -85,9 +87,9 @@ describe( 'BlockSupportPanel', () => {
it( 'should not render when only child has been filtered out', () => {
// This covers case where children prop is not an array.
const { container } = render(
-
+
{ false && Should not show }
-
+
);
expect( getPanel( container ) ).not.toBeInTheDocument();
@@ -95,10 +97,10 @@ describe( 'BlockSupportPanel', () => {
it( 'should not render when all children have been filtered out', () => {
const { container } = render(
-
+
{ false && Should not show }
{ false && Not shown either }
-
+
);
expect( getPanel( container ) ).not.toBeInTheDocument();
@@ -145,8 +147,8 @@ describe( 'BlockSupportPanel', () => {
} );
} );
- describe( 'conditional rendering of inner controls', () => {
- it( 'should render child control when it has a value', () => {
+ describe( 'conditional rendering of children', () => {
+ it( 'should render child when it has a value', () => {
renderPanel();
const exampleControl = screen.getByText( 'Example control' );
@@ -156,7 +158,7 @@ describe( 'BlockSupportPanel', () => {
expect( altControl ).not.toBeInTheDocument();
} );
- it( 'should render child control when corresponding menu is selected', async () => {
+ it( 'should render child when corresponding menu item is selected', async () => {
renderPanel();
await selectMenuItem( altControlProps.label );
const control = await screen.findByText( 'Alt control' );
@@ -164,7 +166,7 @@ describe( 'BlockSupportPanel', () => {
expect( control ).toBeInTheDocument();
} );
- it( 'should prevent child rendering when toggled off via menu', async () => {
+ it( 'should prevent child rendering when toggled off via menu item', async () => {
renderPanel();
await selectMenuItem( controlProps.label );
const control = screen.queryByText( 'Example control' );
@@ -173,24 +175,26 @@ describe( 'BlockSupportPanel', () => {
} );
} );
- describe( 'reset callbacks on menu item selection', () => {
+ describe( 'callbacks on menu item selection', () => {
beforeEach( () => {
jest.clearAllMocks();
controlProps.attributes.value = true;
} );
- it( 'should call reset callback when menu item is toggled off', async () => {
+ it( 'should call onDeselect callback when menu item is toggled off', async () => {
renderPanel();
await selectMenuItem( controlProps.label );
- expect( controlProps.reset ).toHaveBeenCalledTimes( 1 );
+ expect( controlProps.onSelect ).not.toHaveBeenCalled();
+ expect( controlProps.onDeselect ).toHaveBeenCalledTimes( 1 );
} );
- it( 'should not call reset callback when menu item is toggled on', async () => {
+ it( 'should call onSelect callback when menu item is toggled on', async () => {
renderPanel();
await selectMenuItem( altControlProps.label );
- expect( altControlProps.reset ).not.toHaveBeenCalled();
+ expect( altControlProps.onSelect ).toHaveBeenCalledTimes( 1 );
+ expect( altControlProps.onDeselect ).not.toHaveBeenCalled();
} );
it( 'should call resetAll callback when its menu item is selected', async () => {
diff --git a/packages/components/src/block-support-panel/title.js b/packages/components/src/progressive-disclosure-panel/title.js
similarity index 80%
rename from packages/components/src/block-support-panel/title.js
rename to packages/components/src/progressive-disclosure-panel/title.js
index 8383dc10897b7..f0cb6273c773a 100644
--- a/packages/components/src/block-support-panel/title.js
+++ b/packages/components/src/progressive-disclosure-panel/title.js
@@ -11,15 +11,15 @@ import MenuGroup from '../menu-group';
import MenuItem from '../menu-item';
import DropdownMenu from '../dropdown-menu';
-const BlockSupportPanelTitle = ( props ) => {
- const { menuItems = {}, menuLabel, resetAll, title, toggleControl } = props;
+const ProgressiveDisclosurePanelTitle = ( props ) => {
+ const { menuItems = {}, menuLabel, resetAll, title, toggleChild } = props;
if ( ! title ) {
return null;
}
return (
-
+
{ title }
{ ( { onClose } ) => (
@@ -33,7 +33,7 @@ const BlockSupportPanelTitle = ( props ) => {
icon={ isSelected && check }
isSelected={ isSelected }
onClick={ () => {
- toggleControl( label );
+ toggleChild( label );
onClose();
} }
role="menuitemcheckbox"
@@ -61,4 +61,4 @@ const BlockSupportPanelTitle = ( props ) => {
);
};
-export default BlockSupportPanelTitle;
+export default ProgressiveDisclosurePanelTitle;
diff --git a/packages/components/src/style.scss b/packages/components/src/style.scss
index 1d1d252ef27bc..60d3e98e4583e 100644
--- a/packages/components/src/style.scss
+++ b/packages/components/src/style.scss
@@ -1,6 +1,5 @@
@import "./animate/style.scss";
@import "./autocomplete/style.scss";
-@import "./block-support-panel/style.scss";
@import "./button-group/style.scss";
@import "./button/style.scss";
@import "./checkbox-control/style.scss";
@@ -31,6 +30,7 @@
@import "./panel/style.scss";
@import "./placeholder/style.scss";
@import "./popover/style.scss";
+@import "./progressive-disclosure-panel/style.scss";
@import "./radio-control/style.scss";
@import "./resizable-box/style.scss";
@import "./responsive-wrapper/style.scss";
From 0d75ba7681486edcd4a6c1458d567f305de28315 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Thu, 8 Jul 2021 14:48:22 +1000
Subject: [PATCH 18/50] Simplify progressive disclosure panel state
---
.../src/progressive-disclosure-panel/index.js | 60 ++++++-------------
1 file changed, 19 insertions(+), 41 deletions(-)
diff --git a/packages/components/src/progressive-disclosure-panel/index.js b/packages/components/src/progressive-disclosure-panel/index.js
index 371f8bcf58bab..3f785d59061de 100644
--- a/packages/components/src/progressive-disclosure-panel/index.js
+++ b/packages/components/src/progressive-disclosure-panel/index.js
@@ -7,7 +7,7 @@ import noop from 'lodash';
/**
* WordPress dependencies
*/
-import { useEffect, useState } from '@wordpress/element';
+import { useEffect, useMemo, useState } from '@wordpress/element';
/**
* Internal dependencies
@@ -17,55 +17,30 @@ import ProgressiveDisclosurePanelTitle from './title';
const ProgressiveDisclosurePanel = ( props ) => {
const { children, className, label: menuLabel, resetAll, title } = props;
const [ menuItems, setMenuItems ] = useState( {} );
- const [ defaultChildren, setDefaultChildren ] = useState( {} );
// When conditionally including components e.g. { isShown && }
// a boolean `false` will be passed as a child if component is excluded.
// This panel is only interested in the children to be displayed.
- const filteredChildren = Array.isArray( children )
- ? children.filter( Boolean )
- : [];
-
- // Collect which children have custom values. Used to update menu state to
- // reflect customization for children that display by default / always show.
- const customizedChildren = filteredChildren.map( ( child ) =>
- child.props.hasValue( child.props ) ? child.props.label : undefined
- );
+ const filteredChildren = useMemo( () => {
+ return Array.isArray( children ) ? children.filter( Boolean ) : [];
+ }, [ children ] );
- // On first render determine initial menu state and which children should
- // always display by default.
+ // Refresh which children should be reflected in the menu and what their
+ // associated menu item's state is; checked or not.
useEffect( () => {
const items = {};
- const defaults = {};
filteredChildren.forEach( ( child ) => {
- items[ child.props.label ] = child.props.hasValue( child.props );
- defaults[ child.props.label ] = child.props.isShownByDefault;
+ // New item is checked if:
+ // - it currently has a value
+ // - or it was checked in previous menuItems state.
+ items[ child.props.label ] =
+ child.props.hasValue( child.props ) ||
+ menuItems[ child.props.label ];
} );
setMenuItems( items );
- setDefaultChildren( defaults );
- }, [] );
-
- // As the default children are visible all the time. Reflect their
- // customizations in the menu items' selected state.
- useEffect( () => {
- const menuLabels = Object.keys( menuItems );
-
- // Skip if no children or menu state not initialized.
- if ( menuLabels.length === 0 ) {
- return;
- }
-
- const updatedItems = { ...menuItems };
- menuLabels.forEach( ( label ) => {
- if ( defaultChildren[ label ] ) {
- updatedItems[ label ] = customizedChildren.includes( label );
- }
- } );
-
- setMenuItems( updatedItems );
- }, customizedChildren );
+ }, [ filteredChildren ] );
if ( filteredChildren.length === 0 ) {
return null;
@@ -98,9 +73,12 @@ const ProgressiveDisclosurePanel = ( props ) => {
// Resets display of children and executes resetAll callback if available.
const resetAllChildren = () => {
- resetAll();
+ if ( typeof resetAll === 'function' ) {
+ resetAll();
+ }
- // Turn off menu items unless they are to display by default.
+ // Turn off all menu items. Default controls will continue to display
+ // by virtue of their `isShownByDefault` prop.
const resetMenuItems = {};
filteredChildren.forEach( ( child ) => {
@@ -129,7 +107,7 @@ const ProgressiveDisclosurePanel = ( props ) => {
// set to display by default.
const isShown =
menuItems[ child.props.label ] ||
- defaultChildren[ child.props.label ];
+ child.props.isShownByDefault;
return isShown ? child : null;
} ) }
From ffedb612123b4262e101d9370ed94e74a4d7a6af Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Thu, 8 Jul 2021 16:36:05 +1000
Subject: [PATCH 19/50] Add progressive disclosure panel item component
This utilises a new context for the panel's menu items. The menu in the panel title is generated from this and the individual items control their display based of if the item is selected in that menu item context.
---
packages/block-editor/src/hooks/dimensions.js | 27 ++++----
packages/components/src/index.js | 1 +
.../src/progressive-disclosure-panel/index.js | 61 ++++++++++---------
.../src/progressive-disclosure-panel/item.js | 23 +++++++
.../src/progressive-disclosure-panel/title.js | 4 +-
5 files changed, 76 insertions(+), 40 deletions(-)
create mode 100644 packages/components/src/progressive-disclosure-panel/item.js
diff --git a/packages/block-editor/src/hooks/dimensions.js b/packages/block-editor/src/hooks/dimensions.js
index 290cb5ce227c8..57ce3a764b31e 100644
--- a/packages/block-editor/src/hooks/dimensions.js
+++ b/packages/block-editor/src/hooks/dimensions.js
@@ -1,7 +1,10 @@
/**
* WordPress dependencies
*/
-import { __experimentalProgressiveDisclosurePanel as ProgressiveDisclosurePanel } from '@wordpress/components';
+import {
+ __experimentalProgressiveDisclosurePanel as ProgressiveDisclosurePanel,
+ __experimentalProgressiveDisclosurePanelItem as ProgressiveDisclosurePanelItem,
+} from '@wordpress/components';
import { Platform } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { getBlockSupport } from '@wordpress/blocks';
@@ -73,22 +76,24 @@ export function DimensionsPanel( props ) {
resetAll={ resetAll }
>
{ ! isPaddingDisabled && (
- hasPaddingValue( props ) }
label={ __( 'Padding' ) }
- onDeselect={ resetPadding }
+ onDeselect={ () => resetPadding( props ) }
isShownByDefault={ defaultSpacingControls?.padding }
- />
+ >
+
+
) }
{ ! isMarginDisabled && (
- hasMarginValue( props ) }
label={ __( 'Margin' ) }
- onDeselect={ resetMargin }
+ onDeselect={ () => resetMargin( props ) }
isShownByDefault={ defaultSpacingControls?.margin }
- />
+ >
+
+
) }
diff --git a/packages/components/src/index.js b/packages/components/src/index.js
index 540fbf2759518..7dc6c9f5e64fa 100644
--- a/packages/components/src/index.js
+++ b/packages/components/src/index.js
@@ -101,6 +101,7 @@ export { default as PanelRow } from './panel/row';
export { default as Placeholder } from './placeholder';
export { default as Popover } from './popover';
export { default as __experimentalProgressiveDisclosurePanel } from './progressive-disclosure-panel';
+export { default as __experimentalProgressiveDisclosurePanelItem } from './progressive-disclosure-panel/item';
export { default as QueryControls } from './query-controls';
export { default as __experimentalRadio } from './radio';
export { default as __experimentalRadioGroup } from './radio-group';
diff --git a/packages/components/src/progressive-disclosure-panel/index.js b/packages/components/src/progressive-disclosure-panel/index.js
index 3f785d59061de..eba21a8b56c95 100644
--- a/packages/components/src/progressive-disclosure-panel/index.js
+++ b/packages/components/src/progressive-disclosure-panel/index.js
@@ -2,18 +2,30 @@
* External dependencies
*/
import classnames from 'classnames';
-import noop from 'lodash';
/**
* WordPress dependencies
*/
-import { useEffect, useMemo, useState } from '@wordpress/element';
+import {
+ createContext,
+ useContext,
+ useEffect,
+ useMemo,
+ useState,
+} from '@wordpress/element';
/**
* Internal dependencies
*/
+import ProgressiveDisclosurePanelItem from './item';
import ProgressiveDisclosurePanelTitle from './title';
+const PanelContext = createContext( {} );
+
+export const usePanelContext = () => useContext( PanelContext );
+
+const isMenuItem = ( item ) => item.type === ProgressiveDisclosurePanelItem;
+
const ProgressiveDisclosurePanel = ( props ) => {
const { children, className, label: menuLabel, resetAll, title } = props;
const [ menuItems, setMenuItems ] = useState( {} );
@@ -22,7 +34,7 @@ const ProgressiveDisclosurePanel = ( props ) => {
// a boolean `false` will be passed as a child if component is excluded.
// This panel is only interested in the children to be displayed.
const filteredChildren = useMemo( () => {
- return Array.isArray( children ) ? children.filter( Boolean ) : [];
+ return Array.isArray( children ) ? children.filter( isMenuItem ) : [];
}, [ children ] );
// Refresh which children should be reflected in the menu and what their
@@ -30,13 +42,11 @@ const ProgressiveDisclosurePanel = ( props ) => {
useEffect( () => {
const items = {};
- filteredChildren.forEach( ( child ) => {
+ filteredChildren.forEach( ( { props: { hasValue, label } } ) => {
// New item is checked if:
// - it currently has a value
// - or it was checked in previous menuItems state.
- items[ child.props.label ] =
- child.props.hasValue( child.props ) ||
- menuItems[ child.props.label ];
+ items[ label ] = hasValue() || menuItems[ label ];
} );
setMenuItems( items );
@@ -57,12 +67,14 @@ const ProgressiveDisclosurePanel = ( props ) => {
const toggleChild = ( label ) => {
const wasSelected = menuItems[ label ];
const child = getChildByMenuLabel( label );
- const { onDeselect = noop, onSelect = noop } = child.props;
+ const { onDeselect, onSelect } = child.props;
+
+ if ( wasSelected && onDeselect ) {
+ onDeselect();
+ }
- if ( wasSelected ) {
- onDeselect( child.props );
- } else {
- onSelect( child.props );
+ if ( ! wasSelected && onSelect ) {
+ onSelect();
}
setMenuItems( {
@@ -95,22 +107,15 @@ const ProgressiveDisclosurePanel = ( props ) => {
return (
-
- { filteredChildren.map( ( child ) => {
- // Only display the child if it is toggled on in the menu or is
- // set to display by default.
- const isShown =
- menuItems[ child.props.label ] ||
- child.props.isShownByDefault;
-
- return isShown ? child : null;
- } ) }
+
+
+ { children }
+
);
};
diff --git a/packages/components/src/progressive-disclosure-panel/item.js b/packages/components/src/progressive-disclosure-panel/item.js
new file mode 100644
index 0000000000000..5cba0f2f5f7c9
--- /dev/null
+++ b/packages/components/src/progressive-disclosure-panel/item.js
@@ -0,0 +1,23 @@
+/**
+ * Internal dependencies
+ */
+import { usePanelContext } from './';
+
+// This wraps controls to be conditionally displayed within a progressive
+// disclosure panel. It helps prevent props being applied to HTML elements that
+// would otherwise be invalid.
+const ProgressiveDisclosurePanelItem = ( props ) => {
+ const { children, isShownByDefault, label } = props;
+ const menuItems = usePanelContext();
+
+ // Do not show if menu item not selected and not shown by default.
+ // If the item has a value that will be reflected in the menu item's
+ // selected status provided by context.
+ if ( ! menuItems[ label ] && ! isShownByDefault ) {
+ return null;
+ }
+
+ return children;
+};
+
+export default ProgressiveDisclosurePanelItem;
diff --git a/packages/components/src/progressive-disclosure-panel/title.js b/packages/components/src/progressive-disclosure-panel/title.js
index f0cb6273c773a..7f29f80d002e5 100644
--- a/packages/components/src/progressive-disclosure-panel/title.js
+++ b/packages/components/src/progressive-disclosure-panel/title.js
@@ -7,12 +7,14 @@ import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
+import { usePanelContext } from './';
import MenuGroup from '../menu-group';
import MenuItem from '../menu-item';
import DropdownMenu from '../dropdown-menu';
const ProgressiveDisclosurePanelTitle = ( props ) => {
- const { menuItems = {}, menuLabel, resetAll, title, toggleChild } = props;
+ const { menuLabel, resetAll, title, toggleChild } = props;
+ const menuItems = usePanelContext();
if ( ! title ) {
return null;
From ae4a3b23c60c3d71a09ce883350a61a758d306dc Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Thu, 8 Jul 2021 17:06:59 +1000
Subject: [PATCH 20/50] Update dimensions global style sidebar panel
---
.../components/sidebar/dimensions-panel.js | 51 +++++++++++--------
1 file changed, 30 insertions(+), 21 deletions(-)
diff --git a/packages/edit-site/src/components/sidebar/dimensions-panel.js b/packages/edit-site/src/components/sidebar/dimensions-panel.js
index d6df96c0a83ed..abea542bacd08 100644
--- a/packages/edit-site/src/components/sidebar/dimensions-panel.js
+++ b/packages/edit-site/src/components/sidebar/dimensions-panel.js
@@ -3,7 +3,8 @@
*/
import { __ } from '@wordpress/i18n';
import {
- __experimentalBlockSupportPanel as BlockSupportPanel,
+ __experimentalProgressiveDisclosurePanel as ProgressiveDisclosurePanel,
+ __experimentalProgressiveDisclosurePanelItem as ProgressiveDisclosurePanelItem,
__experimentalBoxControl as BoxControl,
__experimentalUseCustomUnits as useCustomUnits,
} from '@wordpress/components';
@@ -103,37 +104,45 @@ export default function DimensionsPanel( { context, getStyle, setStyle } ) {
};
return (
-
{ showPaddingControl && (
-
+ >
+
+
) }
{ showMarginControl && (
-
+ >
+
+
) }
-
+
);
}
From e7a89ea533b93bbd385a67391d3dd9664e5bf19a Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Mon, 12 Jul 2021 18:47:17 +1000
Subject: [PATCH 21/50] Update docs and comments after restructure
---
.../progressive-disclosure-panel/README.md | 68 ++++++++-----------
.../src/progressive-disclosure-panel/index.js | 5 +-
2 files changed, 31 insertions(+), 42 deletions(-)
diff --git a/packages/components/src/progressive-disclosure-panel/README.md b/packages/components/src/progressive-disclosure-panel/README.md
index f56fd865ee285..350f49db58da4 100644
--- a/packages/components/src/progressive-disclosure-panel/README.md
+++ b/packages/components/src/progressive-disclosure-panel/README.md
@@ -6,7 +6,9 @@ example the controls provided via block supports.
## Development guidelines
The `ProgressiveDisclosurePanel` creates a container with a header including a
-dropdown menu. The menu is generated automatically from the panel's children.
+dropdown menu. The menu is generated automatically from the panel's children
+matching the `ProgressiveDisclosurePanelItem` component type.
+
Each menu item allows for the display of the corresponding child to be
toggled on or off. The control's `onSelect` and `onDeselect` callbacks are fired
allowing for greater control over the child e.g. resetting block attributes when
@@ -21,7 +23,12 @@ child's props.
### Usage
```jsx
-import { __experimentalProgressiveDisclosurePanel as ProgressiveDisclosurePanel } from '@wordpress/components';
+import {
+ __experimentalProgressiveDisclosurePanel as ProgressiveDisclosurePanel,
+ __experimentalProgressiveDisclosurePanelItem as ProgressiveDisclosurePanelItem,
+} from '@wordpress/components';
+import { __ } from '@wordpress/i18n';
+
import {
PaddingEdit,
hasPaddingValue,
@@ -44,12 +51,13 @@ export function DimensionPanel( props ) {
resetAll={ resetAll }
>
{ ! isPaddingDisabled && (
- hasPaddingValue( props ) }
label={ __( 'Padding' ) }
- onDeselect={ resetPadding }
- />
+ onDeselect={ () => resetPadding( props ) }
+ >
+
+
) }
);
@@ -73,45 +81,27 @@ Title to be displayed within the panel's title.
### Sub-Components
-#### ProgressiveDisclosurePanelTitle
+#### ProgressiveDisclosurePanelItem
-This is a simple component to display the panel title and house the dropdown
-menu for toggling child display. It is used by the `ProgressiveDisclosurePanel`
-component under the hood, so it does not typically need to be used.
+This component acts a wrapper and controls the display of items to contained
+within a ProgressiveDisclosurePanel. An item is displayed if it is
+flagged as a default control or the corresponding panel menu item, provided via
+context, is toggled on for this item.
##### Props
-###### resetAll
+###### isShownByDefault
-A function to call when the `Reset all` menu option is selected.
+This prop identifies the current item as being displayed by default. This means
+it will show regardless of whether it has a value set or is toggled on in the
+panel's menu.
-- Type: `function`
+- Type: `boolean`
- Required: Yes
-###### toggleChild
+###### label
-Callback used to toggle display of an individual child component.
+The label acts as a key to locate the corresponding item in the panel's menu
+context. This is used when checking if the panel item should be displayed.
-- Type: `function`
+- Type: `string`
- Required: Yes
-
-###### menuItems
-
-This object represents the panel's children and their visibility state. It
-is built by the parent panel from its children prop.
-
-- Type: `Object`
-- Required: No
-
-###### menuLabel
-
-A label for the dropdown menu.
-
-- Type: `String`
-- Required: No
-
-###### title
-
-The panel title to display.
-
-- Type: `String`
-- Required: No
diff --git a/packages/components/src/progressive-disclosure-panel/index.js b/packages/components/src/progressive-disclosure-panel/index.js
index eba21a8b56c95..45c9c410ec4b6 100644
--- a/packages/components/src/progressive-disclosure-panel/index.js
+++ b/packages/components/src/progressive-disclosure-panel/index.js
@@ -30,9 +30,8 @@ const ProgressiveDisclosurePanel = ( props ) => {
const { children, className, label: menuLabel, resetAll, title } = props;
const [ menuItems, setMenuItems ] = useState( {} );
- // When conditionally including components e.g. { isShown && }
- // a boolean `false` will be passed as a child if component is excluded.
- // This panel is only interested in the children to be displayed.
+ // This panel only needs to concern itself with the
+ // ProgressiveDisclosurePanelItem components to be displayed in the menu.
const filteredChildren = useMemo( () => {
return Array.isArray( children ) ? children.filter( isMenuItem ) : [];
}, [ children ] );
From 07420bc5c142c74c37c291fc463d7b70a87bbcff Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Mon, 12 Jul 2021 18:56:17 +1000
Subject: [PATCH 22/50] Update panel story with new item component
---
.../stories/index.js | 27 ++++++++++++-------
1 file changed, 18 insertions(+), 9 deletions(-)
diff --git a/packages/components/src/progressive-disclosure-panel/stories/index.js b/packages/components/src/progressive-disclosure-panel/stories/index.js
index a8525aa25caf5..f081cb68e97d4 100644
--- a/packages/components/src/progressive-disclosure-panel/stories/index.js
+++ b/packages/components/src/progressive-disclosure-panel/stories/index.js
@@ -12,6 +12,7 @@ import { useState } from '@wordpress/element';
* Internal dependencies
*/
import ProgressiveDisclosurePanel from '../';
+import ProgressiveDisclosurePanelItem from '../item';
import Panel from '../../panel';
import UnitControl from '../../unit-control';
@@ -37,20 +38,28 @@ export const _default = () => {
title="Progressive Disclosure Panel"
resetAll={ resetAll }
>
- !! height }
label="Height"
onDeselect={ () => setHeight( undefined ) }
- value={ height }
- onChange={ ( next ) => setHeight( next ) }
- />
-
+ setHeight( next ) }
+ />
+
+ !! width }
label="Width"
onDeselect={ () => setWidth( undefined ) }
- value={ width }
- onChange={ ( next ) => setWidth( next ) }
- />
+ >
+ setWidth( next ) }
+ />
+
@@ -64,6 +73,6 @@ function PlaceholderControl( { label, value, onChange } ) {
}
const PanelWrapperView = styled.div`
- max-width: 232px;
+ max-width: 250px;
font-size: 13px;
`;
From 9ec400faf5feabeddfea81510c2d55b759900730 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Mon, 12 Jul 2021 20:44:08 +1000
Subject: [PATCH 23/50] Update tests to handle new panel item component
---
.../test/index.js | 65 ++++++++++++++-----
.../src/progressive-disclosure-panel/title.js | 2 +-
2 files changed, 48 insertions(+), 19 deletions(-)
diff --git a/packages/components/src/progressive-disclosure-panel/test/index.js b/packages/components/src/progressive-disclosure-panel/test/index.js
index 3fff561a758a8..866aa1305e3fa 100644
--- a/packages/components/src/progressive-disclosure-panel/test/index.js
+++ b/packages/components/src/progressive-disclosure-panel/test/index.js
@@ -7,15 +7,9 @@ import { render, screen, fireEvent } from '@testing-library/react';
* Internal dependencies
*/
import ProgressiveDisclosurePanel from '../';
-
-// Represents a child provided to the panel such as a block support control.
-const MockControl = ( { children } ) => { children }
;
+import PanelItem from '../item';
const resetAll = jest.fn();
-const hasValue = jest.fn().mockImplementation( ( props ) => {
- // Use fudged attribute to determine existence of value for testing.
- return props.attributes.value;
-} );
// Default props for the progressive disclosure panel.
const defaultProps = {
@@ -27,7 +21,9 @@ const defaultProps = {
// Default props for an enabled control to be rendered within panel.
const controlProps = {
attributes: { value: true },
- hasValue,
+ hasValue: jest.fn().mockImplementation( () => {
+ return !! controlProps.attributes.value;
+ } ),
label: 'Example',
onDeselect: jest.fn().mockImplementation( () => {
controlProps.attributes.value = undefined;
@@ -39,7 +35,9 @@ const controlProps = {
// the panel.
const altControlProps = {
attributes: { value: false },
- hasValue,
+ hasValue: jest.fn().mockImplementation( () => {
+ return !! altControlProps.attributes.value;
+ } ),
label: 'Alt',
onDeselect: jest.fn(),
onSelect: jest.fn(),
@@ -49,14 +47,19 @@ const altControlProps = {
const getPanel = ( container ) =>
container.querySelector( '.components-progressive-disclosure-panel' );
-// Renders a default progressive disclosure panel including a child that
-// is being conditionally disabled.
+// Renders a default progressive disclosure panel including children that are
+// not to be represented within the panel's menu.
const renderPanel = () => {
return render(
{ false && Hidden
}
- Example control
- Alt control
+
+ Example control
+
+
+ Alt control
+
+ Visible
);
};
@@ -88,30 +91,56 @@ describe( 'ProgressiveDisclosurePanel', () => {
// This covers case where children prop is not an array.
const { container } = render(
- { false && Should not show }
+ { false && Should not show }
);
expect( getPanel( container ) ).not.toBeInTheDocument();
} );
- it( 'should not render when all children have been filtered out', () => {
+ it( 'should not render when there are no progressive panel items', () => {
const { container } = render(
- { false && Should not show }
- { false && Not shown either }
+ { false && Should not show }
+ { false && Not shown either }
+ Visible but insignificant
);
expect( getPanel( container ) ).not.toBeInTheDocument();
} );
- it( 'should render panel when at least one child', () => {
+ it( 'should render panel when at least one panel item as child', () => {
const { container } = renderPanel();
expect( getPanel( container ) ).toBeInTheDocument();
} );
+ it( 'should render non panel item child', () => {
+ renderPanel();
+
+ const nonPanelItem = screen.queryByText( 'Visible' );
+
+ expect( nonPanelItem ).toBeInTheDocument();
+ } );
+
+ it( 'should render child flagged as default control even without value', () => {
+ render(
+
+
+ Example control
+
+
+ Alt control
+
+
+ );
+
+ const altControl = screen.getByText( 'Alt control' );
+
+ expect( altControl ).toBeInTheDocument();
+ } );
+
it( 'should render display options menu', () => {
renderPanel();
diff --git a/packages/components/src/progressive-disclosure-panel/title.js b/packages/components/src/progressive-disclosure-panel/title.js
index 7f29f80d002e5..636ed67a872e2 100644
--- a/packages/components/src/progressive-disclosure-panel/title.js
+++ b/packages/components/src/progressive-disclosure-panel/title.js
@@ -33,7 +33,7 @@ const ProgressiveDisclosurePanelTitle = ( props ) => {