diff --git a/.github/workflows/enforce-pr-labels.yml b/.github/workflows/enforce-pr-labels.yml new file mode 100644 index 0000000000000..2d55965dc0bae --- /dev/null +++ b/.github/workflows/enforce-pr-labels.yml @@ -0,0 +1,18 @@ +name: Enforce labels on Pull Request +on: + pull_request_target: + types: [opened, labeled, unlabeled, synchronize] +jobs: + type-related-labels: + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - uses: mheap/github-action-required-labels@v5 + with: + mode: exactly + count: 1 + labels: '[Type] Accessibility (a11y), [Type] Automated Testing, [Type] Breaking Change, [Type] Bug, [Type] Build Tooling, [Type] Code Quality, [Type] Copy, [Type] Developer Documentation, [Type] Enhancement, [Type] Experimental, [Type] Feature, [Type] New API, [Type] Task, [Type] Performance, [Type] Project Management, [Type] Security, [Type] WP Core Ticket' + add_comment: true + message: "## ⚠️ Type of PR label error\n To merge this PR, it requires {{ errorString }} {{ count }} label indicating the type of PR. Other labels are optional and not being checked here. \n- **Type-related labels to choose from**: {{ provided }}.\n- **Labels found**: {{ applied }}." + exit_type: success diff --git a/bin/plugin/commands/changelog.js b/bin/plugin/commands/changelog.js index 69be77ac66991..c9e6d039596c5 100644 --- a/bin/plugin/commands/changelog.js +++ b/bin/plugin/commands/changelog.js @@ -126,10 +126,6 @@ const LABEL_FEATURE_MAPPING = { 'REST API Interaction': 'REST API', 'New Block': 'Block Library', 'Accessibility (a11y)': 'Accessibility', - '[a11y] Color Contrast': 'Accessibility', - '[a11y] Keyboard & Focus': 'Accessibility', - '[a11y] Labelling': 'Accessibility', - '[a11y] Zooming': 'Accessibility', '[Package] E2E Tests': 'Testing', '[Package] E2E Test Utils': 'Testing', 'Automated Testing': 'Testing', diff --git a/bin/plugin/commands/test/fixtures/pull-requests.json b/bin/plugin/commands/test/fixtures/pull-requests.json index 34f3c06c5d854..eccb2257a2c1f 100644 --- a/bin/plugin/commands/test/fixtures/pull-requests.json +++ b/bin/plugin/commands/test/fixtures/pull-requests.json @@ -5747,13 +5747,13 @@ "description": "/packages/components" }, { - "id": 1344464662, - "node_id": "MDU6TGFiZWwxMzQ0NDY0NjYy", - "url": "https://api.github.com/repos/WordPress/gutenberg/labels/[a11y]%20Keyboard%20&%20Focus", - "name": "[a11y] Keyboard & Focus", - "color": "efde5d", + "id": 546517042, + "node_id": "MDU6TGFiZWw1NDY1MTcwNDI=", + "url": "https://api.github.com/repos/WordPress/gutenberg/labels/Accessibility%20(a11y)", + "name": "Accessibility (a11y)", + "color": "655104", "default": false, - "description": "" + "description": "Changes that impact accessibility and need corresponding review (e.g. markup changes)." } ], "state": "closed", diff --git a/changelog.txt b/changelog.txt index ede5cff91e4ed..ff4942d8e06a1 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,15 +1,13 @@ == Changelog == -= 16.3.0-rc.1 = - - += 16.3.0 = ## Changelog ### Enhancements #### Site Editor -- Edit Site: Add delay and fade-in animation to loading spinner. ([51902](https://github.com/WordPress/gutenberg/pull/51902)) +- Add delay and fade-in animation to loading spinner. ([51902](https://github.com/WordPress/gutenberg/pull/51902)) - Make "My patterns" category permanently visible. ([52531](https://github.com/WordPress/gutenberg/pull/52531)) - Remove "Theme patterns" heading in Pattern library. ([52570](https://github.com/WordPress/gutenberg/pull/52570)) - Remove sidebar group descriptions. ([52453](https://github.com/WordPress/gutenberg/pull/52453)) @@ -17,27 +15,27 @@ - Swap pattern creation options. ([52726](https://github.com/WordPress/gutenberg/pull/52726)) - Update Dashboard button tooltips in the site editor. ([52465](https://github.com/WordPress/gutenberg/pull/52465)) - Update Site Editor frame z-index. ([52180](https://github.com/WordPress/gutenberg/pull/52180)) -- Update descriptions in Pattern library. ([52468](https://github.com/WordPress/gutenberg/pull/52468)) - Update locked pattern tooltips. ([52497](https://github.com/WordPress/gutenberg/pull/52497)) - Update navigation menu title size & weight in detail panels. ([52477](https://github.com/WordPress/gutenberg/pull/52477)) - Update pattern library copy. ([52340](https://github.com/WordPress/gutenberg/pull/52340)) -- Update: Show more intuitive archive titles on Query Title block. ([52521](https://github.com/WordPress/gutenberg/pull/52521)) +- Show more intuitive archive titles on Query Title block. ([52521](https://github.com/WordPress/gutenberg/pull/52521)) +- Adapt template part hint copy. ([52527](https://github.com/WordPress/gutenberg/pull/52527)) #### Patterns - Add hint to show template part move. ([52395](https://github.com/WordPress/gutenberg/pull/52395)) - Add renaming, duplication, and deletion options. ([52270](https://github.com/WordPress/gutenberg/pull/52270)) - Add sync tooltip. ([52458](https://github.com/WordPress/gutenberg/pull/52458)) - Display all custom template part areas in sidebar nav. ([52355](https://github.com/WordPress/gutenberg/pull/52355)) -- Don't override the rootClientID in create menu - only set if undefined. ([52713](https://github.com/WordPress/gutenberg/pull/52713)) +- Don't override the rootClientID in create menu - only set if undefined. ([52713](https://github.com/WordPress/gutenberg/pull/52713)) - Enable focus mode editing. ([52427](https://github.com/WordPress/gutenberg/pull/52427)) - Remove `reusable` text from menu once rename hint has been dismissed. ([52664](https://github.com/WordPress/gutenberg/pull/52664)) - Stop endless snackbars appearing. ([52012](https://github.com/WordPress/gutenberg/pull/52012)) -- Try: Sticky header and pagination on Patterns page. ([52663](https://github.com/WordPress/gutenberg/pull/52663)) +- Sticky header and pagination on Patterns page. ([52663](https://github.com/WordPress/gutenberg/pull/52663)) - Update manage pattern links to go to site editor if available. ([52403](https://github.com/WordPress/gutenberg/pull/52403)) -- [Patterns] Separate sync status into a filter control. ([52303](https://github.com/WordPress/gutenberg/pull/52303)) +- Separate sync status into a filter control. ([52303](https://github.com/WordPress/gutenberg/pull/52303)) #### Components -- Adapt template part hint copy. ([52527](https://github.com/WordPress/gutenberg/pull/52527)) + - Adding support for defined IDs in `TextControl` component. ([52028](https://github.com/WordPress/gutenberg/pull/52028)) - Updated "position" default value. ([52148](https://github.com/WordPress/gutenberg/pull/52148)) @@ -48,30 +46,17 @@ #### Block Library - Add back old Navigation and File blocks JavaScript implementation when Gutenberg is not installed. ([52553](https://github.com/WordPress/gutenberg/pull/52553)) - Home link block: Add 'current-menu-item'. ([51478](https://github.com/WordPress/gutenberg/pull/51478)) +- Use next40pxDefaultSize on RangeControl components. ([52257](https://github.com/WordPress/gutenberg/pull/52257)) #### Block Editor - Add maxLength to LinkControl search item URLs. ([52523](https://github.com/WordPress/gutenberg/pull/52523)) -- i18n: Make the tab labels of `ColorGradientSettingsDropdown` component translatable. ([52669](https://github.com/WordPress/gutenberg/pull/52669)) +- Make the tab labels of `ColorGradientSettingsDropdown` component translatable. ([52669](https://github.com/WordPress/gutenberg/pull/52669)) +- Add support for arrays to `setImmutably` util. ([52280](https://github.com/WordPress/gutenberg/pull/52280)) +- Stabilize `defaultBlock`, `directInsert` API's and `getDirectInsertBlock` selector. ([52083](https://github.com/WordPress/gutenberg/pull/52083)) #### NUX - Restore `@wordpress/nux` to trunk. ([52455](https://github.com/WordPress/gutenberg/pull/52455)) -#### Block API -- Block Editor: Add support for arrays to `setImmutably` util. ([52280](https://github.com/WordPress/gutenberg/pull/52280)) - -#### Inspector Controls -- Use next40pxDefaultSize on RangeControl components. ([52257](https://github.com/WordPress/gutenberg/pull/52257)) - -#### Fonts API -- Font Face: To generate and print font-face styles for theme.json fonts. ([51770](https://github.com/WordPress/gutenberg/pull/51770)) - - -### New APIs - -#### Nested / Inner Blocks -- Stabilize `defaultBlock`, `directInsert` API's and `getDirectInsertBlock` selector. ([52083](https://github.com/WordPress/gutenberg/pull/52083)) - - ### Bug Fixes #### Patterns @@ -84,27 +69,29 @@ - Ensure that the unsaved title is not persisted when reopening the modal. ([52473](https://github.com/WordPress/gutenberg/pull/52473)) - Fix bug with Create Patterns menu not showing in site editor page editing. ([52671](https://github.com/WordPress/gutenberg/pull/52671)) - Fix renaming in Site View sidebar rename saves all edits for Template Parts and Navigation Menus. ([52373](https://github.com/WordPress/gutenberg/pull/52373)) -- Fix: Patterns & template parts: Remove "apply globally" option from block settings. ([52160](https://github.com/WordPress/gutenberg/pull/52160)) +- Patterns & template parts: Remove "apply globally" option from block settings. ([52160](https://github.com/WordPress/gutenberg/pull/52160)) - Rename edit label to Edit Block Pattern to resolve edge case in Chrome. ([52496](https://github.com/WordPress/gutenberg/pull/52496)) - Show uncategorized patterns on the Editor > Patterns page. ([52633](https://github.com/WordPress/gutenberg/pull/52633)) - Site Editor Patterns: Filter out patterns that are not available in the inserter. ([52675](https://github.com/WordPress/gutenberg/pull/52675)) - Update the title of Pattern block in the block inspector card. ([52010](https://github.com/WordPress/gutenberg/pull/52010)) #### Site Editor -- Edit Site: Fix the pattern with the post types becomes the placeholder pattern when editing template part. ([52503](https://github.com/WordPress/gutenberg/pull/52503)) +- Fix the pattern with the post types becomes the placeholder pattern when editing template part. ([52503](https://github.com/WordPress/gutenberg/pull/52503)) - Fix "Manage all patterns" link appearance. ([52532](https://github.com/WordPress/gutenberg/pull/52532)) - Fix document title icon appearance. ([52424](https://github.com/WordPress/gutenberg/pull/52424)) - Fix entering edit mode in site editor. ([52406](https://github.com/WordPress/gutenberg/pull/52406)) -- Fix missing Add Template Part button in Template Parts page. ([52542](https://github.com/WordPress/gutenberg/pull/52542)) +- Fix missing "Add Template Part" button in Template Parts page. ([52542](https://github.com/WordPress/gutenberg/pull/52542)) - Fix undo/redo in site editor code editor's mode. ([52695](https://github.com/WordPress/gutenberg/pull/52695)) - Remove status icon. ([52457](https://github.com/WordPress/gutenberg/pull/52457)) - Reset device preview type when exiting the editing mode. ([52566](https://github.com/WordPress/gutenberg/pull/52566)) - ResizableFrame: Fix styling in Firefox. ([52700](https://github.com/WordPress/gutenberg/pull/52700)) - Site Editor Pages: Load the appropriate template if posts page set. ([52266](https://github.com/WordPress/gutenberg/pull/52266)) - Site Editor Patterns: Ensure sidebar does not shrink when long pattern titles are used. ([52547](https://github.com/WordPress/gutenberg/pull/52547)) -- Use lowercase p in in "Manage Patterns". ([52617](https://github.com/WordPress/gutenberg/pull/52617)) +- Use lowercase p in "Manage Patterns". ([52617](https://github.com/WordPress/gutenberg/pull/52617)) - Do not navigate to the styles pages unless you're in a random listing page. ([52728](https://github.com/WordPress/gutenberg/pull/52728)) - Fix multiple navigation blocks in pattern template. ([52707](https://github.com/WordPress/gutenberg/pull/52707)) +- Don't allow creating template part on the Patterns page for non-block themes. ([52656](https://github.com/WordPress/gutenberg/pull/52656)) +- Exit template focus when opening the W menu. ([52235](https://github.com/WordPress/gutenberg/pull/52235)) #### Block Library - Fix console warning by improving error handling in Nav block classic menu conversion. ([52591](https://github.com/WordPress/gutenberg/pull/52591)) @@ -117,13 +104,16 @@ - Rich Text/Footnotes: Fix getRichTextValues for useInnerBlocksProps.save. ([52682](https://github.com/WordPress/gutenberg/pull/52682)) - Search block: Enqueue view script through block.json. ([52552](https://github.com/WordPress/gutenberg/pull/52552)) - Use `_get_block_template_file` function and set $area variable. ([52708](https://github.com/WordPress/gutenberg/pull/52708)) +- Cover Block: Fix block deprecation when fixed background is enabled. ([51612](https://github.com/WordPress/gutenberg/pull/51612)) +- Fix image block v6 deprecation. ([52822](https://github.com/WordPress/gutenberg/pull/52822)) +- Image: Use the correct method for caption class in recent deprecation. ([52853](https://github.com/WordPress/gutenberg/pull/52853)) #### Accessibility -- Change Delete page menu item to Move to trash. ([52641](https://github.com/WordPress/gutenberg/pull/52641)) +- Change "Delete page" menu item to "Move to trash". ([52641](https://github.com/WordPress/gutenberg/pull/52641)) - Change password input to type text so contents are visible. ([52622](https://github.com/WordPress/gutenberg/pull/52622)) - Do not autofocus page title field in the 'Draft a new page' modal dialog. ([52603](https://github.com/WordPress/gutenberg/pull/52603)) - Fix Shift+Tab to Block Toolbar. ([52613](https://github.com/WordPress/gutenberg/pull/52613)) -- Item: Unify focus style and add default font styles. ([52495](https://github.com/WordPress/gutenberg/pull/52495)) +- Unify focus style and add default font styles. ([52495](https://github.com/WordPress/gutenberg/pull/52495)) - Navigation block: Add notice on reduced accessibility. ([52251](https://github.com/WordPress/gutenberg/pull/52251)) - Password protected field: Remove autofocus and improve placeholder text consistency. ([52634](https://github.com/WordPress/gutenberg/pull/52634)) - ResizableFrame: Make keyboard accessible. ([52443](https://github.com/WordPress/gutenberg/pull/52443)) @@ -133,12 +123,12 @@ - Add 'reusable' keyword to Pattern blocks. ([52543](https://github.com/WordPress/gutenberg/pull/52543)) - Avoid errors in Dimension visualizers when switching between iframed and non-iframed editors. ([52588](https://github.com/WordPress/gutenberg/pull/52588)) - Ensure synced patterns are accounted for in 'getAllowedBlocks'. ([52546](https://github.com/WordPress/gutenberg/pull/52546)) -- Fix: Remove link action of Link UI for draft pages created from Nav block does not correctly remove link. ([52415](https://github.com/WordPress/gutenberg/pull/52415)) +- Remove link action of Link UI for draft pages created from Nav block does not correctly remove link. ([52415](https://github.com/WordPress/gutenberg/pull/52415)) - LinkControl: Add width to ensure ellipsis truncating works. ([52575](https://github.com/WordPress/gutenberg/pull/52575)) - LinkControl: Fix mark highlight to bold. ([52517](https://github.com/WordPress/gutenberg/pull/52517)) - Post Content link color should not be applied to placeholder component links. ([52367](https://github.com/WordPress/gutenberg/pull/52367)) - Fix highlight change when using transform menu. ([52752](https://github.com/WordPress/gutenberg/pull/52752)) -- Fix: Apply text color selection to link color. ([52379](https://github.com/WordPress/gutenberg/pull/52379)) +- Apply text color selection to link color. ([52379](https://github.com/WordPress/gutenberg/pull/52379)) #### Components - Block Editor: Display variation icon in the 'BlockDraggable' component. ([52502](https://github.com/WordPress/gutenberg/pull/52502)) @@ -149,7 +139,7 @@ - Top Toolbar: Move the preferences selection into the main useSelect. ([52332](https://github.com/WordPress/gutenberg/pull/52332)) #### Post Editor -- Editor: Remove a block select button from the multi-entity saving flow. ([52753](https://github.com/WordPress/gutenberg/pull/52753)) +- Remove a block select button from the multi-entity saving flow. ([52753](https://github.com/WordPress/gutenberg/pull/52753)) - Fix Site editor page when JS support is disabled. ([52376](https://github.com/WordPress/gutenberg/pull/52376)) - Fix initial block parsing. ([52417](https://github.com/WordPress/gutenberg/pull/52417)) - Simplify the code editor of edit-post. ([52751](https://github.com/WordPress/gutenberg/pull/52751)) @@ -161,7 +151,6 @@ #### Themes - Fix admin_url() for preview link of block themes. ([52399](https://github.com/WordPress/gutenberg/pull/52399)) -- Site Editor: Don't allow creating template part on the Patterns page for non-block themes. ([52656](https://github.com/WordPress/gutenberg/pull/52656)) #### Fonts API - Deprecate and make Fonts API non-functional. ([52485](https://github.com/WordPress/gutenberg/pull/52485)) @@ -170,13 +159,6 @@ #### Extensibility - Page Content Focus: Ignore page content within a Query Loop block. ([52351](https://github.com/WordPress/gutenberg/pull/52351)) -#### Page Content Focus -- Exit template focus when opening the W menu. ([52235](https://github.com/WordPress/gutenberg/pull/52235)) - -#### Block Validation/Deprecation -- Cover Block: Fix block deprecation when fixed background is enabled. ([51612](https://github.com/WordPress/gutenberg/pull/51612)) - - ### Performance #### Post Editor @@ -194,16 +176,17 @@ ### Experiments -#### Project Management -- Github workflow: Add a PHP backport changes action. ([52096](https://github.com/WordPress/gutenberg/pull/52096)) - #### Interactivity API - Prevent scripts from loading if behaviors are not used. ([52140](https://github.com/WordPress/gutenberg/pull/52140)) +#### Fonts API +- Font Face: To generate and print font-face styles for theme.json fonts. ([51770](https://github.com/WordPress/gutenberg/pull/51770)) + + ### Documentation -- (readme.md) Document the new process for releasing point releases for old release branches. ([49968](https://github.com/WordPress/gutenberg/pull/49968)) +- Document the new process for releasing point releases for old release branches. ([49968](https://github.com/WordPress/gutenberg/pull/49968)) - Add layout API documentation. ([52673](https://github.com/WordPress/gutenberg/pull/52673)) - Added README for the "caption" component. ([52033](https://github.com/WordPress/gutenberg/pull/52033)) - Added documentation text-transform component #52072. ([52243](https://github.com/WordPress/gutenberg/pull/52243)) @@ -216,9 +199,8 @@ ### Code Quality +#### Block Editor - Add missing `@emotion/react` dep to block-editor. ([52475](https://github.com/WordPress/gutenberg/pull/52475)) -- Code Data: Fix ESLint warning for 'useEntityProp' hook. ([52757](https://github.com/WordPress/gutenberg/pull/52757)) -- Lodash: Deprecate `_.set()`. ([52407](https://github.com/WordPress/gutenberg/pull/52407)) - Lodash: Remove remaining `_.get()` from block editor and deprecate. ([52561](https://github.com/WordPress/gutenberg/pull/52561)) - Make use of accessing private APIs from thunks directly. ([52214](https://github.com/WordPress/gutenberg/pull/52214)) @@ -238,7 +220,7 @@ #### Post Editor - EntityRecordItem: Fix ESLint warnings and remove unnecessary memoization. ([52630](https://github.com/WordPress/gutenberg/pull/52630)) - PostPreviewButton: Rewrite to functional, avoid state transitions in lifecycles. ([44971](https://github.com/WordPress/gutenberg/pull/44971)) -- correct a typo: Sapce -> space. ([52578](https://github.com/WordPress/gutenberg/pull/52578)) +- Correct a typo: Sapce -> space. ([52578](https://github.com/WordPress/gutenberg/pull/52578)) #### Site Editor - Fix incorrect 'useSelect' usage. ([52683](https://github.com/WordPress/gutenberg/pull/52683)) @@ -247,6 +229,10 @@ #### Reusable Blocks - Update package to use relative path. ([52712](https://github.com/WordPress/gutenberg/pull/52712)) +#### Core Data +- Core Data: Fix ESLint warning for 'useEntityProp' hook. ([52757](https://github.com/WordPress/gutenberg/pull/52757)) + + ### Tools @@ -261,21 +247,20 @@ - Try fixing block context end-to-end test failure. ([52513](https://github.com/WordPress/gutenberg/pull/52513)) - Use posts instead of template parts for navigation color tests. ([52654](https://github.com/WordPress/gutenberg/pull/52654)) - end-to-end Test Utils: Improve test reliability in plugins/themes and login procedures. ([52144](https://github.com/WordPress/gutenberg/pull/52144)) -- test: Enable jest-watch-typeahead for native tests. ([51869](https://github.com/WordPress/gutenberg/pull/51869)) -- test: Expand mobile editor tests. ([52446](https://github.com/WordPress/gutenberg/pull/52446)) +- Enable jest-watch-typeahead for native tests. ([51869](https://github.com/WordPress/gutenberg/pull/51869)) +- Expand mobile editor tests. ([52446](https://github.com/WordPress/gutenberg/pull/52446)) #### Build Tooling - Backport tools: Sort PRs to be cherry picked by merged/closed date. ([52667](https://github.com/WordPress/gutenberg/pull/52667)) - Create block interactive template. ([52612](https://github.com/WordPress/gutenberg/pull/52612)) - Fix Webpack to watch the `interactivity` package files. ([52642](https://github.com/WordPress/gutenberg/pull/52642)) - Update caniuse-lite, browserslist and core-js. ([52420](https://github.com/WordPress/gutenberg/pull/52420)) +- Lodash: Deprecate `_.set()`. ([52407](https://github.com/WordPress/gutenberg/pull/52407)) #### Project Management - Update issue gardening automation with new label. ([52173](https://github.com/WordPress/gutenberg/pull/52173)) - -### Various - - Revert "Update Changelog for 16.1.2". ([52433](https://github.com/WordPress/gutenberg/pull/52433)) +- Github workflow: Add a PHP backport changes action. ([52096](https://github.com/WordPress/gutenberg/pull/52096)) ## First time contributors @@ -294,6 +279,8 @@ The following contributors merged PRs in this release: @aaronrobertshaw @afercia @andrewhayward @andrewserong @anomiex @arthur791004 @BenjaminZekavica @bfintal @carolinan @Clorith @dcalhoun @derekblank @diegohaz @draganescu @ellatrix @fluiddot @fullofcaffeine @geriux @getdave @ghorivipul97 @glendaviesnz @hellofromtonya @jameskoster @jeryj @jorgefilipecosta @jsnajdr @juanmaguitar @kevin940726 @luisherranz @MaggieCabrera @Mamaduka @michalczaplinski @mirka @noisysocks @ntsekouras @peterwilsoncc @pooja-muchandikar @Presskopp @priethor @ramonjd @richtabor @SantosGuillamot @SavPhill @SaxonF @scruffian @sethrubenstein @spacedmonkey @swissspidy @t-hamano @tellthemachines @tyxla @walbo @westonruter @youknowriad + + = 16.2.1 = ## Changelog diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index d0a6ca7d356b2..afc50e46ad2d5 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -275,7 +275,7 @@ Add a link to a downloadable file. ([Source](https://github.com/WordPress/gutenb - **Name:** core/footnotes - **Category:** text -- **Supports:** ~~html~~, ~~multiple~~, ~~reusable~~ +- **Supports:** color (background, link, text), ~~html~~, ~~multiple~~, ~~reusable~~ - **Attributes:** ## Classic @@ -646,7 +646,7 @@ Add text that respects your spacing and tabs, and also allows styling. ([Source] - **Name:** core/preformatted - **Category:** text -- **Supports:** anchor, color (background, gradients, text), typography (fontSize, lineHeight) +- **Supports:** anchor, color (background, gradients, text), spacing (margin, padding), typography (fontSize, lineHeight) - **Attributes:** content ## Pullquote diff --git a/docs/reference-guides/data/data-core.md b/docs/reference-guides/data/data-core.md index a66c0991e3d27..f2bc3374f9e72 100644 --- a/docs/reference-guides/data/data-core.md +++ b/docs/reference-guides/data/data-core.md @@ -136,7 +136,7 @@ _Parameters_ _Returns_ -- `Object | null`: The current global styles. +- `Array< object > | null`: The current global styles. ### getCurrentUser diff --git a/docs/reference-guides/theme-json-reference/theme-json-living.md b/docs/reference-guides/theme-json-reference/theme-json-living.md index a2e88320cf58b..da08b4fe9eac1 100644 --- a/docs/reference-guides/theme-json-reference/theme-json-living.md +++ b/docs/reference-guides/theme-json-reference/theme-json-living.md @@ -171,6 +171,16 @@ Settings related to typography. Generate custom CSS custom properties of the form `--wp--custom--{key}--{nested-key}: {value};`. `camelCased` keys are transformed to `kebab-case` as to follow the CSS property naming schema. Keys at different depth levels are separated by `--`, so keys should not include `--` in the name. +--- + +### behaviors + +Settings related to behaviors. + +| Property | Type | Default | Props | +| --- | --- | --- |--- | +| lightbox | boolean | false | | + --- ## Styles diff --git a/gutenberg.php b/gutenberg.php index cb3300bfae0b4..980b2304ac796 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.1 * Requires PHP: 5.6 - * Version: 16.3.0-rc.1 + * Version: 16.3.0 * Author: Gutenberg Team * Text Domain: gutenberg * diff --git a/lib/block-supports/behaviors.php b/lib/block-supports/behaviors.php index a18700a588410..95639178c2de4 100644 --- a/lib/block-supports/behaviors.php +++ b/lib/block-supports/behaviors.php @@ -44,7 +44,6 @@ function gutenberg_register_behaviors_support( $block_type ) { * @return string Filtered block content. */ function gutenberg_render_behaviors_support_lightbox( $block_content, $block ) { - $experiments = get_option( 'gutenberg-experiments' ); $link_destination = isset( $block['attrs']['linkDestination'] ) ? $block['attrs']['linkDestination'] : 'none'; // Get the lightbox setting from the block attributes. if ( isset( $block['attrs']['behaviors']['lightbox'] ) ) { @@ -63,7 +62,7 @@ function gutenberg_render_behaviors_support_lightbox( $block_content, $block ) { return $block_content; } - if ( ! $lightbox_settings || 'none' !== $link_destination || empty( $experiments['gutenberg-interactivity-api-core-blocks'] ) ) { + if ( ! $lightbox_settings || 'none' !== $link_destination ) { return $block_content; } diff --git a/lib/blocks.php b/lib/blocks.php index e98f711b5c85a..f160d2a7080d3 100644 --- a/lib/blocks.php +++ b/lib/blocks.php @@ -172,6 +172,40 @@ function gutenberg_reregister_core_block_types() { add_action( 'init', 'gutenberg_reregister_core_block_types' ); +/** + * Adds the defer loading strategy to all registered blocks. + * + * This function would not be part of core merge. Instead, the register_block_script_handle() function would be patched + * as follows. + * + * ``` + * --- a/wp-includes/blocks.php + * +++ b/wp-includes/blocks.php + * @ @ -153,7 +153,8 @ @ function register_block_script_handle( $metadata, $field_name, $index = 0 ) { + * $script_handle, + * $script_uri, + * $script_dependencies, + * - isset( $script_asset['version'] ) ? $script_asset['version'] : false + * + isset( $script_asset['version'] ) ? $script_asset['version'] : false, + * + array( 'strategy' => 'defer' ) + * ); + * if ( ! $result ) { + * return false; + * ``` + * + * @see register_block_script_handle() + */ +function gutenberg_defer_block_view_scripts() { + $block_types = WP_Block_Type_Registry::get_instance()->get_all_registered(); + foreach ( $block_types as $block_type ) { + foreach ( $block_type->view_script_handles as $view_script_handle ) { + wp_script_add_data( $view_script_handle, 'strategy', 'defer' ); + } + } +} + +add_action( 'init', 'gutenberg_defer_block_view_scripts', 100 ); + /** * Deregisters the existing core block type and its assets. * diff --git a/lib/compat/wordpress-6.3/navigation-fallback.php b/lib/compat/wordpress-6.3/navigation-fallback.php index 5619e3204627a..5cc84f4a1c848 100644 --- a/lib/compat/wordpress-6.3/navigation-fallback.php +++ b/lib/compat/wordpress-6.3/navigation-fallback.php @@ -13,28 +13,36 @@ * Navigation Fallback REST endpoint. * * The endpoint may embed the full Navigation Menu object into the - * response as the `self` link. By default the Posts Controller - * will only exposes a limited subset of fields but the editor requires - * additional fields to be available in order to utilise the menu. + * response as the `self` link. By default, the Posts Controller + * will only expose a limited subset of fields but the editor requires + * additional fields to be available in order to utilize the menu. * * @param array $schema the schema for the `wp_navigation` post. * @return array the modified schema. */ -function gutenberg_add_fields_to_navigation_fallback_embeded_links( $schema ) { +function gutenberg_add_fields_to_navigation_fallback_embedded_links( $schema ) { // Expose top level fields. $schema['properties']['status']['context'] = array_merge( $schema['properties']['status']['context'], array( 'embed' ) ); $schema['properties']['content']['context'] = array_merge( $schema['properties']['content']['context'], array( 'embed' ) ); - // Expose sub properties of content field. - // These aren't exposed by the posts controller by default, see: - // https://github.com/WordPress/wordpress-develop/blob/5c3c6258e468c67ba00bbd13db29994f1a57a52a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php#L2425. + /* + * Exposes sub properties of content field. + * These sub properties aren't exposed by the posts controller by default, + * for requests where context is `embed`. + * + * @see WP_REST_Posts_Controller::get_item_schema() + */ $schema['properties']['content']['properties']['raw']['context'] = array_merge( $schema['properties']['content']['properties']['raw']['context'], array( 'embed' ) ); $schema['properties']['content']['properties']['rendered']['context'] = array_merge( $schema['properties']['content']['properties']['rendered']['context'], array( 'embed' ) ); $schema['properties']['content']['properties']['block_version']['context'] = array_merge( $schema['properties']['content']['properties']['block_version']['context'], array( 'embed' ) ); - // Expose sub properties of title field. - // These aren't exposed by the posts controller by default, see: - // https://github.com/WordPress/wordpress-develop/blob/5c3c6258e468c67ba00bbd13db29994f1a57a52a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php#L2401. + /* + * Exposes sub properties of title field. + * These sub properties aren't exposed by the posts controller by default, + * for requests where context is `embed`. + * + * @see WP_REST_Posts_Controller::get_item_schema() + */ $schema['properties']['title']['properties']['raw']['context'] = array_merge( $schema['properties']['title']['properties']['raw']['context'], array( 'embed' ) ); return $schema; @@ -42,5 +50,5 @@ function gutenberg_add_fields_to_navigation_fallback_embeded_links( $schema ) { add_filter( 'rest_wp_navigation_item_schema', - 'gutenberg_add_fields_to_navigation_fallback_embeded_links' + 'gutenberg_add_fields_to_navigation_fallback_embedded_links' ); diff --git a/lib/compat/wordpress-6.3/rest-api.php b/lib/compat/wordpress-6.3/rest-api.php index ecb8f52392fef..d7fa31cd33fe6 100644 --- a/lib/compat/wordpress-6.3/rest-api.php +++ b/lib/compat/wordpress-6.3/rest-api.php @@ -85,15 +85,18 @@ function add_modified_wp_template_schema() { } add_filter( 'rest_api_init', 'add_modified_wp_template_schema' ); -/** - * Registers the block patterns REST API routes. - */ -function gutenberg_register_rest_block_patterns() { - $block_patterns = new Gutenberg_REST_Block_Patterns_Controller_6_3(); - $block_patterns->register_routes(); +// If the Auto-inserting Blocks experiment is enabled, we load the block patterns +// controller in lib/experimental/rest-api.php instead. +if ( ! gutenberg_is_experiment_enabled( 'gutenberg-auto-inserting-blocks' ) ) { + /** + * Registers the block patterns REST API routes. + */ + function gutenberg_register_rest_block_patterns() { + $block_patterns = new Gutenberg_REST_Block_Patterns_Controller_6_3(); + $block_patterns->register_routes(); + } + add_action( 'rest_api_init', 'gutenberg_register_rest_block_patterns' ); } -add_action( 'rest_api_init', 'gutenberg_register_rest_block_patterns' ); - /** * Registers the Navigation Fallbacks REST API routes. diff --git a/lib/experimental/auto-inserting-blocks.php b/lib/experimental/auto-inserting-blocks.php new file mode 100644 index 0000000000000..9df94fcbfcd8f --- /dev/null +++ b/lib/experimental/auto-inserting-blocks.php @@ -0,0 +1,246 @@ + 0 ) { + if ( ! is_string( $block['innerContent'][ $chunk_index ] ) ) { + $anchor_block_index--; + } + $chunk_index++; + } + // Since WP_Block::render() iterates over `inner_content` (rather than `inner_blocks`) + // when rendering blocks, we also need to insert a value (`null`, to mark a block + // location) into that array. + array_splice( $block['innerContent'], $chunk_index, 0, array( null ) ); + } + return $block; + }; +} + +/** + * Register blocks for auto-insertion, based on their block.json metadata. + * + * @param array $settings Array of determined settings for registering a block type. + * @param array $metadata Metadata provided for registering a block type. + * @return array Updated settings array. + */ +function gutenberg_register_auto_inserted_blocks( $settings, $metadata ) { + if ( ! isset( $metadata['__experimentalAutoInsert'] ) ) { + return $settings; + } + $auto_insert = $metadata['__experimentalAutoInsert']; + + /** + * Map the camelCased position string from block.json to the snake_cased block type position + * used in the auto-inserting block registration function. + * + * @var array + */ + $property_mappings = array( + 'before' => 'before', + 'after' => 'after', + 'firstChild' => 'first_child', + 'lastChild' => 'last_child', + ); + + $inserted_block_name = $metadata['name']; + foreach ( $auto_insert as $anchor_block_name => $position ) { + // Avoid infinite recursion (auto-inserting next to or into self). + if ( $inserted_block_name === $anchor_block_name ) { + _doing_it_wrong( + __METHOD__, + __( 'Cannot auto-insert block next to itself.', 'gutenberg' ), + '6.4.0' + ); + continue; + } + + if ( ! isset( $property_mappings[ $position ] ) ) { + continue; + } + + $mapped_position = $property_mappings[ $position ]; + + gutenberg_register_auto_inserted_block( $inserted_block_name, $mapped_position, $anchor_block_name ); + + $settings['auto_insert'][ $anchor_block_name ] = $mapped_position; + } + + return $settings; +} +add_filter( 'block_type_metadata_settings', 'gutenberg_register_auto_inserted_blocks', 10, 2 ); + +/** + * Register block for auto-insertion into the frontend and REST API. + * + * Register a block for auto-insertion into the frontend and into the markup + * returned by the templates and patterns REST API endpoints. + * + * This is currently done by filtering parsed blocks as obtained from a block template + * template part, or pattern and injecting the auto-inserted block where applicable. + * + * @todo In the long run, we'd likely want some sort of registry for auto-inserted blocks. + * + * @param string $inserted_block The name of the block to insert. + * @param string $position The desired position of the auto-inserted block, relative to its anchor block. + * Can be 'before', 'after', 'first_child', or 'last_child'. + * @param string $anchor_block The name of the block to insert the auto-inserted block next to. + * @return void + */ +function gutenberg_register_auto_inserted_block( $inserted_block, $position, $anchor_block ) { + $inserted_block = array( + 'blockName' => $inserted_block, + 'attrs' => array(), + 'innerHTML' => '', + 'innerContent' => array(), + 'innerBlocks' => array(), + ); + + $inserter = gutenberg_auto_insert_block( $inserted_block, $position, $anchor_block ); + add_filter( 'gutenberg_serialize_block', $inserter, 10, 1 ); +} + +/** + * Parse and reserialize block templates to allow running filters. + * + * By parsing a block template's content and then reserializing it + * via `gutenberg_serialize_blocks()`, we are able to run filters + * on the parsed blocks. + * + * @param WP_Block_Template[] $query_result Array of found block templates. + * @return WP_Block_Template[] Updated array of found block templates. + */ +function gutenberg_parse_and_serialize_block_templates( $query_result ) { + foreach ( $query_result as $block_template ) { + if ( 'custom' === $block_template->source ) { + continue; + } + $blocks = parse_blocks( $block_template->content ); + $block_template->content = gutenberg_serialize_blocks( $blocks ); + } + + return $query_result; +} +add_filter( 'get_block_templates', 'gutenberg_parse_and_serialize_block_templates', 10, 1 ); + +/** + * Filters the block template object after it has been (potentially) fetched from the theme file. + * + * By parsing a block template's content and then reserializing it + * via `gutenberg_serialize_blocks()`, we are able to run filters + * on the parsed blocks. + * + * @param WP_Block_Template|null $block_template The found block template, or null if there is none. + */ +function gutenberg_parse_and_serialize_blocks( $block_template ) { + + $blocks = parse_blocks( $block_template->content ); + $block_template->content = gutenberg_serialize_blocks( $blocks ); + + return $block_template; +} +add_filter( 'get_block_file_template', 'gutenberg_parse_and_serialize_blocks', 10, 1 ); + +// Helper functions. +// ----------------- +// The sole purpose of the following two functions (`gutenberg_serialize_block` +// and `gutenberg_serialize_blocks`), which are otherwise copies of their unprefixed +// counterparts (`serialize_block` and `serialize_blocks`) is to apply a filter +// (also called `gutenberg_serialize_block`) as an entry point for modifications +// to the parsed blocks. + +/** + * Filterable version of `serialize_block()`. + * + * This function is identical to `serialize_block()`, except that it applies + * the `gutenberg_serialize_block` filter to each block before it is serialized. + * + * @param array $block The block to be serialized. + * @return string The serialized block. + * + * @see serialize_block() + */ +function gutenberg_serialize_block( $block ) { + $block_content = ''; + + /** + * Filters a parsed block before it is serialized. + * + * @param array $block The block to be serialized. + */ + $block = apply_filters( 'gutenberg_serialize_block', $block ); + + $index = 0; + foreach ( $block['innerContent'] as $chunk ) { + if ( is_string( $chunk ) ) { + $block_content .= $chunk; + } else { // Compare to WP_Block::render(). + $inner_block = $block['innerBlocks'][ $index++ ]; + $block_content .= gutenberg_serialize_block( $inner_block ); + } + } + + if ( ! is_array( $block['attrs'] ) ) { + $block['attrs'] = array(); + } + + return get_comment_delimited_block_content( + $block['blockName'], + $block['attrs'], + $block_content + ); +} + +/** + * Filterable version of `serialize_blocks()`. + * + * This function is identical to `serialize_blocks()`, except that it applies + * the `gutenberg_serialize_block` filter to each block before it is serialized. + * + * @param array $blocks The blocks to be serialized. + * @return string[] The serialized blocks. + * + * @see serialize_blocks() + */ +function gutenberg_serialize_blocks( $blocks ) { + return implode( '', array_map( 'gutenberg_serialize_block', $blocks ) ); +} diff --git a/lib/experimental/class-gutenberg-rest-block-patterns-controller.php b/lib/experimental/class-gutenberg-rest-block-patterns-controller.php new file mode 100644 index 0000000000000..1ac567959b146 --- /dev/null +++ b/lib/experimental/class-gutenberg-rest-block-patterns-controller.php @@ -0,0 +1,40 @@ +get_data(); + + $blocks = parse_blocks( $data['content'] ); + $data['content'] = gutenberg_serialize_blocks( $blocks ); // Serialize or render? + + return rest_ensure_response( $data ); + } +} diff --git a/lib/experimental/disable-tinymce.php b/lib/experimental/disable-tinymce.php index 824f1ab9a73ae..ff9185cff567d 100644 --- a/lib/experimental/disable-tinymce.php +++ b/lib/experimental/disable-tinymce.php @@ -61,14 +61,15 @@ function gutenberg_post_being_edited_requires_classic_block() { return false; } - // Handle the post editor. - if ( ! empty( $_GET['post'] ) && ! empty( $_GET['action'] ) && 'edit' === $_GET['action'] ) { - $current_post = get_post( intval( $_GET['post'] ) ); - if ( ! $current_post || is_wp_error( $current_post ) ) { - return false; - } + // Continue only if we're in the post editor. + if ( empty( $_GET['post'] ) || empty( $_GET['action'] ) || 'edit' !== $_GET['action'] ) { + return false; + } - $content = $current_post->post_content; + // Bail if for some reason the post isn't found. + $current_post = get_post( absint( $_GET['post'] ) ); + if ( ! $current_post ) { + return false; } // Check if block editor is disabled by "Classic Editor" or another plugin. @@ -79,13 +80,15 @@ function_exists( 'use_block_editor_for_post_type' ) && return true; } + $content = $current_post->post_content; if ( empty( $content ) ) { return false; } $parsed_blocks = parse_blocks( $content ); foreach ( $parsed_blocks as $block ) { - if ( empty( $block['blockName'] ) && strlen( trim( $block['innerHTML'] ) ) > 0 ) { + $is_freeform_block = empty( $block['blockName'] ) || 'core/freeform' === $block['blockName']; + if ( $is_freeform_block && strlen( trim( $block['innerHTML'] ) ) > 0 ) { return true; } } diff --git a/lib/experimental/editor-settings.php b/lib/experimental/editor-settings.php index 15d964e2deec7..9c7f66a587a3a 100644 --- a/lib/experimental/editor-settings.php +++ b/lib/experimental/editor-settings.php @@ -86,9 +86,6 @@ function gutenberg_enable_experiments() { if ( $gutenberg_experiments && array_key_exists( 'gutenberg-group-grid-variation', $gutenberg_experiments ) ) { wp_add_inline_script( 'wp-block-editor', 'window.__experimentalEnableGroupGridVariation = true', 'before' ); } - if ( $gutenberg_experiments && array_key_exists( 'gutenberg-interactivity-api-core-blocks', $gutenberg_experiments ) ) { - wp_add_inline_script( 'wp-block-editor', 'window.__experimentalInteractivityAPI = true', 'before' ); - } if ( gutenberg_is_experiment_enabled( 'gutenberg-no-tinymce' ) ) { wp_add_inline_script( 'wp-block-library', 'window.__experimentalDisableTinymce = true', 'before' ); diff --git a/lib/experimental/interactivity-api/blocks.php b/lib/experimental/interactivity-api/blocks.php deleted file mode 100644 index 6997f2695c9d6..0000000000000 --- a/lib/experimental/interactivity-api/blocks.php +++ /dev/null @@ -1,27 +0,0 @@ -$store"; + echo sprintf( + '', + wp_json_encode( self::$store, JSON_HEX_TAG | JSON_HEX_AMP ) + ); } } diff --git a/lib/experimental/interactivity-api/scripts.php b/lib/experimental/interactivity-api/scripts.php index e95bf518c75f7..ed1fca8550070 100644 --- a/lib/experimental/interactivity-api/scripts.php +++ b/lib/experimental/interactivity-api/scripts.php @@ -7,20 +7,32 @@ */ /** - * Move interactive scripts to the footer. This is a temporary measure to make - * it work with `wp_store` and it should be replaced with deferred scripts or - * modules. + * Makes sure that interactivity scripts execute after all `wp_store` directives have been printed to the page. + * + * In WordPress 6.3+ this is achieved by printing in the head but marking the scripts with defer. This has the benefit + * of early discovery so the script is loaded by the browser, while at the same time not blocking rendering. In older + * versions of WordPress, this is achieved by loading the scripts in the footer. + * + * @link https://make.wordpress.org/core/2023/07/14/registering-scripts-with-async-and-defer-attributes-in-wordpress-6-3/ */ function gutenberg_interactivity_move_interactive_scripts_to_the_footer() { - // Move the @wordpress/interactivity package to the footer. - wp_script_add_data( 'wp-interactivity', 'group', 1 ); + $supports_defer = version_compare( strtok( get_bloginfo( 'version' ), '-' ), '6.3', '>=' ); + if ( $supports_defer ) { + // Defer execution of @wordpress/interactivity package but continue loading in head. + wp_script_add_data( 'wp-interactivity', 'strategy', 'defer' ); + wp_script_add_data( 'wp-interactivity', 'group', 0 ); + } else { + // Move the @wordpress/interactivity package to the footer. + wp_script_add_data( 'wp-interactivity', 'group', 1 ); + } // Move all the view scripts of the interactive blocks to the footer. $registered_blocks = \WP_Block_Type_Registry::get_instance()->get_all_registered(); foreach ( array_values( $registered_blocks ) as $block ) { if ( isset( $block->supports['interactivity'] ) && $block->supports['interactivity'] ) { foreach ( $block->view_script_handles as $handle ) { - wp_script_add_data( $handle, 'group', 1 ); + // Note that all block view scripts are already made defer by default. + wp_script_add_data( $handle, 'group', $supports_defer ? 0 : 1 ); } } } diff --git a/lib/experimental/rest-api.php b/lib/experimental/rest-api.php index 7c6a9bf74d739..8e548600b3875 100644 --- a/lib/experimental/rest-api.php +++ b/lib/experimental/rest-api.php @@ -10,6 +10,17 @@ die( 'Silence is golden.' ); } +if ( gutenberg_is_experiment_enabled( 'gutenberg-auto-inserting-blocks' ) ) { + /** + * Registers the block patterns REST API routes. + */ + function gutenberg_register_rest_block_patterns() { + $block_patterns = new Gutenberg_REST_Block_Patterns_Controller(); + $block_patterns->register_routes(); + } + add_action( 'rest_api_init', 'gutenberg_register_rest_block_patterns' ); +} + /** * Registers the customizer nonces REST API routes. */ diff --git a/lib/experiments-page.php b/lib/experiments-page.php index ea298784aec0c..3f468d0cbd12d 100644 --- a/lib/experiments-page.php +++ b/lib/experiments-page.php @@ -80,26 +80,26 @@ function gutenberg_initialize_experiments_settings() { ); add_settings_field( - 'gutenberg-interactivity-api-core-blocks', - __( 'Interactivity API and Behaviors UI', 'gutenberg' ), + 'gutenberg-no-tinymce', + __( 'Disable TinyMCE and Classic block', 'gutenberg' ), 'gutenberg_display_experiment_field', 'gutenberg-experiments', 'gutenberg_experiments_section', array( - 'label' => __( 'Use the Interactivity API to enable the Behaviors UI in the Image block.', 'gutenberg' ), - 'id' => 'gutenberg-interactivity-api-core-blocks', + 'label' => __( 'Disable TinyMCE and Classic block', 'gutenberg' ), + 'id' => 'gutenberg-no-tinymce', ) ); add_settings_field( - 'gutenberg-no-tinymce', - __( 'Disable TinyMCE and Classic block', 'gutenberg' ), + 'gutenberg-auto-inserting-blocks', + __( 'Auto-inserting blocks', 'gutenberg' ), 'gutenberg_display_experiment_field', 'gutenberg-experiments', 'gutenberg_experiments_section', array( - 'label' => __( 'Disable TinyMCE and Classic block', 'gutenberg' ), - 'id' => 'gutenberg-no-tinymce', + 'label' => __( 'Test Auto-inserting blocks', 'gutenberg' ), + 'id' => 'gutenberg-auto-inserting-blocks', ) ); diff --git a/lib/load.php b/lib/load.php index 3ffd026a8a444..85e9f9575e6e6 100644 --- a/lib/load.php +++ b/lib/load.php @@ -69,6 +69,9 @@ function gutenberg_is_experiment_enabled( $name ) { require_once __DIR__ . '/experimental/class-wp-rest-customizer-nonces.php'; } require_once __DIR__ . '/experimental/class-gutenberg-rest-template-revision-count.php'; + if ( gutenberg_is_experiment_enabled( 'gutenberg-auto-inserting-blocks' ) ) { + require_once __DIR__ . '/experimental/class-gutenberg-rest-block-patterns-controller.php'; + } require_once __DIR__ . '/experimental/rest-api.php'; } @@ -117,8 +120,8 @@ function gutenberg_is_experiment_enabled( $name ) { require __DIR__ . '/experimental/disable-tinymce.php'; } -if ( gutenberg_is_experiment_enabled( 'gutenberg-interactivity-api-core-blocks' ) ) { - require __DIR__ . '/experimental/interactivity-api/blocks.php'; +if ( gutenberg_is_experiment_enabled( 'gutenberg-auto-inserting-blocks' ) ) { + require __DIR__ . '/experimental/auto-inserting-blocks.php'; } require __DIR__ . '/experimental/interactivity-api/class-wp-interactivity-store.php'; require __DIR__ . '/experimental/interactivity-api/store.php'; diff --git a/package-lock.json b/package-lock.json index 12ebe3296be94..0687de2ca68ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "16.3.0-rc.1", + "version": "16.3.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index e91a45d78d55a..e34ee35b9a5f4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "16.3.0-rc.1", + "version": "16.3.0", "private": true, "description": "A new WordPress editor experience.", "author": "The WordPress Contributors", diff --git a/packages/base-styles/_breakpoints.scss b/packages/base-styles/_breakpoints.scss index 3dd164c99753a..e180757b8cac9 100644 --- a/packages/base-styles/_breakpoints.scss +++ b/packages/base-styles/_breakpoints.scss @@ -3,6 +3,7 @@ */ // Most used breakpoints +$break-xhuge: 1920px; $break-huge: 1440px; $break-wide: 1280px; $break-xlarge: 1080px; diff --git a/packages/base-styles/_mixins.scss b/packages/base-styles/_mixins.scss index 7dbba40c95aa9..da49b110ddbe7 100644 --- a/packages/base-styles/_mixins.scss +++ b/packages/base-styles/_mixins.scss @@ -5,6 +5,12 @@ * Breakpoint mixins */ +@mixin break-xhuge() { + @media (min-width: #{ ($break-xhuge) }) { + @content; + } +} + @mixin break-huge() { @media (min-width: #{ ($break-huge) }) { @content; diff --git a/packages/block-editor/src/autocompleters/block.js b/packages/block-editor/src/autocompleters/block.js index 4d189e52b7cdd..bc06c9de5aaaf 100644 --- a/packages/block-editor/src/autocompleters/block.js +++ b/packages/block-editor/src/autocompleters/block.js @@ -5,6 +5,7 @@ import { useSelect } from '@wordpress/data'; import { createBlock, createBlocksFromInnerBlocksTemplate, + parse, } from '@wordpress/blocks'; import { useMemo } from '@wordpress/element'; @@ -116,14 +117,28 @@ function createBlockCompleter() { return ! ( /\S/.test( before ) || /\S/.test( after ) ); }, getOptionCompletion( inserterItem ) { - const { name, initialAttributes, innerBlocks } = inserterItem; + const { + name, + initialAttributes, + innerBlocks, + syncStatus, + content, + } = inserterItem; + return { action: 'replace', - value: createBlock( - name, - initialAttributes, - createBlocksFromInnerBlocksTemplate( innerBlocks ) - ), + value: + syncStatus === 'unsynced' + ? parse( content, { + __unstableSkipMigrationLogs: true, + } ) + : createBlock( + name, + initialAttributes, + createBlocksFromInnerBlocksTemplate( + innerBlocks + ) + ), }; }, }; diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index aa1a40ef48057..a2acb3c7b53be 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -505,9 +505,14 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, registry ) => { ) { __unstableMarkLastChangeAsPersistent(); } + //Unsynced patterns are nested in an array so we need to flatten them. + const replacementBlocks = + blocks?.length === 1 && Array.isArray( blocks[ 0 ] ) + ? blocks[ 0 ] + : blocks; replaceBlocks( [ ownProps.clientId ], - blocks, + replacementBlocks, indexToSelect, initialPosition ); diff --git a/packages/block-editor/src/components/color-palette/test/__snapshots__/control.js.snap b/packages/block-editor/src/components/color-palette/test/__snapshots__/control.js.snap index 93ff7de880717..039d35937294b 100644 --- a/packages/block-editor/src/components/color-palette/test/__snapshots__/control.js.snap +++ b/packages/block-editor/src/components/color-palette/test/__snapshots__/control.js.snap @@ -172,7 +172,7 @@ exports[`ColorPaletteControl matches the snapshot 1`] = ` diff --git a/packages/block-library/src/template-part/index.php b/packages/block-library/src/template-part/index.php index 1066aa0141915..cb4c2228b3e0c 100644 --- a/packages/block-library/src/template-part/index.php +++ b/packages/block-library/src/template-part/index.php @@ -67,14 +67,11 @@ function render_block_core_template_part( $attributes ) { // Else, if the template part was provided by the active theme, // render the corresponding file content. if ( 0 === validate_file( $attributes['slug'] ) ) { - $block_template_file = _get_block_template_file( 'wp_template_part', $attributes['slug'] ); - if ( $block_template_file ) { - $template_part_file_path = $block_template_file['path']; - $content = (string) file_get_contents( $template_part_file_path ); - $content = '' !== $content ? _inject_theme_attribute_in_block_template_content( $content ) : ''; - if ( isset( $block_template_file['area'] ) ) { - $area = $block_template_file['area']; - } + $block_template = get_block_file_template( $template_part_id, 'wp_template_part' ); + + $content = $block_template->content; + if ( isset( $block_template->area ) ) { + $area = $block_template->area; } } diff --git a/packages/block-library/src/verse/edit.js b/packages/block-library/src/verse/edit.js index 4ff910c8c2fa0..1ca846fb848b5 100644 --- a/packages/block-library/src/verse/edit.js +++ b/packages/block-library/src/verse/edit.js @@ -13,7 +13,6 @@ import { AlignmentToolbar, useBlockProps, } from '@wordpress/block-editor'; -import { createBlock, getDefaultBlockName } from '@wordpress/blocks'; export default function VerseEdit( { attributes, @@ -21,7 +20,6 @@ export default function VerseEdit( { mergeBlocks, onRemove, style, - insertBlocksAfter, } ) { const { textAlign, content } = attributes; const blockProps = useBlockProps( { @@ -58,9 +56,6 @@ export default function VerseEdit( { textAlign={ textAlign } { ...blockProps } __unstablePastePlainText - __unstableOnSplitAtEnd={ () => - insertBlocksAfter( createBlock( getDefaultBlockName() ) ) - } /> ); diff --git a/packages/block-library/src/verse/test/edit.native.js b/packages/block-library/src/verse/test/edit.native.js index aa7cedbbbfd46..bbdacbb90366a 100644 --- a/packages/block-library/src/verse/test/edit.native.js +++ b/packages/block-library/src/verse/test/edit.native.js @@ -64,15 +64,13 @@ describe( 'Verse block', () => { const verseTextInput = await screen.findByPlaceholderText( 'Write verse…' ); - typeInRichText( verseTextInput, 'A great statement.Again', { - finalSelectionStart: 18, - finalSelectionEnd: 18, - } ); + typeInRichText( verseTextInput, 'A great statement.' ); fireEvent( verseTextInput, 'onKeyDown', { nativeEvent: {}, preventDefault() {}, keyCode: ENTER, } ); + typeInRichText( verseTextInput, 'Again' ); // Assert expect( getEditorHtml() ).toMatchInlineSnapshot( ` diff --git a/packages/commands/src/components/command-menu.js b/packages/commands/src/components/command-menu.js index b4a828f34303d..1bd3e38675861 100644 --- a/packages/commands/src/components/command-menu.js +++ b/packages/commands/src/components/command-menu.js @@ -1,13 +1,19 @@ /** * External dependencies */ -import { Command } from 'cmdk'; +import { Command, useCommandState } from 'cmdk'; /** * WordPress dependencies */ import { useSelect, useDispatch } from '@wordpress/data'; -import { useState, useEffect, useRef, useCallback } from '@wordpress/element'; +import { + useState, + useEffect, + useRef, + useCallback, + useMemo, +} from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { Modal, @@ -43,6 +49,7 @@ function CommandMenuLoader( { name, search, hook, setLoader, close } ) { key={ command.name } value={ command.searchLabel ?? command.label } onSelect={ () => command.callback( { close } ) } + id={ command.name } > command.callback( { close } ) } + id={ command.name } > state.value ); + const selectedItemId = useMemo( () => { + const item = document.querySelector( + `[cmdk-item=""][data-value="${ _value }"]` + ); + return item?.getAttribute( 'id' ); + }, [ _value ] ); + useEffect( () => { + // Focus the command palette input when mounting the modal. + if ( isOpen ) { + commandMenuInput.current.focus(); + } + }, [ isOpen ] ); + return ( + + ); +} + export function CommandMenu() { const { registerShortcut } = useDispatch( keyboardShortcutsStore ); const [ search, setSearch ] = useState( '' ); @@ -149,7 +183,6 @@ export function CommandMenu() { ); const { open, close } = useDispatch( commandsStore ); const [ loaders, setLoaders ] = useState( {} ); - const commandMenuInput = useRef(); useEffect( () => { registerShortcut( { @@ -191,16 +224,23 @@ export function CommandMenu() { close(); }; - useEffect( () => { - // Focus the command palette input when mounting the modal. - if ( isOpen ) { - commandMenuInput.current.focus(); - } - }, [ isOpen ] ); - if ( ! isOpen ) { return false; } + + const onKeyDown = ( event ) => { + if ( + // Ignore keydowns from IMEs + event.nativeEvent.isComposing || + // Workaround for Mac Safari where the final Enter/Backspace of an IME composition + // is `isComposing=false`, even though it's technically still part of the composition. + // These can only be detected by keyCode. + event.keyCode === 229 + ) { + event.preventDefault(); + } + }; + const isLoading = Object.values( loaders ).some( Boolean ); return ( @@ -211,13 +251,15 @@ export function CommandMenu() { __experimentalHideHeader >
- +
-
diff --git a/packages/commands/src/components/style.scss b/packages/commands/src/components/style.scss index 11114cce856ba..b4545b6ee3844 100644 --- a/packages/commands/src/components/style.scss +++ b/packages/commands/src/components/style.scss @@ -116,8 +116,8 @@ position: relative; } - [cmdk-group]:has([cmdk-group-items]:not(:empty)) + [cmdk-group]:has([cmdk-group-items]:not(:empty)) { - border-top: 1px solid $gray-200; + [cmdk-group]:has([cmdk-group-items]:not(:empty)):not([hidden]) + [cmdk-group]:has([cmdk-group-items]:not(:empty)) { + border-top: $border-width solid $gray-200; } } diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 45ed6d2ec13fc..c903989f45628 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -2,6 +2,14 @@ ## Unreleased +### Enhancements + +- `ColorPalette`, `BorderControl`: Don't hyphenate hex value in `aria-label` ([#52932](https://github.com/WordPress/gutenberg/pull/52932)). + +### Bug Fix + +- `Modal`: Fix loss of focus when clicking outside ([#52653](https://github.com/WordPress/gutenberg/pull/52653)). + ## 25.4.0 (2023-07-20) ### Enhancements diff --git a/packages/components/src/border-control/border-control-dropdown/component.tsx b/packages/components/src/border-control/border-control-dropdown/component.tsx index c9998af094fbd..8eb220a0209c1 100644 --- a/packages/components/src/border-control/border-control-dropdown/component.tsx +++ b/packages/components/src/border-control/border-control-dropdown/component.tsx @@ -30,12 +30,8 @@ import type { DropdownProps as DropdownComponentProps } from '../../dropdown/typ import type { ColorProps, DropdownProps } from '../types'; const getAriaLabelColorValue = ( colorValue: string ) => { - const isHex = colorValue.startsWith( '#' ); - // Leave hex values as-is. Remove the `var()` wrapper from CSS vars. - const displayValue = colorValue.replace( /^var\((.+)\)$/, '$1' ); - - return isHex ? displayValue.split( '' ).join( '-' ) : displayValue; + return colorValue.replace( /^var\((.+)\)$/, '$1' ); }; const getColorObject = ( @@ -79,14 +75,14 @@ const getToggleAriaLabel = ( const ariaLabelValue = getAriaLabelColorValue( colorObject.color ); return style ? sprintf( - // translators: %1$s: The name of the color e.g. "vivid red". %2$s: The color's hex code, with added hyphens e.g: "#-f-0-0". %3$s: The current border style selection e.g. "solid". + // translators: %1$s: The name of the color e.g. "vivid red". %2$s: The color's hex code e.g.: "#f00:". %3$s: The current border style selection e.g. "solid". 'Border color and style picker. The currently selected color is called "%1$s" and has a value of "%2$s". The currently selected style is "%3$s".', colorObject.name, ariaLabelValue, style ) : sprintf( - // translators: %1$s: The name of the color e.g. "vivid red". %2$s: The color's hex code, with added hyphens e.g: "#-f-0-0". + // translators: %1$s: The name of the color e.g. "vivid red". %2$s: The color's hex code e.g.: "#f00:". 'Border color and style picker. The currently selected color is called "%1$s" and has a value of "%2$s".', colorObject.name, ariaLabelValue @@ -97,13 +93,13 @@ const getToggleAriaLabel = ( const ariaLabelValue = getAriaLabelColorValue( colorValue ); return style ? sprintf( - // translators: %1$s: The color's hex code, with added hyphens e.g: "#-f-0-0". %2$s: The current border style selection e.g. "solid". + // translators: %1$s: The color's hex code e.g.: "#f00:". %2$s: The current border style selection e.g. "solid". 'Border color and style picker. The currently selected color has a value of "%1$s". The currently selected style is "%2$s".', ariaLabelValue, style ) : sprintf( - // translators: %1$s: The color's hex code, with added hyphens e.g: "#-f-0-0". + // translators: %1$s: The color's hex code e.g: "#f00". 'Border color and style picker. The currently selected color has a value of "%1$s".', ariaLabelValue ); @@ -114,7 +110,7 @@ const getToggleAriaLabel = ( if ( colorObject ) { return sprintf( - // translators: %1$s: The name of the color e.g. "vivid red". %2$s: The color's hex code, with added hyphens e.g: "#-f-0-0". + // translators: %1$s: The name of the color e.g. "vivid red". %2$s: The color's hex code e.g: "#f00". 'Border color picker. The currently selected color is called "%1$s" and has a value of "%2$s".', colorObject.name, getAriaLabelColorValue( colorObject.color ) @@ -123,7 +119,7 @@ const getToggleAriaLabel = ( if ( colorValue ) { return sprintf( - // translators: %1$s: The color's hex code, with added hyphens e.g: "#-f-0-0". + // translators: %1$s: The color's hex code e.g: "#f00". 'Border color picker. The currently selected color has a value of "%1$s".', getAriaLabelColorValue( colorValue ) ); diff --git a/packages/components/src/border-control/test/index.js b/packages/components/src/border-control/test/index.js index ae54ce5c55a84..4e971e59e87b2 100644 --- a/packages/components/src/border-control/test/index.js +++ b/packages/components/src/border-control/test/index.js @@ -215,7 +215,7 @@ describe( 'BorderControl', () => { expect( screen.getByLabelText( - 'Border color and style picker. The currently selected color is called "Blue" and has a value of "#-7-2-a-e-e-6".' + 'Border color and style picker. The currently selected color is called "Blue" and has a value of "#72aee6".' ) ).toBeInTheDocument(); } ); @@ -226,7 +226,7 @@ describe( 'BorderControl', () => { expect( screen.getByLabelText( - 'Border color and style picker. The currently selected color has a value of "#-4-b-1-d-8-0".' + 'Border color and style picker. The currently selected color has a value of "#4b1d80".' ) ).toBeInTheDocument(); } ); @@ -239,7 +239,7 @@ describe( 'BorderControl', () => { expect( screen.getByLabelText( - 'Border color and style picker. The currently selected color is called "Blue" and has a value of "#-7-2-a-e-e-6". The currently selected style is "dotted".' + 'Border color and style picker. The currently selected color is called "Blue" and has a value of "#72aee6". The currently selected style is "dotted".' ) ).toBeInTheDocument(); } ); @@ -252,7 +252,7 @@ describe( 'BorderControl', () => { expect( screen.getByLabelText( - 'Border color and style picker. The currently selected color has a value of "#-4-b-1-d-8-0". The currently selected style is "dashed".' + 'Border color and style picker. The currently selected color has a value of "#4b1d80". The currently selected style is "dashed".' ) ).toBeInTheDocument(); } ); @@ -280,7 +280,7 @@ describe( 'BorderControl', () => { expect( screen.getByLabelText( - 'Border color picker. The currently selected color is called "Blue" and has a value of "#-7-2-a-e-e-6".' + 'Border color picker. The currently selected color is called "Blue" and has a value of "#72aee6".' ) ).toBeInTheDocument(); } ); @@ -294,7 +294,7 @@ describe( 'BorderControl', () => { expect( screen.getByLabelText( - 'Border color picker. The currently selected color has a value of "#-4-b-1-d-8-0".' + 'Border color picker. The currently selected color has a value of "#4b1d80".' ) ).toBeInTheDocument(); } ); diff --git a/packages/components/src/color-palette/index.tsx b/packages/components/src/color-palette/index.tsx index 2347897540f63..b3b46a68ff21f 100644 --- a/packages/components/src/color-palette/index.tsx +++ b/packages/components/src/color-palette/index.tsx @@ -224,12 +224,12 @@ function UnforwardedColorPalette( const displayValue = value?.replace( /^var\((.+)\)$/, '$1' ); const customColorAccessibleLabel = !! displayValue ? sprintf( - // translators: %1$s: The name of the color e.g: "vivid red". %2$s: The color's hex code, with added hyphens e.g: "#-f-0-0". + // translators: %1$s: The name of the color e.g: "vivid red". %2$s: The color's hex code e.g: "#f00". __( 'Custom color picker. The currently selected color is called "%1$s" and has a value of "%2$s".' ), buttonLabelName, - isHex ? displayValue.split( '' ).join( '-' ) : displayValue + displayValue ) : __( 'Custom color picker.' ); diff --git a/packages/components/src/color-palette/test/__snapshots__/index.tsx.snap b/packages/components/src/color-palette/test/__snapshots__/index.tsx.snap index 8c7557366e37d..a08760a33e252 100644 --- a/packages/components/src/color-palette/test/__snapshots__/index.tsx.snap +++ b/packages/components/src/color-palette/test/__snapshots__/index.tsx.snap @@ -193,7 +193,7 @@ exports[`ColorPalette should render a dynamic toolbar of colors 1`] = ` + { isShown && ( + setIsShown( false ) }> +

