From 3dece5ef458978a018f4280abfaa4e39ad92e554 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9?= <583546+oandregal@users.noreply.github.com> Date: Tue, 5 Nov 2024 11:20:30 +0100 Subject: [PATCH 01/72] Documentation: move docs for filters to proper place (#66743) --- packages/dataviews/README.md | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/packages/dataviews/README.md b/packages/dataviews/README.md index 79875542378b3e..00ab60ff5d0474 100644 --- a/packages/dataviews/README.md +++ b/packages/dataviews/README.md @@ -142,9 +142,22 @@ Each field is an object with the following properties: - `enableHiding`: whether the field can be hidden. True by default. - `enableGlobalSearch`: whether the field is searchable. False by default. - `filterBy`: configuration for the filters enabled by the `elements` property. - - `operators`: the list of [operators](#operators) supported by the field. + - `operators`: the list of operators supported by the field. - `isPrimary`: whether it is a primary filter. A primary filter is always visible and is not listed in the "Add filter" component, except for the list layout where it behaves like a secondary filter. +##### Filter operators + +| Operator | Selection | Description | Example | +| ---------- | -------------- | ----------------------------------------------------------------------- | -------------------------------------------------- | +| `is` | Single item | `EQUAL TO`. The item's field is equal to a single value. | Author is Admin | +| `isNot` | Single item | `NOT EQUAL TO`. The item's field is not equal to a single value. | Author is not Admin | +| `isAny` | Multiple items | `OR`. The item's field is present in a list of values. | Author is any: Admin, Editor | +| `isNone` | Multiple items | `NOT OR`. The item's field is not present in a list of values. | Author is none: Admin, Editor | +| `isAll` | Multiple items | `AND`. The item's field has all of the values in the list. | Category is all: Book, Review, Science Fiction | +| `isNotAll` | Multiple items | `NOT AND`. The item's field doesn't have all of the values in the list. | Category is not all: Book, Review, Science Fiction | + +`is` and `isNot` are single-selection operators, while `isAny`, `isNone`, `isAll`, and `isNotALl` are multi-selection. By default, a filter with no operators declared will support the `isAny` and `isNone` multi-selection operators. A filter cannot mix single-selection & multi-selection operators; if a single-selection operator is present in the list of valid operators, the multi-selection ones will be discarded and the filter won't allow selecting more than one item. + #### `view`: `object` The view object configures how the dataset is visible to the user. @@ -188,23 +201,6 @@ Properties: - `fields`: the `id` of the fields that are visible in the UI and the specific order in which they are displayed. - `layout`: config that is specific to a particular layout type. -##### Filter operators - -Allowed operators: - -| Operator | Selection | Description | Example | -| ---------- | -------------- | ----------------------------------------------------------------------- | -------------------------------------------------- | -| `is` | Single item | `EQUAL TO`. The item's field is equal to a single value. | Author is Admin | -| `isNot` | Single item | `NOT EQUAL TO`. The item's field is not equal to a single value. | Author is not Admin | -| `isAny` | Multiple items | `OR`. The item's field is present in a list of values. | Author is any: Admin, Editor | -| `isNone` | Multiple items | `NOT OR`. The item's field is not present in a list of values. | Author is none: Admin, Editor | -| `isAll` | Multiple items | `AND`. The item's field has all of the values in the list. | Category is all: Book, Review, Science Fiction | -| `isNotAll` | Multiple items | `NOT AND`. The item's field doesn't have all of the values in the list. | Category is not all: Book, Review, Science Fiction | - -`is` and `isNot` are single-selection operators, while `isAny`, `isNone`, `isAll`, and `isNotALl` are multi-selection. By default, a filter with no operators declared will support the `isAny` and `isNone` multi-selection operators. A filter cannot mix single-selection & multi-selection operators; if a single-selection operator is present in the list of valid operators, the multi-selection ones will be discarded and the filter won't allow selecting more than one item. - -> The legacy operators `in` and `notIn` have been deprecated and will be removed soon. In the meantime, they work as `is` and `isNot` operators, respectively. - ##### Properties of `layout` | Properties of `layout` | Table | Grid | List | From 0c869d4b4d2272efdeb37297051876043c2ed556 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9?= <583546+oandregal@users.noreply.github.com> Date: Tue, 5 Nov 2024 13:47:01 +0100 Subject: [PATCH 02/72] Documentation: add missing properties for DataViews/DataForm components (#66749) --- packages/dataviews/README.md | 117 +++++++++++++++++++++++++++-------- 1 file changed, 90 insertions(+), 27 deletions(-) diff --git a/packages/dataviews/README.md b/packages/dataviews/README.md index 00ab60ff5d0474..d61df206409152 100644 --- a/packages/dataviews/README.md +++ b/packages/dataviews/README.md @@ -63,7 +63,7 @@ const data = [ ]; ``` -By default, dataviews would use each record's `id` as an unique identifier. If it's not, the consumer should provide a `getItemId` function that returns one. +By default, dataviews would use each record's `id` as an unique identifier. If the records don't have a `id` property that identify them uniquely, they consumer needs to provide a `getItemId` function that returns an unique identifier for the record. #### `fields`: `Object[]` @@ -109,8 +109,8 @@ const fields = [ enableSorting: false, }, { - label: __( 'Status' ), id: 'status', + label: __( 'Status' ), getValue: ( { item } ) => STATUSES.find( ( { value } ) => value === item.status )?.label ?? item.status, @@ -126,27 +126,35 @@ const fields = [ Each field is an object with the following properties: - `id`: identifier for the field. Unique. +- `type`: the type of the field. See "Field types" section. - `label`: the field's name to be shown in the UI. +- `header`: defaults to the label. Allows providing a React element to render the field labels. - `getValue`: function that returns the value of the field, defaults to `field[id]`. - `render`: function that renders the field. Optional, `getValue` will be used if `render` is not defined. -- elements: The list of options to pick from when using the field as a filter or when editing (DataForm component). It expects an array of objects with the following properties: - - - `value`: The id of the value to filter to (for internal use) - - `label`: The text that will be displayed in the UI for the item. - - `description`: A longer description that describes the element, to also be displayed. Optional. - - To enable the filter by a field we just need to set a proper value to the `elements` property of the field we'd like to filter by. - -- `type`: the type of the field. See "Field types". +- `Edit`: function that renders an edit control for the field. Alternatively, can provide a string declaring one of default controls provided by DataForm `text`, `integer`, `datetime`, `radio`, `select`. +- `sort`: a compare function that determines the order of the records. +- `isValid`: callback to decide if a field should be displayed. +- `isVisible`: callback to decide if a field should be visible. - `enableSorting`: whether the data can be sorted by the given field. True by default. - `enableHiding`: whether the field can be hidden. True by default. - `enableGlobalSearch`: whether the field is searchable. False by default. +- elements: The list of options to pick from when using the field as a filter or when editing (DataForm component). Providing a list of to the field automatically creates a filter for it. It expects an array of objects with the following properties: + - `value`: The id of the value to filter to (for internal use) + - `label`: The text that will be displayed in the UI for the item. + - `description`: A longer description that describes the element, to also be displayed. Optional. - `filterBy`: configuration for the filters enabled by the `elements` property. - - `operators`: the list of operators supported by the field. + - `operators`: the list of operators supported by the field. See "Filter operators" below. - `isPrimary`: whether it is a primary filter. A primary filter is always visible and is not listed in the "Add filter" component, except for the list layout where it behaves like a secondary filter. +##### Field types + +Current supported types include: `text`, `integer`, `datetime`. + +If a field declares a `type` the `sort`, `isValid`, and `Edit` functions will be provided with default implementations. They will overriden if the field provides its own. + ##### Filter operators + | Operator | Selection | Description | Example | | ---------- | -------------- | ----------------------------------------------------------------------- | -------------------------------------------------- | | `is` | Single item | `EQUAL TO`. The item's field is equal to a single value. | Author is Admin | @@ -158,7 +166,7 @@ Each field is an object with the following properties: `is` and `isNot` are single-selection operators, while `isAny`, `isNone`, `isAll`, and `isNotALl` are multi-selection. By default, a filter with no operators declared will support the `isAny` and `isNone` multi-selection operators. A filter cannot mix single-selection & multi-selection operators; if a single-selection operator is present in the list of valid operators, the multi-selection ones will be discarded and the filter won't allow selecting more than one item. -#### `view`: `object` +#### `view`: `Object` The view object configures how the dataset is visible to the user. @@ -198,7 +206,7 @@ Properties: - `field`: the field used for sorting the dataset. - `direction`: the direction to use for sorting, one of `asc` or `desc`. -- `fields`: the `id` of the fields that are visible in the UI and the specific order in which they are displayed. +- `fields`: a list of field `id` that are visible in the UI and the specific order in which they are displayed. - `layout`: config that is specific to a particular layout type. ##### Properties of `layout` @@ -243,9 +251,9 @@ For example, this is how you'd define a `site` field which is a combination of a #### `onChangeView`: `function` -The view is a representation of the visible state of the dataset: what type of layout is used to display it (table, grid, etc.), how the dataset is filtered, how it is sorted or paginated. +Callback executed when the view has changed. It receives the new view object as a parameter. -It's the consumer's responsibility to work with the data provider to make sure the user options defined through the view's config (sort, pagination, filters, etc.) are respected. The `onChangeView` prop allows the consumer to provide a callback to be called when the view config changes, to process the data accordingly. +The view is a representation of the visible state of the dataset: what type of layout is used to display it (table, grid, etc.), how the dataset is filtered, how it is sorted or paginated. It's the consumer's responsibility to use the view config to query the data provider and make sure the user decisions (sort, pagination, filters, etc.) are respected. The following example shows how a view object is used to query the WordPress REST API via the entities abstraction. The same can be done with any other data provider. @@ -313,16 +321,18 @@ Collection of operations that can be performed upon each record. Each action is an object with the following properties: - `id`: string, required. Unique identifier of the action. For example, `move-to-trash`. -- `label`: string|function, required. User facing description of the action. For example, `Move to Trash`. In case we want to adjust the label based on the selected items, a function which accepts the selected records as input can be provided. This function should always return a `string` value. +- `label`: string|function, required. User facing description of the action. For example, `Move to Trash`. It can also take a function that takes the selected items as a parameter and returns a string: this can be useful to provide a dynamic label based on the selection. - `isPrimary`: boolean, optional. Whether the action should be listed inline (primary) or in hidden in the more actions menu (secondary). -- `icon`: icon to show for primary actions. It's required for a primary action, otherwise the action would be considered secondary. +- `icon`: SVG element. Icon to show for primary actions. It's required for a primary action, otherwise the action would be considered secondary. - `isEligible`: function, optional. Whether the action can be performed for a given record. If not present, the action is considered to be eligible for all items. It takes the given record as input. - `isDestructive`: boolean, optional. Whether the action can delete data, in which case the UI would communicate it via red color. -- `callback`: function, required unless `RenderModal` is provided. Callback function that takes the record as input and performs the required action. -- `RenderModal`: ReactElement, optional. If an action requires that some UI be rendered in a modal, it can provide a component which takes as props the record as `item` and a `closeModal` function. When this prop is provided, the `callback` property is ignored. -- `hideModalHeader`: boolean, optional. This property is used in combination with `RenderModal` and controls the visibility of the modal's header. If the action renders a modal and doesn't hide the header, the action's label is going to be used in the modal's header. - `supportsBulk`: Whether the action can be used as a bulk action. False by default. - `disabled`: Whether the action is disabled. False by default. +- `context`: where this action would be visible. One of `list`, `single`. +- `callback`: function, required unless `RenderModal` is provided. Callback function that takes as input the list of items to operate with, and performs the required action. +- `RenderModal`: ReactElement, optional. If an action requires that some UI be rendered in a modal, it can provide a component which takes as input the the list of `items` to operate with, `closeModal` function, and `onActionPerformed` function. When this prop is provided, the `callback` property is ignored. +- `hideModalHeader`: boolean, optional. This property is used in combination with `RenderModal` and controls the visibility of the modal's header. If the action renders a modal and doesn't hide the header, the action's label is going to be used in the modal's header. +- `modalHeader`: string, optional. The header of the modal. #### `paginationInfo`: `Object` @@ -339,7 +349,9 @@ What text to show in the search input. "Search" by default. #### `getItemId`: `function` -Function that receives an item and returns an unique identifier for it. By default, it uses the `id` of the item as unique identifier. If it's not, the consumer should provide their own. +Function that receives an item and returns an unique identifier for it. + +By default, dataviews would use each record's `id` as an unique identifier. If the records don't have a `id` property that identify them uniquely, they consumer needs to provide a `getItemId` function that returns an unique identifier for the record. #### `isLoading`: `boolean` @@ -361,11 +373,23 @@ const defaultLayouts = { }; ``` -The `defaultLayouts` property should be an object that includes properties named `table`, `grid`, or `list`. Each of these properties should contain a `layout` property, which holds the configuration for each specific layout type. Check [here](#properties-of-layout) the full list of properties available for each layout's configuration +The `defaultLayouts` property should be an object that includes properties named `table`, `grid`, or `list`. Each of these properties should contain a `layout` property, which holds the configuration for each specific layout type. Check "Properties of layout" for the full list of properties available for each layout's configuration + +#### `selection`: `string[]` + +The list of selected items' ids. + +If `selection` and `onChangeSelection` are provided, the `DataViews` component behaves as a controlled component, otherwise, it behaves like an uncontrolled component. #### `onChangeSelection`: `function` -Callback that signals the user selected one of more items, and takes them as parameter. So far, only the `list` view implements it. +Callback that signals the user selected one of more items. It receives the list of selected items' ids as a parameter. + +If `selection` and `onChangeSelection` are provided, the `DataViews` component behaves as a controlled component, otherwise, it behaves like an uncontrolled component. + +#### `header`: React component + +React component to be rendered next to the view config button. ## `DataForm` @@ -388,11 +412,13 @@ const Example = () => {
At WordPress Gutenberg's Storybook there's and example implementation of the DataForm component.
-### Props +### Properties -#### `data`: `Object[]` +#### `data`: `Object` + +A single item to be edited. -Same as `data` property of `DataViews`. +This could be thought as as a single record coming from the `data` property of `DataViews` — though it doesn't need to be; it can be, for example, a mix of records if you support bulk editing. #### `fields`: `Object[]` @@ -403,6 +429,43 @@ Same as `fields` property of `DataViews`. - `type`: either `regular` or `panel`. - `fields`: a list of fields ids that should be rendered. +#### `onChange`: `function` + +Callback function that receives an object with the edits done by the user. + +Example: + +```js +const data = { + id: 1, + title: 'Title', + author: 'Admin', + date: '2012-04-23T18:25:43.511Z', +}; + +const onChange = ( edits ) => { + /* + * edits will contain user edits. + * For example, if the user edited the title + * edits will be: + * + * { + * title: 'New title' + * } + * + */ +}; + +return ( + +); +``` + ## Utilities ### `filterSortAndPaginate` From 6cfeadc0d3a5361307877b095fdb981335dac4e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Greg=20Zi=C3=B3=C5=82kowski?= Date: Tue, 5 Nov 2024 15:06:35 +0100 Subject: [PATCH 03/72] Tools: Enforce the same order of fields in `package.json` files (#66239) * Update package managing flow * Adapt packages webpack config for workspaces * Ignore "dev" scripts this should be done another way * Annotate packages included as WordPress scripts * Do not annotate react-native packages * Update packages webpack to use wpScript package.json * Implement Gutenberg specific check-licenses script * Remove redundant entries from the ignore list * Update some docs langauge about lerna and npm workspaces * Tools: Enforce the same order of fields in `package.json` files * Update the PR number in the changelog entry * Update packages/npm-package-json-lint-config/index.js Co-authored-by: Jon Surrell * Update CHANGELOG.md * Update the order of properties added recently --------- Co-authored-by: Aki Hamano Co-authored-by: Jon Surrell Co-authored-by: t-hamano Co-authored-by: sirreal Co-authored-by: gziolo --- packages/a11y/package.json | 6 +++--- packages/annotations/package.json | 4 ++-- packages/api-fetch/package.json | 4 ++-- packages/autop/package.json | 4 ++-- packages/blob/package.json | 4 ++-- packages/block-directory/package.json | 4 ++-- packages/block-editor/package.json | 4 ++-- packages/block-library/package.json | 14 +++++++------- .../package.json | 4 ++-- .../block-serialization-spec-parser/package.json | 4 ++-- packages/blocks/package.json | 4 ++-- packages/commands/package.json | 4 ++-- packages/components/package.json | 6 +++--- packages/compose/package.json | 4 ++-- packages/core-commands/package.json | 4 ++-- packages/core-data/package.json | 4 ++-- packages/customize-widgets/package.json | 4 ++-- packages/data-controls/package.json | 4 ++-- packages/data/package.json | 4 ++-- packages/dataviews/package.json | 4 ++-- packages/date/package.json | 4 ++-- packages/deprecated/package.json | 4 ++-- packages/dom-ready/package.json | 4 ++-- packages/dom/package.json | 4 ++-- packages/edit-post/package.json | 4 ++-- packages/edit-site/package.json | 4 ++-- packages/edit-widgets/package.json | 4 ++-- packages/editor/package.json | 4 ++-- packages/element/package.json | 4 ++-- packages/escape-html/package.json | 4 ++-- packages/fields/package.json | 4 ++-- packages/format-library/package.json | 4 ++-- packages/hooks/package.json | 4 ++-- packages/html-entities/package.json | 4 ++-- packages/i18n/package.json | 4 ++-- packages/icons/package.json | 6 +++--- packages/interactivity-router/package.json | 2 +- packages/interactivity/package.json | 2 +- packages/interface/package.json | 4 ++-- packages/is-shallow-equal/package.json | 4 ++-- packages/keyboard-shortcuts/package.json | 4 ++-- packages/keycodes/package.json | 4 ++-- packages/list-reusable-blocks/package.json | 4 ++-- packages/media-utils/package.json | 4 ++-- packages/notices/package.json | 4 ++-- packages/npm-package-json-lint-config/CHANGELOG.md | 4 ++++ packages/npm-package-json-lint-config/index.js | 4 ++++ packages/nux/package.json | 4 ++-- packages/patterns/package.json | 4 ++-- packages/plugins/package.json | 4 ++-- packages/preferences-persistence/package.json | 4 ++-- packages/preferences/package.json | 4 ++-- packages/primitives/package.json | 6 +++--- packages/priority-queue/package.json | 4 ++-- packages/private-apis/package.json | 4 ++-- packages/react-i18n/package.json | 4 ++-- packages/redux-routine/package.json | 4 ++-- packages/reusable-blocks/package.json | 4 ++-- packages/rich-text/package.json | 6 +++--- packages/router/package.json | 4 ++-- packages/server-side-render/package.json | 4 ++-- packages/shortcode/package.json | 4 ++-- packages/style-engine/package.json | 4 ++-- packages/sync/package.json | 4 ++-- packages/token-list/package.json | 4 ++-- packages/undo-manager/package.json | 4 ++-- packages/url/package.json | 4 ++-- packages/viewport/package.json | 4 ++-- packages/vips/package.json | 4 ++-- packages/warning/package.json | 4 ++-- packages/widgets/package.json | 4 ++-- packages/wordcount/package.json | 4 ++-- 72 files changed, 156 insertions(+), 148 deletions(-) diff --git a/packages/a11y/package.json b/packages/a11y/package.json index 09fa62a9d082b7..008bd9088e0778 100644 --- a/packages/a11y/package.json +++ b/packages/a11y/package.json @@ -27,8 +27,9 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", - "types": "build-types", + "wpScript": true, "wpScriptModuleExports": "./build-module/module/index.js", + "types": "build-types", "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/dom-ready": "*", @@ -36,6 +37,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/annotations/package.json b/packages/annotations/package.json index 8a94b326a78194..b1d6d210807a87 100644 --- a/packages/annotations/package.json +++ b/packages/annotations/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/data": "*", @@ -38,6 +39,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/api-fetch/package.json b/packages/api-fetch/package.json index 26f156a0aa7d77..7d5b8dfd588897 100644 --- a/packages/api-fetch/package.json +++ b/packages/api-fetch/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "dependencies": { "@babel/runtime": "7.25.7", @@ -34,6 +35,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/autop/package.json b/packages/autop/package.json index 2169ff828da47a..336dda06edfe2c 100644 --- a/packages/autop/package.json +++ b/packages/autop/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -32,6 +33,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/blob/package.json b/packages/blob/package.json index 4b04d2ea2ddebd..0dc01ac7198f59 100644 --- a/packages/blob/package.json +++ b/packages/blob/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -32,6 +33,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/block-directory/package.json b/packages/block-directory/package.json index 59025e1ad45c7a..18a40824aa4754 100644 --- a/packages/block-directory/package.json +++ b/packages/block-directory/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/a11y": "*", @@ -54,6 +55,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json index 2b5cc8d6275e89..26b06474f54b18 100644 --- a/packages/block-editor/package.json +++ b/packages/block-editor/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "sideEffects": [ "build-style/**", "src/**/*.scss", @@ -87,6 +88,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/block-library/package.json b/packages/block-library/package.json index 2c880301c8355d..b196e53e5cd0f9 100644 --- a/packages/block-library/package.json +++ b/packages/block-library/package.json @@ -25,11 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", - "sideEffects": [ - "build-style/**", - "src/**/*.scss", - "{src,build,build-module}/*/init.js" - ], + "wpScript": true, "wpScriptModuleExports": { "./file/view": "./build-module/file/view.js", "./image/view": "./build-module/image/view.js", @@ -37,6 +33,11 @@ "./query/view": "./build-module/query/view.js", "./search/view": "./build-module/search/view.js" }, + "sideEffects": [ + "build-style/**", + "src/**/*.scss", + "{src,build,build-module}/*/init.js" + ], "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/a11y": "*", @@ -88,6 +89,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/block-serialization-default-parser/package.json b/packages/block-serialization-default-parser/package.json index d31ab188c0b6d6..010132ac971db1 100644 --- a/packages/block-serialization-default-parser/package.json +++ b/packages/block-serialization-default-parser/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -33,6 +34,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/block-serialization-spec-parser/package.json b/packages/block-serialization-spec-parser/package.json index 7cca4a7ca1a68b..d430d2afeebf5a 100644 --- a/packages/block-serialization-spec-parser/package.json +++ b/packages/block-serialization-spec-parser/package.json @@ -25,6 +25,7 @@ "npm": ">=8.19.2" }, "main": "parser.js", + "wpScript": true, "sideEffects": false, "dependencies": { "pegjs": "^0.10.0", @@ -37,6 +38,5 @@ "build": "concurrently \"npm run build:js\" \"npm run build:php\"", "build:js": "pegjs --format commonjs -o ./parser.js ./grammar.pegjs", "build:php": "node bin/create-php-parser.js" - }, - "wpScript": true + } } diff --git a/packages/blocks/package.json b/packages/blocks/package.json index 2d944fe656363b..42890e9b7d56bc 100644 --- a/packages/blocks/package.json +++ b/packages/blocks/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "sideEffects": [ "{src,build,build-module}/{index.js,store/index.js}" ], @@ -62,6 +63,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/commands/package.json b/packages/commands/package.json index 98eadca595ca86..cebf3237a00d75 100644 --- a/packages/commands/package.json +++ b/packages/commands/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/components": "*", @@ -44,6 +45,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/components/package.json b/packages/components/package.json index faf923ad426bf6..6871511cf5b1e5 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -25,11 +25,12 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, + "types": "build-types", "sideEffects": [ "build-style/**", "src/**/*.scss" ], - "types": "build-types", "dependencies": { "@ariakit/react": "^0.4.10", "@babel/runtime": "7.25.7", @@ -83,6 +84,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/compose/package.json b/packages/compose/package.json index adff24f6663006..68b00b24298d87 100644 --- a/packages/compose/package.json +++ b/packages/compose/package.json @@ -27,6 +27,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -49,6 +50,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/core-commands/package.json b/packages/core-commands/package.json index ac15b3578cf6ff..db2fb900865c80 100644 --- a/packages/core-commands/package.json +++ b/packages/core-commands/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "sideEffects": false, "dependencies": { "@babel/runtime": "7.25.7", @@ -48,6 +49,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/core-data/package.json b/packages/core-data/package.json index 44ed3aaad5dd3a..a7216e931a70ca 100644 --- a/packages/core-data/package.json +++ b/packages/core-data/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": [ "{src,build,build-module}/index.js" @@ -60,6 +61,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/customize-widgets/package.json b/packages/customize-widgets/package.json index ef6748f34205e8..41b3a7bd463aa3 100644 --- a/packages/customize-widgets/package.json +++ b/packages/customize-widgets/package.json @@ -23,6 +23,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/block-editor": "*", @@ -55,6 +56,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/data-controls/package.json b/packages/data-controls/package.json index 948efe7fb64022..e4898ff1d0e1ab 100644 --- a/packages/data-controls/package.json +++ b/packages/data-controls/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "dependencies": { "@babel/runtime": "7.25.7", @@ -38,6 +39,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/data/package.json b/packages/data/package.json index 0e46b1a366918e..fd1ef7ef4d7489 100644 --- a/packages/data/package.json +++ b/packages/data/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -50,6 +51,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/dataviews/package.json b/packages/dataviews/package.json index 5599a84b92d34c..df30fea1a1c714 100644 --- a/packages/dataviews/package.json +++ b/packages/dataviews/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -47,6 +48,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/date/package.json b/packages/date/package.json index f53c6314912271..d67c1dc527caee 100644 --- a/packages/date/package.json +++ b/packages/date/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "dependencies": { "@babel/runtime": "7.25.7", @@ -34,6 +35,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/deprecated/package.json b/packages/deprecated/package.json index 69d2414b0c11a2..64ffc6cd30b251 100644 --- a/packages/deprecated/package.json +++ b/packages/deprecated/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -33,6 +34,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/dom-ready/package.json b/packages/dom-ready/package.json index 0202229b202842..6e7986c4965dc6 100644 --- a/packages/dom-ready/package.json +++ b/packages/dom-ready/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -32,6 +33,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/dom/package.json b/packages/dom/package.json index 58c572545cc526..97576e9a22e0ff 100644 --- a/packages/dom/package.json +++ b/packages/dom/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -34,6 +35,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/edit-post/package.json b/packages/edit-post/package.json index c594722d180e4f..1b28c1d5f31aa6 100644 --- a/packages/edit-post/package.json +++ b/packages/edit-post/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/a11y": "*", @@ -66,6 +67,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/edit-site/package.json b/packages/edit-site/package.json index 95f9cd5f05742a..67932bcb06321e 100644 --- a/packages/edit-site/package.json +++ b/packages/edit-site/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "dependencies": { "@babel/runtime": "7.25.7", "@react-spring/web": "^9.4.5", @@ -82,6 +83,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/edit-widgets/package.json b/packages/edit-widgets/package.json index 0629dca5ec1052..331dbc742dbc8c 100644 --- a/packages/edit-widgets/package.json +++ b/packages/edit-widgets/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/api-fetch": "*", @@ -63,6 +64,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/editor/package.json b/packages/editor/package.json index 61cce478cf08fe..e7b46685719dd4 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "sideEffects": [ "build-style/**", "src/**/*.scss", @@ -85,6 +86,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/element/package.json b/packages/element/package.json index 3004dadb7ea605..4a196255971cfb 100644 --- a/packages/element/package.json +++ b/packages/element/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -40,6 +41,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/escape-html/package.json b/packages/escape-html/package.json index 72863ce15fd8b8..135876460bd05e 100644 --- a/packages/escape-html/package.json +++ b/packages/escape-html/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -32,6 +33,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/fields/package.json b/packages/fields/package.json index e0b7125cf3e4eb..34ef8992f93adf 100644 --- a/packages/fields/package.json +++ b/packages/fields/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": [ "build-style/**", @@ -62,6 +63,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/format-library/package.json b/packages/format-library/package.json index f07ba0f4b61b35..41852910ff5389 100644 --- a/packages/format-library/package.json +++ b/packages/format-library/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/a11y": "*", @@ -46,6 +47,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/hooks/package.json b/packages/hooks/package.json index d0f2fd381b83ca..857cb24a877468 100644 --- a/packages/hooks/package.json +++ b/packages/hooks/package.json @@ -25,12 +25,12 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "dependencies": { "@babel/runtime": "7.25.7" }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/html-entities/package.json b/packages/html-entities/package.json index d717a89bf049b8..4ecab2056c9c3a 100644 --- a/packages/html-entities/package.json +++ b/packages/html-entities/package.json @@ -26,12 +26,12 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "dependencies": { "@babel/runtime": "7.25.7" }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/i18n/package.json b/packages/i18n/package.json index 33de2873f75a6a..53e81b64a65349 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "bin": { "pot-to-php": "./tools/pot-to-php.js" @@ -39,6 +40,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/icons/package.json b/packages/icons/package.json index 125b6f19beaa02..895f2e184b4e48 100644 --- a/packages/icons/package.json +++ b/packages/icons/package.json @@ -16,7 +16,6 @@ "url": "git+https://github.com/WordPress/gutenberg.git", "directory": "packages/icons" }, - "sideEffects": false, "bugs": { "url": "https://github.com/WordPress/gutenberg/issues" }, @@ -27,7 +26,9 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", + "sideEffects": false, "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/element": "*", @@ -35,6 +36,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/interactivity-router/package.json b/packages/interactivity-router/package.json index ce9b268af556ac..378ddda56ee76b 100644 --- a/packages/interactivity-router/package.json +++ b/packages/interactivity-router/package.json @@ -25,8 +25,8 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", - "types": "build-types", "wpScriptModuleExports": "./build-module/index.js", + "types": "build-types", "dependencies": { "@wordpress/a11y": "*", "@wordpress/interactivity": "*" diff --git a/packages/interactivity/package.json b/packages/interactivity/package.json index 106ed44bf44d39..c9edd586bc9959 100644 --- a/packages/interactivity/package.json +++ b/packages/interactivity/package.json @@ -25,11 +25,11 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", - "types": "build-types", "wpScriptModuleExports": { ".": "./build-module/index.js", "./debug": "./build-module/debug.js" }, + "types": "build-types", "dependencies": { "@preact/signals": "^1.3.0", "preact": "^10.24.2" diff --git a/packages/interface/package.json b/packages/interface/package.json index 807135ae0160a6..f6a7952cb0d051 100644 --- a/packages/interface/package.json +++ b/packages/interface/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "sideEffects": [ "build-style/**", "src/**/*.scss", @@ -52,6 +53,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/is-shallow-equal/package.json b/packages/is-shallow-equal/package.json index 8da2643a58b840..7dafdea88e7a4f 100644 --- a/packages/is-shallow-equal/package.json +++ b/packages/is-shallow-equal/package.json @@ -34,6 +34,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -41,6 +42,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/keyboard-shortcuts/package.json b/packages/keyboard-shortcuts/package.json index 50ee79d554a89f..ce029907c0dbf4 100644 --- a/packages/keyboard-shortcuts/package.json +++ b/packages/keyboard-shortcuts/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/data": "*", @@ -36,6 +37,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/keycodes/package.json b/packages/keycodes/package.json index 3e820ef68cf97e..2038db1984760f 100644 --- a/packages/keycodes/package.json +++ b/packages/keycodes/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -33,6 +34,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/list-reusable-blocks/package.json b/packages/list-reusable-blocks/package.json index 129909a948ef1d..328695f31925ee 100644 --- a/packages/list-reusable-blocks/package.json +++ b/packages/list-reusable-blocks/package.json @@ -25,6 +25,7 @@ }, "main": "build/index.js", "module": "build-module/index.js", + "wpScript": true, "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/api-fetch": "*", @@ -41,6 +42,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/media-utils/package.json b/packages/media-utils/package.json index 20eef4dd83c6b4..e57b0f51844504 100644 --- a/packages/media-utils/package.json +++ b/packages/media-utils/package.json @@ -25,6 +25,7 @@ }, "main": "build/index.js", "module": "build-module/index.js", + "wpScript": true, "types": "build-types", "dependencies": { "@babel/runtime": "7.25.7", @@ -36,6 +37,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/notices/package.json b/packages/notices/package.json index 34d2253870128b..f150616445b060 100644 --- a/packages/notices/package.json +++ b/packages/notices/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "dependencies": { "@babel/runtime": "7.25.7", @@ -36,6 +37,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/npm-package-json-lint-config/CHANGELOG.md b/packages/npm-package-json-lint-config/CHANGELOG.md index 9c0aafedcead1a..9dee828f63fea0 100644 --- a/packages/npm-package-json-lint-config/CHANGELOG.md +++ b/packages/npm-package-json-lint-config/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Enhancement + +- Include `exports`, `wpScript`, `wpScriptModuleExports` and `sideEffects` in the `prefer-property-order` rule ([#66239](https://github.com/WordPress/gutenberg/pull/66239)). + ## 5.11.0 (2024-10-30) ## 5.10.0 (2024-10-16) diff --git a/packages/npm-package-json-lint-config/index.js b/packages/npm-package-json-lint-config/index.js index 831970c59bf3a6..e1abd5e34f99c5 100644 --- a/packages/npm-package-json-lint-config/index.js +++ b/packages/npm-package-json-lint-config/index.js @@ -56,8 +56,12 @@ const defaultConfig = { 'type', 'main', 'module', + 'exports', 'react-native', + 'wpScript', + 'wpScriptModuleExports', 'types', + 'sideEffects', 'bin', 'dependencies', 'devDependencies', diff --git a/packages/nux/package.json b/packages/nux/package.json index ccf1cb774c2424..50a4d5acf6d028 100644 --- a/packages/nux/package.json +++ b/packages/nux/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "sideEffects": [ "build-style/**", "src/**/*.scss", @@ -46,6 +47,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/patterns/package.json b/packages/patterns/package.json index 92ef655dc3c9c7..d29fc9bcf49bca 100644 --- a/packages/patterns/package.json +++ b/packages/patterns/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "sideEffects": [ "build-style/**", "src/**/*.scss", @@ -53,6 +54,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/plugins/package.json b/packages/plugins/package.json index b741c0359b32b1..a926c2fd9a4e17 100644 --- a/packages/plugins/package.json +++ b/packages/plugins/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "dependencies": { "@babel/runtime": "7.25.7", @@ -43,6 +44,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/preferences-persistence/package.json b/packages/preferences-persistence/package.json index e2623eac1a021c..05d2cf7031929d 100644 --- a/packages/preferences-persistence/package.json +++ b/packages/preferences-persistence/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "sideEffects": false, "dependencies": { "@babel/runtime": "7.25.7", @@ -33,6 +34,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/preferences/package.json b/packages/preferences/package.json index ef231f359ba143..205f9b5d4966b1 100644 --- a/packages/preferences/package.json +++ b/packages/preferences/package.json @@ -27,6 +27,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "sideEffects": false, "dependencies": { "@babel/runtime": "7.25.7", @@ -47,6 +48,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/primitives/package.json b/packages/primitives/package.json index 04c3cc86dddec3..d24bc1acf25e2d 100644 --- a/packages/primitives/package.json +++ b/packages/primitives/package.json @@ -26,10 +26,11 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, + "types": "build-types", "sideEffects": [ "src/**/*.scss" ], - "types": "build-types", "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/element": "*", @@ -40,6 +41,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/priority-queue/package.json b/packages/priority-queue/package.json index 91020c0f81db24..8c98b0b3552198 100644 --- a/packages/priority-queue/package.json +++ b/packages/priority-queue/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -34,6 +35,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/private-apis/package.json b/packages/private-apis/package.json index 9364a596c39af0..84436470521332 100644 --- a/packages/private-apis/package.json +++ b/packages/private-apis/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -33,6 +34,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/react-i18n/package.json b/packages/react-i18n/package.json index d98a4d7711f89c..31c4d98fa475e0 100644 --- a/packages/react-i18n/package.json +++ b/packages/react-i18n/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -35,6 +36,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/redux-routine/package.json b/packages/redux-routine/package.json index 489bc14632ded2..b3aa6fa79474a8 100644 --- a/packages/redux-routine/package.json +++ b/packages/redux-routine/package.json @@ -27,6 +27,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -40,6 +41,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/reusable-blocks/package.json b/packages/reusable-blocks/package.json index 82a812163a0ccc..e2649609016642 100644 --- a/packages/reusable-blocks/package.json +++ b/packages/reusable-blocks/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "sideEffects": [ "{src,build,build-module}/{index.js,store/index.js}" ], @@ -48,6 +49,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/rich-text/package.json b/packages/rich-text/package.json index 4a5fe0f8e9d5ca..f038e097668984 100644 --- a/packages/rich-text/package.json +++ b/packages/rich-text/package.json @@ -25,11 +25,12 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, + "types": "build-types", "sideEffects": [ "src/**/*.scss", "{src,build,build-module}/{index.js,store/index.js}" ], - "types": "build-types", "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/a11y": "*", @@ -47,6 +48,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/router/package.json b/packages/router/package.json index 1822e79a317102..3d80481c9b6baa 100644 --- a/packages/router/package.json +++ b/packages/router/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "dependencies": { "@babel/runtime": "7.25.7", @@ -38,6 +39,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/server-side-render/package.json b/packages/server-side-render/package.json index 0bb27f17ad62d0..cdfe9679c5049d 100644 --- a/packages/server-side-render/package.json +++ b/packages/server-side-render/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/api-fetch": "*", @@ -45,6 +46,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/shortcode/package.json b/packages/shortcode/package.json index f85efeb91e1803..c476788e3cfaf8 100644 --- a/packages/shortcode/package.json +++ b/packages/shortcode/package.json @@ -25,12 +25,12 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "dependencies": { "@babel/runtime": "7.25.7", "memize": "^2.0.1" }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/style-engine/package.json b/packages/style-engine/package.json index 76ce2ccbb1445d..378f4dce91dad9 100644 --- a/packages/style-engine/package.json +++ b/packages/style-engine/package.json @@ -27,6 +27,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -35,6 +36,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/sync/package.json b/packages/sync/package.json index 95be09803a75f1..d7181479327d3a 100644 --- a/packages/sync/package.json +++ b/packages/sync/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -41,6 +42,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/token-list/package.json b/packages/token-list/package.json index 0d966edeb9ee7d..370d50e5b4700a 100644 --- a/packages/token-list/package.json +++ b/packages/token-list/package.json @@ -25,12 +25,12 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "dependencies": { "@babel/runtime": "7.25.7" }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/undo-manager/package.json b/packages/undo-manager/package.json index f0464d7fd2f9e2..66f895f8da887d 100644 --- a/packages/undo-manager/package.json +++ b/packages/undo-manager/package.json @@ -26,6 +26,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -34,6 +35,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/url/package.json b/packages/url/package.json index 885efe28f8e8b4..2ecdb8eae18113 100644 --- a/packages/url/package.json +++ b/packages/url/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { @@ -33,6 +34,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/viewport/package.json b/packages/viewport/package.json index 05e098f65e1aeb..224da8a871addf 100644 --- a/packages/viewport/package.json +++ b/packages/viewport/package.json @@ -25,6 +25,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/compose": "*", @@ -36,6 +37,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/vips/package.json b/packages/vips/package.json index 810b8d2b5c9fc4..69912b2eaed71e 100644 --- a/packages/vips/package.json +++ b/packages/vips/package.json @@ -25,12 +25,12 @@ }, "main": "build/index.js", "module": "build-module/index.js", + "wpScript": true, "types": "build-types", "dependencies": { "wasm-vips": "^0.0.10" }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/warning/package.json b/packages/warning/package.json index cae2aefa421ffe..d9c5a4dd83dc10 100644 --- a/packages/warning/package.json +++ b/packages/warning/package.json @@ -25,10 +25,10 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "types": "build-types", "sideEffects": false, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/widgets/package.json b/packages/widgets/package.json index b7fa362d615856..a755890c48ed92 100644 --- a/packages/widgets/package.json +++ b/packages/widgets/package.json @@ -23,6 +23,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "dependencies": { "@babel/runtime": "7.25.7", "@wordpress/api-fetch": "*", @@ -44,6 +45,5 @@ }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } diff --git a/packages/wordcount/package.json b/packages/wordcount/package.json index a9f8304f79d8ff..117e2227a926c8 100644 --- a/packages/wordcount/package.json +++ b/packages/wordcount/package.json @@ -25,12 +25,12 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "wpScript": true, "sideEffects": false, "dependencies": { "@babel/runtime": "7.25.7" }, "publishConfig": { "access": "public" - }, - "wpScript": true + } } From e6ee784b3b4e348e4f47369a94c7c86f5469df68 Mon Sep 17 00:00:00 2001 From: Ella <4710635+ellatrix@users.noreply.github.com> Date: Tue, 5 Nov 2024 17:23:19 +0100 Subject: [PATCH 04/72] Site Editor: fix template for page-on-front option (#66739) Co-authored-by: ellatrix Co-authored-by: draganescu Co-authored-by: ntsekouras Co-authored-by: mdawaffe --- .../use-init-edited-entity-from-url.js | 27 +++++++++++++- .../site-editor/template-hierarchy.spec.js | 35 +++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 test/e2e/specs/site-editor/template-hierarchy.spec.js diff --git a/packages/edit-site/src/components/sync-state-with-url/use-init-edited-entity-from-url.js b/packages/edit-site/src/components/sync-state-with-url/use-init-edited-entity-from-url.js index 3498bed4c99a56..2dbd143766e67c 100644 --- a/packages/edit-site/src/components/sync-state-with-url/use-init-edited-entity-from-url.js +++ b/packages/edit-site/src/components/sync-state-with-url/use-init-edited-entity-from-url.js @@ -96,7 +96,32 @@ function useResolveEditedEntityAndContext( { postId, postType } ) { postTypeToResolve === 'page' && homepageId === postIdToResolve ) { - return getDefaultTemplateId( { slug: 'front-page' } ); + // The /lookup endpoint cannot currently handle a lookup + // when a page is set as the front page, so specifically in + // that case, we want to check if there is a front page + // template, and instead of falling back to the home + // template, we want to fall back to the page template. + const templates = getEntityRecords( + 'postType', + TEMPLATE_POST_TYPE, + { + per_page: -1, + } + ); + if ( templates ) { + const id = templates?.find( + ( { slug } ) => slug === 'front-page' + )?.id; + if ( id ) { + return id; + } + + // If no front page template is found, continue with the + // logic below (fetching the page template). + } else { + // Still resolving `templates`. + return undefined; + } } const editedEntity = getEditedEntityRecord( diff --git a/test/e2e/specs/site-editor/template-hierarchy.spec.js b/test/e2e/specs/site-editor/template-hierarchy.spec.js new file mode 100644 index 00000000000000..5fcc77da9942ce --- /dev/null +++ b/test/e2e/specs/site-editor/template-hierarchy.spec.js @@ -0,0 +1,35 @@ +/** + * WordPress dependencies + */ +const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); + +test.describe( 'Template hierarchy', () => { + test.beforeAll( async ( { requestUtils } ) => { + await requestUtils.activateTheme( 'twentytwentyfour' ); + } ); + + test.afterEach( async ( { requestUtils } ) => { + await requestUtils.activateTheme( 'twentytwentyone' ); + } ); + + test( 'shows correct template with page on front option', async ( { + admin, + page, + editor, + } ) => { + await admin.visitAdminPage( 'options-reading.php' ); + await page.click( 'input[name="show_on_front"][value="page"]' ); + await page.selectOption( 'select[name="page_on_front"]', '2' ); + await page.click( 'input[type="submit"]' ); + await admin.visitSiteEditor(); + + // Title block should contain "Sample Page" + await expect( + editor.canvas.locator( 'role=document[name="Block: Title"]' ) + ).toContainText( 'Sample Page' ); + + await admin.visitAdminPage( 'options-reading.php' ); + await page.click( 'input[name="show_on_front"][value="posts"]' ); + await page.click( 'input[type="submit"]' ); + } ); +} ); From bdc3ab224accbc42402a372ded80212c8b2ebd50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Greg=20Zi=C3=B3=C5=82kowski?= Date: Tue, 5 Nov 2024 17:35:20 +0100 Subject: [PATCH 05/72] WP Scripts: Make watch mode more resilient for developer errors (#66752) * WP Scripts: Fix the watch mode so it continues working when errors pop up while developing * Improve the handling for parsing JSON files * Docs: Add changelog entry * Improve the warnings messages Co-authored-by: gziolo Co-authored-by: sirreal --- packages/scripts/CHANGELOG.md | 4 ++ packages/scripts/utils/config.js | 102 ++++++++++++++++--------------- 2 files changed, 56 insertions(+), 50 deletions(-) diff --git a/packages/scripts/CHANGELOG.md b/packages/scripts/CHANGELOG.md index ca5a44bca62b52..c09cfd1ac9aaaa 100644 --- a/packages/scripts/CHANGELOG.md +++ b/packages/scripts/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Bug Fix + +- Make `start` script more resilient for developer errors ([#66752](https://github.com/WordPress/gutenberg/pull/66752)). + ## 30.4.0 (2024-10-30) ### Enhancements diff --git a/packages/scripts/utils/config.js b/packages/scripts/utils/config.js index dfb44730438c4a..3d99f3784859df 100644 --- a/packages/scripts/utils/config.js +++ b/packages/scripts/utils/config.js @@ -1,7 +1,6 @@ /** * External dependencies */ -const chalk = require( 'chalk' ); const { readFileSync } = require( 'fs' ); const { basename, dirname, extname, join, sep } = require( 'path' ); const { sync: glob } = require( 'fast-glob' ); @@ -21,7 +20,8 @@ const { getBlockJsonModuleFields, getBlockJsonScriptFields, } = require( './block-json' ); -const { log } = console; + +const { warn } = console; // See https://babeljs.io/docs/en/config-files#configuration-file-types. const hasBabelConfig = () => @@ -209,10 +209,8 @@ function getWebpackEntryPoints( buildType ) { // Continue only if the source directory exists. if ( ! hasProjectFile( getWordPressSrcDirectory() ) ) { - log( - chalk.yellow( - `Source directory "${ getWordPressSrcDirectory() }" was not found. Please confirm there is a "src" directory in the root or the value passed to --webpack-src-dir is correct.` - ) + warn( + `Source directory "${ getWordPressSrcDirectory() }" was not found. Please confirm there is a "src" directory in the root or the value passed to --webpack-src-dir is correct.` ); return {}; } @@ -240,12 +238,13 @@ function getWebpackEntryPoints( buildType ) { try { parsedBlockJson = JSON.parse( fileContents ); } catch ( error ) { - chalk.yellow( - `Skipping "${ blockMetadataFile.replace( + warn( + `Not scanning "${ blockMetadataFile.replace( fromProjectRoot( sep ), '' - ) }" due to malformed JSON.` + ) }" due to collect entry points due to malformed JSON.` ); + continue; } const fields = @@ -270,18 +269,16 @@ function getWebpackEntryPoints( buildType ) { // Takes the path without the file extension, and relative to the defined source directory. if ( ! filepath.startsWith( srcDirectory ) ) { - log( - chalk.yellow( - `Skipping "${ value.replace( - 'file:', - '' - ) }" listed in "${ blockMetadataFile.replace( - fromProjectRoot( sep ), - '' - ) }". File is located outside of the "${ getWordPressSrcDirectory() }" directory.` - ) + warn( + `Skipping "${ value.replace( + 'file:', + '' + ) }" listed in "${ blockMetadataFile.replace( + fromProjectRoot( sep ), + '' + ) }". File is located outside of the "${ getWordPressSrcDirectory() }" directory.` ); - return; + continue; } const entryName = filepath .replace( extname( filepath ), '' ) @@ -298,18 +295,16 @@ function getWebpackEntryPoints( buildType ) { ); if ( ! entryFilepath ) { - log( - chalk.yellow( - `Skipping "${ value.replace( - 'file:', - '' - ) }" listed in "${ blockMetadataFile.replace( - fromProjectRoot( sep ), - '' - ) }". File does not exist in the "${ getWordPressSrcDirectory() }" directory.` - ) + warn( + `Skipping "${ value.replace( + 'file:', + '' + ) }" listed in "${ blockMetadataFile.replace( + fromProjectRoot( sep ), + '' + ) }". File does not exist in the "${ getWordPressSrcDirectory() }" directory.` ); - return; + continue; } entryPoints[ entryName ] = entryFilepath; } @@ -334,10 +329,8 @@ function getWebpackEntryPoints( buildType ) { } ); if ( ! entryFile ) { - log( - chalk.yellow( - `No entry file discovered in the "${ getWordPressSrcDirectory() }" directory.` - ) + warn( + `No entry file discovered in the "${ getWordPressSrcDirectory() }" directory.` ); return {}; } @@ -370,13 +363,24 @@ function getPhpFilePaths( context, props ) { const srcDirectory = fromProjectRoot( context + sep ); return blockMetadataFiles.flatMap( ( blockMetadataFile ) => { - const blockJson = JSON.parse( readFileSync( blockMetadataFile ) ); - const paths = []; + let parsedBlockJson; + try { + parsedBlockJson = JSON.parse( readFileSync( blockMetadataFile ) ); + } catch ( error ) { + warn( + `Not scanning "${ blockMetadataFile.replace( + fromProjectRoot( sep ), + '' + ) }" due to detect render files due to malformed JSON.` + ); + return paths; + } + for ( const prop of props ) { if ( - typeof blockJson?.[ prop ] !== 'string' || - ! blockJson[ prop ]?.startsWith( 'file:' ) + typeof parsedBlockJson?.[ prop ] !== 'string' || + ! parsedBlockJson[ prop ]?.startsWith( 'file:' ) ) { continue; } @@ -384,21 +388,19 @@ function getPhpFilePaths( context, props ) { // Removes the `file:` prefix. const filepath = join( dirname( blockMetadataFile ), - blockJson[ prop ].replace( 'file:', '' ) + parsedBlockJson[ prop ].replace( 'file:', '' ) ); // Takes the path without the file extension, and relative to the defined source directory. if ( ! filepath.startsWith( srcDirectory ) ) { - log( - chalk.yellow( - `Skipping "${ blockJson[ prop ].replace( - 'file:', - '' - ) }" listed in "${ blockMetadataFile.replace( - fromProjectRoot( sep ), - '' - ) }". File is located outside of the "${ context }" directory.` - ) + warn( + `Skipping "${ parsedBlockJson[ prop ].replace( + 'file:', + '' + ) }" listed in "${ blockMetadataFile.replace( + fromProjectRoot( sep ), + '' + ) }". File is located outside of the "${ context }" directory.` ); continue; } From d50ff80f4f4e688838cf3f2972cf7213e3acb35b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9?= <583546+oandregal@users.noreply.github.com> Date: Tue, 5 Nov 2024 17:39:03 +0100 Subject: [PATCH 06/72] Documentation: add section about the Fields API (#66761) --- packages/dataviews/README.md | 454 +++++++++++++++++++++++++++++++---- 1 file changed, 407 insertions(+), 47 deletions(-) diff --git a/packages/dataviews/README.md b/packages/dataviews/README.md index d61df206409152..fe35ae8f0aaa9a 100644 --- a/packages/dataviews/README.md +++ b/packages/dataviews/README.md @@ -67,7 +67,7 @@ By default, dataviews would use each record's `id` as an unique identifier. If t #### `fields`: `Object[]` -The fields describe the visible items for each record in the dataset. +The fields describe the visible items for each record in the dataset and how they behave (how to sort them, display them, etc.). See "Fields API" as a reference for every property. Example: @@ -95,7 +95,7 @@ const fields = [ }, { id: 'author', - label: __( 'Author' ), + label: 'Author', render: ( { item } ) => { return { item.author }; }, @@ -110,7 +110,7 @@ const fields = [ }, { id: 'status', - label: __( 'Status' ), + label: 'Status', getValue: ( { item } ) => STATUSES.find( ( { value } ) => value === item.status )?.label ?? item.status, @@ -123,49 +123,6 @@ const fields = [ ]; ``` -Each field is an object with the following properties: - -- `id`: identifier for the field. Unique. -- `type`: the type of the field. See "Field types" section. -- `label`: the field's name to be shown in the UI. -- `header`: defaults to the label. Allows providing a React element to render the field labels. -- `getValue`: function that returns the value of the field, defaults to `field[id]`. -- `render`: function that renders the field. Optional, `getValue` will be used if `render` is not defined. -- `Edit`: function that renders an edit control for the field. Alternatively, can provide a string declaring one of default controls provided by DataForm `text`, `integer`, `datetime`, `radio`, `select`. -- `sort`: a compare function that determines the order of the records. -- `isValid`: callback to decide if a field should be displayed. -- `isVisible`: callback to decide if a field should be visible. -- `enableSorting`: whether the data can be sorted by the given field. True by default. -- `enableHiding`: whether the field can be hidden. True by default. -- `enableGlobalSearch`: whether the field is searchable. False by default. -- elements: The list of options to pick from when using the field as a filter or when editing (DataForm component). Providing a list of to the field automatically creates a filter for it. It expects an array of objects with the following properties: - - `value`: The id of the value to filter to (for internal use) - - `label`: The text that will be displayed in the UI for the item. - - `description`: A longer description that describes the element, to also be displayed. Optional. -- `filterBy`: configuration for the filters enabled by the `elements` property. - - `operators`: the list of operators supported by the field. See "Filter operators" below. - - `isPrimary`: whether it is a primary filter. A primary filter is always visible and is not listed in the "Add filter" component, except for the list layout where it behaves like a secondary filter. - -##### Field types - -Current supported types include: `text`, `integer`, `datetime`. - -If a field declares a `type` the `sort`, `isValid`, and `Edit` functions will be provided with default implementations. They will overriden if the field provides its own. - -##### Filter operators - - -| Operator | Selection | Description | Example | -| ---------- | -------------- | ----------------------------------------------------------------------- | -------------------------------------------------- | -| `is` | Single item | `EQUAL TO`. The item's field is equal to a single value. | Author is Admin | -| `isNot` | Single item | `NOT EQUAL TO`. The item's field is not equal to a single value. | Author is not Admin | -| `isAny` | Multiple items | `OR`. The item's field is present in a list of values. | Author is any: Admin, Editor | -| `isNone` | Multiple items | `NOT OR`. The item's field is not present in a list of values. | Author is none: Admin, Editor | -| `isAll` | Multiple items | `AND`. The item's field has all of the values in the list. | Category is all: Book, Review, Science Fiction | -| `isNotAll` | Multiple items | `NOT AND`. The item's field doesn't have all of the values in the list. | Category is not all: Book, Review, Science Fiction | - -`is` and `isNot` are single-selection operators, while `isAny`, `isNone`, `isAll`, and `isNotALl` are multi-selection. By default, a filter with no operators declared will support the `isAny` and `isNone` multi-selection operators. A filter cannot mix single-selection & multi-selection operators; if a single-selection operator is present in the list of valid operators, the multi-selection ones will be discarded and the filter won't allow selecting more than one item. - #### `view`: `Object` The view object configures how the dataset is visible to the user. @@ -422,7 +379,33 @@ This could be thought as as a single record coming from the `data` property of ` #### `fields`: `Object[]` -Same as `fields` property of `DataViews`. +The fields describe which parts of the the data are visible and how they behave (how to edit them, validating them, etc.). See "Fields API" as a reference for every property. + +Example: + +```js +const fields = [ + { + id: 'title', + type: 'text', + label: 'Title', + }, + { + id: 'date', + type: 'datetime', + label: 'Date', + }, + { + id: 'author', + type: 'text' + label: 'Author', + elements: [ + { value: 1, label: 'Admin' }, + { value: 2, label: 'User' }, + ], + }, +]; +``` #### `form`: `Object[]` @@ -497,6 +480,383 @@ Parameters: Returns a boolean indicating if the item is valid (true) or not (false). +## Fields API + +### `id` + +The unique identifier of the field. + +- Type: `string`. +- Required. + +Example: + +```js +{ id: 'field_id' } +``` + +### `type` + +Field type. One of `text`, `integer`, `datetime`. + +If a field declares a `type`, it gets default implementations for the `sort`, `isValid`, and `Edit` functions. They will overriden if the field provides its own. + +- Type: `string`. +- Optional. + +Example: + +```js +{ type: 'text' } +``` + +### `label` + +The field's name. This will be used across the UI. + +- Type: `string`. +- Optional. +- Defaults to the `id` value. + +Example: + +```js +{ label: 'Title' } +``` + +### `header` + +React component used by the layouts to display the field name — useful to add icons, etc. It's complementary to the `label` property. + +- Type: React component. +- Optional. +- Defaults to the `label` value. +- Props: none. +- Returns a React element that represents the field's name. + +Example: + +```js +{ + header: () => { /* Returns a react element. */ } +} +``` + +### `getValue` + +React component that returns the value of a field. This value is used in sorting the fields, or when filtering. + +- Type: React component. +- Optional. +- Defaults to `item[ id ]`. +- Props: + - `item` value to be processed. +- Returns a value that represents the field. + +Example: + +```js +{ + getValue: ( { item } ) => { /* The field's value. */ }; +} +``` + +### `render` + +React component that renders the field. This is used by the layouts. + +- Type: React component. +- Optional. +- Defaults to `getValue`. +- Props + - `item` value to be processed. +- Returns a React element that represents the field's value. + +Example: + +```js +{ + render: ( { item} ) => { /* React element to be displayed. */ } +} +``` + +### `Edit` + +React component that renders the control to edit the field. + +- Type: React component | `string`. If it's a string, it needs to be one of `text`, `integer`, `datetime`, `radio`, `select`. +- Required by DataForm. Optional if the field provided a `type`. +- Props: + - `data`: the item to be processed + - `field`: the field definition + - `onChange`: the callback with the updates + - `hideLabelFromVision`: boolean representing if the label should be hidden +- Returns a React element to edit the field's value. + +Example: + +```js +// A custom control defined by the field. +{ + Edit: ( { + data, + field, + onChange, + hideLabelFromVision + } ) => { + const value = field.getValue( { item: data } ); + + return ( + + ); + } +} + +// Use one of the core controls. +{ + Edit: 'radio' +} + +// Edit is optional when field's type is present. +// The field will use the default Edit function for text. +{ + type: 'text' +} + +// Edit can be provided even if field's type is present. +// The field will use its own custom control. +{ + type: 'text', + Edit: 'radio' +} +``` + +### `sort` + +Function to sort the records. + +- Type: `function`. +- Optional. +- Args + - `a`: the first item to compare + - `b`: the second item to compare + - `direction`: either `asc` (ascending) or `desc` (descending) +- Returns a number where: + - a negative value indicates that `a` should come before `b` + - a positive value indicates that `a` should come after `b` + - 0 indicates that `a` and `b` are considered equal + +Example: + +```js +// A custom sort function defined by the field. +{ + sort: ( a, b, direction ) => { + return direction === 'asc' + ? a.localeCompare( b ) + : b.localeCompare( a ); + } +} +``` + +```js +// If field type is provided, +// the field gets a default sort function. +{ + type: 'number' +} +``` + +```js +// Even if a field type is provided, +// fields can override the default sort function assigned for that type. +{ + type: 'number' + sort: ( a, b, direction ) => { /* Custom sort */ } +} +``` + +### `isValid` + +Function to validate a field's value. + +- Type: function. +- Optional. +- Args + - `item`: the data to validate + - `context`: an object containing the following props: + - `elements`: the elements defined by the field +- Returns a boolean, indicating if the field is valid or not. + +Example: + +```js +// Custom isValid function. +{ + isValid: ( item, context ) => { + return !! item; + } +} +``` + +```js +// If the field defines a type, +// it'll get a default isValid function for the type. +{ + type: 'number', +} +``` + +```js +// Even if the field provides a type, +// the field can override the default isValid function. +{ + type: 'number', + isValid: ( item, context ) => { /* Custom function. */ } +} +``` + +### `isVisible` + +Function that indicates if the field should be visible. + +- Type: `function`. +- Optional. +- Args + - `item`: the data to be processed +- Returns a `boolean` indicating if the field should be visible (`true`) or not (`false`). + +Example: + +```js +// Custom isVisible function. +{ + isVisible: ( item ) => { /* Custom implementation. */ } +} +``` + +### `enableSorting` + +Boolean indicating if the field is sortable. + +- Type: `boolean`. +- Optional. +- Defaults to `true`. + +Example: + +```js +{ enableSorting: true } +``` + +### `enableHiding` + +Boolean indicating if the field can be hidden. + +- Type: `boolean`. +- Optional. +- Defaults to `true`. + +Example: + +```js +{ enableHiding: true } +``` + +### `enableGlobalSearch` + +Boolean indicating if the field is searchable. + +- Type: `boolean`. +- Optional. +- Defaults to `false`. + +Example: + +```js +{ enableGlobalSearch: true } +``` + +### `elements` + +List of valid values for a field. If provided, it creates a DataViews' filter for the field. DataForm's edit control will use these values as well (see `Edit` field property). + +- Type: `array` of objects. +- Optional. +- Each object can have the following properties: + - `value`: required, the value to match against the field's value. + - `label`: required, the name to display to users. + - `description`: optional, a longer description of the item. + +Example: + +```js +{ + elements: [ + { value: '1', label: 'Product A' }, + { value: '2', label: 'Product B' }, + { value: '3', label: 'Product C' }, + { value: '4', label: 'Product D' }, + ] +} +``` + +### `filterBy` + +Configuration of the filters. + +- Type: `object`. +- Optional. +- Properties: + - `operators`: the list of operators supported by the field. See "operators" below. By default, a filter will support the `isAny` and `isNone` multi-selection operators. + - `isPrimary`: boolean, optional. Indicates if the filter is primary. A primary filter is always visible and is not listed in the "Add filter" component, except for the list layout where it behaves like a secondary filter. + +Operators: + +| Operator | Selection | Description | Example | +| ---------- | -------------- | ----------------------------------------------------------------------- | -------------------------------------------------- | +| `is` | Single item | `EQUAL TO`. The item's field is equal to a single value. | Author is Admin | +| `isNot` | Single item | `NOT EQUAL TO`. The item's field is not equal to a single value. | Author is not Admin | +| `isAny` | Multiple items | `OR`. The item's field is present in a list of values. | Author is any: Admin, Editor | +| `isNone` | Multiple items | `NOT OR`. The item's field is not present in a list of values. | Author is none: Admin, Editor | +| `isAll` | Multiple items | `AND`. The item's field has all of the values in the list. | Category is all: Book, Review, Science Fiction | +| `isNotAll` | Multiple items | `NOT AND`. The item's field doesn't have all of the values in the list. | Category is not all: Book, Review, Science Fiction | + +`is` and `isNot` are single-selection operators, while `isAny`, `isNone`, `isAll`, and `isNotALl` are multi-selection. By default, a filter with no operators declared will support the `isAny` and `isNone` multi-selection operators. A filter cannot mix single-selection & multi-selection operators; if a single-selection operator is present in the list of valid operators, the multi-selection ones will be discarded and the filter won't allow selecting more than one item. + +Example: + +```js +// Set a filter as primary. +{ + filterBy: { + isPrimary: true + } +} +``` + +```js +// Configure a filter as single-selection. +{ + filterBy: { + operators: [ `is`, `isNot` ] + } +} +``` + +```js +// Configure a filter as multi-selection with all the options. +{ + filterBy: { + operators: [ `isAny`, `isNone`, `isAll`, `isNotAll` ] + } +} +``` + ## Contributing to this package This is an individual package that's part of the Gutenberg project. The project is organized as a monorepo. It's made up of multiple self-contained software packages, each with a specific purpose. The packages in this monorepo are published to [npm](https://www.npmjs.com/) and used by [WordPress](https://make.wordpress.org/core/) as well as other software projects. From 058354964253523d67f48318cf3eace66dd3de92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9?= <583546+oandregal@users.noreply.github.com> Date: Tue, 5 Nov 2024 18:12:49 +0100 Subject: [PATCH 07/72] Documentation: improve readability of DataViews docs (#66766) --- packages/dataviews/README.md | 60 ++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/packages/dataviews/README.md b/packages/dataviews/README.md index fe35ae8f0aaa9a..ff20386862929e 100644 --- a/packages/dataviews/README.md +++ b/packages/dataviews/README.md @@ -1,9 +1,9 @@ # The `@wordpress/dataviews` package -The DataViews package offers two components to work with a given dataset: +The DataViews package offers two React components and a few utilites to work with a list of data: -- `DataViews`: allows rendering a dataset using different types of layouts (table, grid, list) and interaction capabilities (search, filters, sorting, etc.). -- `DataForm`: allows editing the items from the same dataset. +- `DataViews`: to render the dataset using different types of layouts (table, grid, list) and interaction capabilities (search, filters, sorting, etc.). +- `DataForm`: to edit the items of the dataset. ## Installation @@ -15,15 +15,19 @@ npm install @wordpress/dataviews --save ## `DataViews` + + ### Usage -The component is data agnostic, it just requires the data to be an array of objects with an unique identifier — it can work with data coming from a static (e.g.: JSON file) or dynamic source (e.g.: HTTP Request). Consumers are responsible to query the data source appropiately: +The `DataViews` component receives data and some other configuration to render the dataset. It'll call the `onChangeView` callback every time the user has interacted with the dataset in some way (sorted, filtered, changed layout, etc.): ![DataViews flow](https://developer.wordpress.org/files/2024/09/368600071-20aa078f-7c3d-406d-8dd0-8b764addd22a.png "DataViews flow") +Example: + ```jsx const Example = () => { - // Declare data, fields, etc. + const onChangeView = () => { /* React to user changes. */ } return ( { }; ``` - ### Properties #### `data`: `Object[]` -The dataset to work with, represented as a one-dimensional array. +A one-dimensional array of objects. Example: @@ -63,11 +66,28 @@ const data = [ ]; ``` -By default, dataviews would use each record's `id` as an unique identifier. If the records don't have a `id` property that identify them uniquely, they consumer needs to provide a `getItemId` function that returns an unique identifier for the record. +The data can come from anywhere, from a static JSON file to a dynamic source like a HTTP Request. It's the consumer's responsiblity to query the data source appropiately and update the dataset based on the user's choices for sorting, filtering, etc. + +Each record should have an `id` that identifies them uniquely. If they don't, the consumer should provide the `getItemId` property to `DataViews`: a function that returns an unique identifier for the record. + +#### `getItemId`: `function` + +Function that receives an item and returns an unique identifier for it. + +It's optional. The field will get a default implementation by `DataViews` that returns the value of the `item[ id ]`. + +Example: + +```js +// Custom getItemId function. +{ + getItemId={ ( item ) => item.name ?? item.id } +} +``` #### `fields`: `Object[]` -The fields describe the visible items for each record in the dataset and how they behave (how to sort them, display them, etc.). See "Fields API" as a reference for every property. +The fields describe the visible items for each record in the dataset and how they behave (how to sort them, display them, etc.). See "Fields API" for a description of every property. Example: @@ -304,12 +324,6 @@ Whether the search input is enabled. `true` by default. What text to show in the search input. "Search" by default. -#### `getItemId`: `function` - -Function that receives an item and returns an unique identifier for it. - -By default, dataviews would use each record's `id` as an unique identifier. If the records don't have a `id` property that identify them uniquely, they consumer needs to provide a `getItemId` function that returns an unique identifier for the record. - #### `isLoading`: `boolean` Whether the data is loading. `false` by default. @@ -350,6 +364,8 @@ React component to be rendered next to the view config button. ## `DataForm` + + ### Usage ```jsx @@ -367,19 +383,17 @@ const Example = () => { } ``` - - ### Properties #### `data`: `Object` A single item to be edited. -This could be thought as as a single record coming from the `data` property of `DataViews` — though it doesn't need to be; it can be, for example, a mix of records if you support bulk editing. +It can be think of as a single record coming from the `data` property of `DataViews` — though it doesn't need to be. It can be totally separated or a mix of records if your app supports bulk editing. #### `fields`: `Object[]` -The fields describe which parts of the the data are visible and how they behave (how to edit them, validating them, etc.). See "Fields API" as a reference for every property. +The fields describe which parts of the data are visible and how they behave (how to edit them, validate them, etc.). See "Fields API" for a description of every property. Example: @@ -593,7 +607,7 @@ React component that renders the control to edit the field. - `hideLabelFromVision`: boolean representing if the label should be hidden - Returns a React element to edit the field's value. -Example: +Example: ```js // A custom control defined by the field. @@ -615,18 +629,24 @@ Example: ); } } +``` +```js // Use one of the core controls. { Edit: 'radio' } +``` +```js // Edit is optional when field's type is present. // The field will use the default Edit function for text. { type: 'text' } +``` +```js // Edit can be provided even if field's type is present. // The field will use its own custom control. { From c41b2194d01e4f34ef101bf244c62c5265b99345 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 5 Nov 2024 18:39:00 +0100 Subject: [PATCH 08/72] Zoom-out: Move default background to the iframe component (#66284) Co-authored-by: youknowriad Co-authored-by: ntsekouras Co-authored-by: ellatrix Co-authored-by: jasmussen Co-authored-by: jameskoster --- .../src/components/block-canvas/style.scss | 2 +- .../src/components/editor-canvas-container/style.scss | 1 - .../editor/src/components/visual-editor/style.scss | 11 +++++++++++ storybook/stories/playground/zoom-out/index.js | 2 +- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/block-editor/src/components/block-canvas/style.scss b/packages/block-editor/src/components/block-canvas/style.scss index 0b37b4dd1447e2..8f6064de0b615c 100644 --- a/packages/block-editor/src/components/block-canvas/style.scss +++ b/packages/block-editor/src/components/block-canvas/style.scss @@ -3,7 +3,7 @@ iframe[name="editor-canvas"] { width: 100%; height: 100%; display: block; - background-color: transparent; // Handles transitions between device previews @include editor-canvas-resize-animation; + background-color: $gray-300; } diff --git a/packages/edit-site/src/components/editor-canvas-container/style.scss b/packages/edit-site/src/components/editor-canvas-container/style.scss index 0bdbc2bbe32355..52ac29da0696f6 100644 --- a/packages/edit-site/src/components/editor-canvas-container/style.scss +++ b/packages/edit-site/src/components/editor-canvas-container/style.scss @@ -1,6 +1,5 @@ .edit-site-editor-canvas-container { height: 100%; - background-color: $gray-300; // Controls height of editor and editor canvas container (style book, global styles revisions previews etc.) iframe { diff --git a/packages/editor/src/components/visual-editor/style.scss b/packages/editor/src/components/visual-editor/style.scss index fae61eda6b8013..63df28f0f1ba5a 100644 --- a/packages/editor/src/components/visual-editor/style.scss +++ b/packages/editor/src/components/visual-editor/style.scss @@ -1,8 +1,19 @@ .editor-visual-editor { position: relative; display: flex; + + // This duplicates the iframe background but it's necessary in some situations + // when the iframe doesn't cover the whole canvas + // like the "focused entities". background-color: $gray-300; + // This overrides the iframe background since it's applied again here + // It also prevents some style glitches if `editor-visual-editor` + // like when hovering the preview in the site editor. + iframe[name="editor-canvas"] { + background-color: transparent; + } + // Centralize the editor horizontally (flex-direction is column). align-items: center; diff --git a/storybook/stories/playground/zoom-out/index.js b/storybook/stories/playground/zoom-out/index.js index 4f2efcf48523e8..8b72a831d710e8 100644 --- a/storybook/stories/playground/zoom-out/index.js +++ b/storybook/stories/playground/zoom-out/index.js @@ -50,7 +50,7 @@ export default function EditorZoomOut( { zoomLevel } ) {
event.stopPropagation() } - style={ { background: '#ddd', border: '1px solid gray' } } + style={ { border: '1px solid gray' } } > Date: Tue, 5 Nov 2024 18:44:39 +0100 Subject: [PATCH 09/72] BlockPatternsList: use the Async component (#66744) Co-authored-by: ellatrix Co-authored-by: mcsf --- package-lock.json | 1 + packages/block-editor/package.json | 1 + .../components/block-patterns-list/README.md | 8 --- .../components/block-patterns-list/index.js | 50 +++++++++---------- .../stories/index.story.js | 11 +--- .../src/components/block-preview/async.js} | 0 .../src/components/block-preview/index.js | 7 ++- .../components/block-toolbar/change-design.js | 6 --- .../block-patterns-explorer/pattern-list.js | 3 -- .../pattern-category-previews.js | 1 - .../inserter/hooks/use-patterns-paging.js | 7 +-- .../src/components/inserter/search-results.js | 6 --- packages/block-editor/tsconfig.json | 1 + .../src/query/edit/pattern-selection-modal.js | 3 -- .../src/template-part/edit/index.js | 3 -- .../src/template-part/edit/selection-modal.js | 3 -- .../src/components/page-patterns/fields.js | 5 +- .../src/components/page-templates/fields.js | 5 +- .../post-template/swap-template-button.js | 3 -- .../components/post-transform-panel/index.js | 3 -- .../components/start-page-options/index.js | 3 -- .../start-template-options/index.js | 3 -- 22 files changed, 38 insertions(+), 95 deletions(-) rename packages/{edit-site/src/components/async/index.js => block-editor/src/components/block-preview/async.js} (100%) diff --git a/package-lock.json b/package-lock.json index 9d5b12e749d123..e76ea379cd1a6e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -53169,6 +53169,7 @@ "@wordpress/keycodes": "*", "@wordpress/notices": "*", "@wordpress/preferences": "*", + "@wordpress/priority-queue": "*", "@wordpress/private-apis": "*", "@wordpress/rich-text": "*", "@wordpress/style-engine": "*", diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json index 26b06474f54b18..b4672bc57690eb 100644 --- a/packages/block-editor/package.json +++ b/packages/block-editor/package.json @@ -60,6 +60,7 @@ "@wordpress/keycodes": "*", "@wordpress/notices": "*", "@wordpress/preferences": "*", + "@wordpress/priority-queue": "*", "@wordpress/private-apis": "*", "@wordpress/rich-text": "*", "@wordpress/style-engine": "*", diff --git a/packages/block-editor/src/components/block-patterns-list/README.md b/packages/block-editor/src/components/block-patterns-list/README.md index f63ea449059572..18e7ead5d1805a 100644 --- a/packages/block-editor/src/components/block-patterns-list/README.md +++ b/packages/block-editor/src/components/block-patterns-list/README.md @@ -18,7 +18,6 @@ import { BlockPatternsList } from '@wordpress/block-editor'; const MyBlockPatternsList = () => ( ); @@ -33,13 +32,6 @@ An array of block patterns that can be shown in the block patterns list. - Type: `Array` - Required: Yes -#### shownPatterns - -An array of shown block patterns objects. - -- Type: `Array` -- Required: Yes - #### onClickPattern The performed event after a click on a block pattern. In most cases, the pattern is inserted in the block editor. diff --git a/packages/block-editor/src/components/block-patterns-list/index.js b/packages/block-editor/src/components/block-patterns-list/index.js index 2609cc2db97a13..741a92ddf10dfa 100644 --- a/packages/block-editor/src/components/block-patterns-list/index.js +++ b/packages/block-editor/src/components/block-patterns-list/index.js @@ -134,10 +134,14 @@ function BlockPattern( { } } onMouseLeave={ () => onHover?.( null ) } > - + } + > + + { showTitle && ( - shownPatterns.includes( pattern ) - )?.name; + const firstCompositeItemId = blockPatterns[ 0 ]?.name; setActiveCompositeId( firstCompositeItemId ); - }, [ shownPatterns, blockPatterns ] ); + }, [ blockPatterns ] ); return ( - { blockPatterns.map( ( pattern ) => { - const isShown = shownPatterns.includes( pattern ); - return isShown ? ( - - ) : ( - - ); - } ) } + { blockPatterns.map( ( pattern ) => ( + + ) ) } { pagingProps && } ); diff --git a/packages/block-editor/src/components/block-patterns-list/stories/index.story.js b/packages/block-editor/src/components/block-patterns-list/stories/index.story.js index 9eb393ea13e762..0ebb4520d98fd4 100644 --- a/packages/block-editor/src/components/block-patterns-list/stories/index.story.js +++ b/packages/block-editor/src/components/block-patterns-list/stories/index.story.js @@ -3,11 +3,6 @@ */ import blockLibraryStyles from '!!raw-loader!../../../../../block-library/build-style/style.css'; -/** - * WordPress dependencies - */ -import { useAsyncList } from '@wordpress/compose'; - /** * Internal dependencies */ @@ -26,13 +21,9 @@ export default { export const Default = { render: function Template( props ) { - const shownPatterns = useAsyncList( props.blockPatterns ); return ( - + ); }, diff --git a/packages/edit-site/src/components/async/index.js b/packages/block-editor/src/components/block-preview/async.js similarity index 100% rename from packages/edit-site/src/components/async/index.js rename to packages/block-editor/src/components/block-preview/async.js diff --git a/packages/block-editor/src/components/block-preview/index.js b/packages/block-editor/src/components/block-preview/index.js index 9eef0f1dc2abd5..62b137ff37194b 100644 --- a/packages/block-editor/src/components/block-preview/index.js +++ b/packages/block-editor/src/components/block-preview/index.js @@ -19,6 +19,7 @@ import AutoHeightBlockPreview from './auto'; import EditorStyles from '../editor-styles'; import { store as blockEditorStore } from '../../store'; import { BlockListItems } from '../block-list'; +import { Async } from './async'; const EMPTY_ADDITIONAL_STYLES = []; @@ -86,6 +87,10 @@ export function BlockPreview( { ); } +const MemoizedBlockPreview = memo( BlockPreview ); + +MemoizedBlockPreview.Async = Async; + /** * BlockPreview renders a preview of a block or array of blocks. * @@ -97,7 +102,7 @@ export function BlockPreview( { * * @return {Component} The component to be rendered. */ -export default memo( BlockPreview ); +export default MemoizedBlockPreview; /** * This hook is used to lightly mark an element as a block preview wrapper diff --git a/packages/block-editor/src/components/block-toolbar/change-design.js b/packages/block-editor/src/components/block-toolbar/change-design.js index ecfeff6cb1ed3e..9da1affe4273cc 100644 --- a/packages/block-editor/src/components/block-toolbar/change-design.js +++ b/packages/block-editor/src/components/block-toolbar/change-design.js @@ -10,7 +10,6 @@ import { import { __ } from '@wordpress/i18n'; import { cloneBlock } from '@wordpress/blocks'; import { useMemo } from '@wordpress/element'; -import { useAsyncList } from '@wordpress/compose'; import { useSelect, useDispatch } from '@wordpress/data'; /** @@ -81,10 +80,6 @@ export default function ChangeDesign( { clientId } ) { .slice( 0, MAX_PATTERNS_TO_SHOW ); }, [ categories, currentPatternName, patterns ] ); - const currentShownPatterns = useAsyncList( - sameCategoryPatternsWithSingleWrapper - ); - if ( sameCategoryPatternsWithSingleWrapper.length < 2 ) { return null; } @@ -121,7 +116,6 @@ export default function ChangeDesign( { clientId } ) { paddingSize="none" > { const scrollContainer = getScrollContainer( @@ -68,7 +64,6 @@ export default function usePatternsPaging( return { totalItems, categoryPatterns, - categoryPatternsAsyncList, numPages, changePage, currentPage, diff --git a/packages/block-editor/src/components/inserter/search-results.js b/packages/block-editor/src/components/inserter/search-results.js index 9c001823745e6c..5a5725a3bb08cd 100644 --- a/packages/block-editor/src/components/inserter/search-results.js +++ b/packages/block-editor/src/components/inserter/search-results.js @@ -159,11 +159,6 @@ function InserterSearchResults( { const currentShownBlockTypes = useAsyncList( filteredBlockTypes, { step: INITIAL_INSERTER_RESULTS, } ); - const currentShownPatterns = useAsyncList( - currentShownBlockTypes.length === filteredBlockTypes.length - ? filteredBlockPatterns - : EMPTY_ARRAY - ); const hasItems = filteredBlockTypes.length > 0 || filteredBlockPatterns.length > 0; @@ -190,7 +185,6 @@ function InserterSearchResults( { >
{ return searchPatterns( blockPatterns, searchValue ); }, [ blockPatterns, searchValue ] ); - const shownBlockPatterns = useAsyncList( filteredBlockPatterns ); return ( diff --git a/packages/block-library/src/template-part/edit/index.js b/packages/block-library/src/template-part/edit/index.js index ae941c977e14fd..a318fd285cdc33 100644 --- a/packages/block-library/src/template-part/edit/index.js +++ b/packages/block-library/src/template-part/edit/index.js @@ -21,7 +21,6 @@ import { MenuItem, ToolbarButton, } from '@wordpress/components'; -import { useAsyncList } from '@wordpress/compose'; import { __, sprintf } from '@wordpress/i18n'; import { store as coreStore } from '@wordpress/core-data'; import { useState } from '@wordpress/element'; @@ -85,7 +84,6 @@ function TemplatesList( { area, clientId, isEntityAvailable, onSelect } ) { isEntityAvailable && !! blockPatterns.length && ( area === 'header' || area === 'footer' ); - const shownTemplates = useAsyncList( blockPatterns ); if ( ! canReplace ) { return null; @@ -96,7 +94,6 @@ function TemplatesList( { area, clientId, isEntityAvailable, onSelect } ) { diff --git a/packages/block-library/src/template-part/edit/selection-modal.js b/packages/block-library/src/template-part/edit/selection-modal.js index 5fa5d9786d8c96..71093306b93b2f 100644 --- a/packages/block-library/src/template-part/edit/selection-modal.js +++ b/packages/block-library/src/template-part/edit/selection-modal.js @@ -5,7 +5,6 @@ import { useMemo, useState } from '@wordpress/element'; import { __, sprintf } from '@wordpress/i18n'; import { store as noticesStore } from '@wordpress/notices'; import { useDispatch } from '@wordpress/data'; -import { useAsyncList } from '@wordpress/compose'; import { __experimentalBlockPatternsList as BlockPatternsList } from '@wordpress/block-editor'; import { SearchControl, @@ -43,7 +42,6 @@ export default function TemplatePartSelectionModal( { return searchPatterns( partsAsPatterns, searchValue ); }, [ templateParts, searchValue ] ); - const shownTemplateParts = useAsyncList( filteredTemplateParts ); const blockPatterns = useAlternativeBlockPatterns( area, clientId ); const filteredBlockPatterns = useMemo( () => { return searchPatterns( blockPatterns, searchValue ); @@ -89,7 +87,6 @@ export default function TemplatePartSelectionModal( {

{ __( 'Existing template parts' ) }

{ onTemplatePartSelect( pattern.templatePart ); } } diff --git a/packages/edit-site/src/components/page-patterns/fields.js b/packages/edit-site/src/components/page-patterns/fields.js index 74433b3de72f0e..0ad47e90c20402 100644 --- a/packages/edit-site/src/components/page-patterns/fields.js +++ b/packages/edit-site/src/components/page-patterns/fields.js @@ -25,7 +25,6 @@ import { decodeEntities } from '@wordpress/html-entities'; /** * Internal dependencies */ -import { Async } from '../async'; import { PATTERN_TYPES, TEMPLATE_PART_POST_TYPE, @@ -88,12 +87,12 @@ function PreviewField( { item } ) { { isEmpty && isTemplatePart && __( 'Empty template part' ) } { isEmpty && ! isTemplatePart && __( 'Empty pattern' ) } { ! isEmpty && ( - + - + ) } { !! description && ( diff --git a/packages/edit-site/src/components/page-templates/fields.js b/packages/edit-site/src/components/page-templates/fields.js index d26f1906a10664..69e0596bf49d47 100644 --- a/packages/edit-site/src/components/page-templates/fields.js +++ b/packages/edit-site/src/components/page-templates/fields.js @@ -20,7 +20,6 @@ import { EditorProvider } from '@wordpress/editor'; /** * Internal dependencies */ -import { Async } from '../async'; import { default as Link, useLink } from '../routes/link'; import { useAddedBy } from './hooks'; @@ -63,9 +62,9 @@ function PreviewField( { item } ) { > { isEmpty && __( 'Empty template' ) } { ! isEmpty && ( - + - + ) }
diff --git a/packages/editor/src/components/post-template/swap-template-button.js b/packages/editor/src/components/post-template/swap-template-button.js index 5eed287f7f1709..bdda349398406b 100644 --- a/packages/editor/src/components/post-template/swap-template-button.js +++ b/packages/editor/src/components/post-template/swap-template-button.js @@ -9,7 +9,6 @@ import { __ } from '@wordpress/i18n'; import { useDispatch } from '@wordpress/data'; import { store as coreStore } from '@wordpress/core-data'; import { parse } from '@wordpress/blocks'; -import { useAsyncList } from '@wordpress/compose'; /** * Internal dependencies @@ -71,12 +70,10 @@ function TemplatesList( { postType, onSelect } ) { } ) ), [ availableTemplates ] ); - const shownTemplates = useAsyncList( templatesAsPatterns ); return ( ); diff --git a/packages/editor/src/components/post-transform-panel/index.js b/packages/editor/src/components/post-transform-panel/index.js index 08139c2c3c6645..88ca6dc3965a11 100644 --- a/packages/editor/src/components/post-transform-panel/index.js +++ b/packages/editor/src/components/post-transform-panel/index.js @@ -5,7 +5,6 @@ import { useSelect, useDispatch } from '@wordpress/data'; import { store as coreStore } from '@wordpress/core-data'; import { PanelBody } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; -import { useAsyncList } from '@wordpress/compose'; import { __experimentalBlockPatternsList as BlockPatternsList } from '@wordpress/block-editor'; import { serialize } from '@wordpress/blocks'; @@ -20,7 +19,6 @@ import { } from '../../store/constants'; function TemplatesList( { availableTemplates, onSelect } ) { - const shownTemplates = useAsyncList( availableTemplates ); if ( ! availableTemplates || availableTemplates?.length === 0 ) { return null; } @@ -29,7 +27,6 @@ function TemplatesList( { availableTemplates, onSelect } ) { diff --git a/packages/editor/src/components/start-page-options/index.js b/packages/editor/src/components/start-page-options/index.js index 07fee67fbed19b..783a4a224fa378 100644 --- a/packages/editor/src/components/start-page-options/index.js +++ b/packages/editor/src/components/start-page-options/index.js @@ -9,7 +9,6 @@ import { __experimentalBlockPatternsList as BlockPatternsList, } from '@wordpress/block-editor'; import { useSelect, useDispatch } from '@wordpress/data'; -import { useAsyncList } from '@wordpress/compose'; import { store as coreStore } from '@wordpress/core-data'; import { __unstableSerializeAndClean } from '@wordpress/blocks'; import { store as preferencesStore } from '@wordpress/preferences'; @@ -66,7 +65,6 @@ export function useStartPatterns() { } function PatternSelection( { blockPatterns, onChoosePattern } ) { - const shownBlockPatterns = useAsyncList( blockPatterns ); const { editEntityRecord } = useDispatch( coreStore ); const { postType, postId } = useSelect( ( select ) => { const { getCurrentPostType, getCurrentPostId } = select( editorStore ); @@ -79,7 +77,6 @@ function PatternSelection( { blockPatterns, onChoosePattern } ) { return ( { editEntityRecord( 'postType', postType, postId, { blocks, diff --git a/packages/editor/src/components/start-template-options/index.js b/packages/editor/src/components/start-template-options/index.js index 3651c5c029a2c7..8d3910341aff43 100644 --- a/packages/editor/src/components/start-template-options/index.js +++ b/packages/editor/src/components/start-template-options/index.js @@ -6,7 +6,6 @@ import { __ } from '@wordpress/i18n'; import { useState, useMemo, useEffect } from '@wordpress/element'; import { __experimentalBlockPatternsList as BlockPatternsList } from '@wordpress/block-editor'; import { useSelect } from '@wordpress/data'; -import { useAsyncList } from '@wordpress/compose'; import { parse } from '@wordpress/blocks'; import { store as coreStore, useEntityBlockEditor } from '@wordpress/core-data'; @@ -111,11 +110,9 @@ function useStartPatterns( fallbackContent ) { function PatternSelection( { fallbackContent, onChoosePattern, postType } ) { const [ , , onChange ] = useEntityBlockEditor( 'postType', postType ); const blockPatterns = useStartPatterns( fallbackContent ); - const shownBlockPatterns = useAsyncList( blockPatterns ); return ( { onChange( blocks, { selection: undefined } ); onChoosePattern(); From 177c6a2fc12d818dc864cc95ea1875f4dbc1ff62 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Tue, 5 Nov 2024 21:45:28 +0400 Subject: [PATCH 10/72] Block Editor: Fix ESLint warning for 'useBlockTypesState' hook (#66757) Co-authored-by: Mamaduka Co-authored-by: up1512001 --- .../src/components/inserter/hooks/use-block-types-state.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/inserter/hooks/use-block-types-state.js b/packages/block-editor/src/components/inserter/hooks/use-block-types-state.js index 6f11060c75c494..2ff8f1a1dc23c0 100644 --- a/packages/block-editor/src/components/inserter/hooks/use-block-types-state.js +++ b/packages/block-editor/src/components/inserter/hooks/use-block-types-state.js @@ -94,7 +94,12 @@ const useBlockTypesState = ( rootClientId, onInsert, isQuick ) => { destinationClientId ); }, - [ onInsert, getClosestAllowedInsertionPoint, rootClientId ] + [ + getClosestAllowedInsertionPoint, + rootClientId, + onInsert, + createErrorNotice, + ] ); return [ items, categories, collections, onSelectItem ]; From ae29450a148520524057ca28841809c4e9054f55 Mon Sep 17 00:00:00 2001 From: Ella <4710635+ellatrix@users.noreply.github.com> Date: Tue, 5 Nov 2024 18:57:14 +0100 Subject: [PATCH 11/72] Perf metrics: update select and other metrics to use non-empty paragraphs (#66762) Co-authored-by: ellatrix Co-authored-by: Mamaduka Co-authored-by: youknowriad --- test/performance/fixtures/perf-utils.ts | 4 ++-- test/performance/specs/post-editor.spec.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/performance/fixtures/perf-utils.ts b/test/performance/fixtures/perf-utils.ts index 83213a59520dd5..592e8194852e3b 100644 --- a/test/performance/fixtures/perf-utils.ts +++ b/test/performance/fixtures/perf-utils.ts @@ -150,7 +150,7 @@ export class PerfUtils { } /** - * Generates and loads a 1000 empty paragraphs into the editor canvas. + * Generates and loads a 1000 paragraphs into the editor canvas. */ async load1000Paragraphs() { await this.page.waitForFunction( @@ -161,7 +161,7 @@ export class PerfUtils { const { createBlock } = window.wp.blocks; const { dispatch } = window.wp.data; const blocks = Array.from( { length: 1000 } ).map( () => - createBlock( 'core/paragraph' ) + createBlock( 'core/paragraph', { content: 'paragraph' } ) ); dispatch( 'core/block-editor' ).resetBlocks( blocks ); } ); diff --git a/test/performance/specs/post-editor.spec.js b/test/performance/specs/post-editor.spec.js index becbf375eff2ac..85d334749f6f51 100644 --- a/test/performance/specs/post-editor.spec.js +++ b/test/performance/specs/post-editor.spec.js @@ -270,7 +270,7 @@ test.describe( 'Post Editor Performance', () => { const canvas = await perfUtils.getCanvas(); const paragraphs = canvas.getByRole( 'document', { - name: /Empty block/i, + name: /Block: Paragraph/i, } ); const samples = 10; From 7f49b39e1e3e2eaf274b6fb03ae22508cc583d31 Mon Sep 17 00:00:00 2001 From: Ella <4710635+ellatrix@users.noreply.github.com> Date: Tue, 5 Nov 2024 19:38:09 +0100 Subject: [PATCH 12/72] Patterns: receive intermediate responses while unbound request is resolving (#66713) Co-authored-by: ellatrix Co-authored-by: youknowriad Co-authored-by: mcsf Co-authored-by: sathyapulse --- packages/core-data/src/private-apis.js | 2 + packages/core-data/src/resolvers.js | 57 ++++++++++++++++--- packages/core-data/src/utils/index.js | 1 + .../src/utils/receive-intermediate-results.js | 3 + .../provider/use-block-editor-settings.js | 14 ++--- 5 files changed, 61 insertions(+), 16 deletions(-) create mode 100644 packages/core-data/src/utils/receive-intermediate-results.js diff --git a/packages/core-data/src/private-apis.js b/packages/core-data/src/private-apis.js index 443db97957285d..e9fcf36f7e6090 100644 --- a/packages/core-data/src/private-apis.js +++ b/packages/core-data/src/private-apis.js @@ -2,9 +2,11 @@ * Internal dependencies */ import { useEntityRecordsWithPermissions } from './hooks/use-entity-records'; +import { RECEIVE_INTERMEDIATE_RESULTS } from './utils'; import { lock } from './lock-unlock'; export const privateApis = {}; lock( privateApis, { useEntityRecordsWithPermissions, + RECEIVE_INTERMEDIATE_RESULTS, } ); diff --git a/packages/core-data/src/resolvers.js b/packages/core-data/src/resolvers.js index ae0c7f456e533d..a35403c0493460 100644 --- a/packages/core-data/src/resolvers.js +++ b/packages/core-data/src/resolvers.js @@ -21,6 +21,7 @@ import { getUserPermissionCacheKey, getUserPermissionsFromAllowHeader, ALLOWED_RESOURCE_ACTIONS, + RECEIVE_INTERMEDIATE_RESULTS, } from './utils'; import { getSyncProvider } from './sync'; import { fetchBlockPatterns } from './fetch'; @@ -245,6 +246,14 @@ export const getEntityRecords = { exclusive: false } ); + const key = entityConfig.key || DEFAULT_ENTITY_KEY; + + function getResolutionsArgs( records ) { + return records + .filter( ( record ) => record?.[ key ] ) + .map( ( record ) => [ kind, name, record[ key ] ] ); + } + try { if ( query._fields ) { // If requesting specific fields, items and query association to said @@ -267,7 +276,8 @@ export const getEntityRecords = ...query, } ); - let records, meta; + let records = [], + meta; if ( entityConfig.supportsPagination && query.per_page !== -1 ) { const response = await apiFetch( { path, parse: false } ); records = Object.values( await response.json() ); @@ -279,6 +289,44 @@ export const getEntityRecords = response.headers.get( 'X-WP-TotalPages' ) ), }; + } else if ( + query.per_page === -1 && + query[ RECEIVE_INTERMEDIATE_RESULTS ] === true + ) { + let page = 1; + let totalPages; + + do { + const response = await apiFetch( { + path: addQueryArgs( path, { page, per_page: 100 } ), + parse: false, + } ); + const pageRecords = Object.values( await response.json() ); + + totalPages = parseInt( + response.headers.get( 'X-WP-TotalPages' ) + ); + + records.push( ...pageRecords ); + registry.batch( () => { + dispatch.receiveEntityRecords( + kind, + name, + records, + query + ); + dispatch.finishResolutions( + 'getEntityRecord', + getResolutionsArgs( pageRecords ) + ); + } ); + page++; + } while ( page <= totalPages ); + + meta = { + totalItems: records.length, + totalPages: 1, + }; } else { records = Object.values( await apiFetch( { path } ) ); meta = { @@ -318,11 +366,6 @@ export const getEntityRecords = // See https://github.com/WordPress/gutenberg/pull/26575 // See https://github.com/WordPress/gutenberg/pull/64504 if ( ! query?._fields && ! query.context ) { - const key = entityConfig.key || DEFAULT_ENTITY_KEY; - const resolutionsArgs = records - .filter( ( record ) => record?.[ key ] ) - .map( ( record ) => [ kind, name, record[ key ] ] ); - const targetHints = records .filter( ( record ) => record?.[ key ] ) .map( ( record ) => ( { @@ -356,7 +399,7 @@ export const getEntityRecords = ); dispatch.finishResolutions( 'getEntityRecord', - resolutionsArgs + getResolutionsArgs( records ) ); dispatch.finishResolutions( 'canUser', diff --git a/packages/core-data/src/utils/index.js b/packages/core-data/src/utils/index.js index 189635647779e5..db10359bda07dd 100644 --- a/packages/core-data/src/utils/index.js +++ b/packages/core-data/src/utils/index.js @@ -14,3 +14,4 @@ export { getUserPermissionsFromAllowHeader, ALLOWED_RESOURCE_ACTIONS, } from './user-permissions'; +export { RECEIVE_INTERMEDIATE_RESULTS } from './receive-intermediate-results'; diff --git a/packages/core-data/src/utils/receive-intermediate-results.js b/packages/core-data/src/utils/receive-intermediate-results.js new file mode 100644 index 00000000000000..53d2295b28b390 --- /dev/null +++ b/packages/core-data/src/utils/receive-intermediate-results.js @@ -0,0 +1,3 @@ +export const RECEIVE_INTERMEDIATE_RESULTS = Symbol( + 'RECEIVE_INTERMEDIATE_RESULTS' +); diff --git a/packages/editor/src/components/provider/use-block-editor-settings.js b/packages/editor/src/components/provider/use-block-editor-settings.js index 170ec96c107288..e90be6487a0ea9 100644 --- a/packages/editor/src/components/provider/use-block-editor-settings.js +++ b/packages/editor/src/components/provider/use-block-editor-settings.js @@ -7,6 +7,7 @@ import { store as coreStore, __experimentalFetchLinkSuggestions as fetchLinkSuggestions, __experimentalFetchUrlData as fetchUrlData, + privateApis as coreDataPrivateApis, } from '@wordpress/core-data'; import { __ } from '@wordpress/i18n'; import { store as preferencesStore } from '@wordpress/preferences'; @@ -29,17 +30,12 @@ import { useGlobalStylesContext } from '../global-styles-provider'; const EMPTY_OBJECT = {}; function __experimentalReusableBlocksSelect( select ) { - const { getEntityRecords, hasFinishedResolution } = select( coreStore ); - const reusableBlocks = getEntityRecords( 'postType', 'wp_block', { + const { RECEIVE_INTERMEDIATE_RESULTS } = unlock( coreDataPrivateApis ); + const { getEntityRecords } = select( coreStore ); + return getEntityRecords( 'postType', 'wp_block', { per_page: -1, + [ RECEIVE_INTERMEDIATE_RESULTS ]: true, } ); - return hasFinishedResolution( 'getEntityRecords', [ - 'postType', - 'wp_block', - { per_page: -1 }, - ] ) - ? reusableBlocks - : undefined; } const BLOCK_EDITOR_SETTINGS = [ From 9bd184da7b547d1b46b2e118bde6f0529c71a2d8 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Wed, 6 Nov 2024 09:00:28 +0400 Subject: [PATCH 13/72] Edit Site: Remove leftover 'priority-queue' dependency (#66773) Co-authored-by: Mamaduka Co-authored-by: up1512001 --- package-lock.json | 1 - packages/edit-site/package.json | 1 - 2 files changed, 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index e76ea379cd1a6e..59fb1310a15e72 100644 --- a/package-lock.json +++ b/package-lock.json @@ -54310,7 +54310,6 @@ "@wordpress/plugins": "*", "@wordpress/preferences": "*", "@wordpress/primitives": "*", - "@wordpress/priority-queue": "*", "@wordpress/private-apis": "*", "@wordpress/reusable-blocks": "*", "@wordpress/router": "*", diff --git a/packages/edit-site/package.json b/packages/edit-site/package.json index 67932bcb06321e..41bf39c1e08332 100644 --- a/packages/edit-site/package.json +++ b/packages/edit-site/package.json @@ -61,7 +61,6 @@ "@wordpress/plugins": "*", "@wordpress/preferences": "*", "@wordpress/primitives": "*", - "@wordpress/priority-queue": "*", "@wordpress/private-apis": "*", "@wordpress/reusable-blocks": "*", "@wordpress/router": "*", From 471e4bdaf6f3b6e5b5ca37f8a00f12424870458c Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Wed, 6 Nov 2024 00:17:16 -0500 Subject: [PATCH 14/72] ci: Address stalling Android E2E test tasks (#66771) * ci: Increase AVD cache key specificity Avoid erroneous cache hits that may result in the CI task hanging indefinitely while terminating the emulator. * Revert "ci: Increase AVD cache key specificity" This reverts commit 2e892a66888295cf3edc1b33c4f4df7792fdbb0f. * ci: Update Android test runner to macOS 13 Attempt to resolve indefinitely hanging Android E2E test tasks. * Revert "ci: Update Android test runner to macOS 13" This reverts commit a4c844daf04dfcd517b293518d65d92063230726. * ci: Disable AVD cache AVD cache disabled as it caused emulator termination to hang indefinitely. Co-authored-by: dcalhoun Co-authored-by: Mamaduka --- .github/workflows/rnmobile-android-runner.yml | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/.github/workflows/rnmobile-android-runner.yml b/.github/workflows/rnmobile-android-runner.yml index a57b857aac6234..65bb55e3f63201 100644 --- a/.github/workflows/rnmobile-android-runner.yml +++ b/.github/workflows/rnmobile-android-runner.yml @@ -49,26 +49,28 @@ jobs: - name: Gradle cache uses: gradle/actions/setup-gradle@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0 - - name: AVD cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 - id: avd-cache - with: - path: | - ~/.android/avd/* - ~/.android/adb* - key: avd-${{ matrix.api-level }} - - - name: Create AVD and generate snapshot for caching - if: steps.avd-cache.outputs.cache-hit != 'true' - uses: reactivecircus/android-emulator-runner@62dbb605bba737720e10b196cb4220d374026a6d # v2.33.0 - with: - api-level: ${{ matrix.api-level }} - force-avd-creation: false - emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none - disable-animations: false - arch: x86_64 - profile: Nexus 6 - script: echo "Generated AVD snapshot for caching." + # AVD cache disabled as it caused emulator termination to hang indefinitely. + # https://github.com/ReactiveCircus/android-emulator-runner/issues/385 + # - name: AVD cache + # uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + # id: avd-cache + # with: + # path: | + # ~/.android/avd/* + # ~/.android/adb* + # key: avd-${{ matrix.api-level }} + # + # - name: Create AVD and generate snapshot for caching + # if: steps.avd-cache.outputs.cache-hit != 'true' + # uses: reactivecircus/android-emulator-runner@62dbb605bba737720e10b196cb4220d374026a6d # v2.33.0 + # with: + # api-level: ${{ matrix.api-level }} + # force-avd-creation: false + # emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none + # disable-animations: false + # arch: x86_64 + # profile: Nexus 6 + # script: echo "Generated AVD snapshot for caching." - name: Run tests uses: reactivecircus/android-emulator-runner@62dbb605bba737720e10b196cb4220d374026a6d # v2.33.0 From 7ff76796b8fd7027a3f53933e1f9e5f2917ea2c8 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Wed, 6 Nov 2024 09:44:02 +0400 Subject: [PATCH 15/72] Cover: Fix media library image selection (#66782) * Cover: Fix media library image selection * Fix sizes for uploaded images Co-authored-by: Mamaduka Co-authored-by: aaronrobertshaw Co-authored-by: richtabor --- .../block-library/src/cover/edit/index.js | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/block-library/src/cover/edit/index.js b/packages/block-library/src/cover/edit/index.js index 0ad87601bfba8b..1202f28cee50cd 100644 --- a/packages/block-library/src/cover/edit/index.js +++ b/packages/block-library/src/cover/edit/index.js @@ -206,12 +206,24 @@ function CoverEdit( { // Try to use the previous selected image size if it's available // otherwise try the default image size or fallback to full size. - if ( sizeSlug && newMedia?.sizes?.[ sizeSlug ] ) { + if ( + sizeSlug && + ( newMedia?.sizes?.[ sizeSlug ] || + newMedia?.media_details?.sizes?.[ sizeSlug ] ) + ) { mediaAttributes.sizeSlug = sizeSlug; - mediaAttributes.url = newMedia?.sizes?.[ sizeSlug ]?.url; - } else if ( newMedia?.sizes?.[ imageDefaultSize ] ) { + mediaAttributes.url = + newMedia?.sizes?.[ sizeSlug ]?.url || + newMedia?.media_details?.sizes?.[ sizeSlug ]?.source_url; + } else if ( + newMedia?.sizes?.[ imageDefaultSize ] || + newMedia?.media_details?.sizes?.[ imageDefaultSize ] + ) { mediaAttributes.sizeSlug = imageDefaultSize; - mediaAttributes.url = newMedia?.sizes?.[ sizeSlug ]?.url; + mediaAttributes.url = + newMedia?.sizes?.[ imageDefaultSize ]?.url || + newMedia?.media_details?.sizes?.[ imageDefaultSize ] + ?.source_url; } else { mediaAttributes.sizeSlug = DEFAULT_MEDIA_SIZE_SLUG; } From 5c6e6f33a7288b14b6406a5494342c7711c6cd16 Mon Sep 17 00:00:00 2001 From: Manzoor Wani Date: Tue, 5 Nov 2024 23:21:12 -0800 Subject: [PATCH 16/72] Fix TS types for editor package (#66754) Co-authored-by: manzoorwanijk Co-authored-by: youknowriad --- docs/reference-guides/data/data-core-editor.md | 2 +- packages/editor/README.md | 8 -------- packages/editor/package.json | 1 + packages/editor/src/store/constants.ts | 2 -- packages/editor/src/store/index.js | 4 ---- packages/editor/src/store/selectors.js | 4 ++-- 6 files changed, 4 insertions(+), 17 deletions(-) diff --git a/docs/reference-guides/data/data-core-editor.md b/docs/reference-guides/data/data-core-editor.md index 713a247d88be29..9567d8e4b954fa 100644 --- a/docs/reference-guides/data/data-core-editor.md +++ b/docs/reference-guides/data/data-core-editor.md @@ -786,7 +786,7 @@ Return true if the current post has already been published. _Parameters_ - _state_ `Object`: Global application state. -- _currentPost_ `Object?`: Explicit current post for bypassing registry selector. +- _currentPost_ `[Object]`: Explicit current post for bypassing registry selector. _Returns_ diff --git a/packages/editor/README.md b/packages/editor/README.md index bcd1727e046d07..e0b53362089f1b 100644 --- a/packages/editor/README.md +++ b/packages/editor/README.md @@ -1631,10 +1631,6 @@ _Related_ - -_Type_ - -- `Object` - ### storeConfig Post editor data store configuration. @@ -1643,10 +1639,6 @@ _Related_ - -_Type_ - -- `Object` - ### TableOfContents Renders a table of contents component. diff --git a/packages/editor/package.json b/packages/editor/package.json index e7b46685719dd4..98e0c6d2255b74 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -26,6 +26,7 @@ "module": "build-module/index.js", "react-native": "src/index", "wpScript": true, + "types": "build-types", "sideEffects": [ "build-style/**", "src/**/*.scss", diff --git a/packages/editor/src/store/constants.ts b/packages/editor/src/store/constants.ts index 78708f00cc9eb7..73d6a104370c37 100644 --- a/packages/editor/src/store/constants.ts +++ b/packages/editor/src/store/constants.ts @@ -8,8 +8,6 @@ export const EDIT_MERGE_PROPERTIES = new Set( [ 'meta' ] ); /** * Constant for the store module (or reducer) key. - * - * @type {string} */ export const STORE_NAME = 'core/editor'; diff --git a/packages/editor/src/store/index.js b/packages/editor/src/store/index.js index 392f5f42cbd55b..a3241a2fc7e184 100644 --- a/packages/editor/src/store/index.js +++ b/packages/editor/src/store/index.js @@ -18,8 +18,6 @@ import { unlock } from '../lock-unlock'; * Post editor data store configuration. * * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/data/README.md#registerStore - * - * @type {Object} */ export const storeConfig = { reducer, @@ -31,8 +29,6 @@ export const storeConfig = { * Store definition for the editor namespace. * * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/data/README.md#createReduxStore - * - * @type {Object} */ export const store = createReduxStore( STORE_NAME, { ...storeConfig, diff --git a/packages/editor/src/store/selectors.js b/packages/editor/src/store/selectors.js index 2396cb67c73e69..db452be5c17abb 100644 --- a/packages/editor/src/store/selectors.js +++ b/packages/editor/src/store/selectors.js @@ -442,8 +442,8 @@ export function isCurrentPostPending( state ) { /** * Return true if the current post has already been published. * - * @param {Object} state Global application state. - * @param {Object?} currentPost Explicit current post for bypassing registry selector. + * @param {Object} state Global application state. + * @param {Object} [currentPost] Explicit current post for bypassing registry selector. * * @return {boolean} Whether the post has been published. */ From 452d3b07a420e5b66d5c182a69f99c102a0af23e Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Wed, 6 Nov 2024 19:09:41 +1100 Subject: [PATCH 17/72] Fix Paragraph appender layout shift (building on 66061) (#66779) * Style to prevent Layout shift when placeholder is clicked * Refactor style placement to target block-appender__content specifically * Remove no longer needed rules --------- Co-authored-by: Vrishabhsk Co-authored-by: andrewserong Co-authored-by: ramonjd Co-authored-by: aaronrobertshaw Co-authored-by: jasmussen --- .../default-block-appender/content.scss | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/packages/block-editor/src/components/default-block-appender/content.scss b/packages/block-editor/src/components/default-block-appender/content.scss index e2009daa7358c4..71ede90d25c0ca 100644 --- a/packages/block-editor/src/components/default-block-appender/content.scss +++ b/packages/block-editor/src/components/default-block-appender/content.scss @@ -24,20 +24,11 @@ .block-editor-default-block-appender__content { // Set the opacity of the initial block appender to the same as placeholder text in an empty Paragraph block. opacity: 0.62; - } - - // In "constrained" layout containers, the first and last paragraphs have their margins zeroed out. - // In the case of this appender, it needs to apply those same rules to avoid layout shifts. - // Such shifts happen when the bottom margin of the Title block has been set to less than the default 1em margin of paragraphs. - :where(body .is-layout-constrained) &, - :where(.wp-site-blocks) & { - > :first-child { - margin-block-start: 0; - margin-block-end: 0; - } - // Since this appender will only ever appear on an entirely empty document, we don't account for last-child. - // This is also because it will never be the last child, the block inserter that sits in this appender is the last child. + // The following prevents user agent styles from applying margins to the appender's inner paragraph. + // This in turn prevents layout shift due to layout styles removing margins from first and last children. + margin-block-start: 0; + margin-block-end: 0; } // Dropzone. From fe2dcb6ff022b90516021a5d35a0e8bf23b7ac4b Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Wed, 6 Nov 2024 18:24:56 +0900 Subject: [PATCH 18/72] DataViews: Tweak primary field in patterns grid layout (#66733) Co-authored-by: t-hamano Co-authored-by: youknowriad Co-authored-by: jameskoster Co-authored-by: jasmussen --- .../edit-site/src/components/page-patterns/fields.js | 11 +++-------- .../edit-site/src/components/page-patterns/style.scss | 5 ----- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/packages/edit-site/src/components/page-patterns/fields.js b/packages/edit-site/src/components/page-patterns/fields.js index 0ad47e90c20402..e016dca6cd8557 100644 --- a/packages/edit-site/src/components/page-patterns/fields.js +++ b/packages/edit-site/src/components/page-patterns/fields.js @@ -10,7 +10,7 @@ import { __experimentalHStack as HStack, Button, Tooltip, - Flex, + FlexBlock, } from '@wordpress/components'; import { __, _x } from '@wordpress/i18n'; import { useState, useMemo, useId } from '@wordpress/element'; @@ -122,12 +122,7 @@ function TitleField( { item } ) { const title = decodeEntities( defaultGetTitle( item ) ); return ( - + { item.type === PATTERN_TYPES.theme ? ( title ) : ( @@ -142,7 +137,7 @@ function TitleField( { item } ) { { title } ) } - + { item.type === PATTERN_TYPES.theme && ( Date: Wed, 6 Nov 2024 15:10:49 +0530 Subject: [PATCH 19/72] Added toggle control to set any image as feature image if no feature image is set for post (#65896) * feature: created toggle control to set content image as feature image if no feature image is set * Fix: docs build * revert: php changes for feature image * feature: created toolbar control to set image as feature image is post don't have feature image set * feature: created block settings & added proper notices for success message * update: added required block context for feature image control * update: set feature image control as per suggestion * remove: unnessary isFeature image attribute * rename: feature image control * Fix: query loop issue * Fix: typo in set featured image * update as per suggestions * Fix: minor feedback * Fix: minor issue --- packages/block-library/src/image/block.json | 2 +- packages/block-library/src/image/image.js | 40 ++++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/packages/block-library/src/image/block.json b/packages/block-library/src/image/block.json index f441a6e893290b..16e31217476026 100644 --- a/packages/block-library/src/image/block.json +++ b/packages/block-library/src/image/block.json @@ -4,7 +4,7 @@ "name": "core/image", "title": "Image", "category": "media", - "usesContext": [ "allowResize", "imageCrop", "fixedHeight" ], + "usesContext": [ "allowResize", "imageCrop", "fixedHeight", "postId", "postType", "queryId" ], "description": "Insert an image to make a visual statement.", "keywords": [ "img", "photo", "picture" ], "textdomain": "default", diff --git a/packages/block-library/src/image/image.js b/packages/block-library/src/image/image.js index a8d65951635522..61baba2263989c 100644 --- a/packages/block-library/src/image/image.js +++ b/packages/block-library/src/image/image.js @@ -32,6 +32,7 @@ import { __experimentalUseBorderProps as useBorderProps, __experimentalGetShadowClassesAndStyles as getShadowClassesAndStyles, privateApis as blockEditorPrivateApis, + BlockSettingsMenuControls, } from '@wordpress/block-editor'; import { useEffect, useMemo, useState, useRef } from '@wordpress/element'; import { __, _x, sprintf, isRTL } from '@wordpress/i18n'; @@ -39,7 +40,7 @@ import { getFilename } from '@wordpress/url'; import { getBlockBindingsSource, switchToBlockType } from '@wordpress/blocks'; import { crop, overlayText, upload, chevronDown } from '@wordpress/icons'; import { store as noticesStore } from '@wordpress/notices'; -import { store as coreStore } from '@wordpress/core-data'; +import { store as coreStore, useEntityProp } from '@wordpress/core-data'; /** * Internal dependencies @@ -893,6 +894,16 @@ export default function Image( { const shadowProps = getShadowClassesAndStyles( attributes ); const isRounded = attributes.className?.includes( 'is-style-rounded' ); + const { postType, postId, queryId } = context; + const isDescendentOfQueryLoop = Number.isFinite( queryId ); + + const [ , setFeaturedImage ] = useEntityProp( + 'postType', + postType, + 'featured_media', + postId + ); + let img = temporaryURL && hasImageErrored ? ( // Show a placeholder during upload when the blob URL can't be loaded. This can @@ -1094,10 +1105,37 @@ export default function Image( { ); } + /** + * Set the post's featured image with the current image. + */ + const setPostFeatureImage = () => { + setFeaturedImage( id ); + createSuccessNotice( __( 'Post featured image updated.' ), { + type: 'snackbar', + } ); + }; + + const featuredImageControl = ( + + { ( { selectedClientIds } ) => + selectedClientIds.length === 1 && + ! isDescendentOfQueryLoop && + postId && + id && + clientId === selectedClientIds[ 0 ] && ( + + { __( 'Set featured image' ) } + + ) + } + + ); + return ( <> { mediaReplaceFlow } { controls } + { featuredImageControl } { img } Date: Wed, 6 Nov 2024 11:10:04 +0100 Subject: [PATCH 20/72] Select Mode: Hide tool selector in the post editor and force design mode (#66784) Co-authored-by: youknowriad Co-authored-by: ntsekouras Co-authored-by: richtabor --- packages/block-editor/src/store/selectors.js | 7 +++++-- .../src/components/document-tools/index.js | 8 +++++++- .../provider/use-block-editor-settings.js | 5 +++++ .../editor/various/write-design-mode.spec.js | 20 +++++++++++++------ 4 files changed, 31 insertions(+), 9 deletions(-) diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index b5afb2891ef1cf..b09c3d3d01b165 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -2764,8 +2764,11 @@ export function isNavigationMode( state ) { * @return {string} the editor mode. */ export const __unstableGetEditorMode = createRegistrySelector( - ( select ) => () => { - return select( preferencesStore ).get( 'core', 'editorTool' ); + ( select ) => ( state ) => { + return ( + state.settings.editorTool ?? + select( preferencesStore ).get( 'core', 'editorTool' ) + ); } ); diff --git a/packages/editor/src/components/document-tools/index.js b/packages/editor/src/components/document-tools/index.js index b2295b555ce080..74118caaf5849c 100644 --- a/packages/editor/src/components/document-tools/index.js +++ b/packages/editor/src/components/document-tools/index.js @@ -35,6 +35,7 @@ function DocumentTools( { className, disableBlockTools = false } ) { inserterSidebarToggleRef, listViewToggleRef, showIconLabels, + showTools, } = useSelect( ( select ) => { const { get } = select( preferencesStore ); const { @@ -42,6 +43,8 @@ function DocumentTools( { className, disableBlockTools = false } ) { getEditorMode, getInserterSidebarToggleRef, getListViewToggleRef, + getRenderingMode, + getCurrentPostType, } = unlock( select( editorStore ) ); const { getShortcutRepresentation } = select( keyboardShortcutsStore ); @@ -56,6 +59,9 @@ function DocumentTools( { className, disableBlockTools = false } ) { showIconLabels: get( 'core', 'showIconLabels' ), isDistractionFree: get( 'core', 'distractionFree' ), isVisualMode: getEditorMode() === 'visual', + showTools: + getRenderingMode() !== 'post-only' || + getCurrentPostType() === 'wp_template', }; }, [] ); @@ -128,7 +134,7 @@ function DocumentTools( { className, disableBlockTools = false } ) { ) } { ( isWideViewport || ! showIconLabels ) && ( <> - { isLargeViewport && ( + { showTools && isLargeViewport && ( { - test.beforeEach( async ( { admin, editor } ) => { - await admin.createNewPost(); - await expect( - editor.canvas.getByRole( 'textbox', { name: 'Add title' } ) - ).toBeFocused(); + test.beforeAll( async ( { requestUtils } ) => { + await requestUtils.activateTheme( 'emptytheme' ); + } ); + + test.beforeEach( async ( { admin } ) => { + await admin.visitSiteEditor( { + postId: 'emptytheme//index', + postType: 'wp_template', + canvas: 'edit', + } ); } ); test.afterAll( async ( { requestUtils } ) => { - await requestUtils.deleteAllPosts(); + await requestUtils.activateTheme( 'twentytwentyone' ); } ); test( 'Should prevent selecting intermediary blocks', async ( { editor, page, } ) => { + // Clear all content + await editor.setContent( '' ); + // Insert a section with a nested block and an editable block. await editor.insertBlock( { name: 'core/group', From 624c938244ccd0ceda3decfcb2caa22a37487e73 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 6 Nov 2024 06:44:29 -0800 Subject: [PATCH 21/72] Delay block hydration to allow stores to initialize (#66772) Co-authored-by: westonruter Co-authored-by: michalczaplinski Co-authored-by: annezazu Co-authored-by: artemiomorales Co-authored-by: cbravobernal Co-authored-by: t-hamano --- packages/interactivity/src/init.ts | 11 +++++++++++ packages/interactivity/src/utils.ts | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/interactivity/src/init.ts b/packages/interactivity/src/init.ts index ddf6785d4dfdf4..fa1eec51c3e27e 100644 --- a/packages/interactivity/src/init.ts +++ b/packages/interactivity/src/init.ts @@ -33,6 +33,17 @@ export const init = async () => { `[data-${ directivePrefix }-interactive]` ); + /* + * This `await` with setTimeout is required to apparently ensure that the interactive blocks have their stores + * fully initialized prior to hydrating the blocks. If this is not present, then an error occurs, for example: + * > view.js:46 Uncaught (in promise) ReferenceError: Cannot access 'state' before initialization + * This occurs when splitTask() is implemented with scheduler.yield() as opposed to setTimeout(), as with the former + * split tasks are added to the front of the task queue whereas with the latter they are added to the end of the queue. + */ + await new Promise( ( resolve ) => { + setTimeout( resolve, 0 ); + } ); + for ( const node of nodes ) { if ( ! hydratedIslands.has( node ) ) { await splitTask(); diff --git a/packages/interactivity/src/utils.ts b/packages/interactivity/src/utils.ts index 9cd6f8bebb0d1c..ab6b0074727ee7 100644 --- a/packages/interactivity/src/utils.ts +++ b/packages/interactivity/src/utils.ts @@ -54,7 +54,7 @@ const afterNextFrame = ( callback: () => void ) => { /** * Returns a promise that resolves after yielding to main. * - * @return Promise + * @return Promise */ export const splitTask = typeof window.scheduler?.yield === 'function' From fc740ff4a5515e1baccd36c734e4faa6e2bafd26 Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Wed, 6 Nov 2024 14:50:17 +0000 Subject: [PATCH 22/72] Bump plugin version to 19.6.0 --- gutenberg.php | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gutenberg.php b/gutenberg.php index 128156a947b27f..89e65de4e94794 100644 --- a/gutenberg.php +++ b/gutenberg.php @@ -5,7 +5,7 @@ * Description: Printing since 1440. This is the development plugin for the block editor, site editor, and other future WordPress core functionality. * Requires at least: 6.5 * Requires PHP: 7.2 - * Version: 19.6.0-rc.3 + * Version: 19.6.0 * Author: Gutenberg Team * Text Domain: gutenberg * diff --git a/package-lock.json b/package-lock.json index 59fb1310a15e72..80abc752c92120 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "gutenberg", - "version": "19.6.0-rc.3", + "version": "19.6.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "gutenberg", - "version": "19.6.0-rc.3", + "version": "19.6.0", "hasInstallScript": true, "license": "GPL-2.0-or-later", "workspaces": [ diff --git a/package.json b/package.json index 6e519e2f1070e9..741475d8177705 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "19.6.0-rc.3", + "version": "19.6.0", "private": true, "description": "A new WordPress editor experience.", "author": "The WordPress Contributors", From fb9587e0dafa3f728197913c5292e0a7bae2073b Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Wed, 6 Nov 2024 15:53:11 +0000 Subject: [PATCH 23/72] Update Changelog for 19.6.0 --- changelog.txt | 299 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 297 insertions(+), 2 deletions(-) diff --git a/changelog.txt b/changelog.txt index ad860707a261f7..bcbfbb2dc48449 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,23 +1,318 @@ == Changelog == -= 19.6.0-rc.3 = += 19.6.0 = ## Changelog +### Enhancements + +#### Components +- Add elevation tokens to storybook. ([66122](https://github.com/WordPress/gutenberg/pull/66122)) +- Add foundations:Elevation to storybook. ([66124](https://github.com/WordPress/gutenberg/pull/66124)) +- Add radius foundation and tokens to storybook. ([66219](https://github.com/WordPress/gutenberg/pull/66219)) +- Combobox, FormTokenField: Show message when no matches found. ([66142](https://github.com/WordPress/gutenberg/pull/66142)) +- Storybook: Organizes components under 'Utilities'. ([66210](https://github.com/WordPress/gutenberg/pull/66210)) +- Tabs: Expose active tab item props, use ariakit prop types. ([66223](https://github.com/WordPress/gutenberg/pull/66223)) +- Tabs: Remove custom logic. ([66097](https://github.com/WordPress/gutenberg/pull/66097)) +- `DropdownMenuV2`: Rename to `Menu`. ([66289](https://github.com/WordPress/gutenberg/pull/66289)) + +#### Block Library +- Add Transformation from Separator to Spacer. ([66230](https://github.com/WordPress/gutenberg/pull/66230)) +- Add lightbox option in gallery block link control. ([64014](https://github.com/WordPress/gutenberg/pull/64014)) +- Archives: Add border block support. ([63400](https://github.com/WordPress/gutenberg/pull/63400)) +- Border support added to comments. ([66354](https://github.com/WordPress/gutenberg/pull/66354)) +- Cover Block: Add Image Resolution options. ([62926](https://github.com/WordPress/gutenberg/pull/62926)) +- HTML Block: Force HTML preview in view mode. ([66440](https://github.com/WordPress/gutenberg/pull/66440)) + +#### Zoom Out +- Add keyboard shortcut in editor. ([66400](https://github.com/WordPress/gutenberg/pull/66400)) +- Iterate zoom out shuffle into a more visual control. ([66194](https://github.com/WordPress/gutenberg/pull/66194)) +- Use the zoom-level value to scale the iframe. ([66280](https://github.com/WordPress/gutenberg/pull/66280)) + +#### Site Editor +- Remove synchronization of canvas mode into store. ([66213](https://github.com/WordPress/gutenberg/pull/66213)) +- Style Book: Clean up layout. ([66255](https://github.com/WordPress/gutenberg/pull/66255)) +- Update site icon and title position. ([66171](https://github.com/WordPress/gutenberg/pull/66171)) +- Editor Interface: Remove small header from global styles/plugin sidebar. ([64474](https://github.com/WordPress/gutenberg/pull/64474)) +- Remove purple coloring from DocumentBar and PostCard. ([66451](https://github.com/WordPress/gutenberg/pull/66451)) +- Core Data: Add the 'getEntitiesConfig' resolver. ([65871](https://github.com/WordPress/gutenberg/pull/65871)) +- BlockGroupToolbar: Better i18n context for toolbar labels. ([66211](https://github.com/WordPress/gutenberg/pull/66211)) +- Set `ResizableEditor` height based on border-box. ([66342](https://github.com/WordPress/gutenberg/pull/66342)) +- Add align-item related icons. ([66242](https://github.com/WordPress/gutenberg/pull/66242)) +- Stabilize isPreviewMode flag. ([66149](https://github.com/WordPress/gutenberg/pull/66149)) + +#### Data Views +- Fix alignment of action items in list layout. ([66273](https://github.com/WordPress/gutenberg/pull/66273)) +- Templates: Hide media field in list view. ([66573](https://github.com/WordPress/gutenberg/pull/66573)) + +#### Global Styles +- Backport: Caching of global styles for blocks from core. ([66349](https://github.com/WordPress/gutenberg/pull/66349)) +- Style Book: Add color tab. ([65692](https://github.com/WordPress/gutenberg/pull/65692)) + +#### Real-time Collaboration +- Inline Commenting: Disable comments on published posts for now. ([66583](https://github.com/WordPress/gutenberg/pull/66583)) + + +#### REST API +- Backport from WordPress core: Improvements for the post format query loop filter. ([66037](https://github.com/WordPress/gutenberg/pull/66037)) + +#### Build Tooling +- Create Block: Adds `--target-dir` flag to allow the tool to target where to scaffold. ([53781](https://github.com/WordPress/gutenberg/pull/53781)) +- Scripts: Add build-blocks-manifest command. ([65866](https://github.com/WordPress/gutenberg/pull/65866)) +- Scripts: Add BlueOak-1.0.0 license to GPL2 compatible. ([66139](https://github.com/WordPress/gutenberg/pull/66139)) +- WP Scripts: Add a `--root-folder` argument to the `plugin-zip` command. ([61375](https://github.com/WordPress/gutenberg/pull/61375)) + ### Bug Fixes +#### Zoom Out +- Disable zoom out toggle button when Style Book is open. ([66228](https://github.com/WordPress/gutenberg/pull/66228)) +- Don't switch editor mode when changing entities. ([66452](https://github.com/WordPress/gutenberg/pull/66452)) +- Fix scaling animation for device previews. ([66132](https://github.com/WordPress/gutenberg/pull/66132)) +- Fix zoom out not persisting while switching between editor and code editor. ([65932](https://github.com/WordPress/gutenberg/pull/65932)) +- Fix zoom out shortcut on Windows. ([66506](https://github.com/WordPress/gutenberg/pull/66506)) +- Fix: Activate zoom out on large viewport. ([66308](https://github.com/WordPress/gutenberg/pull/66308)) +- Hide slots and grouping buttons. ([66243](https://github.com/WordPress/gutenberg/pull/66243)) +- Remove zoom-out toolbar. ([66039](https://github.com/WordPress/gutenberg/pull/66039)) +- Resets the Zoom on viewed/edited entity change. ([66232](https://github.com/WordPress/gutenberg/pull/66232)) +- Revise zoom layout shift fix. ([66390](https://github.com/WordPress/gutenberg/pull/66390)) +- Zoom Out: Rely on zoom-level instead of zoom-out mode. ([66141](https://github.com/WordPress/gutenberg/pull/66141)) +- Zoom Out: Fix bouncy drop zones. ([66399](https://github.com/WordPress/gutenberg/pull/66399)) + #### Block Editor +- Block inserter: Prevent editor from crashing if `blockType.parent` is a string. ([66234](https://github.com/WordPress/gutenberg/pull/66234)) +- Block toolbar: Account for scrollable blocks that affect the position of the block toolbar. ([66188](https://github.com/WordPress/gutenberg/pull/66188)) +- Fix extra scrollbar appearing when searching in the inserter. ([66229](https://github.com/WordPress/gutenberg/pull/66229)) +- Fix: Show paragraph block variations in rich text inserter. ([66318](https://github.com/WordPress/gutenberg/pull/66318)) +- Group: Fix padding select. ([65857](https://github.com/WordPress/gutenberg/pull/65857)) +- Link Shortcut: Only trigger the link shortcut if there's a text selection. ([66056](https://github.com/WordPress/gutenberg/pull/66056)) +- Remove relative position from sidebar tabpanel. ([66267](https://github.com/WordPress/gutenberg/pull/66267)) +- Rich text: Remove empty file. ([66553](https://github.com/WordPress/gutenberg/pull/66553)) + +- Block editor: Self nesting and circular nesting block fix. ([66121](https://github.com/WordPress/gutenberg/pull/66121)) - Post Editor: Set the default value of the editorTool to edit. ([66636](https://github.com/WordPress/gutenberg/pull/66636)) +#### Components +- ColorPalette: Prevent overflow of custom color button background. ([66152](https://github.com/WordPress/gutenberg/pull/66152)) +- Fix: Text overflow in Patterns filter. ([66504](https://github.com/WordPress/gutenberg/pull/66504)) +- RadioGroup: Fix arrow key navigation in RTL. ([66202](https://github.com/WordPress/gutenberg/pull/66202)) +- Tabs and TabPanel: Fix arrow key navigation in RTL. ([66201](https://github.com/WordPress/gutenberg/pull/66201)) +- Tabs and ToggleGroupControl: Round indicator size. ([66426](https://github.com/WordPress/gutenberg/pull/66426)) +- Tabs: Fix animation timings. ([66198](https://github.com/WordPress/gutenberg/pull/66198)) +- Tabs: Override tablist's tabindex only when necessary. ([66209](https://github.com/WordPress/gutenberg/pull/66209)) +- Tabs: Restore vertical alignment for tabs content. ([66215](https://github.com/WordPress/gutenberg/pull/66215)) +- Tabs: Update indicator more reactively. ([66207](https://github.com/WordPress/gutenberg/pull/66207)) +- SpacingSizesControl: Use generic label for linked button. ([66304](https://github.com/WordPress/gutenberg/pull/66304)) + +#### i18n +- Miscellaneous i18n fixes. ([66510](https://github.com/WordPress/gutenberg/pull/66510)) +- Remove most of the occurrences of the verb toggle. ([66371](https://github.com/WordPress/gutenberg/pull/66371)) + +#### Post Editor +- Always force iframe in pattern editor. ([65887](https://github.com/WordPress/gutenberg/pull/65887)) +- Editor: Multi-entity saving: Show correct count of entities to be saved. ([66482](https://github.com/WordPress/gutenberg/pull/66482)) +- Fix : "Set featured image" button border flashes on focus. ([66092](https://github.com/WordPress/gutenberg/pull/66092)) +- Fix Parent Check Condition in `buildTermsTree`. ([66006](https://github.com/WordPress/gutenberg/pull/66006)) +- Fix: Improve the DocumentBar post type label for the Homepage and Posts Page cases. ([66355](https://github.com/WordPress/gutenberg/pull/66355)) +- PostTaxonomiesFlatTermSelector: Restore space between tag list and most used tags. ([66566](https://github.com/WordPress/gutenberg/pull/66566)) +- Typo metabox pane is a meta box panel. ([66502](https://github.com/WordPress/gutenberg/pull/66502)) + +#### Block Library +- Block registration: Normalize blockType.parent to `array`. ([66250](https://github.com/WordPress/gutenberg/pull/66250)) +- Button Block: Apply Stretch Styles Correctly. ([64770](https://github.com/WordPress/gutenberg/pull/64770)) +- Buttons: Fix the initial white space in nofollow rel. ([66303](https://github.com/WordPress/gutenberg/pull/66303)) +- Fix - Image block: Aspect ratio not responding when dimensions are not set. ([66217](https://github.com/WordPress/gutenberg/pull/66217)) +- Navigation block: Fix block appender size. ([66153](https://github.com/WordPress/gutenberg/pull/66153)) +- Fix the navigation issue inside cover blocks. ([66093](https://github.com/WordPress/gutenberg/pull/66093)) + +#### Site Editor +- Edit Site: Fix canvasMode var. ([66316](https://github.com/WordPress/gutenberg/pull/66316)) +- Fix button hover style in sidebar navigation screen. ([66505](https://github.com/WordPress/gutenberg/pull/66505)) +- Remove toggle verb from post list data views 'Toggle details panel'. ([66334](https://github.com/WordPress/gutenberg/pull/66334)) +- Revert 66431 (Site editor: Remove "default" admin CSS). ([66540](https://github.com/WordPress/gutenberg/pull/66540)) +- Restore block-library editor.css outside canvas. ([66556](https://github.com/WordPress/gutenberg/pull/66556)) +- Fix extraneous scrollbar in device previews. ([66494](https://github.com/WordPress/gutenberg/pull/66494)) +- Global styles: Move preload paths filter to 6.7 (previously 6.8). ([66543](https://github.com/WordPress/gutenberg/pull/66543)) +- Fix comment output in styles. ([66439](https://github.com/WordPress/gutenberg/pull/66439)) +- Site editor: Fix save shortcut. ([66423](https://github.com/WordPress/gutenberg/pull/66423)) +- Preload: Fix multiple regressions around global styles. ([66468](https://github.com/WordPress/gutenberg/pull/66468)) +#### Build tooling +- Create block: Ensure $slug is replaced with passed slug. ([66528](https://github.com/WordPress/gutenberg/pull/66528)) + +### Accessibility + +#### Post Editor +- Add featured image alt text. ([66189](https://github.com/WordPress/gutenberg/pull/66189)) +- Fix : Badge Color contrast. ([66360](https://github.com/WordPress/gutenberg/pull/66360)) + +#### Block Editor +- ImageSizeControls: Replace ButtonGroup with ToggleGroupControl. ([65386](https://github.com/WordPress/gutenberg/pull/65386)) +- Improve the link preview accessibility and labels. ([60908](https://github.com/WordPress/gutenberg/pull/60908)) + +#### Block Library +- Search: Replace ButtonGroup usage with ToggleGroupControl. ([65340](https://github.com/WordPress/gutenberg/pull/65340)) +- Remove clip & -webkit-clip-path for downloadable-block-list-item style.scss. ([66147](https://github.com/WordPress/gutenberg/pull/66147)) + +#### Global Styles +- Global styles menu: Avoid visible labels and accessible names mismatch. ([65124](https://github.com/WordPress/gutenberg/pull/65124)) + +#### Site Editor +- Fix Global styles panel header buttons text overlap for 'Show button text labels'. ([63243](https://github.com/WordPress/gutenberg/pull/63243)) +- Update the speak messages when switching editor modes. ([66278](https://github.com/WordPress/gutenberg/pull/66278)) + +### Performance + +#### Site Editor +- Remove "default" admin CSS. ([66431](https://github.com/WordPress/gutenberg/pull/66431)) +- Remove content styles outside canvas. ([66432](https://github.com/WordPress/gutenberg/pull/66432)) +- Block Style Variations: Reuse block metadata in WP_Theme_JSON::Get_valid_block_style_variations() for better performance. ([66539](https://github.com/WordPress/gutenberg/pull/66539)) +- Block Bindings: Use `getEntityConfig` instead of `getPostTypes` to get available slugs. ([66101](https://github.com/WordPress/gutenberg/pull/66101)) + +#### Build Tooling +- Fix/compare performance with correct branch. ([66196](https://github.com/WordPress/gutenberg/pull/66196)) + +### Experiments + +- Add Inline comment experimental flag. ([60622](https://github.com/WordPress/gutenberg/pull/60622)) +- QuickEdit: Add slug field control. ([65196](https://github.com/WordPress/gutenberg/pull/65196)) + +#### Data Views +- Quick Edit: Fix JS error when bulk editing pages. ([66358](https://github.com/WordPress/gutenberg/pull/66358)) +- QuickEdit: Add Featured Image Control. ([64496](https://github.com/WordPress/gutenberg/pull/64496)) +- QuickEdit: Add Parent field. ([66527](https://github.com/WordPress/gutenberg/pull/66527)) + +### Documentation + +- BaseControl: Auto-generate readme. ([66500](https://github.com/WordPress/gutenberg/pull/66500)) +- Components: Prevent generated readmes duplicating h1 elements when published. ([66180](https://github.com/WordPress/gutenberg/pull/66180)) +- Correct documentation on display type in flow layouts. ([66224](https://github.com/WordPress/gutenberg/pull/66224)) +- Docs: Interactivity API - Add documentation for `getServerState()` and `getServerContext()`. ([66104](https://github.com/WordPress/gutenberg/pull/66104)) +- Fix undo/redo Button size in Storybook playground. ([66538](https://github.com/WordPress/gutenberg/pull/66538)) +- Fix: JSON Schema Docgen doesn't work on Windows OS. ([66414](https://github.com/WordPress/gutenberg/pull/66414)) +- ItemGroup: Improve stories to default to bordered and separated. ([66191](https://github.com/WordPress/gutenberg/pull/66191)) +- README: Add Storybook badge. ([66529](https://github.com/WordPress/gutenberg/pull/66529)) +- Remove meetings that no longer occur from Contributor Guide. ([66421](https://github.com/WordPress/gutenberg/pull/66421)) +- Site editor routes: Add documentation for areas and prevent `edit` area from rendering when canvas is `edit`. ([66309](https://github.com/WordPress/gutenberg/pull/66309)) +- Update documentation about build process changes. ([66428](https://github.com/WordPress/gutenberg/pull/66428)) +- Docs: Example for getSelectedBlock. ([66108](https://github.com/WordPress/gutenberg/pull/66108)) + +### Code Quality + +#### Block Editor +- ESLint: Remove explicit `react-hooks/exhaustive-deps` disabling. ([66461](https://github.com/WordPress/gutenberg/pull/66461)) +- ESLint: Remove various React Compiler mutation violations. ([66327](https://github.com/WordPress/gutenberg/pull/66327)) +- Fix 'useInstanceId' hook reference. ([66406](https://github.com/WordPress/gutenberg/pull/66406)) +- Fix React Compiler error for Duotone. ([66492](https://github.com/WordPress/gutenberg/pull/66492)) +- Fix React Complier error for 'useEventListeners'. ([66495](https://github.com/WordPress/gutenberg/pull/66495)) +- Import only the actually used PostCSS exports. ([66379](https://github.com/WordPress/gutenberg/pull/66379)) +- No need to unlock public actions. ([66260](https://github.com/WordPress/gutenberg/pull/66260)) +- Remove patterns UI stylesheet from iframe. ([66306](https://github.com/WordPress/gutenberg/pull/66306)) +- Remove unnecessary Tooltip component from radius control linked button. ([66274](https://github.com/WordPress/gutenberg/pull/66274)) +- Rich Text: Fix React Complier error for 'useEventListeners'. ([66460](https://github.com/WordPress/gutenberg/pull/66460)) +- Storybook: Add BlockPatternsList story. ([66227](https://github.com/WordPress/gutenberg/pull/66227)) +- Block editor: Remove reusable blocks stylesheet from iframe. ([66285](https://github.com/WordPress/gutenberg/pull/66285)) + + +#### Components +- BorderBoxControl: Deprecate 36px default size. ([65752](https://github.com/WordPress/gutenberg/pull/65752)) +- BorderControl: Deprecate 36px default size. ([65755](https://github.com/WordPress/gutenberg/pull/65755)) +- DrodownMenuV2: Rename folder to `menu`. ([66473](https://github.com/WordPress/gutenberg/pull/66473)) +- ESLint: Stop disabling `react-hooks/exhaustive-deps` rule. ([66324](https://github.com/WordPress/gutenberg/pull/66324)) +- Fix React Compiler error for 'useAutocomplete'. ([66496](https://github.com/WordPress/gutenberg/pull/66496)) +- PaletteEdit: Use `ItemGroup` and `Item`, and avoid custom styles. ([66164](https://github.com/WordPress/gutenberg/pull/66164)) +- TabPanel: Add 40px size prop to tab Button. ([66557](https://github.com/WordPress/gutenberg/pull/66557)) +- Tabs: Align to standard compound components structure. ([66225](https://github.com/WordPress/gutenberg/pull/66225)) +- Tabs: Simplify styled components code. ([66208](https://github.com/WordPress/gutenberg/pull/66208)) + +#### Compose +- Compose: Fix React Complier error for 'useCopyToClipboard'. ([66444](https://github.com/WordPress/gutenberg/pull/66444)) +- Composer: Fix React Compiler errors for 'useDropZone'. ([66469](https://github.com/WordPress/gutenberg/pull/66469)) + +#### Dataviews +- Fields: Fix React Compiler mutation errors. ([66464](https://github.com/WordPress/gutenberg/pull/66464)) +- Fields: Update a few function definitions. ([66315](https://github.com/WordPress/gutenberg/pull/66315)) +#### Interface +- Interface: Remove duplicate 'withComplementaryAreaContext' file. ([66348](https://github.com/WordPress/gutenberg/pull/66348)) +- Interface: Remove unused private API support. ([66565](https://github.com/WordPress/gutenberg/pull/66565)) +- Interface: Use plugin context hook instead of HoC. ([66362](https://github.com/WordPress/gutenberg/pull/66362)) + +#### Zoom Out +- Bundle behavior in block-editor and add story. ([66240](https://github.com/WordPress/gutenberg/pull/66240)) +- Editor: Handle zoom out state via the 'switchEditorMode' action. ([66262](https://github.com/WordPress/gutenberg/pull/66262)) +- Fix/html scale code quality. ([66181](https://github.com/WordPress/gutenberg/pull/66181)) +- Make useZoomOut hook private. ([66374](https://github.com/WordPress/gutenberg/pull/66374)) +- Remove double click to exit hook from the block-editor package. ([66335](https://github.com/WordPress/gutenberg/pull/66335)) +- Remove viewport check from useZoomOut hook. ([66341](https://github.com/WordPress/gutenberg/pull/66341)) + +#### Post Editor +- Editor: No need to reset mode when changing device preview types. ([66261](https://github.com/WordPress/gutenberg/pull/66261)) +- Editor: Use plugin context hook in 'PluginMoreMenuItem'. ([66351](https://github.com/WordPress/gutenberg/pull/66351)) +- Editor: Use plugin context hook in 'PluginPreviewMenuItem'. ([66350](https://github.com/WordPress/gutenberg/pull/66350)) +- Fix typo after #63669. ([66396](https://github.com/WordPress/gutenberg/pull/66396)) +- Remove leftover JS code from 66451. ([66472](https://github.com/WordPress/gutenberg/pull/66472)) + +#### Block Library +- Cleanup unnecessary notice removal. ([66409](https://github.com/WordPress/gutenberg/pull/66409)) +- ESLint: Fix a couple of React Compiler reassignment errors. ([66331](https://github.com/WordPress/gutenberg/pull/66331)) +- Footnotes: Remove extra callback when parsing content. ([66370](https://github.com/WordPress/gutenberg/pull/66370)) +- Gallery: Fix React Compiler reassignment error. ([66408](https://github.com/WordPress/gutenberg/pull/66408)) +- Table Block: Remove hasArrowIndicator prop. ([66204](https://github.com/WordPress/gutenberg/pull/66204)) + +#### Style Book +- Avoid state/effect combo when generating values. ([66446](https://github.com/WordPress/gutenberg/pull/66446)) +- Fix React Compiler error. ([66445](https://github.com/WordPress/gutenberg/pull/66445)) + +#### Global Styles +- Clean up: Adjust reusable-blocks dependencies. ([66302](https://github.com/WordPress/gutenberg/pull/66302)) +- Fix React Compiler variable mutation error. ([66410](https://github.com/WordPress/gutenberg/pull/66410)) +- Theme JSON: Get_block_nodes - relocate $selectors assignment. ([66265](https://github.com/WordPress/gutenberg/pull/66265)) + +#### Interactivity API +- Interactivity API: Add comments to the `deepMerge()` function. ([66220](https://github.com/WordPress/gutenberg/pull/66220)) +- Interactivity API: Add tests for handling arrays in `deepMerge()`. ([66218](https://github.com/WordPress/gutenberg/pull/66218)) +- Interactivity API: Delay block hydration to allow interactive block stores to initialize. ([66772])(https://github.com/WordPress/gutenberg/pull/66772) + +#### Site Editor +- Prepare route registration by refactoring the site editor router. ([66030](https://github.com/WordPress/gutenberg/pull/66030)) +- Navigation: Improve trigger for fallback navigation. ([66478](https://github.com/WordPress/gutenberg/pull/66478)) +#### Build Tooling +- Scripts: Refactor to extract license logic. ([66179](https://github.com/WordPress/gutenberg/pull/66179)) +- Plugins: Deprecate 'withPluginContext' HOC. ([66363](https://github.com/WordPress/gutenberg/pull/66363)) + +### Tools + +#### Testing +- Fix [Flaky Test] Entering zoomed out mode zooms the canvas. ([66212](https://github.com/WordPress/gutenberg/pull/66212)) +- Fix end-to-end test for padding appender. ([66080](https://github.com/WordPress/gutenberg/pull/66080)) +- Upgrade Playwright to v1.48. ([66296](https://github.com/WordPress/gutenberg/pull/66296)) + +#### Build Tooling +- Add new private `vips` package. ([64845](https://github.com/WordPress/gutenberg/pull/64845)) +- Revert "Use npm workspaces for packages". ([66270](https://github.com/WordPress/gutenberg/pull/66270)) +- Add theme type to the bug report issue template. ([63851](https://github.com/WordPress/gutenberg/pull/63851)) +- Pull request automation: Use full npm install. ([66314](https://github.com/WordPress/gutenberg/pull/66314)) +- Use npm workspaces for packages (second attempt). ([66272](https://github.com/WordPress/gutenberg/pull/66272)) + + +## First-time contributors + +The following PRs were merged by first-time contributors: + +- @AhmarZaidi: Remove toggle verb from post list data views 'Toggle details panel'. ([66334](https://github.com/WordPress/gutenberg/pull/66334)) +- @mediaformat: Remove clip & -webkit-clip-path for downloadable-block-list-item style.scss. ([66147](https://github.com/WordPress/gutenberg/pull/66147)) +- @nicolasgalvez: WP Scripts: Add a `--root-folder` argument to the `plugin-zip` command. ([61375](https://github.com/WordPress/gutenberg/pull/61375)) +- @poojabhimani12: Add Inline comment experimental flag. ([60622](https://github.com/WordPress/gutenberg/pull/60622)) +- @rinkalpagdar: Border support added to comments. ([66354](https://github.com/WordPress/gutenberg/pull/66354)) +- @rudrakshi-gupta: Add Transformation from Separator to Spacer. ([66230](https://github.com/WordPress/gutenberg/pull/66230)) +- @SH4LIN: GH-66090: Self nesting and circular nesting block fix. ([66121](https://github.com/WordPress/gutenberg/pull/66121)) ## Contributors The following contributors merged PRs in this release: -@arthur791004 +@aaronrobertshaw @afercia @AhmarZaidi @akasunil @andrewserong @arthur791004 @carolinan @cbravobernal @ciampo @dhruvang21 @ellatrix @getdave @gigitux @hbhalodia @jameskoster @jeryj @jsnajdr @juanfra @madhusudhand @MaggieCabrera @Mamaduka @mattrwalker @mcsf @mediaformat @michalczaplinski @mirka @mreishus @ndiego @nicolasgalvez @ntsekouras @oandregal @PARTHVATALIYA @peterwilsoncc @poojabhimani12 @prajapatisagar @ramonjd @renatho @rinkalpagdar @rudrakshi-gupta @ryanwelcher @SantosGuillamot @SH4LIN @sirreal @stokesman @swissspidy @t-hamano @talldan @tellthemachines @torounit @troychaplin @tyxla @vipul0425 @Vrishabhsk @vykes-mac @westonruter @youknowriad @zaguiini = 19.5.1 = From b2528743b4d8ab2a240290b09e3df9fba0523a4f Mon Sep 17 00:00:00 2001 From: Rinkal Pagdar <92097119+rinkalpagdar@users.noreply.github.com> Date: Thu, 7 Nov 2024 04:14:49 +0530 Subject: [PATCH 24/72] Post Content: Add border and spacing support (#66366) Co-authored-by: rinkalpagdar Co-authored-by: tellthemachines --- docs/reference-guides/core-blocks.md | 2 +- .../block-library/src/post-content/block.json | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index dd49d156857249..4db90e9b2f8142 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -616,7 +616,7 @@ Displays the contents of a post or page. ([Source](https://github.com/WordPress/ - **Name:** core/post-content - **Category:** theme -- **Supports:** align (full, wide), background (backgroundImage, backgroundSize), color (background, gradients, link, text), dimensions (minHeight), layout, spacing (blockGap, padding), typography (fontSize, lineHeight), ~~html~~ +- **Supports:** align (full, wide), background (backgroundImage, backgroundSize), color (background, gradients, link, text), dimensions (minHeight), layout, spacing (blockGap, margin, padding), typography (fontSize, lineHeight), ~~html~~ ## Date diff --git a/packages/block-library/src/post-content/block.json b/packages/block-library/src/post-content/block.json index 1b9de707cb3220..ed9c47154b2f8e 100644 --- a/packages/block-library/src/post-content/block.json +++ b/packages/block-library/src/post-content/block.json @@ -27,6 +27,7 @@ "spacing": { "blockGap": true, "padding": true, + "margin": true, "__experimentalDefaultControls": { "margin": false, "padding": false @@ -52,8 +53,20 @@ "__experimentalDefaultControls": { "fontSize": true } + }, + "__experimentalBorder": { + "radius": true, + "color": true, + "width": true, + "style": true, + "__experimentalDefaultControls": { + "radius": true, + "color": true, + "width": true, + "style": true + } } }, "style": "wp-block-post-content", "editorStyle": "wp-block-post-content-editor" -} +} \ No newline at end of file From 3e46fe7e222a20b1dff36c1a8f435436e9bc134a Mon Sep 17 00:00:00 2001 From: Ramon Date: Thu, 7 Nov 2024 11:54:46 +1100 Subject: [PATCH 25/72] Global styles revisions: move focus and active state to list item (#66780) * This PR removes the button that loads the global revision and moves it up to an accessible list item. The result is that the list item is tabbable and selectable via keyboard, and the focus state wraps the entire item. * Use button border * Fix e2e tests and use composite component Unlinked contributors: jarekmorawski. Co-authored-by: ramonjd Co-authored-by: ntsekouras Co-authored-by: jasmussen --- .../screen-revisions/revisions-buttons.js | 59 +++++++++---------- .../global-styles/screen-revisions/style.scss | 28 ++++----- .../block-style-variations.spec.js | 2 +- .../user-global-styles-revisions.spec.js | 18 +++--- 4 files changed, 50 insertions(+), 57 deletions(-) diff --git a/packages/edit-site/src/components/global-styles/screen-revisions/revisions-buttons.js b/packages/edit-site/src/components/global-styles/screen-revisions/revisions-buttons.js index b7ce38857c9461..d6acd711108b5c 100644 --- a/packages/edit-site/src/components/global-styles/screen-revisions/revisions-buttons.js +++ b/packages/edit-site/src/components/global-styles/screen-revisions/revisions-buttons.js @@ -1,18 +1,13 @@ -/** - * External dependencies - */ -import clsx from 'clsx'; - /** * WordPress dependencies */ import { __, sprintf } from '@wordpress/i18n'; -import { Button } from '@wordpress/components'; +import { Button, Composite } from '@wordpress/components'; import { dateI18n, getDate, humanTimeDiff, getSettings } from '@wordpress/date'; import { store as coreStore } from '@wordpress/core-data'; import { useSelect } from '@wordpress/data'; import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; - +import { ENTER, SPACE } from '@wordpress/keycodes'; /** * Internal dependencies */ @@ -117,10 +112,11 @@ function RevisionsButtons( { const { datetimeAbbreviated } = getSettings().formats; return ( -
    { userRevisions.map( ( revision, index ) => { const { id, author, modified } = revision; @@ -149,28 +145,26 @@ function RevisionsButtons( { ); return ( -
  1. - + { isSelected && ( areStylesEqual ? (

    @@ -225,16 +219,19 @@ function RevisionsButtons( { variant="primary" className="edit-site-global-styles-screen-revisions__apply-button" onClick={ onApplyRevision } + aria-label={ __( + 'Apply the selected revision to your site.' + ) } > { isReset ? __( 'Reset to defaults' ) : __( 'Apply' ) } ) ) } -

  2. + ); } ) } -
+ ); } diff --git a/packages/edit-site/src/components/global-styles/screen-revisions/style.scss b/packages/edit-site/src/components/global-styles/screen-revisions/style.scss index 5d0e7cb185137a..1773d1a179b3dd 100644 --- a/packages/edit-site/src/components/global-styles/screen-revisions/style.scss +++ b/packages/edit-site/src/components/global-styles/screen-revisions/style.scss @@ -14,6 +14,11 @@ display: flex; flex-direction: column; + &[role="option"]:active, + &[role="option"]:focus { + @include button-style__focus(); + } + &:hover { background: rgba(var(--wp-admin-theme-color--rgb), 0.04); .edit-site-global-styles-screen-revisions__date { @@ -42,7 +47,7 @@ border: 4px solid transparent; } - &.is-selected { + &[aria-selected="true"] { border-radius: $radius-small; // Only visible in Windows High Contrast mode. @@ -52,10 +57,6 @@ color: var(--wp-admin-theme-color); background: rgba(var(--wp-admin-theme-color--rgb), 0.04); - .edit-site-global-styles-screen-revisions__revision-button { - opacity: 1; - } - .edit-site-global-styles-screen-revisions__date { color: var(--wp-admin-theme-color); } @@ -86,23 +87,16 @@ &:last-child::after { height: $grid-unit-20 + 2; } - - // Nested to override specificity of .components-button. - .edit-site-global-styles-screen-revisions__revision-button { - width: 100%; - height: auto; - display: block; - padding: $grid-unit-15 $grid-unit-15 $grid-unit-05 $grid-unit-50; - z-index: 1; - position: relative; - outline-offset: -2px; - } +} +.edit-site-global-styles-screen-revisions__revision-item-wrapper { + display: block; + padding: $grid-unit-15 $grid-unit-15 $grid-unit-05 $grid-unit-50; } .edit-site-global-styles-screen-revisions__apply-button.is-primary, .edit-site-global-styles-screen-revisions__applied-text { align-self: flex-start; - // Left margin = left padding of .edit-site-global-styles-screen-revisions__revision-button. + // Left margin = left padding of .edit-site-global-styles-screen-revisions__revision-item-wrapper. margin: $grid-unit-05 $grid-unit-15 $grid-unit-15 $grid-unit-50; } diff --git a/test/e2e/specs/site-editor/block-style-variations.spec.js b/test/e2e/specs/site-editor/block-style-variations.spec.js index 3c642ea9d1299e..03fc5398f4a0a5 100644 --- a/test/e2e/specs/site-editor/block-style-variations.spec.js +++ b/test/e2e/specs/site-editor/block-style-variations.spec.js @@ -245,7 +245,7 @@ test.describe( 'Block Style Variations', () => { // Click on previous revision. await page - .getByRole( 'button', { + .getByRole( 'option', { name: /^Changes saved by /, } ) .nth( 1 ) diff --git a/test/e2e/specs/site-editor/user-global-styles-revisions.spec.js b/test/e2e/specs/site-editor/user-global-styles-revisions.spec.js index e0b00fe1e59460..91142d5395640c 100644 --- a/test/e2e/specs/site-editor/user-global-styles-revisions.spec.js +++ b/test/e2e/specs/site-editor/user-global-styles-revisions.spec.js @@ -50,7 +50,7 @@ test.describe( 'Style Revisions', () => { // Now there should be enough revisions to show the revisions UI. await page.getByRole( 'button', { name: 'Revisions' } ).click(); - const revisionButtons = page.getByRole( 'button', { + const revisionButtons = page.getByRole( 'option', { name: /^Changes saved by /, } ); @@ -83,14 +83,14 @@ test.describe( 'Style Revisions', () => { await page.getByRole( 'button', { name: 'Revisions' } ).click(); - const unSavedButton = page.getByRole( 'button', { + const unSavedButton = page.getByRole( 'option', { name: /^Unsaved changes/, } ); await expect( unSavedButton ).toBeVisible(); await page - .getByRole( 'button', { name: /^Changes saved by / } ) + .getByRole( 'option', { name: /^Changes saved by / } ) .last() .click(); @@ -118,14 +118,16 @@ test.describe( 'Style Revisions', () => { await editor.canvas.locator( 'body' ).click(); await userGlobalStylesRevisions.openStylesPanel(); await page.getByRole( 'button', { name: 'Revisions' } ).click(); - const lastRevisionButton = page + const lastRevisionItem = page .getByLabel( 'Global styles revisions list' ) - .getByRole( 'button' ) + .getByRole( 'option' ) .last(); - await expect( lastRevisionButton ).toContainText( 'Default styles' ); - await lastRevisionButton.click(); + await expect( lastRevisionItem ).toContainText( 'Default styles' ); + await lastRevisionItem.click(); await expect( - page.getByRole( 'button', { name: 'Reset to defaults' } ) + page.getByRole( 'button', { + name: 'Apply the selected revision to your site.', + } ) ).toBeVisible(); } ); From 4651b8235d5220f02b1133266995f32c34fc9122 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Thu, 7 Nov 2024 11:06:11 +1000 Subject: [PATCH 26/72] Block Gap: Fix block spacing control for axial gap supported blocks (#66783) Co-authored-by: aaronrobertshaw Co-authored-by: andrewserong Co-authored-by: t-hamano Co-authored-by: ramonjd Co-authored-by: getdave Co-authored-by: carolinan Co-authored-by: ndiego Co-authored-by: juanfra Co-authored-by: ltrihan --- .../global-styles/dimensions-panel.js | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/packages/block-editor/src/components/global-styles/dimensions-panel.js b/packages/block-editor/src/components/global-styles/dimensions-panel.js index 4c52de6a3d7d11..c19788ebfcb580 100644 --- a/packages/block-editor/src/components/global-styles/dimensions-panel.js +++ b/packages/block-editor/src/components/global-styles/dimensions-panel.js @@ -147,24 +147,30 @@ function splitStyleValue( value ) { return value; } -function splitGapValue( value ) { - // Check for shorthand value (a string value). - if ( value && typeof value === 'string' ) { - // If the value is a string, treat it as a single side (top) for the spacing controls. - return { - top: value, - }; +function splitGapValue( value, isAxialGap ) { + if ( ! value ) { + return value; } - if ( value ) { - return { - ...value, - right: value?.left, - bottom: value?.top, - }; + // Check for shorthand value (a string value). + if ( typeof value === 'string' ) { + /* + * Map the string value to appropriate sides for the spacing control depending + * on whether the current block has axial gap support or not. + * + * Note: The axial value pairs must match for the spacing control to display + * the appropriate horizontal/vertical sliders. + */ + return isAxialGap + ? { top: value, right: value, bottom: value, left: value } + : { top: value }; } - return value; + return { + ...value, + right: value?.left, + bottom: value?.top, + }; } function DimensionsToolsPanel( { @@ -325,13 +331,13 @@ export default function DimensionsPanel( { // Block Gap const showGapControl = useHasGap( settings ); - const gapValue = decodeValue( inheritedValue?.spacing?.blockGap ); - const gapValues = splitGapValue( gapValue ); const gapSides = Array.isArray( settings?.spacing?.blockGap ) ? settings?.spacing?.blockGap : settings?.spacing?.blockGap?.sides; const isAxialGap = gapSides && gapSides.some( ( side ) => AXIAL_SIDES.includes( side ) ); + const gapValue = decodeValue( inheritedValue?.spacing?.blockGap ); + const gapValues = splitGapValue( gapValue, isAxialGap ); const setGapValue = ( newGapValue ) => { onChange( setImmutably( value, [ 'spacing', 'blockGap' ], newGapValue ) From a0b0e5f00e785c5d8a7f6c4e0ddd0edf268f7dbf Mon Sep 17 00:00:00 2001 From: Hit Bhalodia <58802366+hbhalodia@users.noreply.github.com> Date: Thu, 7 Nov 2024 09:59:46 +0530 Subject: [PATCH 27/72] Group the storybook components in feedback category (#66660) Co-authored-by: hbhalodia Co-authored-by: tyxla --- packages/components/src/notice/stories/index.story.tsx | 3 ++- packages/components/src/progress-bar/stories/index.story.tsx | 3 ++- packages/components/src/snackbar/stories/index.story.tsx | 3 ++- packages/components/src/snackbar/stories/list.story.tsx | 3 ++- packages/components/src/spinner/stories/index.story.tsx | 3 ++- packages/components/src/tip/stories/index.story.tsx | 3 ++- storybook/preview.js | 1 + 7 files changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/components/src/notice/stories/index.story.tsx b/packages/components/src/notice/stories/index.story.tsx index 95bf82016c2b04..042b4ac3ec79b3 100644 --- a/packages/components/src/notice/stories/index.story.tsx +++ b/packages/components/src/notice/stories/index.story.tsx @@ -17,7 +17,8 @@ import NoticeList from '../list'; import type { NoticeListProps } from '../types'; const meta: Meta< typeof Notice > = { - title: 'Components/Notice', + title: 'Components/Feedback/Notice', + id: 'components-notice', component: Notice, // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170 subcomponents: { NoticeList }, diff --git a/packages/components/src/progress-bar/stories/index.story.tsx b/packages/components/src/progress-bar/stories/index.story.tsx index 4396c4cc48eebb..2f6bb4dbe000fd 100644 --- a/packages/components/src/progress-bar/stories/index.story.tsx +++ b/packages/components/src/progress-bar/stories/index.story.tsx @@ -10,7 +10,8 @@ import { ProgressBar } from '..'; const meta: Meta< typeof ProgressBar > = { component: ProgressBar, - title: 'Components/ProgressBar', + title: 'Components/Feedback/ProgressBar', + id: 'components-progressbar', argTypes: { value: { control: { type: 'number', min: 0, max: 100, step: 1 } }, }, diff --git a/packages/components/src/snackbar/stories/index.story.tsx b/packages/components/src/snackbar/stories/index.story.tsx index c1f525b60b3919..9bd1dae42b71be 100644 --- a/packages/components/src/snackbar/stories/index.story.tsx +++ b/packages/components/src/snackbar/stories/index.story.tsx @@ -15,7 +15,8 @@ import Icon from '../../icon'; import Snackbar from '..'; const meta: Meta< typeof Snackbar > = { - title: 'Components/Snackbar', + title: 'Components/Feedback/Snackbar', + id: 'components-snackbar', component: Snackbar, argTypes: { as: { control: { type: null } }, diff --git a/packages/components/src/snackbar/stories/list.story.tsx b/packages/components/src/snackbar/stories/list.story.tsx index 8548ffba1e1f09..1f93f374ec1745 100644 --- a/packages/components/src/snackbar/stories/list.story.tsx +++ b/packages/components/src/snackbar/stories/list.story.tsx @@ -14,7 +14,8 @@ import { useState } from '@wordpress/element'; import SnackbarList from '../list'; const meta: Meta< typeof SnackbarList > = { - title: 'Components/SnackbarList', + title: 'Components/Feedback/SnackbarList', + id: 'components-snackbarlist', component: SnackbarList, argTypes: { as: { control: { type: null } }, diff --git a/packages/components/src/spinner/stories/index.story.tsx b/packages/components/src/spinner/stories/index.story.tsx index 1062c6406bfb4e..1dc5879ac41444 100644 --- a/packages/components/src/spinner/stories/index.story.tsx +++ b/packages/components/src/spinner/stories/index.story.tsx @@ -10,7 +10,8 @@ import Spinner from '../'; import { space } from '../../utils/space'; const meta: Meta< typeof Spinner > = { - title: 'Components/Spinner', + title: 'Components/Feedback/Spinner', + id: 'components-spinner', component: Spinner, parameters: { controls: { diff --git a/packages/components/src/tip/stories/index.story.tsx b/packages/components/src/tip/stories/index.story.tsx index 3999c6b9be45f4..3026f2a58fae73 100644 --- a/packages/components/src/tip/stories/index.story.tsx +++ b/packages/components/src/tip/stories/index.story.tsx @@ -10,7 +10,8 @@ import Tip from '..'; const meta: Meta< typeof Tip > = { component: Tip, - title: 'Components/Tip', + title: 'Components/Feedback/Tip', + id: 'components-tip', argTypes: { children: { control: { type: 'text' } }, }, diff --git a/storybook/preview.js b/storybook/preview.js index 21f2eab912059a..7672e7ebdcdff2 100644 --- a/storybook/preview.js +++ b/storybook/preview.js @@ -132,6 +132,7 @@ export const parameters = { 'Contributing Guidelines', 'Actions', 'Containers', + 'Feedback', 'Utilities', ], 'Components (Experimental)', From 88cf9bd905f00d8967bfe185176afc2892ebae70 Mon Sep 17 00:00:00 2001 From: Kai Hao Date: Thu, 7 Nov 2024 12:50:42 +0800 Subject: [PATCH 28/72] Revert "Set image width to `fit-content` to solve aspect ratio problems in Firefox. (#66217)" (#66804) Co-authored-by: kevin940726 Co-authored-by: ramonjd Co-authored-by: mukeshpanchal27 Co-authored-by: juanfra This reverts commit 0e65adcbe187797a536aa99f56b063a61cafdc3d. --- packages/block-library/src/image/style.scss | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/block-library/src/image/style.scss b/packages/block-library/src/image/style.scss index 79689e82006cdc..1bb19bf29da691 100644 --- a/packages/block-library/src/image/style.scss +++ b/packages/block-library/src/image/style.scss @@ -10,10 +10,6 @@ vertical-align: bottom; box-sizing: border-box; - &:not([src$=".svg"]) { - width: fit-content; - } - @media (prefers-reduced-motion: no-preference) { &.hide { visibility: hidden; From 20b773b7a47bc80aa7516a6955f23eb24782922f Mon Sep 17 00:00:00 2001 From: Hit Bhalodia <58802366+hbhalodia@users.noreply.github.com> Date: Thu, 7 Nov 2024 12:58:27 +0530 Subject: [PATCH 29/72] Feat: Storybook: Improve component organisation - Navigation Category - Issue #66275 (#66658) * Group the storybook components in Navigaiton category * Fix tabs to add inside containers and add experimental id for component * Fix the syntax style Co-authored-by: hbhalodia Co-authored-by: tyxla --- packages/components/src/external-link/stories/index.story.tsx | 3 ++- packages/components/src/navigator/stories/index.story.tsx | 3 ++- packages/components/src/tree-grid/stories/index.story.tsx | 3 ++- storybook/preview.js | 2 ++ 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/components/src/external-link/stories/index.story.tsx b/packages/components/src/external-link/stories/index.story.tsx index 91131e0c88aab0..eb70999413187f 100644 --- a/packages/components/src/external-link/stories/index.story.tsx +++ b/packages/components/src/external-link/stories/index.story.tsx @@ -10,7 +10,8 @@ import ExternalLink from '..'; const meta: Meta< typeof ExternalLink > = { component: ExternalLink, - title: 'Components/ExternalLink', + title: 'Components/Navigation/ExternalLink', + id: 'components-externallink', argTypes: { children: { control: { type: 'text' } }, }, diff --git a/packages/components/src/navigator/stories/index.story.tsx b/packages/components/src/navigator/stories/index.story.tsx index e9e342bb0d2eee..bd2cdc17a1263c 100644 --- a/packages/components/src/navigator/stories/index.story.tsx +++ b/packages/components/src/navigator/stories/index.story.tsx @@ -21,7 +21,8 @@ const meta: Meta< typeof Navigator > = { // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170 BackButton: Navigator.BackButton, }, - title: 'Components/Navigator', + title: 'Components/Navigation/Navigator', + id: 'components-navigator', argTypes: { as: { control: { type: null } }, children: { control: { type: null } }, diff --git a/packages/components/src/tree-grid/stories/index.story.tsx b/packages/components/src/tree-grid/stories/index.story.tsx index 44af154c685b2f..5a1ed95e1fd627 100644 --- a/packages/components/src/tree-grid/stories/index.story.tsx +++ b/packages/components/src/tree-grid/stories/index.story.tsx @@ -16,7 +16,8 @@ import { Button } from '../../button'; import InputControl from '../../input-control'; const meta: Meta< typeof TreeGrid > = { - title: 'Components (Experimental)/TreeGrid', + title: 'Components (Experimental)/Navigation/TreeGrid', + id: 'components-experimental-treegrid', component: TreeGrid, // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170 subcomponents: { TreeGridRow, TreeGridCell }, diff --git a/storybook/preview.js b/storybook/preview.js index 7672e7ebdcdff2..9e9dd587b39c4c 100644 --- a/storybook/preview.js +++ b/storybook/preview.js @@ -133,9 +133,11 @@ export const parameters = { 'Actions', 'Containers', 'Feedback', + 'Navigation', 'Utilities', ], 'Components (Experimental)', + [ 'Navigation' ], 'Icons', ], }, From b9b1a52d967db7a014100dd7d49a403ac26b7238 Mon Sep 17 00:00:00 2001 From: Nik Tsekouras Date: Thu, 7 Nov 2024 09:35:45 +0200 Subject: [PATCH 30/72] =?UTF-8?q?Relocate=20=E2=80=9CView=E2=80=9D=20exter?= =?UTF-8?q?nal=20link=20to=20end=20of=20editor=20header=20controls=20(#667?= =?UTF-8?q?85)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: ntsekouras Co-authored-by: draganescu Co-authored-by: jasmussen Co-authored-by: richtabor --- packages/editor/src/components/header/index.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/editor/src/components/header/index.js b/packages/editor/src/components/header/index.js index 07a5e9ddee5911..5a9cc2f94a5951 100644 --- a/packages/editor/src/components/header/index.js +++ b/packages/editor/src/components/header/index.js @@ -156,19 +156,21 @@ function Header( { ) } - { canBeZoomedOut && isEditorIframed && isWideViewport && ( - - ) } + + - + + { canBeZoomedOut && isEditorIframed && isWideViewport && ( + + ) } { ( isWideViewport || ! showIconLabels ) && ( From 8be8e4675637726b0ff3194ccc547307c4a489bc Mon Sep 17 00:00:00 2001 From: Marin Atanasov <8436925+tyxla@users.noreply.github.com> Date: Thu, 7 Nov 2024 17:26:40 +0900 Subject: [PATCH 31/72] ESLint: Fix React Compiler violations in various commands (#66787) Co-authored-by: tyxla Co-authored-by: Mamaduka --- .../components/use-block-commands/index.js | 479 +++++++++-------- .../src/admin-navigation-commands.js | 129 ++--- .../src/site-editor-navigation-commands.js | 256 ++++----- .../src/hooks/commands/use-common-commands.js | 499 +++++++++--------- .../hooks/commands/use-edit-mode-commands.js | 259 ++++----- .../editor/src/components/commands/index.js | 472 +++++++++-------- 6 files changed, 1069 insertions(+), 1025 deletions(-) diff --git a/packages/block-editor/src/components/use-block-commands/index.js b/packages/block-editor/src/components/use-block-commands/index.js index e739729c8f9e84..ff919710a2284e 100644 --- a/packages/block-editor/src/components/use-block-commands/index.js +++ b/packages/block-editor/src/components/use-block-commands/index.js @@ -24,275 +24,286 @@ import { import BlockIcon from '../block-icon'; import { store as blockEditorStore } from '../../store'; -export const useTransformCommands = () => { - const { replaceBlocks, multiSelect } = useDispatch( blockEditorStore ); - const { - blocks, - clientIds, - canRemove, - possibleBlockTransformations, - invalidSelection, - } = useSelect( ( select ) => { +const getTransformCommands = () => + function useTransformCommands() { + const { replaceBlocks, multiSelect } = useDispatch( blockEditorStore ); const { - getBlockRootClientId, - getBlockTransformItems, - getSelectedBlockClientIds, - getBlocksByClientId, - canRemoveBlocks, - } = select( blockEditorStore ); + blocks, + clientIds, + canRemove, + possibleBlockTransformations, + invalidSelection, + } = useSelect( ( select ) => { + const { + getBlockRootClientId, + getBlockTransformItems, + getSelectedBlockClientIds, + getBlocksByClientId, + canRemoveBlocks, + } = select( blockEditorStore ); + + const selectedBlockClientIds = getSelectedBlockClientIds(); + const selectedBlocks = getBlocksByClientId( + selectedBlockClientIds + ); + + // selectedBlocks can have `null`s when something tries to call `selectBlock` with an inexistent clientId. + // These nulls will cause fatal errors down the line. + // In order to prevent discrepancies between selectedBlockClientIds and selectedBlocks, we effectively treat the entire selection as invalid. + // @see https://github.com/WordPress/gutenberg/pull/59410#issuecomment-2006304536 + if ( selectedBlocks.filter( ( block ) => ! block ).length > 0 ) { + return { + invalidSelection: true, + }; + } - const selectedBlockClientIds = getSelectedBlockClientIds(); - const selectedBlocks = getBlocksByClientId( selectedBlockClientIds ); + const rootClientId = getBlockRootClientId( + selectedBlockClientIds[ 0 ] + ); + return { + blocks: selectedBlocks, + clientIds: selectedBlockClientIds, + possibleBlockTransformations: getBlockTransformItems( + selectedBlocks, + rootClientId + ), + canRemove: canRemoveBlocks( selectedBlockClientIds ), + invalidSelection: false, + }; + }, [] ); - // selectedBlocks can have `null`s when something tries to call `selectBlock` with an inexistent clientId. - // These nulls will cause fatal errors down the line. - // In order to prevent discrepancies between selectedBlockClientIds and selectedBlocks, we effectively treat the entire selection as invalid. - // @see https://github.com/WordPress/gutenberg/pull/59410#issuecomment-2006304536 - if ( selectedBlocks.filter( ( block ) => ! block ).length > 0 ) { + if ( invalidSelection ) { return { - invalidSelection: true, + isLoading: false, + commands: [], }; } + const isTemplate = blocks.length === 1 && isTemplatePart( blocks[ 0 ] ); + + function selectForMultipleBlocks( insertedBlocks ) { + if ( insertedBlocks.length > 1 ) { + multiSelect( + insertedBlocks[ 0 ].clientId, + insertedBlocks[ insertedBlocks.length - 1 ].clientId + ); + } + } - const rootClientId = getBlockRootClientId( - selectedBlockClientIds[ 0 ] - ); - return { - blocks: selectedBlocks, - clientIds: selectedBlockClientIds, - possibleBlockTransformations: getBlockTransformItems( - selectedBlocks, - rootClientId - ), - canRemove: canRemoveBlocks( selectedBlockClientIds ), - invalidSelection: false, - }; - }, [] ); + // Simple block tranformation based on the `Block Transforms` API. + function onBlockTransform( name ) { + const newBlocks = switchToBlockType( blocks, name ); + replaceBlocks( clientIds, newBlocks ); + selectForMultipleBlocks( newBlocks ); + } - if ( invalidSelection ) { - return { - isLoading: false, - commands: [], - }; - } - const isTemplate = blocks.length === 1 && isTemplatePart( blocks[ 0 ] ); - - function selectForMultipleBlocks( insertedBlocks ) { - if ( insertedBlocks.length > 1 ) { - multiSelect( - insertedBlocks[ 0 ].clientId, - insertedBlocks[ insertedBlocks.length - 1 ].clientId - ); + /** + * The `isTemplate` check is a stopgap solution here. + * Ideally, the Transforms API should handle this + * by allowing to exclude blocks from wildcard transformations. + */ + const hasPossibleBlockTransformations = + !! possibleBlockTransformations.length && canRemove && ! isTemplate; + + if ( + ! clientIds || + clientIds.length < 1 || + ! hasPossibleBlockTransformations + ) { + return { isLoading: false, commands: [] }; } - } - - // Simple block tranformation based on the `Block Transforms` API. - function onBlockTransform( name ) { - const newBlocks = switchToBlockType( blocks, name ); - replaceBlocks( clientIds, newBlocks ); - selectForMultipleBlocks( newBlocks ); - } - - /** - * The `isTemplate` check is a stopgap solution here. - * Ideally, the Transforms API should handle this - * by allowing to exclude blocks from wildcard transformations. - */ - const hasPossibleBlockTransformations = - !! possibleBlockTransformations.length && canRemove && ! isTemplate; - - if ( - ! clientIds || - clientIds.length < 1 || - ! hasPossibleBlockTransformations - ) { - return { isLoading: false, commands: [] }; - } - - const commands = possibleBlockTransformations.map( ( transformation ) => { - const { name, title, icon } = transformation; - return { - name: 'core/block-editor/transform-to-' + name.replace( '/', '-' ), - /* translators: %s: Block or block variation name. */ - label: sprintf( __( 'Transform to %s' ), title ), - icon: , - callback: ( { close } ) => { - onBlockTransform( name ); - close(); + + const commands = possibleBlockTransformations.map( + ( transformation ) => { + const { name, title, icon } = transformation; + return { + name: + 'core/block-editor/transform-to-' + + name.replace( '/', '-' ), + /* translators: %s: Block or block variation name. */ + label: sprintf( __( 'Transform to %s' ), title ), + icon: , + callback: ( { close } ) => { + onBlockTransform( name ); + close(); + }, + }; + } + ); + + return { isLoading: false, commands }; + }; + +const getQuickActionsCommands = () => + function useQuickActionsCommands() { + const { clientIds, isUngroupable, isGroupable } = useSelect( + ( select ) => { + const { + getSelectedBlockClientIds, + isUngroupable: _isUngroupable, + isGroupable: _isGroupable, + } = select( blockEditorStore ); + const selectedBlockClientIds = getSelectedBlockClientIds(); + + return { + clientIds: selectedBlockClientIds, + isUngroupable: _isUngroupable(), + isGroupable: _isGroupable(), + }; }, - }; - } ); + [] + ); + const { + canInsertBlockType, + getBlockRootClientId, + getBlocksByClientId, + canRemoveBlocks, + } = useSelect( blockEditorStore ); + const { getDefaultBlockName, getGroupingBlockName } = + useSelect( blocksStore ); - return { isLoading: false, commands }; -}; + const blocks = getBlocksByClientId( clientIds ); -const useQuickActionsCommands = () => { - const { clientIds, isUngroupable, isGroupable } = useSelect( ( select ) => { const { - getSelectedBlockClientIds, - isUngroupable: _isUngroupable, - isGroupable: _isGroupable, - } = select( blockEditorStore ); - const selectedBlockClientIds = getSelectedBlockClientIds(); + removeBlocks, + replaceBlocks, + duplicateBlocks, + insertAfterBlock, + insertBeforeBlock, + } = useDispatch( blockEditorStore ); + + const onGroup = () => { + if ( ! blocks.length ) { + return; + } - return { - clientIds: selectedBlockClientIds, - isUngroupable: _isUngroupable(), - isGroupable: _isGroupable(), + const groupingBlockName = getGroupingBlockName(); + + // Activate the `transform` on `core/group` which does the conversion. + const newBlocks = switchToBlockType( blocks, groupingBlockName ); + + if ( ! newBlocks ) { + return; + } + replaceBlocks( clientIds, newBlocks ); }; - }, [] ); - const { - canInsertBlockType, - getBlockRootClientId, - getBlocksByClientId, - canRemoveBlocks, - } = useSelect( blockEditorStore ); - const { getDefaultBlockName, getGroupingBlockName } = - useSelect( blocksStore ); - - const blocks = getBlocksByClientId( clientIds ); - - const { - removeBlocks, - replaceBlocks, - duplicateBlocks, - insertAfterBlock, - insertBeforeBlock, - } = useDispatch( blockEditorStore ); - - const onGroup = () => { - if ( ! blocks.length ) { - return; - } + const onUngroup = () => { + if ( ! blocks.length ) { + return; + } - const groupingBlockName = getGroupingBlockName(); + const innerBlocks = blocks[ 0 ].innerBlocks; - // Activate the `transform` on `core/group` which does the conversion. - const newBlocks = switchToBlockType( blocks, groupingBlockName ); + if ( ! innerBlocks.length ) { + return; + } - if ( ! newBlocks ) { - return; - } - replaceBlocks( clientIds, newBlocks ); - }; - const onUngroup = () => { - if ( ! blocks.length ) { - return; + replaceBlocks( clientIds, innerBlocks ); + }; + + if ( ! clientIds || clientIds.length < 1 ) { + return { isLoading: false, commands: [] }; } - const innerBlocks = blocks[ 0 ].innerBlocks; + const rootClientId = getBlockRootClientId( clientIds[ 0 ] ); + const canInsertDefaultBlock = canInsertBlockType( + getDefaultBlockName(), + rootClientId + ); + const canDuplicate = blocks.every( ( block ) => { + return ( + !! block && + hasBlockSupport( block.name, 'multiple', true ) && + canInsertBlockType( block.name, rootClientId ) + ); + } ); + const canRemove = canRemoveBlocks( clientIds ); - if ( ! innerBlocks.length ) { - return; + const commands = []; + + if ( canDuplicate ) { + commands.push( { + name: 'duplicate', + label: __( 'Duplicate' ), + callback: () => duplicateBlocks( clientIds, true ), + icon: copy, + } ); } - replaceBlocks( clientIds, innerBlocks ); - }; + if ( canInsertDefaultBlock ) { + commands.push( + { + name: 'add-before', + label: __( 'Add before' ), + callback: () => { + const clientId = Array.isArray( clientIds ) + ? clientIds[ 0 ] + : clientId; + insertBeforeBlock( clientId ); + }, + icon: add, + }, + { + name: 'add-after', + label: __( 'Add after' ), + callback: () => { + const clientId = Array.isArray( clientIds ) + ? clientIds[ clientIds.length - 1 ] + : clientId; + insertAfterBlock( clientId ); + }, + icon: add, + } + ); + } - if ( ! clientIds || clientIds.length < 1 ) { - return { isLoading: false, commands: [] }; - } - - const rootClientId = getBlockRootClientId( clientIds[ 0 ] ); - const canInsertDefaultBlock = canInsertBlockType( - getDefaultBlockName(), - rootClientId - ); - const canDuplicate = blocks.every( ( block ) => { - return ( - !! block && - hasBlockSupport( block.name, 'multiple', true ) && - canInsertBlockType( block.name, rootClientId ) - ); - } ); - const canRemove = canRemoveBlocks( clientIds ); + if ( isGroupable ) { + commands.push( { + name: 'Group', + label: __( 'Group' ), + callback: onGroup, + icon: group, + } ); + } - const commands = []; + if ( isUngroupable ) { + commands.push( { + name: 'ungroup', + label: __( 'Ungroup' ), + callback: onUngroup, + icon: ungroup, + } ); + } - if ( canDuplicate ) { - commands.push( { - name: 'duplicate', - label: __( 'Duplicate' ), - callback: () => duplicateBlocks( clientIds, true ), - icon: copy, - } ); - } - - if ( canInsertDefaultBlock ) { - commands.push( - { - name: 'add-before', - label: __( 'Add before' ), - callback: () => { - const clientId = Array.isArray( clientIds ) - ? clientIds[ 0 ] - : clientId; - insertBeforeBlock( clientId ); - }, - icon: add, - }, - { - name: 'add-after', - label: __( 'Add after' ), - callback: () => { - const clientId = Array.isArray( clientIds ) - ? clientIds[ clientIds.length - 1 ] - : clientId; - insertAfterBlock( clientId ); + if ( canRemove ) { + commands.push( { + name: 'remove', + label: __( 'Delete' ), + callback: () => removeBlocks( clientIds, true ), + icon: remove, + } ); + } + + return { + isLoading: false, + commands: commands.map( ( command ) => ( { + ...command, + name: 'core/block-editor/action-' + command.name, + callback: ( { close } ) => { + command.callback(); + close(); }, - icon: add, - } - ); - } - - if ( isGroupable ) { - commands.push( { - name: 'Group', - label: __( 'Group' ), - callback: onGroup, - icon: group, - } ); - } - - if ( isUngroupable ) { - commands.push( { - name: 'ungroup', - label: __( 'Ungroup' ), - callback: onUngroup, - icon: ungroup, - } ); - } - - if ( canRemove ) { - commands.push( { - name: 'remove', - label: __( 'Delete' ), - callback: () => removeBlocks( clientIds, true ), - icon: remove, - } ); - } - - return { - isLoading: false, - commands: commands.map( ( command ) => ( { - ...command, - name: 'core/block-editor/action-' + command.name, - callback: ( { close } ) => { - command.callback(); - close(); - }, - } ) ), + } ) ), + }; }; -}; export const useBlockCommands = () => { useCommandLoader( { name: 'core/block-editor/blockTransforms', - hook: useTransformCommands, + hook: getTransformCommands(), } ); useCommandLoader( { name: 'core/block-editor/blockQuickActions', - hook: useQuickActionsCommands, + hook: getQuickActionsCommands(), context: 'block-selection-edit', } ); }; diff --git a/packages/core-commands/src/admin-navigation-commands.js b/packages/core-commands/src/admin-navigation-commands.js index c0d8bb084b46ad..9f0883faa3c17a 100644 --- a/packages/core-commands/src/admin-navigation-commands.js +++ b/packages/core-commands/src/admin-navigation-commands.js @@ -18,75 +18,78 @@ import { unlock } from './lock-unlock'; const { useHistory } = unlock( routerPrivateApis ); -function useAddNewPageCommand() { - const isSiteEditor = getPath( window.location.href )?.includes( - 'site-editor.php' - ); - const history = useHistory(); - const isBlockBasedTheme = useSelect( ( select ) => { - return select( coreStore ).getCurrentTheme()?.is_block_theme; - }, [] ); - const { saveEntityRecord } = useDispatch( coreStore ); - const { createErrorNotice } = useDispatch( noticesStore ); +const getAddNewPageCommand = () => + function useAddNewPageCommand() { + const isSiteEditor = getPath( window.location.href )?.includes( + 'site-editor.php' + ); + const history = useHistory(); + const isBlockBasedTheme = useSelect( ( select ) => { + return select( coreStore ).getCurrentTheme()?.is_block_theme; + }, [] ); + const { saveEntityRecord } = useDispatch( coreStore ); + const { createErrorNotice } = useDispatch( noticesStore ); - const createPageEntity = useCallback( - async ( { close } ) => { - try { - const page = await saveEntityRecord( - 'postType', - 'page', - { - status: 'draft', - }, - { - throwOnError: true, + const createPageEntity = useCallback( + async ( { close } ) => { + try { + const page = await saveEntityRecord( + 'postType', + 'page', + { + status: 'draft', + }, + { + throwOnError: true, + } + ); + if ( page?.id ) { + history.push( { + postId: page.id, + postType: 'page', + canvas: 'edit', + } ); } - ); - if ( page?.id ) { - history.push( { - postId: page.id, - postType: 'page', - canvas: 'edit', + } catch ( error ) { + const errorMessage = + error.message && error.code !== 'unknown_error' + ? error.message + : __( + 'An error occurred while creating the item.' + ); + + createErrorNotice( errorMessage, { + type: 'snackbar', } ); + } finally { + close(); } - } catch ( error ) { - const errorMessage = - error.message && error.code !== 'unknown_error' - ? error.message - : __( 'An error occurred while creating the item.' ); - - createErrorNotice( errorMessage, { - type: 'snackbar', - } ); - } finally { - close(); - } - }, - [ createErrorNotice, history, saveEntityRecord ] - ); - - const commands = useMemo( () => { - const addNewPage = - isSiteEditor && isBlockBasedTheme - ? createPageEntity - : () => - ( document.location.href = - 'post-new.php?post_type=page' ); - return [ - { - name: 'core/add-new-page', - label: __( 'Add new page' ), - icon: plus, - callback: addNewPage, }, - ]; - }, [ createPageEntity, isSiteEditor, isBlockBasedTheme ] ); + [ createErrorNotice, history, saveEntityRecord ] + ); + + const commands = useMemo( () => { + const addNewPage = + isSiteEditor && isBlockBasedTheme + ? createPageEntity + : () => + ( document.location.href = + 'post-new.php?post_type=page' ); + return [ + { + name: 'core/add-new-page', + label: __( 'Add new page' ), + icon: plus, + callback: addNewPage, + }, + ]; + }, [ createPageEntity, isSiteEditor, isBlockBasedTheme ] ); - return { - isLoading: false, - commands, + return { + isLoading: false, + commands, + }; }; -} export function useAdminNavigationCommands() { useCommand( { @@ -100,6 +103,6 @@ export function useAdminNavigationCommands() { useCommandLoader( { name: 'core/add-new-page', - hook: useAddNewPageCommand, + hook: getAddNewPageCommand(), } ); } diff --git a/packages/core-commands/src/site-editor-navigation-commands.js b/packages/core-commands/src/site-editor-navigation-commands.js index 4679d4d1523c89..2785d809d41e03 100644 --- a/packages/core-commands/src/site-editor-navigation-commands.js +++ b/packages/core-commands/src/site-editor-navigation-commands.js @@ -275,159 +275,169 @@ const getNavigationCommandLoaderPerTemplate = ( templateType ) => }; }; -const usePageNavigationCommandLoader = - getNavigationCommandLoaderPerPostType( 'page' ); -const usePostNavigationCommandLoader = - getNavigationCommandLoaderPerPostType( 'post' ); -const useTemplateNavigationCommandLoader = - getNavigationCommandLoaderPerTemplate( 'wp_template' ); -const useTemplatePartNavigationCommandLoader = - getNavigationCommandLoaderPerTemplate( 'wp_template_part' ); +const getSiteEditorBasicNavigationCommands = () => + function useSiteEditorBasicNavigationCommands() { + const history = useHistory(); + const isSiteEditor = getPath( window.location.href )?.includes( + 'site-editor.php' + ); + const { isBlockBasedTheme, canCreateTemplate } = useSelect( + ( select ) => { + return { + isBlockBasedTheme: + select( coreStore ).getCurrentTheme()?.is_block_theme, + canCreateTemplate: select( coreStore ).canUser( 'create', { + kind: 'postType', + name: 'wp_template', + } ), + }; + }, + [] + ); + const commands = useMemo( () => { + const result = []; -function useSiteEditorBasicNavigationCommands() { - const history = useHistory(); - const isSiteEditor = getPath( window.location.href )?.includes( - 'site-editor.php' - ); - const { isBlockBasedTheme, canCreateTemplate } = useSelect( ( select ) => { - return { - isBlockBasedTheme: - select( coreStore ).getCurrentTheme()?.is_block_theme, - canCreateTemplate: select( coreStore ).canUser( 'create', { - kind: 'postType', - name: 'wp_template', - } ), - }; - }, [] ); - const commands = useMemo( () => { - const result = []; + if ( canCreateTemplate && isBlockBasedTheme ) { + result.push( { + name: 'core/edit-site/open-navigation', + label: __( 'Navigation' ), + icon: navigation, + callback: ( { close } ) => { + const args = { + postType: 'wp_navigation', + }; + const targetUrl = addQueryArgs( + 'site-editor.php', + args + ); + if ( isSiteEditor ) { + history.push( args ); + } else { + document.location = targetUrl; + } + close(); + }, + } ); - if ( canCreateTemplate && isBlockBasedTheme ) { - result.push( { - name: 'core/edit-site/open-navigation', - label: __( 'Navigation' ), - icon: navigation, - callback: ( { close } ) => { - const args = { - postType: 'wp_navigation', - }; - const targetUrl = addQueryArgs( 'site-editor.php', args ); - if ( isSiteEditor ) { - history.push( args ); - } else { - document.location = targetUrl; - } - close(); - }, - } ); + result.push( { + name: 'core/edit-site/open-styles', + label: __( 'Styles' ), + icon: styles, + callback: ( { close } ) => { + const args = { + path: '/wp_global_styles', + }; + const targetUrl = addQueryArgs( + 'site-editor.php', + args + ); + if ( isSiteEditor ) { + history.push( args ); + } else { + document.location = targetUrl; + } + close(); + }, + } ); - result.push( { - name: 'core/edit-site/open-styles', - label: __( 'Styles' ), - icon: styles, - callback: ( { close } ) => { - const args = { - path: '/wp_global_styles', - }; - const targetUrl = addQueryArgs( 'site-editor.php', args ); - if ( isSiteEditor ) { - history.push( args ); - } else { - document.location = targetUrl; - } - close(); - }, - } ); + result.push( { + name: 'core/edit-site/open-pages', + label: __( 'Pages' ), + icon: page, + callback: ( { close } ) => { + const args = { + postType: 'page', + }; + const targetUrl = addQueryArgs( + 'site-editor.php', + args + ); + if ( isSiteEditor ) { + history.push( args ); + } else { + document.location = targetUrl; + } + close(); + }, + } ); - result.push( { - name: 'core/edit-site/open-pages', - label: __( 'Pages' ), - icon: page, - callback: ( { close } ) => { - const args = { - postType: 'page', - }; - const targetUrl = addQueryArgs( 'site-editor.php', args ); - if ( isSiteEditor ) { - history.push( args ); - } else { - document.location = targetUrl; - } - close(); - }, - } ); + result.push( { + name: 'core/edit-site/open-templates', + label: __( 'Templates' ), + icon: layout, + callback: ( { close } ) => { + const args = { + postType: 'wp_template', + }; + const targetUrl = addQueryArgs( + 'site-editor.php', + args + ); + if ( isSiteEditor ) { + history.push( args ); + } else { + document.location = targetUrl; + } + close(); + }, + } ); + } result.push( { - name: 'core/edit-site/open-templates', - label: __( 'Templates' ), - icon: layout, + name: 'core/edit-site/open-patterns', + label: __( 'Patterns' ), + icon: symbol, callback: ( { close } ) => { - const args = { - postType: 'wp_template', - }; - const targetUrl = addQueryArgs( 'site-editor.php', args ); - if ( isSiteEditor ) { - history.push( args ); + if ( canCreateTemplate ) { + const args = { + postType: 'wp_block', + }; + const targetUrl = addQueryArgs( + 'site-editor.php', + args + ); + if ( isSiteEditor ) { + history.push( args ); + } else { + document.location = targetUrl; + } + close(); } else { - document.location = targetUrl; + // If a user cannot access the site editor + document.location.href = 'edit.php?post_type=wp_block'; } - close(); }, } ); - } - - result.push( { - name: 'core/edit-site/open-patterns', - label: __( 'Patterns' ), - icon: symbol, - callback: ( { close } ) => { - if ( canCreateTemplate ) { - const args = { - postType: 'wp_block', - }; - const targetUrl = addQueryArgs( 'site-editor.php', args ); - if ( isSiteEditor ) { - history.push( args ); - } else { - document.location = targetUrl; - } - close(); - } else { - // If a user cannot access the site editor - document.location.href = 'edit.php?post_type=wp_block'; - } - }, - } ); - return result; - }, [ history, isSiteEditor, canCreateTemplate, isBlockBasedTheme ] ); + return result; + }, [ history, isSiteEditor, canCreateTemplate, isBlockBasedTheme ] ); - return { - commands, - isLoading: false, + return { + commands, + isLoading: false, + }; }; -} export function useSiteEditorNavigationCommands() { useCommandLoader( { name: 'core/edit-site/navigate-pages', - hook: usePageNavigationCommandLoader, + hook: getNavigationCommandLoaderPerPostType( 'page' ), } ); useCommandLoader( { name: 'core/edit-site/navigate-posts', - hook: usePostNavigationCommandLoader, + hook: getNavigationCommandLoaderPerPostType( 'post' ), } ); useCommandLoader( { name: 'core/edit-site/navigate-templates', - hook: useTemplateNavigationCommandLoader, + hook: getNavigationCommandLoaderPerTemplate( 'wp_template' ), } ); useCommandLoader( { name: 'core/edit-site/navigate-template-parts', - hook: useTemplatePartNavigationCommandLoader, + hook: getNavigationCommandLoaderPerTemplate( 'wp_template_part' ), } ); useCommandLoader( { name: 'core/edit-site/basic-navigation', - hook: useSiteEditorBasicNavigationCommands, + hook: getSiteEditorBasicNavigationCommands(), context: 'site-editor', } ); } diff --git a/packages/edit-site/src/hooks/commands/use-common-commands.js b/packages/edit-site/src/hooks/commands/use-common-commands.js index 536817e88d3a47..3e87f8721e116a 100644 --- a/packages/edit-site/src/hooks/commands/use-common-commands.js +++ b/packages/edit-site/src/hooks/commands/use-common-commands.js @@ -28,282 +28,289 @@ import { store as editSiteStore } from '../../store'; const { useGlobalStylesReset } = unlock( blockEditorPrivateApis ); const { useHistory, useLocation } = unlock( routerPrivateApis ); -function useGlobalStylesOpenStylesCommands() { - const { openGeneralSidebar } = unlock( useDispatch( editSiteStore ) ); - const { params } = useLocation(); - const { canvas = 'view' } = params; - const history = useHistory(); - const isBlockBasedTheme = useSelect( ( select ) => { - return select( coreStore ).getCurrentTheme().is_block_theme; - }, [] ); +const getGlobalStylesOpenStylesCommands = () => + function useGlobalStylesOpenStylesCommands() { + const { openGeneralSidebar } = unlock( useDispatch( editSiteStore ) ); + const { params } = useLocation(); + const { canvas = 'view' } = params; + const history = useHistory(); + const isBlockBasedTheme = useSelect( ( select ) => { + return select( coreStore ).getCurrentTheme().is_block_theme; + }, [] ); - const commands = useMemo( () => { - if ( ! isBlockBasedTheme ) { - return []; - } + const commands = useMemo( () => { + if ( ! isBlockBasedTheme ) { + return []; + } - return [ - { - name: 'core/edit-site/open-styles', - label: __( 'Open styles' ), - callback: ( { close } ) => { - close(); - if ( ! params.postId ) { - history.push( { - path: '/wp_global_styles', - canvas: 'edit', - } ); - } - if ( params.postId && canvas !== 'edit' ) { - history.push( - { ...params, canvas: 'edit' }, - undefined, - { - transition: 'canvas-mode-edit-transition', - } - ); - } - openGeneralSidebar( 'edit-site/global-styles' ); + return [ + { + name: 'core/edit-site/open-styles', + label: __( 'Open styles' ), + callback: ( { close } ) => { + close(); + if ( ! params.postId ) { + history.push( { + path: '/wp_global_styles', + canvas: 'edit', + } ); + } + if ( params.postId && canvas !== 'edit' ) { + history.push( + { ...params, canvas: 'edit' }, + undefined, + { + transition: 'canvas-mode-edit-transition', + } + ); + } + openGeneralSidebar( 'edit-site/global-styles' ); + }, + icon: styles, }, - icon: styles, - }, - ]; - }, [ history, openGeneralSidebar, params, canvas, isBlockBasedTheme ] ); + ]; + }, [ history, openGeneralSidebar, params, canvas, isBlockBasedTheme ] ); - return { - isLoading: false, - commands, + return { + isLoading: false, + commands, + }; }; -} -function useGlobalStylesToggleWelcomeGuideCommands() { - const { openGeneralSidebar } = unlock( useDispatch( editSiteStore ) ); - const { params } = useLocation(); - const { canvas = 'view' } = params; - const { set } = useDispatch( preferencesStore ); +const getGlobalStylesToggleWelcomeGuideCommands = () => + function useGlobalStylesToggleWelcomeGuideCommands() { + const { openGeneralSidebar } = unlock( useDispatch( editSiteStore ) ); + const { params } = useLocation(); + const { canvas = 'view' } = params; + const { set } = useDispatch( preferencesStore ); - const history = useHistory(); - const isBlockBasedTheme = useSelect( ( select ) => { - return select( coreStore ).getCurrentTheme().is_block_theme; - }, [] ); + const history = useHistory(); + const isBlockBasedTheme = useSelect( ( select ) => { + return select( coreStore ).getCurrentTheme().is_block_theme; + }, [] ); - const commands = useMemo( () => { - if ( ! isBlockBasedTheme ) { - return []; - } + const commands = useMemo( () => { + if ( ! isBlockBasedTheme ) { + return []; + } - return [ - { - name: 'core/edit-site/toggle-styles-welcome-guide', - label: __( 'Learn about styles' ), - callback: ( { close } ) => { - close(); - if ( ! params.postId ) { - history.push( { - path: '/wp_global_styles', - canvas: 'edit', - } ); - } - if ( params.postId && canvas !== 'edit' ) { - history.push( - { - ...params, + return [ + { + name: 'core/edit-site/toggle-styles-welcome-guide', + label: __( 'Learn about styles' ), + callback: ( { close } ) => { + close(); + if ( ! params.postId ) { + history.push( { + path: '/wp_global_styles', canvas: 'edit', - }, - undefined, - { - transition: 'canvas-mode-edit-transition', - } - ); - } - openGeneralSidebar( 'edit-site/global-styles' ); - set( 'core/edit-site', 'welcomeGuideStyles', true ); - // sometimes there's a focus loss that happens after some time - // that closes the modal, we need to force reopening it. - setTimeout( () => { + } ); + } + if ( params.postId && canvas !== 'edit' ) { + history.push( + { + ...params, + canvas: 'edit', + }, + undefined, + { + transition: 'canvas-mode-edit-transition', + } + ); + } + openGeneralSidebar( 'edit-site/global-styles' ); set( 'core/edit-site', 'welcomeGuideStyles', true ); - }, 500 ); + // sometimes there's a focus loss that happens after some time + // that closes the modal, we need to force reopening it. + setTimeout( () => { + set( 'core/edit-site', 'welcomeGuideStyles', true ); + }, 500 ); + }, + icon: help, }, - icon: help, - }, - ]; - }, [ - history, - openGeneralSidebar, - canvas, - isBlockBasedTheme, - set, - params, - ] ); + ]; + }, [ + history, + openGeneralSidebar, + canvas, + isBlockBasedTheme, + set, + params, + ] ); - return { - isLoading: false, - commands, + return { + isLoading: false, + commands, + }; }; -} -function useGlobalStylesResetCommands() { - const [ canReset, onReset ] = useGlobalStylesReset(); - const commands = useMemo( () => { - if ( ! canReset ) { - return []; - } +const getGlobalStylesResetCommands = () => + function useGlobalStylesResetCommands() { + const [ canReset, onReset ] = useGlobalStylesReset(); + const commands = useMemo( () => { + if ( ! canReset ) { + return []; + } - return [ - { - name: 'core/edit-site/reset-global-styles', - label: __( 'Reset styles' ), - icon: isRTL() ? rotateRight : rotateLeft, - callback: ( { close } ) => { - close(); - onReset(); + return [ + { + name: 'core/edit-site/reset-global-styles', + label: __( 'Reset styles' ), + icon: isRTL() ? rotateRight : rotateLeft, + callback: ( { close } ) => { + close(); + onReset(); + }, }, - }, - ]; - }, [ canReset, onReset ] ); + ]; + }, [ canReset, onReset ] ); - return { - isLoading: false, - commands, + return { + isLoading: false, + commands, + }; }; -} -function useGlobalStylesOpenCssCommands() { - const { openGeneralSidebar, setEditorCanvasContainerView } = unlock( - useDispatch( editSiteStore ) - ); - const { params } = useLocation(); - const { canvas = 'view' } = params; - const history = useHistory(); - const { canEditCSS } = useSelect( ( select ) => { - const { getEntityRecord, __experimentalGetCurrentGlobalStylesId } = - select( coreStore ); +const getGlobalStylesOpenCssCommands = () => + function useGlobalStylesOpenCssCommands() { + const { openGeneralSidebar, setEditorCanvasContainerView } = unlock( + useDispatch( editSiteStore ) + ); + const { params } = useLocation(); + const { canvas = 'view' } = params; + const history = useHistory(); + const { canEditCSS } = useSelect( ( select ) => { + const { getEntityRecord, __experimentalGetCurrentGlobalStylesId } = + select( coreStore ); - const globalStylesId = __experimentalGetCurrentGlobalStylesId(); - const globalStyles = globalStylesId - ? getEntityRecord( 'root', 'globalStyles', globalStylesId ) - : undefined; + const globalStylesId = __experimentalGetCurrentGlobalStylesId(); + const globalStyles = globalStylesId + ? getEntityRecord( 'root', 'globalStyles', globalStylesId ) + : undefined; - return { - canEditCSS: !! globalStyles?._links?.[ 'wp:action-edit-css' ], - }; - }, [] ); + return { + canEditCSS: !! globalStyles?._links?.[ 'wp:action-edit-css' ], + }; + }, [] ); - const commands = useMemo( () => { - if ( ! canEditCSS ) { - return []; - } + const commands = useMemo( () => { + if ( ! canEditCSS ) { + return []; + } - return [ - { - name: 'core/edit-site/open-styles-css', - label: __( 'Customize CSS' ), - icon: brush, - callback: ( { close } ) => { - close(); - if ( ! params.postId ) { - history.push( { - path: '/wp_global_styles', - canvas: 'edit', - } ); - } - if ( params.postId && canvas !== 'edit' ) { - history.push( - { - ...params, + return [ + { + name: 'core/edit-site/open-styles-css', + label: __( 'Customize CSS' ), + icon: brush, + callback: ( { close } ) => { + close(); + if ( ! params.postId ) { + history.push( { + path: '/wp_global_styles', canvas: 'edit', - }, - undefined, - { - transition: 'canvas-mode-edit-transition', - } - ); - } - openGeneralSidebar( 'edit-site/global-styles' ); - setEditorCanvasContainerView( 'global-styles-css' ); + } ); + } + if ( params.postId && canvas !== 'edit' ) { + history.push( + { + ...params, + canvas: 'edit', + }, + undefined, + { + transition: 'canvas-mode-edit-transition', + } + ); + } + openGeneralSidebar( 'edit-site/global-styles' ); + setEditorCanvasContainerView( 'global-styles-css' ); + }, }, - }, - ]; - }, [ - history, - openGeneralSidebar, - setEditorCanvasContainerView, - canEditCSS, - canvas, - params, - ] ); - return { - isLoading: false, - commands, + ]; + }, [ + history, + openGeneralSidebar, + setEditorCanvasContainerView, + canEditCSS, + canvas, + params, + ] ); + return { + isLoading: false, + commands, + }; }; -} -function useGlobalStylesOpenRevisionsCommands() { - const { openGeneralSidebar, setEditorCanvasContainerView } = unlock( - useDispatch( editSiteStore ) - ); - const { params } = useLocation(); - const { canvas = 'view' } = params; - const history = useHistory(); - const hasRevisions = useSelect( ( select ) => { - const { getEntityRecord, __experimentalGetCurrentGlobalStylesId } = - select( coreStore ); - const globalStylesId = __experimentalGetCurrentGlobalStylesId(); - const globalStyles = globalStylesId - ? getEntityRecord( 'root', 'globalStyles', globalStylesId ) - : undefined; - return !! globalStyles?._links?.[ 'version-history' ]?.[ 0 ]?.count; - }, [] ); +const getGlobalStylesOpenRevisionsCommands = () => + function useGlobalStylesOpenRevisionsCommands() { + const { openGeneralSidebar, setEditorCanvasContainerView } = unlock( + useDispatch( editSiteStore ) + ); + const { params } = useLocation(); + const { canvas = 'view' } = params; + const history = useHistory(); + const hasRevisions = useSelect( ( select ) => { + const { getEntityRecord, __experimentalGetCurrentGlobalStylesId } = + select( coreStore ); + const globalStylesId = __experimentalGetCurrentGlobalStylesId(); + const globalStyles = globalStylesId + ? getEntityRecord( 'root', 'globalStyles', globalStylesId ) + : undefined; + return !! globalStyles?._links?.[ 'version-history' ]?.[ 0 ]?.count; + }, [] ); - const commands = useMemo( () => { - if ( ! hasRevisions ) { - return []; - } + const commands = useMemo( () => { + if ( ! hasRevisions ) { + return []; + } - return [ - { - name: 'core/edit-site/open-global-styles-revisions', - label: __( 'Style revisions' ), - icon: backup, - callback: ( { close } ) => { - close(); - if ( ! params.postId ) { - history.push( { - path: '/wp_global_styles', - canvas: 'edit', - } ); - } - if ( params.postId && canvas !== 'edit' ) { - history.push( - { - ...params, + return [ + { + name: 'core/edit-site/open-global-styles-revisions', + label: __( 'Style revisions' ), + icon: backup, + callback: ( { close } ) => { + close(); + if ( ! params.postId ) { + history.push( { + path: '/wp_global_styles', canvas: 'edit', - }, - undefined, - { - transition: 'canvas-mode-edit-transition', - } + } ); + } + if ( params.postId && canvas !== 'edit' ) { + history.push( + { + ...params, + canvas: 'edit', + }, + undefined, + { + transition: 'canvas-mode-edit-transition', + } + ); + } + openGeneralSidebar( 'edit-site/global-styles' ); + setEditorCanvasContainerView( + 'global-styles-revisions' ); - } - openGeneralSidebar( 'edit-site/global-styles' ); - setEditorCanvasContainerView( 'global-styles-revisions' ); + }, }, - }, - ]; - }, [ - hasRevisions, - history, - openGeneralSidebar, - setEditorCanvasContainerView, - canvas, - params, - ] ); + ]; + }, [ + hasRevisions, + history, + openGeneralSidebar, + setEditorCanvasContainerView, + canvas, + params, + ] ); - return { - isLoading: false, - commands, + return { + isLoading: false, + commands, + }; }; -} export function useCommonCommands() { const homeUrl = useSelect( ( select ) => { @@ -324,26 +331,26 @@ export function useCommonCommands() { useCommandLoader( { name: 'core/edit-site/open-styles', - hook: useGlobalStylesOpenStylesCommands, + hook: getGlobalStylesOpenStylesCommands(), } ); useCommandLoader( { name: 'core/edit-site/toggle-styles-welcome-guide', - hook: useGlobalStylesToggleWelcomeGuideCommands, + hook: getGlobalStylesToggleWelcomeGuideCommands(), } ); useCommandLoader( { name: 'core/edit-site/reset-global-styles', - hook: useGlobalStylesResetCommands, + hook: getGlobalStylesResetCommands(), } ); useCommandLoader( { name: 'core/edit-site/open-styles-css', - hook: useGlobalStylesOpenCssCommands, + hook: getGlobalStylesOpenCssCommands(), } ); useCommandLoader( { name: 'core/edit-site/open-styles-revisions', - hook: useGlobalStylesOpenRevisionsCommands, + hook: getGlobalStylesOpenRevisionsCommands(), } ); } diff --git a/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js b/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js index 97283044193892..da36f32e6c0d51 100644 --- a/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js +++ b/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js @@ -22,147 +22,152 @@ import { useLink } from '../../components/routes/link'; const { useHistory, useLocation } = unlock( routerPrivateApis ); -function usePageContentFocusCommands() { - const { record: template } = useEditedEntityRecord(); - const { params } = useLocation(); - const { canvas = 'view' } = params; - const { isPage, templateId, currentPostType } = useSelect( ( select ) => { - const { isPage: _isPage } = unlock( select( editSiteStore ) ); - const { getCurrentPostType, getCurrentTemplateId } = - select( editorStore ); - return { - isPage: _isPage(), - templateId: getCurrentTemplateId(), - currentPostType: getCurrentPostType(), - }; - }, [] ); - - const { onClick: editTemplate } = useLink( { - postType: 'wp_template', - postId: templateId, - } ); - - const { setRenderingMode } = useDispatch( editorStore ); - - if ( ! isPage || canvas !== 'edit' ) { - return { isLoading: false, commands: [] }; - } - - const commands = []; - - if ( currentPostType !== 'wp_template' ) { - commands.push( { - name: 'core/switch-to-template-focus', - label: sprintf( - /* translators: %s: template title */ - __( 'Edit template: %s' ), - decodeEntities( template.title ) - ), - icon: layout, - callback: ( { close } ) => { - editTemplate(); - close(); - }, - } ); - } else { - commands.push( { - name: 'core/switch-to-page-focus', - label: __( 'Back to page' ), - icon: page, - callback: ( { close } ) => { - setRenderingMode( 'template-locked' ); - close(); +const getPageContentFocusCommands = () => + function usePageContentFocusCommands() { + const { record: template } = useEditedEntityRecord(); + const { params } = useLocation(); + const { canvas = 'view' } = params; + const { isPage, templateId, currentPostType } = useSelect( + ( select ) => { + const { isPage: _isPage } = unlock( select( editSiteStore ) ); + const { getCurrentPostType, getCurrentTemplateId } = + select( editorStore ); + return { + isPage: _isPage(), + templateId: getCurrentTemplateId(), + currentPostType: getCurrentPostType(), + }; }, + [] + ); + + const { onClick: editTemplate } = useLink( { + postType: 'wp_template', + postId: templateId, } ); - } - return { isLoading: false, commands }; -} + const { setRenderingMode } = useDispatch( editorStore ); + + if ( ! isPage || canvas !== 'edit' ) { + return { isLoading: false, commands: [] }; + } + + const commands = []; + + if ( currentPostType !== 'wp_template' ) { + commands.push( { + name: 'core/switch-to-template-focus', + label: sprintf( + /* translators: %s: template title */ + __( 'Edit template: %s' ), + decodeEntities( template.title ) + ), + icon: layout, + callback: ( { close } ) => { + editTemplate(); + close(); + }, + } ); + } else { + commands.push( { + name: 'core/switch-to-page-focus', + label: __( 'Back to page' ), + icon: page, + callback: ( { close } ) => { + setRenderingMode( 'template-locked' ); + close(); + }, + } ); + } + + return { isLoading: false, commands }; + }; -function useManipulateDocumentCommands() { - const { isLoaded, record: template } = useEditedEntityRecord(); - const { removeTemplate, revertTemplate } = useDispatch( editSiteStore ); - const history = useHistory(); - const isEditingPage = useSelect( - ( select ) => - select( editSiteStore ).isPage() && - select( editorStore ).getCurrentPostType() !== 'wp_template', - [] - ); - - if ( ! isLoaded ) { - return { isLoading: true, commands: [] }; - } - - const commands = []; - - if ( isTemplateRevertable( template ) && ! isEditingPage ) { - const label = - template.type === TEMPLATE_POST_TYPE - ? sprintf( - /* translators: %s: template title */ - __( 'Reset template: %s' ), - decodeEntities( template.title ) - ) - : sprintf( - /* translators: %s: template part title */ - __( 'Reset template part: %s' ), - decodeEntities( template.title ) - ); - commands.push( { - name: 'core/reset-template', - label, - icon: isRTL() ? rotateRight : rotateLeft, - callback: ( { close } ) => { - revertTemplate( template ); - close(); - }, - } ); - } - - if ( isTemplateRemovable( template ) && ! isEditingPage ) { - const label = - template.type === TEMPLATE_POST_TYPE - ? sprintf( - /* translators: %s: template title */ - __( 'Delete template: %s' ), - decodeEntities( template.title ) - ) - : sprintf( - /* translators: %s: template part title */ - __( 'Delete template part: %s' ), - decodeEntities( template.title ) - ); - commands.push( { - name: 'core/remove-template', - label, - icon: trash, - callback: ( { close } ) => { - removeTemplate( template ); - // Navigate to the template list - history.push( { - postType: template.type, - } ); - close(); - }, - } ); - } +const getManipulateDocumentCommands = () => + function useManipulateDocumentCommands() { + const { isLoaded, record: template } = useEditedEntityRecord(); + const { removeTemplate, revertTemplate } = useDispatch( editSiteStore ); + const history = useHistory(); + const isEditingPage = useSelect( + ( select ) => + select( editSiteStore ).isPage() && + select( editorStore ).getCurrentPostType() !== 'wp_template', + [] + ); + + if ( ! isLoaded ) { + return { isLoading: true, commands: [] }; + } + + const commands = []; + + if ( isTemplateRevertable( template ) && ! isEditingPage ) { + const label = + template.type === TEMPLATE_POST_TYPE + ? sprintf( + /* translators: %s: template title */ + __( 'Reset template: %s' ), + decodeEntities( template.title ) + ) + : sprintf( + /* translators: %s: template part title */ + __( 'Reset template part: %s' ), + decodeEntities( template.title ) + ); + commands.push( { + name: 'core/reset-template', + label, + icon: isRTL() ? rotateRight : rotateLeft, + callback: ( { close } ) => { + revertTemplate( template ); + close(); + }, + } ); + } + + if ( isTemplateRemovable( template ) && ! isEditingPage ) { + const label = + template.type === TEMPLATE_POST_TYPE + ? sprintf( + /* translators: %s: template title */ + __( 'Delete template: %s' ), + decodeEntities( template.title ) + ) + : sprintf( + /* translators: %s: template part title */ + __( 'Delete template part: %s' ), + decodeEntities( template.title ) + ); + commands.push( { + name: 'core/remove-template', + label, + icon: trash, + callback: ( { close } ) => { + removeTemplate( template ); + // Navigate to the template list + history.push( { + postType: template.type, + } ); + close(); + }, + } ); + } - return { - isLoading: ! isLoaded, - commands, + return { + isLoading: ! isLoaded, + commands, + }; }; -} export function useEditModeCommands() { useCommandLoader( { name: 'core/edit-site/page-content-focus', - hook: usePageContentFocusCommands, + hook: getPageContentFocusCommands(), context: 'entity-edit', } ); useCommandLoader( { name: 'core/edit-site/manipulate-document', - hook: useManipulateDocumentCommands, + hook: getManipulateDocumentCommands(), } ); } diff --git a/packages/editor/src/components/commands/index.js b/packages/editor/src/components/commands/index.js index b4b9c05db256d7..16260bed3978fd 100644 --- a/packages/editor/src/components/commands/index.js +++ b/packages/editor/src/components/commands/index.js @@ -30,279 +30,287 @@ import { PATTERN_POST_TYPE } from '../../store/constants'; import { modalName as patternRenameModalName } from '../pattern-rename-modal'; import { modalName as patternDuplicateModalName } from '../pattern-duplicate-modal'; -function useEditorCommandLoader() { - const { - editorMode, - isListViewOpen, - showBlockBreadcrumbs, - isDistractionFree, - isFocusMode, - isPreviewMode, - isViewable, - isCodeEditingEnabled, - isRichEditingEnabled, - isPublishSidebarEnabled, - } = useSelect( ( select ) => { - const { get } = select( preferencesStore ); - const { isListViewOpened, getCurrentPostType, getEditorSettings } = - select( editorStore ); - const { getSettings } = select( blockEditorStore ); - const { getPostType } = select( coreStore ); +const getEditorCommandLoader = () => + function useEditorCommandLoader() { + const { + editorMode, + isListViewOpen, + showBlockBreadcrumbs, + isDistractionFree, + isFocusMode, + isPreviewMode, + isViewable, + isCodeEditingEnabled, + isRichEditingEnabled, + isPublishSidebarEnabled, + } = useSelect( ( select ) => { + const { get } = select( preferencesStore ); + const { isListViewOpened, getCurrentPostType, getEditorSettings } = + select( editorStore ); + const { getSettings } = select( blockEditorStore ); + const { getPostType } = select( coreStore ); - return { - editorMode: get( 'core', 'editorMode' ) ?? 'visual', - isListViewOpen: isListViewOpened(), - showBlockBreadcrumbs: get( 'core', 'showBlockBreadcrumbs' ), - isDistractionFree: get( 'core', 'distractionFree' ), - isFocusMode: get( 'core', 'focusMode' ), - isPreviewMode: getSettings().isPreviewMode, - isViewable: getPostType( getCurrentPostType() )?.viewable ?? false, - isCodeEditingEnabled: getEditorSettings().codeEditingEnabled, - isRichEditingEnabled: getEditorSettings().richEditingEnabled, - isPublishSidebarEnabled: - select( editorStore ).isPublishSidebarEnabled(), - }; - }, [] ); - const { getActiveComplementaryArea } = useSelect( interfaceStore ); - const { toggle } = useDispatch( preferencesStore ); - const { createInfoNotice } = useDispatch( noticesStore ); - const { - __unstableSaveForPreview, - setIsListViewOpened, - switchEditorMode, - toggleDistractionFree, - toggleSpotlightMode, - toggleTopToolbar, - } = useDispatch( editorStore ); - const { openModal, enableComplementaryArea, disableComplementaryArea } = - useDispatch( interfaceStore ); - const { getCurrentPostId } = useSelect( editorStore ); - const allowSwitchEditorMode = isCodeEditingEnabled && isRichEditingEnabled; + return { + editorMode: get( 'core', 'editorMode' ) ?? 'visual', + isListViewOpen: isListViewOpened(), + showBlockBreadcrumbs: get( 'core', 'showBlockBreadcrumbs' ), + isDistractionFree: get( 'core', 'distractionFree' ), + isFocusMode: get( 'core', 'focusMode' ), + isPreviewMode: getSettings().isPreviewMode, + isViewable: + getPostType( getCurrentPostType() )?.viewable ?? false, + isCodeEditingEnabled: getEditorSettings().codeEditingEnabled, + isRichEditingEnabled: getEditorSettings().richEditingEnabled, + isPublishSidebarEnabled: + select( editorStore ).isPublishSidebarEnabled(), + }; + }, [] ); + const { getActiveComplementaryArea } = useSelect( interfaceStore ); + const { toggle } = useDispatch( preferencesStore ); + const { createInfoNotice } = useDispatch( noticesStore ); + const { + __unstableSaveForPreview, + setIsListViewOpened, + switchEditorMode, + toggleDistractionFree, + toggleSpotlightMode, + toggleTopToolbar, + } = useDispatch( editorStore ); + const { openModal, enableComplementaryArea, disableComplementaryArea } = + useDispatch( interfaceStore ); + const { getCurrentPostId } = useSelect( editorStore ); + const allowSwitchEditorMode = + isCodeEditingEnabled && isRichEditingEnabled; - if ( isPreviewMode ) { - return { commands: [], isLoading: false }; - } + if ( isPreviewMode ) { + return { commands: [], isLoading: false }; + } - const commands = []; + const commands = []; - commands.push( { - name: 'core/open-shortcut-help', - label: __( 'Keyboard shortcuts' ), - icon: keyboard, - callback: ( { close } ) => { - close(); - openModal( 'editor/keyboard-shortcut-help' ); - }, - } ); - - commands.push( { - name: 'core/toggle-distraction-free', - label: isDistractionFree - ? __( 'Exit Distraction free' ) - : __( 'Enter Distraction free' ), - callback: ( { close } ) => { - toggleDistractionFree(); - close(); - }, - } ); - - commands.push( { - name: 'core/open-preferences', - label: __( 'Editor preferences' ), - callback: ( { close } ) => { - close(); - openModal( 'editor/preferences' ); - }, - } ); - - commands.push( { - name: 'core/toggle-spotlight-mode', - label: isFocusMode - ? __( 'Exit Spotlight mode' ) - : __( 'Enter Spotlight mode' ), - callback: ( { close } ) => { - toggleSpotlightMode(); - close(); - }, - } ); - - commands.push( { - name: 'core/toggle-list-view', - label: isListViewOpen - ? __( 'Close List View' ) - : __( 'Open List View' ), - icon: listView, - callback: ( { close } ) => { - setIsListViewOpened( ! isListViewOpen ); - close(); - createInfoNotice( - isListViewOpen ? __( 'List View off.' ) : __( 'List View on.' ), - { - id: 'core/editor/toggle-list-view/notice', - type: 'snackbar', - } - ); - }, - } ); + commands.push( { + name: 'core/open-shortcut-help', + label: __( 'Keyboard shortcuts' ), + icon: keyboard, + callback: ( { close } ) => { + close(); + openModal( 'editor/keyboard-shortcut-help' ); + }, + } ); - commands.push( { - name: 'core/toggle-top-toolbar', - label: __( 'Top toolbar' ), - callback: ( { close } ) => { - toggleTopToolbar(); - close(); - }, - } ); + commands.push( { + name: 'core/toggle-distraction-free', + label: isDistractionFree + ? __( 'Exit Distraction free' ) + : __( 'Enter Distraction free' ), + callback: ( { close } ) => { + toggleDistractionFree(); + close(); + }, + } ); - if ( allowSwitchEditorMode ) { commands.push( { - name: 'core/toggle-code-editor', - label: - editorMode === 'visual' - ? __( 'Open code editor' ) - : __( 'Exit code editor' ), - icon: code, + name: 'core/open-preferences', + label: __( 'Editor preferences' ), callback: ( { close } ) => { - switchEditorMode( editorMode === 'visual' ? 'text' : 'visual' ); close(); + openModal( 'editor/preferences' ); }, } ); - } - commands.push( { - name: 'core/toggle-breadcrumbs', - label: showBlockBreadcrumbs - ? __( 'Hide block breadcrumbs' ) - : __( 'Show block breadcrumbs' ), - callback: ( { close } ) => { - toggle( 'core', 'showBlockBreadcrumbs' ); - close(); - createInfoNotice( - showBlockBreadcrumbs - ? __( 'Breadcrumbs hidden.' ) - : __( 'Breadcrumbs visible.' ), - { - id: 'core/editor/toggle-breadcrumbs/notice', - type: 'snackbar', - } - ); - }, - } ); + commands.push( { + name: 'core/toggle-spotlight-mode', + label: isFocusMode + ? __( 'Exit Spotlight mode' ) + : __( 'Enter Spotlight mode' ), + callback: ( { close } ) => { + toggleSpotlightMode(); + close(); + }, + } ); - commands.push( { - name: 'core/open-settings-sidebar', - label: __( 'Show or hide the Settings panel.' ), - icon: isRTL() ? drawerLeft : drawerRight, - callback: ( { close } ) => { - const activeSidebar = getActiveComplementaryArea( 'core' ); - close(); - if ( activeSidebar === 'edit-post/document' ) { - disableComplementaryArea( 'core' ); - } else { - enableComplementaryArea( 'core', 'edit-post/document' ); - } - }, - } ); + commands.push( { + name: 'core/toggle-list-view', + label: isListViewOpen + ? __( 'Close List View' ) + : __( 'Open List View' ), + icon: listView, + callback: ( { close } ) => { + setIsListViewOpened( ! isListViewOpen ); + close(); + createInfoNotice( + isListViewOpen + ? __( 'List View off.' ) + : __( 'List View on.' ), + { + id: 'core/editor/toggle-list-view/notice', + type: 'snackbar', + } + ); + }, + } ); - commands.push( { - name: 'core/open-block-inspector', - label: __( 'Show or hide the Block settings panel' ), - icon: blockDefault, - callback: ( { close } ) => { - const activeSidebar = getActiveComplementaryArea( 'core' ); - close(); - if ( activeSidebar === 'edit-post/block' ) { - disableComplementaryArea( 'core' ); - } else { - enableComplementaryArea( 'core', 'edit-post/block' ); - } - }, - } ); + commands.push( { + name: 'core/toggle-top-toolbar', + label: __( 'Top toolbar' ), + callback: ( { close } ) => { + toggleTopToolbar(); + close(); + }, + } ); - commands.push( { - name: 'core/toggle-publish-sidebar', - label: isPublishSidebarEnabled - ? __( 'Disable pre-publish checks' ) - : __( 'Enable pre-publish checks' ), - icon: formatListBullets, - callback: ( { close } ) => { - close(); - toggle( 'core', 'isPublishSidebarEnabled' ); - createInfoNotice( - isPublishSidebarEnabled - ? __( 'Pre-publish checks disabled.' ) - : __( 'Pre-publish checks enabled.' ), - { - id: 'core/editor/publish-sidebar/notice', - type: 'snackbar', - } - ); - }, - } ); + if ( allowSwitchEditorMode ) { + commands.push( { + name: 'core/toggle-code-editor', + label: + editorMode === 'visual' + ? __( 'Open code editor' ) + : __( 'Exit code editor' ), + icon: code, + callback: ( { close } ) => { + switchEditorMode( + editorMode === 'visual' ? 'text' : 'visual' + ); + close(); + }, + } ); + } - if ( isViewable ) { commands.push( { - name: 'core/preview-link', - label: __( 'Preview in a new tab' ), - icon: external, - callback: async ( { close } ) => { + name: 'core/toggle-breadcrumbs', + label: showBlockBreadcrumbs + ? __( 'Hide block breadcrumbs' ) + : __( 'Show block breadcrumbs' ), + callback: ( { close } ) => { + toggle( 'core', 'showBlockBreadcrumbs' ); close(); - const postId = getCurrentPostId(); - const link = await __unstableSaveForPreview(); - window.open( link, `wp-preview-${ postId }` ); + createInfoNotice( + showBlockBreadcrumbs + ? __( 'Breadcrumbs hidden.' ) + : __( 'Breadcrumbs visible.' ), + { + id: 'core/editor/toggle-breadcrumbs/notice', + type: 'snackbar', + } + ); }, } ); - } - - return { - commands, - isLoading: false, - }; -} -function useEditedEntityContextualCommands() { - const { postType } = useSelect( ( select ) => { - const { getCurrentPostType } = select( editorStore ); - return { - postType: getCurrentPostType(), - }; - }, [] ); - const { openModal } = useDispatch( interfaceStore ); - const commands = []; + commands.push( { + name: 'core/open-settings-sidebar', + label: __( 'Show or hide the Settings panel.' ), + icon: isRTL() ? drawerLeft : drawerRight, + callback: ( { close } ) => { + const activeSidebar = getActiveComplementaryArea( 'core' ); + close(); + if ( activeSidebar === 'edit-post/document' ) { + disableComplementaryArea( 'core' ); + } else { + enableComplementaryArea( 'core', 'edit-post/document' ); + } + }, + } ); - if ( postType === PATTERN_POST_TYPE ) { commands.push( { - name: 'core/rename-pattern', - label: __( 'Rename pattern' ), - icon: edit, + name: 'core/open-block-inspector', + label: __( 'Show or hide the Block settings panel' ), + icon: blockDefault, callback: ( { close } ) => { - openModal( patternRenameModalName ); + const activeSidebar = getActiveComplementaryArea( 'core' ); close(); + if ( activeSidebar === 'edit-post/block' ) { + disableComplementaryArea( 'core' ); + } else { + enableComplementaryArea( 'core', 'edit-post/block' ); + } }, } ); + commands.push( { - name: 'core/duplicate-pattern', - label: __( 'Duplicate pattern' ), - icon: symbol, + name: 'core/toggle-publish-sidebar', + label: isPublishSidebarEnabled + ? __( 'Disable pre-publish checks' ) + : __( 'Enable pre-publish checks' ), + icon: formatListBullets, callback: ( { close } ) => { - openModal( patternDuplicateModalName ); close(); + toggle( 'core', 'isPublishSidebarEnabled' ); + createInfoNotice( + isPublishSidebarEnabled + ? __( 'Pre-publish checks disabled.' ) + : __( 'Pre-publish checks enabled.' ), + { + id: 'core/editor/publish-sidebar/notice', + type: 'snackbar', + } + ); }, } ); - } - return { isLoading: false, commands }; -} + if ( isViewable ) { + commands.push( { + name: 'core/preview-link', + label: __( 'Preview in a new tab' ), + icon: external, + callback: async ( { close } ) => { + close(); + const postId = getCurrentPostId(); + const link = await __unstableSaveForPreview(); + window.open( link, `wp-preview-${ postId }` ); + }, + } ); + } + + return { + commands, + isLoading: false, + }; + }; + +const getEditedEntityContextualCommands = () => + function useEditedEntityContextualCommands() { + const { postType } = useSelect( ( select ) => { + const { getCurrentPostType } = select( editorStore ); + return { + postType: getCurrentPostType(), + }; + }, [] ); + const { openModal } = useDispatch( interfaceStore ); + const commands = []; + + if ( postType === PATTERN_POST_TYPE ) { + commands.push( { + name: 'core/rename-pattern', + label: __( 'Rename pattern' ), + icon: edit, + callback: ( { close } ) => { + openModal( patternRenameModalName ); + close(); + }, + } ); + commands.push( { + name: 'core/duplicate-pattern', + label: __( 'Duplicate pattern' ), + icon: symbol, + callback: ( { close } ) => { + openModal( patternDuplicateModalName ); + close(); + }, + } ); + } + + return { isLoading: false, commands }; + }; export default function useCommands() { useCommandLoader( { name: 'core/editor/edit-ui', - hook: useEditorCommandLoader, + hook: getEditorCommandLoader(), } ); useCommandLoader( { name: 'core/editor/contextual-commands', - hook: useEditedEntityContextualCommands, + hook: getEditedEntityContextualCommands(), context: 'entity-edit', } ); } From f15b4c19175da72843234cc6c862296ce9a4dcf8 Mon Sep 17 00:00:00 2001 From: Andrea Fercia Date: Thu, 7 Nov 2024 09:55:42 +0100 Subject: [PATCH 32/72] Fix inconsistent sidebars close buttons sizes (#66756) * Increase complementary area and tabbed sidebar X close button size. * Adjust widgets header actions right padding and gap. * Adjust edit widgets list view X close button size. Co-authored-by: afercia Co-authored-by: mikachan Co-authored-by: richtabor Co-authored-by: t-hamano Co-authored-by: jameskoster --- .../block-editor/src/components/tabbed-sidebar/index.js | 2 +- .../block-editor/src/components/tabbed-sidebar/style.scss | 2 +- packages/edit-widgets/src/components/header/style.scss | 7 ++++--- .../src/components/secondary-sidebar/list-view-sidebar.js | 2 +- .../src/components/secondary-sidebar/style.scss | 2 +- packages/editor/src/components/sidebar/style.scss | 4 +--- .../src/components/complementary-area-header/style.scss | 4 ++-- .../interface/src/components/complementary-area/index.js | 2 +- 8 files changed, 12 insertions(+), 13 deletions(-) diff --git a/packages/block-editor/src/components/tabbed-sidebar/index.js b/packages/block-editor/src/components/tabbed-sidebar/index.js index a0cb510c720904..c9ff6bbf6555f0 100644 --- a/packages/block-editor/src/components/tabbed-sidebar/index.js +++ b/packages/block-editor/src/components/tabbed-sidebar/index.js @@ -33,7 +33,7 @@ function TabbedSidebar( icon={ closeSmall } label={ closeButtonLabel } onClick={ () => onClose() } - size="small" + size="compact" /> { __( 'List View' ) }
disableComplementaryArea( scope ) } toggleButtonProps={ { label: closeLabel, - size: 'small', + size: 'compact', shortcut: toggleShortcut, scope, identifier, From b0d2041e2b1de19aed1f088b4f9b6068cfd89e47 Mon Sep 17 00:00:00 2001 From: Ella <4710635+ellatrix@users.noreply.github.com> Date: Thu, 7 Nov 2024 10:06:59 +0100 Subject: [PATCH 33/72] Iframe: always enable for block themes, in core too (#66800) Co-authored-by: ellatrix Co-authored-by: jsnajdr --- .../components/layout/use-should-iframe.js | 50 +++++++------------ 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/packages/edit-post/src/components/layout/use-should-iframe.js b/packages/edit-post/src/components/layout/use-should-iframe.js index 6da344c138f64b..cd2a893d8d1cd0 100644 --- a/packages/edit-post/src/components/layout/use-should-iframe.js +++ b/packages/edit-post/src/components/layout/use-should-iframe.js @@ -11,39 +11,27 @@ import { store as blockEditorStore } from '@wordpress/block-editor'; */ import { unlock } from '../../lock-unlock'; -const isGutenbergPlugin = globalThis.IS_GUTENBERG_PLUGIN ? true : false; - export function useShouldIframe() { - const { - isBlockBasedTheme, - hasV3BlocksOnly, - isEditingTemplateOrPattern, - isZoomOutMode, - deviceType, - } = useSelect( ( select ) => { + return useSelect( ( select ) => { const { getEditorSettings, getCurrentPostType, getDeviceType } = select( editorStore ); - const { isZoomOut } = unlock( select( blockEditorStore ) ); - const { getBlockTypes } = select( blocksStore ); - const editorSettings = getEditorSettings(); - return { - isBlockBasedTheme: editorSettings.__unstableIsBlockBasedTheme, - hasV3BlocksOnly: getBlockTypes().every( ( type ) => { - return type.apiVersion >= 3; - } ), - isEditingTemplateOrPattern: [ 'wp_template', 'wp_block' ].includes( - getCurrentPostType() - ), - isZoomOutMode: isZoomOut(), - deviceType: getDeviceType(), - }; + return ( + // If the theme is block based, we ALWAYS use the iframe for + // consistency across the post and site editor. The iframe was + // introduced long before the sited editor and block themes, so + // these themes are expecting it. + getEditorSettings().__unstableIsBlockBasedTheme || + // For classic themes, we also still want to iframe all the special + // editor features and modes such as device previews, zoom out, and + // template/pattern editing. + getDeviceType() !== 'Desktop' || + [ 'wp_template', 'wp_block' ].includes( getCurrentPostType() ) || + unlock( select( blockEditorStore ) ).isZoomOut() || + // Finally, still iframe the editor for classic themes if all blocks + // are v3 (which means they are marked as iframe-compatible). + select( blocksStore ) + .getBlockTypes() + .every( ( type ) => type.apiVersion >= 3 ) + ); }, [] ); - - return ( - hasV3BlocksOnly || - ( isGutenbergPlugin && isBlockBasedTheme ) || - isEditingTemplateOrPattern || - isZoomOutMode || - [ 'Tablet', 'Mobile' ].includes( deviceType ) - ); } From 69f71e1170ced7897d5287ea3fbc9627e4b63c7f Mon Sep 17 00:00:00 2001 From: Marin Atanasov <8436925+tyxla@users.noreply.github.com> Date: Thu, 7 Nov 2024 18:14:20 +0900 Subject: [PATCH 34/72] Data: Rename useSelect internals to fix React Compiler violations (#66807) Co-authored-by: tyxla Co-authored-by: Mamaduka --- packages/data/src/components/use-select/index.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/data/src/components/use-select/index.js b/packages/data/src/components/use-select/index.js index 3e28e9489e977e..15a3c88d2d5543 100644 --- a/packages/data/src/components/use-select/index.js +++ b/packages/data/src/components/use-select/index.js @@ -209,11 +209,11 @@ function Store( registry, suspense ) { }; } -function useStaticSelect( storeName ) { +function _useStaticSelect( storeName ) { return useRegistry().select( storeName ); } -function useMappingSelect( suspense, mapSelect, deps ) { +function _useMappingSelect( suspense, mapSelect, deps ) { const registry = useRegistry(); const isAsync = useAsyncMode(); const store = useMemo( @@ -308,13 +308,11 @@ export default function useSelect( mapSelect, deps ) { ); } - /* eslint-disable react-hooks/rules-of-hooks */ // `staticSelectMode` is not allowed to change during the hook instance's, // lifetime, so the rules of hooks are not really violated. return staticSelectMode - ? useStaticSelect( mapSelect ) - : useMappingSelect( false, mapSelect, deps ); - /* eslint-enable react-hooks/rules-of-hooks */ + ? _useStaticSelect( mapSelect ) + : _useMappingSelect( false, mapSelect, deps ); } /** @@ -337,5 +335,5 @@ export default function useSelect( mapSelect, deps ) { * @return {ReturnType} Data object returned by the `mapSelect` function. */ export function useSuspenseSelect( mapSelect, deps ) { - return useMappingSelect( true, mapSelect, deps ); + return _useMappingSelect( true, mapSelect, deps ); } From 7874fc5526618666f081b131b9cf0c6489c6cd9c Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Thu, 7 Nov 2024 10:46:07 +0100 Subject: [PATCH 35/72] Block Editor: Fix React Compiler error for 'BlockProps' util (#66809) Co-authored-by: Mamaduka Co-authored-by: tyxla --- packages/block-editor/src/hooks/utils.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/hooks/utils.js b/packages/block-editor/src/hooks/utils.js index fa710fa7f08eba..4334f70b9d13bf 100644 --- a/packages/block-editor/src/hooks/utils.js +++ b/packages/block-editor/src/hooks/utils.js @@ -562,8 +562,13 @@ export function createBlockEditFilter( features ) { addFilter( 'editor.BlockEdit', 'core/editor/hooks', withBlockEditHooks ); } -function BlockProps( { index, useBlockProps, setAllWrapperProps, ...props } ) { - const wrapperProps = useBlockProps( props ); +function BlockProps( { + index, + useBlockProps: hook, + setAllWrapperProps, + ...props +} ) { + const wrapperProps = hook( props ); const setWrapperProps = ( next ) => setAllWrapperProps( ( prev ) => { const nextAll = [ ...prev ]; From cb24e93252cf8aaa64d3ea4db337c950c1c74aec Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Thu, 7 Nov 2024 21:03:34 +0900 Subject: [PATCH 36/72] Enable zoom out mode for non-iframe editor (#66789) Co-authored-by: t-hamano Co-authored-by: stokesman Co-authored-by: annezazu Co-authored-by: ndiego Co-authored-by: ellatrix --- packages/editor/src/components/editor-interface/index.js | 1 - packages/editor/src/components/header/index.js | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/editor/src/components/editor-interface/index.js b/packages/editor/src/components/editor-interface/index.js index aee75695b3fda4..3692a5ed797f5e 100644 --- a/packages/editor/src/components/editor-interface/index.js +++ b/packages/editor/src/components/editor-interface/index.js @@ -131,7 +131,6 @@ export default function EditorInterface( { customSaveButton={ customSaveButton } forceDisableBlockTools={ forceDisableBlockTools } title={ title } - isEditorIframed={ ! disableIframe } /> ) } diff --git a/packages/editor/src/components/header/index.js b/packages/editor/src/components/header/index.js index 5a9cc2f94a5951..2a5629b080caf8 100644 --- a/packages/editor/src/components/header/index.js +++ b/packages/editor/src/components/header/index.js @@ -53,7 +53,6 @@ function Header( { forceDisableBlockTools, setEntitiesSavedStatesCallback, title, - isEditorIframed, } ) { const isWideViewport = useViewportMatch( 'large' ); const isLargeViewport = useViewportMatch( 'medium' ); @@ -168,7 +167,7 @@ function Header( { forceIsAutosaveable={ forceIsDirty } /> - { canBeZoomedOut && isEditorIframed && isWideViewport && ( + { canBeZoomedOut && isWideViewport && ( ) } From 9671f7c34a4e72cbba6a2bdfd375c6c29987cbd7 Mon Sep 17 00:00:00 2001 From: Marin Atanasov <8436925+tyxla@users.noreply.github.com> Date: Fri, 8 Nov 2024 00:57:28 +0900 Subject: [PATCH 37/72] Core Commands: Fix add new post URL assignment (#66830) Co-authored-by: tyxla Co-authored-by: Mamaduka --- packages/core-commands/src/admin-navigation-commands.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core-commands/src/admin-navigation-commands.js b/packages/core-commands/src/admin-navigation-commands.js index 9f0883faa3c17a..8a8167bb29b820 100644 --- a/packages/core-commands/src/admin-navigation-commands.js +++ b/packages/core-commands/src/admin-navigation-commands.js @@ -97,7 +97,7 @@ export function useAdminNavigationCommands() { label: __( 'Add new post' ), icon: plus, callback: () => { - document.location.href = 'post-new.php'; + document.location.assign( 'post-new.php' ); }, } ); From ab6e729daf775ee6065a48c210826af8281a0842 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20W=C3=BCnsch?= Date: Thu, 7 Nov 2024 17:32:51 +0100 Subject: [PATCH 38/72] Iframe: Fix relative wp-content URLs (#66751) Unlinked contributors: tambourine-man, Pat-Relentless, nicolasleroy. Co-authored-by: Soean Co-authored-by: youknowriad Co-authored-by: ellatrix Co-authored-by: getdave Co-authored-by: cweiske Co-authored-by: peterwilsoncc --- .../block-editor/src/components/iframe/index.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packages/block-editor/src/components/iframe/index.js b/packages/block-editor/src/components/iframe/index.js index 85e1f12a7c0d63..76d2e09dfb7a30 100644 --- a/packages/block-editor/src/components/iframe/index.js +++ b/packages/block-editor/src/components/iframe/index.js @@ -191,6 +191,22 @@ function Iframe( { preventFileDropDefault, false ); + // Prevent clicks on links from navigating away. Note that links + // inside `contenteditable` are already disabled by the browser, so + // this is for links in blocks outside of `contenteditable`. + iFrameDocument.addEventListener( 'click', ( event ) => { + if ( event.target.tagName === 'A' ) { + event.preventDefault(); + + // Appending a hash to the current URL will not reload the + // page. This is useful for e.g. footnotes. + const href = event.target.getAttribute( 'href' ); + if ( href.startsWith( '#' ) ) { + iFrameDocument.defaultView.location.hash = + href.slice( 1 ); + } + } + } ); } node.addEventListener( 'load', onLoad ); @@ -272,6 +288,7 @@ function Iframe( { +