From 85d9baddc0d3fcec492299ed52e08c09bcb72e24 Mon Sep 17 00:00:00 2001 From: Ankit Kumar Shah Date: Thu, 9 Jan 2025 12:08:35 +0530 Subject: [PATCH 1/8] Remove storybook for internal BlockCard component (#68556) Co-authored-by: Infinite-Null Co-authored-by: Mamaduka --- .../block-card/stories/index.story.js | 79 ------------------- 1 file changed, 79 deletions(-) delete mode 100644 packages/block-editor/src/components/block-card/stories/index.story.js diff --git a/packages/block-editor/src/components/block-card/stories/index.story.js b/packages/block-editor/src/components/block-card/stories/index.story.js deleted file mode 100644 index 0fe68e2032d394..00000000000000 --- a/packages/block-editor/src/components/block-card/stories/index.story.js +++ /dev/null @@ -1,79 +0,0 @@ -/** - * WordPress dependencies - */ -import { box, button, cog, paragraph } from '@wordpress/icons'; - -/** - * Internal dependencies - */ -import BlockCard from '../'; - -const meta = { - title: 'BlockEditor/BlockCard', - component: BlockCard, - parameters: { - docs: { - description: { - component: - 'The `BlockCard` component allows to display a "card" which contains the title of a block, its icon and its description.', - }, - canvas: { sourceState: 'shown' }, - }, - }, - argTypes: { - title: { - control: 'text', - description: 'The title of the block.', - table: { - type: { summary: 'string' }, - }, - }, - description: { - control: 'text', - description: 'A description of the block functionality.', - table: { - type: { summary: 'string' }, - }, - }, - icon: { - control: 'select', - options: [ 'paragraph', 'cog', 'box', 'button' ], - mapping: { - paragraph, - cog, - box, - button, - }, - description: - 'The icon of the block. This can be any of [WordPress Dashicons](https://developer.wordpress.org/resource/dashicons/), or a custom `svg` element.', - table: { - type: { summary: 'string | object' }, - }, - }, - name: { - control: 'text', - description: 'Optional custom name for the block.', - table: { - type: { summary: 'string' }, - }, - }, - className: { - control: 'text', - description: 'Additional CSS class names.', - table: { - type: { summary: 'string' }, - }, - }, - }, -}; - -export default meta; - -export const Default = { - args: { - title: 'Paragraph', - icon: paragraph, - description: 'This is a paragraph block description.', - name: 'Paragraph Block', - }, -}; From e746a95613db3eade8340c940cedbbd2b10145d6 Mon Sep 17 00:00:00 2001 From: Eshaan Dabasiya <76681468+im3dabasia@users.noreply.github.com> Date: Thu, 9 Jan 2025 15:00:33 +0530 Subject: [PATCH 2/8] fix: Update regex in valid-sprintf rule to handle '%%' (#68270) Co-authored-by: im3dabasia Co-authored-by: swissspidy Co-authored-by: colorful-tones --- .../eslint-plugin/rules/__tests__/valid-sprintf.js | 12 ++++++++++++ packages/eslint-plugin/utils/constants.js | 14 +++++++------- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/packages/eslint-plugin/rules/__tests__/valid-sprintf.js b/packages/eslint-plugin/rules/__tests__/valid-sprintf.js index 9b2b7de255d47b..8f5b77458fbaeb 100644 --- a/packages/eslint-plugin/rules/__tests__/valid-sprintf.js +++ b/packages/eslint-plugin/rules/__tests__/valid-sprintf.js @@ -71,6 +71,18 @@ sprintf( { code: `sprintf( '%(greeting)s %(toWhom)s', 'Hello', 'World' )`, }, + { + code: `sprintf( 'Rotated at %d %% degrees', 90 )`, + }, + { + code: `sprintf( 'Rotated at %d%% degrees', 90 )`, + }, + { + code: `sprintf( __( 'Rotated at %d%% degrees' ), 90 )`, + }, + { + code: `sprintf( 'Rotated at %1$d %% degrees, %2$d %% angles', 90, 180 )`, + }, ], invalid: [ { diff --git a/packages/eslint-plugin/utils/constants.js b/packages/eslint-plugin/utils/constants.js index a19add74964c0e..44e881fb867c78 100644 --- a/packages/eslint-plugin/utils/constants.js +++ b/packages/eslint-plugin/utils/constants.js @@ -37,13 +37,13 @@ const TRANSLATION_FUNCTIONS = new Set( [ '__', '_x', '_n', '_nx' ] ); * @type {RegExp} */ const REGEXP_SPRINTF_PLACEHOLDER = - /%(((\d+)\$)|(\(([$_a-zA-Z][$_a-zA-Z0-9]*)\)))?[ +0#-]*\d*(\.(\d+|\*))?(ll|[lhqL])?([cduxXefgsp%])/g; -// ▲ ▲ ▲ ▲ ▲ ▲ ▲ type -// │ │ │ │ │ └ Length (unsupported) -// │ │ │ │ └ Precision / max width -// │ │ │ └ Min width (unsupported) -// │ │ └ Flags (unsupported) -// └ Index └ Name (for named arguments) + /(? Date: Thu, 9 Jan 2025 16:37:53 +0100 Subject: [PATCH 3/8] Add npm script to profile TypeScript builds (#68533) Co-authored-by: ciampo Co-authored-by: tyxla Co-authored-by: t-hamano --- .gitignore | 1 + package.json | 1 + react-scanner.config.js | 1 + 3 files changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 1d75f9f429d869..9e7e4333af8689 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ results /test/e2e/artifacts /perf-envs /composer.lock +/ts-traces # The /.cache folder is needed for phpcs to cache results between runs, while other .cache folders must be ignored # It is not possible to re-include a file if a parent directory of that file is excluded diff --git a/package.json b/package.json index ba2ef003b0dd1d..1d54299515ff64 100644 --- a/package.json +++ b/package.json @@ -181,6 +181,7 @@ "build": "npm run build:packages && wp-scripts build", "build:analyze-bundles": "npm run build -- --webpack-bundle-analyzer", "build:package-types": "node ./bin/packages/validate-typescript-version.js && ( tsc --build || ( echo 'tsc failed. Try cleaning up first: `npm run clean:package-types`'; exit 1 ) ) && node ./bin/packages/check-build-type-declaration-files.js", + "build:profile-types": "rimraf ./ts-traces && npm run clean:package-types && node ./bin/packages/validate-typescript-version.js && ( tsc --build --extendedDiagnostics --generateTrace ./ts-traces || ( echo 'tsc failed.'; exit 1 ) ) && node ./bin/packages/check-build-type-declaration-files.js && npx --yes @typescript/analyze-trace ts-traces > ts-traces/analysis.txt && echo $'\n\nDone! Build traces saved to ts-traces/ directory.\nTrace analysis saved to ts-traces/analysis.txt.'", "prebuild:packages": "npm run clean:packages && npm run --if-present --workspaces build", "build:packages": "npm run --silent build:package-types && node ./bin/packages/build.js", "postbuild:packages": " npm run --if-present --workspaces build:wp", diff --git a/react-scanner.config.js b/react-scanner.config.js index 7501e7e8fc3ab0..853bbde69327c6 100644 --- a/react-scanner.config.js +++ b/react-scanner.config.js @@ -19,6 +19,7 @@ module.exports = { 'storybook', 'test', 'tools', + 'ts-traces', 'typings', 'vendor', ], From ddfc0258d0891d302c3965238ac711fa02bdde62 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Thu, 9 Jan 2025 17:17:21 +0100 Subject: [PATCH 4/8] Menu: auto-generate README (#68249) Co-authored-by: ciampo Co-authored-by: mirka <0mirka00@git.wordpress.org> Co-authored-by: tyxla --- packages/components/src/menu/README.md | 595 +++++++++++++----- .../components/src/menu/checkbox-item.tsx | 24 +- packages/components/src/menu/context.tsx | 6 +- .../components/src/menu/docs-manifest.json | 62 ++ packages/components/src/menu/group-label.tsx | 14 +- packages/components/src/menu/group.tsx | 18 +- packages/components/src/menu/index.tsx | 132 +++- .../components/src/menu/item-help-text.tsx | 12 +- packages/components/src/menu/item-label.tsx | 12 +- packages/components/src/menu/item.tsx | 24 +- packages/components/src/menu/popover.tsx | 18 +- packages/components/src/menu/radio-item.tsx | 24 +- packages/components/src/menu/separator.tsx | 14 +- .../src/menu/stories/best-practices.mdx | 38 ++ .../src/menu/stories/index.story.tsx | 16 +- packages/components/src/menu/styles.ts | 48 +- .../src/menu/submenu-trigger-item.tsx | 18 +- .../components/src/menu/trigger-button.tsx | 12 +- packages/components/src/menu/types.ts | 20 +- storybook/manager-head.html | 1 + 20 files changed, 758 insertions(+), 350 deletions(-) create mode 100644 packages/components/src/menu/docs-manifest.json create mode 100644 packages/components/src/menu/stories/best-practices.mdx diff --git a/packages/components/src/menu/README.md b/packages/components/src/menu/README.md index 6732610c0c6cae..12f120b871f85d 100644 --- a/packages/components/src/menu/README.md +++ b/packages/components/src/menu/README.md @@ -1,344 +1,591 @@ -# `Menu` +# Menu -
-This feature is still experimental. “Experimental” means this is an early implementation subject to drastic and breaking changes. -
+ -`Menu` displays a menu to the user (such as a set of actions or functions). The menu is rendered in a popover (this pattern is also known as a "Dropdown menu"), which is triggered by a button. +🔒 This component is locked as a [private API](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-private-apis/). We do not yet recommend using this outside of the Gutenberg project. -## Design guidelines +

See the WordPress Storybook for more detailed, interactive documentation.

-### Usage +Menu is a collection of React components that combine to render +ARIA-compliant [menu](https://www.w3.org/WAI/ARIA/apg/patterns/menu/) and +[menu button](https://www.w3.org/WAI/ARIA/apg/patterns/menubutton/) patterns. -#### When to use a `Menu` +`Menu` itself is a wrapper component and context provider. +It is responsible for managing the state of the menu and its items, and for +rendering the `Menu.TriggerButton` (or the `Menu.SubmenuTriggerItem`) +component, and the `Menu.Popover` component. -Use a `Menu` when you want users to: +## Props -- Choose an action or change a setting from a list, AND -- Only see the available choices contextually. +### `as` -`Menu` is a React component to render an expandable menu of buttons. It is similar in purpose to a `` element, with the distinction that it does not maintain a value. Instead, each option behaves as an action button. + +If you need to display all the available options at all times, consider using a Toolbar instead. Use a `Menu` to display a list of actions after the user interacts with a button. + +**Do** +Use a `Menu` to display a list of actions after the user interacts with an icon. + +**Don’t** use a `Menu` for important actions that should always be visible. Use a `Toolbar` instead. + +**Don’t** +Don’t use a `Menu` for frequently used actions. Use a `Toolbar` instead. + +### Behavior + +Generally, the parent button should indicate that interacting with it will show a `Menu`. + +The parent button should retain the same visual styling regardless of whether the `Menu` is displayed or not. + +### Placement + +The `Menu` should typically appear directly below, or below and to the left of, the parent button. If there isn’t enough space below to display the full `Menu`, it can be displayed instead above the parent button. diff --git a/packages/components/src/menu/stories/index.story.tsx b/packages/components/src/menu/stories/index.story.tsx index 37ebb6f905dc84..de9e4cdd652102 100644 --- a/packages/components/src/menu/stories/index.story.tsx +++ b/packages/components/src/menu/stories/index.story.tsx @@ -20,10 +20,10 @@ import Button from '../../button'; import Modal from '../../modal'; import { createSlotFill, Provider as SlotFillProvider } from '../../slot-fill'; import { ContextSystemProvider } from '../../context'; -import type { MenuProps } from '../types'; +import type { Props } from '../types'; const meta: Meta< typeof Menu > = { - id: 'components-experimental-menu', + id: 'components-menu', title: 'Components (Experimental)/Actions/Menu', component: Menu, subcomponents: { @@ -183,7 +183,7 @@ export const WithSubmenu: StoryObj< typeof Menu > = { }; export const WithCheckboxes: StoryObj< typeof Menu > = { - render: function WithCheckboxes( props: MenuProps ) { + render: function WithCheckboxes( props: Props ) { const [ isAChecked, setAChecked ] = useState( false ); const [ isBChecked, setBChecked ] = useState( true ); const [ multipleCheckboxesValue, setMultipleCheckboxesValue ] = @@ -333,7 +333,7 @@ export const WithCheckboxes: StoryObj< typeof Menu > = { }; export const WithRadios: StoryObj< typeof Menu > = { - render: function WithRadios( props: MenuProps ) { + render: function WithRadios( props: Props ) { const [ radioValue, setRadioValue ] = useState( 'two' ); const onRadioChange: React.ComponentProps< typeof Menu.RadioItem @@ -411,7 +411,7 @@ const modalOnTopOfMenuPopover = css` `; export const WithModals: StoryObj< typeof Menu > = { - render: function WithModals( props: MenuProps ) { + render: function WithModals( props: Props ) { const [ isOuterModalOpen, setOuterModalOpen ] = useState( false ); const [ isInnerModalOpen, setInnerModalOpen ] = useState( false ); @@ -527,7 +527,7 @@ const Fill = ( { children }: { children: React.ReactNode } ) => { }; export const WithSlotFill: StoryObj< typeof Menu > = { - render: ( props: MenuProps ) => { + render: ( props: Props ) => { return ( @@ -579,7 +579,7 @@ const toolbarVariantContextValue = { }; export const ToolbarVariant: StoryObj< typeof Menu > = { - render: ( props: MenuProps ) => ( + render: ( props: Props ) => ( // TODO: add toolbar @@ -619,7 +619,7 @@ export const ToolbarVariant: StoryObj< typeof Menu > = { }; export const InsideModal: StoryObj< typeof Menu > = { - render: function InsideModal( props: MenuProps ) { + render: function InsideModal( props: Props ) { const [ isModalOpen, setModalOpen ] = useState( false ); return ( <> diff --git a/packages/components/src/menu/styles.ts b/packages/components/src/menu/styles.ts index cda5c7321f38b4..1235d6ae7ec1b4 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 { MenuContext } from './types'; +import type { ContextProps } from './types'; const ANIMATION_PARAMS = { SCALE_AMOUNT_OUTER: 0.82, @@ -42,8 +42,8 @@ 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< MenuContext, 'variant' > +export const PopoverOuterWrapper = styled.div< + Pick< ContextProps, 'variant' > >` position: relative; @@ -95,7 +95,7 @@ export const MenuPopoverOuterWrapper = styled.div< } `; -export const MenuPopoverInnerWrapper = styled.div` +export const PopoverInnerWrapper = styled.div` position: relative; /* Same as popover component */ /* TODO: is there a way to read the sass variable? */ @@ -219,7 +219,7 @@ const baseItem = css` } /* When the item is the trigger of an open submenu */ - ${ MenuPopoverInnerWrapper }:not(:focus) &:not(:focus)[aria-expanded="true"] { + ${ PopoverInnerWrapper }:not(:focus) &:not(:focus)[aria-expanded="true"] { background-color: ${ LIGHT_BACKGROUND_COLOR }; color: ${ COLORS.theme.foreground }; } @@ -229,15 +229,15 @@ const baseItem = css` } `; -export const MenuItem = styled( Ariakit.MenuItem )` +export const Item = styled( Ariakit.MenuItem )` ${ baseItem }; `; -export const MenuCheckboxItem = styled( Ariakit.MenuItemCheckbox )` +export const CheckboxItem = styled( Ariakit.MenuItemCheckbox )` ${ baseItem }; `; -export const MenuRadioItem = styled( Ariakit.MenuItemRadio )` +export const RadioItem = 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. */ - ${ MenuCheckboxItem } > &, - ${ MenuRadioItem } > & { + ${ CheckboxItem } > &, + ${ RadioItem } > & { /* Same width as the check icons */ min-width: ${ space( 6 ) }; } - ${ MenuCheckboxItem } > &, - ${ MenuRadioItem } > &, + ${ CheckboxItem } > &, + ${ RadioItem } > &, &:not( :empty ) { margin-inline-end: ${ space( 2 ) }; } @@ -278,7 +278,7 @@ export const ItemPrefixWrapper = styled.span` } `; -export const MenuItemContentWrapper = styled.div` +export const ItemContentWrapper = 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 MenuItemContentWrapper = styled.div` pointer-events: none; `; -export const MenuItemChildrenWrapper = styled.div` +export const ItemChildrenWrapper = styled.div` flex: 1; display: inline-flex; @@ -317,19 +317,19 @@ export const ItemSuffixWrapper = styled.span` * When the parent menu item is active, except when it's a non-focused/hovered * submenu trigger (in that case, color should not be inherited) */ - [data-active-item]:not( [data-focus-visible] ) *:not(${ MenuPopoverInnerWrapper }) &, + [data-active-item]:not( [data-focus-visible] ) *:not(${ PopoverInnerWrapper }) &, /* When the parent menu item is disabled */ - [aria-disabled='true'] *:not(${ MenuPopoverInnerWrapper }) & { + [aria-disabled='true'] *:not(${ PopoverInnerWrapper }) & { color: inherit; } `; -export const MenuGroup = styled( Ariakit.MenuGroup )` +export const Group = styled( Ariakit.MenuGroup )` /* Ignore this element when calculating the layout. Useful for subgrid */ display: contents; `; -export const MenuGroupLabel = styled( Ariakit.MenuGroupLabel )` +export const GroupLabel = styled( Ariakit.MenuGroupLabel )` /* Occupy the width of all grid columns (ie. full width) */ grid-column: 1 / -1; @@ -338,8 +338,8 @@ export const MenuGroupLabel = styled( Ariakit.MenuGroupLabel )` padding-inline: ${ ITEM_PADDING_INLINE }; `; -export const MenuSeparator = styled( Ariakit.MenuSeparator )< - Pick< MenuContext, 'variant' > +export const Separator = styled( Ariakit.MenuSeparator )< + Pick< ContextProps, 'variant' > >` /* Occupy the width of all grid columns (ie. full width) */ grid-column: 1 / -1; @@ -370,22 +370,22 @@ export const SubmenuChevronIcon = styled( Icon )` ) }; `; -export const MenuItemLabel = styled( Truncate )` +export const ItemLabel = styled( Truncate )` font-size: ${ font( 'default.fontSize' ) }; line-height: 20px; color: inherit; `; -export const MenuItemHelpText = styled( Truncate )` +export const ItemHelpText = styled( Truncate )` font-size: ${ font( 'helpText.fontSize' ) }; line-height: 16px; color: ${ LIGHTER_TEXT_COLOR }; overflow-wrap: anywhere; [data-active-item]:not( [data-focus-visible] ) - *:not( ${ MenuPopoverInnerWrapper } ) + *:not( ${ PopoverInnerWrapper } ) &, - [aria-disabled='true'] *:not( ${ MenuPopoverInnerWrapper } ) & { + [aria-disabled='true'] *:not( ${ PopoverInnerWrapper } ) & { color: inherit; } `; diff --git a/packages/components/src/menu/submenu-trigger-item.tsx b/packages/components/src/menu/submenu-trigger-item.tsx index 23932a14bdaff4..9ea24d259af300 100644 --- a/packages/components/src/menu/submenu-trigger-item.tsx +++ b/packages/components/src/menu/submenu-trigger-item.tsx @@ -13,16 +13,16 @@ import { chevronRightSmall } from '@wordpress/icons'; * Internal dependencies */ import type { WordPressComponentProps } from '../context'; -import type { MenuItemProps } from './types'; -import { MenuContext } from './context'; -import { MenuItem } from './item'; +import type { ItemProps } from './types'; +import { Context } from './context'; +import { Item } from './item'; import * as Styled from './styles'; -export const MenuSubmenuTriggerItem = forwardRef< +export const SubmenuTriggerItem = forwardRef< HTMLDivElement, - WordPressComponentProps< MenuItemProps, 'div', false > ->( function MenuSubmenuTriggerItem( { suffix, ...otherProps }, ref ) { - const menuContext = useContext( MenuContext ); + WordPressComponentProps< ItemProps, 'div', false > +>( function SubmenuTriggerItem( { suffix, ...otherProps }, ref ) { + const menuContext = useContext( Context ); if ( ! menuContext?.store.parent ) { throw new Error( @@ -36,10 +36,10 @@ export const MenuSubmenuTriggerItem = forwardRef< accessibleWhenDisabled store={ menuContext.store } render={ - ->( function MenuTriggerButton( { children, disabled = false, ...props }, ref ) { - const menuContext = useContext( MenuContext ); + WordPressComponentProps< TriggerButtonProps, 'button', false > +>( function TriggerButton( { children, disabled = false, ...props }, ref ) { + const menuContext = useContext( Context ); if ( ! menuContext?.store ) { throw new Error( diff --git a/packages/components/src/menu/types.ts b/packages/components/src/menu/types.ts index f9bb0782529d1f..4532d97fb13dd9 100644 --- a/packages/components/src/menu/types.ts +++ b/packages/components/src/menu/types.ts @@ -3,7 +3,7 @@ */ import type * as Ariakit from '@ariakit/react'; -export interface MenuContext { +export interface ContextProps { /** * The ariakit store shared across all Menu subcomponents. */ @@ -14,7 +14,7 @@ export interface MenuContext { variant?: 'toolbar'; } -export interface MenuProps { +export interface Props { /** * The elements, which should include one instance of the `Menu.TriggerButton` * component and one instance of the `Menu.Popover` component. @@ -50,7 +50,7 @@ export interface MenuProps { placement?: Ariakit.MenuProviderProps[ 'placement' ]; } -export interface MenuPopoverProps { +export interface PopoverProps { /** * The contents of the menu popover, which should include instances of the * `Menu.Item`, `Menu.CheckboxItem`, `Menu.RadioItem`, `Menu.Group`, and @@ -98,7 +98,7 @@ export interface MenuPopoverProps { hideOnEscape?: Ariakit.MenuProps[ 'hideOnEscape' ]; } -export interface MenuTriggerButtonProps { +export interface TriggerButtonProps { /** * The contents of the menu trigger button. */ @@ -139,7 +139,7 @@ export interface MenuTriggerButtonProps { accessibleWhenDisabled?: Ariakit.MenuButtonProps[ 'accessibleWhenDisabled' ]; } -export interface MenuGroupProps { +export interface GroupProps { /** * The contents of the menu group, which should include one instance of the * `Menu.GroupLabel` component and one or more instances of `Menu.Item`, @@ -148,7 +148,7 @@ export interface MenuGroupProps { children: Ariakit.MenuGroupProps[ 'children' ]; } -export interface MenuGroupLabelProps { +export interface GroupLabelProps { /** * The contents of the menu group label, which should provide an accessible * label for the menu group. @@ -156,7 +156,7 @@ export interface MenuGroupLabelProps { children: Ariakit.MenuGroupLabelProps[ 'children' ]; } -export interface MenuItemProps { +export interface ItemProps { /** * The contents of the menu item, which could include one instance of the * `Menu.ItemLabel` component and/or one instance of the `Menu.ItemHelpText` @@ -203,7 +203,7 @@ export interface MenuItemProps { store?: Ariakit.MenuItemProps[ 'store' ]; } -export interface MenuCheckboxItemProps { +export interface CheckboxItemProps { /** * The contents of the menu item, which could include one instance of the * `Menu.ItemLabel` component and/or one instance of the `Menu.ItemHelpText` @@ -267,7 +267,7 @@ export interface MenuCheckboxItemProps { onChange?: Ariakit.MenuItemCheckboxProps[ 'onChange' ]; } -export interface MenuRadioItemProps { +export interface RadioItemProps { /** * The contents of the menu item, which could include one instance of the * `Menu.ItemLabel` component and/or one instance of the `Menu.ItemHelpText` @@ -330,4 +330,4 @@ export interface MenuRadioItemProps { onChange?: Ariakit.MenuItemRadioProps[ 'onChange' ]; } -export interface MenuSeparatorProps {} +export interface SeparatorProps {} diff --git a/storybook/manager-head.html b/storybook/manager-head.html index d3f156a6eb788b..a4f6941e981114 100644 --- a/storybook/manager-head.html +++ b/storybook/manager-head.html @@ -7,6 +7,7 @@ 'boxcontrol', 'customselectcontrol-v2', 'dimensioncontrol', + 'menu', 'navigation', 'navigator', 'progressbar', From 98cb5d67267253af26535421207d03c4970e9924 Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Thu, 9 Jan 2025 19:23:24 +0000 Subject: [PATCH 5/8] Bump plugin version to 20.0.0 --- gutenberg.php | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gutenberg.php b/gutenberg.php index f736359a8b357b..559efc57417abe 100644 --- a/gutenberg.php +++ b/gutenberg.php @@ -5,7 +5,7 @@ * Description: Printing since 1440. This is the development plugin for the block editor, site editor, and other future WordPress core functionality. * Requires at least: 6.6 * Requires PHP: 7.2 - * Version: 20.0.0-rc.1 + * Version: 20.0.0 * Author: Gutenberg Team * Text Domain: gutenberg * diff --git a/package-lock.json b/package-lock.json index 7c7a12a1e7bc7b..a88c85764dbd4f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "gutenberg", - "version": "20.0.0-rc.1", + "version": "20.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "gutenberg", - "version": "20.0.0-rc.1", + "version": "20.0.0", "hasInstallScript": true, "license": "GPL-2.0-or-later", "workspaces": [ diff --git a/package.json b/package.json index 1d54299515ff64..c427e3dcb9bab8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "20.0.0-rc.1", + "version": "20.0.0", "private": true, "description": "A new WordPress editor experience.", "author": "The WordPress Contributors", From be95ec381f847b1ca1adb96527966bc10b84551d Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Thu, 9 Jan 2025 20:33:31 +0100 Subject: [PATCH 6/8] iAPI: Fix the logic path that merges plain objects (#68579) * Fix the logic path that merges plain objects * Add changelog --------- Co-authored-by: DAreRodz Co-authored-by: luisherranz Co-authored-by: priethor --- packages/interactivity/CHANGELOG.md | 4 +++ packages/interactivity/src/proxies/state.ts | 7 ++-- .../src/proxies/test/deep-merge.ts | 32 +++++++++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/packages/interactivity/CHANGELOG.md b/packages/interactivity/CHANGELOG.md index 818a16b8dd5e60..cdc44dca80741b 100644 --- a/packages/interactivity/CHANGELOG.md +++ b/packages/interactivity/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Bug Fixes + +- Fix the logic path that merges plain objects ([#68579](https://github.com/WordPress/gutenberg/pull/68579)). + ## 6.15.0 (2025-01-02) ### Enhancements diff --git a/packages/interactivity/src/proxies/state.ts b/packages/interactivity/src/proxies/state.ts index e86ec05c484611..08977e8e18efae 100644 --- a/packages/interactivity/src/proxies/state.ts +++ b/packages/interactivity/src/proxies/state.ts @@ -340,7 +340,9 @@ const deepMergeRecursive = ( // Handle nested objects } else if ( isPlainObject( source[ key ] ) ) { - if ( isNew || ( override && ! isPlainObject( target[ key ] ) ) ) { + const targetValue = Object.getOwnPropertyDescriptor( target, key ) + ?.value; + if ( isNew || ( override && ! isPlainObject( targetValue ) ) ) { // Create a new object if the property is new or needs to be overridden target[ key ] = {}; if ( propSignal ) { @@ -350,9 +352,10 @@ const deepMergeRecursive = ( proxifyState( ns, target[ key ] as Object ) ); } + deepMergeRecursive( target[ key ], source[ key ], override ); } // Both target and source are plain objects, merge them recursively - if ( isPlainObject( target[ key ] ) ) { + else if ( isPlainObject( targetValue ) ) { deepMergeRecursive( target[ key ], source[ key ], override ); } diff --git a/packages/interactivity/src/proxies/test/deep-merge.ts b/packages/interactivity/src/proxies/test/deep-merge.ts index aaa762cb979f3c..c1e32763a01ef5 100644 --- a/packages/interactivity/src/proxies/test/deep-merge.ts +++ b/packages/interactivity/src/proxies/test/deep-merge.ts @@ -455,6 +455,38 @@ describe( 'Interactivity API', () => { expect( target.message.fontStyle ).toBeUndefined(); } ); + it( 'should not overwrite getters that become objects if `override` is false', () => { + const target: any = proxifyState( 'test', { + get message() { + return 'hello'; + }, + } ); + + const getterSpy = jest.spyOn( target, 'message', 'get' ); + + let message: any; + const spy = jest.fn( () => ( message = target.message ) ); + effect( spy ); + + expect( spy ).toHaveBeenCalledTimes( 1 ); + expect( message ).toBe( 'hello' ); + + deepMerge( + target, + { message: { content: 'hello', fontStyle: 'italic' } }, + false + ); + + // The effect callback reads `target.message`, so the getter is executed once as well. + expect( spy ).toHaveBeenCalledTimes( 1 ); + expect( getterSpy ).toHaveBeenCalledTimes( 1 ); + + expect( message ).toBe( 'hello' ); + expect( target.message ).toBe( 'hello' ); + expect( target.message.content ).toBeUndefined(); + expect( target.message.fontStyle ).toBeUndefined(); + } ); + it( 'should keep reactivity of arrays that are initially undefined', () => { const target: any = proxifyState( 'test', {} ); From b80c1e9a858a31d3825477c1790cc19285c08ca1 Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Thu, 9 Jan 2025 19:51:02 +0000 Subject: [PATCH 7/8] Update Changelog for 20.0.0 --- changelog.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/changelog.txt b/changelog.txt index bb71ae8617d7f4..25e633d96949f8 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,7 +1,6 @@ == Changelog == -= 20.0.0-rc.1 = - += 20.0.0 = ## Changelog @@ -453,6 +452,8 @@ The following contributors merged PRs in this release: @aaronrobertshaw @afercia @akasunil @benazeer-ben @bph @Chrico @ciampo @d-alleyne @DAreRodz @dhruvikpatel18 @draganescu @ellatrix @fabiankaegy @fushar @getdave @gigitux @gziolo @hbhalodia @himanshupathak95 @im3dabasia @Infinite-Null @jameskoster @jasmussen @jeryj @jorgefilipecosta @jsnajdr @juanfra @justlevine @karthick-murugan @kmanijak @louwie17 @Lovor01 @Mamaduka @manzoorwanijk @matiasbenedetto @Mayank-Tripathi32 @mayurprajapatii @mcsf @michalczaplinski @mikachan @mirka @ntsekouras @oandregal @ockham @PARTHVATALIYA @prasadkarmalkar @ramonjd @rilwis @rinkalpagdar @Rishit30G @rohitmathur-7 @SainathPoojary @sarthaknagoshe2002 @SH4LIN @shail-mehta @shimotmk @sirreal @stokesman @Sukhendu2002 @swissspidy @t-hamano @talldan @tellthemachines @timse201 @tyxla @up1512001 @vampdroid @Vrishabhsk @yogeshbhutkar @youknowriad + + = 19.9.0 = ## Changelog From 3a0fb96e3d51fe18b17aaf0243382e4eab1a800c Mon Sep 17 00:00:00 2001 From: tomoki shimomura Date: Fri, 10 Jan 2025 15:37:39 +0900 Subject: [PATCH 8/8] Add clear button to social links (#68564) Add Clear buttons to the icon color and icon background color options on the social links block, to improve consistency with other options. Co-authored-by: shimotmk --- packages/block-library/src/social-links/edit.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/block-library/src/social-links/edit.js b/packages/block-library/src/social-links/edit.js index 72fd265d629fb7..0b8b5c04deffba 100644 --- a/packages/block-library/src/social-links/edit.js +++ b/packages/block-library/src/social-links/edit.js @@ -269,6 +269,7 @@ export function SocialLinksEdit( props ) { isShownByDefault: true, resetAllFilter, enableAlpha: true, + clearable: true, }, ] } panelId={ clientId }