Modal content

+
+ ) } +
+ ); + }; + render( ); + + const opener = screen.getByRole( 'button' ); + await user.click( opener ); + const modalFrame = screen.getByRole( 'dialog' ); + expect( modalFrame ).toHaveFocus(); + + // Disable reason: No semantic query can reach the overlay. + // eslint-disable-next-line testing-library/no-node-access + await user.click( modalFrame.parentElement! ); + expect( opener ).toHaveFocus(); + } ); } ); diff --git a/packages/compose/README.md b/packages/compose/README.md index 0a3fed45bce8f..62ebdef6d798e 100644 --- a/packages/compose/README.md +++ b/packages/compose/README.md @@ -323,7 +323,7 @@ _Returns_ ### useFocusReturn -When opening modals/sidebars/dialogs, the focus must move to the opened area and return to the previously focused element when closed. The current hook implements the returning behavior. +Adds the unmount behavior of returning focus to the element which had it previously as is expected for roles like menus or dialogs. _Usage_ diff --git a/packages/compose/src/hooks/use-focus-return/index.js b/packages/compose/src/hooks/use-focus-return/index.js index 66751b7028d32..2cd93b279cd31 100644 --- a/packages/compose/src/hooks/use-focus-return/index.js +++ b/packages/compose/src/hooks/use-focus-return/index.js @@ -3,11 +3,12 @@ */ import { useRef, useEffect, useCallback } from '@wordpress/element'; +/** @type {Element|null} */ +let origin = null; + /** - * When opening modals/sidebars/dialogs, the focus - * must move to the opened area and return to the - * previously focused element when closed. - * The current hook implements the returning behavior. + * Adds the unmount behavior of returning focus to the element which had it + * previously as is expected for roles like menus or dialogs. * * @param {() => void} [onFocusReturn] Overrides the default return behavior. * @return {import('react').RefCallback} Element Ref. @@ -54,6 +55,7 @@ function useFocusReturn( onFocusReturn ) { ); if ( ref.current?.isConnected && ! isFocused ) { + origin ??= focusedBeforeMount.current; return; } @@ -64,10 +66,13 @@ function useFocusReturn( onFocusReturn ) { if ( onFocusReturnRef.current ) { onFocusReturnRef.current(); } else { - /** @type {null | HTMLElement} */ ( - focusedBeforeMount.current + /** @type {null|HTMLElement} */ ( + ! focusedBeforeMount.current.isConnected + ? origin + : focusedBeforeMount.current )?.focus(); } + origin = null; } }, [] ); } diff --git a/packages/core-commands/src/admin-navigation-commands.js b/packages/core-commands/src/admin-navigation-commands.js index a72b0c7dd5ab9..2a189c1c7404d 100644 --- a/packages/core-commands/src/admin-navigation-commands.js +++ b/packages/core-commands/src/admin-navigation-commands.js @@ -3,9 +3,40 @@ */ import { useCommand } from '@wordpress/commands'; import { __ } from '@wordpress/i18n'; -import { external, plus } from '@wordpress/icons'; +import { external, plus, symbol } from '@wordpress/icons'; +import { useSelect } from '@wordpress/data'; +import { store as coreStore } from '@wordpress/core-data'; +import { addQueryArgs, getPath } from '@wordpress/url'; +import { privateApis as routerPrivateApis } from '@wordpress/router'; + +/** + * Internal dependencies + */ +import { unlock } from './lock-unlock'; + +const { useHistory } = unlock( routerPrivateApis ); export function useAdminNavigationCommands() { + const history = useHistory(); + + const { isBlockTheme, canAccessSiteEditor } = useSelect( ( select ) => { + return { + isBlockTheme: + // To avoid making core-commands dependent on block-editor using store string literal name. + // eslint-disable-next-line @wordpress/data-no-store-string-literals + select( 'core/block-editor' )?.getSettings() + .__unstableIsBlockBasedTheme, + canAccessSiteEditor: select( coreStore ).canUser( + 'read', + 'templates' + ), + }; + }, [] ); + + const isSiteEditor = getPath( window.location.href )?.includes( + 'site-editor.php' + ); + useCommand( { name: 'core/add-new-post', label: __( 'Add new post' ), @@ -24,10 +55,26 @@ export function useAdminNavigationCommands() { } ); useCommand( { name: 'core/manage-reusable-blocks', - label: __( 'Manage all of my patterns' ), - callback: () => { - document.location.href = 'edit.php?post_type=wp_block'; + label: __( 'Open patterns' ), + callback: ( { close } ) => { + if ( + ( ! isSiteEditor && ! isBlockTheme ) || + ! canAccessSiteEditor + ) { + document.location.href = 'edit.php?post_type=wp_block'; + } else { + const args = { + path: '/patterns', + }; + const targetUrl = addQueryArgs( 'site-editor.php', args ); + if ( isSiteEditor ) { + history.push( args ); + } else { + document.location = targetUrl; + } + close(); + } }, - icon: external, + icon: isSiteEditor ? symbol : external, } ); } diff --git a/packages/core-commands/src/site-editor-navigation-commands.js b/packages/core-commands/src/site-editor-navigation-commands.js index 3d551619d63b0..fe562b6e44139 100644 --- a/packages/core-commands/src/site-editor-navigation-commands.js +++ b/packages/core-commands/src/site-editor-navigation-commands.js @@ -13,7 +13,6 @@ import { symbolFilled, styles, navigation, - symbol, } from '@wordpress/icons'; import { privateApis as routerPrivateApis } from '@wordpress/router'; import { getQueryArg, addQueryArgs, getPath } from '@wordpress/url'; @@ -198,23 +197,6 @@ function useSiteEditorBasicNavigationCommands() { }, } ); - result.push( { - name: 'core/edit-site/open-template-parts', - label: __( 'Open patterns' ), - icon: symbol, - callback: ( { close } ) => { - const args = { - path: '/patterns', - }; - const targetUrl = addQueryArgs( 'site-editor.php', args ); - if ( isSiteEditor ) { - history.push( args ); - } else { - document.location = targetUrl; - } - close(); - }, - } ); return result; }, [ history, isSiteEditor ] ); diff --git a/packages/core-data/README.md b/packages/core-data/README.md index 63e6e28db08d5..c778b724149ef 100644 --- a/packages/core-data/README.md +++ b/packages/core-data/README.md @@ -313,7 +313,7 @@ _Parameters_ _Returns_ -- `Object | null`: The current global styles. +- `Array< object > | null`: The current global styles. ### getCurrentUser diff --git a/packages/core-data/src/selectors.ts b/packages/core-data/src/selectors.ts index 142d24a9d2b8d..377134ab7c9a3 100644 --- a/packages/core-data/src/selectors.ts +++ b/packages/core-data/src/selectors.ts @@ -1266,7 +1266,7 @@ export function getBlockPatternCategories( state: State ): Array< any > { */ export function getCurrentThemeGlobalStylesRevisions( state: State -): Object | null { +): Array< object > | null { const currentGlobalStylesId = __experimentalGetCurrentGlobalStylesId( state ); diff --git a/packages/create-block-interactive-template/CHANGELOG.md b/packages/create-block-interactive-template/CHANGELOG.md index a79948001de14..b2a9c87e57f26 100644 --- a/packages/create-block-interactive-template/CHANGELOG.md +++ b/packages/create-block-interactive-template/CHANGELOG.md @@ -1 +1,7 @@ + +## Unreleased + +### Enhancement + +- Moves the `example` property into block.json by leveraging changes to create-block to now support `example`. [#52801](https://github.com/WordPress/gutenberg/pull/52801) diff --git a/packages/create-block-interactive-template/block-templates/index.js.mustache b/packages/create-block-interactive-template/block-templates/index.js.mustache index cf40358217c76..5279d80f5754c 100644 --- a/packages/create-block-interactive-template/block-templates/index.js.mustache +++ b/packages/create-block-interactive-template/block-templates/index.js.mustache @@ -28,14 +28,6 @@ import metadata from './block.json'; * @see https://developer.wordpress.org/block-editor/developers/block-api/#registering-a-block */ registerBlockType( metadata.name, { - /** - * Used to construct a preview for the block to be shown in the block inserter. - */ - example: { - attributes: { - message: '{{title}}', - }, - }, /** * @see ./edit.js */ diff --git a/packages/create-block-interactive-template/block-templates/render.php.mustache b/packages/create-block-interactive-template/block-templates/render.php.mustache index ba791783e6ede..c458473d565e0 100644 --- a/packages/create-block-interactive-template/block-templates/render.php.mustache +++ b/packages/create-block-interactive-template/block-templates/render.php.mustache @@ -1,6 +1,13 @@ {{#isBasicVariant}} " + aria-controls="p-" > - +

