diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md
index d7e78770b56c07..24b8589aacef4c 100644
--- a/packages/components/CHANGELOG.md
+++ b/packages/components/CHANGELOG.md
@@ -4,6 +4,7 @@
### Bug Fix
+- `ToolsPanel`: fix type inconsistencies between types, docs and normal component usage ([47944](https://github.com/WordPress/gutenberg/pull/47944)).
- `SelectControl`: Fix styling when `multiple` prop is enabled ([#47893](https://github.com/WordPress/gutenberg/pull/43213)).
### Enhancements
@@ -28,6 +29,7 @@
- `ToolsPanel`: Allow display of optional items when values are updated externally to item controls ([47727](https://github.com/WordPress/gutenberg/pull/47727)).
- `ToolsPanel`: Ensure display of optional items when values are updated externally and multiple blocks selected ([47864](https://github.com/WordPress/gutenberg/pull/47864)).
- `Navigator`: add more pattern matching tests, refine existing tests ([47910](https://github.com/WordPress/gutenberg/pull/47910)).
+- `ToolsPanel`: Refactor Storybook examples to TypeScript ([47944](https://github.com/WordPress/gutenberg/pull/47944)).
## 23.3.0 (2023-02-01)
diff --git a/packages/components/src/tools-panel/stories/index.js b/packages/components/src/tools-panel/stories/index.tsx
similarity index 60%
rename from packages/components/src/tools-panel/stories/index.js
rename to packages/components/src/tools-panel/stories/index.tsx
index 0ff9824406a914..52c89a6ece5d66 100644
--- a/packages/components/src/tools-panel/stories/index.js
+++ b/packages/components/src/tools-panel/stories/index.tsx
@@ -1,50 +1,73 @@
/**
* External dependencies
*/
+import type { ComponentMeta, ComponentStory } from '@storybook/react';
import styled from '@emotion/styled';
/**
* WordPress dependencies
*/
import { useState } from '@wordpress/element';
+
+/**
+ * Internal dependencies
+ */
import {
- __experimentalToggleGroupControl as ToggleGroupControl,
- __experimentalToggleGroupControlOption as ToggleGroupControlOption,
-} from '@wordpress/components';
+ ToggleGroupControl,
+ ToggleGroupControlOption,
+} from '../../toggle-group-control';
/**
* Internal dependencies
*/
-import { ToolsPanel, ToolsPanelItem } from '../';
+import { ToolsPanel, ToolsPanelItem } from '..';
import Panel from '../../panel';
import UnitControl from '../../unit-control';
import { createSlotFill, Provider as SlotFillProvider } from '../../slot-fill';
-export default {
+const meta: ComponentMeta< typeof ToolsPanel > = {
title: 'Components (Experimental)/ToolsPanel',
component: ToolsPanel,
+ subcomponents: {
+ ToolsPanelItem,
+ },
+ argTypes: {
+ as: { control: { type: null } },
+ children: { control: { type: null } },
+ panelId: { control: { type: null } },
+ resetAll: { action: 'resetAll' },
+ },
+ parameters: {
+ actions: { argTypesRegex: '^on.*' },
+ controls: {
+ expanded: true,
+ },
+ docs: { source: { state: 'open' } },
+ },
};
-
-export const _default = () => {
- const [ height, setHeight ] = useState();
- const [ minHeight, setMinHeight ] = useState();
- const [ width, setWidth ] = useState();
- const [ scale, setScale ] = useState();
-
- const resetAll = () => {
+export default meta;
+
+export const Default: ComponentStory< typeof ToolsPanel > = ( {
+ resetAll: resetAllProp,
+ ...props
+} ) => {
+ const [ height, setHeight ] = useState< string | undefined >();
+ const [ minHeight, setMinHeight ] = useState< string | undefined >();
+ const [ width, setWidth ] = useState< string | undefined >();
+ const [ scale, setScale ] = useState< React.ReactText | undefined >();
+
+ const resetAll: typeof resetAllProp = ( filters ) => {
setHeight( undefined );
setWidth( undefined );
setMinHeight( undefined );
setScale( undefined );
+ resetAllProp( filters );
};
return (
-
+
!! width }
label="Width"
@@ -112,23 +135,27 @@ export const _default = () => {
);
};
+Default.args = {
+ label: 'Tools Panel (default example)',
+};
-export const WithNonToolsPanelItems = () => {
- const [ height, setHeight ] = useState();
- const [ width, setWidth ] = useState();
+export const WithNonToolsPanelItems: ComponentStory< typeof ToolsPanel > = ( {
+ resetAll: resetAllProp,
+ ...props
+} ) => {
+ const [ height, setHeight ] = useState< string | undefined >();
+ const [ width, setWidth ] = useState< string | undefined >();
- const resetAll = () => {
+ const resetAll: typeof resetAllProp = ( filters ) => {
setHeight( undefined );
setWidth( undefined );
+ resetAllProp( filters );
};
return (
-
+
This text illustrates not all items must be wrapped in a
ToolsPanelItem and represented in the panel menu.
@@ -162,81 +189,127 @@ export const WithNonToolsPanelItems = () => {
);
};
+WithNonToolsPanelItems.args = {
+ ...Default.args,
+ label: 'ToolsPanel (with non-menu items)',
+};
-export const WithOptionalItemsPlusIcon = ( { isShownByDefault } ) => {
- const [ height, setHeight ] = useState();
- const [ width, setWidth ] = useState();
- const [ minWidth, setMinWidth ] = useState();
-
- const resetAll = () => {
+export const WithOptionalItemsPlusIcon: ComponentStory<
+ typeof ToolsPanel
+> = ( { resetAll: resetAllProp, ...props } ) => {
+ const [
+ isFirstToolsPanelItemShownByDefault,
+ setIsFirstToolsPanelItemShownByDefault,
+ ] = useState( false );
+ const [ height, setHeight ] = useState< string | undefined >();
+ const [ width, setWidth ] = useState< string | undefined >();
+ const [ minWidth, setMinWidth ] = useState< string | undefined >();
+
+ const resetAll: typeof resetAllProp = ( filters ) => {
setHeight( undefined );
setWidth( undefined );
setMinWidth( undefined );
+ resetAllProp( filters );
};
return (
-
-
-
- !! minWidth }
- label="Minimum width"
- onDeselect={ () => setMinWidth( undefined ) }
- isShownByDefault={ isShownByDefault }
+ <>
+
+
+
- !! minWidth }
label="Minimum width"
- value={ minWidth }
- onChange={ ( next ) => setMinWidth( next ) }
- />
-
- !! width }
- label="Width"
- onDeselect={ () => setWidth( undefined ) }
- isShownByDefault={ false }
- >
- setMinWidth( undefined ) }
+ isShownByDefault={
+ isFirstToolsPanelItemShownByDefault
+ }
+ >
+ setMinWidth( next ) }
+ />
+
+ !! width }
label="Width"
- value={ width }
- onChange={ ( next ) => setWidth( next ) }
- />
-
- !! height }
- label="Height"
- onDeselect={ () => setHeight( undefined ) }
- isShownByDefault={ false }
- >
- setWidth( undefined ) }
+ isShownByDefault={ false }
+ >
+ setWidth( next ) }
+ />
+
+ !! height }
label="Height"
- value={ height }
- onChange={ ( next ) => setHeight( next ) }
- />
-
-
-
-
+ onDeselect={ () => setHeight( undefined ) }
+ isShownByDefault={ false }
+ >
+ setHeight( next ) }
+ />
+
+
+
+
+
+
+ >
);
};
WithOptionalItemsPlusIcon.args = {
- isShownByDefault: false,
+ ...Default.args,
+ label: 'Tools Panel (optional items only)',
};
const { Fill: ToolsPanelItems, Slot } = createSlotFill( 'ToolsPanelSlot' );
-const panelId = 'unique-tools-panel-id';
-export const WithSlotFillItems = () => {
- const [ attributes, setAttributes ] = useState( {} );
+export const WithSlotFillItems: ComponentStory< typeof ToolsPanel > = ( {
+ resetAll: resetAllProp,
+ panelId,
+ ...props
+} ) => {
+ const [ attributes, setAttributes ] = useState< {
+ width?: string;
+ height?: string;
+ } >( {} );
const { width, height } = attributes;
- const resetAll = ( resetFilters = [] ) => {
- let newAttributes = {};
+ const resetAll: typeof resetAllProp = ( resetFilters = [] ) => {
+ let newAttributes: typeof attributes = {};
resetFilters.forEach( ( resetFilter ) => {
newAttributes = {
@@ -246,9 +319,10 @@ export const WithSlotFillItems = () => {
} );
setAttributes( newAttributes );
+ resetAllProp( resetFilters );
};
- const updateAttribute = ( name, value ) => {
+ const updateAttribute = ( name: string, value?: any ) => {
setAttributes( {
...attributes,
[ name ]: value,
@@ -304,7 +378,7 @@ export const WithSlotFillItems = () => {
@@ -315,13 +389,23 @@ export const WithSlotFillItems = () => {
);
};
+WithSlotFillItems.args = {
+ ...Default.args,
+ label: 'Tools Panel With SlotFill Items',
+ panelId: 'unique-tools-panel-id',
+};
-export const WithConditionalDefaultControl = () => {
- const [ attributes, setAttributes ] = useState( {} );
+export const WithConditionalDefaultControl: ComponentStory<
+ typeof ToolsPanel
+> = ( { resetAll: resetAllProp, panelId, ...props } ) => {
+ const [ attributes, setAttributes ] = useState< {
+ height?: string;
+ scale?: React.ReactText;
+ } >( {} );
const { height, scale } = attributes;
- const resetAll = ( resetFilters = [] ) => {
- let newAttributes = {};
+ const resetAll: typeof resetAllProp = ( resetFilters = [] ) => {
+ let newAttributes: typeof attributes = {};
resetFilters.forEach( ( resetFilter ) => {
newAttributes = {
@@ -331,9 +415,11 @@ export const WithConditionalDefaultControl = () => {
} );
setAttributes( newAttributes );
+
+ resetAllProp( resetFilters );
};
- const updateAttribute = ( name, value ) => {
+ const updateAttribute = ( name: string, value?: any ) => {
setAttributes( {
...attributes,
[ name ]: value,
@@ -388,7 +474,7 @@ export const WithConditionalDefaultControl = () => {
@@ -399,13 +485,23 @@ export const WithConditionalDefaultControl = () => {
);
};
+WithConditionalDefaultControl.args = {
+ ...Default.args,
+ label: 'Tools Panel With Conditional Default via SlotFill',
+ panelId: 'unique-tools-panel-id',
+};
-export const WithConditionallyRenderedControl = () => {
- const [ attributes, setAttributes ] = useState( {} );
+export const WithConditionallyRenderedControl: ComponentStory<
+ typeof ToolsPanel
+> = ( { resetAll: resetAllProp, panelId, ...props } ) => {
+ const [ attributes, setAttributes ] = useState< {
+ height?: string;
+ scale?: React.ReactText;
+ } >( {} );
const { height, scale } = attributes;
- const resetAll = ( resetFilters = [] ) => {
- let newAttributes = {};
+ const resetAll: typeof resetAllProp = ( resetFilters = [] ) => {
+ let newAttributes: typeof attributes = {};
resetFilters.forEach( ( resetFilter ) => {
newAttributes = {
@@ -415,9 +511,11 @@ export const WithConditionallyRenderedControl = () => {
} );
setAttributes( newAttributes );
+
+ resetAllProp( resetFilters );
};
- const updateAttribute = ( name, value ) => {
+ const updateAttribute = ( name: string, value?: any ) => {
setAttributes( {
...attributes,
[ name ]: value,
@@ -485,7 +583,7 @@ export const WithConditionallyRenderedControl = () => {
@@ -496,8 +594,11 @@ export const WithConditionallyRenderedControl = () => {
);
};
-
-export { ToolsPanelWithItemGroupSlot } from './utils/tools-panel-with-item-group-slot';
+WithConditionallyRenderedControl.args = {
+ ...Default.args,
+ label: 'Tools Panel With Conditionally Rendered Item via SlotFill',
+ panelId: 'unique-tools-panel-id',
+};
const PanelWrapperView = styled.div`
font-size: 13px;
diff --git a/packages/components/src/tools-panel/stories/utils/tools-panel-with-item-group-slot.js b/packages/components/src/tools-panel/stories/utils/tools-panel-with-item-group-slot.js
deleted file mode 100644
index d1895324df4c3b..00000000000000
--- a/packages/components/src/tools-panel/stories/utils/tools-panel-with-item-group-slot.js
+++ /dev/null
@@ -1,246 +0,0 @@
-/**
- * External dependencies
- */
-import styled from '@emotion/styled';
-import { css } from '@emotion/react';
-
-/**
- * WordPress dependencies
- */
-import { useContext, useState } from '@wordpress/element';
-
-/**
- * Internal dependencies
- */
-import Button from '../../../button';
-import ColorIndicator from '../../../color-indicator';
-import ColorPalette from '../../../color-palette';
-import Dropdown from '../../../dropdown';
-import Panel from '../../../panel';
-import { FlexItem } from '../../../flex';
-import { HStack } from '../../../h-stack';
-import { Item, ItemGroup } from '../../../item-group';
-import { ToolsPanel, ToolsPanelItem, ToolsPanelContext } from '../..';
-import {
- createSlotFill,
- Provider as SlotFillProvider,
-} from '../../../slot-fill';
-import { useCx } from '../../../utils';
-
-// Available border colors.
-const colors = [
- { name: 'Gray 0', color: '#f6f7f7' },
- { name: 'Gray 5', color: '#dcdcde' },
- { name: 'Gray 20', color: '#a7aaad' },
- { name: 'Gray 70', color: '#3c434a' },
- { name: 'Gray 100', color: '#101517' },
- { name: 'Blue 20', color: '#72aee6' },
- { name: 'Blue 40', color: '#3582c4' },
- { name: 'Blue 70', color: '#0a4b78' },
- { name: 'Red 40', color: '#e65054' },
- { name: 'Red 70', color: '#8a2424' },
- { name: 'Green 10', color: '#68de7c' },
- { name: 'Green 40', color: '#00a32a' },
- { name: 'Green 60', color: '#007017' },
- { name: 'Yellow 10', color: '#f2d675' },
- { name: 'Yellow 40', color: '#bd8600' },
-];
-const panelId = 'unique-tools-panel-id';
-
-const { Fill, Slot } = createSlotFill( 'ToolsPanelSlot' );
-
-// This storybook example aims to replicate a virtual bubbling SlotFill use case
-// for the `ToolsPanel` when the Slot itself is an `ItemGroup`.
-
-// In this scenario the `ToolsPanel` has to render item placeholders so fills
-// maintain their order in the DOM. These placeholders in the DOM prevent the
-// normal styling of the `ItemGroup` in particular the border radii on the first
-// and last items. In case consumers of the ItemGroup and ToolsPanel are
-// applying their own styles to these components, the ToolsPanel needs to assist
-// consumers in identifying which of its visible items are first and last.
-
-// This custom fill is required to re-establish the ToolsPanelContext for
-// injected ToolsPanelItem components as they will not have access to the React
-// Context as the Provider is part of the ToolsPanelItems.Slot tree.
-const ToolsPanelItems = ( { children } ) => {
- return (
-
- { ( fillProps ) => (
-
- { children }
-
- ) }
-
- );
-};
-
-// This fetches the ToolsPanelContext and passes it through `fillProps` so that
-// rendered fills can re-establish the `ToolsPanelContext.Provider`.
-const SlotContainer = ( { Slot: ToolsPanelSlot, ...props } ) => {
- const toolsPanelContext = useContext( ToolsPanelContext );
-
- return (
-
- );
-};
-
-// This wraps the slot with a `ToolsPanel` mimicking a real-world use case from
-// the block editor.
-ToolsPanelItems.Slot = ( { resetAll, ...props } ) => (
-
-
-
-);
-
-export const ToolsPanelWithItemGroupSlot = () => {
- const [ attributes, setAttributes ] = useState( {} );
- const { text, background, link } = attributes;
-
- const cx = useCx();
- const slotWrapperClassName = cx( SlotWrapper );
- const itemClassName = cx( ToolsPanelItemClass );
-
- const resetAll = ( resetFilters = [] ) => {
- let newAttributes = {};
-
- resetFilters.forEach( ( resetFilter ) => {
- newAttributes = {
- ...newAttributes,
- ...resetFilter( newAttributes ),
- };
- } );
-
- setAttributes( newAttributes );
- };
-
- const updateAttribute = ( name, value ) => {
- setAttributes( {
- ...attributes,
- [ name ]: value,
- } );
- };
-
- const ToolsPanelColorDropdown = ( { attribute, label, value } ) => {
- return (
- !! value }
- label={ label }
- onDeselect={ () => updateAttribute( attribute, undefined ) }
- resetAllFilter={ () => ( { [ attribute ]: undefined } ) }
- panelId={ panelId }
- as={ Item }
- >
- (
-
- ) }
- renderContent={ () => (
-
- updateAttribute( attribute, newColor )
- }
- />
- ) }
- />
-
- );
- };
-
- // ToolsPanelItems are rendered via two different fills to simulate
- // injection from multiple locations.
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-};
-
-const PanelWrapperView = styled.div`
- font-size: 13px;
-
- .components-dropdown-menu__menu {
- max-width: 220px;
- }
-`;
-
-const SlotWrapper = css`
- &&& {
- row-gap: 0;
- border-radius: 20px;
- }
-
- > div {
- grid-column: span 2;
- border-radius: inherit;
- }
-`;
-
-const ToolsPanelItemClass = css`
- padding: 0;
-
- &&.first {
- border-top-left-radius: inherit;
- border-top-right-radius: inherit;
- }
-
- &.last {
- border-bottom-left-radius: inherit;
- border-bottom-right-radius: inherit;
- border-bottom-color: transparent;
- }
- && > div,
- && > div > button {
- width: 100%;
- border-radius: inherit;
- }
-`;
diff --git a/packages/components/src/tools-panel/tools-panel-item/README.md b/packages/components/src/tools-panel/tools-panel-item/README.md
index 85d03f96d1d04f..91f9c78ff9cbe8 100644
--- a/packages/components/src/tools-panel/tools-panel-item/README.md
+++ b/packages/components/src/tools-panel/tools-panel-item/README.md
@@ -31,7 +31,8 @@ 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.
-- Required: Yes
+- Required: No
+- Default: `false`
### `label`: `string`
@@ -58,18 +59,19 @@ A callback to take action when this item is selected in the `ToolsPanel` menu.
- Required: No
-### `panelId`: `string`
+### `panelId`: `string | null`
Panel items will ensure they are only registering with their intended panel by
-comparing the `panelId` props set on both the item and the panel itself. This
+comparing the `panelId` props set on both the item and the panel itself, or if the `panelId` is explicitly `null`. This
allows items to be injected from a shared source.
- Required: No
-### `resetAllFilter`: `() => void`
+### `resetAllFilter`: `( attributes?: any ) => any`
A `ToolsPanel` will collect each item's `resetAllFilter` and pass an array of
these functions through to the panel's `resetAll` callback. They can then be
iterated over to perform additional tasks.
- Required: No
+- Default: `() => {}`
diff --git a/packages/components/src/tools-panel/tools-panel-item/component.tsx b/packages/components/src/tools-panel/tools-panel-item/component.tsx
index b0cc12c0f19fde..f66d3845ff0832 100644
--- a/packages/components/src/tools-panel/tools-panel-item/component.tsx
+++ b/packages/components/src/tools-panel/tools-panel-item/component.tsx
@@ -13,7 +13,7 @@ import type { ToolsPanelItemProps } from '../types';
// This wraps controls to be conditionally displayed within a tools panel. It
// prevents props being applied to HTML elements that would make them invalid.
-const ToolsPanelItem = (
+const UnconnectedToolsPanelItem = (
props: WordPressComponentProps< ToolsPanelItemProps, 'div' >,
forwardedRef: ForwardedRef< any >
) => {
@@ -37,9 +37,9 @@ const ToolsPanelItem = (
);
};
-const ConnectedToolsPanelItem = contextConnect(
- ToolsPanelItem,
+export const ToolsPanelItem = contextConnect(
+ UnconnectedToolsPanelItem,
'ToolsPanelItem'
);
-export default ConnectedToolsPanelItem;
+export default ToolsPanelItem;
diff --git a/packages/components/src/tools-panel/tools-panel-item/hook.ts b/packages/components/src/tools-panel/tools-panel-item/hook.ts
index c0b5572fee0d17..84572fba451a5e 100644
--- a/packages/components/src/tools-panel/tools-panel-item/hook.ts
+++ b/packages/components/src/tools-panel/tools-panel-item/hook.ts
@@ -13,16 +13,18 @@ import { useContextSystem, WordPressComponentProps } from '../../ui/context';
import { useCx } from '../../utils/hooks/use-cx';
import type { ToolsPanelItemProps } from '../types';
+const noop = () => {};
+
export function useToolsPanelItem(
props: WordPressComponentProps< ToolsPanelItemProps, 'div' >
) {
const {
className,
hasValue,
- isShownByDefault,
+ isShownByDefault = false,
label,
panelId,
- resetAllFilter,
+ resetAllFilter = noop,
onDeselect,
onSelect,
...otherProps
diff --git a/packages/components/src/tools-panel/tools-panel/README.md b/packages/components/src/tools-panel/tools-panel/README.md
index e04b774e9201bb..6802a6436875ad 100644
--- a/packages/components/src/tools-panel/tools-panel/README.md
+++ b/packages/components/src/tools-panel/tools-panel/README.md
@@ -155,6 +155,7 @@ Flags that the items in this ToolsPanel will be contained within an inner
wrapper element allowing the panel to lay them out accordingly.
- Required: No
+- Default: `false`
### `headingLevel`: `1 | 2 | 3 | 4 | 5 | 6 | '1' | '2' | '3' | '4' | '5' | '6'`
@@ -170,7 +171,7 @@ panel's dropdown menu.
- Required: Yes
-### `panelId`: `string`
+### `panelId`: `string | null`
If a `panelId` is set, it is passed through the `ToolsPanelContext` and used
to restrict panel items. When a `panelId` is set, items can only register
@@ -179,10 +180,9 @@ exactly.
- Required: No
-### `resetAll`: `() => void`
+### `resetAll`: `( filters?: ResetAllFilter[] ) => void`
-A function to call when the `Reset all` menu option is selected. This is passed
-through to the panel's header component.
+A function to call when the `Reset all` menu option is selected. As an argument, it receives an array containing the `resetAllFilter` callbacks of all the valid registered `ToolsPanelItems`.
- Required: Yes
@@ -192,3 +192,4 @@ Advises the `ToolsPanel` that all of its `ToolsPanelItem` children should render
placeholder content (instead of `null`) when they are toggled off and hidden.
- Required: No
+- Default: `false`
diff --git a/packages/components/src/tools-panel/tools-panel/component.tsx b/packages/components/src/tools-panel/tools-panel/component.tsx
index 929868e3f1f8c5..c6f3a9ce5469d6 100644
--- a/packages/components/src/tools-panel/tools-panel/component.tsx
+++ b/packages/components/src/tools-panel/tools-panel/component.tsx
@@ -13,7 +13,7 @@ import { Grid } from '../../grid';
import { contextConnect, WordPressComponentProps } from '../../ui/context';
import type { ToolsPanelProps } from '../types';
-const ToolsPanel = (
+const UnconnectedToolsPanel = (
props: WordPressComponentProps< ToolsPanelProps, 'div' >,
forwardedRef: ForwardedRef< any >
) => {
@@ -42,6 +42,58 @@ const ToolsPanel = (
);
};
-const ConnectedToolsPanel = contextConnect( ToolsPanel, 'ToolsPanel' );
+/**
+ * The `ToolsPanel` is a container component that displays its children preceded
+ * by a header. The header includes a dropdown menu which is automatically
+ * generated from the panel's inner `ToolsPanelItems`.
+ *
+ * @example
+ * ```jsx
+ * import { __ } from '@wordpress/i18n';
+ * import {
+ * __experimentalToolsPanel as ToolsPanel,
+ * __experimentalToolsPanelItem as ToolsPanelItem,
+ * __experimentalUnitControl as UnitControl
+ * } from '@wordpress/components';
+ *
+ * function Example() {
+ * const [ height, setHeight ] = useState();
+ * const [ width, setWidth ] = useState();
+ *
+ * const resetAll = () => {
+ * setHeight();
+ * setWidth();
+ * }
+ *
+ * return (
+ *
+ * !! height }
+ * label={ __( 'Height' ) }
+ * onDeselect={ () => setHeight() }
+ * >
+ *
+ *
+ * !! width }
+ * label={ __( 'Width' ) }
+ * onDeselect={ () => setWidth() }
+ * >
+ *
+ *
+ *
+ * );
+ * }
+ * ```
+ */
+export const ToolsPanel = contextConnect( UnconnectedToolsPanel, 'ToolsPanel' );
-export default ConnectedToolsPanel;
+export default ToolsPanel;
diff --git a/packages/components/src/tools-panel/tools-panel/hook.ts b/packages/components/src/tools-panel/tools-panel/hook.ts
index 38751ecb951ad1..66b91f0ee671f2 100644
--- a/packages/components/src/tools-panel/tools-panel/hook.ts
+++ b/packages/components/src/tools-panel/tools-panel/hook.ts
@@ -59,8 +59,8 @@ export function useToolsPanel(
headingLevel = 2,
resetAll,
panelId,
- hasInnerWrapper,
- shouldRenderPlaceholderItems,
+ hasInnerWrapper = false,
+ shouldRenderPlaceholderItems = false,
__experimentalFirstVisibleItemClass,
__experimentalLastVisibleItemClass,
...otherProps
diff --git a/packages/components/src/tools-panel/types.ts b/packages/components/src/tools-panel/types.ts
index ed846c900b18e1..86a8c1579b9b55 100644
--- a/packages/components/src/tools-panel/types.ts
+++ b/packages/components/src/tools-panel/types.ts
@@ -8,7 +8,7 @@ import type { ReactNode } from 'react';
*/
import type { HeadingSize } from '../heading/types';
-type ResetAllFilter = () => void;
+type ResetAllFilter = ( attributes?: any ) => any;
type ResetAll = ( filters?: ResetAllFilter[] ) => void;
export type ToolsPanelProps = {
@@ -19,8 +19,10 @@ export type ToolsPanelProps = {
/**
* Flags that the items in this ToolsPanel will be contained within an inner
* wrapper element allowing the panel to lay them out accordingly.
+ *
+ * @default false
*/
- hasInnerWrapper: boolean;
+ hasInnerWrapper?: boolean;
/**
* The heading level of the panel's header.
*
@@ -34,20 +36,24 @@ export type ToolsPanelProps = {
label: string;
/**
* If a `panelId` is set, it is passed through the `ToolsPanelContext` and
- * used to restrict panel items. Only items with a matching `panelId` will
- * be able to register themselves with this panel.
+ * used to restrict panel items. When a `panelId` is set, items can only
+ * register themselves if the `panelId` is explicitly `null` or the item's
+ * `panelId` matches exactly.
*/
- panelId: string;
+ panelId?: string | null;
/**
- * A function to call when the `Reset all` menu option is selected. This is
- * passed through to the panel's header component.
+ * A function to call when the `Reset all` menu option is selected. As an
+ * argument, it receives an array containing the `resetAllFilter` callbacks
+ * of all the valid registered `ToolsPanelItems`.
*/
resetAll: ResetAll;
/**
* Advises the `ToolsPanel` that its child `ToolsPanelItem`s should render
* placeholder content instead of null when they are toggled off and hidden.
+ *
+ * @default false
*/
- shouldRenderPlaceholderItems: boolean;
+ shouldRenderPlaceholderItems?: boolean;
/**
* Experimental prop allowing for a custom CSS class to be applied to the
* first visible `ToolsPanelItem` within the `ToolsPanel`.
@@ -96,8 +102,10 @@ export type ToolsPanelItem = {
* 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.
+ *
+ * @default false
*/
- isShownByDefault: boolean;
+ isShownByDefault?: boolean;
/**
* The supplied label is dual purpose. It is used as:
* 1. the human-readable label for the panel's dropdown menu
@@ -108,17 +116,20 @@ export type ToolsPanelItem = {
*/
label: string;
/**
- * Panel items will ensure they are only registering with their intended
- * panel by comparing the `panelId` props set on both the item and the panel
- * itself. This allows items to be injected from a shared source.
+ * Panel items will ensure they are only registering with their intended panel
+ * by comparing the `panelId` props set on both the item and the panel itself,
+ * or if the `panelId` is explicitly `null`. This allows items to be injected
+ * from a shared source.
*/
- panelId: string;
+ panelId?: string | null;
/**
* A `ToolsPanel` will collect each item's `resetAllFilter` and pass an
* array of these functions through to the panel's `resetAll` callback. They
* can then be iterated over to perform additional tasks.
+ *
+ * @default noop
*/
- resetAllFilter: ResetAllFilter;
+ resetAllFilter?: ResetAllFilter;
};
export type ToolsPanelItemProps = ToolsPanelItem & {
@@ -145,7 +156,7 @@ export type ToolsPanelMenuItems = {
};
export type ToolsPanelContext = {
- panelId?: string;
+ panelId?: string | null;
menuItems: ToolsPanelMenuItems;
hasMenuItems: boolean;
registerPanelItem: ( item: ToolsPanelItem ) => void;