From bedc00ac266f75a2fb0d6319ff8d9a6d42686d42 Mon Sep 17 00:00:00 2001 From: Dovid Levine Date: Mon, 23 Dec 2024 11:03:57 +0200 Subject: [PATCH 01/14] fix: remove extraneous param from `gutenberg_url()` call in `gutenberg_posts_dashboard()` (#66699) Co-authored-by: justlevine Co-authored-by: gziolo --- lib/experimental/posts/load.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/experimental/posts/load.php b/lib/experimental/posts/load.php index 699534f1886f52..b6dd9d55a8d7d8 100644 --- a/lib/experimental/posts/load.php +++ b/lib/experimental/posts/load.php @@ -51,7 +51,7 @@ function gutenberg_posts_dashboard() { do_action( 'enqueue_block_editor_assets' ); wp_register_style( 'wp-gutenberg-posts-dashboard', - gutenberg_url( 'build/edit-site/posts.css', __FILE__ ), + gutenberg_url( 'build/edit-site/posts.css' ), array( 'wp-components', 'wp-commands', 'wp-edit-site' ) ); wp_enqueue_style( 'wp-gutenberg-posts-dashboard' ); From d22e81bb32899268f4484ba230ad2019a27c4785 Mon Sep 17 00:00:00 2001 From: Dovid Levine Date: Mon, 23 Dec 2024 11:08:17 +0200 Subject: [PATCH 02/14] fix: deprecated `WP_Webfonts()` constructor takes no arguments (#66700) Co-authored-by: justlevine Co-authored-by: gziolo < gziolo@git.wordpress.org> --- lib/experimental/font-face/bc-layer/webfonts-deprecations.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/experimental/font-face/bc-layer/webfonts-deprecations.php b/lib/experimental/font-face/bc-layer/webfonts-deprecations.php index 2534d8db165273..fb5e6b315dbdaf 100644 --- a/lib/experimental/font-face/bc-layer/webfonts-deprecations.php +++ b/lib/experimental/font-face/bc-layer/webfonts-deprecations.php @@ -28,7 +28,7 @@ function wp_webfonts() { global $wp_webfonts; if ( ! ( $wp_webfonts instanceof WP_Webfonts ) ) { - $wp_webfonts = new WP_Webfonts( wp_fonts() ); + $wp_webfonts = new WP_Webfonts(); } return $wp_webfonts; From dda9e56376e6ad182cb28beb6f597e8b3bf56c7b Mon Sep 17 00:00:00 2001 From: Dovid Levine Date: Mon, 23 Dec 2024 11:10:39 +0200 Subject: [PATCH 03/14] fix: remove extraneous param from `remove_filter()` calls (#66697) Co-authored-by: justlevine Co-authored-by: gziolo --- lib/block-supports/elements.php | 8 ++++---- lib/block-supports/layout.php | 6 +++--- lib/block-supports/settings.php | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/block-supports/elements.php b/lib/block-supports/elements.php index 35a41270a19800..f3243bc7178951 100644 --- a/lib/block-supports/elements.php +++ b/lib/block-supports/elements.php @@ -255,12 +255,12 @@ function gutenberg_render_elements_class_name( $block_content, $block ) { } // Remove deprecated WordPress core filters. -remove_filter( 'render_block', 'wp_render_elements_support', 10, 2 ); -remove_filter( 'pre_render_block', 'wp_render_elements_support_styles', 10, 2 ); +remove_filter( 'render_block', 'wp_render_elements_support', 10 ); +remove_filter( 'pre_render_block', 'wp_render_elements_support_styles', 10 ); // Remove WordPress core filters to avoid rendering duplicate elements stylesheet & attaching classes twice. -remove_filter( 'render_block', 'wp_render_elements_class_name', 10, 2 ); -remove_filter( 'render_block_data', 'wp_render_elements_support_styles', 10, 1 ); +remove_filter( 'render_block', 'wp_render_elements_class_name', 10 ); +remove_filter( 'render_block_data', 'wp_render_elements_support_styles', 10 ); add_filter( 'render_block', 'gutenberg_render_elements_class_name', 10, 2 ); add_filter( 'render_block_data', 'gutenberg_render_elements_support_styles', 10, 1 ); diff --git a/lib/block-supports/layout.php b/lib/block-supports/layout.php index ddbd1917c30547..7d63074ccb09bb 100644 --- a/lib/block-supports/layout.php +++ b/lib/block-supports/layout.php @@ -1055,8 +1055,8 @@ static function ( $matches ) { } if ( function_exists( 'wp_restore_group_inner_container' ) ) { - remove_filter( 'render_block', 'wp_restore_group_inner_container', 10, 2 ); - remove_filter( 'render_block_core/group', 'wp_restore_group_inner_container', 10, 2 ); + remove_filter( 'render_block', 'wp_restore_group_inner_container', 10 ); + remove_filter( 'render_block_core/group', 'wp_restore_group_inner_container', 10 ); } add_filter( 'render_block_core/group', 'gutenberg_restore_group_inner_container', 10, 2 ); @@ -1118,6 +1118,6 @@ function gutenberg_restore_image_outer_container( $block_content, $block ) { } if ( function_exists( 'wp_restore_image_outer_container' ) ) { - remove_filter( 'render_block_core/image', 'wp_restore_image_outer_container', 10, 2 ); + remove_filter( 'render_block_core/image', 'wp_restore_image_outer_container', 10 ); } add_filter( 'render_block_core/image', 'gutenberg_restore_image_outer_container', 10, 2 ); diff --git a/lib/block-supports/settings.php b/lib/block-supports/settings.php index b175fe778ce1b0..0246b5c039c86a 100644 --- a/lib/block-supports/settings.php +++ b/lib/block-supports/settings.php @@ -128,7 +128,7 @@ function _gutenberg_add_block_level_preset_styles( $pre_render, $block ) { return null; } // Remove WordPress core filter to avoid rendering duplicate settings style blocks. -remove_filter( 'render_block', '_wp_add_block_level_presets_class', 10, 2 ); -remove_filter( 'pre_render_block', '_wp_add_block_level_preset_styles', 10, 2 ); +remove_filter( 'render_block', '_wp_add_block_level_presets_class', 10 ); +remove_filter( 'pre_render_block', '_wp_add_block_level_preset_styles', 10 ); add_filter( 'render_block', '_gutenberg_add_block_level_presets_class', 10, 2 ); add_filter( 'pre_render_block', '_gutenberg_add_block_level_preset_styles', 10, 2 ); From 59683c1ec5cda75976c7fde7fe6079c0afd85173 Mon Sep 17 00:00:00 2001 From: Dovid Levine Date: Mon, 23 Dec 2024 11:12:12 +0200 Subject: [PATCH 04/14] fix: explicitly return false in `WP_Theme_JSON_Gutenberg::should_ovrride_preset()` (#66696) Co-authored-by: justlevine Co-authored-by: gziolo --- lib/class-wp-theme-json-gutenberg.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 778dcdbec78d96..25e69b859379d3 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -3413,6 +3413,8 @@ protected static function should_override_preset( $theme_json, $path, $override return true; } + + return false; } /** From e354e499b184b405c4b23dd8bf9203de7625f983 Mon Sep 17 00:00:00 2001 From: Dovid Levine Date: Mon, 23 Dec 2024 11:14:03 +0200 Subject: [PATCH 05/14] chore: fix return type in WP_Duotone_Gutenberg::get_selector() (#66695) Co-authored-by: justlevine Co-authored-by: gziolo --- lib/class-wp-duotone-gutenberg.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/class-wp-duotone-gutenberg.php b/lib/class-wp-duotone-gutenberg.php index 5f3b1bb5cd6b11..cc49c320da6506 100644 --- a/lib/class-wp-duotone-gutenberg.php +++ b/lib/class-wp-duotone-gutenberg.php @@ -640,7 +640,7 @@ private static function get_global_styles_presets( $sources ) { * * @param string $block_name The block name. * - * @return string The CSS selector or null if there is no support. + * @return ?string The CSS selector or null if there is no support. */ private static function get_selector( $block_name ) { $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block_name ); @@ -669,6 +669,8 @@ private static function get_selector( $block_name ) { // Regular filter.duotone support uses filter.duotone selectors with fallbacks. return wp_get_block_css_selector( $block_type, array( 'filter', 'duotone' ), true ); } + + return null; } /** From c9790844331f8a35da15d415f5775a80765e922b Mon Sep 17 00:00:00 2001 From: Anh Tran Date: Mon, 23 Dec 2024 16:20:35 +0700 Subject: [PATCH 06/14] Fix wrong `npm start` command (#65221) Co-authored-by: rilwis Co-authored-by: ndiego Co-authored-by: juanmaguitar --- .../fundamentals/javascript-in-the-block-editor.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started/fundamentals/javascript-in-the-block-editor.md b/docs/getting-started/fundamentals/javascript-in-the-block-editor.md index 7accc5d4c2129d..4cd7c0b36fe86a 100644 --- a/docs/getting-started/fundamentals/javascript-in-the-block-editor.md +++ b/docs/getting-started/fundamentals/javascript-in-the-block-editor.md @@ -26,7 +26,7 @@ The diagram below provides an overview of the build process when using the `wp-s - **Production Mode (`npm run build`):** In this mode, `wp-scripts` compiles your JavaScript, minifying the output to reduce file size and improve loading times in the browser. This is ideal for deploying your code to a live site. -- **Development Mode (`npm run start`):** This mode is tailored for active development. It skips minification for easier debugging, generates source maps for better error tracking, and watches your source files for changes. When a change is detected, it automatically rebuilds the affected files, allowing you to see updates in real-time. +- **Development Mode (`npm start`):** This mode is tailored for active development. It skips minification for easier debugging, generates source maps for better error tracking, and watches your source files for changes. When a change is detected, it automatically rebuilds the affected files, allowing you to see updates in real-time. The `wp-scripts` package also facilitates the use of JavaScript modules, allowing code distribution across multiple files and resulting in a streamlined bundle after the build process. The [block-development-example](https://github.com/WordPress/block-development-examples/tree/trunk/plugins/data-basics-59c8f8) GitHub repository provides some good examples. From 09eb60e037bd50be3e15cc62e11fb1dc0ea8ca87 Mon Sep 17 00:00:00 2001 From: Lovro Hrust <33687174+Lovor01@users.noreply.github.com> Date: Mon, 23 Dec 2024 10:33:41 +0100 Subject: [PATCH 07/14] Documenting innerBlocks in save function (#66689) There is an undocumented property 'innerBlocks' of props passed to save function. This change attempts to document it with example of use. Co-authored-by: Lovor01 Co-authored-by: gziolo --- .../block-api/block-edit-save.md | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/docs/reference-guides/block-api/block-edit-save.md b/docs/reference-guides/block-api/block-edit-save.md index 86721c77e463c6..a50a17b75cb54d 100644 --- a/docs/reference-guides/block-api/block-edit-save.md +++ b/docs/reference-guides/block-api/block-edit-save.md @@ -183,9 +183,34 @@ save: ( { attributes } ) => { ``` - When saving your block, you want to save the attributes in the same format specified by the attribute source definition. If no attribute source is specified, the attribute will be saved to the block's comment delimiter. See the [Block Attributes documentation](/docs/reference-guides/block-api/block-attributes.md) for more details. +### innerBlocks + +There is a second property in the props passed to the `save` function, `innerBlocks`. This property is typically used for internal operations, and there are very few scenarios where you would need to use it. + +`innerBlocks`, when initialized, is an array containing object representations of nested blocks. In those rare cases where you might use this property, +it can help you adjust how a block is rendered. For example, you could render a block differently based on the number of nested blocks or if a specific block type is present.. + + +```jsx +save: ( { attributes, innerBlocks } ) => { + const { className, ...rest } = useBlockProps.save(); + + // innerBlocks could also be an object - react element during initialization + const numberOfInnerBlocks = innerBlocks?.length; + if ( numberOfInnerBlocks > 1 ) { + className = className + ( className ? ' ' : '' ) + 'more-than-one'; + }; + const blockProps = { ...rest, className }; + + return
{ attributes.content }
; +}; +``` + + +Here, an additional class is added to the block if number of inner blocks is greater than one, allowing for different styling of the block. + ## Examples Here are a couple examples of using attributes, edit, and save all together. From 2e590d0553b02e5ec735fe8dfd8b2e23c5f1e569 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Mon, 23 Dec 2024 11:21:25 +0100 Subject: [PATCH 08/14] Tabs: use correct ariakit component for root component types (#68207) Co-authored-by: ciampo Co-authored-by: tyxla --- packages/components/src/tabs/types.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/components/src/tabs/types.ts b/packages/components/src/tabs/types.ts index 959a82509a05d6..3e52f11481e85f 100644 --- a/packages/components/src/tabs/types.ts +++ b/packages/components/src/tabs/types.ts @@ -22,7 +22,7 @@ export type TabsProps = { * `Tabs.Tablist` component and as many instances of the `Tabs.TabPanel` * components as there are `Tabs.Tab` components. */ - children: Ariakit.TabProps[ 'children' ]; + children: Ariakit.TabProviderProps[ 'children' ]; /** * Determines if the tab should be selected when it receives focus. If set to * `false`, the tab will only be selected upon clicking, not when using arrow @@ -33,7 +33,7 @@ export type TabsProps = { * * @see https://www.w3.org/WAI/ARIA/apg/patterns/tabpanel/ */ - selectOnMove?: Ariakit.TabStoreProps[ 'selectOnMove' ]; + selectOnMove?: Ariakit.TabProviderProps[ 'selectOnMove' ]; /** * The id of the tab whose panel is currently visible. * @@ -44,7 +44,7 @@ export type TabsProps = { * in "controlled" mode. When in "controlled" mode, the `null` value will * result in no tabs being selected, and the tablist becoming tabbable. */ - selectedTabId?: Ariakit.TabStoreProps[ 'selectedId' ]; + selectedTabId?: Ariakit.TabProviderProps[ 'selectedId' ]; /** * The id of the tab whose panel is currently visible. * @@ -55,11 +55,11 @@ export type TabsProps = { * Note: this prop will be overridden by the `selectedTabId` prop if it is * provided (meaning the component will be used in "controlled" mode). */ - defaultTabId?: Ariakit.TabStoreProps[ 'defaultSelectedId' ]; + defaultTabId?: Ariakit.TabProviderProps[ 'defaultSelectedId' ]; /** * The function called when the `selectedTabId` changes. */ - onSelect?: Ariakit.TabStoreProps[ 'setSelectedId' ]; + onSelect?: Ariakit.TabProviderProps[ 'setSelectedId' ]; /** * The current active tab `id`. The active tab is the tab element within the * tablist widget that has DOM focus. @@ -69,7 +69,7 @@ export type TabsProps = { * itself will have focus and users will be able to navigate to it using * arrow keys.activeTabId */ - activeTabId?: Ariakit.TabStoreProps[ 'activeId' ]; + activeTabId?: Ariakit.TabProviderProps[ 'activeId' ]; /** * The tab id that should be active by default when the composite widget is * rendered. If `null`, the tablist element itself will have focus @@ -79,11 +79,11 @@ export type TabsProps = { * Note: this prop will be overridden by the `activeTabId` prop if it is * provided. */ - defaultActiveTabId?: Ariakit.TabStoreProps[ 'defaultActiveId' ]; + defaultActiveTabId?: Ariakit.TabProviderProps[ 'defaultActiveId' ]; /** * A callback that gets called when the `activeTabId` state changes. */ - onActiveTabIdChange?: Ariakit.TabStoreProps[ 'setActiveId' ]; + onActiveTabIdChange?: Ariakit.TabProviderProps[ 'setActiveId' ]; /** * Defines the orientation of the tablist and determines which arrow keys * can be used to move focus: @@ -93,7 +93,7 @@ export type TabsProps = { * * @default "horizontal" */ - orientation?: Ariakit.TabStoreProps[ 'orientation' ]; + orientation?: Ariakit.TabProviderProps[ 'orientation' ]; }; export type TabListProps = { From f78c60c2deba5c7bc02e93474f5d8e2280cdec15 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Mon, 23 Dec 2024 11:22:32 +0100 Subject: [PATCH 09/14] Menu: use ariakit types (#68206) Co-authored-by: ciampo Co-authored-by: tyxla --- .../components/src/menu/checkbox-item.tsx | 3 +- packages/components/src/menu/item.tsx | 11 +- packages/components/src/menu/radio-item.tsx | 3 +- packages/components/src/menu/types.ts | 214 ++++++++++++------ 4 files changed, 165 insertions(+), 66 deletions(-) diff --git a/packages/components/src/menu/checkbox-item.tsx b/packages/components/src/menu/checkbox-item.tsx index ddb700b43324a6..69339387c3add5 100644 --- a/packages/components/src/menu/checkbox-item.tsx +++ b/packages/components/src/menu/checkbox-item.tsx @@ -21,7 +21,7 @@ export const MenuCheckboxItem = forwardRef< HTMLDivElement, WordPressComponentProps< MenuCheckboxItemProps, 'div', false > >( function MenuCheckboxItem( - { suffix, children, hideOnClick = false, ...props }, + { suffix, children, disabled = false, hideOnClick = false, ...props }, ref ) { const menuContext = useContext( MenuContext ); @@ -37,6 +37,7 @@ export const MenuCheckboxItem = forwardRef< ref={ ref } { ...props } accessibleWhenDisabled + disabled={ disabled } hideOnClick={ hideOnClick } store={ menuContext.store } > diff --git a/packages/components/src/menu/item.tsx b/packages/components/src/menu/item.tsx index 84ff050bcc2236..a716cbcc89654c 100644 --- a/packages/components/src/menu/item.tsx +++ b/packages/components/src/menu/item.tsx @@ -15,7 +15,15 @@ export const MenuItem = forwardRef< HTMLDivElement, WordPressComponentProps< MenuItemProps, 'div', false > >( function MenuItem( - { prefix, suffix, children, hideOnClick = true, store, ...props }, + { + prefix, + suffix, + children, + disabled = false, + hideOnClick = true, + store, + ...props + }, ref ) { const menuContext = useContext( MenuContext ); @@ -37,6 +45,7 @@ export const MenuItem = forwardRef< ref={ ref } { ...props } accessibleWhenDisabled + disabled={ disabled } hideOnClick={ hideOnClick } store={ computedStore } > diff --git a/packages/components/src/menu/radio-item.tsx b/packages/components/src/menu/radio-item.tsx index 5534a6b7f3e10c..28b3199d7d36b8 100644 --- a/packages/components/src/menu/radio-item.tsx +++ b/packages/components/src/menu/radio-item.tsx @@ -28,7 +28,7 @@ export const MenuRadioItem = forwardRef< HTMLDivElement, WordPressComponentProps< MenuRadioItemProps, 'div', false > >( function MenuRadioItem( - { suffix, children, hideOnClick = false, ...props }, + { suffix, children, disabled = false, hideOnClick = false, ...props }, ref ) { const menuContext = useContext( MenuContext ); @@ -44,6 +44,7 @@ export const MenuRadioItem = forwardRef< ref={ ref } { ...props } accessibleWhenDisabled + disabled={ disabled } hideOnClick={ hideOnClick } store={ menuContext.store } > diff --git a/packages/components/src/menu/types.ts b/packages/components/src/menu/types.ts index f58b5bcc89b953..f9bb0782529d1f 100644 --- a/packages/components/src/menu/types.ts +++ b/packages/components/src/menu/types.ts @@ -2,7 +2,6 @@ * External dependencies */ import type * as Ariakit from '@ariakit/react'; -import type { Placement } from '@floating-ui/react-dom'; export interface MenuContext { /** @@ -17,77 +16,93 @@ export interface MenuContext { export interface MenuProps { /** - * The contents of the menu (ie. one or more menu items). + * The elements, which should include one instance of the `Menu.TriggerButton` + * component and one instance of the `Menu.Popover` component. */ - children?: React.ReactNode; + children?: Ariakit.MenuProviderProps[ 'children' ]; /** - * The open state of the menu popover when it is initially rendered. Use when - * not wanting to control its open state. + * Whether the menu popover and its contents should be visible by default. + * + * Note: this prop will be overridden by the `open` prop if it is + * provided (meaning the component will be used in "controlled" mode). * * @default false */ - defaultOpen?: boolean; + defaultOpen?: Ariakit.MenuProviderProps[ 'defaultOpen' ]; /** - * The controlled open state of the menu popover. Must be used in conjunction - * with `onOpenChange`. + * Whether the menu popover and its contents should be visible. + * Should be used in conjunction with `onOpenChange` in order to control + * the open state of the menu popover. + * + * Note: this prop will set the component in "controlled" mode, and it will + * override the `defaultOpen` prop. */ - open?: boolean; + open?: Ariakit.MenuProviderProps[ 'open' ]; /** - * Event handler called when the open state of the menu popover changes. + * A callback that gets called when the `open` state changes. */ - onOpenChange?: ( open: boolean ) => void; + onOpenChange?: Ariakit.MenuProviderProps[ 'setOpen' ]; /** * The placement of the menu popover. * - * @default 'bottom-start' for root-level menus, 'right-start' for nested menus + * @default 'bottom-start' for root-level menus, 'right-start' for submenus */ - placement?: Placement; + placement?: Ariakit.MenuProviderProps[ 'placement' ]; } export interface MenuPopoverProps { /** - * The contents of the dropdown. + * The contents of the menu popover, which should include instances of the + * `Menu.Item`, `Menu.CheckboxItem`, `Menu.RadioItem`, `Menu.Group`, and + * `Menu.Separator` components. */ - children?: React.ReactNode; + children?: Ariakit.MenuProps[ 'children' ]; /** * 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. * + * Determines whether the menu popover is modal. Modal dialogs have distinct + * states and behaviors: + * - The `portal` and `preventBodyScroll` props are set to `true`. They can + * still be manually set to `false`. + * - When the dialog is open, element tree outside it will be inert. + * * @default true */ - modal?: boolean; + modal?: Ariakit.MenuProps[ 'modal' ]; /** * The distance between the popover and the anchor element. * * @default 8 for root-level menus, 16 for nested menus */ - gutter?: number; + gutter?: Ariakit.MenuProps[ 'gutter' ]; /** * The skidding of the popover along the anchor element. Can be set to * negative values to make the popover shift to the opposite side. * * @default 0 for root-level menus, -8 for nested menus */ - shift?: number; + shift?: Ariakit.MenuProps[ 'shift' ]; /** - * Determines whether the menu popover will be hidden when the user presses - * the Escape key. + * Determines if the menu popover will hide when the user presses the + * Escape key. + * + * This prop can be either a boolean or a function that accepts an event as an + * argument and returns a boolean. The event object represents the keydown + * event that initiated the hide action, which could be either a native + * keyboard event or a React synthetic event. * * @default `( event ) => { event.preventDefault(); return true; }` */ - hideOnEscape?: - | boolean - | ( ( - event: KeyboardEvent | React.KeyboardEvent< Element > - ) => boolean ); + hideOnEscape?: Ariakit.MenuProps[ 'hideOnEscape' ]; } export interface MenuTriggerButtonProps { /** * The contents of the menu trigger button. */ - children?: React.ReactNode; + children?: Ariakit.MenuButtonProps[ 'children' ]; /** * Allows the component to be rendered as a different HTML element or React * component. The value can be a React element or a function that takes in the @@ -103,9 +118,6 @@ export interface MenuTriggerButtonProps { * This feature can be combined with the `accessibleWhenDisabled` prop to * make disabled elements still accessible via keyboard. * - * **Note**: For this prop to work, the `focusable` prop must be set to - * `true`, if it's not set by default. - * * @default false */ disabled?: Ariakit.MenuButtonProps[ 'disabled' ]; @@ -129,42 +141,54 @@ export interface MenuTriggerButtonProps { export interface MenuGroupProps { /** - * The contents of the menu group (ie. an optional menu group label and one - * or more menu items). + * The contents of the menu group, which should include one instance of the + * `Menu.GroupLabel` component and one or more instances of `Menu.Item`, + * `Menu.CheckboxItem`, and `Menu.RadioItem`. */ - children: React.ReactNode; + children: Ariakit.MenuGroupProps[ 'children' ]; } export interface MenuGroupLabelProps { /** - * The contents of the menu group label. + * The contents of the menu group label, which should provide an accessible + * label for the menu group. */ - children: React.ReactNode; + children: Ariakit.MenuGroupLabelProps[ 'children' ]; } export interface MenuItemProps { /** - * The contents of the menu item. + * The contents of the menu item, which could include one instance of the + * `Menu.ItemLabel` component and/or one instance of the `Menu.ItemHelpText` + * component. */ - children: React.ReactNode; + children: Ariakit.MenuItemProps[ 'children' ]; /** - * The contents of the menu item's prefix. + * The contents of the menu item's prefix, such as an icon. */ prefix?: React.ReactNode; /** - * The contents of the menu item's suffix. + * The contents of the menu item's suffix, such as a keyboard shortcut. */ suffix?: React.ReactNode; /** - * Whether to hide the menu popover when the menu item is clicked. + * Determines if the menu should hide when this item is clicked. + * + * **Note**: This behavior isn't triggered if this menu item is rendered as a + * link and modifier keys are used to either open the link in a new tab or + * download it. * * @default true */ - hideOnClick?: boolean; + hideOnClick?: Ariakit.MenuItemProps[ 'hideOnClick' ]; /** - * Determines if the element is disabled. + * Determines if the element is disabled. This sets the `aria-disabled` + * attribute accordingly, enabling support for all elements, including those + * that don't support the native `disabled` attribute. + * + * @default false */ - disabled?: boolean; + disabled?: Ariakit.MenuItemProps[ 'disabled' ]; /** * Allows the component to be rendered as a different HTML element or React * component. The value can be a React element or a function that takes in the @@ -173,73 +197,137 @@ export interface MenuItemProps { */ render?: Ariakit.MenuItemProps[ 'render' ]; /** - * The ariakit store. This prop is only meant for internal use. + * The ariakit menu store. This prop is only meant for internal use. * @ignore */ store?: Ariakit.MenuItemProps[ 'store' ]; } -export interface MenuCheckboxItemProps - extends Omit< MenuItemProps, 'prefix' | 'hideOnClick' > { +export interface MenuCheckboxItemProps { /** - * Whether to hide the menu popover when the menu item is clicked. + * The contents of the menu item, which could include one instance of the + * `Menu.ItemLabel` component and/or one instance of the `Menu.ItemHelpText` + * component. + */ + children: Ariakit.MenuItemCheckboxProps[ 'children' ]; + /** + * The contents of the menu item's suffix, such as a keyboard shortcut. + */ + suffix?: React.ReactNode; + /** + * Determines if the menu should hide when this item is clicked. + * + * **Note**: This behavior isn't triggered if this menu item is rendered as a + * link and modifier keys are used to either open the link in a new tab or + * download it. * * @default false */ - hideOnClick?: boolean; + hideOnClick?: Ariakit.MenuItemCheckboxProps[ 'hideOnClick' ]; + /** + * Determines if the element is disabled. This sets the `aria-disabled` + * attribute accordingly, enabling support for all elements, including those + * that don't support the native `disabled` attribute. + * + * @default false + */ + disabled?: Ariakit.MenuItemCheckboxProps[ 'disabled' ]; + /** + * Allows the component to be rendered as a different HTML element or React + * component. The value can be a React element or a function that takes in the + * original component props and gives back a React element with the props + * merged. + */ + render?: Ariakit.MenuItemCheckboxProps[ 'render' ]; /** * The checkbox menu item's name. */ - name: string; + name: Ariakit.MenuItemCheckboxProps[ 'name' ]; /** * The checkbox item's value, useful when using multiple checkbox menu items * associated to the same `name`. */ - value?: string; + value?: Ariakit.MenuItemCheckboxProps[ 'value' ]; /** * The controlled checked state of the checkbox menu item. + * + * Note: this prop will override the `defaultChecked` prop. */ - checked?: boolean; + checked?: Ariakit.MenuItemCheckboxProps[ 'checked' ]; /** * The checked state of the checkbox menu item when it is initially rendered. * Use when not wanting to control its checked state. + * + * Note: this prop will be overriden by the `checked` prop, if it is defined. */ - defaultChecked?: boolean; + defaultChecked?: Ariakit.MenuItemCheckboxProps[ 'defaultChecked' ]; /** - * Event handler called when the checked state of the checkbox menu item changes. + * A function that is called when the checkbox's checked state changes. */ - onChange?: ( event: React.ChangeEvent< HTMLInputElement > ) => void; + onChange?: Ariakit.MenuItemCheckboxProps[ 'onChange' ]; } -export interface MenuRadioItemProps - extends Omit< MenuItemProps, 'prefix' | 'hideOnClick' > { +export interface MenuRadioItemProps { + /** + * The contents of the menu item, which could include one instance of the + * `Menu.ItemLabel` component and/or one instance of the `Menu.ItemHelpText` + * component. + */ + children: Ariakit.MenuItemRadioProps[ 'children' ]; + /** + * The contents of the menu item's suffix, such as a keyboard shortcut. + */ + suffix?: React.ReactNode; + /** + * Determines if the menu should hide when this item is clicked. + * + * **Note**: This behavior isn't triggered if this menu item is rendered as a + * link and modifier keys are used to either open the link in a new tab or + * download it. + * + * @default false + */ + hideOnClick?: Ariakit.MenuItemRadioProps[ 'hideOnClick' ]; /** - * Whether to hide the menu popover when the menu item is clicked. + * Determines if the element is disabled. This sets the `aria-disabled` + * attribute accordingly, enabling support for all elements, including those + * that don't support the native `disabled` attribute. * * @default false */ - hideOnClick?: boolean; + disabled?: Ariakit.MenuItemRadioProps[ 'disabled' ]; + /** + * Allows the component to be rendered as a different HTML element or React + * component. The value can be a React element or a function that takes in the + * original component props and gives back a React element with the props + * merged. + */ + render?: Ariakit.MenuItemRadioProps[ 'render' ]; /** * The radio item's name. */ - name: string; + name: Ariakit.MenuItemRadioProps[ 'name' ]; /** * The radio item's value. */ - value: string | number; + value: Ariakit.MenuItemRadioProps[ 'value' ]; /** * The controlled checked state of the radio menu item. + * + * Note: this prop will override the `defaultChecked` prop. */ - checked?: boolean; + checked?: Ariakit.MenuItemRadioProps[ 'checked' ]; /** * The checked state of the radio menu item when it is initially rendered. * Use when not wanting to control its checked state. + * + * Note: this prop will be overriden by the `checked` prop, if it is defined. */ - defaultChecked?: boolean; + defaultChecked?: Ariakit.MenuItemRadioProps[ 'defaultChecked' ]; /** - * Event handler called when the checked radio menu item changes. + * A function that is called when the checkbox's checked state changes. */ - onChange?: ( event: React.ChangeEvent< HTMLInputElement > ) => void; + onChange?: Ariakit.MenuItemRadioProps[ 'onChange' ]; } export interface MenuSeparatorProps {} From b73c95df51f92a00d925fd61b45c0a709047015a Mon Sep 17 00:00:00 2001 From: Damien Alleyne <31164683+d-alleyne@users.noreply.github.com> Date: Mon, 23 Dec 2024 06:54:51 -0400 Subject: [PATCH 10/14] Declare shadow presets as translatable in theme.json (#66675) Co-authored-by: d-alleyne Co-authored-by: swissspidy Co-authored-by: Mamaduka Co-authored-by: oandregal Co-authored-by: spacedmonkey --- backport-changelog/6.8/8031.md | 3 +++ lib/theme-i18n.json | 7 +++++++ 2 files changed, 10 insertions(+) create mode 100644 backport-changelog/6.8/8031.md diff --git a/backport-changelog/6.8/8031.md b/backport-changelog/6.8/8031.md new file mode 100644 index 00000000000000..96d06cc671e13e --- /dev/null +++ b/backport-changelog/6.8/8031.md @@ -0,0 +1,3 @@ +https://github.com/WordPress/wordpress-develop/pull/8031 + +* https://github.com/WordPress/gutenberg/pull/66675 diff --git a/lib/theme-i18n.json b/lib/theme-i18n.json index e4d14502132cbe..ce11ba036c9e79 100644 --- a/lib/theme-i18n.json +++ b/lib/theme-i18n.json @@ -45,6 +45,13 @@ } ] }, + "shadow": { + "presets": [ + { + "name": "Shadow name" + } + ] + }, "blocks": { "*": { "typography": { From 813c24be7cb64a045f845c0a8c6156c5793cce42 Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Mon, 23 Dec 2024 21:55:24 +0900 Subject: [PATCH 11/14] Grid Visualizer: Improve observation logic (#68230) * Grid Visualizer: Improve observation logic * Add `class` to `attributeFilter` Co-authored-by: t-hamano Co-authored-by: talldan --- .../src/components/grid/grid-visualizer.js | 11 ++++++++++ .../block-editor/src/components/grid/utils.js | 20 ++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/grid/grid-visualizer.js b/packages/block-editor/src/components/grid/grid-visualizer.js index 81da0457ffc5ca..9d89866bbff5f7 100644 --- a/packages/block-editor/src/components/grid/grid-visualizer.js +++ b/packages/block-editor/src/components/grid/grid-visualizer.js @@ -62,6 +62,17 @@ const GridVisualizerGrid = forwardRef( observer.observe( element ); observers.push( observer ); } + + const mutationObserver = new window.MutationObserver( () => { + setGridInfo( getGridInfo( gridElement ) ); + } ); + mutationObserver.observe( gridElement, { + attributeFilter: [ 'style', 'class' ], + childList: true, + subtree: true, + } ); + observers.push( mutationObserver ); + return () => { for ( const observer of observers ) { observer.disconnect(); diff --git a/packages/block-editor/src/components/grid/utils.js b/packages/block-editor/src/components/grid/utils.js index fc012c645f0916..21014108085423 100644 --- a/packages/block-editor/src/components/grid/utils.js +++ b/packages/block-editor/src/components/grid/utils.js @@ -160,6 +160,21 @@ export function getGridInfo( gridElement ) { gridElement, 'grid-template-rows' ); + const borderTopWidth = getComputedCSS( gridElement, 'border-top-width' ); + const borderRightWidth = getComputedCSS( + gridElement, + 'border-right-width' + ); + const borderBottomWidth = getComputedCSS( + gridElement, + 'border-bottom-width' + ); + const borderLeftWidth = getComputedCSS( gridElement, 'border-left-width' ); + const paddingTop = getComputedCSS( gridElement, 'padding-top' ); + const paddingRight = getComputedCSS( gridElement, 'padding-right' ); + const paddingBottom = getComputedCSS( gridElement, 'padding-bottom' ); + const paddingLeft = getComputedCSS( gridElement, 'padding-left' ); + const numColumns = gridTemplateColumns.split( ' ' ).length; const numRows = gridTemplateRows.split( ' ' ).length; const numItems = numColumns * numRows; @@ -172,7 +187,10 @@ export function getGridInfo( gridElement ) { gridTemplateColumns, gridTemplateRows, gap: getComputedCSS( gridElement, 'gap' ), - padding: getComputedCSS( gridElement, 'padding' ), + paddingTop: `calc(${ paddingTop } + ${ borderTopWidth })`, + paddingRight: `calc(${ paddingRight } + ${ borderRightWidth })`, + paddingBottom: `calc(${ paddingBottom } + ${ borderBottomWidth })`, + paddingLeft: `calc(${ paddingLeft } + ${ borderLeftWidth })`, }, }; } From 080189e9acc01a16234fadeac0ab897da0abbecb Mon Sep 17 00:00:00 2001 From: Eshaan Dabasiya <76681468+im3dabasia@users.noreply.github.com> Date: Mon, 23 Dec 2024 18:40:59 +0530 Subject: [PATCH 12/14] Storybook: Add story for the Warning component (#68124) * doc: Add storybook for warning component * feat: Add story with actions, and secondary actions Co-authored-by: im3dabasia Co-authored-by: t-hamano --- .../components/warning/stories/index.story.js | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 packages/block-editor/src/components/warning/stories/index.story.js diff --git a/packages/block-editor/src/components/warning/stories/index.story.js b/packages/block-editor/src/components/warning/stories/index.story.js new file mode 100644 index 00000000000000..ee881059f302d7 --- /dev/null +++ b/packages/block-editor/src/components/warning/stories/index.story.js @@ -0,0 +1,86 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { Button } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import Warning from '../'; + +const meta = { + title: 'BlockEditor/Warning', + component: Warning, + parameters: { + docs: { + canvas: { sourceState: 'shown' }, + description: { + component: + 'Displays a warning message with optional action buttons and secondary actions dropdown.', + }, + }, + }, + argTypes: { + children: { + control: 'text', + description: + 'Intended to represent the block to which the warning pertains.', + table: { + type: { summary: 'string|element' }, + }, + }, + className: { + control: 'text', + description: 'Classes to pass to element.', + table: { + type: { summary: 'string' }, + }, + }, + actions: { + control: 'object', + description: + 'An array of elements to be rendered as action buttons in the warning element.', + table: { + type: { summary: 'Element[]' }, + }, + }, + secondaryActions: { + control: 'object', + description: + 'An array of { title, onClick } to be rendered as options in a dropdown of secondary actions.', + table: { + type: { summary: '{ title: string, onClick: Function }[]' }, + }, + }, + }, +}; + +export default meta; + +export const Default = { + args: { + children: __( 'This block ran into an issue.' ), + }, +}; + +export const WithActions = { + args: { + ...Default.args, + actions: [ + , + ], + }, +}; + +export const WithSecondaryActions = { + args: { + ...Default.args, + secondaryActions: [ + { title: __( 'Get help' ) }, + { title: __( 'Remove block' ) }, + ], + }, +}; From 27bb038623933c51d75d38da5a9c1a1e4be850f5 Mon Sep 17 00:00:00 2001 From: Juan Aldasoro Date: Mon, 23 Dec 2024 14:18:17 +0100 Subject: [PATCH 13/14] Add reset button to ColorGradientSettingsDropdown (#67800) * Add reset button to ColorGradientSettingsDropdown * Toggle if it is open, to avoid multiple selectors being open. * Always show reset button if hover is not supported. * Respect clearable setting to display the reset button. * Toggle if it is open, to avoid multiple selectors being open. * Apply suggestions from code review --------- Co-authored-by: juanfra Co-authored-by: t-hamano --- .../components/colors-gradients/dropdown.js | 65 ++++++++++++++++--- .../components/colors-gradients/style.scss | 5 ++ .../components/global-styles/color-panel.js | 3 + .../global-styles/shadows-edit-panel.js | 7 +- 4 files changed, 71 insertions(+), 9 deletions(-) diff --git a/packages/block-editor/src/components/colors-gradients/dropdown.js b/packages/block-editor/src/components/colors-gradients/dropdown.js index 71b27c06e7ccfc..e667927bee7601 100644 --- a/packages/block-editor/src/components/colors-gradients/dropdown.js +++ b/packages/block-editor/src/components/colors-gradients/dropdown.js @@ -15,6 +15,13 @@ import { __experimentalHStack as HStack, __experimentalToolsPanelItem as ToolsPanelItem, } from '@wordpress/components'; +import { useRef } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import { reset as resetIcon } from '@wordpress/icons'; /** * Internal dependencies @@ -76,7 +83,15 @@ const LabeledColorIndicator = ( { colorValue, label } ) => ( const renderToggle = ( settings ) => ( { onToggle, isOpen } ) => { - const { colorValue, label } = settings; + const { + clearable, + colorValue, + gradientValue, + onColorChange, + onGradientChange, + label, + } = settings; + const colorButtonRef = useRef( undefined ); const toggleProps = { onClick: onToggle, @@ -85,15 +100,45 @@ const renderToggle = { 'is-open': isOpen } ), 'aria-expanded': isOpen, + ref: colorButtonRef, + }; + + const clearValue = () => { + if ( colorValue ) { + onColorChange(); + } else if ( gradientValue ) { + onGradientChange(); + } }; + const value = colorValue ?? gradientValue; + return ( - + <> + + { clearable && value && ( +