-{{/isBasicVariant}} \ No newline at end of file +{{/isBasicVariant}} diff --git a/packages/create-block-interactive-template/index.js b/packages/create-block-interactive-template/index.js index a52475ed7a79e..5717b3e709723 100644 --- a/packages/create-block-interactive-template/index.js +++ b/packages/create-block-interactive-template/index.js @@ -15,6 +15,7 @@ module.exports = { }, render: 'file:./render.php', viewScript: 'file:./view.js', + example: {}, }, variants: { basic: {}, diff --git a/packages/edit-post/src/components/secondary-sidebar/list-view-sidebar.js b/packages/edit-post/src/components/secondary-sidebar/list-view-sidebar.js index a4e6e639fbd76..77a56617cb1c6 100644 --- a/packages/edit-post/src/components/secondary-sidebar/list-view-sidebar.js +++ b/packages/edit-post/src/components/secondary-sidebar/list-view-sidebar.js @@ -11,7 +11,7 @@ import { import { useDispatch } from '@wordpress/data'; import { focus } from '@wordpress/dom'; import { useRef, useState } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; +import { __, _x } from '@wordpress/i18n'; import { closeSmall } from '@wordpress/icons'; import { useShortcut } from '@wordpress/keyboard-shortcuts'; import { ESCAPE } from '@wordpress/keycodes'; @@ -140,12 +140,12 @@ export default function ListViewSidebar() { tabs={ [ { name: 'list-view', - title: 'List View', + title: _x( 'List View', 'Post overview' ), className: 'edit-post-sidebar__panel-tab', }, { name: 'outline', - title: 'Outline', + title: _x( 'Outline', 'Post overview' ), className: 'edit-post-sidebar__panel-tab', }, ] } diff --git a/packages/edit-post/src/index.js b/packages/edit-post/src/index.js index 116dea17a5c04..fc5ce8c948e2a 100644 --- a/packages/edit-post/src/index.js +++ b/packages/edit-post/src/index.js @@ -65,7 +65,11 @@ export function initializeEditor( dispatch( blocksStore ).__experimentalReapplyBlockTypeFilters(); // Check if the block list view should be open by default. - if ( select( editPostStore ).isFeatureActive( 'showListViewByDefault' ) ) { + // If `distractionFree` mode is enabled, the block list view should not be open. + if ( + select( editPostStore ).isFeatureActive( 'showListViewByDefault' ) && + ! select( editPostStore ).isFeatureActive( 'distractionFree' ) + ) { dispatch( editPostStore ).setIsListViewOpened( true ); } diff --git a/packages/edit-site/src/components/add-new-pattern/index.js b/packages/edit-site/src/components/add-new-pattern/index.js index 3879498834f42..e7d97aad5feb4 100644 --- a/packages/edit-site/src/components/add-new-pattern/index.js +++ b/packages/edit-site/src/components/add-new-pattern/index.js @@ -65,6 +65,8 @@ export default function AddNewPattern() { }, ]; + // Remove condition when command palette issues are resolved. + // See: https://github.com/WordPress/gutenberg/issues/52154. if ( ! isTemplatePartsMode ) { controls.push( { icon: symbolFilled, diff --git a/packages/edit-site/src/components/global-styles/screen-revisions/index.js b/packages/edit-site/src/components/global-styles/screen-revisions/index.js index b21c14418cbee..aa8bb1f377f29 100644 --- a/packages/edit-site/src/components/global-styles/screen-revisions/index.js +++ b/packages/edit-site/src/components/global-styles/screen-revisions/index.js @@ -7,6 +7,7 @@ import { __experimentalUseNavigator as useNavigator, __experimentalConfirmDialog as ConfirmDialog, Spinner, + __experimentalSpacer as Spacer, } from '@wordpress/components'; import { useSelect, useDispatch } from '@wordpress/data'; import { useContext, useState, useEffect } from '@wordpress/element'; @@ -89,6 +90,7 @@ function ScreenRevisions() { const isLoadButtonEnabled = !! globalStylesRevision?.id && ! areGlobalStyleConfigsEqual( globalStylesRevision, userConfig ); + const shouldShowRevisions = ! isLoading && revisions.length; return ( <> @@ -101,68 +103,84 @@ function ScreenRevisions() { { isLoading && ( ) } - { ! isLoading && ( - - ) } -
- - { isLoadButtonEnabled && ( - - + + ) } +
+ { isLoadingRevisionWithUnsavedChanges && ( + + restoreRevision( globalStylesRevision ) + } + onCancel={ () => + setIsLoadingRevisionWithUnsavedChanges( false ) } - onClick={ () => { - if ( hasUnsavedChanges ) { - setIsLoadingRevisionWithUnsavedChanges( - true - ); - } else { - restoreRevision( globalStylesRevision ); - } - } } > - { __( 'Apply' ) } - - - ) } - - { isLoadingRevisionWithUnsavedChanges && ( - +

+ { __( + 'Loading this revision will discard all unsaved changes.' + ) } +

+

+ { __( + 'Do you want to replace your unsaved changes in the editor?' + ) } +

+ +
) } - isOpen={ isLoadingRevisionWithUnsavedChanges } - confirmButtonText={ __( ' Discard unsaved changes' ) } - onConfirm={ () => restoreRevision( globalStylesRevision ) } - onCancel={ () => - setIsLoadingRevisionWithUnsavedChanges( false ) + + ) : ( + + { + // Adding an existing translation here in case these changes are shipped to WordPress 6.3. + // Later we could update to something better, e.g., "There are currently no style revisions.". + __( 'No results found.' ) } - > - <> -

- { __( - 'Loading this revision will discard all unsaved changes.' - ) } -

-

- { __( - 'Do you want to replace your unsaved changes in the editor?' - ) } -

- -
+ ) } ); diff --git a/packages/edit-site/src/components/global-styles/screen-revisions/test/use-global-styles-revisions.js b/packages/edit-site/src/components/global-styles/screen-revisions/test/use-global-styles-revisions.js index 0b7d086c1120f..8f618a4897edc 100644 --- a/packages/edit-site/src/components/global-styles/screen-revisions/test/use-global-styles-revisions.js +++ b/packages/edit-site/src/components/global-styles/screen-revisions/test/use-global-styles-revisions.js @@ -49,6 +49,7 @@ describe( 'useGlobalStylesRevisions', () => { styles: {}, }, ], + isLoadingGlobalStylesRevisions: false, }; it( 'returns loaded revisions with no unsaved changes', () => { @@ -117,11 +118,23 @@ describe( 'useGlobalStylesRevisions', () => { const { result } = renderHook( () => useGlobalStylesRevisions() ); const { revisions, isLoading, hasUnsavedChanges } = result.current; - expect( isLoading ).toBe( true ); + expect( isLoading ).toBe( false ); expect( hasUnsavedChanges ).toBe( false ); expect( revisions ).toEqual( [] ); } ); + it( 'returns loading status when resolving global revisions', () => { + useSelect.mockImplementation( () => ( { + ...selectValue, + isLoadingGlobalStylesRevisions: true, + } ) ); + + const { result } = renderHook( () => useGlobalStylesRevisions() ); + const { isLoading } = result.current; + + expect( isLoading ).toBe( true ); + } ); + it( 'returns empty revisions when authors are not yet available', () => { useSelect.mockImplementation( () => ( { ...selectValue, diff --git a/packages/edit-site/src/components/global-styles/screen-revisions/use-global-styles-revisions.js b/packages/edit-site/src/components/global-styles/screen-revisions/use-global-styles-revisions.js index 5aee31f1ff99a..bc82a5f2b1293 100644 --- a/packages/edit-site/src/components/global-styles/screen-revisions/use-global-styles-revisions.js +++ b/packages/edit-site/src/components/global-styles/screen-revisions/use-global-styles-revisions.js @@ -21,34 +21,40 @@ const EMPTY_ARRAY = []; const { GlobalStylesContext } = unlock( blockEditorPrivateApis ); export default function useGlobalStylesRevisions() { const { user: userConfig } = useContext( GlobalStylesContext ); - const { authors, currentUser, isDirty, revisions } = useSelect( - ( select ) => { - const { - __experimentalGetDirtyEntityRecords, - getCurrentUser, - getUsers, - getCurrentThemeGlobalStylesRevisions, - } = select( coreStore ); - const dirtyEntityRecords = __experimentalGetDirtyEntityRecords(); - const _currentUser = getCurrentUser(); - const _isDirty = dirtyEntityRecords.length > 0; - const globalStylesRevisions = - getCurrentThemeGlobalStylesRevisions() || EMPTY_ARRAY; - const _authors = - getUsers( SITE_EDITOR_AUTHORS_QUERY ) || EMPTY_ARRAY; + const { + authors, + currentUser, + isDirty, + revisions, + isLoadingGlobalStylesRevisions, + } = useSelect( ( select ) => { + const { + __experimentalGetDirtyEntityRecords, + getCurrentUser, + getUsers, + getCurrentThemeGlobalStylesRevisions, + isResolving, + } = select( coreStore ); + const dirtyEntityRecords = __experimentalGetDirtyEntityRecords(); + const _currentUser = getCurrentUser(); + const _isDirty = dirtyEntityRecords.length > 0; + const globalStylesRevisions = + getCurrentThemeGlobalStylesRevisions() || EMPTY_ARRAY; + const _authors = getUsers( SITE_EDITOR_AUTHORS_QUERY ) || EMPTY_ARRAY; - return { - authors: _authors, - currentUser: _currentUser, - isDirty: _isDirty, - revisions: globalStylesRevisions, - }; - }, - [] - ); + return { + authors: _authors, + currentUser: _currentUser, + isDirty: _isDirty, + revisions: globalStylesRevisions, + isLoadingGlobalStylesRevisions: isResolving( + 'getCurrentThemeGlobalStylesRevisions' + ), + }; + }, [] ); return useMemo( () => { let _modifiedRevisions = []; - if ( ! authors.length || ! revisions.length ) { + if ( ! authors.length || isLoadingGlobalStylesRevisions ) { return { revisions: _modifiedRevisions, hasUnsavedChanges: isDirty, @@ -66,31 +72,33 @@ export default function useGlobalStylesRevisions() { }; } ); - // Flags the most current saved revision. - if ( _modifiedRevisions[ 0 ].id !== 'unsaved' ) { - _modifiedRevisions[ 0 ].isLatest = true; - } + if ( _modifiedRevisions.length ) { + // Flags the most current saved revision. + if ( _modifiedRevisions[ 0 ].id !== 'unsaved' ) { + _modifiedRevisions[ 0 ].isLatest = true; + } - // Adds an item for unsaved changes. - if ( - isDirty && - userConfig && - Object.keys( userConfig ).length > 0 && - currentUser - ) { - const unsavedRevision = { - id: 'unsaved', - styles: userConfig?.styles, - settings: userConfig?.settings, - behaviors: userConfig?.behaviors, - author: { - name: currentUser?.name, - avatar_urls: currentUser?.avatar_urls, - }, - modified: new Date(), - }; + // Adds an item for unsaved changes. + if ( + isDirty && + userConfig && + Object.keys( userConfig ).length > 0 && + currentUser + ) { + const unsavedRevision = { + id: 'unsaved', + styles: userConfig?.styles, + settings: userConfig?.settings, + behaviors: userConfig?.behaviors, + author: { + name: currentUser?.name, + avatar_urls: currentUser?.avatar_urls, + }, + modified: new Date(), + }; - _modifiedRevisions.unshift( unsavedRevision ); + _modifiedRevisions.unshift( unsavedRevision ); + } } return { @@ -98,5 +106,12 @@ export default function useGlobalStylesRevisions() { hasUnsavedChanges: isDirty, isLoading: false, }; - }, [ isDirty, revisions, currentUser, authors, userConfig ] ); + }, [ + isDirty, + revisions, + currentUser, + authors, + userConfig, + isLoadingGlobalStylesRevisions, + ] ); } diff --git a/packages/edit-site/src/components/header-edit-mode/document-actions/index.js b/packages/edit-site/src/components/header-edit-mode/document-actions/index.js index 3a048e753631f..eea8616be1536 100644 --- a/packages/edit-site/src/components/header-edit-mode/document-actions/index.js +++ b/packages/edit-site/src/components/header-edit-mode/document-actions/index.js @@ -128,7 +128,10 @@ function TemplateDocumentActions( { className, onBack } ) { return ( diff --git a/packages/edit-site/src/components/header-edit-mode/document-actions/style.scss b/packages/edit-site/src/components/header-edit-mode/document-actions/style.scss index d26bbdaf28ff6..95ebfb28ff000 100644 --- a/packages/edit-site/src/components/header-edit-mode/document-actions/style.scss +++ b/packages/edit-site/src/components/header-edit-mode/document-actions/style.scss @@ -24,6 +24,15 @@ @include break-large() { width: min(100%, 450px); } + + &.is-synced-entity { + .edit-site-document-actions__title { + color: var(--wp-block-synced-color); + h1 { + color: var(--wp-block-synced-color); + } + } + } } .edit-site-document-actions__command { @@ -36,7 +45,6 @@ .edit-site-document-actions__title { flex-grow: 1; - color: var(--wp-block-synced-color); overflow: hidden; grid-column: 2 / 3; @@ -48,7 +56,6 @@ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; - color: var(--wp-block-synced-color); } .edit-site-document-actions.is-page & { diff --git a/packages/edit-site/src/components/keyboard-shortcuts/edit-mode.js b/packages/edit-site/src/components/keyboard-shortcuts/edit-mode.js index 6bc2d59ec9e97..49efa11368a7d 100644 --- a/packages/edit-site/src/components/keyboard-shortcuts/edit-mode.js +++ b/packages/edit-site/src/components/keyboard-shortcuts/edit-mode.js @@ -33,10 +33,8 @@ function KeyboardShortcutsEditMode() { ); const { redo, undo } = useDispatch( coreStore ); const { - isFeatureActive, setIsListViewOpened, switchEditorMode, - toggleFeature, setIsInserterOpened, closeGeneralSidebar, } = useDispatch( editSiteStore ); @@ -47,7 +45,8 @@ function KeyboardShortcutsEditMode() { const { getBlockName, getSelectedBlockClientId, getBlockAttributes } = useSelect( blockEditorStore ); - const { set: setPreference } = useDispatch( preferencesStore ); + const { get: getPreference } = useSelect( preferencesStore ); + const { set: setPreference, toggle } = useDispatch( preferencesStore ); const { createInfoNotice } = useDispatch( noticesStore ); const toggleDistractionFree = () => { @@ -135,9 +134,9 @@ function KeyboardShortcutsEditMode() { useShortcut( 'core/edit-site/toggle-distraction-free', () => { toggleDistractionFree(); - toggleFeature( 'distractionFree' ); + toggle( 'core/edit-site', 'distractionFree' ); createInfoNotice( - isFeatureActive( 'distractionFree' ) + getPreference( 'core/edit-site', 'distractionFree' ) ? __( 'Distraction free mode turned on.' ) : __( 'Distraction free mode turned off.' ), { diff --git a/packages/edit-site/src/components/layout/index.js b/packages/edit-site/src/components/layout/index.js index 17c660b94f003..1a1c2f3a3830a 100644 --- a/packages/edit-site/src/components/layout/index.js +++ b/packages/edit-site/src/components/layout/index.js @@ -340,6 +340,12 @@ export default function Layout() { ! isEditorLoading } isFullWidth={ isEditing } + defaultSize={ { + width: + canvasSize.width - + 24 /* $canvas-padding */, + height: canvasSize.height, + } } isOversized={ isResizableFrameOversized } diff --git a/packages/edit-site/src/components/page-patterns/patterns-list.js b/packages/edit-site/src/components/page-patterns/patterns-list.js index 2744359bf0628..01525fc5dccab 100644 --- a/packages/edit-site/src/components/page-patterns/patterns-list.js +++ b/packages/edit-site/src/components/page-patterns/patterns-list.js @@ -78,6 +78,16 @@ export default function PatternsList( { categoryId, type } ) { } ); + const updateSearchFilter = ( value ) => { + setCurrentPage( 1 ); + setFilterValue( value ); + }; + + const updateSyncFilter = ( value ) => { + setCurrentPage( 1 ); + setSyncFilter( value ); + }; + const id = useId(); const titleId = `${ id }-title`; const descriptionId = `${ id }-description`; @@ -90,14 +100,12 @@ export default function PatternsList( { categoryId, type } ) { const pageIndex = currentPage - 1; const numPages = Math.ceil( patterns.length / PAGE_SIZE ); - const list = useMemo( - () => - patterns.slice( - pageIndex * PAGE_SIZE, - pageIndex * PAGE_SIZE + PAGE_SIZE - ), - [ pageIndex, patterns ] - ); + const list = useMemo( () => { + return patterns.slice( + pageIndex * PAGE_SIZE, + pageIndex * PAGE_SIZE + PAGE_SIZE + ); + }, [ pageIndex, patterns ] ); const asyncList = useAsyncList( list, { step: 10 } ); @@ -138,7 +146,9 @@ export default function PatternsList( { categoryId, type } ) { setFilterValue( value ) } + onChange={ ( value ) => + updateSearchFilter( value ) + } placeholder={ __( 'Search patterns' ) } label={ __( 'Search patterns' ) } value={ filterValue } @@ -152,7 +162,7 @@ export default function PatternsList( { categoryId, type } ) { label={ __( 'Filter by sync status' ) } value={ syncFilter } isBlock - onChange={ ( value ) => setSyncFilter( value ) } + onChange={ ( value ) => updateSyncFilter( value ) } __nextHasNoMarginBottom > { Object.entries( SYNC_FILTERS ).map( diff --git a/packages/edit-site/src/components/page-patterns/style.scss b/packages/edit-site/src/components/page-patterns/style.scss index ae35bcb86137b..400cc36aeaa43 100644 --- a/packages/edit-site/src/components/page-patterns/style.scss +++ b/packages/edit-site/src/components/page-patterns/style.scss @@ -68,6 +68,9 @@ width: 300px; } } + .edit-site-patterns__sync-status-filter-option:not([aria-checked="true"]) { + color: $gray-600; + } .edit-site-patterns__sync-status-filter-option:active { background: $gray-700; color: $gray-100; @@ -128,6 +131,12 @@ @include break-large { grid-template-columns: 1fr 1fr; } + @include break-huge { + grid-template-columns: 1fr 1fr 1fr; + } + @include break-xhuge { + grid-template-columns: 1fr 1fr 1fr 1fr; + } .edit-site-patterns__pattern { break-inside: avoid-column; display: flex; diff --git a/packages/edit-site/src/components/page-template-parts/index.js b/packages/edit-site/src/components/page-template-parts/index.js index 24b042ed92001..e03d726a57525 100644 --- a/packages/edit-site/src/components/page-template-parts/index.js +++ b/packages/edit-site/src/components/page-template-parts/index.js @@ -39,7 +39,6 @@ export default function PageTemplateParts() { params={ { postId: templatePart.id, postType: templatePart.type, - canvas: 'view', } } state={ { backPath: '/wp_template_part/all' } } > diff --git a/packages/edit-site/src/components/page-templates/index.js b/packages/edit-site/src/components/page-templates/index.js index 3246d0fe884b7..5b0a306fa7fef 100644 --- a/packages/edit-site/src/components/page-templates/index.js +++ b/packages/edit-site/src/components/page-templates/index.js @@ -8,8 +8,7 @@ import { __experimentalVStack as VStack, } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; -import { useSelect } from '@wordpress/data'; -import { store as coreStore, useEntityRecords } from '@wordpress/core-data'; +import { useEntityRecords } from '@wordpress/core-data'; import { decodeEntities } from '@wordpress/html-entities'; /** @@ -21,7 +20,6 @@ import Link from '../routes/link'; import AddedBy from '../list/added-by'; import TemplateActions from '../template-actions'; import AddNewTemplate from '../add-new-template'; -import { store as editSiteStore } from '../../store'; export default function PageTemplates() { const { records: templates } = useEntityRecords( @@ -32,15 +30,6 @@ export default function PageTemplates() { } ); - const { canCreate } = useSelect( ( select ) => { - const { supportsTemplatePartsMode } = - select( editSiteStore ).getSettings(); - return { - postType: select( coreStore ).getPostType( 'wp_template' ), - canCreate: ! supportsTemplatePartsMode, - }; - } ); - const columns = [ { header: __( 'Template' ), @@ -89,13 +78,11 @@ export default function PageTemplates() { - ) + } > { templates && } diff --git a/packages/edit-site/src/components/resizable-frame/index.js b/packages/edit-site/src/components/resizable-frame/index.js index 2b98e46a71532..ee5c98da09033 100644 --- a/packages/edit-site/src/components/resizable-frame/index.js +++ b/packages/edit-site/src/components/resizable-frame/index.js @@ -6,7 +6,7 @@ import classnames from 'classnames'; /** * WordPress dependencies */ -import { useState, useRef, useEffect } from '@wordpress/element'; +import { useState, useRef } from '@wordpress/element'; import { ResizableBox, Tooltip, @@ -82,6 +82,8 @@ function ResizableFrame( { setIsOversized, isReady, children, + /** The default (unresized) width/height of the frame, based on the space availalbe in the viewport. */ + defaultSize, innerContentStyle, } ) { const [ frameSize, setFrameSize ] = useState( INITIAL_FRAME_SIZE ); @@ -95,22 +97,13 @@ function ResizableFrame( { [] ); const { setCanvasMode } = unlock( useDispatch( editSiteStore ) ); - const initialAspectRatioRef = useRef( null ); - // The width of the resizable frame on initial render. - const initialComputedWidthRef = useRef( null ); const FRAME_TRANSITION = { type: 'tween', duration: isResizing ? 0 : 0.5 }; const frameRef = useRef( null ); const resizableHandleHelpId = useInstanceId( ResizableFrame, 'edit-site-resizable-frame-handle-help' ); - - // Remember frame dimensions on initial render. - useEffect( () => { - const { offsetWidth, offsetHeight } = frameRef.current.resizable; - initialComputedWidthRef.current = offsetWidth; - initialAspectRatioRef.current = offsetWidth / offsetHeight; - }, [] ); + const defaultAspectRatio = defaultSize.width / defaultSize.height; const handleResizeStart = ( _event, _direction, ref ) => { // Remember the starting width so we don't have to get `ref.offsetWidth` on @@ -126,7 +119,7 @@ function ResizableFrame( { const maxDoubledDelta = delta.width < 0 // is shrinking ? deltaAbs - : ( initialComputedWidthRef.current - startingWidth ) / 2; + : ( defaultSize.width - startingWidth ) / 2; const deltaToDouble = Math.min( deltaAbs, maxDoubledDelta ); const doubleSegment = deltaAbs === 0 ? 0 : deltaToDouble / deltaAbs; const singleSegment = 1 - doubleSegment; @@ -135,17 +128,14 @@ function ResizableFrame( { const updatedWidth = startingWidth + delta.width; - setIsOversized( updatedWidth > initialComputedWidthRef.current ); + setIsOversized( updatedWidth > defaultSize.width ); // Width will be controlled by the library (via `resizeRatio`), // so we only need to update the height. setFrameSize( { height: isOversized ? '100%' - : calculateNewHeight( - updatedWidth, - initialAspectRatioRef.current - ), + : calculateNewHeight( updatedWidth, defaultAspectRatio ), } ); }; @@ -186,15 +176,12 @@ function ResizableFrame( { FRAME_MIN_WIDTH, frameRef.current.resizable.offsetWidth + delta ), - initialComputedWidthRef.current + defaultSize.width ); setFrameSize( { width: newWidth, - height: calculateNewHeight( - newWidth, - initialAspectRatioRef.current - ), + height: calculateNewHeight( newWidth, defaultAspectRatio ), } ); }; @@ -291,9 +278,7 @@ function ResizableFrame( { undefined } aria-valuemin={ FRAME_MIN_WIDTH } - aria-valuemax={ - initialComputedWidthRef.current - } + aria-valuemax={ defaultSize.width } onKeyDown={ handleResizableHandleKeyDown } initial="hidden" exit="hidden" diff --git a/packages/edit-site/src/components/sidebar-edit-mode/template-panel/index.js b/packages/edit-site/src/components/sidebar-edit-mode/template-panel/index.js index 2fcdcbb2c7e1b..a76626d247af9 100644 --- a/packages/edit-site/src/components/sidebar-edit-mode/template-panel/index.js +++ b/packages/edit-site/src/components/sidebar-edit-mode/template-panel/index.js @@ -7,7 +7,7 @@ import { store as editorStore } from '@wordpress/editor'; import { store as coreStore } from '@wordpress/core-data'; import { decodeEntities } from '@wordpress/html-entities'; import { __ } from '@wordpress/i18n'; -import { navigation as navigationIcon } from '@wordpress/icons'; +import { navigation, symbol } from '@wordpress/icons'; /** * Internal dependencies @@ -18,11 +18,13 @@ import TemplateAreas from './template-areas'; import LastRevision from './last-revision'; import SidebarCard from '../sidebar-card'; +const CARD_ICONS = { + wp_block: symbol, + wp_navigation: navigation, +}; + export default function TemplatePanel() { - const { - info: { title, description, icon }, - record, - } = useSelect( ( select ) => { + const { title, description, icon, record } = useSelect( ( select ) => { const { getEditedPostType, getEditedPostId } = select( editSiteStore ); const { getEditedEntityRecord } = select( coreStore ); const { __experimentalGetTemplateInfo: getTemplateInfo } = @@ -31,10 +33,14 @@ export default function TemplatePanel() { const postType = getEditedPostType(); const postId = getEditedPostId(); const _record = getEditedEntityRecord( 'postType', postType, postId ); + const info = getTemplateInfo( _record ); - const info = _record ? getTemplateInfo( _record ) : {}; - - return { info, record: _record }; + return { + title: info.title, + description: info.description, + icon: info.icon, + record: _record, + }; }, [] ); if ( ! title && ! description ) { @@ -46,9 +52,7 @@ export default function TemplatePanel() { } > diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-global-styles/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-global-styles/index.js index 1e9200bf0af01..c885ca97ba789 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-global-styles/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-global-styles/index.js @@ -37,10 +37,10 @@ export function SidebarNavigationItemGlobalStyles( props ) { const { setCanvasMode } = unlock( useDispatch( editSiteStore ) ); const { createNotice } = useDispatch( noticesStore ); const { set: setPreference } = useDispatch( preferencesStore ); - const { get: getPrefference } = useSelect( preferencesStore ); + const { get: getPreference } = useSelect( preferencesStore ); const turnOffDistractionFreeMode = useCallback( () => { - const isDistractionFree = getPrefference( + const isDistractionFree = getPreference( editSiteStore.name, 'distractionFree' ); @@ -52,7 +52,7 @@ export function SidebarNavigationItemGlobalStyles( props ) { isDismissible: true, type: 'snackbar', } ); - }, [ createNotice, setPreference, getPrefference ] ); + }, [ createNotice, setPreference, getPreference ] ); const hasGlobalStyleVariations = useSelect( ( select ) => !! select( @@ -170,7 +170,7 @@ export default function SidebarNavigationScreenGlobalStyles() { ); const { createNotice } = useDispatch( noticesStore ); const { set: setPreference } = useDispatch( preferencesStore ); - const { get: getPrefference } = useSelect( preferencesStore ); + const { get: getPreference } = useSelect( preferencesStore ); const { isViewMode, isStyleBookOpened } = useSelect( ( select ) => { const { getCanvasMode, getEditorCanvasContainerView } = unlock( select( editSiteStore ) @@ -182,7 +182,7 @@ export default function SidebarNavigationScreenGlobalStyles() { }, [] ); const turnOffDistractionFreeMode = useCallback( () => { - const isDistractionFree = getPrefference( + const isDistractionFree = getPreference( editSiteStore.name, 'distractionFree' ); @@ -194,7 +194,7 @@ export default function SidebarNavigationScreenGlobalStyles() { isDismissible: true, type: 'snackbar', } ); - }, [ createNotice, setPreference, getPrefference ] ); + }, [ createNotice, setPreference, getPreference ] ); const openGlobalStyles = useCallback( async () => { turnOffDistractionFreeMode(); diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/leaf-more-menu.js b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/leaf-more-menu.js index d714ecb43b15d..14d78405b3abe 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/leaf-more-menu.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/leaf-more-menu.js @@ -23,10 +23,12 @@ import { currentlyPreviewingTheme, } from '../../utils/is-previewing-theme'; import { unlock } from '../../lock-unlock'; +import { getPathFromURL } from '../sync-state-with-url/use-sync-path-with-url'; -const { useHistory } = unlock( routerPrivateApis ); +const { useLocation, useHistory } = unlock( routerPrivateApis ); export default function LeafMoreMenu( props ) { + const location = useLocation(); const history = useHistory(); const { block } = props; const { clientId } = block; @@ -63,22 +65,32 @@ export default function LeafMoreMenu( props ) { attributes.type && history ) { - history.push( { - postType: attributes.type, - postId: attributes.id, - ...( isPreviewingTheme() && { - wp_theme_preview: currentlyPreviewingTheme(), - } ), - } ); + history.push( + { + postType: attributes.type, + postId: attributes.id, + ...( isPreviewingTheme() && { + wp_theme_preview: currentlyPreviewingTheme(), + } ), + }, + { + backPath: getPathFromURL( location.params ), + } + ); } if ( name === 'core/page-list-item' && attributes.id && history ) { - history.push( { - postType: 'page', - postId: attributes.id, - ...( isPreviewingTheme() && { - wp_theme_preview: currentlyPreviewingTheme(), - } ), - } ); + history.push( + { + postType: 'page', + postId: attributes.id, + ...( isPreviewingTheme() && { + wp_theme_preview: currentlyPreviewingTheme(), + } ), + }, + { + backPath: getPathFromURL( location.params ), + } + ); } }, [ history ] diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-pages/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-pages/index.js index 331221dde7985..ff6466f98ab5f 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-pages/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-pages/index.js @@ -28,10 +28,15 @@ import { unlock } from '../../lock-unlock'; const { useHistory } = unlock( routerPrivateApis ); const PageItem = ( { postType = 'page', postId, ...props } ) => { - const linkInfo = useLink( { - postType, - postId, - } ); + const linkInfo = useLink( + { + postType, + postId, + }, + { + backPath: '/page', + } + ); return ; }; diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-pattern/use-navigation-menu-content.js b/packages/edit-site/src/components/sidebar-navigation-screen-pattern/use-navigation-menu-content.js index 73d0905f4001a..0a5de0a6274d4 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-pattern/use-navigation-menu-content.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-pattern/use-navigation-menu-content.js @@ -1,3 +1,8 @@ +/** + * WordPress dependencies + */ +import { parse } from '@wordpress/blocks'; + /** * Internal dependencies */ @@ -54,9 +59,14 @@ export default function useNavigationMenuContent( postType, postId ) { return; } + const blocks = + record?.content && typeof record.content !== 'function' + ? parse( record.content ) + : []; + const navigationBlocks = getBlocksOfTypeFromBlocks( 'core/navigation', - record?.blocks + blocks ); if ( ! navigationBlocks.length ) { diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-patterns/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-patterns/index.js index 86155c166d583..f25a13552c424 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-patterns/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-patterns/index.js @@ -10,7 +10,6 @@ import { __experimentalHeading as Heading, } from '@wordpress/components'; import { useViewportMatch } from '@wordpress/compose'; -import { useSelect } from '@wordpress/data'; import { getTemplatePartIcon } from '@wordpress/editor'; import { __, sprintf } from '@wordpress/i18n'; import { getQueryArgs } from '@wordpress/url'; @@ -24,7 +23,6 @@ import SidebarNavigationItem from '../sidebar-navigation-item'; import SidebarNavigationScreen from '../sidebar-navigation-screen'; import CategoryItem from './category-item'; import { DEFAULT_CATEGORY, DEFAULT_TYPE } from '../page-patterns/utils'; -import { store as editSiteStore } from '../../store'; import { useLink } from '../routes/link'; import usePatternCategories from './use-pattern-categories'; import useMyPatterns from './use-my-patterns'; @@ -108,11 +106,6 @@ export default function SidebarNavigationScreenPatterns() { const { patternCategories, hasPatterns } = usePatternCategories(); const { myPatterns } = useMyPatterns(); - const isTemplatePartsMode = useSelect( ( select ) => { - const settings = select( editSiteStore ).getSettings(); - return !! settings.supportsTemplatePartsMode; - }, [] ); - const templatePartsLink = useLink( { path: '/wp_template_part/all' } ); const footer = ! isMobileViewport ? ( @@ -131,7 +124,6 @@ export default function SidebarNavigationScreenPatterns() { return ( { + const settings = select( editSiteStore ).getSettings(); + + return !! settings.supportsTemplatePartsMode; + }, [] ); + return ( { @@ -31,11 +29,6 @@ const TemplateItem = ( { postType, postId, ...props } ) => { export default function SidebarNavigationScreenTemplates() { const isMobileViewport = useViewportMatch( 'medium', '<' ); - const isTemplatePartsMode = useSelect( ( select ) => { - const settings = select( editSiteStore ).getSettings(); - - return !! settings.supportsTemplatePartsMode; - }, [] ); const { records: templates, isResolving: isLoading } = useEntityRecords( 'postType', @@ -51,10 +44,9 @@ export default function SidebarNavigationScreenTemplates() { ); const browseAllLink = useLink( { path: '/wp_template/all' } ); - const canCreate = ! isMobileViewport && ! isTemplatePartsMode; + const canCreate = ! isMobileViewport; return ( { const { getSettings } = unlock( select( editSiteStore ) ); @@ -40,6 +43,7 @@ export default function SidebarNavigationScreen( { }; }, [] ); const { getTheme } = useSelect( coreStore ); + const location = useLocation(); const navigator = useNavigator(); const theme = getTheme( currentlyPreviewingTheme() ); const icon = isRTL() ? chevronRight : chevronLeft; @@ -56,13 +60,17 @@ export default function SidebarNavigationScreen( { alignment="flex-start" className="edit-site-sidebar-navigation-screen__title-icon" > - { ! isRoot && ! backPath && ( + { ! isRoot && ( { - if ( navigator.location.isInitial ) { - navigator.goToParent( { replace: true } ); + const backPath = + backPathProp ?? location.state?.backPath; + if ( backPath ) { + navigator.goTo( backPath, { + isBack: true, + } ); } else { - navigator.goBack(); + navigator.goToParent(); } } } icon={ icon } @@ -70,16 +78,6 @@ export default function SidebarNavigationScreen( { showTooltip={ false } /> ) } - { ! isRoot && backPath && ( - - navigator.goTo( backPath, { isBack: true } ) - } - icon={ icon } - label={ __( 'Back' ) } - showTooltip={ false } - /> - ) } { isRoot && ( { icon={ search } onClick={ () => openCommandCenter() } label={ __( 'Open command palette' ) } + shortcut={ displayShortcut.primary( 'k' ) } /> ) } 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 e10a04526bee1..0933776e7bb14 100644 --- a/packages/edit-site/src/hooks/commands/use-common-commands.js +++ b/packages/edit-site/src/hooks/commands/use-common-commands.js @@ -114,13 +114,68 @@ function useGlobalStylesOpenCssCommands() { }; } -export function useCommonCommands() { +function useGlobalStylesOpenRevisionsCommands() { const { openGeneralSidebar, setEditorCanvasContainerView, setCanvasMode } = unlock( useDispatch( editSiteStore ) ); + const { getCanvasMode } = unlock( useSelect( editSiteStore ) ); const { params } = useLocation(); const isMobileViewport = useViewportMatch( 'medium', '<' ); - const isListPage = getIsListPage( params, isMobileViewport ); - const isEditorPage = ! isListPage; + const isEditorPage = ! getIsListPage( params, isMobileViewport ); + const history = useHistory(); + const hasRevisions = useSelect( + ( select ) => + select( coreStore ).getCurrentThemeGlobalStylesRevisions()?.length, + [] + ); + const commands = useMemo( () => { + if ( ! hasRevisions ) { + return []; + } + + return [ + { + name: 'core/edit-site/open-global-styles-revisions', + label: __( 'Open styles revisions' ), + icon: backup, + callback: ( { close } ) => { + close(); + if ( ! isEditorPage ) { + history.push( { + path: '/wp_global_styles', + canvas: 'edit', + } ); + } + if ( isEditorPage && getCanvasMode() !== 'edit' ) { + setCanvasMode( 'edit' ); + } + openGeneralSidebar( 'edit-site/global-styles' ); + setEditorCanvasContainerView( 'global-styles-revisions' ); + }, + }, + ]; + }, [ + hasRevisions, + history, + openGeneralSidebar, + setEditorCanvasContainerView, + isEditorPage, + getCanvasMode, + setCanvasMode, + ] ); + + return { + isLoading: false, + commands, + }; +} + +export function useCommonCommands() { + const { openGeneralSidebar, setCanvasMode } = unlock( + useDispatch( editSiteStore ) + ); + const { params } = useLocation(); + const isMobileViewport = useViewportMatch( 'medium', '<' ); + const isEditorPage = ! getIsListPage( params, isMobileViewport ); const { getCanvasMode } = unlock( useSelect( editSiteStore ) ); const { set } = useDispatch( preferencesStore ); const { createInfoNotice } = useDispatch( noticesStore ); @@ -139,26 +194,6 @@ export function useCommonCommands() { }; }, [] ); - useCommand( { - name: 'core/edit-site/open-global-styles-revisions', - label: __( 'Open styles revisions' ), - icon: backup, - callback: ( { close } ) => { - close(); - if ( ! isEditorPage ) { - history.push( { - path: '/wp_global_styles', - canvas: 'edit', - } ); - } - if ( isEditorPage && getCanvasMode() !== 'edit' ) { - setCanvasMode( 'edit' ); - } - openGeneralSidebar( 'edit-site/global-styles' ); - setEditorCanvasContainerView( 'global-styles-revisions' ); - }, - } ); - useCommand( { name: 'core/edit-site/open-styles', label: __( 'Open styles' ), @@ -228,4 +263,9 @@ export function useCommonCommands() { name: 'core/edit-site/open-styles-css', hook: useGlobalStylesOpenCssCommands, } ); + + useCommandLoader( { + name: 'core/edit-site/open-styles-revisions', + hook: useGlobalStylesOpenRevisionsCommands, + } ); } 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 3bd1b561a3ab1..2c66a8f2d9f09 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 @@ -19,6 +19,7 @@ import { useCommandLoader } from '@wordpress/commands'; import { privateApis as routerPrivateApis } from '@wordpress/router'; import { store as preferencesStore } from '@wordpress/preferences'; import { store as interfaceStore } from '@wordpress/interface'; +import { store as noticesStore } from '@wordpress/notices'; /** * Internal dependencies @@ -137,8 +138,13 @@ function useManipulateDocumentCommands() { } function useEditUICommands() { - const { openGeneralSidebar, closeGeneralSidebar, switchEditorMode } = - useDispatch( editSiteStore ); + const { + openGeneralSidebar, + closeGeneralSidebar, + setIsInserterOpened, + setIsListViewOpened, + switchEditorMode, + } = useDispatch( editSiteStore ); const { canvasMode, editorMode, activeSidebar } = useSelect( ( select ) => ( { canvasMode: unlock( select( editSiteStore ) ).getCanvasMode(), @@ -150,7 +156,9 @@ function useEditUICommands() { [] ); const { openModal } = useDispatch( interfaceStore ); - const { toggle } = useDispatch( preferencesStore ); + const { get: getPreference } = useSelect( preferencesStore ); + const { set: setPreference, toggle } = useDispatch( preferencesStore ); + const { createInfoNotice } = useDispatch( noticesStore ); if ( canvasMode !== 'edit' ) { return { isLoading: false, commands: [] }; @@ -196,6 +204,29 @@ function useEditUICommands() { }, } ); + commands.push( { + name: 'core/toggle-distraction-free', + label: __( 'Toggle distraction free' ), + icon: cog, + callback: ( { close } ) => { + setPreference( 'core/edit-site', 'fixedToolbar', false ); + setIsInserterOpened( false ); + setIsListViewOpened( false ); + closeGeneralSidebar(); + toggle( 'core/edit-site', 'distractionFree' ); + createInfoNotice( + getPreference( 'core/edit-site', 'distractionFree' ) + ? __( 'Distraction free mode turned on.' ) + : __( 'Distraction free mode turned off.' ), + { + id: 'core/edit-site/distraction-free-mode/notice', + type: 'snackbar', + } + ); + close(); + }, + } ); + commands.push( { name: 'core/toggle-top-toolbar', label: __( 'Toggle top toolbar' ), diff --git a/packages/edit-site/src/store/private-actions.js b/packages/edit-site/src/store/private-actions.js index 1b97959277760..f3dd4c10cec43 100644 --- a/packages/edit-site/src/store/private-actions.js +++ b/packages/edit-site/src/store/private-actions.js @@ -18,11 +18,15 @@ export const setCanvasMode = mode, } ); // Check if the block list view should be open by default. + // If `distractionFree` mode is enabled, the block list view should not be open. if ( mode === 'edit' && registry .select( preferencesStore ) - .get( 'core/edit-site', 'showListViewByDefault' ) + .get( 'core/edit-site', 'showListViewByDefault' ) && + ! registry + .select( preferencesStore ) + .get( 'core/edit-site', 'distractionFree' ) ) { dispatch.setIsListViewOpened( true ); } diff --git a/packages/react-native-editor/CHANGELOG.md b/packages/react-native-editor/CHANGELOG.md index 766c9d725f2bf..20a21c6729e40 100644 --- a/packages/react-native-editor/CHANGELOG.md +++ b/packages/react-native-editor/CHANGELOG.md @@ -11,6 +11,7 @@ For each user feature we should also add a importance categorization label to i ## Unreleased - [*] Display custom color value in mobile Cover Block color picker [#51414] +- [*] Remove visual gap in mobile toolbar when a Gallery block is selected [#52966] ## 1.100.1 - [**] Add WP hook for registering non-core blocks [#52791] diff --git a/packages/rich-text/src/component/use-selection-change-compat.js b/packages/rich-text/src/component/use-selection-change-compat.js index 7a684f584263e..d067d5ec70ff7 100644 --- a/packages/rich-text/src/component/use-selection-change-compat.js +++ b/packages/rich-text/src/component/use-selection-change-compat.js @@ -21,7 +21,7 @@ export function useSelectionChangeCompat() { return useRefEffect( ( element ) => { const { ownerDocument } = element; const { defaultView } = ownerDocument; - const selection = defaultView.getSelection(); + const selection = defaultView?.getSelection(); let range; diff --git a/phpunit/class-gutenberg-rest-navigation-fallback-controller-test.php b/phpunit/class-gutenberg-rest-navigation-fallback-controller-test.php index 70d84cb716951..d6df0c3c504ee 100644 --- a/phpunit/class-gutenberg-rest-navigation-fallback-controller-test.php +++ b/phpunit/class-gutenberg-rest-navigation-fallback-controller-test.php @@ -165,7 +165,7 @@ public function test_prepare_item() { * to the embedded Navigation Post, when the navigation fallback endpoint * is called with the `_embed` param. * - * @covers wp_add_fields_to_navigation_fallback_embeded_links + * @covers wp_add_fields_to_navigation_fallback_embedded_links */ public function test_embedded_navigation_post_contains_required_fields() { // First we'll use the navigation fallback to get a link to the navigation endpoint. diff --git a/phpunit/experimental/interactivity-api/class-wp-interactivity-store-test.php b/phpunit/experimental/interactivity-api/class-wp-interactivity-store-test.php index 84286457f2612..22205289b20be 100644 --- a/phpunit/experimental/interactivity-api/class-wp-interactivity-store-test.php +++ b/phpunit/experimental/interactivity-api/class-wp-interactivity-store-test.php @@ -165,4 +165,22 @@ public function test_store_should_be_correctly_rendered() { $rendered ); } + + public function test_store_should_also_escape_tags_and_amps() { + WP_Interactivity_Store::merge_data( + array( + 'state' => array( + 'amps' => 'http://site.test/?foo=1&baz=2&bar=3', + 'tags' => 'Do not do this: \n
\n', - } ); - await pageUtils.pressKeys( 'primary+v' ); + // Add empty list block + await page.getByPlaceholder( 'Start writing with text or HTML' ) + .fill( ` +
    +
  • +
+` ); // Go back to normal editor await pageUtils.pressKeys( 'secondary+M' ); // Emulates CTRL+Shift+Alt + M => toggle code editor @@ -1365,4 +1367,85 @@ test.describe( 'List (@firefox)', () => { ` ); } ); + + test( 'should merge two list items with nested lists', async ( { + editor, + page, + } ) => { + await editor.insertBlock( { + name: 'core/list', + innerBlocks: [ + { + name: 'core/list-item', + attributes: { content: '1' }, + innerBlocks: [ + { + name: 'core/list', + innerBlocks: [ + { + name: 'core/list-item', + attributes: { content: 'a' }, + }, + ], + }, + ], + }, + { + name: 'core/list-item', + attributes: { content: '2' }, + innerBlocks: [ + { + name: 'core/list', + innerBlocks: [ + { + name: 'core/list-item', + attributes: { content: 'b' }, + }, + ], + }, + ], + }, + ], + } ); + + // Navigate to the third item. + await page.keyboard.press( 'ArrowDown' ); + await page.keyboard.press( 'ArrowDown' ); + await page.keyboard.press( 'ArrowDown' ); + await page.keyboard.press( 'ArrowDown' ); + await page.keyboard.press( 'ArrowDown' ); + await page.keyboard.press( 'ArrowDown' ); + + await page.keyboard.press( 'Backspace' ); + + // Test caret position. + await page.keyboard.type( '‸' ); + + await expect.poll( editor.getBlocks ).toMatchObject( [ + { + name: 'core/list', + innerBlocks: [ + { + name: 'core/list-item', + attributes: { content: '1' }, + innerBlocks: [ + { + name: 'core/list', + innerBlocks: [ + { + name: 'core/list-item', + attributes: { content: 'a‸2' }, + }, + { + name: 'core/list-item', + attributes: { content: 'b' }, + }, + ], + }, + ], + }, + ], + }, + ] ); + } ); } ); diff --git a/test/e2e/specs/editor/various/behaviors.spec.js b/test/e2e/specs/editor/various/behaviors.spec.js index a1b76d6c6105a..438eca45e876d 100644 --- a/test/e2e/specs/editor/various/behaviors.spec.js +++ b/test/e2e/specs/editor/various/behaviors.spec.js @@ -21,32 +21,12 @@ test.describe( 'Testing behaviors functionality', () => { await requestUtils.activateTheme( 'twentytwentyone' ); await requestUtils.deleteAllPosts(); } ); - test.beforeEach( async ( { admin, page, requestUtils } ) => { + test.beforeEach( async ( { requestUtils } ) => { await requestUtils.deleteAllMedia(); - await admin.visitAdminPage( - '/admin.php', - 'page=gutenberg-experiments' - ); - - await page - .locator( `#gutenberg-interactivity-api-core-blocks` ) - .setChecked( true ); - await page.locator( `input[name="submit"]` ).click(); - await page.waitForLoadState(); } ); - test.afterEach( async ( { admin, page, requestUtils } ) => { + test.afterEach( async ( { requestUtils } ) => { await requestUtils.deleteAllMedia(); - await admin.visitAdminPage( - '/admin.php', - 'page=gutenberg-experiments' - ); - - await page - .locator( `#gutenberg-interactivity-api-core-blocks` ) - .setChecked( false ); - await page.locator( `input[name="submit"]` ).click(); - await page.waitForLoadState(); } ); test( 'Behaviors UI can be disabled in the `theme.json`', async ( { diff --git a/test/e2e/specs/editor/various/content-only-lock.spec.js b/test/e2e/specs/editor/various/content-only-lock.spec.js index 962fe0a627909..d6ea152d65f3f 100644 --- a/test/e2e/specs/editor/various/content-only-lock.spec.js +++ b/test/e2e/specs/editor/various/content-only-lock.spec.js @@ -15,13 +15,14 @@ test.describe( 'Content-only lock', () => { } ) => { // Add content only locked block in the code editor await pageUtils.pressKeys( 'secondary+M' ); // Emulates CTRL+Shift+Alt + M => toggle code editor - await page.click( '.editor-post-text-editor' ); - await page.keyboard - .type( ` -
-

Hello

-
- ` ); + + await page.getByPlaceholder( 'Start writing with text or HTML' ) + .fill( ` +
+

Hello

+
+` ); + await pageUtils.pressKeys( 'secondary+M' ); await page.waitForSelector( 'iframe[name="editor-canvas"]' ); await editor.canvas.click( 'role=document[name="Paragraph block"i]' ); diff --git a/test/e2e/specs/editor/various/multi-block-selection.spec.js b/test/e2e/specs/editor/various/multi-block-selection.spec.js index 26dee1291ba2f..4ca43d3f292d2 100644 --- a/test/e2e/specs/editor/various/multi-block-selection.spec.js +++ b/test/e2e/specs/editor/various/multi-block-selection.spec.js @@ -1113,13 +1113,9 @@ test.describe( 'Multi-block selection', () => { name: 'core/paragraph', attributes: { content: '1' }, }, - { - name: 'core/paragraph', - attributes: { content: '|' }, - }, { name: 'core/heading', - attributes: { level: 2, content: '2' }, + attributes: { level: 2, content: '|2' }, }, ] ); } ); diff --git a/test/e2e/specs/site-editor/hybrid-theme.spec.js b/test/e2e/specs/site-editor/hybrid-theme.spec.js index 993ca5eb6bde8..060daf508491a 100644 --- a/test/e2e/specs/site-editor/hybrid-theme.spec.js +++ b/test/e2e/specs/site-editor/hybrid-theme.spec.js @@ -12,33 +12,6 @@ test.describe( 'Hybrid theme', () => { await requestUtils.activateTheme( 'twentytwentyone' ); } ); - test( 'should not show the Add Template Part button', async ( { - admin, - page, - } ) => { - await admin.visitAdminPage( - '/site-editor.php', - 'postType=wp_template_part&path=/wp_template_part/all' - ); - - await expect( - page.locator( 'role=button[name="Add New Template Part"i]' ) - ).toBeHidden(); - - // Back to Patterns page. - await page.click( - 'role=region[name="Navigation"i] >> role=button[name="Back"i]' - ); - - await page.click( - 'role=region[name="Navigation"i] >> role=button[name="Create pattern"i]' - ); - - await expect( - page.locator( 'role=menuitem[name="Create template part"i]' ) - ).toBeHidden(); - } ); - test( 'can access template parts list page', async ( { admin, page } ) => { await admin.visitAdminPage( 'site-editor.php', 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 4ab61111df419..ada0dc3152ac2 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 @@ -22,9 +22,8 @@ test.describe( 'Global styles revisions', () => { await requestUtils.activateTheme( 'twentytwentyone' ); } ); - test.beforeEach( async ( { admin, editor } ) => { + test.beforeEach( async ( { admin } ) => { await admin.visitSiteEditor(); - await editor.canvas.click( 'body' ); } ); test( 'should display revisions UI when there is more than 1 revision', async ( { @@ -32,6 +31,7 @@ test.describe( 'Global styles revisions', () => { editor, userGlobalStylesRevisions, } ) => { + await editor.canvas.click( 'body' ); const currentRevisions = await userGlobalStylesRevisions.getGlobalStylesRevisions(); await userGlobalStylesRevisions.openStylesPanel(); @@ -78,6 +78,7 @@ test.describe( 'Global styles revisions', () => { editor, userGlobalStylesRevisions, } ) => { + await editor.canvas.click( 'body' ); await userGlobalStylesRevisions.openStylesPanel(); await page.getByRole( 'button', { name: 'Colors styles' } ).click(); await page