From 7714bce8d9356707a020b5d7df84797832c423f8 Mon Sep 17 00:00:00 2001 From: Lena Morita Date: Tue, 29 Oct 2024 23:18:06 +0900 Subject: [PATCH 01/56] TabPanel: Add 40px size prop to tab Button (#66557) * TabPanel: Add 40px size prop to tab Button * Add changelog Co-authored-by: mirka <0mirka00@git.wordpress.org> Co-authored-by: tyxla --- packages/components/CHANGELOG.md | 1 + packages/components/src/tab-panel/index.tsx | 1 + packages/components/src/tab-panel/style.scss | 4 +--- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index ba0d68d5c12c09..45f99c71af58ac 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -30,6 +30,7 @@ ### Internal - ESLint: Stop disabling `react-hooks/exhaustive-deps` rule ([#66324](https://github.com/WordPress/gutenberg/pull/66324)). +- `TabPanel`: Add 40px size prop to tab Button ([#66557](https://github.com/WordPress/gutenberg/pull/66557)). ## 28.10.0 (2024-10-16) diff --git a/packages/components/src/tab-panel/index.tsx b/packages/components/src/tab-panel/index.tsx index f9db745b424260..be06b42fcd013f 100644 --- a/packages/components/src/tab-panel/index.tsx +++ b/packages/components/src/tab-panel/index.tsx @@ -220,6 +220,7 @@ const UnforwardedTabPanel = ( ) }-view` } render={ ) } - + { isOuterModalOpen && ( setOuterModalOpen( false ) } @@ -451,19 +377,16 @@ WithModals.args = { const ExampleSlotFill = createSlotFill( 'Example' ); const Slot = () => { - const dropdownMenuContext = useContext( DropdownMenuV2.Context ); + const menuContext = useContext( Menu.Context ); // Forwarding the content of the slot so that it can be used by the fill const fillProps = useMemo( () => ( { forwardedContext: [ - [ - DropdownMenuV2.Context.Provider, - { value: dropdownMenuContext }, - ], + [ Menu.Context.Provider, { value: menuContext } ], ], } ), - [ dropdownMenuContext ] + [ menuContext ] ); return ( @@ -499,37 +422,31 @@ const Fill = ( { children }: { children: React.ReactNode } ) => { ); }; -export const WithSlotFill: StoryFn< typeof DropdownMenuV2 > = ( props ) => { +export const WithSlotFill: StoryFn< typeof Menu > = ( props ) => { return ( - - - Item - + + + Item + - + - - - Item from fill - - - + Item from fill + + - - Submenu from fill - - + + Submenu from fill + } > - - - Submenu item from fill - - - + + Submenu item from fill + + ); @@ -539,48 +456,40 @@ WithSlotFill.args = { }; const toolbarVariantContextValue = { - DropdownMenuV2: { + Menu: { variant: 'toolbar', }, }; -export const ToolbarVariant: StoryFn< typeof DropdownMenuV2 > = ( props ) => ( +export const ToolbarVariant: StoryFn< typeof Menu > = ( props ) => ( // TODO: add toolbar - - - - Level 1 item - - - - - Level 1 item - - - - + + Level 1 item + + + Level 1 item + + + - - Submenu trigger - - + + Submenu trigger + } > - - - Level 2 item - - - - + + Level 2 item + + + ); ToolbarVariant.args = { ...Default.args, }; -export const InsideModal: StoryFn< typeof DropdownMenuV2 > = ( props ) => { +export const InsideModal: StoryFn< typeof Menu > = ( props ) => { const [ isModalOpen, setModalOpen ] = useState( false ); return ( <> @@ -593,34 +502,28 @@ export const InsideModal: StoryFn< typeof DropdownMenuV2 > = ( props ) => { { isModalOpen && ( setModalOpen( false ) }> - - - - Level 1 item - - - - - Level 1 item - - - - + + Level 1 item + + + Level 1 item + + + - + + Submenu trigger - - + + } > - - - Level 2 item - - - - + + Level 2 item + + + diff --git a/packages/components/src/menu/styles.ts b/packages/components/src/menu/styles.ts index b25613e9feb52b..3312c8cb2de161 100644 --- a/packages/components/src/menu/styles.ts +++ b/packages/components/src/menu/styles.ts @@ -12,7 +12,7 @@ import { COLORS, font, rtl, CONFIG } from '../utils'; import { space } from '../utils/space'; import Icon from '../icon'; import { Truncate } from '../truncate'; -import type { DropdownMenuContext } from './types'; +import type { MenuContext } from './types'; const ANIMATION_PARAMS = { SCALE_AMOUNT_OUTER: 0.82, @@ -43,7 +43,7 @@ const TOOLBAR_VARIANT_BOX_SHADOW = `0 0 0 ${ CONFIG.borderWidth } ${ TOOLBAR_VAR const GRID_TEMPLATE_COLS = 'minmax( 0, max-content ) 1fr'; export const MenuPopoverOuterWrapper = styled.div< - Pick< DropdownMenuContext, 'variant' > + Pick< MenuContext, 'variant' > >` position: relative; @@ -229,15 +229,15 @@ const baseItem = css` } `; -export const DropdownMenuItem = styled( Ariakit.MenuItem )` +export const MenuItem = styled( Ariakit.MenuItem )` ${ baseItem }; `; -export const DropdownMenuCheckboxItem = styled( Ariakit.MenuItemCheckbox )` +export const MenuCheckboxItem = styled( Ariakit.MenuItemCheckbox )` ${ baseItem }; `; -export const DropdownMenuRadioItem = styled( Ariakit.MenuItemRadio )` +export const MenuRadioItem = styled( Ariakit.MenuItemRadio )` ${ baseItem }; `; @@ -249,14 +249,14 @@ export const ItemPrefixWrapper = styled.span` * Even when the item is not checked, occupy the same screen space to avoid * the space collapside when no items are checked. */ - ${ DropdownMenuCheckboxItem } > &, - ${ DropdownMenuRadioItem } > & { + ${ MenuCheckboxItem } > &, + ${ MenuRadioItem } > & { /* Same width as the check icons */ min-width: ${ space( 6 ) }; } - ${ DropdownMenuCheckboxItem } > &, - ${ DropdownMenuRadioItem } > &, + ${ MenuCheckboxItem } > &, + ${ MenuRadioItem } > &, &:not( :empty ) { margin-inline-end: ${ space( 2 ) }; } @@ -278,7 +278,7 @@ export const ItemPrefixWrapper = styled.span` } `; -export const DropdownMenuItemContentWrapper = styled.div` +export const MenuItemContentWrapper = styled.div` /* * Always occupy the second column, since the first column * is taken by the prefix wrapper (when displayed). @@ -293,7 +293,7 @@ export const DropdownMenuItemContentWrapper = styled.div` pointer-events: none; `; -export const DropdownMenuItemChildrenWrapper = styled.div` +export const MenuItemChildrenWrapper = styled.div` flex: 1; display: inline-flex; @@ -324,12 +324,12 @@ export const ItemSuffixWrapper = styled.span` } `; -export const DropdownMenuGroup = styled( Ariakit.MenuGroup )` +export const MenuGroup = styled( Ariakit.MenuGroup )` /* Ignore this element when calculating the layout. Useful for subgrid */ display: contents; `; -export const DropdownMenuGroupLabel = styled( Ariakit.MenuGroupLabel )` +export const MenuGroupLabel = styled( Ariakit.MenuGroupLabel )` /* Occupy the width of all grid columns (ie. full width) */ grid-column: 1 / -1; @@ -338,8 +338,8 @@ export const DropdownMenuGroupLabel = styled( Ariakit.MenuGroupLabel )` padding-inline: ${ ITEM_PADDING_INLINE }; `; -export const DropdownMenuSeparator = styled( Ariakit.MenuSeparator )< - Pick< DropdownMenuContext, 'variant' > +export const MenuSeparator = styled( Ariakit.MenuSeparator )< + Pick< MenuContext, 'variant' > >` /* Occupy the width of all grid columns (ie. full width) */ grid-column: 1 / -1; @@ -370,13 +370,13 @@ export const SubmenuChevronIcon = styled( Icon )` ) }; `; -export const DropdownMenuItemLabel = styled( Truncate )` +export const MenuItemLabel = styled( Truncate )` font-size: ${ font( 'default.fontSize' ) }; line-height: 20px; color: inherit; `; -export const DropdownMenuItemHelpText = styled( Truncate )` +export const MenuItemHelpText = styled( Truncate )` font-size: ${ font( 'helpText.fontSize' ) }; line-height: 16px; color: ${ LIGHTER_TEXT_COLOR }; diff --git a/packages/components/src/menu/test/index.tsx b/packages/components/src/menu/test/index.tsx index cb674f27edaacf..60276cdb2379a0 100644 --- a/packages/components/src/menu/test/index.tsx +++ b/packages/components/src/menu/test/index.tsx @@ -12,34 +12,24 @@ import { useState } from '@wordpress/element'; /** * Internal dependencies */ -import { DropdownMenuV2 } from '..'; +import { Menu } from '..'; const delay = ( delayInMs: number ) => { return new Promise( ( resolve ) => setTimeout( resolve, delayInMs ) ); }; -describe( 'DropdownMenu', () => { +describe( 'Menu', () => { // See https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/ it( 'should follow the WAI-ARIA spec', async () => { render( - Open dropdown }> - Dropdown menu item - - - Dropdown submenu - - } - > - - Dropdown submenu item 1 - - - Dropdown submenu item 2 - - - + Open dropdown }> + Menu item + + Submenu trigger item }> + Submenu item 1 + Submenu item 2 + + ); const toggleButton = screen.getByRole( 'button', { @@ -68,7 +58,7 @@ describe( 'DropdownMenu', () => { expect( screen.getAllByRole( 'menuitem' ) ).toHaveLength( 2 ); const submenuTrigger = screen.getByRole( 'menuitem', { - name: 'Dropdown submenu', + name: 'Submenu trigger item', } ); expect( submenuTrigger ).toHaveAttribute( 'aria-haspopup', 'menu' ); expect( submenuTrigger ).toHaveAttribute( 'aria-expanded', 'false' ); @@ -94,36 +84,32 @@ describe( 'DropdownMenu', () => { describe( 'pointer and keyboard interactions', () => { it( 'should open and focus the menu when clicking the trigger', async () => { render( - Open dropdown }> - - Dropdown menu item - - + Open dropdown }> + Menu item + ); const toggleButton = screen.getByRole( 'button', { name: 'Open dropdown', } ); - // DropdownMenu closed + // Menu closed expect( screen.queryByRole( 'menu' ) ).not.toBeInTheDocument(); // Click to open the menu await click( toggleButton ); - // DropdownMenu open, focus is on the menu wrapper + // Menu open, focus is on the menu wrapper expect( screen.getByRole( 'menu' ) ).toHaveFocus(); } ); it( 'should open and focus the first item when pressing the arrow down key on the trigger', async () => { render( - Open dropdown }> - - First item - - Second item - Third item - + Open dropdown }> + First item + Second item + Third item + ); const toggleButton = screen.getByRole( 'button', { @@ -135,12 +121,12 @@ describe( 'DropdownMenu', () => { expect( toggleButton ).toHaveFocus(); - // DropdownMenu closed + // Menu closed expect( screen.queryByRole( 'menuitem' ) ).not.toBeInTheDocument(); await press.ArrowDown(); - // DropdownMenu open, focus is on the first focusable item + // Menu open, focus is on the first focusable item // (disabled items are still focusable and accessible) expect( screen.getByRole( 'menuitem', { name: 'First item' } ) @@ -149,13 +135,11 @@ describe( 'DropdownMenu', () => { it( 'should open and focus the first item when pressing the space key on the trigger', async () => { render( - Open dropdown }> - - First item - - Second item - Third item - + Open dropdown }> + First item + Second item + Third item + ); const toggleButton = screen.getByRole( 'button', { @@ -167,12 +151,12 @@ describe( 'DropdownMenu', () => { expect( toggleButton ).toHaveFocus(); - // DropdownMenu closed + // Menu closed expect( screen.queryByRole( 'menuitem' ) ).not.toBeInTheDocument(); await press.Space(); - // DropdownMenu open, focus is on the first focusable item + // Menu open, focus is on the first focusable item // (disabled items are still focusable and accessible expect( screen.getByRole( 'menuitem', { name: 'First item' } ) @@ -181,11 +165,9 @@ describe( 'DropdownMenu', () => { it( 'should close when pressing the escape key', async () => { render( - Open dropdown }> - - Dropdown menu item - - + Open dropdown }> + Menu item + ); const trigger = screen.getByRole( 'button', { @@ -212,14 +194,9 @@ describe( 'DropdownMenu', () => { it( 'should close when clicking outside of the content', async () => { render( - Open dropdown } - > - - Dropdown menu item - - + Open dropdown }> + Menu item + ); expect( screen.getByRole( 'menu' ) ).toBeInTheDocument(); @@ -232,14 +209,9 @@ describe( 'DropdownMenu', () => { it( 'should close when clicking on a menu item', async () => { render( - Open dropdown } - > - - Dropdown menu item - - + Open dropdown }> + Menu item + ); expect( screen.getByRole( 'menu' ) ).toBeInTheDocument(); @@ -252,14 +224,9 @@ describe( 'DropdownMenu', () => { it( 'should not close when clicking on a menu item when the `hideOnClick` prop is set to `false`', async () => { render( - Open dropdown } - > - - Dropdown menu item - - + Open dropdown }> + Menu item + ); expect( screen.getByRole( 'menu' ) ).toBeVisible(); @@ -272,14 +239,9 @@ describe( 'DropdownMenu', () => { it( 'should not close when clicking on a disabled menu item', async () => { render( - Open dropdown } - > - - Dropdown menu item - - + Open dropdown }> + Menu item + ); expect( screen.getByRole( 'menu' ) ).toBeInTheDocument(); @@ -292,85 +254,51 @@ describe( 'DropdownMenu', () => { it( 'should reveal submenu content when hovering over the submenu trigger', async () => { render( - Open dropdown } - > - - Dropdown menu item 1 - - - Dropdown menu item 2 - - - Dropdown submenu - - } + Open dropdown }> + Menu item 1 + Menu item 2 + Submenu trigger item } > - - Dropdown submenu item 1 - - - Dropdown submenu item 2 - - - - Dropdown menu item 3 - - + Submenu item 1 + Submenu item 2 + + Menu item 3 + ); // Before hover, submenu items are not rendered expect( screen.queryByRole( 'menuitem', { - name: 'Dropdown submenu item 1', + name: 'Submenu item 1', } ) ).not.toBeInTheDocument(); await hover( - screen.getByRole( 'menuitem', { name: 'Dropdown submenu' } ) + screen.getByRole( 'menuitem', { name: 'Submenu trigger item' } ) ); // After hover, submenu items are rendered // Reason for `findByRole`: due to the animation, we've got to wait // a short amount of time for the submenu to appear await screen.findByRole( 'menuitem', { - name: 'Dropdown submenu item 1', + name: 'Submenu item 1', } ); } ); it( 'should navigate menu items and subitems using the arrow, spacebar and enter keys', async () => { render( - Open dropdown } - > - - Dropdown menu item 1 - - - Dropdown menu item 2 - - - Dropdown submenu - - } + Open dropdown }> + Menu item 1 + Menu item 2 + Submenu trigger item } > - - Dropdown submenu item 1 - - - Dropdown submenu item 2 - - - - Dropdown menu item 3 - - + Submenu item 1 + Submenu item 2 + + Menu item 3 + ); // The menu is focused automatically when `defaultOpen` is set. @@ -382,58 +310,58 @@ describe( 'DropdownMenu', () => { // The selection wraps around from last to first and viceversa await press.ArrowDown(); expect( - screen.getByRole( 'menuitem', { name: 'Dropdown menu item 1' } ) + screen.getByRole( 'menuitem', { name: 'Menu item 1' } ) ).toHaveFocus(); await press.ArrowDown(); expect( - screen.getByRole( 'menuitem', { name: 'Dropdown menu item 2' } ) + screen.getByRole( 'menuitem', { name: 'Menu item 2' } ) ).toHaveFocus(); await press.ArrowDown(); expect( - screen.getByRole( 'menuitem', { name: 'Dropdown submenu' } ) + screen.getByRole( 'menuitem', { name: 'Submenu trigger item' } ) ).toHaveFocus(); await press.ArrowDown(); expect( - screen.getByRole( 'menuitem', { name: 'Dropdown menu item 3' } ) + screen.getByRole( 'menuitem', { name: 'Menu item 3' } ) ).toHaveFocus(); await press.ArrowDown(); expect( - screen.getByRole( 'menuitem', { name: 'Dropdown menu item 1' } ) + screen.getByRole( 'menuitem', { name: 'Menu item 1' } ) ).toHaveFocus(); await press.ArrowUp(); expect( - screen.getByRole( 'menuitem', { name: 'Dropdown menu item 3' } ) + screen.getByRole( 'menuitem', { name: 'Menu item 3' } ) ).toHaveFocus(); await press.ArrowUp(); expect( - screen.getByRole( 'menuitem', { name: 'Dropdown submenu' } ) + screen.getByRole( 'menuitem', { name: 'Submenu trigger item' } ) ).toHaveFocus(); // Arrow right/left can be used to enter/leave submenus await press.ArrowRight(); expect( screen.getByRole( 'menuitem', { - name: 'Dropdown submenu item 1', + name: 'Submenu item 1', } ) ).toHaveFocus(); await press.ArrowDown(); expect( screen.getByRole( 'menuitem', { - name: 'Dropdown submenu item 2', + name: 'Submenu item 2', } ) ).toHaveFocus(); await press.ArrowLeft(); expect( screen.getByRole( 'menuitem', { - name: 'Dropdown submenu', + name: 'Submenu trigger item', } ) ).toHaveFocus(); @@ -441,28 +369,28 @@ describe( 'DropdownMenu', () => { await press.Enter(); expect( screen.getByRole( 'menuitem', { - name: 'Dropdown submenu item 1', + name: 'Submenu item 1', } ) ).toHaveFocus(); await press.ArrowLeft(); expect( screen.getByRole( 'menuitem', { - name: 'Dropdown submenu', + name: 'Submenu trigger item', } ) ).toHaveFocus(); await press.Space(); expect( screen.getByRole( 'menuitem', { - name: 'Dropdown submenu item 1', + name: 'Submenu item 1', } ) ).toHaveFocus(); await press.ArrowLeft(); expect( screen.getByRole( 'menuitem', { - name: 'Dropdown submenu', + name: 'Submenu trigger item', } ) ).toHaveFocus(); } ); @@ -473,32 +401,32 @@ describe( 'DropdownMenu', () => { const ControlledRadioGroup = () => { const [ radioValue, setRadioValue ] = useState( 'two' ); const onRadioChange: React.ComponentProps< - typeof DropdownMenuV2.RadioItem + typeof Menu.RadioItem >[ 'onChange' ] = ( e ) => { onRadioValueChangeSpy( e.target.value ); setRadioValue( e.target.value ); }; return ( - Open dropdown }> - - Open dropdown }> + + Radio item one - - + Radio item two - - - + + + ); }; @@ -556,9 +484,9 @@ describe( 'DropdownMenu', () => { it( 'should check radio items and keep the menu open when clicking (uncontrolled)', async () => { const onRadioValueChangeSpy = jest.fn(); render( - Open dropdown }> - - Open dropdown }> + + @@ -566,8 +494,8 @@ describe( 'DropdownMenu', () => { } > Radio item one - - + { } > Radio item two - - - + + + ); // Open dropdown @@ -640,8 +568,8 @@ describe( 'DropdownMenu', () => { useState< boolean >(); return ( - Open dropdown }> - Open dropdown }> + { } } > Checkbox item one - + - { } } > Checkbox item two - - + + ); }; @@ -763,8 +691,8 @@ describe( 'DropdownMenu', () => { const onCheckboxValueChangeSpy = jest.fn(); render( - Open dropdown }> - Open dropdown }> + { @@ -776,9 +704,9 @@ describe( 'DropdownMenu', () => { } } > Checkbox item one - + - { } } > Checkbox item two - - + + ); // Open dropdown @@ -881,11 +809,9 @@ describe( 'DropdownMenu', () => { it( 'should be modal by default', async () => { render( <> - Open dropdown }> - - Dropdown menu item - - + Open dropdown }> + Menu item + ); @@ -897,7 +823,7 @@ describe( 'DropdownMenu', () => { } ) ); - // DropdownMenu open, focus is on the menu wrapper + // Menu open, focus is on the menu wrapper expect( screen.getByRole( 'menu' ) ).toHaveFocus(); expect( @@ -910,14 +836,12 @@ describe( 'DropdownMenu', () => { it( 'should not be modal when the `modal` prop is set to `false`', async () => { render( <> - Open dropdown } modal={ false } > - - Dropdown menu item - - + Menu item + ); @@ -929,17 +853,17 @@ describe( 'DropdownMenu', () => { } ) ); - // DropdownMenu open, focus is on the menu wrapper + // Menu open, focus is on the menu wrapper expect( screen.getByRole( 'menu' ) ).toHaveFocus(); - // DropdownMenu is not modal, therefore the outer button is part of the + // Menu is not modal, therefore the outer button is part of the // accessibility tree and can be found. const outerButton = screen.getByRole( 'button', { name: 'Button outside of dropdown', } ); // The outer button can be focused by pressing tab. Doing so will cause - // the DropdownMenu to close. + // the Menu to close. await press.Tab(); expect( outerButton ).toBeInTheDocument(); expect( screen.queryByRole( 'menu' ) ).not.toBeInTheDocument(); @@ -949,11 +873,9 @@ describe( 'DropdownMenu', () => { describe( 'items prefix and suffix', () => { it( 'should display a prefix on regular items', async () => { render( - Open dropdown }> - Item prefix }> - Dropdown menu item - - + Open dropdown }> + Item prefix }>Menu item + ); // Click to open the menu @@ -966,18 +888,16 @@ describe( 'DropdownMenu', () => { // The contents of the prefix are rendered before the item's children expect( screen.getByRole( 'menuitem', { - name: 'Item prefix Dropdown menu item', + name: 'Item prefix Menu item', } ) ).toBeInTheDocument(); } ); it( 'should display a suffix on regular items', async () => { render( - Open dropdown }> - Item suffix }> - Dropdown menu item - - + Open dropdown }> + Item suffix }>Menu item + ); // Click to open the menu @@ -990,22 +910,22 @@ describe( 'DropdownMenu', () => { // The contents of the suffix are rendered after the item's children expect( screen.getByRole( 'menuitem', { - name: 'Dropdown menu item Item suffix', + name: 'Menu item Item suffix', } ) ).toBeInTheDocument(); } ); it( 'should display a suffix on radio items', async () => { render( - Open dropdown }> - Open dropdown }> + Radio item one - - + + ); // Click to open the menu @@ -1025,15 +945,15 @@ describe( 'DropdownMenu', () => { it( 'should display a suffix on checkbox items', async () => { render( - Open dropdown }> - Open dropdown }> + Checkbox item one - - + + ); // Click to open the menu @@ -1055,10 +975,10 @@ describe( 'DropdownMenu', () => { describe( 'typeahead', () => { it( 'should highlight matching item', async () => { render( - Open dropdown }> - One - Two - + Open dropdown }> + One + Two + ); // Click to open the menu @@ -1088,10 +1008,10 @@ describe( 'DropdownMenu', () => { it( 'should keep previous focus when no matches are found', async () => { render( - Open dropdown }> - One - Two - + Open dropdown }> + One + Two + ); // Click to open the menu diff --git a/packages/components/src/menu/types.ts b/packages/components/src/menu/types.ts index 795cd9ac76ff58..7b58cef241743e 100644 --- a/packages/components/src/menu/types.ts +++ b/packages/components/src/menu/types.ts @@ -4,9 +4,9 @@ import type * as Ariakit from '@ariakit/react'; import type { Placement } from '@floating-ui/react-dom'; -export interface DropdownMenuContext { +export interface MenuContext { /** - * The ariakit store shared across all DropdownMenu subcomponents. + * The ariakit store shared across all Menu subcomponents. */ store: Ariakit.MenuStore; /** @@ -15,33 +15,33 @@ export interface DropdownMenuContext { variant?: 'toolbar'; } -export interface DropdownMenuProps { +export interface MenuProps { /** - * The trigger button. + * The button triggering the menu popover. */ trigger: React.ReactElement; /** - * The contents of the dropdown. + * The contents of the menu (ie. one or more menu items). */ children?: React.ReactNode; /** - * The open state of the dropdown menu when it is initially rendered. Use when + * The open state of the menu popover when it is initially rendered. Use when * not wanting to control its open state. * * @default false */ defaultOpen?: boolean; /** - * The controlled open state of the dropdown menu. Must be used in conjunction + * The controlled open state of the menu popover. Must be used in conjunction * with `onOpenChange`. */ open?: boolean; /** - * Event handler called when the open state of the dropdown menu changes. + * Event handler called when the open state of the menu popover changes. */ onOpenChange?: ( open: boolean ) => void; /** - * The modality of the dropdown menu. When set to true, interaction with + * The modality of the menu popover. When set to true, interaction with * outside elements will be disabled and only menu content will be visible to * screen readers. * @@ -49,7 +49,7 @@ export interface DropdownMenuProps { */ modal?: boolean; /** - * The placement of the dropdown menu popover. + * The placement of the menu popover. * * @default 'bottom-start' for root-level menus, 'right-start' for nested menus */ @@ -80,21 +80,22 @@ export interface DropdownMenuProps { ) => boolean ); } -export interface DropdownMenuGroupProps { +export interface MenuGroupProps { /** - * The contents of the dropdown menu group. + * The contents of the menu group (ie. an optional menu group label and one + * or more menu items). */ children: React.ReactNode; } -export interface DropdownMenuGroupLabelProps { +export interface MenuGroupLabelProps { /** - * The contents of the dropdown menu group. + * The contents of the menu group label. */ children: React.ReactNode; } -export interface DropdownMenuItemProps { +export interface MenuItemProps { /** * The contents of the menu item. */ @@ -108,7 +109,7 @@ export interface DropdownMenuItemProps { */ suffix?: React.ReactNode; /** - * Whether to hide the parent menu when the item is clicked. + * Whether to hide the menu popover when the menu item is clicked. * * @default true */ @@ -119,10 +120,10 @@ export interface DropdownMenuItemProps { disabled?: boolean; } -export interface DropdownMenuCheckboxItemProps - extends Omit< DropdownMenuItemProps, 'prefix' | 'hideOnClick' > { +export interface MenuCheckboxItemProps + extends Omit< MenuItemProps, 'prefix' | 'hideOnClick' > { /** - * Whether to hide the dropdown menu when the item is clicked. + * Whether to hide the menu popover when the menu item is clicked. * * @default false */ @@ -151,10 +152,10 @@ export interface DropdownMenuCheckboxItemProps onChange?: ( event: React.ChangeEvent< HTMLInputElement > ) => void; } -export interface DropdownMenuRadioItemProps - extends Omit< DropdownMenuItemProps, 'prefix' | 'hideOnClick' > { +export interface MenuRadioItemProps + extends Omit< MenuItemProps, 'prefix' | 'hideOnClick' > { /** - * Whether to hide the dropdown menu when the item is clicked. + * Whether to hide the menu popover when the menu item is clicked. * * @default false */ @@ -182,4 +183,4 @@ export interface DropdownMenuRadioItemProps onChange?: ( event: React.ChangeEvent< HTMLInputElement > ) => void; } -export interface DropdownMenuSeparatorProps {} +export interface MenuSeparatorProps {} diff --git a/packages/components/src/private-apis.ts b/packages/components/src/private-apis.ts index 6d28765c2f685e..bea16b719a463d 100644 --- a/packages/components/src/private-apis.ts +++ b/packages/components/src/private-apis.ts @@ -3,7 +3,7 @@ */ import { positionToPlacement as __experimentalPopoverLegacyPositionToPlacement } from './popover/utils'; import { createPrivateSlotFill } from './slot-fill'; -import { DropdownMenuV2 } from './menu'; +import { Menu } from './menu'; import { ComponentsContext } from './context/context-system-provider'; import Theme from './theme'; import { Tabs } from './tabs'; @@ -17,6 +17,6 @@ lock( privateApis, { ComponentsContext, Tabs, Theme, - DropdownMenuV2, + Menu, kebabCase, } ); diff --git a/packages/components/src/toolbar/toolbar/index.tsx b/packages/components/src/toolbar/toolbar/index.tsx index ba2e8062aed85d..6b7f4843560fe7 100644 --- a/packages/components/src/toolbar/toolbar/index.tsx +++ b/packages/components/src/toolbar/toolbar/index.tsx @@ -40,6 +40,9 @@ function UnforwardedToolbar( Dropdown: { variant: 'toolbar', }, + Menu: { + variant: 'toolbar', + }, }; }, [ isVariantDefined ] ); diff --git a/packages/dataviews/src/components/dataviews-filters/add-filter.tsx b/packages/dataviews/src/components/dataviews-filters/add-filter.tsx index c8b6b5fda38fcc..94aebb71ea5874 100644 --- a/packages/dataviews/src/components/dataviews-filters/add-filter.tsx +++ b/packages/dataviews/src/components/dataviews-filters/add-filter.tsx @@ -19,7 +19,7 @@ import { forwardRef } from '@wordpress/element'; import { unlock } from '../../lock-unlock'; import type { NormalizedFilter, View } from '../../types'; -const { DropdownMenuV2 } = unlock( componentsPrivateApis ); +const { Menu } = unlock( componentsPrivateApis ); interface AddFilterProps { filters: NormalizedFilter[]; @@ -28,7 +28,7 @@ interface AddFilterProps { setOpenedFilter: ( filter: string | null ) => void; } -export function AddFilterDropdownMenu( { +export function AddFilterMenu( { filters, view, onChangeView, @@ -39,10 +39,10 @@ export function AddFilterDropdownMenu( { } ) { const inactiveFilters = filters.filter( ( filter ) => ! filter.isVisible ); return ( - + { inactiveFilters.map( ( filter ) => { return ( - { setOpenedFilter( filter.field ); @@ -60,13 +60,11 @@ export function AddFilterDropdownMenu( { } ); } } > - - { filter.name } - - + { filter.name } + ); } ) } - + ); } @@ -79,7 +77,7 @@ function AddFilter( } const inactiveFilters = filters.filter( ( filter ) => ! filter.isVisible ); return ( - { action: Action< Item >; @@ -43,7 +43,7 @@ interface ActionWithModalProps< Item > extends ActionModalProps< Item > { isBusy?: boolean; } -interface ActionsDropdownMenuGroupProps< Item > { +interface ActionsMenuGroupProps< Item > { actions: Action< Item >[]; item: Item; } @@ -77,7 +77,7 @@ function ButtonTrigger< Item >( { ); } -function DropdownMenuItemTrigger< Item >( { +function MenuItemTrigger< Item >( { action, onClick, items, @@ -85,12 +85,12 @@ function DropdownMenuItemTrigger< Item >( { const label = typeof action.label === 'string' ? action.label : action.label( items ); return ( - - { label } - + { label } + ); } @@ -146,13 +146,13 @@ export function ActionWithModal< Item >( { ); } -export function ActionsDropdownMenuGroup< Item >( { +export function ActionsMenuGroup< Item >( { actions, item, -}: ActionsDropdownMenuGroupProps< Item > ) { +}: ActionsMenuGroupProps< Item > ) { const registry = useRegistry(); return ( - + { actions.map( ( action ) => { if ( 'RenderModal' in action ) { return ( @@ -160,12 +160,12 @@ export function ActionsDropdownMenuGroup< Item >( { key={ action.id } action={ action } items={ [ item ] } - ActionTrigger={ DropdownMenuItemTrigger } + ActionTrigger={ MenuItemTrigger } /> ); } return ( - { @@ -175,7 +175,7 @@ export function ActionsDropdownMenuGroup< Item >( { /> ); } ) } - + ); } @@ -245,7 +245,7 @@ function CompactItemActions< Item >( { actions, }: CompactItemActionsProps< Item > ) { return ( - ( { } placement="bottom-end" > - - + + ); } diff --git a/packages/dataviews/src/components/dataviews-view-config/index.tsx b/packages/dataviews/src/components/dataviews-view-config/index.tsx index 9824f9c8e8c6f6..c8b26c51275891 100644 --- a/packages/dataviews/src/components/dataviews-view-config/index.tsx +++ b/packages/dataviews/src/components/dataviews-view-config/index.tsx @@ -51,7 +51,7 @@ import DataViewsContext from '../dataviews-context'; import { unlock } from '../../lock-unlock'; import DensityPicker from '../../dataviews-layouts/grid/density-picker'; -const { DropdownMenuV2 } = unlock( componentsPrivateApis ); +const { Menu } = unlock( componentsPrivateApis ); interface ViewTypeMenuProps { defaultLayouts?: SupportedLayouts; @@ -69,7 +69,7 @@ function ViewTypeMenu( { } const activeView = VIEW_LAYOUTS.find( ( v ) => view.type === v.type ); return ( - - - { config.label } - - + { config.label } + ); } ) } - + ); } diff --git a/packages/dataviews/src/dataviews-layouts/list/index.tsx b/packages/dataviews/src/dataviews-layouts/list/index.tsx index e737b18f5b02a9..82f9b5ea4d4fc5 100644 --- a/packages/dataviews/src/dataviews-layouts/list/index.tsx +++ b/packages/dataviews/src/dataviews-layouts/list/index.tsx @@ -32,7 +32,7 @@ import { useRegistry } from '@wordpress/data'; */ import { unlock } from '../../lock-unlock'; import { - ActionsDropdownMenuGroup, + ActionsMenuGroup, ActionModal, } from '../../components/dataviews-item-actions'; import type { Action, NormalizedField, ViewListProps } from '../../types'; @@ -49,7 +49,7 @@ interface ListViewItemProps< Item > { onDropdownTriggerKeyDown: React.KeyboardEventHandler< HTMLButtonElement >; } -const { DropdownMenuV2: DropdownMenu } = unlock( componentsPrivateApis ); +const { Menu } = unlock( componentsPrivateApis ); function generateItemWrapperCompositeId( idPrefix: string ) { return `${ idPrefix }-item-wrapper`; @@ -195,7 +195,7 @@ function ListItem< Item >( { /> ) }
- ( { } placement="bottom-end" > - - +
); diff --git a/packages/dataviews/src/dataviews-layouts/table/column-header-menu.tsx b/packages/dataviews/src/dataviews-layouts/table/column-header-menu.tsx index aff211fb613dcf..7071e54620f369 100644 --- a/packages/dataviews/src/dataviews-layouts/table/column-header-menu.tsx +++ b/packages/dataviews/src/dataviews-layouts/table/column-header-menu.tsx @@ -29,7 +29,7 @@ import type { } from '../../types'; import { getVisibleFieldIds } from '../index'; -const { DropdownMenuV2 } = unlock( componentsPrivateApis ); +const { Menu } = unlock( componentsPrivateApis ); interface HeaderMenuProps< Item > { fieldId: string; @@ -40,12 +40,12 @@ interface HeaderMenuProps< Item > { setOpenedFilter: ( fieldId: string ) => void; } -function WithDropDownMenuSeparators( { children }: { children: ReactNode } ) { +function WithMenuSeparators( { children }: { children: ReactNode } ) { return Children.toArray( children ) .filter( Boolean ) .map( ( child, i ) => ( - { i > 0 && } + { i > 0 && } { child } ) ); @@ -101,7 +101,7 @@ const _HeaderMenu = forwardRef( function HeaderMenu< Item >( } return ( - ( } style={ { minWidth: '240px' } } > - + { isSortable && ( - + { SORTING_DIRECTIONS.map( ( direction: SortDirection ) => { const isChecked = @@ -133,7 +133,7 @@ const _HeaderMenu = forwardRef( function HeaderMenu< Item >( const value = `${ fieldId }-${ direction }`; return ( - ( } ); } } > - + { sortLabels[ direction ] } - - + + ); } ) } - + ) } { canAddFilter && ( - - + } onClick={ () => { setOpenedFilter( fieldId ); @@ -182,14 +182,14 @@ const _HeaderMenu = forwardRef( function HeaderMenu< Item >( } ); } } > - + { __( 'Add filter' ) } - - - + + + ) } - - + } disabled={ index < 1 } onClick={ () => { @@ -207,11 +207,9 @@ const _HeaderMenu = forwardRef( function HeaderMenu< Item >( } ); } } > - - { __( 'Move left' ) } - - - { __( 'Move left' ) } + + } disabled={ index >= visibleFieldIds.length - 1 } onClick={ () => { @@ -227,12 +225,10 @@ const _HeaderMenu = forwardRef( function HeaderMenu< Item >( } ); } } > - - { __( 'Move right' ) } - - + { __( 'Move right' ) } + { isHidable && field && ( - } onClick={ () => { onHide( field ); @@ -244,14 +240,14 @@ const _HeaderMenu = forwardRef( function HeaderMenu< Item >( } ); } } > - + { __( 'Hide column' ) } - - + + ) } - - - + + + ); } ); diff --git a/packages/edit-site/src/components/global-styles/font-sizes/font-size.js b/packages/edit-site/src/components/global-styles/font-sizes/font-size.js index 43418f9caf5eb6..25dcc69185cae6 100644 --- a/packages/edit-site/src/components/global-styles/font-sizes/font-size.js +++ b/packages/edit-site/src/components/global-styles/font-sizes/font-size.js @@ -27,7 +27,7 @@ import ConfirmDeleteFontSizeDialog from './confirm-delete-font-size-dialog'; import RenameFontSizeDialog from './rename-font-size-dialog'; import SizeControl from '../size-control'; -const { DropdownMenuV2 } = unlock( componentsPrivateApis ); +const { Menu } = unlock( componentsPrivateApis ); const { useGlobalSetting } = unlock( blockEditorPrivateApis ); function FontSize() { @@ -166,7 +166,7 @@ function FontSize() { marginBottom={ 0 } paddingX={ 4 } > - } > - - + + { __( 'Rename' ) } - - - - + + + + { __( 'Delete' ) } - - - + + + ) } diff --git a/packages/edit-site/src/components/global-styles/font-sizes/font-sizes.js b/packages/edit-site/src/components/global-styles/font-sizes/font-sizes.js index e09dda1a82fe81..4bda7a7b3266b5 100644 --- a/packages/edit-site/src/components/global-styles/font-sizes/font-sizes.js +++ b/packages/edit-site/src/components/global-styles/font-sizes/font-sizes.js @@ -27,7 +27,7 @@ import { useState } from '@wordpress/element'; * Internal dependencies */ import { unlock } from '../../../lock-unlock'; -const { DropdownMenuV2 } = unlock( componentsPrivateApis ); +const { Menu } = unlock( componentsPrivateApis ); const { useGlobalSetting } = unlock( blockEditorPrivateApis ); import Subtitle from '../subtitle'; import { NavigationButtonAsItem } from '../navigation-button'; @@ -81,7 +81,7 @@ function FontSizeGroup( { /> ) } { !! handleResetFontSizes && ( - } > - - + + { origin === 'custom' ? __( 'Remove font size presets' ) : __( 'Reset font size presets' ) } - - - + + + ) } diff --git a/packages/edit-site/src/components/global-styles/shadows-edit-panel.js b/packages/edit-site/src/components/global-styles/shadows-edit-panel.js index 7321e927fbbae3..127480ee5af497 100644 --- a/packages/edit-site/src/components/global-styles/shadows-edit-panel.js +++ b/packages/edit-site/src/components/global-styles/shadows-edit-panel.js @@ -51,7 +51,7 @@ import { } from './shadow-utils'; const { useGlobalSetting } = unlock( blockEditorPrivateApis ); -const { DropdownMenuV2 } = unlock( componentsPrivateApis ); +const { Menu } = unlock( componentsPrivateApis ); const customShadowMenuItems = [ { @@ -163,7 +163,7 @@ export default function ShadowsEditPanel() { - ( - onMenuClick( item.action ) } disabled={ @@ -185,12 +185,12 @@ export default function ShadowsEditPanel() { baseSelectedShadow.shadow } > - + { item.label } - - + + ) ) } - + diff --git a/packages/editor/src/components/post-actions/index.js b/packages/editor/src/components/post-actions/index.js index 8a3850e8f547c1..9f39b1f3305aeb 100644 --- a/packages/editor/src/components/post-actions/index.js +++ b/packages/editor/src/components/post-actions/index.js @@ -18,7 +18,7 @@ import { store as coreStore } from '@wordpress/core-data'; import { unlock } from '../../lock-unlock'; import { usePostActions } from './actions'; -const { DropdownMenuV2, kebabCase } = unlock( componentsPrivateApis ); +const { Menu, kebabCase } = unlock( componentsPrivateApis ); export default function PostActions( { postType, postId, onActionPerformed } ) { const [ isActionsMenuOpen, setIsActionsMenuOpen ] = useState( false ); @@ -54,7 +54,7 @@ export default function PostActions( { postType, postId, onActionPerformed } ) { }, [ allActions, itemWithPermissions ] ); return ( - - + ); } @@ -93,12 +93,9 @@ function DropdownMenuItemTrigger( { action, onClick, items } ) { const label = typeof action.label === 'string' ? action.label : action.label( items ); return ( - - { label } - + + { label } + ); } @@ -145,7 +142,7 @@ function ActionWithModal( { action, item, ActionTrigger, onClose } ) { // With an added onClose prop. function ActionsDropdownMenuGroup( { actions, item, onClose } ) { return ( - + { actions.map( ( action ) => { if ( action.RenderModal ) { return ( @@ -167,6 +164,6 @@ function ActionsDropdownMenuGroup( { actions, item, onClose } ) { /> ); } ) } - + ); } From bcdba599018c2b210ab1af789b21326bd53211b7 Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Wed, 30 Oct 2024 01:27:34 +0900 Subject: [PATCH 03/56] PostTaxonomiesFlatTermSelector: Restore space between tag list and most used tags (#66566) * PostTaxonomiesFlatTermSelector: Restore space between tag list and most used tags * Update packages/editor/src/components/post-taxonomies/flat-term-selector.js Co-authored-by: Lena Morita --------- Co-authored-by: t-hamano Co-authored-by: mirka <0mirka00@git.wordpress.org> --- .../post-taxonomies/flat-term-selector.js | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/packages/editor/src/components/post-taxonomies/flat-term-selector.js b/packages/editor/src/components/post-taxonomies/flat-term-selector.js index 5f581e898c953e..21a62c9819026d 100644 --- a/packages/editor/src/components/post-taxonomies/flat-term-selector.js +++ b/packages/editor/src/components/post-taxonomies/flat-term-selector.js @@ -2,8 +2,12 @@ * WordPress dependencies */ import { __, _x, sprintf } from '@wordpress/i18n'; -import { useEffect, useMemo, useState } from '@wordpress/element'; -import { FormTokenField, withFilters } from '@wordpress/components'; +import { Fragment, useEffect, useMemo, useState } from '@wordpress/element'; +import { + FormTokenField, + withFilters, + __experimentalVStack as VStack, +} from '@wordpress/components'; import { useSelect, useDispatch } from '@wordpress/data'; import deprecated from '@wordpress/deprecated'; import { store as coreStore } from '@wordpress/core-data'; @@ -288,8 +292,15 @@ export function FlatTermSelector( { slug, __nextHasNoMarginBottom } ) { singularName ); + const Wrapper = ( { children } ) => + __nextHasNoMarginBottom ? ( + { children } + ) : ( + { children } + ); + return ( - <> + - + ); } From aa7bb5aab3ba601afe4a3f887fb18150021bd437 Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Wed, 30 Oct 2024 01:28:24 +0900 Subject: [PATCH 04/56] Fix zoom out shortcut on Windows (#66506) Co-authored-by: t-hamano Co-authored-by: ntsekouras Co-authored-by: Mamaduka Co-authored-by: carolinan Co-authored-by: torounit --- packages/editor/src/components/zoom-out-toggle/index.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/editor/src/components/zoom-out-toggle/index.js b/packages/editor/src/components/zoom-out-toggle/index.js index 81506add699c97..080a4c58578069 100644 --- a/packages/editor/src/components/zoom-out-toggle/index.js +++ b/packages/editor/src/components/zoom-out-toggle/index.js @@ -12,6 +12,7 @@ import { useShortcut, store as keyboardShortcutsStore, } from '@wordpress/keyboard-shortcuts'; +import { isAppleOS } from '@wordpress/keycodes'; /** * Internal dependencies @@ -40,7 +41,9 @@ const ZoomOutToggle = ( { disabled } ) => { category: 'global', description: __( 'Enter or exit zoom out.' ), keyCombination: { - modifier: 'primaryShift', + // `primaryShift+0` (`ctrl+shift+0`) is the shortcut for switching + // to input mode in Windows, so apply a different key combination. + modifier: isAppleOS() ? 'primaryShift' : 'secondary', character: '0', }, } ); From 6ffb6720e1edbfa7ccf8603ea31c66a1e7521423 Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Tue, 29 Oct 2024 20:41:54 +0100 Subject: [PATCH 05/56] Interface: remove unused private API support (#66565) Co-authored-by: jsnajdr Co-authored-by: ellatrix Co-authored-by: tyxla --- package-lock.json | 1 - packages/interface/lock-unlock.js | 10 ---------- packages/interface/package.json | 1 - packages/private-apis/src/implementation.js | 1 - 4 files changed, 13 deletions(-) delete mode 100644 packages/interface/lock-unlock.js diff --git a/package-lock.json b/package-lock.json index a1060b1267af50..c3ffa28d21f3bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -54605,7 +54605,6 @@ "@wordpress/icons": "*", "@wordpress/plugins": "*", "@wordpress/preferences": "*", - "@wordpress/private-apis": "*", "@wordpress/viewport": "*", "clsx": "^2.1.1" }, diff --git a/packages/interface/lock-unlock.js b/packages/interface/lock-unlock.js deleted file mode 100644 index 1e5bb39ed18c6d..00000000000000 --- a/packages/interface/lock-unlock.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * WordPress dependencies - */ -import { __dangerousOptInToUnstableAPIsOnlyForCoreModules } from '@wordpress/private-apis'; - -export const { lock, unlock } = - __dangerousOptInToUnstableAPIsOnlyForCoreModules( - 'I acknowledge private features are not for use in themes or plugins and doing so will break in the next version of WordPress.', - '@wordpress/interface' - ); diff --git a/packages/interface/package.json b/packages/interface/package.json index e5870e0dfadfb2..ed9e0debf96d7c 100644 --- a/packages/interface/package.json +++ b/packages/interface/package.json @@ -43,7 +43,6 @@ "@wordpress/icons": "*", "@wordpress/plugins": "*", "@wordpress/preferences": "*", - "@wordpress/private-apis": "*", "@wordpress/viewport": "*", "clsx": "^2.1.1" }, diff --git a/packages/private-apis/src/implementation.js b/packages/private-apis/src/implementation.js index ab0ebfae7ecb05..b32a95986d32c9 100644 --- a/packages/private-apis/src/implementation.js +++ b/packages/private-apis/src/implementation.js @@ -25,7 +25,6 @@ const CORE_MODULES_USING_PRIVATE_APIS = [ '@wordpress/edit-widgets', '@wordpress/editor', '@wordpress/format-library', - '@wordpress/interface', '@wordpress/patterns', '@wordpress/preferences', '@wordpress/reusable-blocks', From 828868d680a4f052c35854d0ed0fb217179664b8 Mon Sep 17 00:00:00 2001 From: Ramon Date: Wed, 30 Oct 2024 07:24:47 +1100 Subject: [PATCH 06/56] Backport from Core: Reuse block metadata in WP_Theme_JSON::get_valid_block_style_variations() for better performance (#66539) * Backporting theme json performance optimizations from https://github.com/WordPress/wordpress-develop/pull/7586 * Tentatively backport using `get_blocks_metadata` instead of `WP_Block_Type_Registry` to get block names. Co-authored-by: ramonjd Co-authored-by: mukeshpanchal27 Co-authored-by: aaronrobertshaw --- lib/class-wp-theme-json-gutenberg.php | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 5975e4fe9852ff..09f388e7d3b93f 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -745,10 +745,10 @@ public function __construct( $theme_json = array( 'version' => WP_Theme_JSON_Gut } $this->theme_json = WP_Theme_JSON_Schema_Gutenberg::migrate( $theme_json, $origin ); - $registry = WP_Block_Type_Registry::get_instance(); - $valid_block_names = array_keys( $registry->get_all_registered() ); + $blocks_metadata = static::get_blocks_metadata(); + $valid_block_names = array_keys( $blocks_metadata ); $valid_element_names = array_keys( static::ELEMENTS ); - $valid_variations = static::get_valid_block_style_variations(); + $valid_variations = static::get_valid_block_style_variations( $blocks_metadata ); $this->theme_json = static::unwrap_shared_block_style_variations( $this->theme_json, $valid_variations ); $this->theme_json = static::sanitize( $this->theme_json, $valid_block_names, $valid_element_names, $valid_variations ); $this->theme_json = static::maybe_opt_in_into_settings( $this->theme_json ); @@ -3506,9 +3506,10 @@ public static function remove_insecure_properties( $theme_json, $origin = 'theme $theme_json = WP_Theme_JSON_Schema_Gutenberg::migrate( $theme_json, $origin ); - $valid_block_names = array_keys( static::get_blocks_metadata() ); + $blocks_metadata = static::get_blocks_metadata(); + $valid_block_names = array_keys( $blocks_metadata ); $valid_element_names = array_keys( static::ELEMENTS ); - $valid_variations = static::get_valid_block_style_variations(); + $valid_variations = static::get_valid_block_style_variations( $blocks_metadata ); $theme_json = static::sanitize( $theme_json, $valid_block_names, $valid_element_names, $valid_variations ); @@ -4511,11 +4512,16 @@ function ( $matches ) use ( $variation_class ) { /** * Collects valid block style variations keyed by block type. * + * @since 6.6.0 + * @since 6.8.0 Added the `$blocks_metadata` parameter. + * + * @param array $blocks_metadata Optional. List of metadata per block. Default is the metadata for all blocks. * @return array Valid block style variations by block type. */ - protected static function get_valid_block_style_variations() { + protected static function get_valid_block_style_variations( $blocks_metadata = array() ) { $valid_variations = array(); - foreach ( self::get_blocks_metadata() as $block_name => $block_meta ) { + $blocks_metadata = empty( $blocks_metadata ) ? static::get_blocks_metadata() : $blocks_metadata; + foreach ( $blocks_metadata as $block_name => $block_meta ) { if ( ! isset( $block_meta['styleVariations'] ) ) { continue; } From 246258c765e8d44623e72fbb629a8c21b6e25650 Mon Sep 17 00:00:00 2001 From: Hiroshi Urabe Date: Wed, 30 Oct 2024 16:52:51 +0900 Subject: [PATCH 07/56] Fix Parent Check Condition in `buildTermsTree` (#66006) * Use undefined instead of null in `buildTermsTree` * allow null * use undefined * remove null checking --- packages/editor/src/utils/terms.js | 6 ++++-- packages/editor/src/utils/test/terms.js | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/editor/src/utils/terms.js b/packages/editor/src/utils/terms.js index 2d34879548a56c..e3154a6480a0fa 100644 --- a/packages/editor/src/utils/terms.js +++ b/packages/editor/src/utils/terms.js @@ -14,14 +14,16 @@ export function buildTermsTree( flatTerms ) { const flatTermsWithParentAndChildren = flatTerms.map( ( term ) => { return { children: [], - parent: null, + parent: undefined, ...term, }; } ); // All terms should have a `parent` because we're about to index them by it. if ( - flatTermsWithParentAndChildren.some( ( { parent } ) => parent === null ) + flatTermsWithParentAndChildren.some( + ( { parent } ) => parent === undefined + ) ) { return flatTermsWithParentAndChildren; } diff --git a/packages/editor/src/utils/test/terms.js b/packages/editor/src/utils/test/terms.js index 68a1a7f10fb190..fa7d1622370c7a 100644 --- a/packages/editor/src/utils/test/terms.js +++ b/packages/editor/src/utils/test/terms.js @@ -4,14 +4,14 @@ import { buildTermsTree } from '../terms'; describe( 'buildTermsTree()', () => { - it( 'Should return same array as input with null parent and empty children added if parent is never specified.', () => { + it( 'Should return same array as input with undefined parent and empty children added if parent is never specified.', () => { const input = Object.freeze( [ { id: 2232, dummy: true }, { id: 2245, dummy: true }, ] ); const output = Object.freeze( [ - { id: 2232, parent: null, children: [], dummy: true }, - { id: 2245, parent: null, children: [], dummy: true }, + { id: 2232, parent: undefined, children: [], dummy: true }, + { id: 2245, parent: undefined, children: [], dummy: true }, ] ); const termsTreem = buildTermsTree( input ); expect( termsTreem ).toEqual( output ); From 49a931897ac2a5d438f05dbaaa59ceedab71a0f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9?= <583546+oandregal@users.noreply.github.com> Date: Wed, 30 Oct 2024 09:45:30 +0100 Subject: [PATCH 08/56] Templates: hide mediaField in list view if not provided (#66573) Co-authored-by: oandregal Co-authored-by: ntsekouras --- .../dataviews/src/dataviews-layouts/list/index.tsx | 12 +++++------- .../dataviews/src/dataviews-layouts/list/style.scss | 6 ------ .../edit-site/src/components/page-templates/index.js | 1 - 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/packages/dataviews/src/dataviews-layouts/list/index.tsx b/packages/dataviews/src/dataviews-layouts/list/index.tsx index 82f9b5ea4d4fc5..a4f94e482c69b3 100644 --- a/packages/dataviews/src/dataviews-layouts/list/index.tsx +++ b/packages/dataviews/src/dataviews-layouts/list/index.tsx @@ -176,10 +176,10 @@ function ListItem< Item >( { }, [ actions, item ] ); const renderedMediaField = mediaField?.render ? ( - - ) : ( -
- ); +
+ +
+ ) : null; const renderedPrimaryField = primaryField?.render ? ( @@ -248,9 +248,7 @@ function ListItem< Item >( { /> -
- { renderedMediaField } -
+ { renderedMediaField }