From 2cdd37d45d9eea93377314acfa803786d9049dec Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Mon, 16 Dec 2024 11:17:15 +0100 Subject: [PATCH] Menu: more granular sub-components (#67422) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * MenuItem: add render and store props * Extract sub-components: popover, trigger button, submenu trigger item * Unit tests * CHANGELOG * Add more memory to node on CI * Refactor block bindings panel menu (#67633) Co-authored-by: ciampo * Storybook (#67632) * Refactor dataviews item actions menu (#67636) * Refactor dataviews view config menu (#67637) * Refactor global styles shadows edit panel menu (#67641) * Refactor global styles font size menus (#67642) * Refactor "Add filter" dataviews menu (#67634) * Menu granular subcomponents: Refactor dataviews list layout actions menu (#67639) Co-authored-by: ciampo Co-authored-by: tyxla Co-authored-by: oandregal * Menu granular subcomponents: Refactor dataviews table layout header menu (#67640) Co-authored-by: ciampo Co-authored-by: tyxla Co-authored-by: oandregal * Menu granular subcomponents: Refactor post actions menu (#67645) Co-authored-by: ciampo Co-authored-by: tyxla * Better comments for submenu trigger store * Typo * Remove unnecessary MenuSubmenuTriggerItemProps type * Don't break the rules of hooks 🪝 * Move CHANGELOG entry to unreleased section * Add explicit MenuProps to improve TS performance * Remove node memory settings --------- Co-authored-by: ciampo Co-authored-by: tyxla Co-authored-by: mirka <0mirka00@git.wordpress.org> Co-authored-by: oandregal --- .../block-editor/src/hooks/block-bindings.js | 31 +- packages/components/CHANGELOG.md | 1 + packages/components/src/menu/index.tsx | 225 ++----- packages/components/src/menu/item.tsx | 10 +- packages/components/src/menu/popover.tsx | 103 +++ .../src/menu/stories/index.story.tsx | 619 ++++++++++-------- .../src/menu/submenu-trigger-item.tsx | 61 ++ packages/components/src/menu/test/index.tsx | 448 ++++++++----- .../components/src/menu/trigger-button.tsx | 46 ++ packages/components/src/menu/types.ts | 79 ++- .../dataviews-filters/add-filter.tsx | 82 +-- .../components/dataviews-filters/index.tsx | 2 +- .../dataviews-item-actions/index.tsx | 38 +- .../dataviews-view-config/index.tsx | 93 +-- .../src/dataviews-layouts/list/index.tsx | 54 +- .../table/column-header-menu.tsx | 299 ++++----- .../global-styles/font-sizes/font-size.js | 47 +- .../global-styles/font-sizes/font-sizes.js | 48 +- .../global-styles/shadows-edit-panel.js | 59 +- .../src/components/post-actions/index.js | 36 +- 20 files changed, 1384 insertions(+), 997 deletions(-) create mode 100644 packages/components/src/menu/popover.tsx create mode 100644 packages/components/src/menu/submenu-trigger-item.tsx create mode 100644 packages/components/src/menu/trigger-button.tsx diff --git a/packages/block-editor/src/hooks/block-bindings.js b/packages/block-editor/src/hooks/block-bindings.js index 2dab67d6293328..11e17aba3b30da 100644 --- a/packages/block-editor/src/hooks/block-bindings.js +++ b/packages/block-editor/src/hooks/block-bindings.js @@ -51,7 +51,7 @@ const useToolsPanelDropdownMenuProps = () => { : {}; }; -function BlockBindingsPanelDropdown( { fieldsList, attribute, binding } ) { +function BlockBindingsPanelMenuContent( { fieldsList, attribute, binding } ) { const { clientId } = useBlockEditContext(); const registeredSources = getBlockBindingsSources(); const { updateBlockBindings } = useBlockBindingsUtils(); @@ -179,22 +179,21 @@ function EditableBlockBindingsPanelItems( { placement={ isMobile ? 'bottom-start' : 'left-start' } - gutter={ isMobile ? 8 : 36 } - trigger={ - - - - } > - + }> + + + + + ); diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index fef1769c19b0f7..7b5ec64bd44ca5 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -19,6 +19,7 @@ ### Experimental - Add new `Badge` component ([#66555](https://github.com/WordPress/gutenberg/pull/66555)). +- `Menu`: refactor to more granular sub-components ([#67422](https://github.com/WordPress/gutenberg/pull/67422)). ### Internal diff --git a/packages/components/src/menu/index.tsx b/packages/components/src/menu/index.tsx index 9886f324823212..2e0fc91cfbc34f 100644 --- a/packages/components/src/menu/index.tsx +++ b/packages/components/src/menu/index.tsx @@ -6,23 +6,14 @@ import * as Ariakit from '@ariakit/react'; /** * WordPress dependencies */ -import { - useContext, - useMemo, - cloneElement, - isValidElement, - useCallback, -} from '@wordpress/element'; -import { isRTL } from '@wordpress/i18n'; -import { chevronRightSmall } from '@wordpress/icons'; +import { useContext, useMemo } from '@wordpress/element'; +import { isRTL as isRTLFn } from '@wordpress/i18n'; /** * Internal dependencies */ -import { useContextSystem, contextConnect } from '../context'; -import type { WordPressComponentProps } from '../context'; +import { useContextSystem, contextConnectWithoutRef } from '../context'; import type { MenuContext as MenuContextType, MenuProps } from './types'; -import * as Styled from './styles'; import { MenuContext } from './context'; import { MenuItem } from './item'; import { MenuCheckboxItem } from './checkbox-item'; @@ -32,49 +23,36 @@ import { MenuGroupLabel } from './group-label'; import { MenuSeparator } from './separator'; import { MenuItemLabel } from './item-label'; import { MenuItemHelpText } from './item-help-text'; +import { MenuTriggerButton } from './trigger-button'; +import { MenuSubmenuTriggerItem } from './submenu-trigger-item'; +import { MenuPopover } from './popover'; -const UnconnectedMenu = ( - props: WordPressComponentProps< MenuProps, 'div', false >, - ref: React.ForwardedRef< HTMLDivElement > -) => { +const UnconnectedMenu = ( props: MenuProps ) => { const { - // Store props - open, + children, defaultOpen = false, + open, onOpenChange, placement, - // Menu trigger props - trigger, - - // Menu props - gutter, - children, - shift, - modal = true, - // From internal components context variant, - - // Rest - ...otherProps - } = useContextSystem< typeof props & Pick< MenuContextType, 'variant' > >( - props, - 'Menu' - ); + } = useContextSystem< + // @ts-expect-error TODO: missing 'className' in MenuProps + typeof props & Pick< MenuContextType, 'variant' > + >( props, 'Menu' ); const parentContext = useContext( MenuContext ); - const computedDirection = isRTL() ? 'rtl' : 'ltr'; + const rtl = isRTLFn(); // If an explicit value for the `placement` prop is not passed, // apply a default placement of `bottom-start` for the root menu popover, // and of `right-start` for nested menu popovers. let computedPlacement = - props.placement ?? - ( parentContext?.store ? 'right-start' : 'bottom-start' ); + placement ?? ( parentContext?.store ? 'right-start' : 'bottom-start' ); // Swap left/right in case of RTL direction - if ( computedDirection === 'rtl' ) { + if ( rtl ) { if ( /right/.test( computedPlacement ) ) { computedPlacement = computedPlacement.replace( 'right', @@ -97,7 +75,7 @@ const UnconnectedMenu = ( setOpen( willBeOpen ) { onOpenChange?.( willBeOpen ); }, - rtl: computedDirection === 'rtl', + rtl, } ); const contextValue = useMemo( @@ -105,134 +83,53 @@ const UnconnectedMenu = ( [ menuStore, variant ] ); - // Extract the side from the applied placement — useful for animations. - // Using `currentPlacement` instead of `placement` to make sure that we - // use the final computed placement (including "flips" etc). - const appliedPlacementSide = Ariakit.useStoreState( - menuStore, - 'currentPlacement' - ).split( '-' )[ 0 ]; - - if ( - menuStore.parent && - ! ( isValidElement( trigger ) && MenuItem === trigger.type ) - ) { - // eslint-disable-next-line no-console - console.warn( - 'For nested Menus, the `trigger` should always be a `MenuItem`.' - ); - } - - const hideOnEscape = useCallback( - ( event: React.KeyboardEvent< Element > ) => { - // Pressing Escape can cause unexpected consequences (ie. exiting - // full screen mode on MacOs, close parent modals...). - event.preventDefault(); - // Returning `true` causes the menu to hide. - return true; - }, - [] - ); - - const wrapperProps = useMemo( - () => ( { - dir: computedDirection, - style: { - direction: - computedDirection as React.CSSProperties[ 'direction' ], - }, - } ), - [ computedDirection ] - ); - return ( - <> - { /* Menu trigger */ } - - { trigger.props.suffix } -