From 9943802726955083da398290ea39d5a3ae149078 Mon Sep 17 00:00:00 2001 From: Vipul Patil Date: Fri, 10 May 2024 12:29:25 +0530 Subject: [PATCH 001/468] Replace the AM/PM toggle ButtonGroup with ToggleGroupControl. The previous ButtonGroup for toggling between AM/PM is replaced with ToggleGroupControl for better accessibility. --- .../components/src/date-time/time/index.tsx | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/packages/components/src/date-time/time/index.tsx b/packages/components/src/date-time/time/index.tsx index b0d8c20be74ac6..cfe512df377dde 100644 --- a/packages/components/src/date-time/time/index.tsx +++ b/packages/components/src/date-time/time/index.tsx @@ -13,8 +13,6 @@ import { __ } from '@wordpress/i18n'; * Internal dependencies */ import BaseControl from '../../base-control'; -import Button from '../../button'; -import ButtonGroup from '../../button-group'; import SelectControl from '../../select-control'; import TimeZone from './timezone'; import type { TimePickerProps } from '../types'; @@ -41,6 +39,10 @@ import { } from '../../input-control/reducer/actions'; import { inputToDate } from '../utils'; import { TIMEZONELESS_FORMAT } from '../constants'; +import { + ToggleGroupControl, + ToggleGroupControlOption, +} from '../../toggle-group-control'; function from12hTo24h( hours: number, isPm: boolean ) { return isPm ? ( ( hours % 12 ) + 12 ) % 24 : hours % 12; @@ -294,30 +296,30 @@ export function TimePicker( { /> { is12Hour && ( - - - - + value="vertical" + label={ __( 'PM' ) } + /> + ) } From db9f59534c0d6b22e992c9e19489bcd3b0277e42 Mon Sep 17 00:00:00 2001 From: Vipul Patil Date: Fri, 10 May 2024 16:22:32 +0530 Subject: [PATCH 002/468] Update the AM/PM toggle tests with respect to ToggleGroupControl. --- packages/components/src/date-time/time/test/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/components/src/date-time/time/test/index.tsx b/packages/components/src/date-time/time/test/index.tsx index a4f7df4db2acba..3a2eb05f134fa5 100644 --- a/packages/components/src/date-time/time/test/index.tsx +++ b/packages/components/src/date-time/time/test/index.tsx @@ -285,8 +285,8 @@ describe( 'TimePicker', () => { * This is not ideal, but best of we can do for now until we refactor * AM/PM into accessible elements, like radio buttons. */ - expect( screen.getByText( 'AM' ) ).not.toHaveClass( 'is-primary' ); - expect( screen.getByText( 'PM' ) ).toHaveClass( 'is-primary' ); + expect( screen.getByText( 'AM' ) ).not.toBeChecked(); + expect( screen.getByText( 'PM' ) ).toBeChecked(); } ); it( 'should have different layouts/orders for 12/24 hour formats', () => { From ae92dde20804e2376be09af8047a27621b9fcd3c Mon Sep 17 00:00:00 2001 From: Vipul Patil Date: Tue, 14 May 2024 19:11:01 +0530 Subject: [PATCH 003/468] Update the AM/PM toggle tests with respect to ToggleGroupControl. --- packages/components/src/date-time/time/test/index.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/components/src/date-time/time/test/index.tsx b/packages/components/src/date-time/time/test/index.tsx index 3a2eb05f134fa5..22f3b55f425d01 100644 --- a/packages/components/src/date-time/time/test/index.tsx +++ b/packages/components/src/date-time/time/test/index.tsx @@ -285,8 +285,12 @@ describe( 'TimePicker', () => { * This is not ideal, but best of we can do for now until we refactor * AM/PM into accessible elements, like radio buttons. */ - expect( screen.getByText( 'AM' ) ).not.toBeChecked(); - expect( screen.getByText( 'PM' ) ).toBeChecked(); + expect( + screen.getByLabelText( 'AM', { selector: 'button' } ) + ).not.toBeChecked(); + expect( + screen.getByLabelText( 'PM', { selector: 'button' } ) + ).toBeChecked(); } ); it( 'should have different layouts/orders for 12/24 hour formats', () => { From 353e851b3192516550b4b98df9e9fc9b002d8757 Mon Sep 17 00:00:00 2001 From: Vipul Patil Date: Fri, 17 May 2024 10:51:22 +0530 Subject: [PATCH 004/468] Update the AM/PM toggle tests with respect to ToggleGroupControl. --- packages/components/src/date-time/time/test/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/components/src/date-time/time/test/index.tsx b/packages/components/src/date-time/time/test/index.tsx index 22f3b55f425d01..dcbc77bd55c494 100644 --- a/packages/components/src/date-time/time/test/index.tsx +++ b/packages/components/src/date-time/time/test/index.tsx @@ -165,7 +165,7 @@ describe( 'TimePicker', () => { /> ); - const pmButton = screen.getByText( 'PM' ); + const pmButton = screen.getByLabelText( 'PM', { selector: 'button' } ); await user.click( pmButton ); @@ -185,7 +185,7 @@ describe( 'TimePicker', () => { /> ); - const amButton = screen.getByText( 'AM' ); + const amButton = screen.getByLabelText( 'AM', { selector: 'button' } ); await user.click( amButton ); @@ -205,7 +205,7 @@ describe( 'TimePicker', () => { /> ); - const pmButton = screen.getByText( 'PM' ); + const pmButton = screen.getByLabelText( 'PM', { selector: 'button' } ); await user.click( pmButton ); const hoursInput = screen.getByLabelText( 'Hours' ); From d367263be338fd360c0b1c7fef948c4fcfa52cb9 Mon Sep 17 00:00:00 2001 From: Vipul Patil Date: Fri, 17 May 2024 10:57:12 +0530 Subject: [PATCH 005/468] Updated CHANGELOG.md --- packages/components/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index f170fd81f7d593..0b1d40a51aa287 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -7,6 +7,7 @@ - Replaced `classnames` package with the faster and smaller `clsx` package ([#61138](https://github.com/WordPress/gutenberg/pull/61138)). - Upgrade `@use-gesture/react` package to `^10.3.1` ([#61503](https://github.com/WordPress/gutenberg/pull/61503)). - Upgrade `framer-motion` package to version `^11.1.9` ([#61572](https://github.com/WordPress/gutenberg/pull/61572)). +- Replaced `ButtonGroup` with `ToggleGroupControl` component for "AM/PM" selector in DateTime component ([#61562](https://github.com/WordPress/gutenberg/pull/61562)). ### Enhancements From a740dcc5bfd73a090b02d4379c901c3d6709a5fa Mon Sep 17 00:00:00 2001 From: Vipul Patil Date: Fri, 17 May 2024 11:00:34 +0530 Subject: [PATCH 006/468] Moved text within CHANGELOG.md to appropriate position. --- packages/components/CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index d7b3578df48509..2bd516dc5e0fe3 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +- Replaced `ButtonGroup` with `ToggleGroupControl` component for "AM/PM" selector in DateTime component ([#61562](https://github.com/WordPress/gutenberg/pull/61562)). + ## 27.6.0 (2024-05-16) ### Internal @@ -9,7 +11,6 @@ - Replaced `classnames` package with the faster and smaller `clsx` package ([#61138](https://github.com/WordPress/gutenberg/pull/61138)). - Upgrade `@use-gesture/react` package to `^10.3.1` ([#61503](https://github.com/WordPress/gutenberg/pull/61503)). - Upgrade `framer-motion` package to version `^11.1.9` ([#61572](https://github.com/WordPress/gutenberg/pull/61572)). -- Replaced `ButtonGroup` with `ToggleGroupControl` component for "AM/PM" selector in DateTime component ([#61562](https://github.com/WordPress/gutenberg/pull/61562)). ### Enhancements From da43be98b619e1bc6780381ace6a9a50eb58ddc2 Mon Sep 17 00:00:00 2001 From: Vipul Patil Date: Fri, 17 May 2024 12:22:27 +0530 Subject: [PATCH 007/468] FIX: Add missing value parameter to ToggleGroupControl. --- packages/components/src/date-time/time/index.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/components/src/date-time/time/index.tsx b/packages/components/src/date-time/time/index.tsx index cfe512df377dde..7b90991790b5a6 100644 --- a/packages/components/src/date-time/time/index.tsx +++ b/packages/components/src/date-time/time/index.tsx @@ -304,19 +304,20 @@ export function TimePicker( { __next40pxDefaultSize __nextHasNoMarginBottom isBlock + value={ am } > From 47e2f8996ee6284eacfe5782be4df1730bd698d6 Mon Sep 17 00:00:00 2001 From: Artemio Morales Date: Fri, 17 May 2024 15:25:43 +0200 Subject: [PATCH 008/468] Add Block Bindings Panel to Block Inspector (#61527) * Add initial pass at bindings panel in Block Inspector * Add bindings panel to inspector controls with tabs * Revise classnames and structure, add styles * Rename BindingsPanel to BlockBindingsPanel * Include Bindings Panel with hooks instead * Revert extraneous changes * Revert deletion of space * Remove unnecessary unlock * Remove unused declaration * Simplify check for bindings * Rename file; update imports * Merge useSelect calls * Use block context to look up bindings * Add handling for unknown sources * Remove unnecessary use of index * Simplify access of bindings * Use existing HStack instead of CSS * Remove error state; show source name if label is undefined Co-authored-by: artemiomorales Co-authored-by: gziolo Co-authored-by: SantosGuillamot Co-authored-by: cbravobernal Co-authored-by: jasmussen Co-authored-by: mirka <0mirka00@git.wordpress.org> --- .../block-editor/src/hooks/block-bindings.js | 70 +++++++++++++++++++ .../src/hooks/block-bindings.scss | 3 + packages/block-editor/src/hooks/index.js | 2 + packages/block-editor/src/style.scss | 1 + 4 files changed, 76 insertions(+) create mode 100644 packages/block-editor/src/hooks/block-bindings.js create mode 100644 packages/block-editor/src/hooks/block-bindings.scss diff --git a/packages/block-editor/src/hooks/block-bindings.js b/packages/block-editor/src/hooks/block-bindings.js new file mode 100644 index 00000000000000..72ac01a099e6d6 --- /dev/null +++ b/packages/block-editor/src/hooks/block-bindings.js @@ -0,0 +1,70 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { store as blocksStore } from '@wordpress/blocks'; +import { + PanelBody, + __experimentalHStack as HStack, + __experimentalItemGroup as ItemGroup, + __experimentalItem as Item, +} from '@wordpress/components'; +import { useSelect } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import { unlock } from '../lock-unlock'; +import InspectorControls from '../components/inspector-controls'; + +export const BlockBindingsPanel = ( { metadata } ) => { + const { bindings } = metadata || {}; + const { sources } = useSelect( ( select ) => { + const _sources = unlock( + select( blocksStore ) + ).getAllBlockBindingsSources(); + + return { + sources: _sources, + }; + }, [] ); + + if ( ! bindings ) { + return null; + } + + return ( + + + + { Object.keys( bindings ).map( ( key ) => { + return ( + + + { key } + + { sources[ bindings[ key ].source ] + ? sources[ bindings[ key ].source ] + .label + : bindings[ key ].source } + + + + ); + } ) } + + + + ); +}; + +export default { + edit: BlockBindingsPanel, + attributeKeys: [ 'metadata' ], + hasSupport() { + return true; + }, +}; diff --git a/packages/block-editor/src/hooks/block-bindings.scss b/packages/block-editor/src/hooks/block-bindings.scss new file mode 100644 index 00000000000000..fd46674ad11426 --- /dev/null +++ b/packages/block-editor/src/hooks/block-bindings.scss @@ -0,0 +1,3 @@ +.components-panel__block-bindings-panel .components-item__block-bindings-source { + color: $gray-700; +} diff --git a/packages/block-editor/src/hooks/index.js b/packages/block-editor/src/hooks/index.js index ec5cf29b49c5a6..4a59c2faa0073f 100644 --- a/packages/block-editor/src/hooks/index.js +++ b/packages/block-editor/src/hooks/index.js @@ -29,11 +29,13 @@ import childLayout from './layout-child'; import contentLockUI from './content-lock-ui'; import './metadata'; import blockHooks from './block-hooks'; +import blockBindingsPanel from './block-bindings'; import './block-renaming'; import './use-bindings-attributes'; createBlockEditFilter( [ + blockBindingsPanel, align, textAlign, anchor, diff --git a/packages/block-editor/src/style.scss b/packages/block-editor/src/style.scss index 5080aa05718bb3..484d79e8db9fa0 100644 --- a/packages/block-editor/src/style.scss +++ b/packages/block-editor/src/style.scss @@ -46,6 +46,7 @@ @import "./components/tool-selector/style.scss"; @import "./components/url-input/style.scss"; @import "./components/url-popover/style.scss"; +@import "./hooks/block-bindings.scss"; @import "./hooks/block-hooks.scss"; @import "./hooks/border.scss"; @import "./hooks/color.scss"; From 4d0902c26905d35111a1e7463278aa5a4c7c15eb Mon Sep 17 00:00:00 2001 From: Jerry Jones Date: Sat, 18 May 2024 01:02:48 +0900 Subject: [PATCH 009/468] Fix being unable to switch modes while inserter is open (#61563) --- packages/block-editor/src/hooks/use-zoom-out.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/hooks/use-zoom-out.js b/packages/block-editor/src/hooks/use-zoom-out.js index ce20cb5bd7a179..3ec701cfc4a14d 100644 --- a/packages/block-editor/src/hooks/use-zoom-out.js +++ b/packages/block-editor/src/hooks/use-zoom-out.js @@ -29,7 +29,10 @@ export function useZoomOut( zoomOut = true ) { return () => { // We need to use __unstableGetEditorMode() here and not `mode`, as mode may not update on unmount - if ( __unstableGetEditorMode() !== originalEditingMode.current ) { + if ( + __unstableGetEditorMode() === 'zoom-out' && + __unstableGetEditorMode() !== originalEditingMode.current + ) { __unstableSetEditorMode( originalEditingMode.current ); } }; @@ -39,7 +42,11 @@ export function useZoomOut( zoomOut = true ) { useEffect( () => { if ( zoomOut && mode !== 'zoom-out' ) { __unstableSetEditorMode( 'zoom-out' ); - } else if ( ! zoomOut && originalEditingMode.current !== mode ) { + } else if ( + ! zoomOut && + __unstableGetEditorMode() === 'zoom-out' && + originalEditingMode.current !== mode + ) { __unstableSetEditorMode( originalEditingMode.current ); } }, [ __unstableSetEditorMode, zoomOut, mode ] ); From 05496ebda222c8eb742fb79eb59d0ee670811a74 Mon Sep 17 00:00:00 2001 From: Nick Diego Date: Fri, 17 May 2024 15:20:25 -0500 Subject: [PATCH 010/468] Add an example and improve readability/grammar. (#61770) Co-authored-by: ndiego Co-authored-by: ryanwelcher --- .../reference-guides/filters/block-filters.md | 121 ++++++++++-------- 1 file changed, 65 insertions(+), 56 deletions(-) diff --git a/docs/reference-guides/filters/block-filters.md b/docs/reference-guides/filters/block-filters.md index 7fa20249760408..ee100431eb512e 100644 --- a/docs/reference-guides/filters/block-filters.md +++ b/docs/reference-guides/filters/block-filters.md @@ -1,61 +1,76 @@ # Block Filters -To modify the behavior of existing blocks, WordPress exposes several APIs. +WordPress exposes several APIs that allow you to modify the behavior of existing blocks. ## Registration -The following filters are available to extend the settings for blocks during their registration. +The following filters are available to extend block settings during their registration. ### `block_type_metadata` -Filters the raw metadata loaded from the `block.json` file when registering a block type on the server with PHP. It allows applying modifications before the metadata gets processed. +Filters the raw metadata loaded from the `block.json` file when registering a block type on the server with PHP. It allows modifications to be applied before the metadata gets processed. -The filter takes one param: +The filter takes one parameter: -- `$metadata` (`array`) – metadata loaded from `block.json` for registering a block type. +- `$metadata` (`array`) – metadata loaded from `block.json` for registering a block type. -_Example_: +The following example sets the `apiVersion` of all blocks to `2`. ```php - { @@ -233,7 +244,7 @@ const withMyPluginControls = createHigherOrderComponent( ( BlockEdit ) => { Used to modify the block's wrapper component containing the block's `edit` component and all toolbars. It receives the original `BlockListBlock` component and returns a new wrapped component. -_Example:_ +The following example adds a unique class name. ```js const { createHigherOrderComponent } = wp.compose; @@ -259,10 +270,7 @@ wp.hooks.addFilter( ); ``` -Adding new properties to the block's wrapper component can be achieved by adding them to the `wrapperProps` property of the returned component. - -_Example:_ - +You can add new properties to the block's wrapper component using the `wrapperProps` property of the returned component as shown in the following example. ```js const { createHigherOrderComponent } = wp.compose; @@ -275,6 +283,7 @@ const withMyWrapperProp = createHigherOrderComponent( ( BlockListBlock ) => { return ; }; }, 'withMyWrapperProp' ); + wp.hooks.addFilter( 'editor.BlockListBlock', 'my-plugin/with-my-wrapper-prop', @@ -282,12 +291,11 @@ wp.hooks.addFilter( ); ``` - ### `editor.postContentBlockTypes` -Used to modify the list of blocks that should be enabled even when used inside a locked template. Any block that saves data to a post should be added here. Examples of this are the post featured image block. Which often gets used in templates but should still allow selecting the image even when the template is locked. +Used to modify the list of blocks that should be enabled even when used inside a locked template. Any block that saves data to a post should be added here. An example of this is the Post Featured Image block. Often used in templates, this block should still allow selecting the image even when the template is locked. -_Example:_ +The following example enables the fictitious block `namespace/example`. ```js const addExampleBlockToPostContentBlockTypes = ( blockTypes ) => { @@ -305,8 +313,9 @@ wp.hooks.addFilter( ### Using a deny list -Adding blocks is easy enough, removing them is as easy. Plugin or theme authors have the possibility to "unregister" blocks. +Adding blocks is easy enough, and removing them is as easy. Plugin or theme authors can "unregister" blocks using a deny list in JavaScript. +Place the following code in a `my-plugin.js` file. ```js // my-plugin.js @@ -318,8 +327,7 @@ domReady( function () { } ); ``` - -and load this script in the Editor +Then, load this script in the Editor using the following function. ```php + When unregistering a block, there can be a race condition on which code runs first: registering the block or unregistering the block. You want your unregister code to run last. To do this, you must specify the component that is registering the block as a dependency, in this case, wp-edit-post. Additionally, using wp.domReady() ensures the unregister code runs once the dom is loaded. + ### Using an allow list @@ -362,37 +372,37 @@ wp.blocks.getBlockTypes().forEach( function ( blockType ) { ### `allowed_block_types_all` -_**Note:** Before WordPress 5.8 known as `allowed_block_types`. In the case when you want to support older versions of WordPress you might need a way to detect which filter should be used – the deprecated one vs the new one. The recommended way to proceed is to check if the `WP_Block_Editor_Context` class exists._ +
+ Before WordPress 5.8, this hook was known as allowed_block_types, which is now deprecated. If you need to support older versions of WordPress, you might need a way to detect which filter should be used. You can check if allowed_block_types is safe to use by seeing if the WP_Block_Editor_Context class exists, which was introduced in 5.8. +
-On the server, you can filter the list of blocks shown in the inserter using the `allowed_block_types_all` filter. You can return either true (all block types supported), false (no block types supported), or an array of block type names to allow. You can also use the second provided param `$editor_context` to filter block types based on its content. +On the server, you can filter the list of blocks shown in the inserter using the `allowed_block_types_all` filter. You can return either true (all block types supported), false (no block types supported), or an array of block type names to allow. You can also use the second provided parameter `$editor_context` to filter block types based on their content. ```php post ) ) { return array( 'core/paragraph', 'core/heading' ); } return $allowed_block_types; } - -add_filter( 'allowed_block_types_all', 'wpdocs_filter_allowed_block_types_when_post_provided', 10, 2 ); +add_filter( 'allowed_block_types_all', 'example_filter_allowed_block_types_when_post_provided', 10, 2 ); ``` ## Managing block categories ### `block_categories_all` -_**Note:** Before WordPress 5.8 known as `block_categories`. In the case when you want to support older versions of WordPress you might need a way to detect which filter should be used – the deprecated one vs the new one. The recommended way to proceed is to check if the `WP_Block_Editor_Context` class exists._ +
+ Before WordPress 5.8, this hook was known as block_categories, which is now deprecated. If you need to support older versions of WordPress, you might need a way to detect which filter should be used. You can check if block_categories is safe to use by seeing if the WP_Block_Editor_Context class exists, which was introduced in 5.8. +
-It is possible to filter the list of default block categories using the `block_categories_all` filter. You can do it on the server by implementing a function which returns a list of categories. It is going to be used during blocks registration and to group blocks in the inserter. You can also use the second provided param `$editor_context` to filter the based on its content. +It is possible to filter the list of default block categories using the `block_categories_all` filter. You can do it on the server by implementing a function which returns a list of categories. It is going to be used during block registration and to group blocks in the inserter. You can also use the second provided parameter `$editor_context` to filter the based on its content. ```php -post ) ) { array_push( $block_categories, @@ -405,8 +415,7 @@ function wpdocs_filter_block_categories_when_post_provided( $block_categories, $ } return $block_categories; } - -add_filter( 'block_categories_all', 'wpdocs_filter_block_categories_when_post_provided', 10, 2 ); +add_filter( 'block_categories_all', 'example_filter_block_categories_when_post_provided', 10, 2 ); ``` ### `wp.blocks.updateCategory` From b31dccf40b788839f087b49ec3c6475586986dd3 Mon Sep 17 00:00:00 2001 From: Nick Diego Date: Fri, 17 May 2024 15:20:37 -0500 Subject: [PATCH 011/468] Add a section about block filters. (#61771) Co-authored-by: ndiego Co-authored-by: ryanwelcher --- .../filters-and-hooks.md | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/docs/how-to-guides/curating-the-editor-experience/filters-and-hooks.md b/docs/how-to-guides/curating-the-editor-experience/filters-and-hooks.md index f5e21340adeaa0..b28fdf7d9e5db1 100644 --- a/docs/how-to-guides/curating-the-editor-experience/filters-and-hooks.md +++ b/docs/how-to-guides/curating-the-editor-experience/filters-and-hooks.md @@ -136,6 +136,43 @@ addFilter( ); ``` +## Block Filters + +Beyond curating the Editor itself, there are many ways that you can modify individual blocks. Perhaps you want to disable particular block supports like background color or define which settings should be displayed by default on specific blocks. + +One of the most commonly used filters is [`block_type_metadata`](https://developer.wordpress.org/reference/hooks/block_type_metadata/). It allows you to filter the raw metadata loaded from a block's `block.json` file when a block type is registered on the server with PHP. + +The filter takes one parameter: + +- `$metadata` (`array`) – metadata loaded from `block.json` for registering a block type. + +The `$metadata` array contains everything you might want to know about a block, from its description and [attributes](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-attributes/) to block [supports](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-supports/). + +In the following example, background color and gradient support are disabled for Heading blocks. + +```php +function example_disable_heading_background_color_and_gradients( $metadata ) { + + // Only apply the filter to Heading blocks. + if ( ! isset( $metadata['name'] ) || 'core/heading' !== $metadata['name'] ) { + return $metadata; + } + + // Check if 'supports' key exists. + if ( isset( $metadata['supports'] ) && isset( $metadata['supports']['color'] ) ) { + + // Remove Background color and Gradients support. + $metadata['supports']['color']['background'] = false; + $metadata['supports']['color']['gradients'] = false; + } + + return $metadata; +} +add_filter( 'block_type_metadata', 'example_disable_heading_background_color_and_gradients' ); +``` + +You can learn more about the available block filters in the [Block Filters](https://developer.wordpress.org/block-editor/reference-guides/filters/block-filters/) documentation. + ## Additional resources - [How to modify theme.json data using server-side filters](https://developer.wordpress.org/news/2023/07/05/how-to-modify-theme-json-data-using-server-side-filters/) (WordPress Developer Blog) From 9ec48c941c8eae2ba91a78d654b458e6b719347c Mon Sep 17 00:00:00 2001 From: Brian Gosnell Date: Sat, 18 May 2024 00:10:54 -0500 Subject: [PATCH 012/468] Add JSDoc to PostVisibility, PostVisibilityCheck, and PostVisibilityLabel (#61735) Co-authored-by: dbrian Co-authored-by: colorful-tones Co-authored-by: ntsekouras --- packages/editor/README.md | 34 ++++++++++++++++--- .../src/components/post-visibility/check.js | 9 +++++ .../src/components/post-visibility/index.js | 7 ++++ .../src/components/post-visibility/label.js | 10 ++++++ 4 files changed, 56 insertions(+), 4 deletions(-) diff --git a/packages/editor/README.md b/packages/editor/README.md index 14ec88d86fc344..7241f8c965903d 100644 --- a/packages/editor/README.md +++ b/packages/editor/README.md @@ -1184,15 +1184,37 @@ Undocumented declaration. ### PostVisibility -Undocumented declaration. +Allows users to set the visibility of a post. + +_Parameters_ + +- _props_ `Object`: The component props. +- _props.onClose_ `Function`: Function to call when the popover is closed. + +_Returns_ + +- `JSX.Element`: The rendered component. ### PostVisibilityCheck -Undocumented declaration. +Determines if the current post can be edited (published) and passes this information to the provided render function. + +_Parameters_ + +- _props_ `Object`: The component props. +- _props.render_ `Function`: Function to render the component. Receives an object with a `canEdit` property. + +_Returns_ + +- `JSX.Element`: The rendered component. ### PostVisibilityLabel -Undocumented declaration. +Returns the label for the current post visibility setting. + +_Returns_ + +- `string`: Post visibility label. ### privateApis @@ -1305,7 +1327,11 @@ Undocumented declaration. ### usePostVisibilityLabel -Undocumented declaration. +Get the label for the current post visibility setting. + +_Returns_ + +- `string`: Post visibility label. ### userAutocompleter diff --git a/packages/editor/src/components/post-visibility/check.js b/packages/editor/src/components/post-visibility/check.js index 116db0f546de2b..19a241ae1110ae 100644 --- a/packages/editor/src/components/post-visibility/check.js +++ b/packages/editor/src/components/post-visibility/check.js @@ -8,6 +8,15 @@ import { useSelect } from '@wordpress/data'; */ import { store as editorStore } from '../../store'; +/** + * Determines if the current post can be edited (published) + * and passes this information to the provided render function. + * + * @param {Object} props The component props. + * @param {Function} props.render Function to render the component. + * Receives an object with a `canEdit` property. + * @return {JSX.Element} The rendered component. + */ export default function PostVisibilityCheck( { render } ) { const canEdit = useSelect( ( select ) => { return ( diff --git a/packages/editor/src/components/post-visibility/index.js b/packages/editor/src/components/post-visibility/index.js index 0dde8905cbb36f..fef07d0033d364 100644 --- a/packages/editor/src/components/post-visibility/index.js +++ b/packages/editor/src/components/post-visibility/index.js @@ -17,6 +17,13 @@ import { __experimentalInspectorPopoverHeader as InspectorPopoverHeader } from ' import { visibilityOptions } from './utils'; import { store as editorStore } from '../../store'; +/** + * Allows users to set the visibility of a post. + * + * @param {Object} props The component props. + * @param {Function} props.onClose Function to call when the popover is closed. + * @return {JSX.Element} The rendered component. + */ export default function PostVisibility( { onClose } ) { const instanceId = useInstanceId( PostVisibility ); diff --git a/packages/editor/src/components/post-visibility/label.js b/packages/editor/src/components/post-visibility/label.js index 580eed75620b4d..50a6e14aa1a14e 100644 --- a/packages/editor/src/components/post-visibility/label.js +++ b/packages/editor/src/components/post-visibility/label.js @@ -9,10 +9,20 @@ import { useSelect } from '@wordpress/data'; import { visibilityOptions } from './utils'; import { store as editorStore } from '../../store'; +/** + * Returns the label for the current post visibility setting. + * + * @return {string} Post visibility label. + */ export default function PostVisibilityLabel() { return usePostVisibilityLabel(); } +/** + * Get the label for the current post visibility setting. + * + * @return {string} Post visibility label. + */ export function usePostVisibilityLabel() { const visibility = useSelect( ( select ) => select( editorStore ).getEditedPostVisibility() From c1a61919e92ecd79d7a5339c41732758aeef37a5 Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Sun, 19 May 2024 09:22:15 +1000 Subject: [PATCH 013/468] Update child layout selector to match core. (#61777) Co-authored-by: tellthemachines Co-authored-by: andrewserong --- lib/block-supports/layout.php | 2 +- phpunit/block-supports/layout-test.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/block-supports/layout.php b/lib/block-supports/layout.php index aac62e402148a3..dc5fe2e6a87dae 100644 --- a/lib/block-supports/layout.php +++ b/lib/block-supports/layout.php @@ -577,7 +577,7 @@ function gutenberg_render_layout_support_flag( $block_content, $block ) { // Child layout specific logic. if ( $child_layout ) { - $container_content_class = wp_unique_id( 'wp-container-content-' ); + $container_content_class = wp_unique_prefixed_id( 'wp-container-content-' ); $child_layout_declarations = array(); $child_layout_styles = array(); diff --git a/phpunit/block-supports/layout-test.php b/phpunit/block-supports/layout-test.php index 6344d7617aeeb7..dd40283171e5cd 100644 --- a/phpunit/block-supports/layout-test.php +++ b/phpunit/block-supports/layout-test.php @@ -501,7 +501,7 @@ public function data_layout_support_flag_renders_classnames_on_wrapper() { ), ), ), - 'expected_output' => '

Some text.

', // The generated classname number assumes `wp_unique_id` will not have run previously in this test. + 'expected_output' => '

Some text.

', // The generated classname number assumes `wp_unique_prefixed_id( 'wp-container-content-' )` will not have run previously in this test. ), ); } From c51164a3d342998580a58365f876d7109008dbc5 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Sun, 19 May 2024 11:54:14 +0200 Subject: [PATCH 014/468] Update: Implement new author panel design. (#61362) Co-authored-by: jorgefilipecosta Co-authored-by: jameskoster --- .../src/components/post-author/combobox.js | 1 + .../editor/src/components/post-author/hook.js | 2 +- .../src/components/post-author/panel.js | 66 ++++++++++++++++++- .../src/components/post-author/select.js | 1 + .../src/components/post-author/style.scss | 6 ++ .../src/components/sidebar/post-summary.js | 2 +- 6 files changed, 74 insertions(+), 4 deletions(-) diff --git a/packages/editor/src/components/post-author/combobox.js b/packages/editor/src/components/post-author/combobox.js index 0ba74b072d3956..867eca7947976a 100644 --- a/packages/editor/src/components/post-author/combobox.js +++ b/packages/editor/src/components/post-author/combobox.js @@ -50,6 +50,7 @@ export default function PostAuthorCombobox() { onFilterValueChange={ debounce( handleKeydown, 300 ) } onChange={ handleSelect } allowReset={ false } + hideLabelFromVision /> ); } diff --git a/packages/editor/src/components/post-author/hook.js b/packages/editor/src/components/post-author/hook.js index 9189736f21502a..116c354669ca8e 100644 --- a/packages/editor/src/components/post-author/hook.js +++ b/packages/editor/src/components/post-author/hook.js @@ -59,5 +59,5 @@ export function useAuthorsQuery( search ) { return fetchedAuthors; }, [ authors, postAuthor ] ); - return { authorId, authorOptions }; + return { authorId, authorOptions, postAuthor }; } diff --git a/packages/editor/src/components/post-author/panel.js b/packages/editor/src/components/post-author/panel.js index ad2aa01dee3ab2..908617a86f2900 100644 --- a/packages/editor/src/components/post-author/panel.js +++ b/packages/editor/src/components/post-author/panel.js @@ -1,9 +1,36 @@ +/** + * WordPress dependencies + */ +import { __, sprintf } from '@wordpress/i18n'; +import { Button, Dropdown } from '@wordpress/components'; +import { useState, useMemo } from '@wordpress/element'; +import { __experimentalInspectorPopoverHeader as InspectorPopoverHeader } from '@wordpress/block-editor'; + /** * Internal dependencies */ import PostAuthorCheck from './check'; import PostAuthorForm from './index'; import PostPanelRow from '../post-panel-row'; +import { useAuthorsQuery } from './hook'; + +function PostAuthorToggle( { isOpen, onClick } ) { + const { postAuthor } = useAuthorsQuery(); + const authorName = postAuthor?.name || ''; + return ( + + ); +} /** * Renders the Post Author Panel component. @@ -11,10 +38,45 @@ import PostPanelRow from '../post-panel-row'; * @return {Component} The component to be rendered. */ export function PostAuthor() { + // Use internal state instead of a ref to make sure that the component + // re-renders when the popover's anchor updates. + const [ popoverAnchor, setPopoverAnchor ] = useState( null ); + // Memoize popoverProps to avoid returning a new object every time. + const popoverProps = useMemo( + () => ( { + // Anchor the popover to the middle of the entire row so that it doesn't + // move around when the label changes. + anchor: popoverAnchor, + placement: 'left-start', + offset: 36, + shift: true, + } ), + [ popoverAnchor ] + ); return ( - - + + ( + + ) } + renderContent={ ( { onClose } ) => ( +
+ + +
+ ) } + />
); diff --git a/packages/editor/src/components/post-author/select.js b/packages/editor/src/components/post-author/select.js index 03bb9be23060f1..e51cc288cc4530 100644 --- a/packages/editor/src/components/post-author/select.js +++ b/packages/editor/src/components/post-author/select.js @@ -29,6 +29,7 @@ export default function PostAuthorSelect() { options={ authorOptions } onChange={ setAuthorId } value={ authorId } + hideLabelFromVision /> ); } diff --git a/packages/editor/src/components/post-author/style.scss b/packages/editor/src/components/post-author/style.scss index 349ad712334c8d..548f781241b0a2 100644 --- a/packages/editor/src/components/post-author/style.scss +++ b/packages/editor/src/components/post-author/style.scss @@ -5,3 +5,9 @@ .editor-post-author__panel .editor-post-panel__row-control > div { width: 100%; } + +.editor-post-author__panel-dialog .editor-post-author { + // sidebar width - popover padding - form margin + min-width: $sidebar-width - $grid-unit-20 - $grid-unit-20; + margin: $grid-unit-10; +} diff --git a/packages/editor/src/components/sidebar/post-summary.js b/packages/editor/src/components/sidebar/post-summary.js index 807ff25c2d9ff0..839d60c3b188a2 100644 --- a/packages/editor/src/components/sidebar/post-summary.js +++ b/packages/editor/src/components/sidebar/post-summary.js @@ -81,12 +81,12 @@ export default function PostSummary( { onActionPerformed } ) { + - { isTemplate && } { fills } { ! isPattern && From 1fac8b733d58caf041863c44b53a9396a14f2237 Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Mon, 20 May 2024 14:26:38 +0900 Subject: [PATCH 015/468] Command Palette: Remove unused url parameter (#61783) Co-authored-by: t-hamano Co-authored-by: aaronrobertshaw --- .../src/site-editor-navigation-commands.js | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/packages/core-commands/src/site-editor-navigation-commands.js b/packages/core-commands/src/site-editor-navigation-commands.js index 410a3e875a8dfb..6ef9be58b4d007 100644 --- a/packages/core-commands/src/site-editor-navigation-commands.js +++ b/packages/core-commands/src/site-editor-navigation-commands.js @@ -26,7 +26,7 @@ import { useIsBlockBasedTheme } from './hooks'; import { unlock } from './lock-unlock'; import { orderEntityRecordsBySearch } from './utils/order-entity-records-by-search'; -const { useHistory, useLocation } = unlock( routerPrivateApis ); +const { useHistory } = unlock( routerPrivateApis ); const icons = { post, @@ -160,14 +160,6 @@ const getNavigationCommandLoaderPerPostType = ( postType ) => const getNavigationCommandLoaderPerTemplate = ( templateType ) => function useNavigationCommandLoader( { search } ) { const history = useHistory(); - const location = useLocation(); - - const isPatternsPage = - location?.params?.path === '/patterns' || - location?.params?.postType === 'wp_block'; - const didAccessPatternsPage = - !! location?.params?.didAccessPatternsPage; - const isBlockBasedTheme = useIsBlockBasedTheme(); const { records, isLoading } = useSelect( ( select ) => { const { getEntityRecords } = select( coreStore ); @@ -223,11 +215,6 @@ const getNavigationCommandLoaderPerTemplate = ( templateType ) => const args = { postType: templateType, postId: record.id, - didAccessPatternsPage: - ! isBlockBasedTheme && - ( isPatternsPage || didAccessPatternsPage ) - ? 1 - : undefined, ...extraArgs, }; const targetUrl = addQueryArgs( From 666d13115d48e86c1e511bf5e017a3b3aeb0ce1a Mon Sep 17 00:00:00 2001 From: Anver Sadutt Date: Mon, 20 May 2024 10:56:57 +0530 Subject: [PATCH 016/468] Update plugin-document-setting-panel.md (#61782) Typo / Grammar mistake --- .../reference-guides/slotfills/plugin-document-setting-panel.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference-guides/slotfills/plugin-document-setting-panel.md b/docs/reference-guides/slotfills/plugin-document-setting-panel.md index d278a9e96981d1..aa3e55abe2f7ee 100644 --- a/docs/reference-guides/slotfills/plugin-document-setting-panel.md +++ b/docs/reference-guides/slotfills/plugin-document-setting-panel.md @@ -33,7 +33,7 @@ registerPlugin( 'plugin-document-setting-panel-demo', { ## Accessing a panel programmatically -Core and custom panels can be access programmatically using their panel name. The core panel names are: +Core and custom panels can be accessed programmatically using their panel name. The core panel names are: - Summary Panel: `post-status` - Categories Panel: `taxonomy-panel-category` From 62761c7bfc30e7eae9520661e5589f5ea08cc31b Mon Sep 17 00:00:00 2001 From: paolopiaggio Date: Mon, 20 May 2024 07:54:54 +0200 Subject: [PATCH 017/468] Playwright E2E Utils: add fullscreenMode option to createNewPost (#61766) Unlinked contributors: paolopiaggio. * add fullscreenMode option to createNewPost function * fix lint * retrigger checks --- .../e2e-test-utils-playwright/src/admin/create-new-post.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/e2e-test-utils-playwright/src/admin/create-new-post.ts b/packages/e2e-test-utils-playwright/src/admin/create-new-post.ts index 5b06c6a2c53cbc..a5d6617946ae31 100644 --- a/packages/e2e-test-utils-playwright/src/admin/create-new-post.ts +++ b/packages/e2e-test-utils-playwright/src/admin/create-new-post.ts @@ -9,6 +9,7 @@ interface NewPostOptions { content?: string; excerpt?: string; showWelcomeGuide?: boolean; + fullscreenMode?: boolean; } /** @@ -41,6 +42,6 @@ export async function createNewPost( await this.editor.setPreferences( 'core/edit-post', { welcomeGuide: options.showWelcomeGuide ?? false, - fullscreenMode: false, + fullscreenMode: options.fullscreenMode ?? false, } ); } From 5135bc91037820cb1d1716f83803dc6b14601d37 Mon Sep 17 00:00:00 2001 From: Vipul Gupta <55375170+vipul0425@users.noreply.github.com> Date: Mon, 20 May 2024 11:34:49 +0530 Subject: [PATCH 018/468] fix: The latest post block - post titles overlapping. (#61356) Co-authored-by: vipul0425 Co-authored-by: carolinan Co-authored-by: t-hamano --- packages/block-library/src/latest-posts/style.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/block-library/src/latest-posts/style.scss b/packages/block-library/src/latest-posts/style.scss index 17d92a4ad91b92..5d3e360d0804a8 100644 --- a/packages/block-library/src/latest-posts/style.scss +++ b/packages/block-library/src/latest-posts/style.scss @@ -16,6 +16,7 @@ li { clear: both; + overflow-wrap: break-word; } } From 115201b8fd8d464006986b1a8c63d04e6ef7a40f Mon Sep 17 00:00:00 2001 From: Damon Cook Date: Mon, 20 May 2024 02:48:10 -0400 Subject: [PATCH 019/468] Update PostFormat, PostFormatCheck editor component docs (#61732) Co-authored-by: colorful-tones Co-authored-by: ntsekouras --- packages/editor/README.md | 23 +++++++++++++++++-- .../src/components/post-format/check.js | 8 +++++++ .../src/components/post-format/index.js | 10 ++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/packages/editor/README.md b/packages/editor/README.md index 7241f8c965903d..2bb42c8ca1d982 100644 --- a/packages/editor/README.md +++ b/packages/editor/README.md @@ -966,11 +966,30 @@ Undocumented declaration. ### PostFormat -Undocumented declaration. +`PostFormat` a component that allows changing the post format while also providing a suggestion for the current post. + +_Usage_ + +```jsx + +``` + +_Returns_ + +- `JSX.Element`: The rendered PostFormat component. ### PostFormatCheck -Undocumented declaration. +Component check if there are any post formats. + +_Parameters_ + +- _props_ `Object`: The component props. +- _props.children_ `Element`: The child elements to render. + +_Returns_ + +- `Component|null`: The rendered component or null if post formats are disabled. ### PostLastRevision diff --git a/packages/editor/src/components/post-format/check.js b/packages/editor/src/components/post-format/check.js index 0810c9d613aae4..35729770b93c40 100644 --- a/packages/editor/src/components/post-format/check.js +++ b/packages/editor/src/components/post-format/check.js @@ -27,4 +27,12 @@ function PostFormatCheck( { children } ) { ); } +/** + * Component check if there are any post formats. + * + * @param {Object} props The component props. + * @param {Element} props.children The child elements to render. + * + * @return {Component|null} The rendered component or null if post formats are disabled. + */ export default PostFormatCheck; diff --git a/packages/editor/src/components/post-format/index.js b/packages/editor/src/components/post-format/index.js index 2b422ccebbaa84..2aec930298c204 100644 --- a/packages/editor/src/components/post-format/index.js +++ b/packages/editor/src/components/post-format/index.js @@ -38,6 +38,16 @@ export const POST_FORMATS = [ return 0; } ); +/** + * `PostFormat` a component that allows changing the post format while also providing a suggestion for the current post. + * + * @example + * ```jsx + * + * ``` + * + * @return {JSX.Element} The rendered PostFormat component. + */ export default function PostFormat() { const instanceId = useInstanceId( PostFormat ); const postFormatSelectorId = `post-format-selector-${ instanceId }`; From 2eab6f219e3483756ba6228d538a5800b8be2a63 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Mon, 20 May 2024 11:18:47 +0400 Subject: [PATCH 020/468] Unlock the private 'kebabCase' function at a file level (#60755) Co-authored-by: Mamaduka Co-authored-by: t-hamano Co-authored-by: tyxla Co-authored-by: ellatrix Co-authored-by: dcalhoun Co-authored-by: geriux --- packages/block-editor/src/components/colors/utils.js | 4 ++-- packages/block-editor/src/components/colors/with-colors.js | 3 ++- packages/block-editor/src/components/font-sizes/utils.js | 3 ++- .../components/global-styles/use-global-styles-output.js | 7 +------ packages/block-editor/src/hooks/font-family.js | 2 +- packages/block-editor/src/hooks/layout.js | 3 +-- packages/block-editor/src/hooks/use-typography-props.js | 3 ++- packages/block-library/src/embed/util.js | 2 +- .../font-library-modal/collection-font-variant.js | 3 ++- .../font-library-modal/library-font-variant.js | 3 ++- .../global-styles/font-library-modal/utils/index.js | 2 +- 11 files changed, 17 insertions(+), 18 deletions(-) diff --git a/packages/block-editor/src/components/colors/utils.js b/packages/block-editor/src/components/colors/utils.js index d6d51ad0013632..5ec42a6bae7007 100644 --- a/packages/block-editor/src/components/colors/utils.js +++ b/packages/block-editor/src/components/colors/utils.js @@ -17,6 +17,8 @@ import { unlock } from '../../lock-unlock'; extend( [ namesPlugin, a11yPlugin ] ); +const { kebabCase } = unlock( componentsPrivateApis ); + /** * Provided an array of color objects as set by the theme or by the editor defaults, * and the values of the defined color or custom color returns a color object describing the color. @@ -75,8 +77,6 @@ export function getColorClassName( colorContextName, colorSlug ) { return undefined; } - const { kebabCase } = unlock( componentsPrivateApis ); - return `has-${ kebabCase( colorSlug ) }-${ colorContextName }`; } diff --git a/packages/block-editor/src/components/colors/with-colors.js b/packages/block-editor/src/components/colors/with-colors.js index 33079f8b409d6e..680521fb6519c3 100644 --- a/packages/block-editor/src/components/colors/with-colors.js +++ b/packages/block-editor/src/components/colors/with-colors.js @@ -17,6 +17,8 @@ import { import { useSettings } from '../use-settings'; import { unlock } from '../../lock-unlock'; +const { kebabCase } = unlock( componentsPrivateApis ); + /** * Capitalizes the first letter in a string. * @@ -80,7 +82,6 @@ const withEditorColorPalette = () => * @return {Component} The component that can be used as a HOC. */ function createColorHOC( colorTypes, withColorPalette ) { - const { kebabCase } = unlock( componentsPrivateApis ); const colorMap = colorTypes.reduce( ( colorObject, colorType ) => { return { ...colorObject, diff --git a/packages/block-editor/src/components/font-sizes/utils.js b/packages/block-editor/src/components/font-sizes/utils.js index dff28c7a770d40..b43ed049507aa4 100644 --- a/packages/block-editor/src/components/font-sizes/utils.js +++ b/packages/block-editor/src/components/font-sizes/utils.js @@ -8,6 +8,8 @@ import { privateApis as componentsPrivateApis } from '@wordpress/components'; */ import { unlock } from '../../lock-unlock'; +const { kebabCase } = unlock( componentsPrivateApis ); + /** * Returns the font size object based on an array of named font sizes and the namedFontSize and customFontSize values. * If namedFontSize is undefined or not found in fontSizes an object with just the size value based on customFontSize is returned. @@ -69,6 +71,5 @@ export function getFontSizeClass( fontSizeSlug ) { return; } - const { kebabCase } = unlock( componentsPrivateApis ); return `has-${ kebabCase( fontSizeSlug ) }-font-size`; } diff --git a/packages/block-editor/src/components/global-styles/use-global-styles-output.js b/packages/block-editor/src/components/global-styles/use-global-styles-output.js index 23bb88efc9991e..d9be99efa86efd 100644 --- a/packages/block-editor/src/components/global-styles/use-global-styles-output.js +++ b/packages/block-editor/src/components/global-styles/use-global-styles-output.js @@ -44,6 +44,7 @@ const BLOCK_SUPPORT_FEATURE_LEVEL_SELECTORS = { spacing: 'spacing', typography: 'typography', }; +const { kebabCase } = unlock( componentsPrivateApis ); function compileStyleValue( uncompiledValue ) { const VARIABLE_REFERENCE_PREFIX = 'var:'; @@ -69,8 +70,6 @@ function compileStyleValue( uncompiledValue ) { * @return {Array} An array of style declarations. */ function getPresetsDeclarations( blockPresets = {}, mergedSettings ) { - const { kebabCase } = unlock( componentsPrivateApis ); - return PRESET_METADATA.reduce( ( declarations, { path, valueKey, valueFunc, cssVarInfix } ) => { const presetByOrigin = getValueFromObjectPath( @@ -115,8 +114,6 @@ function getPresetsDeclarations( blockPresets = {}, mergedSettings ) { * @return {string} CSS declarations for the preset classes. */ function getPresetsClasses( blockSelector = '*', blockPresets = {} ) { - const { kebabCase } = unlock( componentsPrivateApis ); - return PRESET_METADATA.reduce( ( declarations, { path, cssVarInfix, classes } ) => { if ( ! classes ) { @@ -181,7 +178,6 @@ function getPresetsSvgFilters( blockPresets = {} ) { } function flattenTree( input = {}, prefix, token ) { - const { kebabCase } = unlock( componentsPrivateApis ); let result = []; Object.keys( input ).forEach( ( key ) => { const newKey = prefix + kebabCase( key.replace( '/', '-' ) ); @@ -323,7 +319,6 @@ export function getStylesDeclarations( tree = {}, isTemplate = true ) { - const { kebabCase } = unlock( componentsPrivateApis ); const isRoot = ROOT_BLOCK_SELECTOR === selector; const output = Object.entries( STYLE_PROPERTY ).reduce( ( diff --git a/packages/block-editor/src/hooks/font-family.js b/packages/block-editor/src/hooks/font-family.js index db6515ef1c2fe0..ba9a66a8bcf04f 100644 --- a/packages/block-editor/src/hooks/font-family.js +++ b/packages/block-editor/src/hooks/font-family.js @@ -14,6 +14,7 @@ import { TYPOGRAPHY_SUPPORT_KEY } from './typography'; import { unlock } from '../lock-unlock'; export const FONT_FAMILY_SUPPORT_KEY = 'typography.__experimentalFontFamily'; +const { kebabCase } = unlock( componentsPrivateApis ); /** * Filters registered block settings, extending attributes to include @@ -68,7 +69,6 @@ function addSaveProps( props, blockType, attributes ) { // Use TokenList to dedupe classes. const classes = new TokenList( props.className ); - const { kebabCase } = unlock( componentsPrivateApis ); classes.add( `has-${ kebabCase( attributes?.fontFamily ) }-font-family` ); const newClassName = classes.value; props.className = newClassName ? newClassName : undefined; diff --git a/packages/block-editor/src/hooks/layout.js b/packages/block-editor/src/hooks/layout.js index 88192ddf5f055e..a19c5604701f6d 100644 --- a/packages/block-editor/src/hooks/layout.js +++ b/packages/block-editor/src/hooks/layout.js @@ -32,6 +32,7 @@ import { useBlockSettings, useStyleOverride } from './utils'; import { unlock } from '../lock-unlock'; const layoutBlockSupportKey = 'layout'; +const { kebabCase } = unlock( componentsPrivateApis ); function hasLayoutBlockSupport( blockName ) { return ( @@ -49,7 +50,6 @@ function hasLayoutBlockSupport( blockName ) { * @return { Array } Array of CSS classname strings. */ export function useLayoutClasses( blockAttributes = {}, blockName = '' ) { - const { kebabCase } = unlock( componentsPrivateApis ); const { layout } = blockAttributes; const { default: defaultBlockLayout } = getBlockSupport( blockName, layoutBlockSupportKey ) || {}; @@ -371,7 +371,6 @@ function BlockWithLayoutStyles( { ? { ...layout, type: 'constrained' } : layout || defaultBlockLayout || {}; - const { kebabCase } = unlock( componentsPrivateApis ); const selectorPrefix = `wp-container-${ kebabCase( name ) }-is-layout-`; // Higher specificity to override defaults from theme.json. const selector = `.${ selectorPrefix }${ id }`; diff --git a/packages/block-editor/src/hooks/use-typography-props.js b/packages/block-editor/src/hooks/use-typography-props.js index ec76cbf4bf6a06..0f1a2caefde048 100644 --- a/packages/block-editor/src/hooks/use-typography-props.js +++ b/packages/block-editor/src/hooks/use-typography-props.js @@ -16,6 +16,8 @@ import { getFontSizeClass } from '../components/font-sizes'; import { getTypographyFontSizeValue } from '../components/global-styles/typography-utils'; import { unlock } from '../lock-unlock'; +const { kebabCase } = unlock( componentsPrivateApis ); + /* * This utility is intended to assist where the serialization of the typography * block support is being skipped for a block but the typography related CSS @@ -31,7 +33,6 @@ import { unlock } from '../lock-unlock'; * @return {Object} Typography block support derived CSS classes & styles. */ export function getTypographyClassesAndStyles( attributes, settings ) { - const { kebabCase } = unlock( componentsPrivateApis ); let typographyStyles = attributes?.style?.typography || {}; typographyStyles = { ...typographyStyles, diff --git a/packages/block-library/src/embed/util.js b/packages/block-library/src/embed/util.js index 2af0e6adbfc6f2..eae45cc397e7b7 100644 --- a/packages/block-library/src/embed/util.js +++ b/packages/block-library/src/embed/util.js @@ -23,6 +23,7 @@ import { ASPECT_RATIOS, WP_EMBED_TYPE } from './constants'; import { unlock } from '../lock-unlock'; const { name: DEFAULT_EMBED_BLOCK } = metadata; +const { kebabCase } = unlock( componentsPrivateApis ); /** @typedef {import('@wordpress/blocks').WPBlockVariation} WPBlockVariation */ @@ -288,7 +289,6 @@ export const getAttributesFromPreview = memoize( // If we got a provider name from the API, use it for the slug, otherwise we use the title, // because not all embed code gives us a provider name. const { html, provider_name: providerName } = preview; - const { kebabCase } = unlock( componentsPrivateApis ); const providerNameSlug = kebabCase( ( providerName || title ).toLowerCase() ); diff --git a/packages/edit-site/src/components/global-styles/font-library-modal/collection-font-variant.js b/packages/edit-site/src/components/global-styles/font-library-modal/collection-font-variant.js index 6cc56b0a542f3e..0719cb873f8f78 100644 --- a/packages/edit-site/src/components/global-styles/font-library-modal/collection-font-variant.js +++ b/packages/edit-site/src/components/global-styles/font-library-modal/collection-font-variant.js @@ -14,6 +14,8 @@ import { getFontFaceVariantName } from './utils'; import FontDemo from './font-demo'; import { unlock } from '../../../lock-unlock'; +const { kebabCase } = unlock( componentsPrivateApis ); + function CollectionFontVariant( { face, font, @@ -29,7 +31,6 @@ function CollectionFontVariant( { }; const displayName = font.name + ' ' + getFontFaceVariantName( face ); - const { kebabCase } = unlock( componentsPrivateApis ); const checkboxId = kebabCase( `${ font.slug }-${ getFontFaceVariantName( face ) }` ); diff --git a/packages/edit-site/src/components/global-styles/font-library-modal/library-font-variant.js b/packages/edit-site/src/components/global-styles/font-library-modal/library-font-variant.js index b994d1f4c1e6b1..2f84bd1f2c749c 100644 --- a/packages/edit-site/src/components/global-styles/font-library-modal/library-font-variant.js +++ b/packages/edit-site/src/components/global-styles/font-library-modal/library-font-variant.js @@ -16,6 +16,8 @@ import { FontLibraryContext } from './context'; import FontDemo from './font-demo'; import { unlock } from '../../../lock-unlock'; +const { kebabCase } = unlock( componentsPrivateApis ); + function LibraryFontVariant( { face, font } ) { const { isFontActivated, toggleActivateFont } = useContext( FontLibraryContext ); @@ -39,7 +41,6 @@ function LibraryFontVariant( { face, font } ) { }; const displayName = font.name + ' ' + getFontFaceVariantName( face ); - const { kebabCase } = unlock( componentsPrivateApis ); const checkboxId = kebabCase( `${ font.slug }-${ getFontFaceVariantName( face ) }` ); diff --git a/packages/edit-site/src/components/global-styles/font-library-modal/utils/index.js b/packages/edit-site/src/components/global-styles/font-library-modal/utils/index.js index bd97b5b7fdcdd6..1ca7bb68c87a06 100644 --- a/packages/edit-site/src/components/global-styles/font-library-modal/utils/index.js +++ b/packages/edit-site/src/components/global-styles/font-library-modal/utils/index.js @@ -15,6 +15,7 @@ import { formatFontFaceName } from './preview-styles'; * Browser dependencies */ const { File } = window; +const { kebabCase } = unlock( componentsPrivateApis ); export function setUIValuesNeeded( font, extraValues = {} ) { if ( ! font.name && ( font.fontFamily || font.slug ) ) { @@ -184,7 +185,6 @@ export function getDisplaySrcFromFontFace( input ) { export function makeFontFamilyFormData( fontFamily ) { const formData = new FormData(); - const { kebabCase } = unlock( componentsPrivateApis ); const { fontFace, category, ...familyWithValidParameters } = fontFamily; const fontFamilySettings = { From 5f69d15f560614b2f2a8a71cc5f0d4571d0b63fe Mon Sep 17 00:00:00 2001 From: Gerardo Pacheco Date: Mon, 20 May 2024 09:25:03 +0200 Subject: [PATCH 021/468] [Mobile] Upgrade target sdk version to Android API 34 (#61727) * Update Gutenberg targetSdkVersion to 34 * Bump third-party libraries publisher version to v6 * Update forked third-party libraries with snapshot version * Restore react-native-video and react-native-slider implementation references to the package.json * Update react-native-slider and react-native-video * Update changelog Co-authored-by: geriux Co-authored-by: twstokes Co-authored-by: fluiddot --- package-lock.json | 28 +++++++++---------- .../react-native-aztec/android/build.gradle | 2 +- .../android/react-native-bridge/build.gradle | 4 +-- packages/react-native-editor/CHANGELOG.md | 1 + .../android/app/build.gradle | 2 +- packages/react-native-editor/package.json | 4 +-- 6 files changed, 21 insertions(+), 20 deletions(-) diff --git a/package-lock.json b/package-lock.json index bba5bb915c4abb..35ea50b60b7ada 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10715,9 +10715,9 @@ } }, "node_modules/@react-native-community/slider": { - "version": "3.0.2-wp-4", - "resolved": "https://raw.githubusercontent.com/wordpress-mobile/react-native-slider/v3.0.2-wp-4/react-native-community-slider-3.0.2-wp-4.tgz", - "integrity": "sha512-BrfAehlhFSUN0kzIhpeF8X4BvCJp6Rtxqfr+1Kpfn2B4kCuRmJoaqIDc+igPCl8ya99vzV1Uifw30P16NTMhuQ==", + "version": "3.0.2-wp-5", + "resolved": "https://raw.githubusercontent.com/wordpress-mobile/react-native-slider/v3.0.2-wp-5/react-native-community-slider-3.0.2-wp-5.tgz", + "integrity": "sha512-19yl9Em1mFKFLB9o3IO3VpBFw+U7cswjRk6cZ7kkhDCpDD3u0cpzkaIZK6NuFIPCLqOwU0MPtP+gonanN1JQKw==", "workspaces": [ "src", "example" @@ -44164,9 +44164,9 @@ } }, "node_modules/react-native-video": { - "version": "5.2.0-wp-6", - "resolved": "https://raw.githubusercontent.com/wordpress-mobile/react-native-video/5.2.0-wp-6/react-native-video-5.2.0-wp-6.tgz", - "integrity": "sha512-3MZ9UsoagCWc5qiQzd2tG5qQbLT+PF8BXjoahEIptQ5mNPX9P4OkJOE3cyoGEDhby6ZYwWvUQNnwhhObhlP0DQ==", + "version": "5.2.0-wp-7", + "resolved": "https://raw.githubusercontent.com/wordpress-mobile/react-native-video/5.2.0-wp-7/react-native-video-5.2.0-wp-7.tgz", + "integrity": "sha512-DTwhGwqkc3IyDWsyJe8d+cEwaw7aHBgT7Q9+X+N3zDIC7qP3bZnXuTUeag8oz9XhfxRDRQJmF+t5is3qVNIuUA==", "dependencies": { "deprecated-react-native-prop-types": "^2.2.0", "prop-types": "^15.7.2" @@ -55070,7 +55070,7 @@ "@babel/runtime": "^7.20.0", "@react-native-clipboard/clipboard": "1.11.2", "@react-native-community/blur": "4.2.0", - "@react-native-community/slider": "https://raw.githubusercontent.com/wordpress-mobile/react-native-slider/v3.0.2-wp-4/react-native-community-slider-3.0.2-wp-4.tgz", + "@react-native-community/slider": "https://raw.githubusercontent.com/wordpress-mobile/react-native-slider/v3.0.2-wp-5/react-native-community-slider-3.0.2-wp-5.tgz", "@react-native-masked-view/masked-view": "0.3.0", "@react-navigation/core": "5.12.0", "@react-navigation/native": "6.0.14", @@ -55107,7 +55107,7 @@ "react-native-screens": "3.29.0", "react-native-svg": "14.0.0", "react-native-url-polyfill": "1.3.0", - "react-native-video": "https://raw.githubusercontent.com/wordpress-mobile/react-native-video/5.2.0-wp-6/react-native-video-5.2.0-wp-6.tgz", + "react-native-video": "https://raw.githubusercontent.com/wordpress-mobile/react-native-video/5.2.0-wp-7/react-native-video-5.2.0-wp-7.tgz", "react-native-webview": "13.6.1" }, "engines": { @@ -63056,8 +63056,8 @@ } }, "@react-native-community/slider": { - "version": "https://raw.githubusercontent.com/wordpress-mobile/react-native-slider/v3.0.2-wp-4/react-native-community-slider-3.0.2-wp-4.tgz", - "integrity": "sha512-BrfAehlhFSUN0kzIhpeF8X4BvCJp6Rtxqfr+1Kpfn2B4kCuRmJoaqIDc+igPCl8ya99vzV1Uifw30P16NTMhuQ==" + "version": "https://raw.githubusercontent.com/wordpress-mobile/react-native-slider/v3.0.2-wp-5/react-native-community-slider-3.0.2-wp-5.tgz", + "integrity": "sha512-19yl9Em1mFKFLB9o3IO3VpBFw+U7cswjRk6cZ7kkhDCpDD3u0cpzkaIZK6NuFIPCLqOwU0MPtP+gonanN1JQKw==" }, "@react-native-masked-view/masked-view": { "version": "0.3.0", @@ -69780,7 +69780,7 @@ "@babel/runtime": "^7.20.0", "@react-native-clipboard/clipboard": "1.11.2", "@react-native-community/blur": "4.2.0", - "@react-native-community/slider": "https://raw.githubusercontent.com/wordpress-mobile/react-native-slider/v3.0.2-wp-4/react-native-community-slider-3.0.2-wp-4.tgz", + "@react-native-community/slider": "https://raw.githubusercontent.com/wordpress-mobile/react-native-slider/v3.0.2-wp-5/react-native-community-slider-3.0.2-wp-5.tgz", "@react-native-masked-view/masked-view": "0.3.0", "@react-navigation/core": "5.12.0", "@react-navigation/native": "6.0.14", @@ -69817,7 +69817,7 @@ "react-native-screens": "3.29.0", "react-native-svg": "14.0.0", "react-native-url-polyfill": "1.3.0", - "react-native-video": "https://raw.githubusercontent.com/wordpress-mobile/react-native-video/5.2.0-wp-6/react-native-video-5.2.0-wp-6.tgz", + "react-native-video": "https://raw.githubusercontent.com/wordpress-mobile/react-native-video/5.2.0-wp-7/react-native-video-5.2.0-wp-7.tgz", "react-native-webview": "13.6.1" }, "dependencies": { @@ -90129,8 +90129,8 @@ } }, "react-native-video": { - "version": "https://raw.githubusercontent.com/wordpress-mobile/react-native-video/5.2.0-wp-6/react-native-video-5.2.0-wp-6.tgz", - "integrity": "sha512-3MZ9UsoagCWc5qiQzd2tG5qQbLT+PF8BXjoahEIptQ5mNPX9P4OkJOE3cyoGEDhby6ZYwWvUQNnwhhObhlP0DQ==", + "version": "https://raw.githubusercontent.com/wordpress-mobile/react-native-video/5.2.0-wp-7/react-native-video-5.2.0-wp-7.tgz", + "integrity": "sha512-DTwhGwqkc3IyDWsyJe8d+cEwaw7aHBgT7Q9+X+N3zDIC7qP3bZnXuTUeag8oz9XhfxRDRQJmF+t5is3qVNIuUA==", "requires": { "deprecated-react-native-prop-types": "^2.2.0", "prop-types": "^15.7.2" diff --git a/packages/react-native-aztec/android/build.gradle b/packages/react-native-aztec/android/build.gradle index 0c93a9c858e928..45312e89f0b9d9 100644 --- a/packages/react-native-aztec/android/build.gradle +++ b/packages/react-native-aztec/android/build.gradle @@ -53,7 +53,7 @@ android { defaultConfig { minSdkVersion 24 - targetSdkVersion 33 + targetSdkVersion 34 } compileOptions { diff --git a/packages/react-native-bridge/android/react-native-bridge/build.gradle b/packages/react-native-bridge/android/react-native-bridge/build.gradle index 8f3fcd35a95ab6..d8249b9d1b640f 100644 --- a/packages/react-native-bridge/android/react-native-bridge/build.gradle +++ b/packages/react-native-bridge/android/react-native-bridge/build.gradle @@ -39,7 +39,7 @@ android { defaultConfig { minSdkVersion 24 - targetSdkVersion 33 + targetSdkVersion 34 buildConfigField "boolean", "SHOULD_ATTACH_JS_BUNDLE", willPublishReactNativeBridgeBinary.toString() } @@ -93,7 +93,7 @@ dependencies { // Published by `wordpress-mobile/react-native-libraries-publisher` // See the documentation for this value in `build.gradle.kts` of `wordpress-mobile/react-native-libraries-publisher` - def reactNativeLibrariesPublisherVersion = "v5" + def reactNativeLibrariesPublisherVersion = "v6" def reactNativeLibrariesGroupId = "org.wordpress.react-native-libraries.$reactNativeLibrariesPublisherVersion" implementation "$reactNativeLibrariesGroupId:react-native-get-random-values:${extractPackageVersion(packageJson, 'react-native-get-random-values', 'dependencies')}" implementation "$reactNativeLibrariesGroupId:react-native-safe-area-context:${extractPackageVersion(packageJson, 'react-native-safe-area-context', 'dependencies')}" diff --git a/packages/react-native-editor/CHANGELOG.md b/packages/react-native-editor/CHANGELOG.md index c7ae81c1bdbc76..7e3fc89ddbd6f3 100644 --- a/packages/react-native-editor/CHANGELOG.md +++ b/packages/react-native-editor/CHANGELOG.md @@ -11,6 +11,7 @@ For each user feature we should also add a importance categorization label to i ## Unreleased - [internal] Remove circular dependencies within the components package [#61102] +- [internal] Upgrade target sdk version to Android API 34 [#61727] ## 1.118.0 - [*] Fix a crash when pasting file images and special comment markup [#60476] diff --git a/packages/react-native-editor/android/app/build.gradle b/packages/react-native-editor/android/app/build.gradle index 6f2ed72318b13e..09da2a438eae1d 100644 --- a/packages/react-native-editor/android/app/build.gradle +++ b/packages/react-native-editor/android/app/build.gradle @@ -105,7 +105,7 @@ dependencies { // Published by `wordpress-mobile/react-native-libraries-publisher` // See the documentation for this value in `build.gradle.kts` of `wordpress-mobile/react-native-libraries-publisher` - def reactNativeLibrariesPublisherVersion = "v5" + def reactNativeLibrariesPublisherVersion = "v6" def reactNativeLibrariesGroupId = "org.wordpress.react-native-libraries.$reactNativeLibrariesPublisherVersion" implementation "$reactNativeLibrariesGroupId:react-native-get-random-values:${extractPackageVersion(packageJson, 'react-native-get-random-values', 'dependencies')}" implementation "$reactNativeLibrariesGroupId:react-native-safe-area-context:${extractPackageVersion(packageJson, 'react-native-safe-area-context', 'dependencies')}" diff --git a/packages/react-native-editor/package.json b/packages/react-native-editor/package.json index a682a4ae451866..16988d1c5cbcd3 100644 --- a/packages/react-native-editor/package.json +++ b/packages/react-native-editor/package.json @@ -32,7 +32,7 @@ "@babel/runtime": "^7.20.0", "@react-native-clipboard/clipboard": "1.11.2", "@react-native-community/blur": "4.2.0", - "@react-native-community/slider": "https://raw.githubusercontent.com/wordpress-mobile/react-native-slider/v3.0.2-wp-4/react-native-community-slider-3.0.2-wp-4.tgz", + "@react-native-community/slider": "https://raw.githubusercontent.com/wordpress-mobile/react-native-slider/v3.0.2-wp-5/react-native-community-slider-3.0.2-wp-5.tgz", "@react-native-masked-view/masked-view": "0.3.0", "@react-navigation/core": "5.12.0", "@react-navigation/native": "6.0.14", @@ -69,7 +69,7 @@ "react-native-screens": "3.29.0", "react-native-svg": "14.0.0", "react-native-url-polyfill": "1.3.0", - "react-native-video": "https://raw.githubusercontent.com/wordpress-mobile/react-native-video/5.2.0-wp-6/react-native-video-5.2.0-wp-6.tgz", + "react-native-video": "https://raw.githubusercontent.com/wordpress-mobile/react-native-video/5.2.0-wp-7/react-native-video-5.2.0-wp-7.tgz", "react-native-webview": "13.6.1" }, "publishConfig": { From a4f08b765698e3b2855ca94ad7422dbe0d49ffd2 Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Mon, 20 May 2024 17:34:54 +0900 Subject: [PATCH 022/468] Fix: Enable Text Align UI to be controlled correctly with theme.json (#61182) * Fix: Text Align block support inheritance * Use `useBlockSettings` instead of creating a new hook * Simplify `getValidTextAlignments` function Co-authored-by: t-hamano Co-authored-by: aaronrobertshaw --- lib/theme.json | 2 +- packages/block-editor/src/hooks/text-align.js | 28 +++++++++++-------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/lib/theme.json b/lib/theme.json index 49aa2abb08005b..7cd6129923df2a 100644 --- a/lib/theme.json +++ b/lib/theme.json @@ -304,7 +304,7 @@ "fontWeight": true, "letterSpacing": true, "lineHeight": false, - "textAlign": false, + "textAlign": true, "textColumns": false, "textDecoration": true, "textTransform": true, diff --git a/packages/block-editor/src/hooks/text-align.js b/packages/block-editor/src/hooks/text-align.js index 7735a65ecc5764..af20404d79eccf 100644 --- a/packages/block-editor/src/hooks/text-align.js +++ b/packages/block-editor/src/hooks/text-align.js @@ -15,7 +15,11 @@ import { alignLeft, alignRight, alignCenter } from '@wordpress/icons'; */ import { AlignmentControl, BlockControls } from '../components'; import { useBlockEditingMode } from '../components/block-editing-mode'; -import { cleanEmptyObject, shouldSkipSerialization } from './utils'; +import { + cleanEmptyObject, + shouldSkipSerialization, + useBlockSettings, +} from './utils'; import { TYPOGRAPHY_SUPPORT_KEY } from './typography'; export const TEXT_ALIGN_SUPPORT_KEY = 'typography.textAlign'; @@ -39,6 +43,7 @@ const TEXT_ALIGNMENT_OPTIONS = [ ]; const VALID_TEXT_ALIGNMENTS = [ 'left', 'center', 'right' ]; +const NO_TEXT_ALIGNMENTS = []; /** * Returns the valid text alignments. @@ -50,19 +55,13 @@ const VALID_TEXT_ALIGNMENTS = [ 'left', 'center', 'right' ]; * @return {string[]} Valid text alignments. */ export function getValidTextAlignments( blockTextAlign ) { - let validTextAlignments; if ( Array.isArray( blockTextAlign ) ) { - validTextAlignments = VALID_TEXT_ALIGNMENTS.filter( ( textAlign ) => + return VALID_TEXT_ALIGNMENTS.filter( ( textAlign ) => blockTextAlign.includes( textAlign ) ); - } else if ( blockTextAlign === true ) { - // `true` includes all alignments... - validTextAlignments = [ ...VALID_TEXT_ALIGNMENTS ]; - } else { - validTextAlignments = []; } - return validTextAlignments; + return blockTextAlign === true ? VALID_TEXT_ALIGNMENTS : NO_TEXT_ALIGNMENTS; } function BlockEditTextAlignmentToolbarControlsPure( { @@ -70,11 +69,18 @@ function BlockEditTextAlignmentToolbarControlsPure( { name: blockName, setAttributes, } ) { + const settings = useBlockSettings( blockName ); + const hasTextAlignControl = settings?.typography?.textAlign; + const blockEditingMode = useBlockEditingMode(); + + if ( ! hasTextAlignControl || blockEditingMode !== 'default' ) { + return null; + } + const validTextAlignments = getValidTextAlignments( getBlockSupport( blockName, TEXT_ALIGN_SUPPORT_KEY ) ); - const blockEditingMode = useBlockEditingMode(); - if ( ! validTextAlignments.length || blockEditingMode !== 'default' ) { + if ( ! validTextAlignments.length ) { return null; } From 850b22e05fa16654015f5e2ba16312db1a40811f Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Mon, 20 May 2024 17:37:11 +0900 Subject: [PATCH 023/468] Block Editor: Remove some utility functions (#61784) * Block Editor: Remove some utility functions * Fix e2e test Co-authored-by: t-hamano Co-authored-by: youknowriad --- .../components/global-styles/border-panel.js | 9 ++-- .../global-styles/typography-panel.js | 15 ++++-- .../src/store/get-block-settings.js | 47 +------------------ 3 files changed, 17 insertions(+), 54 deletions(-) diff --git a/packages/block-editor/src/components/global-styles/border-panel.js b/packages/block-editor/src/components/global-styles/border-panel.js index e02616082cf6a8..3816bec9159c37 100644 --- a/packages/block-editor/src/components/global-styles/border-panel.js +++ b/packages/block-editor/src/components/global-styles/border-panel.js @@ -19,7 +19,6 @@ import { __ } from '@wordpress/i18n'; import BorderRadiusControl from '../border-radius-control'; import { useColorsPerOrigin } from './hooks'; import { getValueFromVariable, TOOLSPANEL_DROPDOWNMENU_PROPS } from './utils'; -import { overrideOrigins } from '../../store/get-block-settings'; import { setImmutably } from '../../utils/object'; import { useBorderPanelLabel } from '../../hooks/border'; import { ShadowPopover, useShadowPresets } from './shadow-panel-components'; @@ -161,9 +160,13 @@ export default function BorderPanel( { // Shadow const shadow = decodeValue( inheritedValue?.shadow ); const shadowPresets = settings?.shadow?.presets ?? {}; - const overriddenShadowPresets = overrideOrigins( shadowPresets ) ?? []; + const mergedShadowPresets = + shadowPresets.custom ?? + shadowPresets.theme ?? + shadowPresets.default ?? + []; const setShadow = ( newValue ) => { - const slug = overriddenShadowPresets?.find( + const slug = mergedShadowPresets?.find( ( { shadow: shadowName } ) => shadowName === newValue )?.slug; diff --git a/packages/block-editor/src/components/global-styles/typography-panel.js b/packages/block-editor/src/components/global-styles/typography-panel.js index 76836c775bb807..64a7be0443e1e9 100644 --- a/packages/block-editor/src/components/global-styles/typography-panel.js +++ b/packages/block-editor/src/components/global-styles/typography-panel.js @@ -8,12 +8,11 @@ import { __experimentalToolsPanelItem as ToolsPanelItem, } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; -import { useCallback } from '@wordpress/element'; +import { useCallback, useMemo } from '@wordpress/element'; /** * Internal dependencies */ -import { mergeOrigins, hasOriginValue } from '../../store/get-block-settings'; import FontFamilyControl from '../font-family'; import FontAppearanceControl from '../font-appearance-control'; import LineHeightControl from '../line-height-control'; @@ -62,7 +61,9 @@ function useHasFontSizeControl( settings ) { } function useHasFontFamilyControl( settings ) { - return hasOriginValue( settings?.typography?.fontFamilies ); + return [ 'default', 'theme', 'custom' ].some( + ( key ) => settings?.typography?.fontFamilies?.[ key ]?.length + ); } function useHasLineHeightControl( settings ) { @@ -170,8 +171,12 @@ export default function TypographyPanel( { // Font Family const hasFontFamilyEnabled = useHasFontFamilyControl( settings ); - const fontFamilies = settings?.typography?.fontFamilies ?? {}; - const mergedFontFamilies = fontFamilies ? mergeOrigins( fontFamilies ) : []; + const fontFamilies = settings?.typography?.fontFamilies; + const mergedFontFamilies = useMemo( () => { + return [ 'default', 'theme', 'custom' ].flatMap( + ( key ) => fontFamilies?.[ key ] ?? [] + ); + }, [ fontFamilies ] ); const fontFamily = decodeValue( inheritedValue?.typography?.fontFamily ); const setFontFamily = ( newValue ) => { const slug = mergedFontFamilies?.find( diff --git a/packages/block-editor/src/store/get-block-settings.js b/packages/block-editor/src/store/get-block-settings.js index 1bffebf931e818..9ea8fab15f597a 100644 --- a/packages/block-editor/src/store/get-block-settings.js +++ b/packages/block-editor/src/store/get-block-settings.js @@ -91,51 +91,6 @@ const removeCustomPrefixes = ( path ) => { return prefixedFlags[ path ] || path; }; -/** - * For settings like `color.palette`, which have a value that is an object - * with `default`, `theme`, `custom`, with field values that are arrays of - * items, merge these three arrays into one and return it. The calculation - * is memoized so that identical input values produce identical output. - * @param {Object} value Object to merge - * @return {Array} Array of merged items - */ -export function mergeOrigins( value ) { - let result = mergeCache.get( value ); - if ( ! result ) { - result = [ 'default', 'theme', 'custom' ].flatMap( - ( key ) => value[ key ] ?? [] - ); - mergeCache.set( value, result ); - } - return result; -} -const mergeCache = new WeakMap(); - -/** - * For settings like `color.palette`, which have a value that is an object - * with `default`, `theme`, `custom`, with field values that are arrays of - * items, returns the one with the highest priority among these three arrays. - * @param {Object} value Object to extract from - * @return {Array} Array of items extracted from the three origins - */ -export function overrideOrigins( value ) { - return value.custom ?? value.theme ?? value.default; -} - -/** - * For settings like `color.palette`, which have a value that is an object - * with `default`, `theme`, `custom`, with field values that are arrays of - * items, see if any of the three origins have values. - * - * @param {Object} value Object to check - * @return {boolean} Whether the object has values in any of the three origins - */ -export function hasOriginValue( value ) { - return [ 'default', 'theme', 'custom' ].some( - ( key ) => value?.[ key ]?.length - ); -} - export function getBlockSettings( state, clientId, ...paths ) { const blockName = getBlockName( state, clientId ); const candidates = []; @@ -215,7 +170,7 @@ export function getBlockSettings( state, clientId, ...paths ) { // Return if the setting was found in either the block instance or the store. if ( result !== undefined ) { if ( PATHS_WITH_OVERRIDE[ normalizedPath ] ) { - return overrideOrigins( result ); + return result.custom ?? result.theme ?? result.default; } return result; } From 9190ee59a10f9c60a48a0982ad8a2ac99baf7ae8 Mon Sep 17 00:00:00 2001 From: Nik Tsekouras Date: Mon, 20 May 2024 14:27:23 +0300 Subject: [PATCH 024/468] Add home template details to inspector controls (#61762) Co-authored-by: ntsekouras Co-authored-by: youknowriad Co-authored-by: jameskoster --- .../editor/src/components/blog-title/index.js | 124 ++++++++++++++++ .../src/components/blog-title/style.scss | 4 + .../src/components/posts-per-page/index.js | 101 +++++++++++++ .../src/components/posts-per-page/style.scss | 4 + .../src/components/sidebar/post-summary.js | 8 +- .../src/components/site-discussion/index.js | 138 ++++++++++++++++++ .../src/components/site-discussion/style.scss | 19 +++ .../src/components/template-areas/index.js | 21 ++- packages/editor/src/style.scss | 3 + 9 files changed, 415 insertions(+), 7 deletions(-) create mode 100644 packages/editor/src/components/blog-title/index.js create mode 100644 packages/editor/src/components/blog-title/style.scss create mode 100644 packages/editor/src/components/posts-per-page/index.js create mode 100644 packages/editor/src/components/posts-per-page/style.scss create mode 100644 packages/editor/src/components/site-discussion/index.js create mode 100644 packages/editor/src/components/site-discussion/style.scss diff --git a/packages/editor/src/components/blog-title/index.js b/packages/editor/src/components/blog-title/index.js new file mode 100644 index 00000000000000..a82498b5220f4d --- /dev/null +++ b/packages/editor/src/components/blog-title/index.js @@ -0,0 +1,124 @@ +/** + * WordPress dependencies + */ +import { __, sprintf } from '@wordpress/i18n'; +import { debounce } from '@wordpress/compose'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { store as coreStore } from '@wordpress/core-data'; +import { decodeEntities } from '@wordpress/html-entities'; +import { + Button, + Dropdown, + __experimentalInputControl as InputControl, +} from '@wordpress/components'; +import { useState, useMemo } from '@wordpress/element'; +import { __experimentalInspectorPopoverHeader as InspectorPopoverHeader } from '@wordpress/block-editor'; + +/** + * Internal dependencies + */ +import { TEMPLATE_POST_TYPE } from '../../store/constants'; +import PostPanelRow from '../post-panel-row'; +import { store as editorStore } from '../../store'; + +const EMPTY_OBJECT = {}; + +export default function BlogTitle() { + const { editEntityRecord } = useDispatch( coreStore ); + const { postsPageTitle, postsPageId, isTemplate, postSlug } = useSelect( + ( select ) => { + const { getEntityRecord, getEditedEntityRecord } = + select( coreStore ); + const siteSettings = getEntityRecord( 'root', 'site' ); + const _postsPageRecord = siteSettings?.page_for_posts + ? getEditedEntityRecord( + 'postType', + 'page', + siteSettings?.page_for_posts + ) + : EMPTY_OBJECT; + const { getEditedPostAttribute, getCurrentPostType } = + select( editorStore ); + return { + postsPageId: _postsPageRecord?.id, + postsPageTitle: _postsPageRecord?.title, + isTemplate: getCurrentPostType() === TEMPLATE_POST_TYPE, + postSlug: getEditedPostAttribute( 'slug' ), + }; + }, + [] + ); + // Use internal state instead of a ref to make sure that the component + // re-renders when the popover's anchor updates. + const [ popoverAnchor, setPopoverAnchor ] = useState( null ); + // Memoize popoverProps to avoid returning a new object every time. + const popoverProps = useMemo( + () => ( { + // Anchor the popover to the middle of the entire row so that it doesn't + // move around when the label changes. + anchor: popoverAnchor, + placement: 'left-start', + offset: 36, + shift: true, + } ), + [ popoverAnchor ] + ); + + if ( + ! isTemplate || + ! [ 'home', 'index' ].includes( postSlug ) || + ! postsPageId + ) { + return null; + } + + const setPostsPageTitle = ( newValue ) => { + editEntityRecord( 'postType', 'page', postsPageId, { + title: newValue, + } ); + }; + const decodedTitle = decodeEntities( postsPageTitle ); + return ( + + ( + + ) } + renderContent={ ( { onClose } ) => ( + <> + + + + ) } + /> + + ); +} diff --git a/packages/editor/src/components/blog-title/style.scss b/packages/editor/src/components/blog-title/style.scss new file mode 100644 index 00000000000000..164e82979e0b30 --- /dev/null +++ b/packages/editor/src/components/blog-title/style.scss @@ -0,0 +1,4 @@ +.editor-blog-title-dropdown__content .components-popover__content { + min-width: 320px; + padding: $grid-unit-20; +} diff --git a/packages/editor/src/components/posts-per-page/index.js b/packages/editor/src/components/posts-per-page/index.js new file mode 100644 index 00000000000000..625aeba7bd824f --- /dev/null +++ b/packages/editor/src/components/posts-per-page/index.js @@ -0,0 +1,101 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { store as coreStore } from '@wordpress/core-data'; +import { + Button, + Dropdown, + __experimentalNumberControl as NumberControl, +} from '@wordpress/components'; +import { useState, useMemo } from '@wordpress/element'; +import { __experimentalInspectorPopoverHeader as InspectorPopoverHeader } from '@wordpress/block-editor'; + +/** + * Internal dependencies + */ +import { TEMPLATE_POST_TYPE } from '../../store/constants'; +import { store as editorStore } from '../../store'; +import PostPanelRow from '../post-panel-row'; + +export default function PostsPerPage() { + const { editEntityRecord } = useDispatch( coreStore ); + const { postsPerPage, isTemplate, postSlug } = useSelect( ( select ) => { + const { getEditedPostAttribute, getCurrentPostType } = + select( editorStore ); + const { getEditedEntityRecord } = select( coreStore ); + const siteSettings = getEditedEntityRecord( 'root', 'site' ); + return { + isTemplate: getCurrentPostType() === TEMPLATE_POST_TYPE, + postSlug: getEditedPostAttribute( 'slug' ), + postsPerPage: siteSettings?.posts_per_page || 1, + }; + }, [] ); + // Use internal state instead of a ref to make sure that the component + // re-renders when the popover's anchor updates. + const [ popoverAnchor, setPopoverAnchor ] = useState( null ); + // Memoize popoverProps to avoid returning a new object every time. + const popoverProps = useMemo( + () => ( { + // Anchor the popover to the middle of the entire row so that it doesn't + // move around when the label changes. + anchor: popoverAnchor, + placement: 'left-start', + offset: 36, + shift: true, + } ), + [ popoverAnchor ] + ); + + if ( ! isTemplate || ! [ 'home', 'index' ].includes( postSlug ) ) { + return null; + } + const setPostsPerPage = ( newValue ) => { + editEntityRecord( 'root', 'site', undefined, { + posts_per_page: newValue, + } ); + }; + return ( + + ( + + ) } + renderContent={ ( { onClose } ) => ( + <> + + + + ) } + /> + + ); +} diff --git a/packages/editor/src/components/posts-per-page/style.scss b/packages/editor/src/components/posts-per-page/style.scss new file mode 100644 index 00000000000000..e77e9b33206320 --- /dev/null +++ b/packages/editor/src/components/posts-per-page/style.scss @@ -0,0 +1,4 @@ +.editor-posts-per-page-dropdown__content .components-popover__content { + min-width: 320px; + padding: $grid-unit-20; +} diff --git a/packages/editor/src/components/sidebar/post-summary.js b/packages/editor/src/components/sidebar/post-summary.js index 839d60c3b188a2..8f1fb23092a5bf 100644 --- a/packages/editor/src/components/sidebar/post-summary.js +++ b/packages/editor/src/components/sidebar/post-summary.js @@ -25,6 +25,9 @@ import PostSyncStatus from '../post-sync-status'; import PostTemplatePanel from '../post-template/panel'; import PostTrashPanel from '../post-trash/panel'; import PostURLPanel from '../post-url/panel'; +import BlogTitle from '../blog-title'; +import PostsPerPage from '../posts-per-page'; +import SiteDiscussion from '../site-discussion'; import { store as editorStore } from '../../store'; import { NAVIGATION_POST_TYPE, @@ -84,10 +87,13 @@ export default function PostSummary( { onActionPerformed } ) { + + + - { isTemplate && } + { fills } { ! isPattern && ! isTemplate && diff --git a/packages/editor/src/components/site-discussion/index.js b/packages/editor/src/components/site-discussion/index.js new file mode 100644 index 00000000000000..e4bd60db3f8a7c --- /dev/null +++ b/packages/editor/src/components/site-discussion/index.js @@ -0,0 +1,138 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { store as coreStore } from '@wordpress/core-data'; +import { + Button, + Dropdown, + RadioControl, + __experimentalVStack as VStack, + __experimentalText as Text, +} from '@wordpress/components'; +import { useState, useMemo } from '@wordpress/element'; +import { __experimentalInspectorPopoverHeader as InspectorPopoverHeader } from '@wordpress/block-editor'; + +/** + * Internal dependencies + */ +import { TEMPLATE_POST_TYPE } from '../../store/constants'; +import PostPanelRow from '../post-panel-row'; +import { store as editorStore } from '../../store'; + +const COMMENT_OPTIONS = [ + { + label: ( + <> + { __( 'Open' ) } + + { __( 'Visitors can add new comments and replies.' ) } + + + ), + value: 'open', + }, + { + label: ( + <> + { __( 'Closed' ) } + + { __( 'Visitors cannot add new comments or replies.' ) } + + + { __( 'Existing comments remain visible.' ) } + + + ), + value: '', + }, +]; + +export default function SiteDiscussion() { + const { editEntityRecord } = useDispatch( coreStore ); + const { allowCommentsOnNewPosts, isTemplate, postSlug } = useSelect( + ( select ) => { + const { getEditedPostAttribute, getCurrentPostType } = + select( editorStore ); + const { getEditedEntityRecord } = select( coreStore ); + const siteSettings = getEditedEntityRecord( 'root', 'site' ); + return { + isTemplate: getCurrentPostType() === TEMPLATE_POST_TYPE, + postSlug: getEditedPostAttribute( 'slug' ), + allowCommentsOnNewPosts: + siteSettings?.default_comment_status || '', + }; + }, + [] + ); + // Use internal state instead of a ref to make sure that the component + // re-renders when the popover's anchor updates. + const [ popoverAnchor, setPopoverAnchor ] = useState( null ); + // Memoize popoverProps to avoid returning a new object every time. + const popoverProps = useMemo( + () => ( { + // Anchor the popover to the middle of the entire row so that it doesn't + // move around when the label changes. + anchor: popoverAnchor, + placement: 'left-start', + offset: 36, + shift: true, + } ), + [ popoverAnchor ] + ); + + if ( ! isTemplate || ! [ 'home', 'index' ].includes( postSlug ) ) { + return null; + } + const setAllowCommentsOnNewPosts = ( newValue ) => { + editEntityRecord( 'root', 'site', undefined, { + default_comment_status: newValue ? 'open' : null, + } ); + }; + return ( + + ( + + ) } + renderContent={ ( { onClose } ) => ( + <> + + + + { __( + 'Changes will apply to new posts only. Individual posts may override these settings.' + ) } + + + + + ) } + /> + + ); +} diff --git a/packages/editor/src/components/site-discussion/style.scss b/packages/editor/src/components/site-discussion/style.scss new file mode 100644 index 00000000000000..2c54424207ea5c --- /dev/null +++ b/packages/editor/src/components/site-discussion/style.scss @@ -0,0 +1,19 @@ +.editor-site-discussion-dropdown__content .components-popover__content { + min-width: 320px; + padding: $grid-unit-20; +} + +.editor-site-discussion__options { + // TODO: it's not great to override component styles.. This might be resolved + // by the new radio control component. + .components-radio-control__option { + align-items: flex-start; + } + + label { + .components-text { + display: block; + margin-top: $grid-unit-05; + } + } +} diff --git a/packages/editor/src/components/template-areas/index.js b/packages/editor/src/components/template-areas/index.js index 25cb59bf9b2ed5..0d3bbe42d02eed 100644 --- a/packages/editor/src/components/template-areas/index.js +++ b/packages/editor/src/components/template-areas/index.js @@ -15,6 +15,7 @@ import { __ } from '@wordpress/i18n'; */ import { store as editorStore } from '../../store'; import { unlock } from '../../lock-unlock'; +import { TEMPLATE_POST_TYPE } from '../../store/constants'; function TemplateAreaItem( { area, clientId } ) { const { selectBlock, toggleBlockHighlight } = @@ -54,13 +55,21 @@ function TemplateAreaItem( { area, clientId } ) { } export default function TemplateAreas() { - const templateParts = useSelect( - ( select ) => - unlock( select( editorStore ) ).getCurrentTemplateTemplateParts(), - [] - ); + const { isTemplate, templateParts } = useSelect( ( select ) => { + const _isTemplate = + select( editorStore ).getCurrentPostType() === TEMPLATE_POST_TYPE; + + return { + isTemplate: _isTemplate, + templateParts: + _isTemplate && + unlock( + select( editorStore ) + ).getCurrentTemplateTemplateParts(), + }; + }, [] ); - if ( ! templateParts.length ) { + if ( ! isTemplate || ! templateParts.length ) { return null; } diff --git a/packages/editor/src/style.scss b/packages/editor/src/style.scss index a28a2db3ae4a28..793d83e54c5488 100644 --- a/packages/editor/src/style.scss +++ b/packages/editor/src/style.scss @@ -4,6 +4,7 @@ @import "./components/block-manager/style.scss"; @import "./components/collapsible-block-toolbar/style.scss"; @import "./components/block-settings-menu/style.scss"; +@import "./components/blog-title/style.scss"; @import "./components/document-bar/style.scss"; @import "./components/document-outline/style.scss"; @import "./components/document-tools/style.scss"; @@ -39,11 +40,13 @@ @import "./components/post-title/style.scss"; @import "./components/post-url/style.scss"; @import "./components/post-visibility/style.scss"; +@import "./components/posts-per-page/style.scss"; @import "./components/post-trash/style.scss"; @import "./components/preview-dropdown/style.scss"; @import "./components/save-publish-panels/style.scss"; @import "./components/start-page-options/style.scss"; @import "./components/start-template-options/style.scss"; @import "./components/sidebar/style.scss"; +@import "./components/site-discussion/style.scss"; @import "./components/table-of-contents/style.scss"; @import "./components/template-areas/style.scss"; From da56e2b231e9c7c9f808fd67a323f790fb3af18b Mon Sep 17 00:00:00 2001 From: Michal Date: Mon, 20 May 2024 13:27:29 +0100 Subject: [PATCH 025/468] Interactivity API : Refactor interactivity-router to TS (#61730) * Migrate interactivity-router to TS * Migrate head.js to TypeScript * chore: Update headElements type in fetchHeadAssets function * chore: Update getTagId function in head.ts * Removed jsdocs types --------- Co-authored-by: michalczaplinski Co-authored-by: cbravobernal --- .../src/{head.js => head.ts} | 27 ++-- .../src/{index.js => index.ts} | 119 ++++++++++++------ packages/interactivity/src/store.ts | 2 +- 3 files changed, 97 insertions(+), 51 deletions(-) rename packages/interactivity-router/src/{head.js => head.ts} (71%) rename packages/interactivity-router/src/{index.js => index.ts} (73%) diff --git a/packages/interactivity-router/src/head.js b/packages/interactivity-router/src/head.ts similarity index 71% rename from packages/interactivity-router/src/head.js rename to packages/interactivity-router/src/head.ts index b16b5a0e1db83c..2bde7cea520404 100644 --- a/packages/interactivity-router/src/head.js +++ b/packages/interactivity-router/src/head.ts @@ -2,19 +2,19 @@ * Helper to update only the necessary tags in the head. * * @async - * @param {Array} newHead The head elements of the new page. + * @param newHead The head elements of the new page. */ -export const updateHead = async ( newHead ) => { +export const updateHead = async ( newHead: HTMLHeadElement[] ) => { // Helper to get the tag id store in the cache. - const getTagId = ( tag ) => tag.id || tag.outerHTML; + const getTagId = ( tag: Element ) => tag.id || tag.outerHTML; // Map incoming head tags by their content. - const newHeadMap = new Map(); + const newHeadMap = new Map< string, Element >(); for ( const child of newHead ) { newHeadMap.set( getTagId( child ), child ); } - const toRemove = []; + const toRemove: Element[] = []; // Detect nodes that should be added or removed. for ( const child of document.head.children ) { @@ -41,12 +41,17 @@ export const updateHead = async ( newHead ) => { * Fetches and processes head assets (stylesheets and scripts) from a specified document. * * @async - * @param {Document} doc The document from which to fetch head assets. It should support standard DOM querying methods. - * @param {Map} headElements A map of head elements to modify tracking the URLs of already processed assets to avoid duplicates. + * @param doc The document from which to fetch head assets. It should support standard DOM querying methods. + * @param headElements A map of head elements to modify tracking the URLs of already processed assets to avoid duplicates. + * @param headElements.tag + * @param headElements.text * - * @return {Promise} Returns an array of HTML elements representing the head assets. + * @return Returns an array of HTML elements representing the head assets. */ -export const fetchHeadAssets = async ( doc, headElements ) => { +export const fetchHeadAssets = async ( + doc: Document, + headElements: Map< string, { tag: HTMLElement; text: string } > +): Promise< HTMLElement[] > => { const headTags = []; const assets = [ { @@ -58,7 +63,9 @@ export const fetchHeadAssets = async ( doc, headElements ) => { ]; for ( const asset of assets ) { const { tagName, selector, attribute } = asset; - const tags = doc.querySelectorAll( selector ); + const tags = doc.querySelectorAll< + HTMLScriptElement | HTMLStyleElement + >( selector ); // Use Promise.all to wait for fetch to complete await Promise.all( diff --git a/packages/interactivity-router/src/index.js b/packages/interactivity-router/src/index.ts similarity index 73% rename from packages/interactivity-router/src/index.js rename to packages/interactivity-router/src/index.ts index 7e786700338f50..09f484131b62ee 100644 --- a/packages/interactivity-router/src/index.js +++ b/packages/interactivity-router/src/index.ts @@ -21,23 +21,50 @@ const { 'I acknowledge that using private APIs means my theme or plugin will inevitably break in the next version of WordPress.' ); +interface NavigateOptions { + force?: boolean; + html?: string; + replace?: boolean; + timeout?: number; + loadingAnimation?: boolean; + screenReaderAnnouncement?: boolean; +} + +interface PrefetchOptions { + force?: boolean; + html?: string; +} + +interface VdomParams { + vdom?: typeof initialVdom; +} + +interface Page { + regions: Record< string, any >; + head: HTMLHeadElement[]; + title: string; + initialData: any; +} + +type RegionsToVdom = ( dom: Document, params?: VdomParams ) => Promise< Page >; + // Check if the navigation mode is full page or region based. -const navigationMode = +const navigationMode: 'regionBased' | 'fullPage' = getConfig( 'core/router' ).navigationMode ?? 'regionBased'; // The cache of visited and prefetched pages, stylesheets and scripts. -const pages = new Map(); -const headElements = new Map(); +const pages = new Map< string, Promise< Page | false > >(); +const headElements = new Map< string, { tag: HTMLElement; text: string } >(); // Helper to remove domain and hash from the URL. We are only interesting in // caching the path and the query. -const getPagePath = ( url ) => { - const u = new URL( url, window.location ); +const getPagePath = ( url: string ) => { + const u = new URL( url, window.location.href ); return u.pathname + u.search; }; // Fetch a new page and convert it to a static virtual DOM. -const fetchPage = async ( url, { html } ) => { +const fetchPage = async ( url: string, { html }: { html: string } ) => { try { if ( ! html ) { const res = await window.fetch( url ); @@ -55,9 +82,10 @@ const fetchPage = async ( url, { html } ) => { // Return an object with VDOM trees of those HTML regions marked with a // `router-region` directive. -const regionsToVdom = async ( dom, { vdom } = {} ) => { - const regions = {}; - let head; +const regionsToVdom: RegionsToVdom = async ( dom, { vdom } = {} ) => { + const regions = { body: undefined }; + let head: HTMLElement[]; + // @ts-ignore if ( process.env.IS_GUTENBERG_PLUGIN ) { if ( navigationMode === 'fullPage' ) { head = await fetchHeadAssets( dom, headElements ); @@ -81,8 +109,9 @@ const regionsToVdom = async ( dom, { vdom } = {} ) => { }; // Render all interactive regions contained in the given page. -const renderRegions = ( page ) => { +const renderRegions = ( page: Page ) => { batch( () => { + // @ts-ignore if ( process.env.IS_GUTENBERG_PLUGIN ) { if ( navigationMode === 'fullPage' ) { // Once this code is tested and more mature, the head should be updated for region based navigation as well. @@ -115,10 +144,10 @@ const renderRegions = ( page ) => { * potential feedback indicating that the navigation has finished while the new * page is being loaded. * - * @param {string} href The page href. - * @return {Promise} Promise that never resolves. + * @param href The page href. + * @return Promise that never resolves. */ -const forcePageReload = ( href ) => { +const forcePageReload = ( href: string ) => { window.location.assign( href ); return new Promise( () => {} ); }; @@ -126,7 +155,7 @@ const forcePageReload = ( href ) => { // Listen to the back and forward buttons and restore the page if it's in the // cache. window.addEventListener( 'popstate', async () => { - const pagePath = getPagePath( window.location ); // Remove hash. + const pagePath = getPagePath( window.location.href ); // Remove hash. const page = pages.has( pagePath ) && ( await pages.get( pagePath ) ); if ( page ) { renderRegions( page ); @@ -138,7 +167,9 @@ window.addEventListener( 'popstate', async () => { } ); // Initialize the router and cache the initial page using the initial vDOM. -// Once this code is tested and more mature, the head should be updated for region based navigation as well. +// Once this code is tested and more mature, the head should be updated for +// region based navigation as well. +// @ts-ignore if ( process.env.IS_GUTENBERG_PLUGIN ) { if ( navigationMode === 'fullPage' ) { // Cache the scripts. Has to be called before fetching the assets. @@ -152,12 +183,12 @@ if ( process.env.IS_GUTENBERG_PLUGIN ) { } } pages.set( - getPagePath( window.location ), + getPagePath( window.location.href ), Promise.resolve( regionsToVdom( document, { vdom: initialVdom } ) ) ); // Check if the link is valid for client-side navigation. -const isValidLink = ( ref ) => +const isValidLink = ( ref: HTMLAnchorElement ) => ref && ref instanceof window.HTMLAnchorElement && ref.href && @@ -169,7 +200,7 @@ const isValidLink = ( ref ) => ! new URL( ref.href ).searchParams.has( '_wpnonce' ); // Check if the event is valid for client-side navigation. -const isValidEvent = ( event ) => +const isValidEvent = ( event: MouseEvent ) => event && event.button === 0 && // Left clicks only. ! event.metaKey && // Open in new tab (Mac). @@ -187,7 +218,11 @@ export const { state, actions } = store( 'core/router', { navigation: { hasStarted: false, hasFinished: false, - texts: {}, + texts: { + loading: '', + loaded: '', + }, + message: '', }, }, actions: { @@ -198,18 +233,18 @@ export const { state, actions } = store( 'core/router', { * needed, and updates any interactive regions whose contents have * changed. It also creates a new entry in the browser session history. * - * @param {string} href The page href. - * @param {Object} [options] Options object. - * @param {boolean} [options.force] If true, it forces re-fetching the URL. - * @param {string} [options.html] HTML string to be used instead of fetching the requested URL. - * @param {boolean} [options.replace] If true, it replaces the current entry in the browser session history. - * @param {number} [options.timeout] Time until the navigation is aborted, in milliseconds. Default is 10000. - * @param {boolean} [options.loadingAnimation] Whether an animation should be shown while navigating. Default to `true`. - * @param {boolean} [options.screenReaderAnnouncement] Whether a message for screen readers should be announced while navigating. Default to `true`. + * @param href The page href. + * @param [options] Options object. + * @param [options.force] If true, it forces re-fetching the URL. + * @param [options.html] HTML string to be used instead of fetching the requested URL. + * @param [options.replace] If true, it replaces the current entry in the browser session history. + * @param [options.timeout] Time until the navigation is aborted, in milliseconds. Default is 10000. + * @param [options.loadingAnimation] Whether an animation should be shown while navigating. Default to `true`. + * @param [options.screenReaderAnnouncement] Whether a message for screen readers should be announced while navigating. Default to `true`. * - * @return {Promise} Promise that resolves once the navigation is completed or aborted. + * @return Promise that resolves once the navigation is completed or aborted. */ - *navigate( href, options = {} ) { + *navigate( href: string, options: NavigateOptions = {} ) { const { clientNavigationDisabled } = getConfig(); if ( clientNavigationDisabled ) { yield forcePageReload( href ); @@ -228,7 +263,7 @@ export const { state, actions } = store( 'core/router', { // Create a promise that resolves when the specified timeout ends. // The timeout value is 10 seconds by default. - const timeoutPromise = new Promise( ( resolve ) => + const timeoutPromise = new Promise< void >( ( resolve ) => setTimeout( resolve, timeout ) ); @@ -294,7 +329,7 @@ export const { state, actions } = store( 'core/router', { } // Scroll to the anchor if exits in the link. - const { hash } = new URL( href, window.location ); + const { hash } = new URL( href, window.location.href ); if ( hash ) { document.querySelector( hash )?.scrollIntoView(); } @@ -309,12 +344,12 @@ export const { state, actions } = store( 'core/router', { * The function normalizes the URL and stores internally the fetch * promise, to avoid triggering a second fetch for an ongoing request. * - * @param {string} url The page URL. - * @param {Object} [options] Options object. - * @param {boolean} [options.force] Force fetching the URL again. - * @param {string} [options.html] HTML string to be used instead of fetching the requested URL. + * @param url The page URL. + * @param [options] Options object. + * @param [options.force] Force fetching the URL again. + * @param [options.html] HTML string to be used instead of fetching the requested URL. */ - prefetch( url, options = {} ) { + prefetch( url: string, options: PrefetchOptions = {} ) { const { clientNavigationDisabled } = getConfig(); if ( clientNavigationDisabled ) { return; @@ -322,20 +357,24 @@ export const { state, actions } = store( 'core/router', { const pagePath = getPagePath( url ); if ( options.force || ! pages.has( pagePath ) ) { - pages.set( pagePath, fetchPage( pagePath, options ) ); + pages.set( + pagePath, + fetchPage( pagePath, { html: options.html } ) + ); } }, }, } ); // Add click and prefetch to all links. +// @ts-ignore if ( process.env.IS_GUTENBERG_PLUGIN ) { if ( navigationMode === 'fullPage' ) { // Navigate on click. document.addEventListener( 'click', function ( event ) { - const ref = event.target.closest( 'a' ); + const ref = ( event.target as Element ).closest( 'a' ); if ( isValidLink( ref ) && isValidEvent( event ) ) { event.preventDefault(); actions.navigate( ref.href ); @@ -347,8 +386,8 @@ if ( process.env.IS_GUTENBERG_PLUGIN ) { document.addEventListener( 'mouseenter', function ( event ) { - if ( event.target?.nodeName === 'A' ) { - const ref = event.target.closest( 'a' ); + if ( ( event.target as Element )?.nodeName === 'A' ) { + const ref = ( event.target as Element ).closest( 'a' ); if ( isValidLink( ref ) && isValidEvent( event ) ) { actions.prefetch( ref.href ); } diff --git a/packages/interactivity/src/store.ts b/packages/interactivity/src/store.ts index 87c9333c744296..d173f2cd842dc1 100644 --- a/packages/interactivity/src/store.ts +++ b/packages/interactivity/src/store.ts @@ -177,7 +177,7 @@ const handlers = { * @param namespace Store's namespace from which to retrieve the config. * @return Defined config for the given namespace. */ -export const getConfig = ( namespace: string ) => +export const getConfig = ( namespace?: string ) => storeConfigs.get( namespace || getNamespace() ) || {}; interface StoreOptions { From 7dda999e0f3a2f038c90145e1a51f25905f1aaa9 Mon Sep 17 00:00:00 2001 From: James Koster Date: Mon, 20 May 2024 13:53:54 +0100 Subject: [PATCH 026/468] Data views: Fix pagination position on pages with short lists (#61712) Co-authored-by: jameskoster Co-authored-by: t-hamano --- packages/dataviews/src/style.scss | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/dataviews/src/style.scss b/packages/dataviews/src/style.scss index aa46489db63924..e2c4b52977a8e3 100644 --- a/packages/dataviews/src/style.scss +++ b/packages/dataviews/src/style.scss @@ -3,6 +3,8 @@ overflow: auto; box-sizing: border-box; scroll-padding-bottom: $grid-unit-80; + display: flex; + flex-direction: column; } .dataviews-filters__view-actions { @@ -68,6 +70,7 @@ border-collapse: collapse; position: relative; color: $gray-700; + margin-bottom: auto; a { text-decoration: none; @@ -278,10 +281,10 @@ } .dataviews-view-grid { - margin-bottom: $grid-unit-30; + margin-bottom: auto; grid-template-columns: repeat(2, minmax(0, 1fr)) !important; grid-template-rows: max-content; - padding: 0 $grid-unit-40; + padding: 0 $grid-unit-40 $grid-unit-30; @include break-xlarge() { grid-template-columns: repeat(3, minmax(0, 1fr)) !important; // Todo: eliminate !important dependency @@ -396,7 +399,7 @@ } .dataviews-view-list { - margin: 0; + margin: 0 0 auto; li { margin: 0; From 5bd79bc3e1d28a79d4a3c68e6d1bdbce5da21145 Mon Sep 17 00:00:00 2001 From: James Koster Date: Mon, 20 May 2024 13:59:44 +0100 Subject: [PATCH 027/468] Visually hide 'Actions' column header (#61710) Co-authored-by: jameskoster Co-authored-by: jasmussen Co-authored-by: t-hamano --- packages/dataviews/src/style.scss | 7 +------ packages/dataviews/src/view-table.tsx | 5 +++-- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/dataviews/src/style.scss b/packages/dataviews/src/style.scss index e2c4b52977a8e3..ae886842b2e9fd 100644 --- a/packages/dataviews/src/style.scss +++ b/packages/dataviews/src/style.scss @@ -107,8 +107,7 @@ th:first-child { padding-left: $grid-unit-40; - .dataviews-view-table-header-button, - .dataviews-view-table-header { + .dataviews-view-table-header-button { margin-left: - #{$grid-unit-10}; } } @@ -224,10 +223,6 @@ } } - .dataviews-view-table-header { - padding-left: $grid-unit-05; - } - .dataviews-view-table__actions-column { width: 1%; } diff --git a/packages/dataviews/src/view-table.tsx b/packages/dataviews/src/view-table.tsx index 566d098216d43f..9ecfa9c9c0770d 100644 --- a/packages/dataviews/src/view-table.tsx +++ b/packages/dataviews/src/view-table.tsx @@ -15,6 +15,7 @@ import { privateApis as componentsPrivateApis, CheckboxControl, Spinner, + VisuallyHidden, } from '@wordpress/components'; import { forwardRef, @@ -551,9 +552,9 @@ function ViewTable< Item extends AnyItem >( { data-field-id="actions" className="dataviews-view-table__actions-column" > - + { __( 'Actions' ) } - + ) } From 2c91d731b59ecc16520ac29c4dc7d97716529bc7 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Mon, 20 May 2024 14:03:04 +0100 Subject: [PATCH 028/468] DataViews: Type the ViewActions component (#61729) Co-authored-by: youknowriad Co-authored-by: sirreal --- .../dataviews/src/{layouts.js => layouts.ts} | 0 packages/dataviews/src/types.ts | 16 ++-- .../src/{view-actions.js => view-actions.tsx} | 79 ++++++++++++++++--- packages/dataviews/src/view-grid.tsx | 2 +- 4 files changed, 76 insertions(+), 21 deletions(-) rename packages/dataviews/src/{layouts.js => layouts.ts} (100%) rename packages/dataviews/src/{view-actions.js => view-actions.tsx} (78%) diff --git a/packages/dataviews/src/layouts.js b/packages/dataviews/src/layouts.ts similarity index 100% rename from packages/dataviews/src/layouts.js rename to packages/dataviews/src/layouts.ts diff --git a/packages/dataviews/src/types.ts b/packages/dataviews/src/types.ts index 3735eaf0dd2033..e4bcb3440f5854 100644 --- a/packages/dataviews/src/types.ts +++ b/packages/dataviews/src/types.ts @@ -190,12 +190,12 @@ export interface ViewTable extends ViewBase { /** * The field to use as the primary field. */ - primaryField: string; + primaryField?: string; /** * The field to use as the media field. */ - mediaField: string; + mediaField?: string; }; } @@ -206,12 +206,12 @@ export interface ViewList extends ViewBase { /** * The field to use as the primary field. */ - primaryField: string; + primaryField?: string; /** * The field to use as the media field. */ - mediaField: string; + mediaField?: string; }; } @@ -222,22 +222,22 @@ export interface ViewGrid extends ViewBase { /** * The field to use as the primary field. */ - primaryField: string; + primaryField?: string; /** * The field to use as the media field. */ - mediaField: string; + mediaField?: string; /** * The fields to use as columns. */ - columnFields: string[]; + columnFields?: string[]; /** * The fields to use as badge fields. */ - badgeFields: string[]; + badgeFields?: string[]; }; } diff --git a/packages/dataviews/src/view-actions.js b/packages/dataviews/src/view-actions.tsx similarity index 78% rename from packages/dataviews/src/view-actions.js rename to packages/dataviews/src/view-actions.tsx index ee65b60c37b075..0e041cc070c525 100644 --- a/packages/dataviews/src/view-actions.js +++ b/packages/dataviews/src/view-actions.tsx @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import type { ChangeEvent } from 'react'; + /** * WordPress dependencies */ @@ -15,6 +20,7 @@ import { settings } from '@wordpress/icons'; import { unlock } from './lock-unlock'; import { SORTING_DIRECTIONS, sortLabels } from './constants'; import { VIEW_LAYOUTS } from './layouts'; +import type { AnyItem, NormalizedField, View } from './types'; const { DropdownMenuV2: DropdownMenu, @@ -25,7 +31,41 @@ const { DropdownMenuItemLabelV2: DropdownMenuItemLabel, } = unlock( componentsPrivateApis ); -function ViewTypeMenu( { view, onChangeView, supportedLayouts } ) { +interface ViewTypeMenuProps { + view: View; + onChangeView: ( view: View ) => void; + supportedLayouts?: string[]; +} + +interface PageSizeMenuProps { + view: View; + onChangeView: ( view: View ) => void; +} + +interface FieldsVisibilityMenuProps< Item extends AnyItem > { + view: View; + onChangeView: ( view: View ) => void; + fields: NormalizedField< Item >[]; +} + +interface SortMenuProps< Item extends AnyItem > { + fields: NormalizedField< Item >[]; + view: View; + onChangeView: ( view: View ) => void; +} + +interface ViewActionsProps< Item extends AnyItem > { + fields: NormalizedField< Item >[]; + view: View; + onChangeView: ( view: View ) => void; + supportedLayouts?: string[]; +} + +function ViewTypeMenu( { + view, + onChangeView, + supportedLayouts, +}: ViewTypeMenuProps ) { let _availableViews = VIEW_LAYOUTS; if ( supportedLayouts ) { _availableViews = _availableViews.filter( ( _view ) => @@ -41,7 +81,7 @@ function ViewTypeMenu( { view, onChangeView, supportedLayouts } ) { trigger={