From e9b925e02564d5b94f2c301072cc6cd3f027283e Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Fri, 3 Mar 2023 14:35:18 +1100 Subject: [PATCH 001/910] Don't add Post Content layout styles to title in the post editor. (#48663) --- packages/edit-post/src/components/visual-editor/index.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/edit-post/src/components/visual-editor/index.js b/packages/edit-post/src/components/visual-editor/index.js index 5c4c1678496924..0e5a2d876332c7 100644 --- a/packages/edit-post/src/components/visual-editor/index.js +++ b/packages/edit-post/src/components/visual-editor/index.js @@ -376,15 +376,11 @@ export default function VisualEditor( { styles } ) { { ! isTemplateMode && (
From 456d5d44ad83c7350902e2ce9471e9e20bf6762b Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Fri, 3 Mar 2023 08:19:05 +0400 Subject: [PATCH 002/910] Widget Importer: Fix Widget Group block imports (#48669) --- .../src/template-part/edit/import-controls.js | 31 +---- .../template-part/edit/utils/transformers.js | 115 +++++++++++++++--- packages/edit-post/src/index.js | 6 +- packages/edit-site/src/index.js | 6 +- packages/widgets/src/index.js | 15 ++- 5 files changed, 121 insertions(+), 52 deletions(-) diff --git a/packages/block-library/src/template-part/edit/import-controls.js b/packages/block-library/src/template-part/edit/import-controls.js index 48178451e33dec..f629691535a327 100644 --- a/packages/block-library/src/template-part/edit/import-controls.js +++ b/packages/block-library/src/template-part/edit/import-controls.js @@ -12,10 +12,6 @@ import { __experimentalHStack as HStack, __experimentalSpacer as Spacer, } from '@wordpress/components'; -import { - switchToBlockType, - getPossibleBlockTransformations, -} from '@wordpress/blocks'; import { store as coreStore } from '@wordpress/core-data'; import { store as noticesStore } from '@wordpress/notices'; @@ -110,36 +106,13 @@ export function TemplatePartImportControls( { area, setAttributes } ) { const blocks = widgets.flatMap( ( widget ) => { const block = transformWidgetToBlock( widget ); - if ( block.name !== 'core/legacy-widget' ) { - return block; - } - - const transforms = getPossibleBlockTransformations( [ - block, - ] ).filter( ( item ) => { - // The block without any transformations can't be a wildcard. - if ( ! item.transforms ) { - return true; - } - - const hasWildCardFrom = item.transforms?.from?.find( - ( from ) => from.blocks && from.blocks.includes( '*' ) - ); - const hasWildCardTo = item.transforms?.to?.find( - ( to ) => to.blocks && to.blocks.includes( '*' ) - ); - - return ! hasWildCardFrom && ! hasWildCardTo; - } ); - // Skip the block if we have no matching transformations. - if ( ! transforms.length ) { + if ( ! block ) { skippedWidgets.add( widget.id_base ); return []; } - // Try transforming the Legacy Widget into a first matching block. - return switchToBlockType( block, transforms[ 0 ].name ); + return block; } ); await createFromBlocks( diff --git a/packages/block-library/src/template-part/edit/utils/transformers.js b/packages/block-library/src/template-part/edit/utils/transformers.js index fdef84d785b909..7932a005729414 100644 --- a/packages/block-library/src/template-part/edit/utils/transformers.js +++ b/packages/block-library/src/template-part/edit/utils/transformers.js @@ -1,7 +1,14 @@ /** * WordPress dependencies */ -import { createBlock, parse } from '@wordpress/blocks'; +import { + parse, + cloneBlock, + createBlock, + getGroupingBlockName, + getPossibleBlockTransformations, + switchToBlockType, +} from '@wordpress/blocks'; /** * Converts a widget entity record into a block. @@ -10,28 +17,98 @@ import { createBlock, parse } from '@wordpress/blocks'; * @return {Object} a block (converted from the entity record). */ export function transformWidgetToBlock( widget ) { - if ( widget.id_base === 'block' ) { - const parsedBlocks = parse( widget.instance.raw.content, { - __unstableSkipAutop: true, - } ); - if ( ! parsedBlocks.length ) { - return createBlock( 'core/paragraph', {}, [] ); + if ( widget.id_base !== 'block' ) { + let attributes; + if ( widget._embedded.about[ 0 ].is_multi ) { + attributes = { + idBase: widget.id_base, + instance: widget.instance, + }; + } else { + attributes = { + id: widget.id, + }; } - return parsedBlocks[ 0 ]; + return switchLegacyWidgetType( + createBlock( 'core/legacy-widget', attributes ) + ); } - let attributes; - if ( widget._embedded.about[ 0 ].is_multi ) { - attributes = { - idBase: widget.id_base, - instance: widget.instance, - }; - } else { - attributes = { - id: widget.id, - }; + const parsedBlocks = parse( widget.instance.raw.content, { + __unstableSkipAutop: true, + } ); + + if ( ! parsedBlocks.length ) { + return undefined; + } + + const block = parsedBlocks[ 0 ]; + + if ( block.name === 'core/widget-group' ) { + return createBlock( + getGroupingBlockName(), + undefined, + transformInnerBlocks( block.innerBlocks ) + ); + } + + if ( block.innerBlocks.length > 0 ) { + return cloneBlock( + block, + undefined, + transformInnerBlocks( block.innerBlocks ) + ); + } + + return block; +} + +/** + * Switch Legacy Widget to the first matching transformation block. + * + * @param {Object} block Legacy Widget block object + * @return {Object|undefined} a block + */ +function switchLegacyWidgetType( block ) { + const transforms = getPossibleBlockTransformations( [ block ] ).filter( + ( item ) => { + // The block without any transformations can't be a wildcard. + if ( ! item.transforms ) { + return true; + } + + const hasWildCardFrom = item.transforms?.from?.find( + ( from ) => from.blocks && from.blocks.includes( '*' ) + ); + const hasWildCardTo = item.transforms?.to?.find( + ( to ) => to.blocks && to.blocks.includes( '*' ) + ); + + // Skip wildcard transformations. + return ! hasWildCardFrom && ! hasWildCardTo; + } + ); + + if ( ! transforms.length ) { + return undefined; } - return createBlock( 'core/legacy-widget', attributes, [] ); + return switchToBlockType( block, transforms[ 0 ].name ); +} + +function transformInnerBlocks( innerBlocks = [] ) { + return innerBlocks + .flatMap( ( block ) => { + if ( block.name === 'core/legacy-widget' ) { + return switchLegacyWidgetType( block ); + } + + return createBlock( + block.name, + block.attributes, + transformInnerBlocks( block.innerBlocks ) + ); + } ) + .filter( ( block ) => !! block ); } diff --git a/packages/edit-post/src/index.js b/packages/edit-post/src/index.js index d2889c407690a7..1e0509f3e8f218 100644 --- a/packages/edit-post/src/index.js +++ b/packages/edit-post/src/index.js @@ -11,7 +11,10 @@ import { createRoot } from '@wordpress/element'; import { dispatch, select } from '@wordpress/data'; import { addFilter } from '@wordpress/hooks'; import { store as preferencesStore } from '@wordpress/preferences'; -import { registerLegacyWidgetBlock } from '@wordpress/widgets'; +import { + registerLegacyWidgetBlock, + registerWidgetGroupBlock, +} from '@wordpress/widgets'; /** * Internal dependencies @@ -68,6 +71,7 @@ export function initializeEditor( registerCoreBlocks(); registerLegacyWidgetBlock( { inserter: false } ); + registerWidgetGroupBlock( { inserter: false } ); if ( process.env.IS_GUTENBERG_PLUGIN ) { __experimentalRegisterExperimentalCoreBlocks( { enableFSEBlocks: settings.__unstableEnableFullSiteEditingBlocks, diff --git a/packages/edit-site/src/index.js b/packages/edit-site/src/index.js index f07f9eed5121d6..d920b02e562ec6 100644 --- a/packages/edit-site/src/index.js +++ b/packages/edit-site/src/index.js @@ -17,7 +17,10 @@ import { import { store as editorStore } from '@wordpress/editor'; import { store as interfaceStore } from '@wordpress/interface'; import { store as preferencesStore } from '@wordpress/preferences'; -import { registerLegacyWidgetBlock } from '@wordpress/widgets'; +import { + registerLegacyWidgetBlock, + registerWidgetGroupBlock, +} from '@wordpress/widgets'; /** * Internal dependencies @@ -47,6 +50,7 @@ export function initializeEditor( id, settings ) { registerCoreBlocks( coreBlocks ); dispatch( blocksStore ).setFreeformFallbackBlockName( 'core/html' ); registerLegacyWidgetBlock( { inserter: false } ); + registerWidgetGroupBlock( { inserter: false } ); if ( process.env.IS_GUTENBERG_PLUGIN ) { __experimentalRegisterExperimentalCoreBlocks( { enableFSEBlocks: true, diff --git a/packages/widgets/src/index.js b/packages/widgets/src/index.js index e55b16ff12b35d..9520943bcae873 100644 --- a/packages/widgets/src/index.js +++ b/packages/widgets/src/index.js @@ -37,10 +37,21 @@ export function registerLegacyWidgetBlock( supports = {} ) { /** * Registers the Widget Group block. + * + * @param {Object} supports Block support settings. */ -export function registerWidgetGroupBlock() { +export function registerWidgetGroupBlock( supports = {} ) { const { metadata, settings, name } = widgetGroup; - registerBlockType( { name, ...metadata }, settings ); + registerBlockType( + { name, ...metadata }, + { + ...settings, + supports: { + ...settings.supports, + ...supports, + }, + } + ); } export { default as registerLegacyWidgetVariations } from './register-legacy-widget-variations'; From 05c549eb625f8a236659dbbde3d6330e2a2a36e3 Mon Sep 17 00:00:00 2001 From: Bart Kalisz Date: Fri, 3 Mar 2023 06:13:41 +0100 Subject: [PATCH 003/910] Migrate multi-block selection E2E tests to Playwright (#48035) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Init multi-block-selection.spec.js 🥵 * Migrate `multi-block-selection` to Playwright * Document and comments * Fix code reviews --------- Co-authored-by: Kai Hao --- .../multi-block-selection.test.js.snap | 347 ----- .../various/multi-block-selection.test.js | 1048 ------------- .../various/multi-block-selection.spec.js | 1380 +++++++++++++++++ 3 files changed, 1380 insertions(+), 1395 deletions(-) delete mode 100644 packages/e2e-tests/specs/editor/various/__snapshots__/multi-block-selection.test.js.snap delete mode 100644 packages/e2e-tests/specs/editor/various/multi-block-selection.test.js create mode 100644 test/e2e/specs/editor/various/multi-block-selection.spec.js diff --git a/packages/e2e-tests/specs/editor/various/__snapshots__/multi-block-selection.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/multi-block-selection.test.js.snap deleted file mode 100644 index 915b52ba94edab..00000000000000 --- a/packages/e2e-tests/specs/editor/various/__snapshots__/multi-block-selection.test.js.snap +++ /dev/null @@ -1,347 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Multi-block selection should allow selecting outer edge if there is no sibling block 1`] = ` -" -

2

-" -`; - -exports[`Multi-block selection should always expand single line selection 1`] = ` -" -

2

-" -`; - -exports[`Multi-block selection should clear selection when clicking next to blocks 1`] = ` -" -

1

- - - -

2

-" -`; - -exports[`Multi-block selection should copy and paste 1`] = ` -" -

1

- - - -

2

-" -`; - -exports[`Multi-block selection should copy and paste 2`] = `""`; - -exports[`Multi-block selection should copy and paste 3`] = ` -" -

1

- - - -

2

-" -`; - -exports[`Multi-block selection should copy multiple blocks 1`] = ` -" -

1

- - - -

2

- - - -

1

- - - -

2

-" -`; - -exports[`Multi-block selection should cut and paste 1`] = `""`; - -exports[`Multi-block selection should cut and paste 2`] = ` -" -

1

- - - -

2

-" -`; - -exports[`Multi-block selection should forward delete across blocks 1`] = ` -" -

1[

- - - -

.

- - - -

]2

-" -`; - -exports[`Multi-block selection should forward delete across blocks 2`] = ` -" -

1&2

-" -`; - -exports[`Multi-block selection should gradually multi-select 1`] = ` -" -
-
-

1

- - - -

2

-
- - - -
-
-" -`; - -exports[`Multi-block selection should gradually multi-select 2`] = ` -" -
-" -`; - -exports[`Multi-block selection should handle Enter across blocks 1`] = ` -" -

1[

- - - -

.

- - - -

]2

-" -`; - -exports[`Multi-block selection should handle Enter across blocks 2`] = ` -" -

1

- - - -

&

- - - -

2

-" -`; - -exports[`Multi-block selection should multi-select from within the list block 1`] = ` -" -

1

- - - -
    -
  • 1
  • -
-" -`; - -exports[`Multi-block selection should not multi select single block 1`] = ` -" -

- - - -

-" -`; - -exports[`Multi-block selection should only trigger multi-selection when at the end 1`] = ` -" -

1.

- - - -

-" -`; - -exports[`Multi-block selection should partially select with shift + click 1`] = ` -" -

1[

- - - -

]2

-" -`; - -exports[`Multi-block selection should partially select with shift + click 2`] = ` -" -

1&2

-" -`; - -exports[`Multi-block selection should place the caret at the end of last pasted paragraph (paste mid-block) 1`] = ` -" -

first paragraph

- - - -

second paragr

- - - -

first paragraph

- - - -

second paragrap

- - - -

aph

-" -`; - -exports[`Multi-block selection should place the caret at the end of last pasted paragraph (paste to empty editor) 1`] = ` -" -

first paragraph

- - - -

second paragrap

-" -`; - -exports[`Multi-block selection should place the caret at the end of last pasted paragraph (replace) 1`] = ` -" -

first paragraph

- - - -

second paragrap

-" -`; - -exports[`Multi-block selection should preserve dragged selection on move 1`] = ` -" -

2

- - - -

3

- - - -

1

-" -`; - -exports[`Multi-block selection should properly select multiple blocks if selected nested blocks belong to different parent 1`] = ` -" -
-

first

- - - -

group

-
- - - -
-

second

- - - -

group

-
-" -`; - -exports[`Multi-block selection should return original focus after failed multi selection attempt 1`] = ` -" -

2

-" -`; - -exports[`Multi-block selection should select all from empty selection 1`] = ` -" -

1

- - - -

2

-" -`; - -exports[`Multi-block selection should select all from empty selection 2`] = `""`; - -exports[`Multi-block selection should select separator (single element block) 1`] = ` -" -

a

- - - -

- - - -
-" -`; - -exports[`Multi-block selection should select separator (single element block) 2`] = ` -" -

&

-" -`; - -exports[`Multi-block selection should set attributes for multiple paragraphs 1`] = ` -" -

1

- - - -

2

-" -`; - -exports[`Multi-block selection should use selection direction to determine vertical edge 1`] = ` -" -

1
2.

- - - -

3

-" -`; - -exports[`Multi-block selection should write over selection 1`] = ` -" -

1[

- - - -

]2

-" -`; - -exports[`Multi-block selection should write over selection 2`] = ` -" -

1...2

-" -`; diff --git a/packages/e2e-tests/specs/editor/various/multi-block-selection.test.js b/packages/e2e-tests/specs/editor/various/multi-block-selection.test.js deleted file mode 100644 index 9e96c5b14cef49..00000000000000 --- a/packages/e2e-tests/specs/editor/various/multi-block-selection.test.js +++ /dev/null @@ -1,1048 +0,0 @@ -/** - * WordPress dependencies - */ -import { - clickBlockAppender, - createNewPost, - pressKeyWithModifier, - pressKeyTimes, - getEditedPostContent, - clickBlockToolbarButton, - clickButton, - clickMenuItem, - insertBlock, - openListView, - saveDraft, - transformBlockTo, -} from '@wordpress/e2e-test-utils'; - -async function getSelectedFlatIndices() { - return await page.evaluate( () => { - const indices = []; - let single; - - Array.from( - document.querySelectorAll( '.wp-block:not(.editor-post-title)' ) - ).forEach( ( node, index ) => { - if ( node.classList.contains( 'is-selected' ) ) { - single = index + 1; - } - - if ( node.classList.contains( 'is-multi-selected' ) ) { - indices.push( index + 1 ); - } - } ); - - return single !== undefined ? single : indices; - } ); -} - -/** - * Tests if the native selection matches the block selection. - */ -async function testNativeSelection() { - // Wait for the selection to update and async mode to update classes of - // deselected blocks. - await page.evaluate( () => new Promise( window.requestIdleCallback ) ); - await page.evaluate( () => { - const selection = window.getSelection(); - const elements = Array.from( - document.querySelectorAll( '.is-multi-selected' ) - ); - - if ( ! elements.length ) { - const element = document.querySelector( '.is-selected' ); - - if ( ! element || ! selection.rangeCount ) { - return; - } - - const { startContainer, endContainer } = selection.getRangeAt( 0 ); - - if ( ! element.contains( startContainer ) ) { - throw 'expected selection to start in the selected block'; - } - - if ( ! element.contains( endContainer ) ) { - throw 'expected selection to start in the selected block'; - } - - return; - } - - if ( selection.rangeCount !== 1 ) { - throw 'expected one range'; - } - - if ( selection.isCollapsed ) { - throw 'expected an uncollapsed selection'; - } - - const firstElement = elements[ 0 ]; - const lastElement = elements[ elements.length - 1 ]; - - if ( ! selection.containsNode( firstElement, true ) ) { - throw 'expected selection to include in the first selected block'; - } - - if ( ! selection.containsNode( lastElement, true ) ) { - throw 'expected selection to include in the last selected block'; - } - } ); -} - -describe( 'Multi-block selection', () => { - beforeEach( async () => { - await createNewPost(); - } ); - - it( 'should select with double ctrl+a and speak', async () => { - await clickBlockAppender(); - await page.keyboard.type( '1' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '2' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '3' ); - - // Multiselect via keyboard. - await pressKeyWithModifier( 'primary', 'a' ); - await pressKeyWithModifier( 'primary', 'a' ); - - expect( await getSelectedFlatIndices() ).toEqual( [ 1, 2, 3 ] ); - - // TODO: It would be great to do this test by spying on `wp.a11y.speak`, - // but it's very difficult to do that because `wp.a11y` has - // DOM-dependant side-effect setup code and doesn't seem straightforward - // to mock. Instead, we check for the DOM node that `wp.a11y.speak()` - // inserts text into. - const speakTextContent = await page.$eval( - '#a11y-speak-assertive', - ( element ) => element.textContent - ); - expect( speakTextContent.trim() ).toEqual( '3 blocks selected.' ); - } ); - - // See #14448: an incorrect buffer may trigger multi-selection too soon. - it( 'should only trigger multi-selection when at the end', async () => { - // Create a paragraph with four lines. - await clickBlockAppender(); - await page.keyboard.type( '1.' ); - await pressKeyWithModifier( 'shift', 'Enter' ); - await page.keyboard.type( '2.' ); - await pressKeyWithModifier( 'shift', 'Enter' ); - await page.keyboard.type( '3.' ); - await pressKeyWithModifier( 'shift', 'Enter' ); - await page.keyboard.type( '4.' ); - // Create a second block. - await page.keyboard.press( 'Enter' ); - // Move to the middle of the first line. - await pressKeyTimes( 'ArrowUp', 4 ); - await page.keyboard.press( 'ArrowRight' ); - // Select mid line one to mid line four. - await pressKeyWithModifier( 'shift', 'ArrowDown' ); - await pressKeyWithModifier( 'shift', 'ArrowDown' ); - await pressKeyWithModifier( 'shift', 'ArrowDown' ); - // Delete the text to see if the selection was correct. - await page.keyboard.press( 'Backspace' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should use selection direction to determine vertical edge', async () => { - await clickBlockAppender(); - await page.keyboard.type( '1' ); - await pressKeyWithModifier( 'shift', 'Enter' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '3' ); - await page.keyboard.press( 'ArrowUp' ); - await page.keyboard.type( '2' ); - - await pressKeyWithModifier( 'shift', 'ArrowUp' ); - await pressKeyWithModifier( 'shift', 'ArrowDown' ); - - // Should type at the end of the paragraph. - await page.keyboard.type( '.' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should always expand single line selection', async () => { - await clickBlockAppender(); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '12' ); - await page.keyboard.press( 'ArrowLeft' ); - await pressKeyWithModifier( 'shift', 'ArrowLeft' ); - await pressKeyWithModifier( 'shift', 'ArrowUp' ); - await testNativeSelection(); - // This deletes all blocks. - await page.keyboard.press( 'Backspace' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should allow selecting outer edge if there is no sibling block', async () => { - await clickBlockAppender(); - await page.keyboard.type( '1' ); - await pressKeyWithModifier( 'shift', 'ArrowUp' ); - // This should replace the content. - await page.keyboard.type( '2' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should select and deselect with shift and arrow keys', async () => { - await clickBlockAppender(); - await page.keyboard.type( '1' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '2' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '3' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '4' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '5' ); - await page.keyboard.press( 'ArrowUp' ); - await page.keyboard.press( 'ArrowUp' ); - await pressKeyWithModifier( 'shift', 'ArrowDown' ); - - await testNativeSelection(); - expect( await getSelectedFlatIndices() ).toEqual( [ 3, 4 ] ); - - await pressKeyWithModifier( 'shift', 'ArrowDown' ); - - await testNativeSelection(); - expect( await getSelectedFlatIndices() ).toEqual( [ 3, 4, 5 ] ); - - await pressKeyWithModifier( 'shift', 'ArrowUp' ); - - await testNativeSelection(); - expect( await getSelectedFlatIndices() ).toEqual( [ 3, 4 ] ); - - await pressKeyWithModifier( 'shift', 'ArrowUp' ); - - await testNativeSelection(); - expect( await getSelectedFlatIndices() ).toBe( 3 ); - - await pressKeyWithModifier( 'shift', 'ArrowUp' ); - - await testNativeSelection(); - expect( await getSelectedFlatIndices() ).toEqual( [ 2, 3 ] ); - - await pressKeyWithModifier( 'shift', 'ArrowUp' ); - - await testNativeSelection(); - expect( await getSelectedFlatIndices() ).toEqual( [ 1, 2, 3 ] ); - - await pressKeyWithModifier( 'shift', 'ArrowDown' ); - - await testNativeSelection(); - expect( await getSelectedFlatIndices() ).toEqual( [ 2, 3 ] ); - - await pressKeyWithModifier( 'shift', 'ArrowDown' ); - - await testNativeSelection(); - expect( await getSelectedFlatIndices() ).toBe( 3 ); - } ); - - // Flaky test. - it.skip( 'should deselect with Escape', async () => { - await clickBlockAppender(); - await page.keyboard.type( '1' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '2' ); - - await pressKeyWithModifier( 'primary', 'a' ); - await pressKeyWithModifier( 'primary', 'a' ); - - await testNativeSelection(); - expect( await getSelectedFlatIndices() ).toEqual( [ 1, 2 ] ); - - await page.keyboard.press( 'Escape' ); - - // Wait for blocks to have updated asynchronously. - await page.evaluate( () => new Promise( window.requestIdleCallback ) ); - expect( await getSelectedFlatIndices() ).toEqual( [] ); - } ); - - it( 'should select with shift + click', async () => { - await clickBlockAppender(); - await page.keyboard.type( '1' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '2' ); - await page.keyboard.down( 'Shift' ); - await page.click( '[data-type="core/paragraph"]' ); - await page.keyboard.up( 'Shift' ); - - await testNativeSelection(); - expect( await getSelectedFlatIndices() ).toEqual( [ 1, 2 ] ); - - // Group the blocks and test that multiselection also works for nested - // blocks. Checks for regressions of - // https://github.com/WordPress/gutenberg/issues/32056 - - await clickBlockToolbarButton( 'Options' ); - await clickMenuItem( 'Group' ); - await page.click( '[data-type="core/paragraph"]' ); - await page.keyboard.down( 'Shift' ); - await page.click( '[data-type="core/paragraph"]:nth-child(2)' ); - await page.keyboard.up( 'Shift' ); - await testNativeSelection(); - expect( await getSelectedFlatIndices() ).toEqual( [ 2, 3 ] ); - } ); - - // @see https://github.com/WordPress/gutenberg/issues/34118 - it( 'should properly select a single block even if `shift` was held for the selection', async () => { - await clickBlockAppender(); - await page.keyboard.type( 'test' ); - - await saveDraft(); - await page.reload(); - await page.waitForSelector( '.edit-post-layout' ); - - await page.keyboard.down( 'Shift' ); - await page.click( '[data-type="core/paragraph"]', { visible: true } ); - await page.keyboard.up( 'Shift' ); - - await pressKeyWithModifier( 'primary', 'a' ); - await page.keyboard.type( 'new content' ); - expect( await getEditedPostContent() ).toMatchInlineSnapshot( ` - " -

new content

- " - ` ); - } ); - - it( 'should properly select multiple blocks if selected nested blocks belong to different parent', async () => { - await clickBlockAppender(); - await page.keyboard.type( 'first' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( 'group' ); - // Multiselect via keyboard. - await page.keyboard.down( 'Shift' ); - await page.keyboard.press( 'ArrowUp' ); - await page.keyboard.up( 'Shift' ); - await transformBlockTo( 'Group' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( 'second' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( 'group' ); - await page.keyboard.down( 'Shift' ); - await page.keyboard.press( 'ArrowUp' ); - await page.keyboard.up( 'Shift' ); - await transformBlockTo( 'Group' ); - - // Confirm setup. - expect( await getEditedPostContent() ).toMatchSnapshot(); - - // Click the first paragraph in the first Group block while pressing `shift` key. - const firstParagraph = await page.waitForXPath( "//p[text()='first']" ); - await page.keyboard.down( 'Shift' ); - await firstParagraph.click(); - await page.keyboard.up( 'Shift' ); - - await page.waitForSelector( '.is-multi-selected' ); - const selectedBlocks = await page.$$( '.is-multi-selected' ); - expect( selectedBlocks ).toHaveLength( 2 ); - } ); - it( 'should properly select part of nested rich text block while holding shift', async () => { - await clickBlockAppender(); - await page.keyboard.type( 'rich text in group' ); - await transformBlockTo( 'Group' ); - await page.keyboard.press( 'ArrowDown' ); - - await page.keyboard.down( 'Shift' ); - const paragraph = await page.$( '[data-type="core/paragraph"]' ); - const { x, y } = await paragraph.boundingBox(); - await page.mouse.move( x + 20, y ); - await page.mouse.down(); - await page.keyboard.up( 'Shift' ); - await page.mouse.up(); - await page.keyboard.type( 'hi' ); - expect( await getEditedPostContent() ).toMatchInlineSnapshot( ` - " -
-

hih text in group

-
- " - ` ); - } ); - - it( 'should select by dragging', async () => { - await clickBlockAppender(); - await page.keyboard.type( '1' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '2' ); - await page.keyboard.press( 'ArrowUp' ); - - const [ paragraph1, paragraph2 ] = await page.$$( - '[data-type="core/paragraph"]' - ); - const coord1 = await paragraph1.clickablePoint(); - const coord2 = await paragraph2.clickablePoint(); - - await page.mouse.move( coord1.x, coord1.y ); - await page.mouse.down(); - await page.mouse.move( coord2.x, coord2.y, { steps: 10 } ); - await page.mouse.up(); - - await testNativeSelection(); - expect( await getSelectedFlatIndices() ).toEqual( [ 1, 2 ] ); - } ); - - it( 'should select by dragging into nested block', async () => { - await clickBlockAppender(); - await page.keyboard.type( '1' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '/group' ); - await page.waitForXPath( - '//button[@aria-selected="true"][text()="Group"]' - ); - await page.keyboard.press( 'Enter' ); - - // Select the default, selected Group layout from the variation picker. - await page.click( - 'button[aria-label="Group: Gather blocks in a container."]' - ); - - const groupAppender = await page.waitForSelector( - '.block-editor-button-block-appender' - ); - await groupAppender.click(); - - const paragraphBlockButton = await page.waitForSelector( - '.editor-block-list-item-paragraph' - ); - await paragraphBlockButton.click(); - - await page.keyboard.type( '2' ); - - const [ paragraph1, paragraph2 ] = await page.$$( - '[data-type="core/paragraph"]' - ); - const coord1 = await paragraph1.clickablePoint(); - const coord2 = await paragraph2.clickablePoint(); - - await page.mouse.move( coord1.x, coord1.y ); - await page.mouse.down(); - await page.mouse.move( coord2.x, coord2.y, { steps: 10 } ); - await page.mouse.up(); - - expect( await getSelectedFlatIndices() ).toEqual( [ 1, 2 ] ); - } ); - - it( 'should cut and paste', async () => { - await clickBlockAppender(); - await page.keyboard.type( '1' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '2' ); - await pressKeyWithModifier( 'primary', 'a' ); - await pressKeyWithModifier( 'primary', 'a' ); - await pressKeyWithModifier( 'primary', 'x' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - - await pressKeyWithModifier( 'primary', 'v' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should copy and paste', async () => { - await clickBlockAppender(); - await page.keyboard.type( '1' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '2' ); - await pressKeyWithModifier( 'primary', 'a' ); - await pressKeyWithModifier( 'primary', 'a' ); - await pressKeyWithModifier( 'primary', 'c' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - - await page.keyboard.press( 'Backspace' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - - await pressKeyWithModifier( 'primary', 'v' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should return original focus after failed multi selection attempt', async () => { - await clickBlockAppender(); - await page.keyboard.type( '1' ); - await page.keyboard.type( '2' ); - await page.keyboard.press( 'ArrowLeft' ); - - const [ coord1, coord2 ] = await page.evaluate( () => { - const selection = window.getSelection(); - - if ( ! selection.rangeCount ) { - return; - } - - const range = selection.getRangeAt( 0 ); - const rect1 = range.getClientRects()[ 0 ]; - const element = document.querySelector( - '[data-type="core/paragraph"]' - ); - const rect2 = element.getBoundingClientRect(); - - return [ - { - x: rect1.x, - y: rect1.y + rect1.height / 2, - }, - { - // Move a bit outside the paragraph. - x: rect2.x - 5, - y: rect2.y + rect2.height / 2, - }, - ]; - } ); - - await page.mouse.move( coord1.x, coord1.y ); - await page.mouse.down(); - await page.mouse.move( coord2.x, coord2.y, { steps: 10 } ); - await page.mouse.up(); - - // Wait for the selection to update. - await page.evaluate( - () => new Promise( window.requestAnimationFrame ) - ); - - // Only "1" should be deleted. - await page.keyboard.press( 'Backspace' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should preserve dragged selection on move', async () => { - await clickBlockAppender(); - await page.keyboard.type( '1' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '2' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '3' ); - - const paragraphs = await page.$$( '[data-type="core/paragraph"]' ); - const coord1 = await paragraphs[ 2 ].clickablePoint(); - const coord2 = await paragraphs[ 1 ].clickablePoint(); - - await page.mouse.move( coord1.x, coord1.y ); - await page.mouse.down(); - await page.mouse.move( coord2.x, coord2.y ); - await page.mouse.up(); - - await testNativeSelection(); - expect( await getSelectedFlatIndices() ).toEqual( [ 2, 3 ] ); - - await clickBlockToolbarButton( 'Move up' ); - - await testNativeSelection(); - expect( await getSelectedFlatIndices() ).toEqual( [ 1, 2 ] ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should clear selection when clicking next to blocks', async () => { - await clickBlockAppender(); - await page.keyboard.type( '1' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '2' ); - await pressKeyWithModifier( 'shift', 'ArrowUp' ); - await testNativeSelection(); - expect( await getSelectedFlatIndices() ).toEqual( [ 1, 2 ] ); - const paragraph = await page.$( '[data-type="core/paragraph"]' ); - const rect = await paragraph.boundingBox(); - const coord = { - x: rect.x - 1, - y: rect.y + rect.height / 2, - }; - - await page.mouse.click( coord.x, coord.y ); - - // Wait for blocks to have updated asynchronously. - await page.evaluate( () => new Promise( window.requestIdleCallback ) ); - await testNativeSelection(); - expect( await getSelectedFlatIndices() ).toEqual( [] ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should place the caret at the end of last pasted paragraph (paste to empty editor)', async () => { - await clickBlockAppender(); - await page.keyboard.type( 'first paragraph' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( 'second paragraph' ); - await pressKeyWithModifier( 'primary', 'a' ); - await pressKeyWithModifier( 'primary', 'a' ); - await pressKeyWithModifier( 'primary', 'c' ); - await page.keyboard.press( 'Backspace' ); - await pressKeyWithModifier( 'primary', 'v' ); - await page.keyboard.press( 'Backspace' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should place the caret at the end of last pasted paragraph (paste mid-block)', async () => { - await clickBlockAppender(); - await page.keyboard.type( 'first paragraph' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( 'second paragraph' ); - await pressKeyWithModifier( 'primary', 'a' ); - await pressKeyWithModifier( 'primary', 'a' ); - await pressKeyWithModifier( 'primary', 'c' ); - - await page.keyboard.press( 'ArrowRight' ); - await page.keyboard.press( 'ArrowLeft' ); - await page.keyboard.press( 'ArrowLeft' ); - await page.keyboard.press( 'ArrowLeft' ); - - await pressKeyWithModifier( 'primary', 'v' ); - await page.keyboard.press( 'Backspace' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should place the caret at the end of last pasted paragraph (replace)', async () => { - await clickBlockAppender(); - await page.keyboard.type( 'first paragraph' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( 'second paragraph' ); - await pressKeyWithModifier( 'primary', 'a' ); - await pressKeyWithModifier( 'primary', 'a' ); - await pressKeyWithModifier( 'primary', 'c' ); - await pressKeyWithModifier( 'primary', 'v' ); - await page.keyboard.press( 'Backspace' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should set attributes for multiple paragraphs', async () => { - await clickBlockAppender(); - await page.keyboard.type( '1' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '2' ); - await pressKeyWithModifier( 'primary', 'a' ); - await pressKeyWithModifier( 'primary', 'a' ); - await clickBlockToolbarButton( 'Align text' ); - await clickButton( 'Align text center' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should copy multiple blocks', async () => { - await clickBlockAppender(); - await page.keyboard.type( '1' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '2' ); - await pressKeyWithModifier( 'primary', 'a' ); - await pressKeyWithModifier( 'primary', 'a' ); - await pressKeyWithModifier( 'primary', 'c' ); - await page.keyboard.press( 'ArrowUp' ); - await pressKeyWithModifier( 'primary', 'v' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - // Previously we would unexpectedly duplicate the block on Enter. - it( 'should not multi select single block', async () => { - await clickBlockAppender(); - await page.keyboard.type( '1' ); - await pressKeyWithModifier( 'primary', 'a' ); - await pressKeyWithModifier( 'primary', 'a' ); - await page.keyboard.press( 'Enter' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should gradually multi-select', async () => { - await clickBlockAppender(); - await page.keyboard.type( '/columns' ); - await page.waitForXPath( - '//button[@aria-selected="true"][text()="Columns"]' - ); - await page.keyboard.press( 'Enter' ); - // Select two columns. - await page.keyboard.press( 'ArrowRight' ); - await page.keyboard.press( 'Enter' ); - // Navigate to appender. - await page.keyboard.press( 'ArrowRight' ); - await page.keyboard.press( 'Enter' ); - // Wait for inserter results to appear and then select a paragraph. - await page.waitForSelector( - '.block-editor-inserter__quick-inserter-results .block-editor-block-types-list__item' - ); - await page.keyboard.press( 'Tab' ); - await page.keyboard.press( 'Enter' ); - // Type two paragraphs - await page.keyboard.type( '1' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '2' ); - - // Confirm correct setup: two columns with two paragraphs in the first. - expect( await getEditedPostContent() ).toMatchSnapshot(); - - await pressKeyWithModifier( 'primary', 'a' ); - await pressKeyWithModifier( 'primary', 'a' ); - - await page.waitForSelector( - '[data-type="core/paragraph"].is-multi-selected' - ); - - await pressKeyWithModifier( 'primary', 'a' ); - - await page.waitForSelector( '[data-type="core/column"].is-selected' ); - - await pressKeyWithModifier( 'primary', 'a' ); - - await page.waitForSelector( - '[data-type="core/column"].is-multi-selected' - ); - - await page.keyboard.press( 'Backspace' ); - - // Expect both columns to be deleted. - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should multi-select from within the list block', async () => { - await clickBlockAppender(); - // Select a paragraph. - await page.keyboard.type( '1' ); - await page.keyboard.press( 'Enter' ); - // Add a list. - await page.keyboard.type( '/list' ); - await page.waitForXPath( - '//button[@aria-selected="true"][text()="List"]' - ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '1' ); - - // Confirm correct setup: a paragraph and a list. - expect( await getEditedPostContent() ).toMatchSnapshot(); - - await pressKeyWithModifier( 'primary', 'a' ); - await pressKeyWithModifier( 'primary', 'a' ); - await pressKeyWithModifier( 'primary', 'a' ); - - await page.waitForSelector( - '[data-type="core/paragraph"].is-multi-selected' - ); - } ); - - it( 'should select all from empty selection', async () => { - await clickBlockAppender(); - - await page.keyboard.type( '1' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '2' ); - - // Confirm setup. - expect( await getEditedPostContent() ).toMatchSnapshot(); - - // Clear the selected block. - await page.keyboard.press( 'Escape' ); - await page.keyboard.press( 'Escape' ); - - await pressKeyWithModifier( 'primary', 'a' ); - - await page.keyboard.press( 'Backspace' ); - - // Expect both paragraphs to be deleted. - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should select title if the cursor is on title', async () => { - await clickBlockAppender(); - - await page.keyboard.type( '1' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '2' ); - - await page.type( '.editor-post-title__input', 'Post title' ); - - await pressKeyWithModifier( 'primary', 'a' ); - const selectedText = await page.evaluate( () => { - return window.getSelection().toString(); - } ); - expect( selectedText ).toEqual( 'Post title' ); - } ); - - it( 'should multi-select in the ListView component with shift + click', async () => { - // Create four blocks. - await clickBlockAppender(); - await page.keyboard.type( '1' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '2' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '3' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '4' ); - - // Open up the list view, and get a reference to each of the list items. - await openListView(); - const navButtons = await page.$$( - '.block-editor-list-view-block-select-button' - ); - - // Clicking on the second list item should result in the second block being selected. - await navButtons[ 1 ].click(); - expect( await getSelectedFlatIndices() ).toEqual( 2 ); - - // Shift clicking the fourth list item should result in blocks 2 through 4 being selected. - await page.keyboard.down( 'Shift' ); - await navButtons[ 3 ].click(); - expect( await getSelectedFlatIndices() ).toEqual( [ 2, 3, 4 ] ); - - // With the shift key still held down, clicking the first block should result in - // the first two blocks being selected. - await navButtons[ 0 ].click(); - expect( await getSelectedFlatIndices() ).toEqual( [ 1, 2 ] ); - - // With the shift key up, clicking the fourth block should result in only that block - // being selected. - await page.keyboard.up( 'Shift' ); - await navButtons[ 3 ].click(); - expect( await getSelectedFlatIndices() ).toEqual( 4 ); - } ); - - it( 'should multi-select in the ListView component with shift + up and down keys', async () => { - // Create four blocks. - await clickBlockAppender(); - await page.keyboard.type( '1' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '2' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '3' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '4' ); - - // Open up the list view. The fourth block button will be focused. - await openListView(); - - // Press Up twice to focus over the second block. - await pressKeyTimes( 'ArrowUp', 2 ); - - // Shift + press Down to select the 2nd and 3rd blocks. - await page.keyboard.down( 'Shift' ); - await page.keyboard.press( 'ArrowDown' ); - expect( await getSelectedFlatIndices() ).toEqual( [ 2, 3 ] ); - - // Press Down once more to also select the 4th block. - await page.keyboard.press( 'ArrowDown' ); - expect( await getSelectedFlatIndices() ).toEqual( [ 2, 3, 4 ] ); - - // Press Up three times to adjust the selection to only include the first two blocks. - await pressKeyTimes( 'ArrowUp', 3 ); - expect( await getSelectedFlatIndices() ).toEqual( [ 1, 2 ] ); - - // Raise the shift key - await page.keyboard.up( 'Shift' ); - - // Navigate to the bottom of the list of blocks. - await pressKeyTimes( 'ArrowDown', 3 ); - - // Shift + press UP to select the 3rd and 4th blocks. - // This tests that shift selecting blocks by keyboard that are not adjacent - // to an existing selection resets the selection. - await page.keyboard.down( 'Shift' ); - await page.keyboard.press( 'ArrowUp' ); - await page.keyboard.up( 'Shift' ); - expect( await getSelectedFlatIndices() ).toEqual( [ 3, 4 ] ); - } ); - - it( 'should forward delete across blocks', async () => { - await clickBlockAppender(); - await page.keyboard.type( '1[' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '.' ); - await page.keyboard.press( 'Enter' ); - // "## " creates h2. - await page.keyboard.type( '## ]2' ); - await page.keyboard.press( 'ArrowLeft' ); - // Select everything between []. - await pressKeyWithModifier( 'shift', 'ArrowLeft' ); - await pressKeyWithModifier( 'shift', 'ArrowLeft' ); - await pressKeyWithModifier( 'shift', 'ArrowLeft' ); - await pressKeyWithModifier( 'shift', 'ArrowLeft' ); - await pressKeyWithModifier( 'shift', 'ArrowLeft' ); - - // Test setup. - expect( await getEditedPostContent() ).toMatchSnapshot(); - - await page.keyboard.press( 'Delete' ); - - // Ensure selection is in the correct place. - await page.keyboard.type( '&' ); - - // Expect a heading with "1&2" as its content. - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should write over selection', async () => { - await clickBlockAppender(); - await page.keyboard.type( '1[' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( ']2' ); - await page.keyboard.press( 'ArrowLeft' ); - // Select everything between []. - await pressKeyWithModifier( 'shift', 'ArrowLeft' ); - await pressKeyWithModifier( 'shift', 'ArrowLeft' ); - await pressKeyWithModifier( 'shift', 'ArrowLeft' ); - - // Test setup. - expect( await getEditedPostContent() ).toMatchSnapshot(); - - // Ensure selection is in the correct place. - await page.keyboard.type( '...' ); - - // Expect a heading with "1&2" as its content. - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should handle Enter across blocks', async () => { - await clickBlockAppender(); - await page.keyboard.type( '1[' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '.' ); - await page.keyboard.press( 'Enter' ); - // "## " creates h2. - await page.keyboard.type( '## ]2' ); - await page.keyboard.press( 'ArrowLeft' ); - // Select everything between []. - await pressKeyWithModifier( 'shift', 'ArrowLeft' ); - await pressKeyWithModifier( 'shift', 'ArrowLeft' ); - await pressKeyWithModifier( 'shift', 'ArrowLeft' ); - await pressKeyWithModifier( 'shift', 'ArrowLeft' ); - await pressKeyWithModifier( 'shift', 'ArrowLeft' ); - - // Test setup. - expect( await getEditedPostContent() ).toMatchSnapshot(); - - await page.keyboard.press( 'Enter' ); - - // Ensure selection is in the correct place. - await page.keyboard.type( '&' ); - - // Expect two blocks with "&" in between. - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should select separator (single element block)', async () => { - await clickBlockAppender(); - await page.keyboard.type( 'a' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( '/hr' ); - await page.waitForXPath( - '//button[@aria-selected="true"][text()="Separator"]' - ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.press( 'ArrowUp' ); - await page.keyboard.press( 'ArrowUp' ); - await page.keyboard.press( 'ArrowRight' ); - await pressKeyWithModifier( 'shift', 'ArrowDown' ); - await pressKeyWithModifier( 'shift', 'ArrowDown' ); - - // Test setup. - expect( await getEditedPostContent() ).toMatchSnapshot(); - - await page.keyboard.press( 'Backspace' ); - - // Ensure selection is in the correct place. - await page.keyboard.type( '&' ); - - // Expect a paragraph with "&". - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should partially select with shift + click', async () => { - await clickBlockAppender(); - await pressKeyWithModifier( 'primary', 'b' ); - await page.keyboard.type( '1' ); - await pressKeyWithModifier( 'primary', 'b' ); - await page.keyboard.type( '[' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( ']2' ); - await page.keyboard.press( 'ArrowLeft' ); - await page.keyboard.down( 'Shift' ); - await page.click( 'strong' ); - await page.keyboard.up( 'Shift' ); - - // Test setup. - expect( await getEditedPostContent() ).toMatchSnapshot(); - - await page.keyboard.press( 'Backspace' ); - - // Ensure selection is in the correct place. - await page.keyboard.type( '&' ); - - // Expect two blocks with "&" in between. - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - describe( 'shift+click multi-selection', () => { - it( 'should multi-select block with text selection and a block without text selection', async () => { - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( 'hi' ); - await page.keyboard.press( 'Enter' ); - await insertBlock( 'Spacer' ); - await page.keyboard.press( 'ArrowUp' ); - - const spacerBlock = await page.waitForSelector( - '.wp-block.wp-block-spacer' - ); - const boundingBox = await spacerBlock.boundingBox(); - const mousePosition = { - x: boundingBox.x + boundingBox.width / 2, - y: boundingBox.y + boundingBox.height / 2, - }; - await page.keyboard.down( 'Shift' ); - await page.mouse.click( mousePosition.x, mousePosition.y ); - await page.keyboard.up( 'Shift' ); - - const selectedBlocks = await page.$$( - '.wp-block.is-multi-selected' - ); - expect( selectedBlocks.length ).toBe( 2 ); - } ); - it( 'should multi-select blocks without text selection', async () => { - await insertBlock( 'Spacer' ); - // Get the first spacer block element. - const spacerBlock = await page.waitForSelector( - '.wp-block.wp-block-spacer' - ); - const boundingBox = await spacerBlock.boundingBox(); - await page.keyboard.press( 'Enter' ); - await insertBlock( 'Spacer' ); - const mousePosition = { - x: boundingBox.x + boundingBox.width / 2, - y: boundingBox.y + boundingBox.height / 2, - }; - await page.keyboard.down( 'Shift' ); - await page.mouse.click( mousePosition.x, mousePosition.y ); - await page.keyboard.up( 'Shift' ); - const selectedBlocks = await page.$$( - '.wp-block.is-multi-selected' - ); - expect( selectedBlocks.length ).toBe( 2 ); - } ); - } ); - - it( 'should select by dragging into separator', async () => { - await clickBlockAppender(); - await page.keyboard.type( '1' ); - await insertBlock( 'Separator' ); - await page.keyboard.press( 'ArrowUp' ); - - const [ paragraph, hr ] = await page.$$( '[data-type]' ); - const coord1 = await paragraph.clickablePoint(); - const coord2 = await hr.clickablePoint(); - - await page.mouse.move( coord1.x, coord1.y ); - await page.mouse.down(); - await page.mouse.move( coord2.x, coord2.y, { steps: 10 } ); - await page.mouse.up(); - - expect( await getSelectedFlatIndices() ).toEqual( [ 1, 2 ] ); - } ); -} ); diff --git a/test/e2e/specs/editor/various/multi-block-selection.spec.js b/test/e2e/specs/editor/various/multi-block-selection.spec.js new file mode 100644 index 00000000000000..d651001c8f2640 --- /dev/null +++ b/test/e2e/specs/editor/various/multi-block-selection.spec.js @@ -0,0 +1,1380 @@ +/** + * WordPress dependencies + */ +const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); + +/** @typedef {import('@playwright/test').Page} Page */ +/** @typedef {import('@wordpress/e2e-test-utils-playwright').Editor} Editor */ + +/** + * Some tests in this file use the character `|` to represent the caret's position + * in a more readable format. + */ +test.describe( 'Multi-block selection', () => { + test.use( { + multiBlockSelectionUtils: async ( { page, editor }, use ) => { + await use( new MultiBlockSelectionUtils( { page, editor } ) ); + }, + } ); + + test.beforeEach( async ( { admin } ) => { + await admin.createNewPost(); + } ); + + test( 'should select with double ctrl+a and speak', async ( { + page, + editor, + pageUtils, + multiBlockSelectionUtils, + } ) => { + for ( let i = 1; i <= 3; i += 1 ) { + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: `${ i }` }, + } ); + } + + // Multiselect via keyboard. + await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + + await expect + .poll( multiBlockSelectionUtils.getSelectedFlatIndices ) + .toEqual( [ 1, 2, 3 ] ); + + await expect( page.locator( '[aria-live="assertive"]' ) ).toHaveText( + '3 blocks selected.' + ); + } ); + + // See #14448: an incorrect buffer may trigger multi-selection too soon. + test( 'should only trigger multi-selection when at the end', async ( { + page, + editor, + pageUtils, + } ) => { + // Create a paragraph with four lines. + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: '1.
2.
3.
4.' }, + } ); + // Create a second block and focus it. + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: '' }, + } ); + await editor.canvas + .getByRole( 'document', { name: 'Empty block' } ) + .click(); + // Move to the middle of the first line. + await pageUtils.pressKeyTimes( 'ArrowUp', 4 ); + await page.keyboard.press( 'ArrowRight' ); + // Select mid line one to mid line four. + await pageUtils.pressKeyTimes( 'Shift+ArrowDown', 3 ); + // Delete the text to see if the selection was correct. + await page.keyboard.press( 'Backspace' ); + + await expect.poll( editor.getBlocks ).toMatchObject( [ + { name: 'core/paragraph', attributes: { content: '1.' } }, + { name: 'core/paragraph', attributes: { content: '' } }, + ] ); + } ); + + test( 'should use selection direction to determine vertical edge', async ( { + page, + editor, + } ) => { + await editor.canvas + .getByRole( 'button', { name: 'Add default block' } ) + .click(); + await page.keyboard.type( '1' ); + await page.keyboard.press( 'Shift+Enter' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( '3' ); + await page.keyboard.press( 'ArrowUp' ); + await page.keyboard.type( '2' ); + + await page.keyboard.press( 'Shift+ArrowUp' ); + await page.keyboard.press( 'Shift+ArrowDown' ); + + // Should type at the end of the paragraph. + await page.keyboard.type( '|' ); + + await expect.poll( editor.getBlocks ).toMatchObject( [ + { name: 'core/paragraph', attributes: { content: '1
2|' } }, + { name: 'core/paragraph', attributes: { content: '3' } }, + ] ); + } ); + + test( 'should always expand single line selection', async ( { + page, + editor, + multiBlockSelectionUtils, + } ) => { + await editor.canvas + .getByRole( 'button', { name: 'Add default block' } ) + .click(); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( '12' ); + await page.keyboard.press( 'ArrowLeft' ); + await page.keyboard.press( 'Shift+ArrowLeft' ); + await page.keyboard.press( 'Shift+ArrowUp' ); + await expect( multiBlockSelectionUtils.assertNativeSelection ).toPass(); + + await page.keyboard.press( 'Backspace' ); + await expect.poll( editor.getBlocks ).toMatchObject( [ + { + name: 'core/paragraph', + attributes: { content: '2' }, + }, + ] ); + } ); + + test( 'should allow selecting outer edge if there is no sibling block', async ( { + page, + editor, + } ) => { + await editor.canvas + .getByRole( 'button', { name: 'Add default block' } ) + .click(); + await page.keyboard.type( '1' ); + await page.keyboard.press( 'Shift+ArrowUp' ); + // This should replace the content. + await page.keyboard.type( '2' ); + + await expect.poll( editor.getBlocks ).toMatchObject( [ + { + name: 'core/paragraph', + attributes: { content: '2' }, + }, + ] ); + } ); + + test( 'should select and deselect with shift and arrow keys', async ( { + page, + editor, + multiBlockSelectionUtils, + } ) => { + for ( let i = 1; i <= 5; i += 1 ) { + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: `${ i }` }, + } ); + } + await editor.canvas + .getByRole( 'document', { name: 'Paragraph block' } ) + .filter( { hasText: '3' } ) + .click(); + + await page.keyboard.press( 'Shift+ArrowDown' ); + await expect( multiBlockSelectionUtils.assertNativeSelection ).toPass(); + await expect + .poll( multiBlockSelectionUtils.getSelectedFlatIndices ) + .toEqual( [ 3, 4 ] ); + + await page.keyboard.press( 'Shift+ArrowDown' ); + await expect( multiBlockSelectionUtils.assertNativeSelection ).toPass(); + await expect + .poll( multiBlockSelectionUtils.getSelectedFlatIndices ) + .toEqual( [ 3, 4, 5 ] ); + + await page.keyboard.press( 'Shift+ArrowUp' ); + await expect( multiBlockSelectionUtils.assertNativeSelection ).toPass(); + await expect + .poll( multiBlockSelectionUtils.getSelectedFlatIndices ) + .toEqual( [ 3, 4 ] ); + + await page.keyboard.press( 'Shift+ArrowUp' ); + await expect( multiBlockSelectionUtils.assertNativeSelection ).toPass(); + await expect + .poll( multiBlockSelectionUtils.getSelectedFlatIndices ) + .toEqual( [ 3 ] ); + + await page.keyboard.press( 'Shift+ArrowUp' ); + await expect( multiBlockSelectionUtils.assertNativeSelection ).toPass(); + await expect + .poll( multiBlockSelectionUtils.getSelectedFlatIndices ) + .toEqual( [ 2, 3 ] ); + + await page.keyboard.press( 'Shift+ArrowUp' ); + await expect( multiBlockSelectionUtils.assertNativeSelection ).toPass(); + await expect + .poll( multiBlockSelectionUtils.getSelectedFlatIndices ) + .toEqual( [ 1, 2, 3 ] ); + + await page.keyboard.press( 'Shift+ArrowDown' ); + await expect( multiBlockSelectionUtils.assertNativeSelection ).toPass(); + await expect + .poll( multiBlockSelectionUtils.getSelectedFlatIndices ) + .toEqual( [ 2, 3 ] ); + + await page.keyboard.press( 'Shift+ArrowDown' ); + await expect( multiBlockSelectionUtils.assertNativeSelection ).toPass(); + await expect + .poll( multiBlockSelectionUtils.getSelectedFlatIndices ) + .toEqual( [ 3 ] ); + } ); + + test( 'should deselect with Escape', async ( { + page, + editor, + pageUtils, + multiBlockSelectionUtils, + } ) => { + for ( let i = 1; i <= 2; i += 1 ) { + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: `${ i }` }, + } ); + } + await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + + await expect + .poll( multiBlockSelectionUtils.getSelectedFlatIndices ) + .toEqual( [ 1, 2 ] ); + + await page.keyboard.press( 'Escape' ); + + // FIXME: This doesn't seem to work anymore. + // await expect + // .poll( multiBlockSelectionUtils.getSelectedFlatIndices ) + // .toEqual( [] ); + } ); + + test( 'should select with shift + click', async ( { + page, + editor, + multiBlockSelectionUtils, + } ) => { + await editor.canvas + .getByRole( 'button', { name: 'Add default block' } ) + .click(); + await page.keyboard.type( '1' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( '2' ); + + await editor.canvas + .getByRole( 'document', { name: 'Paragraph block' } ) + .filter( { hasText: '1' } ) + .click( { modifiers: [ 'Shift' ] } ); + + await expect( multiBlockSelectionUtils.assertNativeSelection ).toPass(); + await expect + .poll( multiBlockSelectionUtils.getSelectedFlatIndices ) + .toEqual( [ 1, 2 ] ); + + // Group the blocks and test that multiselection also works for nested + // blocks. Checks for regressions of + // https://github.com/WordPress/gutenberg/issues/32056 + await page + .getByRole( 'toolbar', { name: 'Block tools' } ) + .getByRole( 'button', { name: 'Group' } ) + .click(); + await editor.canvas + .getByRole( 'document', { name: 'Paragraph block' } ) + .filter( { hasText: '1' } ) + .click(); + await editor.canvas + .getByRole( 'document', { name: 'Paragraph block' } ) + .filter( { hasText: '2' } ) + .click( { modifiers: [ 'Shift' ] } ); + + await expect( multiBlockSelectionUtils.assertNativeSelection ).toPass(); + await expect + .poll( multiBlockSelectionUtils.getSelectedFlatIndices ) + .toEqual( [ 2, 3 ] ); + } ); + + // @see https://github.com/WordPress/gutenberg/issues/34118 + test( 'should properly select a single block even if `shift` was held for the selection', async ( { + page, + editor, + pageUtils, + } ) => { + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: 'test' }, + } ); + + await page.getByRole( 'button', { name: 'Save draft' } ).click(); + await expect( + page + .getByRole( 'button', { name: 'Dismiss this notice' } ) + .filter( { hasText: 'Draft saved' } ) + ).toBeVisible(); + await page.reload(); + + await editor.canvas + .getByRole( 'document', { name: 'Paragraph block' } ) + .click( { modifiers: [ 'Shift' ] } ); + await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await page.keyboard.type( 'new content' ); + + await expect.poll( editor.getBlocks ).toMatchObject( [ + { + name: 'core/paragraph', + attributes: { content: 'new content' }, + }, + ] ); + } ); + + test( 'should properly select multiple blocks if selected nested blocks belong to different parent', async ( { + editor, + multiBlockSelectionUtils, + } ) => { + await editor.insertBlock( { + name: 'core/group', + innerBlocks: [ + { name: 'core/paragraph', attributes: { content: 'first' } }, + { name: 'core/paragraph', attributes: { content: 'group' } }, + ], + } ); + await editor.insertBlock( { + name: 'core/group', + innerBlocks: [ + { name: 'core/paragraph', attributes: { content: 'second' } }, + { name: 'core/paragraph', attributes: { content: 'group' } }, + ], + } ); + + await editor.canvas + .getByRole( 'document', { name: 'Paragraph block' } ) + .filter( { hasText: 'group' } ) + .nth( 1 ) + .click(); + await editor.canvas + .getByRole( 'document', { name: 'Paragraph block' } ) + .filter( { hasText: 'first' } ) + .click( { modifiers: [ 'Shift' ] } ); + + await expect + .poll( multiBlockSelectionUtils.getSelectedFlatIndices ) + .toEqual( [ 1, 4 ] ); + } ); + + test( 'should properly select part of nested rich text block while holding shift', async ( { + page, + editor, + } ) => { + await editor.insertBlock( { + name: 'core/group', + innerBlocks: [ + { + name: 'core/paragraph', + attributes: { content: 'rich text in group' }, + }, + ], + } ); + + const paragraphBlock = editor.canvas.getByRole( 'document', { + name: 'Paragraph block', + } ); + const { height } = await paragraphBlock.boundingBox(); + await paragraphBlock.click( { position: { x: 0, y: height / 2 } } ); + await paragraphBlock.click( { + position: { x: 20, y: height / 2 }, + modifiers: [ 'Shift' ], + } ); + await page.keyboard.type( 'hi' ); + + await expect.poll( editor.getBlocks ).toMatchObject( [ + { + name: 'core/group', + innerBlocks: [ + { + name: 'core/paragraph', + attributes: { content: 'hih text in group' }, + }, + ], + }, + ] ); + } ); + + test( 'should select by dragging', async ( { + page, + editor, + multiBlockSelectionUtils, + } ) => { + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: '1' }, + } ); + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: '2' }, + } ); + // To hide the block tool bar. + await page.keyboard.press( 'ArrowDown' ); + + const [ paragraph1, paragraph2 ] = await editor.canvas + .getByRole( 'document', { name: 'Paragraph block' } ) + .all(); + + await paragraph1.hover(); + await page.mouse.down(); + await paragraph2.hover(); + await page.mouse.up(); + + await expect( multiBlockSelectionUtils.assertNativeSelection ).toPass(); + await expect + .poll( multiBlockSelectionUtils.getSelectedFlatIndices ) + .toEqual( [ 1, 2 ] ); + } ); + + test( 'should select by dragging into nested block', async ( { + page, + editor, + multiBlockSelectionUtils, + } ) => { + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: '1' }, + } ); + await editor.insertBlock( { + name: 'core/group', + innerBlocks: [ + { + name: 'core/paragraph', + attributes: { content: '2' }, + }, + ], + } ); + // To hide the block tool bar. + await page.keyboard.press( 'ArrowDown' ); + await page.keyboard.press( 'ArrowDown' ); + + const [ paragraph1, paragraph2 ] = await editor.canvas + .getByRole( 'document', { name: 'Paragraph block' } ) + .all(); + + await paragraph1.hover(); + await page.mouse.down(); + await paragraph2.hover(); + await page.mouse.up(); + + await expect + .poll( multiBlockSelectionUtils.getSelectedFlatIndices ) + .toEqual( [ 1, 2 ] ); + } ); + + test( 'should cut and paste', async ( { editor, pageUtils } ) => { + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: '1' }, + } ); + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: '2' }, + } ); + + await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await pageUtils.pressKeyWithModifier( 'primary', 'x' ); + + await expect.poll( editor.getBlocks ).toEqual( [] ); + + await pageUtils.pressKeyWithModifier( 'primary', 'v' ); + + await expect.poll( editor.getBlocks ).toMatchObject( [ + { name: 'core/paragraph', attributes: { content: '1' } }, + { name: 'core/paragraph', attributes: { content: '2' } }, + ] ); + } ); + + test( 'should copy and paste', async ( { page, editor, pageUtils } ) => { + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: 'first paragraph' }, + } ); + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: 'second paragraph' }, + } ); + + await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await pageUtils.pressKeyWithModifier( 'primary', 'c' ); + + await page.keyboard.press( 'Backspace' ); + await expect + .poll( editor.getBlocks, 'should select all blocks after copying' ) + .toEqual( [] ); + + await pageUtils.pressKeyWithModifier( 'primary', 'v' ); + await expect + .poll( editor.getBlocks, 'should copy and paste multiple blocks' ) + .toMatchObject( [ + { attributes: { content: 'first paragraph' } }, + { attributes: { content: 'second paragraph' } }, + ] ); + + await page.keyboard.type( '|' ); + await expect + .poll( + editor.getBlocks, + 'should place the caret at the end of last pasted paragraph' + ) + .toMatchObject( [ + { attributes: { content: 'first paragraph' } }, + { attributes: { content: 'second paragraph|' } }, + ] ); + + await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await pageUtils.pressKeyWithModifier( 'primary', 'v' ); + await page.keyboard.type( '|' ); + await expect + .poll( editor.getBlocks, 'should replace blocks' ) + .toMatchObject( [ + { attributes: { content: 'first paragraph' } }, + { attributes: { content: 'second paragraph|' } }, + ] ); + await page.keyboard.press( 'Backspace' ); + + await pageUtils.pressKeyTimes( 'ArrowLeft', 3 ); + await pageUtils.pressKeyWithModifier( 'primary', 'v' ); + await page.keyboard.type( '|' ); + await expect + .poll( editor.getBlocks, 'should paste mid-block' ) + .toMatchObject( [ + { attributes: { content: 'first paragraph' } }, + { attributes: { content: 'second paragr' } }, + { attributes: { content: 'first paragraph' } }, + { attributes: { content: 'second paragraph|' } }, + { attributes: { content: 'aph' } }, + ] ); + } ); + + test( 'should return original focus after failed multi selection attempt', async ( { + page, + editor, + } ) => { + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: '1' }, + } ); + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: '2' }, + } ); + + const [ paragraph1, paragraph2 ] = await editor.canvas + .getByRole( 'document', { name: 'Paragraph block' } ) + .all(); + const { height } = await paragraph2.boundingBox(); + + // Move caret to the start of the second block. + await paragraph2.click( { position: { x: 0, y: height / 2 } } ); + await page.mouse.down(); + await paragraph1.hover( { + position: { x: -5, y: height / 2 }, + // Use force since it's outside the bounding box of the element. + force: true, + } ); + await page.mouse.up(); + + // Only "1" should be deleted. + await page.keyboard.press( 'Backspace' ); + + await expect.poll( editor.getBlocks ).toMatchObject( [ + { + name: 'core/paragraph', + attributes: { content: '2' }, + }, + ] ); + } ); + + test( 'should preserve dragged selection on move', async ( { + page, + editor, + multiBlockSelectionUtils, + } ) => { + for ( let i = 1; i <= 3; i += 1 ) { + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: `${ i }` }, + } ); + } + + const [ , paragraph2, paragraph3 ] = await editor.canvas + .getByRole( 'document', { name: 'Paragraph block' } ) + .all(); + + // Focus the last paragraph block and hide the block toolbar. + await paragraph3.click(); + await page.keyboard.press( 'ArrowRight' ); + + await paragraph3.hover(); + await page.mouse.down(); + await paragraph2.hover(); + await page.mouse.up(); + + await expect( multiBlockSelectionUtils.assertNativeSelection ).toPass(); + await expect + .poll( multiBlockSelectionUtils.getSelectedFlatIndices ) + .toEqual( [ 2, 3 ] ); + + await editor.showBlockToolbar(); + await page + .getByRole( 'toolbar', { name: 'Block tools' } ) + .getByRole( 'button', { name: 'Move up' } ) + .click(); + + await expect( multiBlockSelectionUtils.assertNativeSelection ).toPass(); + await expect + .poll( multiBlockSelectionUtils.getSelectedFlatIndices ) + .toEqual( [ 1, 2 ] ); + + await expect.poll( editor.getBlocks ).toMatchObject( [ + { name: 'core/paragraph', attributes: { content: '2' } }, + { name: 'core/paragraph', attributes: { content: '3' } }, + { name: 'core/paragraph', attributes: { content: '1' } }, + ] ); + } ); + + test( 'should clear selection when clicking next to blocks', async ( { + page, + editor, + multiBlockSelectionUtils, + } ) => { + await editor.canvas + .getByRole( 'button', { + name: 'Add default block', + } ) + .click(); + await page.keyboard.type( '1' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( '2' ); + await page.keyboard.press( 'Shift+ArrowUp' ); + await expect( multiBlockSelectionUtils.assertNativeSelection ).toPass(); + await expect + .poll( multiBlockSelectionUtils.getSelectedFlatIndices ) + .toEqual( [ 1, 2 ] ); + + const paragraph1 = editor.canvas + .getByRole( 'document', { + name: 'Paragraph block', + } ) + .filter( { hasText: '1' } ); + await paragraph1.click( { + position: { x: -1, y: 0 }, + // Use force since it's outside the bounding box of the element. + force: true, + } ); + + await expect + .poll( () => + page.evaluate( () => window.getSelection().rangeCount ) + ) + .toBe( 0 ); + await expect + .poll( multiBlockSelectionUtils.getSelectedFlatIndices ) + .toEqual( [] ); + + await expect.poll( editor.getBlocks ).toMatchObject( [ + { name: 'core/paragraph', attributes: { content: '1' } }, + { name: 'core/paragraph', attributes: { content: '2' } }, + ] ); + } ); + + test( 'should set attributes for multiple paragraphs', async ( { + page, + editor, + pageUtils, + } ) => { + for ( let i = 1; i <= 2; i += 1 ) { + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: `${ i }` }, + } ); + } + await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + + await page + .getByRole( 'toolbar', { name: 'Block tools' } ) + .getByRole( 'button', { name: 'Align text' } ) + .click(); + await page + .getByRole( 'menu', { name: 'Align text' } ) + .getByRole( 'menuitemradio', { name: 'Align text center' } ) + .click(); + + await expect + .poll( editor.getBlocks ) + .toMatchObject( [ + { attributes: { align: 'center', content: '1' } }, + { attributes: { align: 'center', content: '2' } }, + ] ); + } ); + + // Previously we would unexpectedly duplicate the block on Enter. + test( 'should not multi select single block', async ( { + page, + editor, + pageUtils, + } ) => { + await editor.canvas + .getByRole( 'button', { name: 'Add default block' } ) + .click(); + await page.keyboard.type( '1' ); + + await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await page.keyboard.press( 'Enter' ); + + await expect.poll( editor.getBlocks ).toMatchObject( [ + { name: 'core/paragraph', attributes: { content: '' } }, + { name: 'core/paragraph', attributes: { content: '' } }, + ] ); + } ); + + test( 'should gradually multi-select', async ( { + page, + editor, + pageUtils, + multiBlockSelectionUtils, + } ) => { + await editor.insertBlock( { + name: 'core/columns', + innerBlocks: [ + { + name: 'core/column', + innerBlocks: [ + { + name: 'core/paragraph', + attributes: { content: '1' }, + }, + { + name: 'core/paragraph', + attributes: { content: '2' }, + }, + ], + }, + { name: 'core/column' }, + ], + } ); + // Focus the last paragraph block. + await editor.canvas + .getByRole( 'document', { name: 'Paragraph block' } ) + .nth( 1 ) + .click(); + + await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + + await expect + .poll( multiBlockSelectionUtils.getSelectedBlocks ) + .toMatchObject( [ + { name: 'core/paragraph' }, + { name: 'core/paragraph' }, + ] ); + + await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + + await expect + .poll( multiBlockSelectionUtils.getSelectedBlocks ) + .toMatchObject( [ { name: 'core/column' } ] ); + + await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + + await expect + .poll( multiBlockSelectionUtils.getSelectedBlocks ) + .toMatchObject( [ + { name: 'core/column' }, + { name: 'core/column' }, + ] ); + + await page.keyboard.press( 'Backspace' ); + + // Expect both columns to be deleted. + await expect + .poll( editor.getBlocks ) + .toMatchObject( [ { name: 'core/columns', innerBlocks: [] } ] ); + } ); + + test( 'should multi-select from within the list block', async ( { + editor, + pageUtils, + multiBlockSelectionUtils, + } ) => { + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: '1' }, + } ); + await editor.insertBlock( { + name: 'core/list', + innerBlocks: [ + { + name: 'core/list-item', + attributes: { content: '1' }, + }, + ], + } ); + // Focus on the list item. + await editor.canvas + .getByRole( 'document', { name: 'Block: List item' } ) + .getByRole( 'textbox' ) + .click(); + + await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await expect + .poll( multiBlockSelectionUtils.getSelectedBlocks ) + .toMatchObject( [ { name: 'core/list-item' } ] ); + + await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await expect + .poll( multiBlockSelectionUtils.getSelectedBlocks ) + .toMatchObject( [ { name: 'core/list' } ] ); + + await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await expect + .poll( multiBlockSelectionUtils.getSelectedBlocks ) + .toMatchObject( [ + { name: 'core/paragraph' }, + { name: 'core/list' }, + ] ); + } ); + + test( 'should select all from empty selection', async ( { + page, + editor, + pageUtils, + multiBlockSelectionUtils, + } ) => { + for ( let i = 1; i <= 2; i += 1 ) { + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: `${ i }` }, + } ); + } + + // Clear the selected block. + await page.keyboard.press( 'Escape' ); + await page.keyboard.press( 'Escape' ); + + await expect + .poll( multiBlockSelectionUtils.getSelectedBlocks ) + .toEqual( [] ); + + await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + + await page.keyboard.press( 'Backspace' ); + + // Expect both paragraphs to be deleted. + await expect.poll( editor.getBlocks ).toEqual( [] ); + } ); + + test( 'should select title if the cursor is on title', async ( { + page, + editor, + pageUtils, + multiBlockSelectionUtils, + } ) => { + for ( let i = 1; i <= 2; i += 1 ) { + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: `${ i }` }, + } ); + } + + await editor.canvas + .getByRole( 'textbox', { name: 'Add title' } ) + .type( 'Post title' ); + + await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + + await expect + .poll( multiBlockSelectionUtils.getSelectedBlocks ) + .toEqual( [] ); + await expect + .poll( () => + page.evaluate( () => window.getSelection().toString() ) + ) + .toBe( 'Post title' ); + } ); + + test( 'should multi-select in the ListView component with shift + click', async ( { + page, + editor, + multiBlockSelectionUtils, + pageUtils, + } ) => { + for ( let i = 1; i <= 4; i += 1 ) { + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: `${ i }` }, + } ); + } + + await page + .getByRole( 'toolbar', { name: 'Document tools' } ) + .getByRole( 'button', { name: 'Document Overview' } ) + .click(); + + const listView = page.getByRole( 'treegrid', { + name: 'Block navigation structure', + } ); + const navButtons = listView.getByRole( 'gridcell', { + name: 'Paragraph link', + } ); + + await navButtons.nth( 1 ).click(); + await expect + .poll( + multiBlockSelectionUtils.getSelectedFlatIndices, + 'Clicking on the second list item should result in the second block being selected.' + ) + .toEqual( [ 2 ] ); + + await navButtons.nth( 3 ).click( { modifiers: [ 'Shift' ] } ); + await expect + .poll( + multiBlockSelectionUtils.getSelectedFlatIndices, + 'Shift clicking the fourth list item should result in blocks 2 through 4 being selected.' + ) + .toEqual( [ 2, 3, 4 ] ); + + await navButtons.nth( 0 ).click( { modifiers: [ 'Shift' ] } ); + await expect + .poll( + multiBlockSelectionUtils.getSelectedFlatIndices, + 'With the shift key still held down, clicking the first block should result in the first two blocks being selected.' + ) + .toEqual( [ 1, 2 ] ); + + await navButtons.nth( 3 ).click(); + await expect + .poll( + multiBlockSelectionUtils.getSelectedFlatIndices, + 'With the shift key up, clicking the fourth block should result in only that block being selected.' + ) + .toEqual( [ 4 ] ); + + // Move focus to the list view link to prepare for the keyboard navigation. + await navButtons.nth( 3 ).click(); + await expect( + navButtons.nth( 3 ).getByRole( 'link', { includeHidden: true } ) + ).toBeFocused(); + // Press Up twice to highlight the second block. + await pageUtils.pressKeyTimes( 'ArrowUp', 2 ); + + await page.keyboard.press( 'Shift+ArrowDown' ); + await expect + .poll( + multiBlockSelectionUtils.getSelectedFlatIndices, + 'Shift + press Down to select the 2nd and 3rd blocks.' + ) + .toEqual( [ 2, 3 ] ); + + await page.keyboard.press( 'Shift+ArrowDown' ); + await expect + .poll( + multiBlockSelectionUtils.getSelectedFlatIndices, + 'Press Down once more to also select the 4th block.' + ) + .toEqual( [ 2, 3, 4 ] ); + + await pageUtils.pressKeyTimes( 'Shift+ArrowUp', 3 ); + await expect + .poll( + multiBlockSelectionUtils.getSelectedFlatIndices, + 'Press Up three times to adjust the selection to only include the first two blocks.' + ) + .toEqual( [ 1, 2 ] ); + + // Navigate to the bottom of the list of blocks. + await pageUtils.pressKeyTimes( 'ArrowDown', 3 ); + + // This tests that shift selecting blocks by keyboard that are not adjacent + // to an existing selection resets the selection. + await page.keyboard.press( 'Shift+ArrowUp' ); + await expect + .poll( + multiBlockSelectionUtils.getSelectedFlatIndices, + 'Shift + press UP to select the 3rd and 4th blocks.' + ) + .toEqual( [ 3, 4 ] ); + } ); + + test( 'should forward delete across blocks', async ( { + page, + editor, + pageUtils, + } ) => { + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: '1[' }, + } ); + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: '.' }, + } ); + await editor.insertBlock( { + name: 'core/heading', + attributes: { level: 2, content: ']2' }, + } ); + // Focus the heading block. + await editor.canvas + .getByRole( 'document', { name: 'Block: Heading' } ) + .click(); + + await page.keyboard.press( 'ArrowLeft' ); + // Select everything between []. + await pageUtils.pressKeyTimes( 'Shift+ArrowLeft', 5 ); + + await page.keyboard.press( 'Delete' ); + + // Ensure selection is in the correct place. + await page.keyboard.type( '|' ); + await expect.poll( editor.getBlocks ).toMatchObject( [ + { + name: 'core/heading', + attributes: { level: 2, content: '1|2' }, + }, + ] ); + } ); + + test( 'should write over selection', async ( { + page, + editor, + pageUtils, + } ) => { + await editor.canvas + .getByRole( 'button', { name: 'Add default block' } ) + .click(); + await page.keyboard.type( '1[' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( ']2' ); + await page.keyboard.press( 'ArrowLeft' ); + // Select everything between []. + await pageUtils.pressKeyTimes( 'Shift+ArrowLeft', 3 ); + + // Ensure selection is in the correct place. + await page.keyboard.type( '|' ); + await expect.poll( editor.getBlocks ).toMatchObject( [ + { + name: 'core/paragraph', + attributes: { content: '1|2' }, + }, + ] ); + } ); + + test( 'should handle Enter across blocks', async ( { + page, + editor, + pageUtils, + } ) => { + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: '1[' }, + } ); + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: '.' }, + } ); + await editor.insertBlock( { + name: 'core/heading', + attributes: { level: 2, content: ']2' }, + } ); + // Focus the heading block. + await editor.canvas + .getByRole( 'document', { name: 'Block: Heading' } ) + .click(); + await page.keyboard.press( 'ArrowLeft' ); + // Select everything between []. + await pageUtils.pressKeyTimes( 'Shift+ArrowLeft', 5 ); + + await page.keyboard.press( 'Enter' ); + + // Ensure selection is in the correct place. + await page.keyboard.type( '|' ); + await expect.poll( editor.getBlocks ).toMatchObject( [ + { + name: 'core/paragraph', + attributes: { content: '1' }, + }, + { + name: 'core/paragraph', + attributes: { content: '|' }, + }, + { + name: 'core/heading', + attributes: { level: 2, content: '2' }, + }, + ] ); + } ); + + test( 'should select separator (single element block)', async ( { + page, + editor, + pageUtils, + } ) => { + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: 'a' }, + } ); + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: '' }, + } ); + await editor.insertBlock( { name: 'core/separator' } ); + + // Focus and move the caret to the right of the first paragraph. + await editor.canvas + .getByRole( 'document', { name: 'Paragraph block' } ) + .filter( { hasText: 'a' } ) + .click(); + + await pageUtils.pressKeyTimes( 'Shift+ArrowDown', 2 ); + await page.keyboard.press( 'Backspace' ); + + // Ensure selection is in the correct place. + await page.keyboard.type( '|' ); + await expect.poll( editor.getBlocks ).toMatchObject( [ + { + name: 'core/paragraph', + attributes: { content: '|' }, + }, + ] ); + } ); + + test( 'should partially select with shift + click', async ( { + page, + editor, + } ) => { + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: '1[' }, + } ); + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: ']2' }, + } ); + // Focus and move the caret to the end. + await editor.canvas + .getByRole( 'document', { name: 'Paragraph block' } ) + .filter( { hasText: ']2' } ) + .click(); + + await page.keyboard.press( 'ArrowLeft' ); + const strongText = editor.canvas + .getByRole( 'region', { name: 'Editor content' } ) + .getByText( '1', { exact: true } ); + const strongBox = await strongText.boundingBox(); + await strongText.click( { + // Ensure clicking on the right half of the element. + position: { x: strongBox.width, y: strongBox.height / 2 }, + modifiers: [ 'Shift' ], + } ); + await page.keyboard.press( 'Backspace' ); + + // Ensure selection is in the correct place. + await page.keyboard.type( '|' ); + await expect.poll( editor.getBlocks ).toMatchObject( [ + { + name: 'core/paragraph', + attributes: { content: '1|2' }, + }, + ] ); + } ); + + test.describe( 'shift+click multi-selection', () => { + test( 'should multi-select block with text selection and a block without text selection', async ( { + page, + editor, + multiBlockSelectionUtils, + } ) => { + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: '' }, + } ); + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: 'hi' }, + } ); + await editor.insertBlock( { name: 'core/spacer' } ); + await page.keyboard.press( 'ArrowUp' ); + + await editor.canvas + .getByRole( 'document', { name: 'Block: Spacer' } ) + .click( { modifiers: [ 'Shift' ] } ); + + await expect + .poll( multiBlockSelectionUtils.getSelectedBlocks ) + .toMatchObject( [ + { name: 'core/paragraph', attributes: { content: 'hi' } }, + { name: 'core/spacer' }, + ] ); + } ); + + test( 'should multi-select blocks without text selection', async ( { + editor, + multiBlockSelectionUtils, + } ) => { + await editor.insertBlock( { name: 'core/spacer' } ); + await editor.insertBlock( { name: 'core/spacer' } ); + + const spacerBlocks = editor.canvas.getByRole( 'document', { + name: 'Block: Spacer', + } ); + + await spacerBlocks.nth( 1 ).click(); + await spacerBlocks.nth( 0 ).click( { modifiers: [ 'Shift' ] } ); + + await expect + .poll( multiBlockSelectionUtils.getSelectedBlocks ) + .toMatchObject( [ + { name: 'core/spacer' }, + { name: 'core/spacer' }, + ] ); + } ); + } ); + + test( 'should select by dragging into separator', async ( { + page, + editor, + multiBlockSelectionUtils, + } ) => { + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: '1' }, + } ); + await editor.insertBlock( { name: 'core/separator' } ); + // Hide the block toolbar. + await page.keyboard.press( 'ArrowUp' ); + + await editor.canvas + .getByRole( 'document', { name: 'Paragraph block' } ) + .hover(); + await page.mouse.down(); + await editor.canvas + .getByRole( 'document', { name: 'Block: Separator' } ) + .hover(); + await page.mouse.up(); + + await expect + .poll( multiBlockSelectionUtils.getSelectedFlatIndices ) + .toEqual( [ 1, 2 ] ); + } ); +} ); + +class MultiBlockSelectionUtils { + /** @type {Page} */ + #page; + /** @type {Editor} */ + #editor; + + constructor( { page, editor } ) { + this.#page = page; + this.#editor = editor; + } + + getSelectedBlocks = async () => { + return await this.#page.evaluate( () => { + const { + hasMultiSelection, + hasSelectedBlock, + getMultiSelectedBlocks, + getSelectedBlock, + } = window.wp.data.select( 'core/block-editor' ); + if ( hasMultiSelection() ) { + return getMultiSelectedBlocks(); + } else if ( hasSelectedBlock() ) { + return [ getSelectedBlock() ]; + } + return []; + } ); + }; + + getSelectedFlatIndices = async () => { + return await this.#page.evaluate( () => { + const { getSelectedBlockClientIds, getClientIdsWithDescendants } = + window.wp.data.select( 'core/block-editor' ); + const selectedClientIds = getSelectedBlockClientIds(); + const allClientIds = getClientIdsWithDescendants(); + return selectedClientIds.map( + ( clientId ) => allClientIds.indexOf( clientId ) + 1 + ); + } ); + }; + + /** + * Tests if the native selection matches the block selection. + */ + assertNativeSelection = async () => { + const selection = await this.#editor.canvas.evaluateHandle( () => + window.getSelection() + ); + + const { isMultiSelected, selectionStart, selectionEnd } = + await this.#page.evaluate( () => { + const { + hasMultiSelection, + getSelectionStart, + getSelectionEnd, + } = window.wp.data.select( 'core/block-editor' ); + return { + isMultiSelected: hasMultiSelection(), + selectionStart: getSelectionStart().clientId, + selectionEnd: getSelectionEnd().clientId, + }; + } ); + const startBlock = this.#editor.canvas.locator( + `[data-block="${ selectionStart }"]` + ); + const endBlock = this.#editor.canvas.locator( + `[data-block="${ selectionEnd }"]` + ); + + expect( + await selection.evaluate( ( _selection ) => _selection.rangeCount ), + 'Expected one range' + ).toBe( 1 ); + + const range = await selection.evaluateHandle( ( _selection ) => + _selection.getRangeAt( 0 ) + ); + const isCollapsed = await range.evaluate( + ( { collapsed } ) => collapsed + ); + + if ( isMultiSelected ) { + expect( isCollapsed, 'Expected an uncollapsed selection' ).toBe( + false + ); + + expect( + await startBlock.evaluate( + ( block, _selection ) => + _selection.containsNode( block, true ), + selection + ), + 'Expected selection to include in the first selected block' + ).toBe( true ); + expect( + await endBlock.evaluate( + ( block, _selection ) => + _selection.containsNode( block, true ), + selection + ), + 'Expected selection to include in the last selected block' + ).toBe( true ); + } else { + expect( isCollapsed, 'Expected a collapsed selection' ).toBe( + true + ); + + expect( + await startBlock.evaluate( + ( block, { startContainer, endContainer } ) => + block.contains( startContainer ) && + block.contains( endContainer ), + range + ), + 'Expected selection to start and end in the selected block' + ).toBe( true ); + } + }; +} From d75ef592adecf463f8908b7b9e922c4bc2b4edbc Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Fri, 3 Mar 2023 15:02:43 +0900 Subject: [PATCH 004/910] Columns Block: Don't show the column count change UI when templateLock is all (#48691) --- packages/block-library/src/columns/edit.js | 40 ++++++++++++++-------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/packages/block-library/src/columns/edit.js b/packages/block-library/src/columns/edit.js index dd30d0e400f018..3c99f73cce0f29 100644 --- a/packages/block-library/src/columns/edit.js +++ b/packages/block-library/src/columns/edit.js @@ -61,10 +61,13 @@ function ColumnsEditContainer( { } ) { const { isStackedOnMobile, verticalAlignment } = attributes; - const { count } = useSelect( + const { count, canInsertColumnBlock } = useSelect( ( select ) => { return { count: select( blockEditorStore ).getBlockCount( clientId ), + canInsertColumnBlock: select( + blockEditorStore + ).canInsertBlockType( 'core/column', clientId ), }; }, [ clientId ] @@ -94,20 +97,29 @@ function ColumnsEditContainer( { - updateColumns( count, value ) } - min={ 1 } - max={ Math.max( 6, count ) } - /> - { count > 6 && ( - - { __( - 'This column count exceeds the recommended amount and may cause visual breakage.' + { canInsertColumnBlock && ( + <> + + updateColumns( count, value ) + } + min={ 1 } + max={ Math.max( 6, count ) } + /> + { count > 6 && ( + + { __( + 'This column count exceeds the recommended amount and may cause visual breakage.' + ) } + ) } - + ) } Date: Fri, 3 Mar 2023 15:20:06 +0700 Subject: [PATCH 005/910] Welcome Guide link points to a redirected help article (#48582) * Link points to a redirected help article The link title Here's a detailed guide opens in a new tab to a help article which is a redirected link. We should point directly to the correct article. My PR addresses this small change and points to https://wordpress.org/documentation/article/wordpress-block-editor/. * Update Redirected link * Updated redirected link * Updated redirect link * Reverting changes which were incorrect Reverting this incorrect link edit. This does require changing, but will be addressed in a different PR --- .../customize-widgets/src/components/welcome-guide/index.js | 2 +- packages/edit-post/src/components/welcome-guide/default.js | 2 +- packages/edit-widgets/src/components/welcome-guide/index.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/customize-widgets/src/components/welcome-guide/index.js b/packages/customize-widgets/src/components/welcome-guide/index.js index 28826cae2cc722..52807118baec7c 100644 --- a/packages/customize-widgets/src/components/welcome-guide/index.js +++ b/packages/customize-widgets/src/components/welcome-guide/index.js @@ -70,7 +70,7 @@ export default function WelcomeGuide( { sidebar } ) {
{ __( "Here's a detailed guide." ) } diff --git a/packages/edit-post/src/components/welcome-guide/default.js b/packages/edit-post/src/components/welcome-guide/default.js index d83eab0fa13ddb..c4723c65a33df2 100644 --- a/packages/edit-post/src/components/welcome-guide/default.js +++ b/packages/edit-post/src/components/welcome-guide/default.js @@ -110,7 +110,7 @@ export default function WelcomeGuideDefault() { ) } { __( "Here's a detailed guide." ) } diff --git a/packages/edit-widgets/src/components/welcome-guide/index.js b/packages/edit-widgets/src/components/welcome-guide/index.js index 48ffecf6c8c3c9..51244956286d8d 100644 --- a/packages/edit-widgets/src/components/welcome-guide/index.js +++ b/packages/edit-widgets/src/components/welcome-guide/index.js @@ -177,7 +177,7 @@ export default function WelcomeGuide() { ) } { __( "Here's a detailed guide." ) } From cae03de5cd092de93c105a0fc3711882d1587f7f Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Fri, 3 Mar 2023 22:48:29 +1300 Subject: [PATCH 006/910] Site editor: Add hover animation to site editor canvas (#48575) --- .../src/components/block-editor/editor-canvas.js | 3 ++- packages/edit-site/src/components/layout/index.js | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/edit-site/src/components/block-editor/editor-canvas.js b/packages/edit-site/src/components/block-editor/editor-canvas.js index 4a4e84939cd845..4489b78ef520f8 100644 --- a/packages/edit-site/src/components/block-editor/editor-canvas.js +++ b/packages/edit-site/src/components/block-editor/editor-canvas.js @@ -44,7 +44,8 @@ function EditorCanvas( { enableResizing, settings, children, ...props } ) { // Forming a "block formatting context" to prevent margin collapsing. // @see https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Block_formatting_context `.is-root-container { display: flow-root; } - body { position: relative; }` + body { position: relative; + ${ canvasMode === 'view' ? 'cursor: pointer;' : '' }}}` } { enableResizing && ( + + } + name="style-book-canvas" + tabIndex={ 0 } + > + { /* Filters need to be rendered before children to avoid Safari rendering issues. */ } + { settings.svgFilters } + 600, + } + ) } + examples={ examples } + category={ tab.name } + label={ sprintf( + // translators: %s: Category of blocks, e.g. Text. + __( + 'Examples of blocks in the %s category' + ), + tab.title + ) } + isSelected={ isSelected } + onSelect={ onSelect } + /> + ) } @@ -169,52 +293,83 @@ function StyleBook( { isSelected, onSelect, onClose } ) { ); } -const Examples = memo( ( { examples, category, isSelected, onSelect } ) => ( -
- { examples - .filter( ( example ) => example.category === category ) - .map( ( example ) => ( - { - onSelect( example.name ); - } } - /> - ) ) } -
-) ); - -const Example = memo( ( { title, blocks, isSelected, onClick } ) => ( - -) ); +const Examples = memo( + ( { className, examples, category, label, isSelected, onSelect } ) => { + const composite = useCompositeState( { orientation: 'vertical' } ); + return ( + + { examples + .filter( ( example ) => example.category === category ) + .map( ( example ) => ( + { + onSelect( example.name ); + } } + /> + ) ) } + + ); + } +); + +const Example = ( { composite, id, title, blocks, isSelected, onClick } ) => { + const originalSettings = useSelect( + ( select ) => select( blockEditorStore ).getSettings(), + [] + ); + const settings = useMemo( + () => ( { ...originalSettings, __unstableIsPreviewMode: true } ), + [ originalSettings ] + ); + + // Cache the list of blocks to avoid additional processing when the component is re-rendered. + const renderedBlocks = useMemo( + () => ( Array.isArray( blocks ) ? blocks : [ blocks ] ), + [ blocks ] + ); + + return ( + + + { title } + +
+ + + + + +
+
+ ); +}; function useHasStyleBook() { const fills = useSlotFills( SLOT_FILL_NAME ); diff --git a/packages/edit-site/src/components/style-book/style.scss b/packages/edit-site/src/components/style-book/style.scss index fc88c399cac207..881b117a75ccb3 100644 --- a/packages/edit-site/src/components/style-book/style.scss +++ b/packages/edit-site/src/components/style-book/style.scss @@ -26,53 +26,9 @@ bottom: 0; left: 0; overflow: auto; - padding: $grid-unit-40; + padding: 0; position: absolute; right: 0; top: $grid-unit-60; // Height of tabs. } } - -.edit-site-style-book__examples { - max-width: 900px; - margin: 0 auto; -} - -.edit-site-style-book__example { - background: none; - border-radius: $radius-block-ui; - border: none; - color: inherit; - cursor: pointer; - display: flex; - flex-direction: column; - gap: $grid-unit-50; - margin-bottom: $grid-unit-50; - padding: $grid-unit-20; - width: 100%; - - &.is-selected { - box-shadow: 0 0 0 1px var(--wp-admin-theme-color); - } - - .edit-site-style-book.is-wide & { - flex-direction: row; - } -} - -.edit-site-style-book__example-title { - font-size: 11px; - font-weight: 500; - margin: 0; - text-align: left; - text-transform: uppercase; - - .edit-site-style-book.is-wide & { - text-align: right; - width: 120px; - } -} - -.edit-site-style-book__example-preview { - width: 100%; -} diff --git a/test/e2e/specs/site-editor/style-book.spec.js b/test/e2e/specs/site-editor/style-book.spec.js index f3c06308b92154..56d0ca0cf20f1b 100644 --- a/test/e2e/specs/site-editor/style-book.spec.js +++ b/test/e2e/specs/site-editor/style-book.spec.js @@ -59,37 +59,45 @@ test.describe( 'Style Book', () => { ).toBeVisible(); await expect( page.locator( 'role=tab[name="Theme"i]' ) ).toBeVisible(); + // Buttons to select block examples are rendered within the Style Book iframe. + const styleBookIframe = page.frameLocator( + '[name="style-book-canvas"]' + ); + await expect( - page.locator( - 'role=button[name="Open Headings styles in Styles panel"i]' - ) + styleBookIframe.getByRole( 'button', { + name: 'Open Headings styles in Styles panel', + } ) ).toBeVisible(); await expect( - page.locator( - 'role=button[name="Open Paragraph styles in Styles panel"i]' - ) + styleBookIframe.getByRole( 'button', { + name: 'Open Paragraph styles in Styles panel', + } ) ).toBeVisible(); await page.click( 'role=tab[name="Media"i]' ); await expect( - page.locator( - 'role=button[name="Open Image styles in Styles panel"i]' - ) + styleBookIframe.getByRole( 'button', { + name: 'Open Image styles in Styles panel', + } ) ).toBeVisible(); await expect( - page.locator( - 'role=button[name="Open Gallery styles in Styles panel"i]' - ) + styleBookIframe.getByRole( 'button', { + name: 'Open Gallery styles in Styles panel', + } ) ).toBeVisible(); } ); test( 'should open correct Global Styles panel when example is clicked', async ( { page, } ) => { - await page.click( - 'role=button[name="Open Headings styles in Styles panel"i]' - ); + await page + .frameLocator( '[name="style-book-canvas"]' ) + .getByRole( 'button', { + name: 'Open Headings styles in Styles panel', + } ) + .click(); await expect( page.locator( @@ -105,9 +113,12 @@ test.describe( 'Style Book', () => { await page.click( 'role=button[name="Heading block styles"]' ); await page.click( 'role=button[name="Typography styles"]' ); - await page.click( - 'role=button[name="Open Quote styles in Styles panel"i]' - ); + await page + .frameLocator( '[name="style-book-canvas"]' ) + .getByRole( 'button', { + name: 'Open Quote styles in Styles panel', + } ) + .click(); await page.click( 'role=button[name="Navigate to the previous view"]' ); await page.click( 'role=button[name="Navigate to the previous view"]' ); From 321bea29290cd438e5b87cc97f9e388ac0f83ea3 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 8 Mar 2023 16:27:34 +0800 Subject: [PATCH 063/910] Fix navigation block off-canvas appender for empty menus (#48907) - Pass the parent client id in as a prop instead of trying to determine the id from inner blocks --- .../src/components/off-canvas-editor/appender.js | 2 +- .../src/components/off-canvas-editor/index.js | 11 ++++------- .../src/navigation/edit/menu-inspector-controls.js | 1 + 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/block-editor/src/components/off-canvas-editor/appender.js b/packages/block-editor/src/components/off-canvas-editor/appender.js index 36f52c6aa1a761..88624be2ef304a 100644 --- a/packages/block-editor/src/components/off-canvas-editor/appender.js +++ b/packages/block-editor/src/components/off-canvas-editor/appender.js @@ -102,7 +102,7 @@ export const Appender = forwardRef( ref={ ref } rootClientId={ clientId } position="bottom right" - isAppender={ true } + isAppender selectBlockOnInsert={ false } shouldDirectInsert={ false } __experimentalIsQuick diff --git a/packages/block-editor/src/components/off-canvas-editor/index.js b/packages/block-editor/src/components/off-canvas-editor/index.js index 0ec7eeef57d0cc..b0f18c56176967 100644 --- a/packages/block-editor/src/components/off-canvas-editor/index.js +++ b/packages/block-editor/src/components/off-canvas-editor/index.js @@ -56,6 +56,7 @@ export const BLOCK_LIST_ITEM_HEIGHT = 36; * * @param {Object} props Components props. * @param {string} props.id An HTML element id for the root element of ListView. + * @param {string} props.parentClientId The client id of the parent block. * @param {Array} props.blocks Custom subset of block client IDs to be used instead of the default hierarchy. * @param {boolean} props.showBlockMovers Flag to enable block movers * @param {boolean} props.isExpanded Flag to determine whether nested levels are expanded by default. @@ -68,6 +69,7 @@ export const BLOCK_LIST_ITEM_HEIGHT = 36; function OffCanvasEditor( { id, + parentClientId, blocks, showBlockMovers = false, isExpanded = false, @@ -82,10 +84,9 @@ function OffCanvasEditor( const { clientIdsTree, draggedClientIds, selectedClientIds } = useListViewClientIds( blocks ); - const { visibleBlockCount, shouldShowInnerBlocks, parentId } = useSelect( + const { visibleBlockCount, shouldShowInnerBlocks } = useSelect( ( select ) => { const { - getBlockRootClientId, getGlobalBlockCount, getClientIdsOfDescendants, __unstableGetEditorMode, @@ -97,10 +98,6 @@ function OffCanvasEditor( return { visibleBlockCount: getGlobalBlockCount() - draggedBlockCount, shouldShowInnerBlocks: __unstableGetEditorMode() !== 'zoom-out', - parentId: - blocks.length > 0 - ? getBlockRootClientId( blocks[ 0 ].clientId ) - : undefined, }; }, [ draggedClientIds, blocks ] @@ -234,7 +231,7 @@ function OffCanvasEditor( > Date: Wed, 8 Mar 2023 12:34:28 +0400 Subject: [PATCH 064/910] Site Editor: Improve the Navigation panel's menu query (#48908) * Site Editor: Improve the Navigation panel's menu query * Update hasNavigationMenus query * Add comment about queries --- .../sidebar-navigation-screen-main/index.js | 23 ++++++++++++------- .../index.js | 19 ++++++--------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-main/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-main/index.js index 26b01999262d38..fa7aa217134dcf 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-main/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-main/index.js @@ -17,14 +17,21 @@ import SidebarNavigationScreen from '../sidebar-navigation-screen'; import SidebarNavigationItem from '../sidebar-navigation-item'; export default function SidebarNavigationScreenMain() { - const { navigationMenus } = useSelect( ( select ) => { - const { getEntityRecords } = select( coreStore ); - return { - navigationMenus: getEntityRecords( 'postType', 'wp_navigation', { - per_page: -1, + const hasNavigationMenus = useSelect( ( select ) => { + // The query needs to be the same as in the "SidebarNavigationScreenNavigationMenus" component, + // to avoid double network calls. + const navigationMenus = select( coreStore ).getEntityRecords( + 'postType', + 'wp_navigation', + { + per_page: 1, status: 'publish', - } ), - }; + order: 'desc', + orderby: 'date', + } + ); + + return navigationMenus?.length > 0; } ); return ( @@ -36,7 +43,7 @@ export default function SidebarNavigationScreenMain() { ) } content={ - { !! navigationMenus && navigationMenus.length > 0 && ( + { hasNavigationMenus && ( {}; -const NAVIGATION_MENUS_QUERY = { per_page: -1, status: 'publish' }; +const NAVIGATION_MENUS_QUERY = { + per_page: 1, + status: 'publish', + order: 'desc', + orderby: 'date', +}; function SidebarNavigationScreenWrapper( { children, actions } ) { return ( @@ -66,17 +71,7 @@ export default function SidebarNavigationScreenNavigationMenus() { }; }, [] ); - // Sort navigation menus by date. - const orderedNavigationMenus = useMemo( - () => - navigationMenus?.sort( ( menuA, menuB ) => { - const menuADate = new Date( menuA.date ); - const menuBDate = new Date( menuB.date ); - return menuADate.getTime() > menuBDate.getTime(); - } ), - [ navigationMenus ] - ); - const firstNavigationMenu = orderedNavigationMenus?.[ 0 ]?.id; + const firstNavigationMenu = navigationMenus?.[ 0 ]?.id; const blocks = useMemo( () => { return [ createBlock( 'core/navigation', { ref: firstNavigationMenu } ), From ee4a3ad99a191dabcc175ba8dd60344be1bc46d9 Mon Sep 17 00:00:00 2001 From: Marin Atanasov <8436925+tyxla@users.noreply.github.com> Date: Wed, 8 Mar 2023 10:55:19 +0200 Subject: [PATCH 065/910] Lodash: Remove from e2e-tests package (#48775) --- package-lock.json | 1 - .../e2e-tests/config/setup-test-framework.js | 7 +----- packages/e2e-tests/package.json | 1 - .../specs/widgets/editing-widgets.test.js | 22 ++++++++++++------- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5f314106697431..6e2b0b37b72649 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17721,7 +17721,6 @@ "filenamify": "^4.2.0", "jest-message-util": "^27.4.2", "jest-snapshot": "^27.4.5", - "lodash": "^4.17.21", "puppeteer-testing-library": "^0.5.0", "uuid": "^8.3.0" } diff --git a/packages/e2e-tests/config/setup-test-framework.js b/packages/e2e-tests/config/setup-test-framework.js index ed1d92f67bf197..6f5a264ad4da63 100644 --- a/packages/e2e-tests/config/setup-test-framework.js +++ b/packages/e2e-tests/config/setup-test-framework.js @@ -1,7 +1,6 @@ /** * External dependencies */ -import { get } from 'lodash'; import { toMatchInlineSnapshot, toMatchSnapshot } from 'jest-snapshot'; /** @@ -172,11 +171,7 @@ function observeConsoleLogging() { // correctly. Instead, the logic here synchronously inspects the // internal object shape of the JSHandle to find the error text. If it // cannot be found, the default text value is used instead. - text = get( - message.args(), - [ 0, '_remoteObject', 'description' ], - text - ); + text = message.args()[ 0 ]?._remoteObject?.description ?? text; // Disable reason: We intentionally bubble up the console message // which, unless the test explicitly anticipates the logging via diff --git a/packages/e2e-tests/package.json b/packages/e2e-tests/package.json index 4b77e18f5ddd3c..57304f14e02a57 100644 --- a/packages/e2e-tests/package.json +++ b/packages/e2e-tests/package.json @@ -33,7 +33,6 @@ "filenamify": "^4.2.0", "jest-message-util": "^27.4.2", "jest-snapshot": "^27.4.5", - "lodash": "^4.17.21", "puppeteer-testing-library": "^0.5.0", "uuid": "^8.3.0" }, diff --git a/packages/e2e-tests/specs/widgets/editing-widgets.test.js b/packages/e2e-tests/specs/widgets/editing-widgets.test.js index 00d34b4ae108d7..2d77e297aaeabc 100644 --- a/packages/e2e-tests/specs/widgets/editing-widgets.test.js +++ b/packages/e2e-tests/specs/widgets/editing-widgets.test.js @@ -24,7 +24,6 @@ import { */ // eslint-disable-next-line no-restricted-imports import { find, findAll } from 'puppeteer-testing-library'; -import { groupBy, mapValues } from 'lodash'; describe( 'Widgets screen', () => { beforeEach( async () => { @@ -945,13 +944,20 @@ async function saveWidgets() { async function getSerializedWidgetAreas() { const widgets = await rest( { path: '/wp/v2/widgets' } ); - const serializedWidgetAreas = mapValues( - groupBy( widgets, 'sidebar' ), - ( sidebarWidgets ) => - sidebarWidgets - .map( ( widget ) => widget.rendered ) - .filter( Boolean ) - .join( '\n' ) + const serializedWidgetAreas = widgets.reduce( + ( acc, { sidebar, rendered } ) => { + const currentWidgets = acc[ sidebar ] || ''; + let newWidgets = Boolean( rendered ) ? rendered : ''; + if ( currentWidgets.length && newWidgets.length ) { + newWidgets = '\n' + newWidgets; + } + + return { + ...acc, + [ sidebar ]: currentWidgets + newWidgets, + }; + }, + {} ); return serializedWidgetAreas; From 9413f79fef584614e60c6196a9e981a2e174ace1 Mon Sep 17 00:00:00 2001 From: Marin Atanasov <8436925+tyxla@users.noreply.github.com> Date: Wed, 8 Mar 2023 11:16:38 +0200 Subject: [PATCH 066/910] Editor: Simplify native editor help slugs (#48802) --- .../components/editor-help/index.native.js | 47 +++++++++---------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/packages/editor/src/components/editor-help/index.native.js b/packages/editor/src/components/editor-help/index.native.js index 306bfd229aed82..fb802ba7560cde 100644 --- a/packages/editor/src/components/editor-help/index.native.js +++ b/packages/editor/src/components/editor-help/index.native.js @@ -1,7 +1,6 @@ /** * External dependencies */ -import { paramCase } from 'change-case'; import { SafeAreaView, ScrollView, StyleSheet, View } from 'react-native'; import { TransitionPresets } from '@react-navigation/stack'; @@ -40,35 +39,36 @@ import HelpSectionTitle from './help-section-title'; const HELP_TOPICS = [ { label: __( 'What is a block?' ), + slug: 'what-is-a-block', icon: helpFilled, view: , }, { label: __( 'Add blocks' ), + slug: 'add-blocks', icon: plusCircleFilled, view: , }, - { label: __( 'Move blocks' ), icon: moveBlocksIcon, view: }, - { label: __( 'Remove blocks' ), icon: trash, view: }, + { + label: __( 'Move blocks' ), + slug: 'move-blocks', + icon: moveBlocksIcon, + view: , + }, + { + label: __( 'Remove blocks' ), + slug: 'remove-blocks', + icon: trash, + view: , + }, { label: __( 'Customize blocks' ), + slug: 'customize-blocks', icon: cog, view: , }, ]; -const kebabCaseSettings = { - splitRegexp: [ - /([\p{Ll}\p{Lo}\p{N}])([\p{Lu}\p{Lt}])/gu, // One lowercase or digit, followed by one uppercase. - /([\p{Lu}\p{Lt}])([\p{Lu}\p{Lt}][\p{Ll}\p{Lo}])/gu, // One uppercase followed by one uppercase and one lowercase. - ], - stripRegexp: /(\p{C}|\p{P}|\p{S})+/giu, // Anything that's not a punctuation, symbol or control/format character. -}; - -function kebabCase( string ) { - return paramCase( string, kebabCaseSettings ); -} - function EditorHelpTopics( { close, isVisible, onClose, showSupport } ) { const { postType } = useSelect( ( select ) => ( { postType: select( editorStore ).getEditedPostAttribute( 'type' ), @@ -153,24 +153,20 @@ function EditorHelpTopics( { close, isVisible, onClose, showSupport } ) { { /* Print out help topics. */ } { HELP_TOPICS.map( ( - { label, icon }, + { label, icon, slug }, index ) => { - const labelSlug = - kebabCase( label ); const isLastItem = index === HELP_TOPICS.length - 1; return ( { /* Print out help detail screens. */ } - { HELP_TOPICS.map( ( { view, label } ) => { - const labelSlug = kebabCase( label ); + { HELP_TOPICS.map( ( { view, label, slug } ) => { return ( Date: Wed, 8 Mar 2023 11:17:54 +0200 Subject: [PATCH 067/910] Lodash: Refactor away from edit-post package (#48786) --- package-lock.json | 1 - packages/edit-post/package.json | 1 - .../header/post-publish-button-or-toggle.js | 14 ++++--------- .../template-title/edit-template-title.js | 20 ++++++++----------- .../header/tools-more-menu-group/index.js | 7 +------ .../src/components/preferences-modal/index.js | 10 +--------- .../sidebar/post-taxonomies/taxonomy-panel.js | 9 ++------- 7 files changed, 16 insertions(+), 46 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6e2b0b37b72649..fd9e28516aa2c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17758,7 +17758,6 @@ "@wordpress/warning": "file:packages/warning", "@wordpress/widgets": "file:packages/widgets", "classnames": "^2.3.1", - "lodash": "^4.17.21", "memize": "^1.1.0", "rememo": "^4.0.0" }, diff --git a/packages/edit-post/package.json b/packages/edit-post/package.json index 3fe1368a756811..2ec53ca879fa15 100644 --- a/packages/edit-post/package.json +++ b/packages/edit-post/package.json @@ -56,7 +56,6 @@ "@wordpress/warning": "file:../warning", "@wordpress/widgets": "file:../widgets", "classnames": "^2.3.1", - "lodash": "^4.17.21", "memize": "^1.1.0", "rememo": "^4.0.0" }, diff --git a/packages/edit-post/src/components/header/post-publish-button-or-toggle.js b/packages/edit-post/src/components/header/post-publish-button-or-toggle.js index dd2eeaf3be927d..837369293acbc0 100644 --- a/packages/edit-post/src/components/header/post-publish-button-or-toggle.js +++ b/packages/edit-post/src/components/header/post-publish-button-or-toggle.js @@ -1,8 +1,3 @@ -/** - * External dependencies - */ -import { get } from 'lodash'; - /** * WordPress dependencies */ @@ -83,11 +78,10 @@ export function PostPublishButtonOrToggle( { export default compose( withSelect( ( select ) => ( { - hasPublishAction: get( - select( editorStore ).getCurrentPost(), - [ '_links', 'wp:action-publish' ], - false - ), + hasPublishAction: + select( editorStore ).getCurrentPost()?._links?.[ + 'wp:action-publish' + ] ?? false, isBeingScheduled: select( editorStore ).isEditedPostBeingScheduled(), isPending: select( editorStore ).isCurrentPostPending(), isPublished: select( editorStore ).isCurrentPostPublished(), diff --git a/packages/edit-post/src/components/header/template-title/edit-template-title.js b/packages/edit-post/src/components/header/template-title/edit-template-title.js index 4e81fb4d9ee131..56970f25aafe67 100644 --- a/packages/edit-post/src/components/header/template-title/edit-template-title.js +++ b/packages/edit-post/src/components/header/template-title/edit-template-title.js @@ -1,8 +1,3 @@ -/** - * External dependencies - */ -import { mapValues } from 'lodash'; - /** * WordPress dependencies */ @@ -61,14 +56,15 @@ export default function EditTemplateTitle() { setForceEmpty( false ); const settings = getEditorSettings(); - const newAvailableTemplates = mapValues( - settings.availableTemplates, - ( existingTitle, id ) => { - if ( id !== template.slug ) { - return existingTitle; + const newAvailableTemplates = Object.fromEntries( + Object.entries( settings.availableTemplates ?? {} ).map( + ( [ id, existingTitle ] ) => { + if ( id !== template.slug ) { + return existingTitle; + } + return newTitle; } - return newTitle; - } + ) ); updateEditorSettings( { ...settings, diff --git a/packages/edit-post/src/components/header/tools-more-menu-group/index.js b/packages/edit-post/src/components/header/tools-more-menu-group/index.js index aac3dba2088700..b7ff7b8ce25f22 100644 --- a/packages/edit-post/src/components/header/tools-more-menu-group/index.js +++ b/packages/edit-post/src/components/header/tools-more-menu-group/index.js @@ -1,8 +1,3 @@ -/** - * External dependencies - */ -import { isEmpty } from 'lodash'; - /** * WordPress dependencies */ @@ -15,7 +10,7 @@ const { Fill: ToolsMoreMenuGroup, Slot } = ToolsMoreMenuGroup.Slot = ( { fillProps } ) => ( { ( fills ) => - ! isEmpty( fills ) && ( + fills.length > 0 && ( { fills } ) } diff --git a/packages/edit-post/src/components/preferences-modal/index.js b/packages/edit-post/src/components/preferences-modal/index.js index cbed0ece079823..77c6383b13f32c 100644 --- a/packages/edit-post/src/components/preferences-modal/index.js +++ b/packages/edit-post/src/components/preferences-modal/index.js @@ -1,8 +1,3 @@ -/** - * External dependencies - */ -import { get } from 'lodash'; - /** * WordPress dependencies */ @@ -213,10 +208,7 @@ export default function EditPostPreferencesModal() { ( ) } diff --git a/packages/edit-post/src/components/sidebar/post-taxonomies/taxonomy-panel.js b/packages/edit-post/src/components/sidebar/post-taxonomies/taxonomy-panel.js index aaa92ae63bcfba..9e254003c8cf1e 100644 --- a/packages/edit-post/src/components/sidebar/post-taxonomies/taxonomy-panel.js +++ b/packages/edit-post/src/components/sidebar/post-taxonomies/taxonomy-panel.js @@ -1,8 +1,3 @@ -/** - * External dependencies - */ -import { get } from 'lodash'; - /** * WordPress dependencies */ @@ -26,7 +21,7 @@ function TaxonomyPanel( { return null; } - const taxonomyMenuName = get( taxonomy, [ 'labels', 'menu_name' ] ); + const taxonomyMenuName = taxonomy?.labels?.menu_name; if ( ! taxonomyMenuName ) { return null; } @@ -44,7 +39,7 @@ function TaxonomyPanel( { export default compose( withSelect( ( select, ownProps ) => { - const slug = get( ownProps.taxonomy, [ 'slug' ] ); + const slug = ownProps.taxonomy?.slug; const panelName = slug ? `taxonomy-panel-${ slug }` : ''; return { panelName, From fe54a9232cdbd890b2f9156e4e3436c33ddc5f84 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Wed, 8 Mar 2023 10:25:18 +0100 Subject: [PATCH 068/910] [RNMobile] Add block transforms integration tests for Text blocks (#48823) * Allow wildcard block transformations of container blocks * Add open block actions menu test helper * Allow trigger block list layout by index * Add Paragraph block transform tests * Add transform block integration test helper * Add get block transform options helper * Add Heading block transform tests * Add List block transform tests * Add Quote block transform tests * Add Preformatted block transform tests * Add Shortcode block transform tests * Add Pullquote block transform tests * Add Verse block transform tests * Update Paragraph block transform tests --- .../block-transformations-menu.native.js | 1 + .../__snapshots__/transforms.native.js.snap | 47 ++++++++++ .../src/heading/test/transforms.native.js | 46 ++++++++++ .../__snapshots__/transforms.native.js.snap | 85 +++++++++++++++++++ .../src/list/test/transforms.native.js | 56 ++++++++++++ .../src/paragraph/test/transforms.native.js | 41 +++++---- .../__snapshots__/transforms.native.js.snap | 31 +++++++ .../preformatted/test/transforms.native.js | 42 +++++++++ .../__snapshots__/transforms.native.js.snap | 47 ++++++++++ .../src/pullquote/test/transforms.native.js | 46 ++++++++++ .../__snapshots__/transforms.native.js.snap | 39 +++++++++ .../src/quote/test/transforms.native.js | 67 +++++++++++++++ .../__snapshots__/transforms.native.js.snap | 19 +++++ .../src/shortcode/test/transforms.native.js | 42 +++++++++ .../__snapshots__/transforms.native.js.snap | 25 ++++++ .../src/verse/test/transforms.native.js | 42 +++++++++ .../native/integration-test-helpers/README.md | 8 ++ .../get-block-transform-options.js | 47 ++++++++++ test/native/integration-test-helpers/index.js | 2 + .../transform-block.js | 52 ++++++++++++ 20 files changed, 763 insertions(+), 22 deletions(-) create mode 100644 packages/block-library/src/heading/test/__snapshots__/transforms.native.js.snap create mode 100644 packages/block-library/src/heading/test/transforms.native.js create mode 100644 packages/block-library/src/list/test/__snapshots__/transforms.native.js.snap create mode 100644 packages/block-library/src/list/test/transforms.native.js create mode 100644 packages/block-library/src/preformatted/test/__snapshots__/transforms.native.js.snap create mode 100644 packages/block-library/src/preformatted/test/transforms.native.js create mode 100644 packages/block-library/src/pullquote/test/__snapshots__/transforms.native.js.snap create mode 100644 packages/block-library/src/pullquote/test/transforms.native.js create mode 100644 packages/block-library/src/quote/test/__snapshots__/transforms.native.js.snap create mode 100644 packages/block-library/src/quote/test/transforms.native.js create mode 100644 packages/block-library/src/shortcode/test/__snapshots__/transforms.native.js.snap create mode 100644 packages/block-library/src/shortcode/test/transforms.native.js create mode 100644 packages/block-library/src/verse/test/__snapshots__/transforms.native.js.snap create mode 100644 packages/block-library/src/verse/test/transforms.native.js create mode 100644 test/native/integration-test-helpers/get-block-transform-options.js create mode 100644 test/native/integration-test-helpers/transform-block.js diff --git a/packages/block-editor/src/components/block-switcher/block-transformations-menu.native.js b/packages/block-editor/src/components/block-switcher/block-transformations-menu.native.js index fc91bdd6d37dc1..33952378f601f5 100644 --- a/packages/block-editor/src/components/block-switcher/block-transformations-menu.native.js +++ b/packages/block-editor/src/components/block-switcher/block-transformations-menu.native.js @@ -75,6 +75,7 @@ const BlockTransformationsMenu = ( { return ( +
+
+

Example text

+
+
+" +`; + +exports[`Heading block transforms to Group block 1`] = ` +" +
+

Example text

+
+" +`; + +exports[`Heading block transforms to List block 1`] = ` +" +
    +
  • Example text
  • +
+" +`; + +exports[`Heading block transforms to Paragraph block 1`] = ` +" +

Example text

+" +`; + +exports[`Heading block transforms to Pullquote block 1`] = ` +" +

Example text

+" +`; + +exports[`Heading block transforms to Quote block 1`] = ` +" +
+

Example text

+
+" +`; diff --git a/packages/block-library/src/heading/test/transforms.native.js b/packages/block-library/src/heading/test/transforms.native.js new file mode 100644 index 00000000000000..39aaaa53cf9785 --- /dev/null +++ b/packages/block-library/src/heading/test/transforms.native.js @@ -0,0 +1,46 @@ +/** + * External dependencies + */ +import { + getEditorHtml, + initializeEditor, + setupCoreBlocks, + transformBlock, + getBlockTransformOptions, +} from 'test/helpers'; + +const block = 'Heading'; +const initialHtml = ` + +

Example text

+`; + +const transformsWithInnerBlocks = [ 'List', 'Quote', 'Columns', 'Group' ]; +const blockTransforms = [ + 'Paragraph', + 'Pullquote', + ...transformsWithInnerBlocks, +]; + +setupCoreBlocks(); + +describe( `${ block } block transforms`, () => { + test.each( blockTransforms )( 'to %s block', async ( blockTransform ) => { + const screen = await initializeEditor( { initialHtml } ); + const newBlock = await transformBlock( screen, block, blockTransform, { + hasInnerBlocks: + transformsWithInnerBlocks.includes( blockTransform ), + } ); + expect( newBlock ).toBeVisible(); + expect( getEditorHtml() ).toMatchSnapshot(); + } ); + + it( 'matches expected transformation options', async () => { + const screen = await initializeEditor( { initialHtml } ); + const transformOptions = await getBlockTransformOptions( + screen, + block + ); + expect( transformOptions ).toHaveLength( blockTransforms.length ); + } ); +} ); diff --git a/packages/block-library/src/list/test/__snapshots__/transforms.native.js.snap b/packages/block-library/src/list/test/__snapshots__/transforms.native.js.snap new file mode 100644 index 00000000000000..723d19a658366f --- /dev/null +++ b/packages/block-library/src/list/test/__snapshots__/transforms.native.js.snap @@ -0,0 +1,85 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`List block transforms to Columns block 1`] = ` +" +
+
+
    +
  • First Item
  • + + + +
  • Second Item
  • + + + +
  • Third Item
  • +
+
+
+" +`; + +exports[`List block transforms to Group block 1`] = ` +" +
+
    +
  • First Item
  • + + + +
  • Second Item
  • + + + +
  • Third Item
  • +
+
+" +`; + +exports[`List block transforms to Heading block 1`] = ` +" +

First Item

+ + + +

Second Item

+ + + +

Third Item

+" +`; + +exports[`List block transforms to Paragraph block 1`] = ` +" +

First Item

+ + + +

Second Item

+ + + +

Third Item

+" +`; + +exports[`List block transforms to Quote block 1`] = ` +" +
+
    +
  • First Item
  • + + + +
  • Second Item
  • + + + +
  • Third Item
  • +
+
+" +`; diff --git a/packages/block-library/src/list/test/transforms.native.js b/packages/block-library/src/list/test/transforms.native.js new file mode 100644 index 00000000000000..dd8ec5ccd2c772 --- /dev/null +++ b/packages/block-library/src/list/test/transforms.native.js @@ -0,0 +1,56 @@ +/** + * External dependencies + */ +import { + getEditorHtml, + initializeEditor, + setupCoreBlocks, + transformBlock, + getBlockTransformOptions, +} from 'test/helpers'; + +const block = 'List'; +const initialHtml = ` + +
    +
  • First Item
  • + + + +
  • Second Item
  • + + + +
  • Third Item
  • +
+`; + +const transformsWithInnerBlocks = [ 'Quote', 'Columns', 'Group' ]; +const blockTransforms = [ + 'Paragraph', + 'Heading', + ...transformsWithInnerBlocks, +]; + +setupCoreBlocks(); + +describe( `${ block } block transforms`, () => { + test.each( blockTransforms )( 'to %s block', async ( blockTransform ) => { + const screen = await initializeEditor( { initialHtml } ); + const newBlock = await transformBlock( screen, block, blockTransform, { + hasInnerBlocks: + transformsWithInnerBlocks.includes( blockTransform ), + } ); + expect( newBlock ).toBeVisible(); + expect( getEditorHtml() ).toMatchSnapshot(); + } ); + + it( 'matches expected transformation options', async () => { + const screen = await initializeEditor( { initialHtml } ); + const transformOptions = await getBlockTransformOptions( + screen, + block + ); + expect( transformOptions ).toHaveLength( blockTransforms.length ); + } ); +} ); diff --git a/packages/block-library/src/paragraph/test/transforms.native.js b/packages/block-library/src/paragraph/test/transforms.native.js index 76271fa757c6aa..0f34038ca298c7 100644 --- a/packages/block-library/src/paragraph/test/transforms.native.js +++ b/packages/block-library/src/paragraph/test/transforms.native.js @@ -2,13 +2,11 @@ * External dependencies */ import { - fireEvent, - getBlock, getEditorHtml, initializeEditor, - openBlockActionsMenu, setupCoreBlocks, - triggerBlockListLayout, + transformBlock, + getBlockTransformOptions, } from 'test/helpers'; const block = 'Paragraph'; @@ -31,23 +29,22 @@ const blockTransforms = [ setupCoreBlocks(); describe( `${ block } block transforms`, () => { - test.each( blockTransforms )( - 'to %s block', - async ( blockTransformation ) => { - const screen = await initializeEditor( { initialHtml } ); - const { getByText } = screen; - fireEvent.press( getBlock( screen, block ) ); + test.each( blockTransforms )( 'to %s block', async ( blockTransform ) => { + const screen = await initializeEditor( { initialHtml } ); + const newBlock = await transformBlock( screen, block, blockTransform, { + hasInnerBlocks: + transformsWithInnerBlocks.includes( blockTransform ), + } ); + expect( newBlock ).toBeVisible(); + expect( getEditorHtml() ).toMatchSnapshot(); + } ); - await openBlockActionsMenu( screen ); - fireEvent.press( getByText( 'Transform block…' ) ); - fireEvent.press( getByText( blockTransformation ) ); - - const newBlock = getBlock( screen, blockTransformation ); - if ( transformsWithInnerBlocks.includes( blockTransformation ) ) - await triggerBlockListLayout( newBlock ); - - expect( newBlock ).toBeVisible(); - expect( getEditorHtml() ).toMatchSnapshot(); - } - ); + it( 'matches expected transformation options', async () => { + const screen = await initializeEditor( { initialHtml } ); + const transformOptions = await getBlockTransformOptions( + screen, + block + ); + expect( transformOptions ).toHaveLength( blockTransforms.length ); + } ); } ); diff --git a/packages/block-library/src/preformatted/test/__snapshots__/transforms.native.js.snap b/packages/block-library/src/preformatted/test/__snapshots__/transforms.native.js.snap new file mode 100644 index 00000000000000..81adef5b228388 --- /dev/null +++ b/packages/block-library/src/preformatted/test/__snapshots__/transforms.native.js.snap @@ -0,0 +1,31 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Preformatted block transforms to Code block 1`] = ` +" +
Some preformatted text...
And more!
+" +`; + +exports[`Preformatted block transforms to Columns block 1`] = ` +" +
+
+
Some preformatted text...
And more!
+
+
+" +`; + +exports[`Preformatted block transforms to Group block 1`] = ` +" +
+
Some preformatted text...
And more!
+
+" +`; + +exports[`Preformatted block transforms to Paragraph block 1`] = ` +" +

Some preformatted text...
And more!

+" +`; diff --git a/packages/block-library/src/preformatted/test/transforms.native.js b/packages/block-library/src/preformatted/test/transforms.native.js new file mode 100644 index 00000000000000..86a7d6134b1135 --- /dev/null +++ b/packages/block-library/src/preformatted/test/transforms.native.js @@ -0,0 +1,42 @@ +/** + * External dependencies + */ +import { + getEditorHtml, + initializeEditor, + setupCoreBlocks, + transformBlock, + getBlockTransformOptions, +} from 'test/helpers'; + +const block = 'Preformatted'; +const initialHtml = ` + +
Some preformatted text...
And more!
+`; + +const transformsWithInnerBlocks = [ 'Columns', 'Group' ]; +const blockTransforms = [ 'Paragraph', 'Code', ...transformsWithInnerBlocks ]; + +setupCoreBlocks(); + +describe( `${ block } block transforms`, () => { + test.each( blockTransforms )( 'to %s block', async ( blockTransform ) => { + const screen = await initializeEditor( { initialHtml } ); + const newBlock = await transformBlock( screen, block, blockTransform, { + hasInnerBlocks: + transformsWithInnerBlocks.includes( blockTransform ), + } ); + expect( newBlock ).toBeVisible(); + expect( getEditorHtml() ).toMatchSnapshot(); + } ); + + it( 'matches expected transformation options', async () => { + const screen = await initializeEditor( { initialHtml } ); + const transformOptions = await getBlockTransformOptions( + screen, + block + ); + expect( transformOptions ).toHaveLength( blockTransforms.length ); + } ); +} ); diff --git a/packages/block-library/src/pullquote/test/__snapshots__/transforms.native.js.snap b/packages/block-library/src/pullquote/test/__snapshots__/transforms.native.js.snap new file mode 100644 index 00000000000000..6840d561dda0cc --- /dev/null +++ b/packages/block-library/src/pullquote/test/__snapshots__/transforms.native.js.snap @@ -0,0 +1,47 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Pullquote block transforms to Columns block 1`] = ` +" +
+
+

One of the hardest things to do in technology is disrupt yourself.

Matt Mullenweg
+
+
+" +`; + +exports[`Pullquote block transforms to Group block 1`] = ` +" +
+

One of the hardest things to do in technology is disrupt yourself.

Matt Mullenweg
+
+" +`; + +exports[`Pullquote block transforms to Heading block 1`] = ` +" +

One of the hardest things to do in technology is disrupt yourself.

+ + + +

Matt Mullenweg

+" +`; + +exports[`Pullquote block transforms to Paragraph block 1`] = ` +" +

One of the hardest things to do in technology is disrupt yourself.

+ + + +

Matt Mullenweg

+" +`; + +exports[`Pullquote block transforms to Quote block 1`] = ` +" +
+

One of the hardest things to do in technology is disrupt yourself.

+Matt Mullenweg
+" +`; diff --git a/packages/block-library/src/pullquote/test/transforms.native.js b/packages/block-library/src/pullquote/test/transforms.native.js new file mode 100644 index 00000000000000..c0360f82b6e87a --- /dev/null +++ b/packages/block-library/src/pullquote/test/transforms.native.js @@ -0,0 +1,46 @@ +/** + * External dependencies + */ +import { + getEditorHtml, + initializeEditor, + setupCoreBlocks, + transformBlock, + getBlockTransformOptions, +} from 'test/helpers'; + +const block = 'Pullquote'; +const initialHtml = ` + +

One of the hardest things to do in technology is disrupt yourself.

Matt Mullenweg
+`; + +const transformsWithInnerBlocks = [ 'Quote', 'Columns', 'Group' ]; +const blockTransforms = [ + 'Paragraph', + 'Heading', + ...transformsWithInnerBlocks, +]; + +setupCoreBlocks(); + +describe( `${ block } block transforms`, () => { + test.each( blockTransforms )( 'to %s block', async ( blockTransform ) => { + const screen = await initializeEditor( { initialHtml } ); + const newBlock = await transformBlock( screen, block, blockTransform, { + hasInnerBlocks: + transformsWithInnerBlocks.includes( blockTransform ), + } ); + expect( newBlock ).toBeVisible(); + expect( getEditorHtml() ).toMatchSnapshot(); + } ); + + it( 'matches expected transformation options', async () => { + const screen = await initializeEditor( { initialHtml } ); + const transformOptions = await getBlockTransformOptions( + screen, + block + ); + expect( transformOptions ).toHaveLength( blockTransforms.length ); + } ); +} ); diff --git a/packages/block-library/src/quote/test/__snapshots__/transforms.native.js.snap b/packages/block-library/src/quote/test/__snapshots__/transforms.native.js.snap new file mode 100644 index 00000000000000..bc337691dd27a7 --- /dev/null +++ b/packages/block-library/src/quote/test/__snapshots__/transforms.native.js.snap @@ -0,0 +1,39 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Quote block transforms to Columns block 1`] = ` +" +
+
+
+

"This will make running your own blog a viable alternative again."

+Adrian Zumbrunnen
+
+
+" +`; + +exports[`Quote block transforms to Group block 1`] = ` +" +
+
+

"This will make running your own blog a viable alternative again."

+Adrian Zumbrunnen
+
+" +`; + +exports[`Quote block transforms to Pullquote block 1`] = ` +" +

"This will make running your own blog a viable alternative again."

Adrian Zumbrunnen
+" +`; + +exports[`Quote block transforms unwraps content 1`] = ` +" +

"This will make running your own blog a viable alternative again."

+ + + +

Adrian Zumbrunnen

+" +`; diff --git a/packages/block-library/src/quote/test/transforms.native.js b/packages/block-library/src/quote/test/transforms.native.js new file mode 100644 index 00000000000000..75cb887a4872be --- /dev/null +++ b/packages/block-library/src/quote/test/transforms.native.js @@ -0,0 +1,67 @@ +/** + * External dependencies + */ +import { + getEditorHtml, + initializeEditor, + setupCoreBlocks, + transformBlock, + getBlock, + openBlockActionsMenu, + fireEvent, + getBlockTransformOptions, +} from 'test/helpers'; + +const block = 'Quote'; +const initialHtml = ` + +
+

"This will make running your own blog a viable alternative again."

+Adrian Zumbrunnen
+`; + +const transformsWithInnerBlocks = [ 'Columns', 'Group' ]; +const blockTransforms = [ 'Pullquote', ...transformsWithInnerBlocks ]; + +setupCoreBlocks(); + +describe( `${ block } block transforms`, () => { + test.each( blockTransforms )( 'to %s block', async ( blockTransform ) => { + const screen = await initializeEditor( { initialHtml } ); + const newBlock = await transformBlock( screen, block, blockTransform, { + hasInnerBlocks: + transformsWithInnerBlocks.includes( blockTransform ), + } ); + expect( newBlock ).toBeVisible(); + expect( getEditorHtml() ).toMatchSnapshot(); + } ); + + it( 'unwraps content', async () => { + const screen = await initializeEditor( { initialHtml } ); + const { getByText } = screen; + fireEvent.press( getBlock( screen, block ) ); + + await openBlockActionsMenu( screen ); + fireEvent.press( getByText( 'Transform block…' ) ); + fireEvent.press( getByText( 'Unwrap' ) ); + + // The first block created is the content of the Paragraph block. + const paragraph = getBlock( screen, 'Paragraph', 0 ); + expect( paragraph ).toBeVisible(); + // The second block created is the content of the citation element. + const citation = getBlock( screen, 'Paragraph', 1 ); + expect( citation ).toBeVisible(); + + expect( getEditorHtml() ).toMatchSnapshot(); + } ); + + it( 'matches expected transformation options', async () => { + const screen = await initializeEditor( { initialHtml } ); + const transformOptions = await getBlockTransformOptions( + screen, + block, + { canUnwrap: true } + ); + expect( transformOptions ).toHaveLength( blockTransforms.length ); + } ); +} ); diff --git a/packages/block-library/src/shortcode/test/__snapshots__/transforms.native.js.snap b/packages/block-library/src/shortcode/test/__snapshots__/transforms.native.js.snap new file mode 100644 index 00000000000000..a9fcc98410d45a --- /dev/null +++ b/packages/block-library/src/shortcode/test/__snapshots__/transforms.native.js.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Shortcode block transforms to Columns block 1`] = ` +" +
+
+[youtube https://www.youtube.com/watch?v=ssfHW5lwFZg] +
+
+" +`; + +exports[`Shortcode block transforms to Group block 1`] = ` +" +
+[youtube https://www.youtube.com/watch?v=ssfHW5lwFZg] +
+" +`; diff --git a/packages/block-library/src/shortcode/test/transforms.native.js b/packages/block-library/src/shortcode/test/transforms.native.js new file mode 100644 index 00000000000000..7a4d05c684cfaa --- /dev/null +++ b/packages/block-library/src/shortcode/test/transforms.native.js @@ -0,0 +1,42 @@ +/** + * External dependencies + */ +import { + getEditorHtml, + initializeEditor, + setupCoreBlocks, + transformBlock, + getBlockTransformOptions, +} from 'test/helpers'; + +const block = 'Shortcode'; +const initialHtml = ` + +[youtube https://www.youtube.com/watch?v=ssfHW5lwFZg] +`; + +const transformsWithInnerBlocks = [ 'Columns', 'Group' ]; +const blockTransforms = [ ...transformsWithInnerBlocks ]; + +setupCoreBlocks(); + +describe( `${ block } block transforms`, () => { + test.each( blockTransforms )( 'to %s block', async ( blockTransform ) => { + const screen = await initializeEditor( { initialHtml } ); + const newBlock = await transformBlock( screen, block, blockTransform, { + hasInnerBlocks: + transformsWithInnerBlocks.includes( blockTransform ), + } ); + expect( newBlock ).toBeVisible(); + expect( getEditorHtml() ).toMatchSnapshot(); + } ); + + it( 'matches expected transformation options', async () => { + const screen = await initializeEditor( { initialHtml } ); + const transformOptions = await getBlockTransformOptions( + screen, + block + ); + expect( transformOptions ).toHaveLength( blockTransforms.length ); + } ); +} ); diff --git a/packages/block-library/src/verse/test/__snapshots__/transforms.native.js.snap b/packages/block-library/src/verse/test/__snapshots__/transforms.native.js.snap new file mode 100644 index 00000000000000..5570a6cf5d67d9 --- /dev/null +++ b/packages/block-library/src/verse/test/__snapshots__/transforms.native.js.snap @@ -0,0 +1,25 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Verse block transforms to Columns block 1`] = ` +" +
+
+
Come
Home.
+
+
+" +`; + +exports[`Verse block transforms to Group block 1`] = ` +" +
+
Come
Home.
+
+" +`; + +exports[`Verse block transforms to Paragraph block 1`] = ` +" +

Come
Home.

+" +`; diff --git a/packages/block-library/src/verse/test/transforms.native.js b/packages/block-library/src/verse/test/transforms.native.js new file mode 100644 index 00000000000000..bab71da95fbc16 --- /dev/null +++ b/packages/block-library/src/verse/test/transforms.native.js @@ -0,0 +1,42 @@ +/** + * External dependencies + */ +import { + getEditorHtml, + initializeEditor, + setupCoreBlocks, + transformBlock, + getBlockTransformOptions, +} from 'test/helpers'; + +const block = 'Verse'; +const initialHtml = ` + +
Come
Home.
+`; + +const transformsWithInnerBlocks = [ 'Columns', 'Group' ]; +const blockTransforms = [ 'Paragraph', ...transformsWithInnerBlocks ]; + +setupCoreBlocks(); + +describe( `${ block } block transforms`, () => { + test.each( blockTransforms )( 'to %s block', async ( blockTransform ) => { + const screen = await initializeEditor( { initialHtml } ); + const newBlock = await transformBlock( screen, block, blockTransform, { + hasInnerBlocks: + transformsWithInnerBlocks.includes( blockTransform ), + } ); + expect( newBlock ).toBeVisible(); + expect( getEditorHtml() ).toMatchSnapshot(); + } ); + + it( 'matches expected transformation options', async () => { + const screen = await initializeEditor( { initialHtml } ); + const transformOptions = await getBlockTransformOptions( + screen, + block + ); + expect( transformOptions ).toHaveLength( blockTransforms.length ); + } ); +} ); diff --git a/test/native/integration-test-helpers/README.md b/test/native/integration-test-helpers/README.md index 950532d429feff..900f8f2fb896d7 100644 --- a/test/native/integration-test-helpers/README.md +++ b/test/native/integration-test-helpers/README.md @@ -18,6 +18,10 @@ Dismisses a modal. Gets a block from the root block list. +### [`getBlockTransformOptions`](https://github.com/WordPress/gutenberg/blob/HEAD/test/native/integration-test-helpers/get-block-transform-options.js) + +Get the block transform options of a block. + ### [`getEditorHtml`](https://github.com/WordPress/gutenberg/blob/HEAD/test/native/integration-test-helpers/get-editor-html.js) Gets the current HTML output of the editor. @@ -66,6 +70,10 @@ Sets up the media upload mock functions for testing. Changes the text of a TextInput component. +### [`transformBlock`](https://github.com/WordPress/gutenberg/blob/HEAD/test/native/integration-test-helpers/transform-block.js) + +Transform the specified block to another block using the Block actions menu. + ### [`triggerBlockListLayout`](https://github.com/WordPress/gutenberg/blob/HEAD/test/native/integration-test-helpers/trigger-block-list-layout.js) Helper for ensuring that all items of a Block List component are rendered. diff --git a/test/native/integration-test-helpers/get-block-transform-options.js b/test/native/integration-test-helpers/get-block-transform-options.js new file mode 100644 index 00000000000000..e9bd35261ed740 --- /dev/null +++ b/test/native/integration-test-helpers/get-block-transform-options.js @@ -0,0 +1,47 @@ +/** + * External dependencies + */ +import { fireEvent, within } from '@testing-library/react-native'; + +/** + * Internal dependencies + */ +import { getBlock } from './get-block'; +import { openBlockActionsMenu } from './open-block-actions-menu'; + +/** + * Transforms the selected block to a specified block. + * + * @param {import('@testing-library/react-native').RenderAPI} screen A Testing Library screen. + * @param {string} blockName Name of the block. + * @param {Object} [options] Configuration options. + * @param {number} [options.canUnwrap] True if the block can be unwrapped. + * @return {[import('react-test-renderer').ReactTestInstance]} Block transform options. + */ +export const getBlockTransformOptions = async ( + screen, + blockName, + { canUnwrap = false } = {} +) => { + const { getByTestId, getByText } = screen; + + fireEvent.press( getBlock( screen, blockName ) ); + await openBlockActionsMenu( screen ); + fireEvent.press( getByText( 'Transform block…' ) ); + + let blockTransformButtons = within( + getByTestId( 'block-transformations-menu' ) + ).getAllByRole( 'button' ); + + // Remove Unwrap option as it's not a direct block transformation. + if ( canUnwrap ) { + const unwrapButton = within( + getByTestId( 'block-transformations-menu' ) + ).getByLabelText( 'Unwrap' ); + blockTransformButtons = blockTransformButtons.filter( + ( button ) => button !== unwrapButton + ); + } + + return blockTransformButtons; +}; diff --git a/test/native/integration-test-helpers/index.js b/test/native/integration-test-helpers/index.js index 5f8a3b6c84a3ad..1d1c2666f309b7 100644 --- a/test/native/integration-test-helpers/index.js +++ b/test/native/integration-test-helpers/index.js @@ -5,6 +5,7 @@ export { } from './advance-animation'; export { dismissModal } from './dismiss-modal'; export { getBlock } from './get-block'; +export { getBlockTransformOptions } from './get-block-transform-options'; export { getEditorHtml } from './get-editor-html'; export { getInnerBlock } from './get-inner-block'; export { initializeEditor } from './initialize-editor'; @@ -17,6 +18,7 @@ export { setupCoreBlocks } from './setup-core-blocks'; export { setupMediaPicker } from './setup-media-picker'; export { setupMediaUpload } from './setup-media-upload'; export { changeTextOfTextInput } from './text-input-change-text'; +export { transformBlock } from './transform-block'; export { triggerBlockListLayout } from './trigger-block-list-layout'; export { waitForModalVisible } from './wait-for-modal-visible'; export { waitForStoreResolvers } from './wait-for-store-resolvers'; diff --git a/test/native/integration-test-helpers/transform-block.js b/test/native/integration-test-helpers/transform-block.js new file mode 100644 index 00000000000000..3feb6fbb32827e --- /dev/null +++ b/test/native/integration-test-helpers/transform-block.js @@ -0,0 +1,52 @@ +/** + * External dependencies + */ +import { act, fireEvent } from '@testing-library/react-native'; + +/** + * Internal dependencies + */ +import { getBlock } from './get-block'; +import { openBlockActionsMenu } from './open-block-actions-menu'; +import { triggerBlockListLayout } from './trigger-block-list-layout'; + +const apiFetchPromise = Promise.resolve( {} ); + +/** + * Transforms the selected block to a specified block. + * + * @param {import('@testing-library/react-native').RenderAPI} screen A Testing Library screen. + * @param {string} blockName Name of the block to transform. + * @param {string} targetBlockName Name of the target block to transform to. + * @param {Object} [options] Configuration options for the transformation. + * @param {number} [options.isMediaBlock] True if the block transformation will result in a media block. + * @param {number} [options.hasInnerBlocks] True if the block transformation will result in a block that contains inner blocks. + * @return {import('react-test-renderer').ReactTestInstance} Block instance after the block transformation result. + */ +export const transformBlock = async ( + screen, + blockName, + targetBlockName, + { isMediaBlock = false, hasInnerBlocks = false } = {} +) => { + const { getByText } = screen; + + fireEvent.press( getBlock( screen, blockName ) ); + + await openBlockActionsMenu( screen ); + fireEvent.press( getByText( 'Transform block…' ) ); + fireEvent.press( getByText( targetBlockName ) ); + + // For media blocks, we must wait for the media fetch via `getMedia`. + if ( isMediaBlock ) { + await act( () => apiFetchPromise ); + } + + const newBlock = getBlock( screen, targetBlockName ); + // For blocks that contain inner blocks, we must trigger the inner + // block list layout to render its content. + if ( hasInnerBlocks ) { + await triggerBlockListLayout( newBlock ); + } + return newBlock; +}; From f6f34386cf9cf040d0780e12871538ff46123cd8 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 8 Mar 2023 10:40:43 +0100 Subject: [PATCH 069/910] Navigation: refactor to TypeScript (#48742) * Remove component from `ts-config.json` * Rename implementation files to TSX * getAnimateClassName: change return type from `void` to `undefined` * Add component prop types, fix basic TS component errors * Type utils * Type internal context and related utilities * CHANGELOG * Add missing `titleAction` prop types and description * Add missing `icon` prop type and description * Expand types in README * Add missing closing brace to types * Update pre-check on `normalizedSearch` call * Type tests, make `children` prop on `Navigation` optional * Rename storybook files to tsx * Fix type errors in Storybook files & adjust prop types accordingly * Add named exports to allow Storybook to pick up props for docs generation * Add missing prop descriptions * Enable Storybook controls * Add JSDoc on main export * Tidy up `Item` and `Menu` types * Improve `NavigationMenuProps` props * Add button props to navigation item * Typo * Better `debouncedSpeak` types * DRY-up `onSearch` + `search` props by creating `IfDiscriminantDefinedBothRequired` type utility * Fix type errors * Fix inline comments * Remove `variant` prop from `ItemTitleUI` --- packages/components/CHANGELOG.md | 1 + packages/components/src/animate/index.js | 4 +- packages/components/src/navigation/README.md | 18 +- .../back-button/{index.js => index.tsx} | 28 +- .../{constants.js => constants.tsx} | 0 .../navigation/{context.js => context.tsx} | 13 +- .../src/navigation/group/context.js | 9 - .../src/navigation/group/context.tsx | 15 + .../navigation/group/{index.js => index.tsx} | 10 +- .../src/navigation/{index.js => index.tsx} | 70 +++- .../{base-content.js => base-content.tsx} | 7 +- .../src/navigation/item/{base.js => base.tsx} | 4 +- .../navigation/item/{index.js => index.tsx} | 10 +- ...e-item.js => use-navigation-tree-item.tsx} | 11 +- .../components/src/navigation/menu/context.js | 11 - .../src/navigation/menu/context.tsx | 18 + .../navigation/menu/{index.js => index.tsx} | 6 +- ...-title-search.js => menu-title-search.tsx} | 18 +- .../menu/{menu-title.js => menu-title.tsx} | 8 +- ...s-found.js => search-no-results-found.tsx} | 6 +- ...e-menu.js => use-navigation-tree-menu.tsx} | 4 +- .../src/navigation/stories/index.js | 33 -- .../src/navigation/stories/index.tsx | 53 +++ ...ntrolled-state.js => controlled-state.tsx} | 45 ++- .../stories/utils/{default.js => default.tsx} | 26 +- .../stories/utils/{group.js => group.tsx} | 28 +- .../{hide-if-empty.js => hide-if-empty.tsx} | 25 +- .../{more-examples.js => more-examples.tsx} | 42 ++- .../stories/utils/{search.js => search.tsx} | 28 +- ...gation-styles.js => navigation-styles.tsx} | 0 .../navigation/test/{index.js => index.tsx} | 21 +- packages/components/src/navigation/types.ts | 325 ++++++++++++++++++ ...tree.js => use-create-navigation-tree.tsx} | 62 ++-- ...nodes.js => use-navigation-tree-nodes.tsx} | 16 +- .../src/navigation/{utils.js => utils.tsx} | 4 +- packages/components/tsconfig.json | 1 - 36 files changed, 796 insertions(+), 184 deletions(-) rename packages/components/src/navigation/back-button/{index.js => index.tsx} (65%) rename packages/components/src/navigation/{constants.js => constants.tsx} (100%) rename packages/components/src/navigation/{context.js => context.tsx} (62%) delete mode 100644 packages/components/src/navigation/group/context.js create mode 100644 packages/components/src/navigation/group/context.tsx rename packages/components/src/navigation/group/{index.js => index.tsx} (88%) rename packages/components/src/navigation/{index.js => index.tsx} (52%) rename packages/components/src/navigation/item/{base-content.js => base-content.tsx} (73%) rename packages/components/src/navigation/item/{base.js => base.tsx} (87%) rename packages/components/src/navigation/item/{index.js => index.tsx} (88%) rename packages/components/src/navigation/item/{use-navigation-tree-item.js => use-navigation-tree-item.tsx} (79%) delete mode 100644 packages/components/src/navigation/menu/context.js create mode 100644 packages/components/src/navigation/menu/context.tsx rename packages/components/src/navigation/menu/{index.js => index.tsx} (94%) rename packages/components/src/navigation/menu/{menu-title-search.js => menu-title-search.tsx} (86%) rename packages/components/src/navigation/menu/{menu-title.js => menu-title.tsx} (92%) rename packages/components/src/navigation/menu/{search-no-results-found.js => search-no-results-found.tsx} (75%) rename packages/components/src/navigation/menu/{use-navigation-tree-menu.js => use-navigation-tree-menu.tsx} (82%) delete mode 100644 packages/components/src/navigation/stories/index.js create mode 100644 packages/components/src/navigation/stories/index.tsx rename packages/components/src/navigation/stories/utils/{controlled-state.js => controlled-state.tsx} (75%) rename packages/components/src/navigation/stories/utils/{default.js => default.tsx} (77%) rename packages/components/src/navigation/stories/utils/{group.js => group.tsx} (62%) rename packages/components/src/navigation/stories/utils/{hide-if-empty.js => hide-if-empty.tsx} (69%) rename packages/components/src/navigation/stories/utils/{more-examples.js => more-examples.tsx} (81%) rename packages/components/src/navigation/stories/utils/{search.js => search.tsx} (77%) rename packages/components/src/navigation/styles/{navigation-styles.js => navigation-styles.tsx} (100%) rename packages/components/src/navigation/test/{index.js => index.tsx} (95%) create mode 100644 packages/components/src/navigation/types.ts rename packages/components/src/navigation/{use-create-navigation-tree.js => use-create-navigation-tree.tsx} (50%) rename packages/components/src/navigation/{use-navigation-tree-nodes.js => use-navigation-tree-nodes.tsx} (52%) rename packages/components/src/navigation/{utils.js => utils.tsx} (69%) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 4af772a5060927..4466716bdc47c5 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -14,6 +14,7 @@ - `Guide`: Convert to TypeScript ([#47493](https://github.com/WordPress/gutenberg/pull/47493)). - `SelectControl`: improve prop types for single vs multiple selection ([#47390](https://github.com/WordPress/gutenberg/pull/47390)). +- `Navigation`: Convert to TypeScript ([#48742](https://github.com/WordPress/gutenberg/pull/48742)). - `PanelBody`: Convert to TypeScript ([#47702](https://github.com/WordPress/gutenberg/pull/47702)). - `withFallbackStyles` HOC: Convert to TypeScript ([#48720](https://github.com/WordPress/gutenberg/pull/48720)). - `navigateRegions` HOC: Convert to TypeScript ([#48632](https://github.com/WordPress/gutenberg/pull/48632)). diff --git a/packages/components/src/animate/index.js b/packages/components/src/animate/index.js index 1ef1e272dbb453..f648b00ed3a640 100644 --- a/packages/components/src/animate/index.js +++ b/packages/components/src/animate/index.js @@ -25,7 +25,7 @@ function getDefaultOrigin( type ) { /** * @param {GetAnimateOptions} options * - * @return {string | void} ClassName that applies the animations + * @return {string | undefined} ClassName that applies the animations */ export function getAnimateClassName( options ) { if ( options.type === 'loading' ) { @@ -48,6 +48,8 @@ export function getAnimateClassName( options ) { 'is-from-' + origin ); } + + return undefined; } // @ts-ignore Reason: Planned for deprecation diff --git a/packages/components/src/navigation/README.md b/packages/components/src/navigation/README.md index 1128874463d574..3a1fa992611b62 100644 --- a/packages/components/src/navigation/README.md +++ b/packages/components/src/navigation/README.md @@ -122,7 +122,7 @@ The unique identifier of the menu. The root menu can omit this, and it will defa ### onSearch -- Type: `function` +- Type: `( searchString: string ) => void;` - Required: No When `hasSearch` is active, this function handles the search input's `onChange` event, making it controlled from the outside. It requires setting the `search` prop as well. @@ -162,6 +162,13 @@ Indicates whether the menu is empty or not. Used together with the `hideIfTarget The menu title. It's also the field used by the menu search function. +### `titleAction` + +- Type: `React.ReactNode` +- Required: No + +Use this prop to render additional actions in the menu title. + ## Navigation Group Props `NavigationGroup` supports the following props. @@ -205,6 +212,13 @@ Optional className for the `NavigationItem` component. If provided, renders `a` instead of `button`. +### `icon` + +- Type: `JSX.Element` +- Required: No + +If no `children` are passed, this prop allows to specify a custom icon for the menu item. + ### `item` - Type: `string` @@ -228,7 +242,7 @@ Indicates whether this item should be hidden if the menu specified in `navigateT ### `onClick` -- Type: `function` +- Type: `React.MouseEventHandler` - Required: No A callback to handle clicking on a menu item. diff --git a/packages/components/src/navigation/back-button/index.js b/packages/components/src/navigation/back-button/index.tsx similarity index 65% rename from packages/components/src/navigation/back-button/index.js rename to packages/components/src/navigation/back-button/index.tsx index 5912e104d2cbb7..182faa8773726d 100644 --- a/packages/components/src/navigation/back-button/index.js +++ b/packages/components/src/navigation/back-button/index.tsx @@ -15,9 +15,17 @@ import { Icon, chevronLeft, chevronRight } from '@wordpress/icons'; import { useNavigationContext } from '../context'; import { MenuBackButtonUI } from '../styles/navigation-styles'; -function NavigationBackButton( - { backButtonLabel, className, href, onClick, parentMenu }, - ref +import type { NavigationBackButtonProps } from '../types'; + +function UnforwardedNavigationBackButton( + { + backButtonLabel, + className, + href, + onClick, + parentMenu, + }: NavigationBackButtonProps, + ref: React.ForwardedRef< HTMLAnchorElement | HTMLButtonElement > ) { const { setActiveMenu, navigationTree } = useNavigationContext(); @@ -26,9 +34,12 @@ function NavigationBackButton( className ); - const parentMenuTitle = navigationTree.getMenu( parentMenu )?.title; + const parentMenuTitle = + parentMenu !== undefined + ? navigationTree.getMenu( parentMenu )?.title + : undefined; - const handleOnClick = ( event ) => { + const handleOnClick: React.MouseEventHandler< HTMLElement > = ( event ) => { if ( typeof onClick === 'function' ) { onClick( event ); } @@ -52,4 +63,9 @@ function NavigationBackButton( ); } -export default forwardRef( NavigationBackButton ); + +export const NavigationBackButton = forwardRef( + UnforwardedNavigationBackButton +); + +export default NavigationBackButton; diff --git a/packages/components/src/navigation/constants.js b/packages/components/src/navigation/constants.tsx similarity index 100% rename from packages/components/src/navigation/constants.js rename to packages/components/src/navigation/constants.tsx diff --git a/packages/components/src/navigation/context.js b/packages/components/src/navigation/context.tsx similarity index 62% rename from packages/components/src/navigation/context.js rename to packages/components/src/navigation/context.tsx index 58fcb9712a22eb..3b14faf47d315d 100644 --- a/packages/components/src/navigation/context.js +++ b/packages/components/src/navigation/context.tsx @@ -8,27 +8,30 @@ import { createContext, useContext } from '@wordpress/element'; */ import { ROOT_MENU } from './constants'; +import type { NavigationContext as NavigationContextType } from './types'; + const noop = () => {}; +const defaultIsEmpty = () => false; +const defaultGetter = () => undefined; -export const NavigationContext = createContext( { +export const NavigationContext = createContext< NavigationContextType >( { activeItem: undefined, activeMenu: ROOT_MENU, setActiveMenu: noop, - isMenuEmpty: noop, navigationTree: { items: {}, - getItem: noop, + getItem: defaultGetter, addItem: noop, removeItem: noop, menus: {}, - getMenu: noop, + getMenu: defaultGetter, addMenu: noop, removeMenu: noop, childMenu: {}, traverseMenu: noop, - isMenuEmpty: noop, + isMenuEmpty: defaultIsEmpty, }, } ); export const useNavigationContext = () => useContext( NavigationContext ); diff --git a/packages/components/src/navigation/group/context.js b/packages/components/src/navigation/group/context.js deleted file mode 100644 index d6725504ba8f2c..00000000000000 --- a/packages/components/src/navigation/group/context.js +++ /dev/null @@ -1,9 +0,0 @@ -/** - * WordPress dependencies - */ -import { createContext, useContext } from '@wordpress/element'; - -export const NavigationGroupContext = createContext( { group: undefined } ); - -export const useNavigationGroupContext = () => - useContext( NavigationGroupContext ); diff --git a/packages/components/src/navigation/group/context.tsx b/packages/components/src/navigation/group/context.tsx new file mode 100644 index 00000000000000..822a0bb067c882 --- /dev/null +++ b/packages/components/src/navigation/group/context.tsx @@ -0,0 +1,15 @@ +/** + * WordPress dependencies + */ +import { createContext, useContext } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import type { NavigationGroupContext as NavigationGroupContextType } from '../types'; + +export const NavigationGroupContext = + createContext< NavigationGroupContextType >( { group: undefined } ); + +export const useNavigationGroupContext = () => + useContext( NavigationGroupContext ); diff --git a/packages/components/src/navigation/group/index.js b/packages/components/src/navigation/group/index.tsx similarity index 88% rename from packages/components/src/navigation/group/index.js rename to packages/components/src/navigation/group/index.tsx index 5ebebc2ed3b27f..cd91fb97365d26 100644 --- a/packages/components/src/navigation/group/index.js +++ b/packages/components/src/navigation/group/index.tsx @@ -15,9 +15,15 @@ import { NavigationGroupContext } from './context'; import { GroupTitleUI } from '../styles/navigation-styles'; import { useNavigationContext } from '../context'; +import type { NavigationGroupProps } from '../types'; + let uniqueId = 0; -export default function NavigationGroup( { children, className, title } ) { +export function NavigationGroup( { + children, + className, + title, +}: NavigationGroupProps ) { const [ groupId ] = useState( `group-${ ++uniqueId }` ); const { navigationTree: { items }, @@ -60,3 +66,5 @@ export default function NavigationGroup( { children, className, title } ) { ); } + +export default NavigationGroup; diff --git a/packages/components/src/navigation/index.js b/packages/components/src/navigation/index.tsx similarity index 52% rename from packages/components/src/navigation/index.js rename to packages/components/src/navigation/index.tsx index ab1f5cb5b58aa3..e3d309783e1ee9 100644 --- a/packages/components/src/navigation/index.js +++ b/packages/components/src/navigation/index.tsx @@ -18,21 +18,70 @@ import { NavigationContext } from './context'; import { NavigationUI } from './styles/navigation-styles'; import { useCreateNavigationTree } from './use-create-navigation-tree'; +import type { + NavigationProps, + NavigationContext as NavigationContextType, +} from './types'; + const noop = () => {}; -export default function Navigation( { +/** + * Render a navigation list with optional groupings and hierarchy. + * + * @example + * ```jsx + * import { + * __experimentalNavigation as Navigation, + * __experimentalNavigationGroup as NavigationGroup, + * __experimentalNavigationItem as NavigationItem, + * __experimentalNavigationMenu as NavigationMenu, + * } from '@wordpress/components'; + * + * const MyNavigation = () => ( + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * ); + * ``` + */ +export function Navigation( { activeItem, activeMenu = ROOT_MENU, children, className, onActivateMenu = noop, -} ) { +}: NavigationProps ) { const [ menu, setMenu ] = useState( activeMenu ); - const [ slideOrigin, setSlideOrigin ] = useState(); + const [ slideOrigin, setSlideOrigin ] = useState< 'left' | 'right' >(); const navigationTree = useCreateNavigationTree(); const defaultSlideOrigin = isRTL() ? 'right' : 'left'; - const setActiveMenu = ( menuId, slideInOrigin = defaultSlideOrigin ) => { + const setActiveMenu: NavigationContextType[ 'setActiveMenu' ] = ( + menuId, + slideInOrigin = defaultSlideOrigin + ) => { if ( ! navigationTree.getMenu( menuId ) ) { return; } @@ -76,9 +125,14 @@ export default function Navigation( {
{ children } @@ -87,3 +141,5 @@ export default function Navigation( { ); } + +export default Navigation; diff --git a/packages/components/src/navigation/item/base-content.js b/packages/components/src/navigation/item/base-content.tsx similarity index 73% rename from packages/components/src/navigation/item/base-content.js rename to packages/components/src/navigation/item/base-content.tsx index f4b6fc5b41a8a8..3bda9a71251698 100644 --- a/packages/components/src/navigation/item/base-content.js +++ b/packages/components/src/navigation/item/base-content.tsx @@ -3,7 +3,11 @@ */ import { ItemBadgeUI, ItemTitleUI } from '../styles/navigation-styles'; -export default function NavigationItemBaseContent( props ) { +import type { NavigationItemBaseContentProps } from '../types'; + +export default function NavigationItemBaseContent( + props: NavigationItemBaseContentProps +) { const { badge, title } = props; return ( @@ -11,7 +15,6 @@ export default function NavigationItemBaseContent( props ) { { title && ( { title } diff --git a/packages/components/src/navigation/item/base.js b/packages/components/src/navigation/item/base.tsx similarity index 87% rename from packages/components/src/navigation/item/base.js rename to packages/components/src/navigation/item/base.tsx index 1cbe80d466941b..e5d106bf71c1ac 100644 --- a/packages/components/src/navigation/item/base.js +++ b/packages/components/src/navigation/item/base.tsx @@ -15,9 +15,11 @@ import { useNavigationContext } from '../context'; import { useNavigationTreeItem } from './use-navigation-tree-item'; import { ItemBaseUI } from '../styles/navigation-styles'; +import type { NavigationItemBaseProps } from '../types'; + let uniqueId = 0; -export default function NavigationItemBase( props ) { +export default function NavigationItemBase( props: NavigationItemBaseProps ) { // Also avoid to pass the `title` and `href` props to the ItemBaseUI styled component. const { children, className, title, href, ...restProps } = props; diff --git a/packages/components/src/navigation/item/index.js b/packages/components/src/navigation/item/index.tsx similarity index 88% rename from packages/components/src/navigation/item/index.js rename to packages/components/src/navigation/item/index.tsx index 7f9369b5c9e2bd..2fd1aebefcbbac 100644 --- a/packages/components/src/navigation/item/index.js +++ b/packages/components/src/navigation/item/index.tsx @@ -18,9 +18,11 @@ import { ItemUI, ItemIconUI } from '../styles/navigation-styles'; import NavigationItemBaseContent from './base-content'; import NavigationItemBase from './base'; +import type { NavigationItemProps } from '../types'; + const noop = () => {}; -export default function NavigationItem( props ) { +export function NavigationItem( props: NavigationItemProps ) { const { badge, children, @@ -59,7 +61,9 @@ export default function NavigationItem( props ) { 'is-active': isActive, } ); - const onItemClick = ( event ) => { + const onItemClick: React.MouseEventHandler< + HTMLButtonElement | HTMLAnchorElement + > = ( event ) => { if ( navigateToMenu ) { setActiveMenu( navigateToMenu ); } @@ -99,3 +103,5 @@ export default function NavigationItem( props ) { ); } + +export default NavigationItem; diff --git a/packages/components/src/navigation/item/use-navigation-tree-item.js b/packages/components/src/navigation/item/use-navigation-tree-item.tsx similarity index 79% rename from packages/components/src/navigation/item/use-navigation-tree-item.js rename to packages/components/src/navigation/item/use-navigation-tree-item.tsx index 28db2e526e1b4a..e294af4169517e 100644 --- a/packages/components/src/navigation/item/use-navigation-tree-item.js +++ b/packages/components/src/navigation/item/use-navigation-tree-item.tsx @@ -11,7 +11,12 @@ import { useNavigationGroupContext } from '../group/context'; import { useNavigationMenuContext } from '../menu/context'; import { normalizedSearch } from '../utils'; -export const useNavigationTreeItem = ( itemId, props ) => { +import type { NavigationItemProps } from '../types'; + +export const useNavigationTreeItem = ( + itemId: string, + props: NavigationItemProps +) => { const { activeMenu, navigationTree: { addItem, removeItem }, @@ -22,7 +27,9 @@ export const useNavigationTreeItem = ( itemId, props ) => { useEffect( () => { const isMenuActive = activeMenu === menu; const isItemVisible = - ! search || normalizedSearch( props.title, search ); + ! search || + ( props.title !== undefined && + normalizedSearch( props.title, search ) ); addItem( itemId, { ...props, diff --git a/packages/components/src/navigation/menu/context.js b/packages/components/src/navigation/menu/context.js deleted file mode 100644 index 29b4814757c7cd..00000000000000 --- a/packages/components/src/navigation/menu/context.js +++ /dev/null @@ -1,11 +0,0 @@ -/** - * WordPress dependencies - */ -import { createContext, useContext } from '@wordpress/element'; - -export const NavigationMenuContext = createContext( { - menu: undefined, - search: '', -} ); -export const useNavigationMenuContext = () => - useContext( NavigationMenuContext ); diff --git a/packages/components/src/navigation/menu/context.tsx b/packages/components/src/navigation/menu/context.tsx new file mode 100644 index 00000000000000..53b71e4ccc368f --- /dev/null +++ b/packages/components/src/navigation/menu/context.tsx @@ -0,0 +1,18 @@ +/** + * WordPress dependencies + */ +import { createContext, useContext } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import type { NavigationMenuContext as NavigationMenuContextType } from '../types'; + +export const NavigationMenuContext = createContext< NavigationMenuContextType >( + { + menu: undefined, + search: '', + } +); +export const useNavigationMenuContext = () => + useContext( NavigationMenuContext ); diff --git a/packages/components/src/navigation/menu/index.js b/packages/components/src/navigation/menu/index.tsx similarity index 94% rename from packages/components/src/navigation/menu/index.js rename to packages/components/src/navigation/menu/index.tsx index 2e3aa30db0de8e..b001bd0fa0f3d8 100644 --- a/packages/components/src/navigation/menu/index.js +++ b/packages/components/src/navigation/menu/index.tsx @@ -21,7 +21,9 @@ import NavigationSearchNoResultsFound from './search-no-results-found'; import { NavigableMenu } from '../../navigable-container'; import { MenuUI } from '../styles/navigation-styles'; -export default function NavigationMenu( props ) { +import type { NavigationMenuProps } from '../types'; + +export function NavigationMenu( props: NavigationMenuProps ) { const { backButtonLabel, children, @@ -96,3 +98,5 @@ export default function NavigationMenu( props ) { ); } + +export default NavigationMenu; diff --git a/packages/components/src/navigation/menu/menu-title-search.js b/packages/components/src/navigation/menu/menu-title-search.tsx similarity index 86% rename from packages/components/src/navigation/menu/menu-title-search.js rename to packages/components/src/navigation/menu/menu-title-search.tsx index cba2ddf2ffe308..d03766f28d7522 100644 --- a/packages/components/src/navigation/menu/menu-title-search.js +++ b/packages/components/src/navigation/menu/menu-title-search.tsx @@ -13,24 +13,26 @@ import { useNavigationContext } from '../context'; import { MenuTitleSearchUI } from '../styles/navigation-styles'; import { SEARCH_FOCUS_DELAY } from '../constants'; +import type { NavigationMenuTitleSearchProps } from '../types'; + function MenuTitleSearch( { debouncedSpeak, onCloseSearch, onSearch, search, title, -} ) { +}: NavigationMenuTitleSearchProps ) { const { navigationTree: { items }, } = useNavigationContext(); const { menu } = useNavigationMenuContext(); - const inputRef = useRef(); + const inputRef = useRef< HTMLInputElement >( null ); // Wait for the slide-in animation to complete before autofocusing the input. // This prevents scrolling to the input during the animation. useEffect( () => { const delayedFocus = setTimeout( () => { - inputRef.current.focus(); + inputRef.current?.focus(); }, SEARCH_FOCUS_DELAY ); return () => { @@ -57,16 +59,18 @@ function MenuTitleSearch( { }, [ items, search ] ); const onClose = () => { - onSearch( '' ); + onSearch?.( '' ); onCloseSearch(); }; - function onKeyDown( event ) { + const onKeyDown: React.KeyboardEventHandler< HTMLInputElement > = ( + event + ) => { if ( event.code === 'Escape' && ! event.defaultPrevented ) { event.preventDefault(); onClose(); } - } + }; const inputId = `components-navigation__menu-title-search-${ menu }`; const placeholder = sprintf( @@ -81,7 +85,7 @@ function MenuTitleSearch( { autoComplete="off" className="components-navigation__menu-search-input" id={ inputId } - onChange={ ( value ) => onSearch( value ) } + onChange={ ( value ) => onSearch?.( value ) } onKeyDown={ onKeyDown } placeholder={ placeholder } onClose={ onClose } diff --git a/packages/components/src/navigation/menu/menu-title.js b/packages/components/src/navigation/menu/menu-title.tsx similarity index 92% rename from packages/components/src/navigation/menu/menu-title.js rename to packages/components/src/navigation/menu/menu-title.tsx index 7dcc0a02084902..8a9c7fcd59963c 100644 --- a/packages/components/src/navigation/menu/menu-title.js +++ b/packages/components/src/navigation/menu/menu-title.tsx @@ -19,16 +19,18 @@ import { import { useNavigationMenuContext } from './context'; import { SEARCH_FOCUS_DELAY } from '../constants'; +import type { NavigationMenuTitleProps } from '../types'; + export default function NavigationMenuTitle( { hasSearch, onSearch, search, title, titleAction, -} ) { +}: NavigationMenuTitleProps ) { const [ isSearching, setIsSearching ] = useState( false ); const { menu } = useNavigationMenuContext(); - const searchButtonRef = useRef(); + const searchButtonRef = useRef< HTMLElement >( null ); if ( ! title ) { return null; @@ -40,7 +42,7 @@ export default function NavigationMenuTitle( { // Wait for the slide-in animation to complete before focusing the search button. // eslint-disable-next-line @wordpress/react-no-unsafe-timeout setTimeout( () => { - searchButtonRef.current.focus(); + searchButtonRef.current?.focus(); }, SEARCH_FOCUS_DELAY ); }; diff --git a/packages/components/src/navigation/menu/search-no-results-found.js b/packages/components/src/navigation/menu/search-no-results-found.tsx similarity index 75% rename from packages/components/src/navigation/menu/search-no-results-found.js rename to packages/components/src/navigation/menu/search-no-results-found.tsx index 1297ff1656db9b..c239f74ce8173a 100644 --- a/packages/components/src/navigation/menu/search-no-results-found.js +++ b/packages/components/src/navigation/menu/search-no-results-found.tsx @@ -9,7 +9,11 @@ import { __ } from '@wordpress/i18n'; import { useNavigationContext } from '../context'; import { ItemBaseUI, ItemUI } from '../styles/navigation-styles'; -export default function NavigationSearchNoResultsFound( { search } ) { +import type { NavigationSearchNoResultsFoundProps } from '../types'; + +export default function NavigationSearchNoResultsFound( { + search, +}: NavigationSearchNoResultsFoundProps ) { const { navigationTree: { items }, } = useNavigationContext(); diff --git a/packages/components/src/navigation/menu/use-navigation-tree-menu.js b/packages/components/src/navigation/menu/use-navigation-tree-menu.tsx similarity index 82% rename from packages/components/src/navigation/menu/use-navigation-tree-menu.js rename to packages/components/src/navigation/menu/use-navigation-tree-menu.tsx index f50f25370f9380..1db2ee87fa7847 100644 --- a/packages/components/src/navigation/menu/use-navigation-tree-menu.js +++ b/packages/components/src/navigation/menu/use-navigation-tree-menu.tsx @@ -9,7 +9,9 @@ import { useEffect } from '@wordpress/element'; import { useNavigationContext } from '../context'; import { ROOT_MENU } from '../constants'; -export const useNavigationTreeMenu = ( props ) => { +import type { NavigationMenuProps } from '../types'; + +export const useNavigationTreeMenu = ( props: NavigationMenuProps ) => { const { navigationTree: { addMenu, removeMenu }, } = useNavigationContext(); diff --git a/packages/components/src/navigation/stories/index.js b/packages/components/src/navigation/stories/index.js deleted file mode 100644 index 8fbdde44fe7dff..00000000000000 --- a/packages/components/src/navigation/stories/index.js +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Internal dependencies - */ -import Navigation from '..'; -import NavigationBackButton from '../back-button'; -import NavigationGroup from '../group'; -import NavigationItem from '../item'; -import NavigationMenu from '../menu'; -import { DefaultStory } from './utils/default'; -import { GroupStory } from './utils/group'; -import { ControlledStateStory } from './utils/controlled-state'; -import { SearchStory } from './utils/search'; -import { MoreExamplesStory } from './utils/more-examples'; -import { HideIfEmptyStory } from './utils/hide-if-empty'; -import './style.css'; - -export default { - title: 'Components (Experimental)/Navigation', - component: Navigation, - subcomponents: { - NavigationBackButton, - NavigationGroup, - NavigationItem, - NavigationMenu, - }, -}; - -export const _default = () => ; -export const controlledState = () => ; -export const groups = () => ; -export const search = () => ; -export const moreExamples = () => ; -export const hideIfEmpty = () => ; diff --git a/packages/components/src/navigation/stories/index.tsx b/packages/components/src/navigation/stories/index.tsx new file mode 100644 index 00000000000000..2605de6f296673 --- /dev/null +++ b/packages/components/src/navigation/stories/index.tsx @@ -0,0 +1,53 @@ +/** + * External dependencies + */ +import type { ComponentMeta } from '@storybook/react'; + +/** + * Internal dependencies + */ +import { Navigation } from '..'; +import { NavigationBackButton } from '../back-button'; +import { NavigationGroup } from '../group'; +import { NavigationItem } from '../item'; +import { NavigationMenu } from '../menu'; +import { DefaultStory } from './utils/default'; +import { GroupStory } from './utils/group'; +import { ControlledStateStory } from './utils/controlled-state'; +import { SearchStory } from './utils/search'; +import { MoreExamplesStory } from './utils/more-examples'; +import { HideIfEmptyStory } from './utils/hide-if-empty'; +import './style.css'; + +const meta: ComponentMeta< typeof Navigation > = { + title: 'Components (Experimental)/Navigation', + component: Navigation, + subcomponents: { + NavigationBackButton, + NavigationGroup, + NavigationItem, + NavigationMenu, + }, + argTypes: { + activeItem: { control: { type: null } }, + activeMenu: { control: { type: null } }, + children: { control: { type: null } }, + onActivateMenu: { control: { type: null } }, + }, + parameters: { + actions: { argTypesRegex: '^on.*' }, + controls: { + expanded: true, + }, + docs: { source: { state: 'open' } }, + }, +}; + +export default meta; + +export const _default = DefaultStory.bind( {} ); +export const controlledState = ControlledStateStory.bind( {} ); +export const groups = GroupStory.bind( {} ); +export const search = SearchStory.bind( {} ); +export const moreExamples = MoreExamplesStory.bind( {} ); +export const hideIfEmpty = HideIfEmptyStory.bind( {} ); diff --git a/packages/components/src/navigation/stories/utils/controlled-state.js b/packages/components/src/navigation/stories/utils/controlled-state.tsx similarity index 75% rename from packages/components/src/navigation/stories/utils/controlled-state.js rename to packages/components/src/navigation/stories/utils/controlled-state.tsx index ed7d652d0df778..fa9687c981f2ee 100644 --- a/packages/components/src/navigation/stories/utils/controlled-state.js +++ b/packages/components/src/navigation/stories/utils/controlled-state.tsx @@ -1,30 +1,46 @@ +/** + * External dependencies + */ +import type { ComponentStory } from '@storybook/react'; + /** * WordPress dependencies */ -import { Button } from '@wordpress/components'; import { useState } from '@wordpress/element'; /** * Internal dependencies */ -import Navigation from '../..'; -import NavigationItem from '../../item'; -import NavigationMenu from '../../menu'; +import Button from '../../../button'; +import { Navigation } from '../..'; +import { NavigationItem } from '../../item'; +import { NavigationMenu } from '../../menu'; -export function ControlledStateStory() { +export const ControlledStateStory: ComponentStory< typeof Navigation > = ( { + className, + ...props +} ) => { const [ activeItem, setActiveItem ] = useState( 'item-1' ); const [ activeMenu, setActiveMenu ] = useState( 'root' ); // Mock navigation link. - const MockLink = ( { href, children } ) => ( + const MockLink = ( { + href, + children, + }: { + href: string; + children: React.ReactNode; + } ) => ( @@ -33,9 +49,12 @@ export function ControlledStateStory() { return ( <> @@ -124,4 +143,4 @@ export function ControlledStateStory() {
); -} +}; diff --git a/packages/components/src/navigation/stories/utils/default.js b/packages/components/src/navigation/stories/utils/default.tsx similarity index 77% rename from packages/components/src/navigation/stories/utils/default.js rename to packages/components/src/navigation/stories/utils/default.tsx index 569cb8aa17b4bc..78188802861d36 100644 --- a/packages/components/src/navigation/stories/utils/default.js +++ b/packages/components/src/navigation/stories/utils/default.tsx @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import type { ComponentStory } from '@storybook/react'; + /** * WordPress dependencies */ @@ -6,15 +11,24 @@ import { useState } from '@wordpress/element'; /** * Internal dependencies */ -import Navigation from '../..'; -import NavigationItem from '../../item'; -import NavigationMenu from '../../menu'; +import { Navigation } from '../..'; +import { NavigationItem } from '../../item'; +import { NavigationMenu } from '../../menu'; -export function DefaultStory() { +export const DefaultStory: ComponentStory< typeof Navigation > = ( { + className, + ...props +} ) => { const [ activeItem, setActiveItem ] = useState( 'item-1' ); return ( - + ); -} +}; diff --git a/packages/components/src/navigation/stories/utils/group.js b/packages/components/src/navigation/stories/utils/group.tsx similarity index 62% rename from packages/components/src/navigation/stories/utils/group.js rename to packages/components/src/navigation/stories/utils/group.tsx index 7e11071a63b9a4..007a71e3f40d16 100644 --- a/packages/components/src/navigation/stories/utils/group.js +++ b/packages/components/src/navigation/stories/utils/group.tsx @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import type { ComponentStory } from '@storybook/react'; + /** * WordPress dependencies */ @@ -6,16 +11,25 @@ import { useState } from '@wordpress/element'; /** * Internal dependencies */ -import Navigation from '../..'; -import NavigationItem from '../../item'; -import NavigationMenu from '../../menu'; -import NavigationGroup from '../../group'; +import { Navigation } from '../..'; +import { NavigationItem } from '../../item'; +import { NavigationMenu } from '../../menu'; +import { NavigationGroup } from '../../group'; -export function GroupStory() { +export const GroupStory: ComponentStory< typeof Navigation > = ( { + className, + ...props +} ) => { const [ activeItem, setActiveItem ] = useState( 'item-1' ); return ( - + ); -} +}; diff --git a/packages/components/src/navigation/stories/utils/hide-if-empty.js b/packages/components/src/navigation/stories/utils/hide-if-empty.tsx similarity index 69% rename from packages/components/src/navigation/stories/utils/hide-if-empty.js rename to packages/components/src/navigation/stories/utils/hide-if-empty.tsx index ef9e889932a895..2595aba6b26a7b 100644 --- a/packages/components/src/navigation/stories/utils/hide-if-empty.js +++ b/packages/components/src/navigation/stories/utils/hide-if-empty.tsx @@ -1,14 +1,27 @@ +/** + * External dependencies + */ +import type { ComponentStory } from '@storybook/react'; + /** * Internal dependencies */ -import Navigation from '../..'; -import NavigationItem from '../../item'; -import NavigationMenu from '../../menu'; +import { Navigation } from '../..'; +import { NavigationItem } from '../../item'; +import { NavigationMenu } from '../../menu'; -export function HideIfEmptyStory() { +export const HideIfEmptyStory: ComponentStory< typeof Navigation > = ( { + className, + ...props +} ) => { return ( <> - + ); -} +}; diff --git a/packages/components/src/navigation/stories/utils/more-examples.js b/packages/components/src/navigation/stories/utils/more-examples.tsx similarity index 81% rename from packages/components/src/navigation/stories/utils/more-examples.js rename to packages/components/src/navigation/stories/utils/more-examples.tsx index 43bd33835db370..c2a181dce58f69 100644 --- a/packages/components/src/navigation/stories/utils/more-examples.js +++ b/packages/components/src/navigation/stories/utils/more-examples.tsx @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import type { ComponentStory } from '@storybook/react'; + /** * WordPress dependencies */ @@ -7,14 +12,17 @@ import { Icon, wordpress, home } from '@wordpress/icons'; /** * Internal dependencies */ -import Navigation from '../..'; -import NavigationGroup from '../../group'; -import NavigationItem from '../../item'; -import NavigationMenu from '../../menu'; +import { Navigation } from '../..'; +import { NavigationGroup } from '../../group'; +import { NavigationItem } from '../../item'; +import { NavigationMenu } from '../../menu'; -export function MoreExamplesStory() { +export const MoreExamplesStory: ComponentStory< typeof Navigation > = ( { + className, + ...props +} ) => { const [ activeItem, setActiveItem ] = useState( 'child-1' ); - const [ delayedBadge, setDelayedBadge ] = useState(); + const [ delayedBadge, setDelayedBadge ] = useState< number | undefined >(); useEffect( () => { const timeout = setTimeout( () => setDelayedBadge( 2 ), 1500 ); return () => clearTimeout( timeout ); @@ -24,7 +32,13 @@ export function MoreExamplesStory() { useState( 1 ); return ( - + @@ -126,10 +140,14 @@ export function MoreExamplesStory() { menu="custom-back-click-handler-prevented-menu" title="Custom back button click handler prevented" parentMenu="root" - onBackButtonClick={ ( event ) => { - event.preventDefault(); - setBackButtonPreventedBadge( backButtonPreventedBadge + 1 ); - } } + onBackButtonClick={ + ( ( event ) => { + event.preventDefault(); + setBackButtonPreventedBadge( + backButtonPreventedBadge + 1 + ); + } ) as React.MouseEventHandler + } backButtonLabel="Increment badge" > ); -} +}; diff --git a/packages/components/src/navigation/stories/utils/search.js b/packages/components/src/navigation/stories/utils/search.tsx similarity index 77% rename from packages/components/src/navigation/stories/utils/search.js rename to packages/components/src/navigation/stories/utils/search.tsx index 6fb1a176970233..74f1457adf0b70 100644 --- a/packages/components/src/navigation/stories/utils/search.js +++ b/packages/components/src/navigation/stories/utils/search.tsx @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import type { ComponentStory } from '@storybook/react'; + /** * WordPress dependencies */ @@ -6,10 +11,10 @@ import { useState } from '@wordpress/element'; /** * Internal dependencies */ -import Navigation from '../..'; -import NavigationGroup from '../../group'; -import NavigationItem from '../../item'; -import NavigationMenu from '../../menu'; +import { Navigation } from '../..'; +import { NavigationGroup } from '../../group'; +import { NavigationItem } from '../../item'; +import { NavigationMenu } from '../../menu'; import { normalizedSearch } from '../../utils'; const searchItems = [ @@ -24,12 +29,21 @@ const searchItems = [ { item: 'waldo', title: 'Waldo' }, ]; -export function SearchStory() { +export const SearchStory: ComponentStory< typeof Navigation > = ( { + className, + ...props +} ) => { const [ activeItem, setActiveItem ] = useState( 'item-1' ); const [ search, setSearch ] = useState( '' ); return ( - + { searchItems.map( ( { item, title } ) => ( @@ -74,4 +88,4 @@ export function SearchStory() { ); -} +}; diff --git a/packages/components/src/navigation/styles/navigation-styles.js b/packages/components/src/navigation/styles/navigation-styles.tsx similarity index 100% rename from packages/components/src/navigation/styles/navigation-styles.js rename to packages/components/src/navigation/styles/navigation-styles.tsx diff --git a/packages/components/src/navigation/test/index.js b/packages/components/src/navigation/test/index.tsx similarity index 95% rename from packages/components/src/navigation/test/index.js rename to packages/components/src/navigation/test/index.tsx index 7a8d7dec67c77f..20646a6c809bfc 100644 --- a/packages/components/src/navigation/test/index.js +++ b/packages/components/src/navigation/test/index.tsx @@ -16,11 +16,19 @@ import Navigation from '..'; import NavigationItem from '../item'; import NavigationMenu from '../menu'; -const TestNavigation = ( { activeItem, rootTitle, showBadge } = {} ) => ( +const TestNavigation = ( { + activeItem, + rootTitle, + showBadge, +}: { + activeItem?: string; + rootTitle?: string; + showBadge?: boolean; +} ) => ( @@ -55,9 +63,14 @@ const TestNavigationControlled = () => { const [ activeItem, setActiveItem ] = useState( 'item-1' ); const [ activeMenu, setActiveMenu ] = useState( 'root' ); - const onMockLinkClick = ( event ) => { + const onMockLinkClick: React.MouseEventHandler< HTMLAnchorElement > = ( + event + ) => { event.preventDefault(); - const item = event.target.href.replace( 'https://example.com/', '' ); + const item = ( event.target as HTMLAnchorElement ).href.replace( + 'https://example.com/', + '' + ); setActiveItem( item ); }; diff --git a/packages/components/src/navigation/types.ts b/packages/components/src/navigation/types.ts new file mode 100644 index 00000000000000..4dbc9f5236cc33 --- /dev/null +++ b/packages/components/src/navigation/types.ts @@ -0,0 +1,325 @@ +/** + * Internal dependencies + */ +import type { ButtonProps } from '../button/types'; + +type IfDiscriminantDefinedBothRequired< TDiscriminant, TOther > = + // When props in TDiscriminant are specified, + // then props from TOther are required too + | { + [ K in keyof ( TDiscriminant & TOther ) ]: NonNullable< + ( TDiscriminant & TOther )[ K ] + >; + } + // When props in TDiscriminant are not specified, + // then props from TOther are optional + | ( { + [ K in keyof TDiscriminant ]?: never; + } & { + [ K in keyof TOther ]?: TOther[ K ]; + } ); + +// React Components + +export type NavigationProps = { + /** + * The active item slug. + */ + activeItem?: string; + /** + * The active menu slug. + * + * @default 'root' + */ + activeMenu?: string; + /** + * The children components. + */ + children?: React.ReactNode; + /** + * Optional classname for the component. + */ + className?: string; + /** + * Callback used to sync the active menu between the external state + * and the Navigation's internal state. + */ + onActivateMenu?: ( activeMenuSlug: string ) => void; +}; + +// When `onSearch` is specified, `search` should be specified too +type _NavigationMenuSearchProps = IfDiscriminantDefinedBothRequired< + { + /** + * When the `hasSearch` prop is `true`, this callback handles the search + * input's `onChange` event, making it controlled from the outside. + * When using this prop, the `search` prop should be also set. + */ + onSearch: ( searchString: string ) => void; + }, + { + /** + * When the `hasSearch` is `true` and the `onSearch` prop is provided, this + * prop controls the value of the search input. + * Required when the `onSearch` prop is provided. + */ + search: string; + } +>; + +export type NavigationMenuProps = _NavigationMenuSearchProps & { + /** + * The back button label used in nested menus. + * If not provided, the label will be inferred from the parent menu's title. + * If the parent menu's title is not available then it will default to "Back". + * + * @default parentMenuTitle ?? 'Back' + */ + backButtonLabel?: string; + /** + * A callback to handle clicking on the back button. + * If this prop is provided then the back button will be shown. + */ + onBackButtonClick?: React.MouseEventHandler< HTMLElement >; + /** + * The children components. + */ + children?: React.ReactNode; + /** + * Optional classname for the component. + */ + className?: string; + /** + * When `true`, enables the search feature on the menu title. + */ + hasSearch?: boolean; + /** + * Indicates whether the menu is empty or not. Used together with the + * `hideIfTargetMenuEmpty` prop of `NavigationItem`. + */ + isEmpty?: boolean; + /** + * Indicates whether the search is debouncing or not. In case of `true`, the + * "No results found." text is omitted. Used to prevent showing the + * "No results found." text between debounced searches. + */ + isSearchDebouncing?: boolean; + /** + * The unique identifier of the menu. + * The root menu can omit this prop, and it will default to "root". + * All other menus need to specify it. + * + * @default 'root' + */ + menu?: string; + /** + * The parent menu slug; used by nested menus to indicate their parent menu. + */ + parentMenu?: string; + /** + * The menu title. It's also the field used by the menu search function. + */ + title?: string; + /** + * Use this prop to render additional actions in the menu title. + */ + titleAction?: React.ReactNode; +}; + +export type NavigationGroupProps = { + /** + * Optional classname for the component. + */ + className?: string; + /** + * The group title. + */ + title?: string; + /** + * The children components. + */ + children: React.ReactNode; +}; + +type _NavigationItemButtonProps = + | ( ButtonProps & { + /** + * If set to `true` the menu item will only act as a text-only item, + * rather than a ` - { showLabels &&
{ siteTitle }
} + { showLabels && ( +
+ { siteTitle } +
+ ) } ); diff --git a/packages/edit-site/src/components/site-hub/style.scss b/packages/edit-site/src/components/site-hub/style.scss index afe41baae7f829..885c24e38b7884 100644 --- a/packages/edit-site/src/components/site-hub/style.scss +++ b/packages/edit-site/src/components/site-hub/style.scss @@ -26,3 +26,7 @@ white-space: nowrap; overflow: hidden; } + +.edit-site-site-hub__site-title { + margin-left: $grid-unit-05; +} From c6d69e171303e32c35942a8feb007140624c0305 Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Tue, 7 Mar 2023 17:19:56 -0700 Subject: [PATCH 107/910] HTML API: Backport updates from Core --- .../html-api/class-wp-html-tag-processor.php | 29 ++++++++++--------- ...class-gutenberg-html-tag-processor-6-3.php | 29 ++++++++++--------- 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/lib/compat/wordpress-6.2/html-api/class-wp-html-tag-processor.php b/lib/compat/wordpress-6.2/html-api/class-wp-html-tag-processor.php index 044d1f0c366587..5a9221fc9e3dbf 100644 --- a/lib/compat/wordpress-6.2/html-api/class-wp-html-tag-processor.php +++ b/lib/compat/wordpress-6.2/html-api/class-wp-html-tag-processor.php @@ -40,7 +40,7 @@ * Example: * ```php * $tags = new WP_HTML_Tag_Processor( $html ); - * if ( $tags->next_tag( array( 'tag_name' => 'option' ) ) ) { + * if ( $tags->next_tag( 'option' ) ) { * $tags->set_attribute( 'selected', true ); * } * ``` @@ -58,10 +58,11 @@ * $tags->next_tag(); * ``` * - * | Goal | Query | - * |-----------------------------------------------------------|----------------------------------------------------------------------------| - * | Find any tag. | `$tags->next_tag();` | + * | Goal | Query | + * |-----------------------------------------------------------|---------------------------------------------------------------------------------| + * | Find any tag. | `$tags->next_tag();` | * | Find next image tag. | `$tags->next_tag( array( 'tag_name' => 'img' ) );` | + * | Find next image tag (without passing the array). | `$tags->next_tag( 'img' );` | * | Find next tag containing the `fullwidth` CSS class. | `$tags->next_tag( array( 'class_name' => 'fullwidth' ) );` | * | Find next image tag containing the `fullwidth` CSS class. | `$tags->next_tag( array( 'tag_name' => 'img', 'class_name' => 'fullwidth' ) );` | * @@ -775,7 +776,8 @@ private function skip_rcdata( $tag_name ) { return false; } - $at += 2; + $closer_potentially_starts_at = $at; + $at += 2; /* * Find a case-insensitive match to the tag name. @@ -818,7 +820,7 @@ private function skip_rcdata( $tag_name ) { } if ( '>' === $html[ $at ] || '/' === $html[ $at ] ) { - ++$this->bytes_already_parsed; + $this->bytes_already_parsed = $closer_potentially_starts_at; return true; } } @@ -887,7 +889,8 @@ private function skip_script_data() { } if ( '/' === $html[ $at ] ) { - $is_closing = true; + $closer_potentially_starts_at = $at - 1; + $is_closing = true; ++$at; } else { $is_closing = false; @@ -938,7 +941,7 @@ private function skip_script_data() { } if ( $is_closing ) { - $this->bytes_already_parsed = $at; + $this->bytes_already_parsed = $closer_potentially_starts_at; if ( $this->bytes_already_parsed >= $doc_length ) { return false; } @@ -948,7 +951,7 @@ private function skip_script_data() { } if ( '>' === $html[ $this->bytes_already_parsed ] ) { - ++$this->bytes_already_parsed; + $this->bytes_already_parsed = $closer_potentially_starts_at; return true; } } @@ -1608,7 +1611,7 @@ private function get_enqueued_attribute_value( $comparable_name ) { * $p->get_attribute( 'enabled' ) === true; * $p->get_attribute( 'aria-label' ) === null; * - * $p->next_tag( array() ) === false; + * $p->next_tag() === false; * $p->get_attribute( 'class' ) === null; * ``` * @@ -1689,7 +1692,7 @@ public function get_attribute( $name ) { * $p->next_tag( array( 'class_name' => 'test' ) ) === true; * $p->get_attribute_names_with_prefix( 'data-' ) === array( 'data-enabled', 'data-test-id' ); * - * $p->next_tag( array() ) === false; + * $p->next_tag() === false; * $p->get_attribute_names_with_prefix( 'data-' ) === null; * ``` * @@ -1720,10 +1723,10 @@ function get_attribute_names_with_prefix( $prefix ) { * Example: * ```php * $p = new WP_HTML_Tag_Processor( '
Test
' ); - * $p->next_tag( array() ) === true; + * $p->next_tag() === true; * $p->get_tag() === 'DIV'; * - * $p->next_tag( array() ) === false; + * $p->next_tag() === false; * $p->get_tag() === null; * ``` * diff --git a/lib/compat/wordpress-6.3/html-api/class-gutenberg-html-tag-processor-6-3.php b/lib/compat/wordpress-6.3/html-api/class-gutenberg-html-tag-processor-6-3.php index feebe3fae13c1e..bc947e0be41df3 100644 --- a/lib/compat/wordpress-6.3/html-api/class-gutenberg-html-tag-processor-6-3.php +++ b/lib/compat/wordpress-6.3/html-api/class-gutenberg-html-tag-processor-6-3.php @@ -40,7 +40,7 @@ * Example: * ```php * $tags = new WP_HTML_Tag_Processor( $html ); - * if ( $tags->next_tag( array( 'tag_name' => 'option' ) ) ) { + * if ( $tags->next_tag( 'option' ) ) { * $tags->set_attribute( 'selected', true ); * } * ``` @@ -58,10 +58,11 @@ * $tags->next_tag(); * ``` * - * | Goal | Query | - * |-----------------------------------------------------------|----------------------------------------------------------------------------| - * | Find any tag. | `$tags->next_tag();` | + * | Goal | Query | + * |-----------------------------------------------------------|---------------------------------------------------------------------------------| + * | Find any tag. | `$tags->next_tag();` | * | Find next image tag. | `$tags->next_tag( array( 'tag_name' => 'img' ) );` | + * | Find next image tag (without passing the array). | `$tags->next_tag( 'img' );` | * | Find next tag containing the `fullwidth` CSS class. | `$tags->next_tag( array( 'class_name' => 'fullwidth' ) );` | * | Find next image tag containing the `fullwidth` CSS class. | `$tags->next_tag( array( 'tag_name' => 'img', 'class_name' => 'fullwidth' ) );` | * @@ -775,7 +776,8 @@ private function skip_rcdata( $tag_name ) { return false; } - $at += 2; + $closer_potentially_starts_at = $at; + $at += 2; /* * Find a case-insensitive match to the tag name. @@ -818,7 +820,7 @@ private function skip_rcdata( $tag_name ) { } if ( '>' === $html[ $at ] || '/' === $html[ $at ] ) { - ++$this->bytes_already_parsed; + $this->bytes_already_parsed = $closer_potentially_starts_at; return true; } } @@ -887,7 +889,8 @@ private function skip_script_data() { } if ( '/' === $html[ $at ] ) { - $is_closing = true; + $closer_potentially_starts_at = $at - 1; + $is_closing = true; ++$at; } else { $is_closing = false; @@ -938,7 +941,7 @@ private function skip_script_data() { } if ( $is_closing ) { - $this->bytes_already_parsed = $at; + $this->bytes_already_parsed = $closer_potentially_starts_at; if ( $this->bytes_already_parsed >= $doc_length ) { return false; } @@ -948,7 +951,7 @@ private function skip_script_data() { } if ( '>' === $html[ $this->bytes_already_parsed ] ) { - ++$this->bytes_already_parsed; + $this->bytes_already_parsed = $closer_potentially_starts_at; return true; } } @@ -1623,7 +1626,7 @@ private function get_enqueued_attribute_value( $comparable_name ) { * $p->get_attribute( 'enabled' ) === true; * $p->get_attribute( 'aria-label' ) === null; * - * $p->next_tag( array() ) === false; + * $p->next_tag() === false; * $p->get_attribute( 'class' ) === null; * ``` * @@ -1704,7 +1707,7 @@ public function get_attribute( $name ) { * $p->next_tag( array( 'class_name' => 'test' ) ) === true; * $p->get_attribute_names_with_prefix( 'data-' ) === array( 'data-enabled', 'data-test-id' ); * - * $p->next_tag( array() ) === false; + * $p->next_tag() === false; * $p->get_attribute_names_with_prefix( 'data-' ) === null; * ``` * @@ -1735,10 +1738,10 @@ function get_attribute_names_with_prefix( $prefix ) { * Example: * ```php * $p = new WP_HTML_Tag_Processor( '
Test
' ); - * $p->next_tag( array() ) === true; + * $p->next_tag() === true; * $p->get_tag() === 'DIV'; * - * $p->next_tag( array() ) === false; + * $p->next_tag() === false; * $p->get_tag() === null; * ``` * From f41791277e7f6c7ac242c8b9f917f2ecdd626493 Mon Sep 17 00:00:00 2001 From: Rich Tabor Date: Thu, 9 Mar 2023 12:31:55 -0500 Subject: [PATCH 108/910] Fix settings tab active state border in block inspector (#48945) * Update style.scss * Try margin on panel body instead * Add comment --- .../block-editor/src/components/block-inspector/style.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/block-editor/src/components/block-inspector/style.scss b/packages/block-editor/src/components/block-inspector/style.scss index 3dd4744fb87e2a..8e114089ad31f9 100644 --- a/packages/block-editor/src/components/block-inspector/style.scss +++ b/packages/block-editor/src/components/block-inspector/style.scss @@ -27,6 +27,9 @@ .components-panel__body { border: none; border-top: $border-width solid $gray-200; + + // Ensures this PanelBody is treated like the ToolsPanel, removing double borders. + margin-top: -$border-width; } .block-editor-block-card { From 00cfd4baa4efdfb7bf50255e2d4088dcbafa8667 Mon Sep 17 00:00:00 2001 From: Anver Sadutt Date: Fri, 10 Mar 2023 00:29:59 +0530 Subject: [PATCH 109/910] Env: Fix typo / grammar README.md (#48952) * Update README.md * Update packages/env/README.md --------- Co-authored-by: George Mamadashvili --- packages/env/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/env/README.md b/packages/env/README.md index 4118e8e00ab7ed..efc4172c708ede 100644 --- a/packages/env/README.md +++ b/packages/env/README.md @@ -119,7 +119,7 @@ $ WP_ENV_PORT=3333 wp-env start Running `docker ps` and inspecting the `PORTS` column allows you to determine which port `wp-env` is currently using. -You may also specify the port numbers in your `.wp-env.json` file, but the environment variables take precedent. +You may also specify the port numbers in your `.wp-env.json` file, but the environment variables will take precedence. ### 3. Restart `wp-env` with updates From 3f20fdc09293020ab350ca8e95621ce06b092acf Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Thu, 9 Mar 2023 23:57:42 +0400 Subject: [PATCH 110/910] Inserter: Remove outer scope values dependencies (#48961) --- packages/block-editor/src/components/inserter/tabs.js | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/block-editor/src/components/inserter/tabs.js b/packages/block-editor/src/components/inserter/tabs.js index 8a80e96cb78cc4..6f8377892059b5 100644 --- a/packages/block-editor/src/components/inserter/tabs.js +++ b/packages/block-editor/src/components/inserter/tabs.js @@ -52,15 +52,7 @@ function InserterTabs( { tempTabs.push( reusableBlocksTab ); } return tempTabs; - }, [ - prioritizePatterns, - blocksTab, - showPatterns, - patternsTab, - showReusableBlocks, - showMedia, - reusableBlocksTab, - ] ); + }, [ prioritizePatterns, showPatterns, showReusableBlocks, showMedia ] ); return ( Date: Fri, 10 Mar 2023 17:14:21 +0900 Subject: [PATCH 111/910] withFilters: Convert to TypeScript (#48721) * withFilters: Convert to TypeScript * Fixup * Add changelog * Convert tests * Fix import error on CI * Remove obsolete snapshot file --- packages/components/CHANGELOG.md | 1 + .../with-filters/{index.js => index.tsx} | 56 +++++++++++++++---- .../{index.js.snap => index.tsx.snap} | 0 .../with-filters/test/{index.js => index.tsx} | 6 +- packages/components/tsconfig.json | 1 - 5 files changed, 50 insertions(+), 14 deletions(-) rename packages/components/src/higher-order/with-filters/{index.js => index.tsx} (69%) rename packages/components/src/higher-order/with-filters/test/__snapshots__/{index.js.snap => index.tsx.snap} (100%) rename packages/components/src/higher-order/with-filters/test/{index.js => index.tsx} (94%) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 90df12abba3cf3..d672fdb7356c38 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -17,6 +17,7 @@ - `SelectControl`: improve prop types for single vs multiple selection ([#47390](https://github.com/WordPress/gutenberg/pull/47390)). - `Navigation`: Convert to TypeScript ([#48742](https://github.com/WordPress/gutenberg/pull/48742)). - `PanelBody`: Convert to TypeScript ([#47702](https://github.com/WordPress/gutenberg/pull/47702)). +- `withFilters` HOC: Convert to TypeScript ([#48721](https://github.com/WordPress/gutenberg/pull/48721)). - `withFallbackStyles` HOC: Convert to TypeScript ([#48720](https://github.com/WordPress/gutenberg/pull/48720)). - `navigateRegions` HOC: Convert to TypeScript ([#48632](https://github.com/WordPress/gutenberg/pull/48632)). - `withSpokenMessages`: HOC: Convert to TypeScript ([#48163](https://github.com/WordPress/gutenberg/pull/48163)). diff --git a/packages/components/src/higher-order/with-filters/index.js b/packages/components/src/higher-order/with-filters/index.tsx similarity index 69% rename from packages/components/src/higher-order/with-filters/index.js rename to packages/components/src/higher-order/with-filters/index.tsx index 9a65c7a5fff9d0..eddd0829f3716e 100644 --- a/packages/components/src/higher-order/with-filters/index.js +++ b/packages/components/src/higher-order/with-filters/index.tsx @@ -13,11 +13,37 @@ const ANIMATION_FRAME_PERIOD = 16; * to be mounted. When a filter is added or removed that matches the hook name, * the wrapped component re-renders. * - * @param {string} hookName Hook name exposed to be used by filters. + * @param hookName Hook name exposed to be used by filters. * - * @return {Function} Higher-order component factory. + * @return Higher-order component factory. + * + * ```jsx + * import { withFilters } from '@wordpress/components'; + * import { addFilter } from '@wordpress/hooks'; + * + * const MyComponent = ( { title } ) =>

{ title }

; + * + * const ComponentToAppend = () =>
Appended component
; + * + * function withComponentAppended( FilteredComponent ) { + * return ( props ) => ( + * <> + * + * + * + * ); + * } + * + * addFilter( + * 'MyHookName', + * 'my-plugin/with-component-appended', + * withComponentAppended + * ); + * + * const MyComponentWithFilters = withFilters( 'MyHookName' )( MyComponent ); + * ``` */ -export default function withFilters( hookName ) { +export default function withFilters( hookName: string ) { return createHigherOrderComponent( ( OriginalComponent ) => { const namespace = 'core/with-filters/' + hookName; @@ -25,10 +51,8 @@ export default function withFilters( hookName ) { * The component definition with current filters applied. Each instance * reuse this shared reference as an optimization to avoid excessive * calls to `applyFilters` when many instances exist. - * - * @type {?Component} */ - let FilteredComponent; + let FilteredComponent: React.ComponentType; /** * Initializes the FilteredComponent variable once, if not already @@ -36,13 +60,18 @@ export default function withFilters( hookName ) { */ function ensureFilteredComponent() { if ( FilteredComponent === undefined ) { - FilteredComponent = applyFilters( hookName, OriginalComponent ); + FilteredComponent = applyFilters( + hookName, + OriginalComponent + ) as React.ComponentType; } } class FilteredComponentRenderer extends Component { - constructor() { - super( ...arguments ); + static instances: FilteredComponentRenderer[]; + + constructor( props: { [ key: string ]: any } ) { + super( props ); ensureFilteredComponent(); } @@ -86,7 +115,10 @@ export default function withFilters( hookName ) { const throttledForceUpdate = debounce( () => { // Recreate the filtered component, only after delay so that it's // computed once, even if many filters added. - FilteredComponent = applyFilters( hookName, OriginalComponent ); + FilteredComponent = applyFilters( + hookName, + OriginalComponent + ) as React.ComponentType; // Force each instance to render. FilteredComponentRenderer.instances.forEach( ( instance ) => { @@ -99,9 +131,9 @@ export default function withFilters( hookName ) { * mounted instance should re-render with the new filters having been * applied to the original component. * - * @param {string} updatedHookName Name of the hook that was updated. + * @param updatedHookName Name of the hook that was updated. */ - function onHooksUpdated( updatedHookName ) { + function onHooksUpdated( updatedHookName: string ) { if ( updatedHookName === hookName ) { throttledForceUpdate(); } diff --git a/packages/components/src/higher-order/with-filters/test/__snapshots__/index.js.snap b/packages/components/src/higher-order/with-filters/test/__snapshots__/index.tsx.snap similarity index 100% rename from packages/components/src/higher-order/with-filters/test/__snapshots__/index.js.snap rename to packages/components/src/higher-order/with-filters/test/__snapshots__/index.tsx.snap diff --git a/packages/components/src/higher-order/with-filters/test/index.js b/packages/components/src/higher-order/with-filters/test/index.tsx similarity index 94% rename from packages/components/src/higher-order/with-filters/test/index.js rename to packages/components/src/higher-order/with-filters/test/index.tsx index b7aacb1584079c..0f84054cee7376 100644 --- a/packages/components/src/higher-order/with-filters/test/index.js +++ b/packages/components/src/higher-order/with-filters/test/index.tsx @@ -18,7 +18,11 @@ describe( 'withFilters', () => { const MyComponent = () =>
My component
; afterEach( () => { - removeAllFilters( hookName ); + removeAllFilters( hookName, 'test/enhanced-component-override' ); + removeAllFilters( hookName, 'test/enhanced-component-compose' ); + removeAllFilters( hookName, 'test/enhanced-component-spy' ); + removeAllFilters( hookName, 'test/enhanced-component-spy-1' ); + removeAllFilters( hookName, 'test/enhanced-component-spy-2' ); } ); it( 'should display original component when no filters applied', () => { diff --git a/packages/components/tsconfig.json b/packages/components/tsconfig.json index 953bf2b67c3c9a..8fdf16e5df7338 100644 --- a/packages/components/tsconfig.json +++ b/packages/components/tsconfig.json @@ -48,7 +48,6 @@ "src/custom-gradient-picker", "src/duotone-picker", "src/gradient-picker", - "src/higher-order/with-filters", "src/higher-order/with-focus-return", "src/higher-order/with-notices", "src/palette-edit" From 4b283a4b96f1ceaf3e66cced97da311a7a475ba6 Mon Sep 17 00:00:00 2001 From: Ben Dwyer Date: Fri, 10 Mar 2023 10:23:01 +0000 Subject: [PATCH 112/910] Navigation Link: Remove color generation code (#48927) --- .../src/navigation-link/index.php | 74 +------------------ 1 file changed, 1 insertion(+), 73 deletions(-) diff --git a/packages/block-library/src/navigation-link/index.php b/packages/block-library/src/navigation-link/index.php index fc3131e9a20510..4744f7571b6dcd 100644 --- a/packages/block-library/src/navigation-link/index.php +++ b/packages/block-library/src/navigation-link/index.php @@ -5,76 +5,6 @@ * @package WordPress */ -/** - * Build an array with CSS classes and inline styles defining the colors - * which will be applied to the navigation markup in the front-end. - * - * @param array $context Navigation block context. - * @param array $attributes Block attributes. - * @param bool $is_sub_menu Whether the link is part of a sub-menu. - * @return array Colors CSS classes and inline styles. - */ -function block_core_navigation_link_build_css_colors( $context, $attributes, $is_sub_menu = false ) { - $colors = array( - 'css_classes' => array(), - 'inline_styles' => '', - ); - - // Text color. - $named_text_color = null; - $custom_text_color = null; - - if ( $is_sub_menu && array_key_exists( 'customOverlayTextColor', $context ) ) { - $custom_text_color = $context['customOverlayTextColor']; - } elseif ( $is_sub_menu && array_key_exists( 'overlayTextColor', $context ) ) { - $named_text_color = $context['overlayTextColor']; - } elseif ( array_key_exists( 'customTextColor', $context ) ) { - $custom_text_color = $context['customTextColor']; - } elseif ( array_key_exists( 'textColor', $context ) ) { - $named_text_color = $context['textColor']; - } elseif ( isset( $context['style']['color']['text'] ) ) { - $custom_text_color = $context['style']['color']['text']; - } - - // If has text color. - if ( ! is_null( $named_text_color ) ) { - // Add the color class. - array_push( $colors['css_classes'], 'has-text-color', sprintf( 'has-%s-color', $named_text_color ) ); - } elseif ( ! is_null( $custom_text_color ) ) { - // Add the custom color inline style. - $colors['css_classes'][] = 'has-text-color'; - $colors['inline_styles'] .= sprintf( 'color: %s;', $custom_text_color ); - } - - // Background color. - $named_background_color = null; - $custom_background_color = null; - - if ( $is_sub_menu && array_key_exists( 'customOverlayBackgroundColor', $context ) ) { - $custom_background_color = $context['customOverlayBackgroundColor']; - } elseif ( $is_sub_menu && array_key_exists( 'overlayBackgroundColor', $context ) ) { - $named_background_color = $context['overlayBackgroundColor']; - } elseif ( array_key_exists( 'customBackgroundColor', $context ) ) { - $custom_background_color = $context['customBackgroundColor']; - } elseif ( array_key_exists( 'backgroundColor', $context ) ) { - $named_background_color = $context['backgroundColor']; - } elseif ( isset( $context['style']['color']['background'] ) ) { - $custom_background_color = $context['style']['color']['background']; - } - - // If has background color. - if ( ! is_null( $named_background_color ) ) { - // Add the background-color class. - array_push( $colors['css_classes'], 'has-background', sprintf( 'has-%s-background-color', $named_background_color ) ); - } elseif ( ! is_null( $custom_background_color ) ) { - // Add the custom background-color inline style. - $colors['css_classes'][] = 'has-background'; - $colors['inline_styles'] .= sprintf( 'background-color: %s;', $custom_background_color ); - } - - return $colors; -} - /** * Build an array with CSS classes and inline styles defining the font sizes * which will be applied to the navigation markup in the front-end. @@ -173,13 +103,11 @@ function render_block_core_navigation_link( $attributes, $content, $block ) { return ''; } - $colors = block_core_navigation_link_build_css_colors( $block->context, $attributes ); $font_sizes = block_core_navigation_link_build_css_font_sizes( $block->context ); $classes = array_merge( - $colors['css_classes'], $font_sizes['css_classes'] ); - $style_attribute = ( $colors['inline_styles'] . $font_sizes['inline_styles'] ); + $style_attribute = ( $font_sizes['inline_styles'] ); $css_classes = trim( implode( ' ', $classes ) ); $has_submenu = count( $block->inner_blocks ) > 0; From f0664316a071a70edb8e090f854835a3f74c3d70 Mon Sep 17 00:00:00 2001 From: James Koster Date: Fri, 10 Mar 2023 13:09:17 +0000 Subject: [PATCH 113/910] Site Editor Navigation panel: Update appearance of non-link blocks (#48933) --- .../style.scss | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/style.scss b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/style.scss index 821ecd80ac88d4..245f9d867bd7e1 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/style.scss +++ b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/style.scss @@ -34,6 +34,54 @@ .block-editor-list-view-block__menu { margin-left: -$grid-unit-10; } + &.is-selected { + > td { + background: transparent; + } + + .block-editor-list-view-block-contents { + color: inherit; + } + + &:not(:hover) { + .block-editor-list-view-block__menu { + opacity: 0; + } + } + + &:hover, + &:focus { + color: $white; + + .block-editor-list-view-block__menu-cell { + opacity: 1; + } + } + + .block-editor-list-view-block__menu { + opacity: 1; + + &:focus { + box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color); + } + } + } + + .block-editor-list-view-block-contents { + &:focus { + &::after { + box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color); + } + } + } + + &.is-branch-selected:not(.is-selected):not(.is-synced-branch) { + background: transparent; + + &:hover { + background: $gray-800; + } + } } .block-editor-list-view-leaf .block-editor-list-view-block__contents-cell { From 605aeb0f4f7d2225120e498f95ae27b9f56d77a3 Mon Sep 17 00:00:00 2001 From: Lena Morita Date: Sat, 11 Mar 2023 01:55:28 +0900 Subject: [PATCH 114/910] withFocusReturn: Convert to TypeScript (#48748) * withFocusReturn: Convert to TypeScript * Add changelog * Fix import error on CI --- packages/components/CHANGELOG.md | 1 + .../higher-order/with-focus-return/index.js | 64 ---------------- .../higher-order/with-focus-return/index.tsx | 74 +++++++++++++++++++ .../test/{index.js => index.tsx} | 4 +- packages/components/tsconfig.json | 1 - 5 files changed, 77 insertions(+), 67 deletions(-) delete mode 100644 packages/components/src/higher-order/with-focus-return/index.js create mode 100644 packages/components/src/higher-order/with-focus-return/index.tsx rename packages/components/src/higher-order/with-focus-return/test/{index.js => index.tsx} (95%) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index d672fdb7356c38..ee0a64ca5ff3fb 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -19,6 +19,7 @@ - `PanelBody`: Convert to TypeScript ([#47702](https://github.com/WordPress/gutenberg/pull/47702)). - `withFilters` HOC: Convert to TypeScript ([#48721](https://github.com/WordPress/gutenberg/pull/48721)). - `withFallbackStyles` HOC: Convert to TypeScript ([#48720](https://github.com/WordPress/gutenberg/pull/48720)). +- `withFocusReturn` HOC: Convert to TypeScript ([#48748](https://github.com/WordPress/gutenberg/pull/48748)). - `navigateRegions` HOC: Convert to TypeScript ([#48632](https://github.com/WordPress/gutenberg/pull/48632)). - `withSpokenMessages`: HOC: Convert to TypeScript ([#48163](https://github.com/WordPress/gutenberg/pull/48163)). - `DimensionControl(Experimental)`: Convert to TypeScript ([#47351](https://github.com/WordPress/gutenberg/pull/47351)). diff --git a/packages/components/src/higher-order/with-focus-return/index.js b/packages/components/src/higher-order/with-focus-return/index.js deleted file mode 100644 index c232bc6a1788ee..00000000000000 --- a/packages/components/src/higher-order/with-focus-return/index.js +++ /dev/null @@ -1,64 +0,0 @@ -/** - * WordPress dependencies - */ -import { Component } from '@wordpress/element'; -import { createHigherOrderComponent, useFocusReturn } from '@wordpress/compose'; -import deprecated from '@wordpress/deprecated'; - -/** - * Returns true if the given object is component-like. An object is component- - * like if it is an instance of wp.element.Component, or is a function. - * - * @param {*} object Object to test. - * - * @return {boolean} Whether object is component-like. - */ -function isComponentLike( object ) { - return object instanceof Component || typeof object === 'function'; -} - -/** - * Higher Order Component used to be used to wrap disposable elements like - * sidebars, modals, dropdowns. When mounting the wrapped component, we track a - * reference to the current active element so we know where to restore focus - * when the component is unmounted. - * - * @param {(WPComponent|Object)} options The component to be enhanced with - * focus return behavior, or an object - * describing the component and the - * focus return characteristics. - * - * @return {Function} Higher Order Component with the focus restauration behaviour. - */ -export default createHigherOrderComponent( ( options ) => { - const HoC = - ( { onFocusReturn } = {} ) => - ( WrappedComponent ) => { - const WithFocusReturn = ( props ) => { - const ref = useFocusReturn( onFocusReturn ); - return ( -
- -
- ); - }; - - return WithFocusReturn; - }; - - if ( isComponentLike( options ) ) { - const WrappedComponent = options; - return HoC()( WrappedComponent ); - } - - return HoC( options ); -}, 'withFocusReturn' ); - -export const Provider = ( { children } ) => { - deprecated( 'wp.components.FocusReturnProvider component', { - since: '5.7', - hint: 'This provider is not used anymore. You can just remove it from your codebase', - } ); - - return children; -}; diff --git a/packages/components/src/higher-order/with-focus-return/index.tsx b/packages/components/src/higher-order/with-focus-return/index.tsx new file mode 100644 index 00000000000000..2596617ad382d2 --- /dev/null +++ b/packages/components/src/higher-order/with-focus-return/index.tsx @@ -0,0 +1,74 @@ +/** + * WordPress dependencies + */ +import { Component } from '@wordpress/element'; +import { createHigherOrderComponent, useFocusReturn } from '@wordpress/compose'; +import deprecated from '@wordpress/deprecated'; + +/** + * Returns true if the given object is component-like. An object is component- + * like if it is an instance of wp.element.Component, or is a function. + * + * @param object Object to test. + * + * @return Whether object is component-like. + */ +function isComponentLike( object: any ): object is React.ComponentType { + return object instanceof Component || typeof object === 'function'; +} + +type Props = { + onFocusReturn?: () => void; +}; + +/** + * Higher Order Component used to be used to wrap disposable elements like + * sidebars, modals, dropdowns. When mounting the wrapped component, we track a + * reference to the current active element so we know where to restore focus + * when the component is unmounted. + * + * @param options The component to be enhanced with + * focus return behavior, or an object + * describing the component and the + * focus return characteristics. + * + * @return Higher Order Component with the focus restauration behaviour. + */ +export default createHigherOrderComponent( + // @ts-expect-error TODO: Reconcile with intended `createHigherOrderComponent` types + ( options: WPComponent | Record< string, unknown > ) => { + const HoC = + ( { onFocusReturn }: Props = {} ) => + ( WrappedComponent: React.ComponentType ) => { + const WithFocusReturn = ( + props: Record< string, unknown > + ) => { + const ref = useFocusReturn( onFocusReturn ); + return ( +
+ +
+ ); + }; + + return WithFocusReturn; + }; + + if ( isComponentLike( options ) ) { + const WrappedComponent = options; + return HoC()( WrappedComponent ); + } + + return HoC( options ); + }, + 'withFocusReturn' +); + +export const Provider = ( { children }: { children: React.ReactNode } ) => { + deprecated( 'wp.components.FocusReturnProvider component', { + since: '5.7', + hint: 'This provider is not used anymore. You can just remove it from your codebase', + } ); + + return children; +}; diff --git a/packages/components/src/higher-order/with-focus-return/test/index.js b/packages/components/src/higher-order/with-focus-return/test/index.tsx similarity index 95% rename from packages/components/src/higher-order/with-focus-return/test/index.js rename to packages/components/src/higher-order/with-focus-return/test/index.tsx index d9905840d731b2..74e7b58b8de189 100644 --- a/packages/components/src/higher-order/with-focus-return/test/index.js +++ b/packages/components/src/higher-order/with-focus-return/test/index.tsx @@ -12,9 +12,9 @@ import { Component } from '@wordpress/element'; /** * Internal dependencies */ -import withFocusReturn from '../'; +import withFocusReturn from '..'; -class Test extends Component { +class Test extends Component< { className: string; focusHistory: unknown } > { render() { const { className, focusHistory } = this.props; return ( diff --git a/packages/components/tsconfig.json b/packages/components/tsconfig.json index 8fdf16e5df7338..11da77d3cc83fb 100644 --- a/packages/components/tsconfig.json +++ b/packages/components/tsconfig.json @@ -48,7 +48,6 @@ "src/custom-gradient-picker", "src/duotone-picker", "src/gradient-picker", - "src/higher-order/with-focus-return", "src/higher-order/with-notices", "src/palette-edit" ] From 0d7bf7cee269c725556d763c2f155847332ec2af Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Sat, 11 Mar 2023 10:30:51 +0900 Subject: [PATCH 115/910] Comments Block (Legacy): Update missing translation (#48820) --- .../src/comments/edit/placeholder.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/block-library/src/comments/edit/placeholder.js b/packages/block-library/src/comments/edit/placeholder.js index 93cb78588e4dfe..7cbd1eaf19c7e2 100644 --- a/packages/block-library/src/comments/edit/placeholder.js +++ b/packages/block-library/src/comments/edit/placeholder.js @@ -5,6 +5,7 @@ import { store as blockEditorStore } from '@wordpress/block-editor'; import { __, sprintf } from '@wordpress/i18n'; import { useSelect } from '@wordpress/data'; import { useEntityProp } from '@wordpress/core-data'; +import { createInterpolateElement } from '@wordpress/element'; /** * Internal dependencies @@ -45,7 +46,7 @@ export default function PostCommentsPlaceholder( { postType, postId } ) {
) } @@ -133,7 +146,7 @@ function ColorPickerPopover( { ); } -function Option( { +function Option< T extends Color | Gradient >( { canOnlyChangeValues, element, onChange, @@ -143,7 +156,7 @@ function Option( { onStopEditing, slugPrefix, isGradient, -} ) { +}: OptionProps< T > ) { const focusOutsideProps = useFocusOutside( onStopEditing ); const value = isGradient ? element.gradient : element.color; @@ -175,11 +188,13 @@ function Option( { : __( 'Color name' ) } value={ element.name } - onChange={ ( nextName ) => + onChange={ ( nextName?: string ) => onChange( { ...element, name: nextName, - slug: slugPrefix + kebabCase( nextName ), + slug: + slugPrefix + + kebabCase( nextName ?? '' ), } ) } /> @@ -209,7 +224,10 @@ function Option( { ); } -function isTemporaryElement( slugPrefix, { slug, color, gradient } ) { +function isTemporaryElement( + slugPrefix: string, + { slug, color, gradient }: Color | Gradient +) { const regex = new RegExp( `^${ slugPrefix }color-([\\d]+)$` ); return ( regex.test( slug ) && @@ -218,7 +236,7 @@ function isTemporaryElement( slugPrefix, { slug, color, gradient } ) { ); } -function PaletteEditListView( { +function PaletteEditListView< T extends Color | Gradient >( { elements, onChange, editingElement, @@ -226,17 +244,17 @@ function PaletteEditListView( { canOnlyChangeValues, slugPrefix, isGradient, -} ) { +}: PaletteEditListViewProps< T > ) { // When unmounting the component if there are empty elements (the user did not complete the insertion) clean them. - const elementsReference = useRef(); + const elementsReference = useRef< typeof elements >(); useEffect( () => { elementsReference.current = elements; }, [ elements ] ); useEffect( () => { return () => { if ( - elementsReference.current.some( ( element, index ) => - isTemporaryElement( slugPrefix, element, index ) + elementsReference.current?.some( ( element ) => + isTemporaryElement( slugPrefix, element ) ) ) { const newElements = elementsReference.current.filter( @@ -306,9 +324,29 @@ function PaletteEditListView( { ); } -const EMPTY_ARRAY = []; +const EMPTY_ARRAY: Color[] = []; -export default function PaletteEdit( { +/** + * Allows editing a palette of colors or gradients. + * + * ```jsx + * import { PaletteEdit } from '@wordpress/components'; + * const MyPaletteEdit = () => { + * const [ controlledColors, setControlledColors ] = useState( colors ); + * + * return ( + * { + * setControlledColors( newColors ); + * } } + * paletteLabel="Here is a label" + * /> + * ); + * }; + * ``` + */ +export function PaletteEdit( { gradients, colors = EMPTY_ARRAY, onChange, @@ -318,22 +356,30 @@ export default function PaletteEdit( { canOnlyChangeValues, canReset, slugPrefix = '', -} ) { +}: PaletteEditProps ) { const isGradient = !! gradients; const elements = isGradient ? gradients : colors; const [ isEditing, setIsEditing ] = useState( false ); - const [ editingElement, setEditingElement ] = useState( null ); + const [ editingElement, setEditingElement ] = useState< + number | null | undefined + >( null ); const isAdding = isEditing && - editingElement && + !! editingElement && elements[ editingElement ] && ! elements[ editingElement ].slug; const elementsLength = elements.length; const hasElements = elementsLength > 0; const debounceOnChange = useDebounce( onChange, 100 ); const onSelectPaletteItem = useCallback( - ( value, newEditingElementIndex ) => { - const selectedElement = elements[ newEditingElementIndex ]; + ( + value?: PaletteElement[ keyof PaletteElement ], + newEditingElementIndex?: number + ) => { + const selectedElement = + newEditingElementIndex === undefined + ? undefined + : elements[ newEditingElementIndex ]; const key = isGradient ? 'gradient' : 'color'; // Ensures that the index returned matches a known element value. if ( !! selectedElement && selectedElement[ key ] === value ) { @@ -379,18 +425,29 @@ export default function PaletteEdit( { slugPrefix ); - onChange( [ - ...elements, - { - ...( isGradient - ? { gradient: DEFAULT_GRADIENT } - : { color: DEFAULT_COLOR } ), - name: tempOptionName, - slug: - slugPrefix + - kebabCase( tempOptionName ), - }, - ] ); + if ( !! gradients ) { + onChange( [ + ...gradients, + { + gradient: DEFAULT_GRADIENT, + name: tempOptionName, + slug: + slugPrefix + + kebabCase( tempOptionName ), + }, + ] ); + } else { + onChange( [ + ...colors, + { + color: DEFAULT_COLOR, + name: tempOptionName, + slug: + slugPrefix + + kebabCase( tempOptionName ), + }, + ] ); + } setIsEditing( true ); setEditingElement( elements.length ); } } @@ -412,7 +469,7 @@ export default function PaletteEdit( { isSmall: true, } } > - { ( { onClose } ) => ( + { ( { onClose }: { onClose: () => void } ) => ( <> { ! isEditing && ( @@ -475,9 +532,10 @@ export default function PaletteEdit( { { hasElements && ( <> { isEditing && ( - canOnlyChangeValues={ canOnlyChangeValues } elements={ elements } + // @ts-expect-error TODO: Don't know how to resolve onChange={ onChange } editingElement={ editingElement } setEditingElement={ setEditingElement } @@ -489,10 +547,16 @@ export default function PaletteEdit( { setEditingElement( null ) } - onChange={ ( newElement ) => { + onChange={ ( + newElement: typeof elements[ number ] + ) => { debounceOnChange( + // @ts-expect-error TODO: Don't know how to resolve elements.map( - ( currentElement, currentIndex ) => { + ( + currentElement: typeof elements[ number ], + currentIndex: number + ) => { if ( currentIndex === editingElement ) { @@ -503,11 +567,12 @@ export default function PaletteEdit( { ) ); } } - element={ elements[ editingElement ] } + element={ elements[ editingElement ?? -1 ] } /> ) } { ! isEditing && ( isGradient ? ( + // @ts-expect-error TODO: Remove when GradientPicker is typed. ); } + +export default PaletteEdit; diff --git a/packages/components/src/palette-edit/stories/index.tsx b/packages/components/src/palette-edit/stories/index.tsx new file mode 100644 index 00000000000000..5fd0d45592a823 --- /dev/null +++ b/packages/components/src/palette-edit/stories/index.tsx @@ -0,0 +1,82 @@ +/** + * External dependencies + */ +import type { ComponentMeta, ComponentStory } from '@storybook/react'; + +/** + * WordPress dependencies + */ +import { useState } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import PaletteEdit from '..'; +import type { Color, Gradient } from '../types'; + +const meta: ComponentMeta< typeof PaletteEdit > = { + title: 'Components/PaletteEdit', + component: PaletteEdit, + parameters: { + actions: { argTypesRegex: '^on.*' }, + controls: { expanded: true }, + docs: { source: { state: 'open' } }, + }, +}; +export default meta; + +const Template: ComponentStory< typeof PaletteEdit > = ( args ) => { + const { colors, gradients, onChange, ...props } = args; + const [ value, setValue ] = useState( gradients || colors ); + + return ( + { + setValue( newValue ); + onChange( newValue ); + }, + } + : { + colors: value as Color[], + onChange: ( newValue?: Color[] ) => { + setValue( newValue ); + onChange( newValue ); + }, + } ) } + { ...props } + /> + ); +}; + +export const Default = Template.bind( {} ); +Default.args = { + colors: [ + { color: '#1a4548', name: 'Primary', slug: 'primary' }, + { color: '#0000ff', name: 'Secondary', slug: 'secondary' }, + ], + paletteLabel: 'Colors', + emptyMessage: 'Colors are empty', +}; + +export const Gradients = Template.bind( {} ); +Gradients.args = { + gradients: [ + { + gradient: + 'linear-gradient(135deg,rgb(255,245,203) 0%,rgb(182,227,212) 50%,rgb(51,167,181) 100%)', + name: 'Pale ocean', + slug: 'pale-ocean', + }, + { + gradient: + 'linear-gradient(135deg,rgb(2,3,129) 0%,rgb(40,116,252) 100%)', + name: 'Midnight', + slug: 'midnight', + }, + ], + paletteLabel: 'Gradients', + emptyMessage: 'Gradients are empty', +}; diff --git a/packages/components/src/palette-edit/test/index.js b/packages/components/src/palette-edit/test/index.tsx similarity index 80% rename from packages/components/src/palette-edit/test/index.js rename to packages/components/src/palette-edit/test/index.tsx index c08185cc4fc653..1bf2802709de7f 100644 --- a/packages/components/src/palette-edit/test/index.js +++ b/packages/components/src/palette-edit/test/index.tsx @@ -6,12 +6,13 @@ import { render, fireEvent, screen } from '@testing-library/react'; /** * Internal dependencies */ -import PaletteEdit, { getNameForPosition } from '../'; +import PaletteEdit, { getNameForPosition } from '..'; +import type { PaletteElement } from '../types'; describe( 'getNameForPosition', () => { test( 'should return 1 by default', () => { const slugPrefix = 'test-'; - const elements = []; + const elements: PaletteElement[] = []; expect( getNameForPosition( elements, slugPrefix ) ).toEqual( 'Color 1' @@ -23,6 +24,8 @@ describe( 'getNameForPosition', () => { const elements = [ { slug: 'test-color-1', + color: '#ffffff', + name: 'Test Color 1', }, ]; @@ -36,6 +39,8 @@ describe( 'getNameForPosition', () => { const elements = [ { slug: 'a-sweet-color-2', + color: '#ffffff', + name: 'Test Color 1', }, ]; @@ -49,15 +54,23 @@ describe( 'getNameForPosition', () => { const elements = [ { slug: 'test-color-1', + color: '#ffffff', + name: 'Test Color 1', }, { slug: 'test-color-2', + color: '#1a4548', + name: 'Test Color 2', }, { slug: 'test-color-150', + color: '#f6f6f6', + name: 'Test Color 150', }, { slug: 'a-sweet-color-100', + color: '#ffe2c7', + name: 'A Sweet Color 100', }, ]; @@ -69,7 +82,6 @@ describe( 'getNameForPosition', () => { describe( 'PaletteEdit', () => { const defaultProps = { - gradients: false, colors: [ { color: '#ffffff', name: 'Base', slug: 'base' } ], onChange: jest.fn(), paletteLabel: 'Test label', diff --git a/packages/components/src/palette-edit/types.ts b/packages/components/src/palette-edit/types.ts new file mode 100644 index 00000000000000..5513b945537784 --- /dev/null +++ b/packages/components/src/palette-edit/types.ts @@ -0,0 +1,126 @@ +/** + * External dependencies + */ +import type { Key, MouseEventHandler } from 'react'; + +/** + * Internal dependencies + */ +import type { HeadingSize } from '../heading/types'; + +export type Color = { + color: string; + name: string; + slug: string; + gradient?: never; +}; + +export type Gradient = { + gradient: string; + name: string; + slug: string; + color?: never; +}; + +export type PaletteElement = Color | Gradient; + +export type BasePaletteEdit = { + /** + * Whether the user can only change the color or gradient values. + * If true, they cannot change names or delete values. + * + * @default false + */ + canOnlyChangeValues?: boolean; + /** + * Whether the user can reset the editor. + * + * @default false + */ + canReset?: boolean; + /** + * A message to show if there's nothing to edit. + */ + emptyMessage?: string; + /** + * A heading label for the palette. + */ + paletteLabel: string; + /** + * The label's heading level. + * + * @default 2 + */ + paletteLabelHeadingLevel?: HeadingSize; + /** + * The prefix for the element slug. + * + * @default '' + */ + slugPrefix?: string; +}; + +type PaletteEditColors = { + /** + * The colors in the palette. + */ + colors?: Color[]; + /** + * Runs on changing the value. + */ + onChange: ( values?: Color[] ) => void; + gradients?: never; +}; + +type PaletteEditGradients = { + /** + * The gradients in the palette. + */ + gradients: Gradient[]; + /** + * Runs on changing the value. + */ + onChange: ( values?: Gradient[] ) => void; + colors?: never; +}; + +export type PaletteEditProps = BasePaletteEdit & + ( PaletteEditColors | PaletteEditGradients ); + +type EditingElement = number | null; + +export type ColorPickerPopoverProps< T extends Color | Gradient > = { + element: T; + onChange: ( newElement: T ) => void; + isGradient?: T extends Gradient ? true : false; + onClose?: () => void; +}; + +export type NameInputProps = { + label: string; + onChange: ( nextName?: PaletteElement[ 'name' ] ) => void; + value: PaletteElement[ 'name' ]; +}; + +export type OptionProps< T extends Color | Gradient > = { + element: T; + onChange: ( newElement: T ) => void; + isGradient: T extends Gradient ? true : false; + canOnlyChangeValues: PaletteEditProps[ 'canOnlyChangeValues' ]; + isEditing: boolean; + key: Key; + onRemove: MouseEventHandler< HTMLButtonElement >; + onStartEditing: () => void; + onStopEditing: () => void; + slugPrefix: string; +}; + +export type PaletteEditListViewProps< T extends Color | Gradient > = { + elements: T[]; + onChange: ( newElements?: T[] ) => void; + isGradient: T extends Gradient ? true : false; + canOnlyChangeValues: PaletteEditProps[ 'canOnlyChangeValues' ]; + editingElement?: EditingElement; + setEditingElement: ( newEditingElement?: EditingElement ) => void; + slugPrefix: string; +}; diff --git a/packages/components/tsconfig.json b/packages/components/tsconfig.json index 11da77d3cc83fb..aa2434d026ca17 100644 --- a/packages/components/tsconfig.json +++ b/packages/components/tsconfig.json @@ -45,10 +45,7 @@ "src/**/stories/**/*.js", // only exclude js files, tsx files should be checked "src/**/test/**/*.js", // only exclude js files, ts{x} files should be checked "src/index.js", - "src/custom-gradient-picker", "src/duotone-picker", - "src/gradient-picker", - "src/higher-order/with-notices", - "src/palette-edit" + "src/higher-order/with-notices" ] } From 73162de16b9d7f1cf39fb9a5b15872cc28f8eb97 Mon Sep 17 00:00:00 2001 From: Nik Tsekouras Date: Tue, 14 Mar 2023 07:19:49 +0200 Subject: [PATCH 131/910] Hide navigation screen in site editor (#49043) --- .../src/components/sidebar-navigation-screen-main/index.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-main/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-main/index.js index fa7aa217134dcf..04faf480bb0609 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-main/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-main/index.js @@ -34,6 +34,9 @@ export default function SidebarNavigationScreenMain() { return navigationMenus?.length > 0; } ); + const showNavigationScreen = process.env.IS_GUTENBERG_PLUGIN + ? hasNavigationMenus + : false; return ( - { hasNavigationMenus && ( + { showNavigationScreen && ( Date: Tue, 14 Mar 2023 09:24:01 +0400 Subject: [PATCH 132/910] List View: Remove unused selector from the 'useBlockSelection' hook (#48984) --- .../src/components/list-view/use-block-selection.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/block-editor/src/components/list-view/use-block-selection.js b/packages/block-editor/src/components/list-view/use-block-selection.js index 59aaaeacb01d40..e612ccaab6329f 100644 --- a/packages/block-editor/src/components/list-view/use-block-selection.js +++ b/packages/block-editor/src/components/list-view/use-block-selection.js @@ -21,7 +21,6 @@ export default function useBlockSelection() { getBlockName, getBlockParents, getBlockSelectionStart, - getBlockSelectionEnd, getSelectedBlockClientIds, hasMultiSelection, hasSelectedBlock, @@ -154,7 +153,6 @@ export default function useBlockSelection() { getBlockType, getBlockParents, getBlockSelectionStart, - getBlockSelectionEnd, getSelectedBlockClientIds, hasMultiSelection, hasSelectedBlock, From c2dfd230bf9d708ce532e3ee31ef961f51d0eeae Mon Sep 17 00:00:00 2001 From: Ramon Ahnert Date: Tue, 14 Mar 2023 03:34:44 -0300 Subject: [PATCH 133/910] i18n: Fix the typo in the media categories component (#49047) --- packages/editor/src/components/media-categories/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/editor/src/components/media-categories/index.js b/packages/editor/src/components/media-categories/index.js index d3d7e524d9e78c..8bd3f5947ff851 100644 --- a/packages/editor/src/components/media-categories/index.js +++ b/packages/editor/src/components/media-categories/index.js @@ -131,7 +131,7 @@ const getOpenverseCaption = ( item ) => { ) : sprintf( // translators: %1s: Link attributes for a given Openverse media work; %2s: Works's licence e.g: "CC0 1.0". - _x( 'Work/ %3$s', 'caption' ), + _x( 'Work/ %2$s', 'caption' ), getExternalLinkAttributes( foreignLandingUrl ), licenseUrl ? getExternalLink( From 96dc83b95e20022caf0b12450373c73540181ac5 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Tue, 14 Mar 2023 11:05:17 +0400 Subject: [PATCH 134/910] Don't offer Classic block as a recovery action when not registered (#49051) --- .../components/block-list/block-invalid-warning.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/block-list/block-invalid-warning.js b/packages/block-editor/src/components/block-list/block-invalid-warning.js index c638da242c375f..922d7080f26ebc 100644 --- a/packages/block-editor/src/components/block-list/block-invalid-warning.js +++ b/packages/block-editor/src/components/block-list/block-invalid-warning.js @@ -23,6 +23,7 @@ export function BlockInvalidWarning( { block, } ) { const hasHTMLBlock = !! getBlockType( 'core/html' ); + const hasClassicBlock = !! getBlockType( 'core/freeform' ); const [ compare, setCompare ] = useState( false ); const onCompare = useCallback( () => setCompare( true ), [] ); @@ -41,12 +42,18 @@ export function BlockInvalidWarning( { title: __( 'Convert to HTML' ), onClick: convertToHTML, }, - { + hasClassicBlock && { title: __( 'Convert to Classic Block' ), onClick: convertToClassic, }, ].filter( Boolean ), - [ onCompare, convertToHTML, convertToClassic ] + [ + onCompare, + hasHTMLBlock, + convertToHTML, + hasClassicBlock, + convertToClassic, + ] ); return ( From 18887c434a54fb472aed751d15a179591e91c262 Mon Sep 17 00:00:00 2001 From: Marin Atanasov <8436925+tyxla@users.noreply.github.com> Date: Tue, 14 Mar 2023 09:10:11 +0200 Subject: [PATCH 135/910] Tests: Cleanup unnecessary jest timers setup (#49030) --- .../block-editor/src/components/block-preview/test/index.js | 2 -- .../block-editor/src/components/link-control/test/index.js | 2 -- .../block-editor/src/components/url-popover/test/index.js | 2 -- packages/block-editor/src/utils/test/parse-css-unit-to-px.js | 2 -- packages/components/src/border-control/test/index.js | 2 -- packages/compose/src/hooks/use-disabled/test/index.js | 2 -- packages/core-data/src/locks/test/engine.js | 2 -- .../data/src/components/use-dispatch/test/use-dispatch.js | 2 -- packages/data/src/components/use-select/test/index.js | 2 -- packages/data/src/components/use-select/test/suspense.js | 2 -- packages/data/src/components/with-dispatch/test/index.js | 2 -- packages/data/src/components/with-select/test/index.js | 2 -- packages/data/src/redux-store/metadata/test/selectors.js | 1 - packages/data/src/redux-store/test/index.js | 2 -- packages/data/src/test/controls.js | 2 -- packages/dependency-extraction-webpack-plugin/test/build.js | 2 -- packages/edit-site/src/store/test/actions.js | 2 -- packages/editor/src/components/autosave-monitor/test/index.js | 4 +++- packages/editor/src/components/post-text-editor/test/index.js | 2 -- packages/editor/src/store/test/actions.js | 2 -- packages/readable-js-assets-webpack-plugin/test/build.js | 2 -- 21 files changed, 3 insertions(+), 40 deletions(-) diff --git a/packages/block-editor/src/components/block-preview/test/index.js b/packages/block-editor/src/components/block-preview/test/index.js index 8687f2e066db51..fcd5f48e8a0fc6 100644 --- a/packages/block-editor/src/components/block-preview/test/index.js +++ b/packages/block-editor/src/components/block-preview/test/index.js @@ -17,8 +17,6 @@ import { */ import { useBlockPreview } from '../'; -jest.useRealTimers(); - describe( 'useBlockPreview', () => { beforeAll( () => { registerBlockType( 'core/test-block', { diff --git a/packages/block-editor/src/components/link-control/test/index.js b/packages/block-editor/src/components/link-control/test/index.js index 68fa00a22762c9..de2d0607056173 100644 --- a/packages/block-editor/src/components/link-control/test/index.js +++ b/packages/block-editor/src/components/link-control/test/index.js @@ -51,8 +51,6 @@ jest.mock( '@wordpress/data/src/components/use-dispatch', () => ( { useDispatch: () => ( { saveEntityRecords: jest.fn() } ), } ) ); -jest.useRealTimers(); - jest.mock( '@wordpress/compose', () => ( { ...jest.requireActual( '@wordpress/compose' ), useReducedMotion: jest.fn( () => true ), diff --git a/packages/block-editor/src/components/url-popover/test/index.js b/packages/block-editor/src/components/url-popover/test/index.js index 859d21fd0305b5..1a60d846b2e904 100644 --- a/packages/block-editor/src/components/url-popover/test/index.js +++ b/packages/block-editor/src/components/url-popover/test/index.js @@ -9,8 +9,6 @@ import userEvent from '@testing-library/user-event'; */ import URLPopover from '../'; -jest.useRealTimers(); - /** * Returns the first found popover element up the DOM tree. * diff --git a/packages/block-editor/src/utils/test/parse-css-unit-to-px.js b/packages/block-editor/src/utils/test/parse-css-unit-to-px.js index 2f0745fca2cf38..17fd36e1cddcb6 100644 --- a/packages/block-editor/src/utils/test/parse-css-unit-to-px.js +++ b/packages/block-editor/src/utils/test/parse-css-unit-to-px.js @@ -6,8 +6,6 @@ import { getPxFromCssUnit, } from '../parse-css-unit-to-px'; -jest.useRealTimers(); - describe( 'getPxFromCssUnit', () => { // Absolute units. describe( 'absolute unites should return px values', () => { diff --git a/packages/components/src/border-control/test/index.js b/packages/components/src/border-control/test/index.js index 2d599d0c9f8c18..4e971e59e87b21 100644 --- a/packages/components/src/border-control/test/index.js +++ b/packages/components/src/border-control/test/index.js @@ -14,8 +14,6 @@ import { */ import { BorderControl } from '../'; -jest.useRealTimers(); - const colors = [ { name: 'Gray', color: '#f6f7f7' }, { name: 'Blue', color: '#72aee6' }, diff --git a/packages/compose/src/hooks/use-disabled/test/index.js b/packages/compose/src/hooks/use-disabled/test/index.js index 491d4a6cd12f62..6f4c7bfe36256d 100644 --- a/packages/compose/src/hooks/use-disabled/test/index.js +++ b/packages/compose/src/hooks/use-disabled/test/index.js @@ -13,8 +13,6 @@ import { forwardRef } from '@wordpress/element'; */ import useDisabled from '../'; -jest.useRealTimers(); - describe( 'useDisabled', () => { const Form = forwardRef( ( { showButton }, ref ) => { return ( diff --git a/packages/core-data/src/locks/test/engine.js b/packages/core-data/src/locks/test/engine.js index 64ec8f62ebbf8a..089065bee86345 100644 --- a/packages/core-data/src/locks/test/engine.js +++ b/packages/core-data/src/locks/test/engine.js @@ -3,8 +3,6 @@ */ import createLocks from '../engine'; -jest.useRealTimers(); - // We correctly await all promises with expect calls, but the rule doesn't detect that. /* eslint-disable jest/valid-expect-in-promise */ diff --git a/packages/data/src/components/use-dispatch/test/use-dispatch.js b/packages/data/src/components/use-dispatch/test/use-dispatch.js index 24bf29c75ba9b9..c47339c94e0dd6 100644 --- a/packages/data/src/components/use-dispatch/test/use-dispatch.js +++ b/packages/data/src/components/use-dispatch/test/use-dispatch.js @@ -12,8 +12,6 @@ import createReduxStore from '../../../redux-store'; import { createRegistry } from '../../../registry'; import { RegistryProvider } from '../../registry-provider'; -jest.useRealTimers(); - describe( 'useDispatch', () => { const counterStore = { reducer: ( state = 0, action ) => { diff --git a/packages/data/src/components/use-select/test/index.js b/packages/data/src/components/use-select/test/index.js index 4f7ab0e2589316..f4b2ca83f24bf2 100644 --- a/packages/data/src/components/use-select/test/index.js +++ b/packages/data/src/components/use-select/test/index.js @@ -19,8 +19,6 @@ import { } from '../../..'; import useSelect from '..'; -jest.useRealTimers(); - describe( 'useSelect', () => { let registry; beforeEach( () => { diff --git a/packages/data/src/components/use-select/test/suspense.js b/packages/data/src/components/use-select/test/suspense.js index aa53151ad742db..de215c297df018 100644 --- a/packages/data/src/components/use-select/test/suspense.js +++ b/packages/data/src/components/use-select/test/suspense.js @@ -14,8 +14,6 @@ import { } from '@wordpress/data'; import { Component, Suspense } from '@wordpress/element'; -jest.useRealTimers(); - function createRegistryWithStore() { const initialState = { prefix: 'pre-', diff --git a/packages/data/src/components/with-dispatch/test/index.js b/packages/data/src/components/with-dispatch/test/index.js index d71d087e289aeb..f4e38d28edba0a 100644 --- a/packages/data/src/components/with-dispatch/test/index.js +++ b/packages/data/src/components/with-dispatch/test/index.js @@ -16,8 +16,6 @@ import withDispatch from '../'; import { createRegistry } from '../../../registry'; import { RegistryProvider } from '../../registry-provider'; -jest.useRealTimers(); - describe( 'withDispatch', () => { const storeOptions = { reducer: ( state = 0, action ) => { diff --git a/packages/data/src/components/with-select/test/index.js b/packages/data/src/components/with-select/test/index.js index 491be6ddfcd5fc..01afcfe5db6ad1 100644 --- a/packages/data/src/components/with-select/test/index.js +++ b/packages/data/src/components/with-select/test/index.js @@ -18,8 +18,6 @@ import withDispatch from '../../with-dispatch'; import { createRegistry } from '../../../registry'; import { RegistryProvider } from '../../registry-provider'; -jest.useRealTimers(); - describe( 'withSelect', () => { it( 'passes the relevant data to the component', () => { const registry = createRegistry(); diff --git a/packages/data/src/redux-store/metadata/test/selectors.js b/packages/data/src/redux-store/metadata/test/selectors.js index c2c7ed8d03eb63..d84ef52bddd048 100644 --- a/packages/data/src/redux-store/metadata/test/selectors.js +++ b/packages/data/src/redux-store/metadata/test/selectors.js @@ -3,7 +3,6 @@ */ import { createRegistry } from '@wordpress/data'; -jest.useRealTimers(); const testStore = { reducer: ( state = null, action ) => { if ( action.type === 'RECEIVE' ) { diff --git a/packages/data/src/redux-store/test/index.js b/packages/data/src/redux-store/test/index.js index 1d36daababaa36..daca128480daeb 100644 --- a/packages/data/src/redux-store/test/index.js +++ b/packages/data/src/redux-store/test/index.js @@ -4,8 +4,6 @@ import { createRegistry } from '../../registry'; import { createRegistryControl } from '../../factory'; -jest.useRealTimers(); - describe( 'controls', () => { let registry; diff --git a/packages/data/src/test/controls.js b/packages/data/src/test/controls.js index adcaccf06dae5d..97c503b92c0c4d 100644 --- a/packages/data/src/test/controls.js +++ b/packages/data/src/test/controls.js @@ -3,8 +3,6 @@ */ import { createRegistry, controls } from '..'; -jest.useRealTimers(); - describe( 'controls', () => { // Create a registry with store to test select controls. function createSelectTestRegistry() { diff --git a/packages/dependency-extraction-webpack-plugin/test/build.js b/packages/dependency-extraction-webpack-plugin/test/build.js index b749b66f1e74bd..9ad88a26c10c33 100644 --- a/packages/dependency-extraction-webpack-plugin/test/build.js +++ b/packages/dependency-extraction-webpack-plugin/test/build.js @@ -11,8 +11,6 @@ const webpack = require( 'webpack' ); const fixturesPath = path.join( __dirname, 'fixtures' ); const configFixtures = fs.readdirSync( fixturesPath ).sort(); -jest.useRealTimers(); - describe( 'DependencyExtractionWebpackPlugin', () => { afterAll( () => rimraf( path.join( __dirname, 'build' ) ) ); diff --git a/packages/edit-site/src/store/test/actions.js b/packages/edit-site/src/store/test/actions.js index fc2e5ca6a7c617..48b7d1ed71d70d 100644 --- a/packages/edit-site/src/store/test/actions.js +++ b/packages/edit-site/src/store/test/actions.js @@ -14,8 +14,6 @@ import { store as preferencesStore } from '@wordpress/preferences'; */ import { store as editSiteStore } from '..'; -jest.useRealTimers(); - const ENTITY_TYPES = { wp_template: { description: 'Templates to include in your theme.', diff --git a/packages/editor/src/components/autosave-monitor/test/index.js b/packages/editor/src/components/autosave-monitor/test/index.js index a089641d07abe2..0bb68998162ced 100644 --- a/packages/editor/src/components/autosave-monitor/test/index.js +++ b/packages/editor/src/components/autosave-monitor/test/index.js @@ -8,7 +8,9 @@ import { render } from '@testing-library/react'; */ import { AutosaveMonitor } from '../'; -jest.useFakeTimers( { legacyFakeTimers: true } ); +jest.useFakeTimers(); +jest.spyOn( global, 'clearTimeout' ); +jest.spyOn( global, 'setTimeout' ); describe( 'AutosaveMonitor', () => { let setAutosaveTimerSpy; diff --git a/packages/editor/src/components/post-text-editor/test/index.js b/packages/editor/src/components/post-text-editor/test/index.js index a9d2d742e9d13e..acaa7e95600b82 100644 --- a/packages/editor/src/components/post-text-editor/test/index.js +++ b/packages/editor/src/components/post-text-editor/test/index.js @@ -38,8 +38,6 @@ jest.mock( '@wordpress/data/src/components/use-dispatch', () => { }; } ); -jest.useRealTimers(); - describe( 'PostTextEditor', () => { beforeEach( () => { useSelect.mockImplementation( () => 'Hello World' ); diff --git a/packages/editor/src/store/test/actions.js b/packages/editor/src/store/test/actions.js index ecc567aa69a8ce..5f9a33b2479d6a 100644 --- a/packages/editor/src/store/test/actions.js +++ b/packages/editor/src/store/test/actions.js @@ -15,8 +15,6 @@ import { store as preferencesStore } from '@wordpress/preferences'; import * as actions from '../actions'; import { store as editorStore } from '..'; -jest.useRealTimers(); - const postId = 44; const postTypeConfig = { diff --git a/packages/readable-js-assets-webpack-plugin/test/build.js b/packages/readable-js-assets-webpack-plugin/test/build.js index e3a7e26dcdf12f..00ef7beb7c6250 100644 --- a/packages/readable-js-assets-webpack-plugin/test/build.js +++ b/packages/readable-js-assets-webpack-plugin/test/build.js @@ -8,8 +8,6 @@ const path = require( 'path' ); const rimraf = require( 'rimraf' ).sync; const webpack = require( 'webpack' ); -jest.useRealTimers(); - describe( 'ReadableJsAssetsWebpackPlugin', () => { const outputDirectory = path.join( __dirname, 'build' ); const testDirectory = path.join( __dirname, 'fixtures' ); From 56050d109aa96d9fe1fb8b001ca9567421da90f2 Mon Sep 17 00:00:00 2001 From: Marin Atanasov <8436925+tyxla@users.noreply.github.com> Date: Tue, 14 Mar 2023 09:10:33 +0200 Subject: [PATCH 136/910] Data: Use real timers for private APIs tests (#49029) --- packages/data/src/test/privateAPIs.js | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/packages/data/src/test/privateAPIs.js b/packages/data/src/test/privateAPIs.js index 25b96b369c062a..93df00d287d600 100644 --- a/packages/data/src/test/privateAPIs.js +++ b/packages/data/src/test/privateAPIs.js @@ -5,19 +5,6 @@ import { createRegistry } from '../registry'; import createReduxStore from '../redux-store'; import { unlock } from '../private-apis'; -/** - * WordPress dependencies - */ - -beforeEach( () => { - jest.useFakeTimers( { legacyFakeTimers: true } ); -} ); - -afterEach( () => { - jest.runOnlyPendingTimers(); - jest.useRealTimers(); -} ); - describe( 'Private data APIs', () => { let registry; From 2ba91cf500d6d768d3e5d9985a0049d8cf00c2cf Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Tue, 14 Mar 2023 08:20:14 +0000 Subject: [PATCH 137/910] Fix: Global Styles crash in updateConfigWithSeparator when not block styles are passed. (#49045) --- .../components/global-styles/use-global-styles-output.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/block-editor/src/components/global-styles/use-global-styles-output.js b/packages/block-editor/src/components/global-styles/use-global-styles-output.js index b436a595207b2c..cce2b61f0786e7 100644 --- a/packages/block-editor/src/components/global-styles/use-global-styles-output.js +++ b/packages/block-editor/src/components/global-styles/use-global-styles-output.js @@ -973,10 +973,10 @@ export const getBlockSelectors = ( blockTypes, getBlockStyles ) => { */ function updateConfigWithSeparator( config ) { const needsSeparatorStyleUpdate = - config.styles?.blocks[ 'core/separator' ] && - config.styles?.blocks[ 'core/separator' ].color?.background && - ! config.styles?.blocks[ 'core/separator' ].color?.text && - ! config.styles?.blocks[ 'core/separator' ].border?.color; + config.styles?.blocks?.[ 'core/separator' ] && + config.styles?.blocks?.[ 'core/separator' ].color?.background && + ! config.styles?.blocks?.[ 'core/separator' ].color?.text && + ! config.styles?.blocks?.[ 'core/separator' ].border?.color; if ( needsSeparatorStyleUpdate ) { return { ...config, From 420013b3f3e24f00a4dcfb80d4a173dacca92849 Mon Sep 17 00:00:00 2001 From: Kai Hao Date: Tue, 14 Mar 2023 17:16:57 +0800 Subject: [PATCH 138/910] Add pressKeys (#49009) --- docs/contributors/code/e2e/migration.md | 4 +- packages/e2e-test-utils-playwright/README.md | 2 +- .../src/page-utils/index.ts | 13 +- .../src/page-utils/press-key-times.js | 11 -- .../src/page-utils/press-key-with-modifier.ts | 132 ------------- .../src/page-utils/press-keys.ts | 176 ++++++++++++++++++ test/e2e/specs/editor/blocks/buttons.spec.js | 10 +- test/e2e/specs/editor/blocks/classic.spec.js | 8 +- test/e2e/specs/editor/blocks/code.spec.js | 2 +- test/e2e/specs/editor/blocks/gallery.spec.js | 2 +- test/e2e/specs/editor/blocks/heading.spec.js | 12 +- test/e2e/specs/editor/blocks/image.spec.js | 8 +- test/e2e/specs/editor/blocks/list.spec.js | 30 +-- test/e2e/specs/editor/blocks/quote.spec.js | 4 +- .../plugins/deprecated-node-matcher.spec.js | 2 +- .../specs/editor/plugins/format-api.spec.js | 2 +- .../plugins/post-type-templates.spec.js | 2 +- test/e2e/specs/editor/various/a11y.spec.js | 43 ++--- .../various/autocomplete-and-mentions.spec.js | 8 +- .../editor/various/block-deletion.spec.js | 4 +- .../editor/various/content-only-lock.spec.js | 4 +- .../editor/various/copy-cut-paste.spec.js | 108 +++++------ .../editor/various/duplicating-blocks.spec.js | 6 +- .../editor/various/font-size-picker.spec.js | 12 +- .../specs/editor/various/list-view.spec.js | 52 +++--- .../e2e/specs/editor/various/mentions.spec.js | 2 +- .../various/multi-block-selection.spec.js | 80 ++++---- test/e2e/specs/editor/various/rtl.spec.js | 4 +- .../editor/various/splitting-merging.spec.js | 26 +-- .../various/toolbar-roving-tabindex.spec.js | 17 +- .../specs/editor/various/writing-flow.spec.js | 75 ++++---- .../specs/site-editor/writing-flow.spec.js | 4 +- .../specs/widgets/customizing-widgets.spec.js | 5 +- 33 files changed, 450 insertions(+), 420 deletions(-) delete mode 100644 packages/e2e-test-utils-playwright/src/page-utils/press-key-times.js delete mode 100644 packages/e2e-test-utils-playwright/src/page-utils/press-key-with-modifier.ts create mode 100644 packages/e2e-test-utils-playwright/src/page-utils/press-keys.ts diff --git a/docs/contributors/code/e2e/migration.md b/docs/contributors/code/e2e/migration.md index 030c6c87f2c304..4b0400b85d3c06 100644 --- a/docs/contributors/code/e2e/migration.md +++ b/docs/contributors/code/e2e/migration.md @@ -17,7 +17,7 @@ This document outlines a typical flow of migrating a Jest + Puppeteer test to Pl ## Migration steps for test utils -Before migrating a test utility function, think twice about whether it's necessary. Playwright offers a lot of readable and powerful APIs which make a lot of the utils obsolete. Try implementing the same thing inline directly in the test first. Only follow the below guide if that doesn't work for you. Some examples of utils that deserve to be implemented in the `e2e-test-utils-playwright` package include complex browser APIs (like `pageUtils.dragFiles` and `pageUtils.pressKeyWithModifier`) and APIs that set states (`requestUtils.*`). +Before migrating a test utility function, think twice about whether it's necessary. Playwright offers a lot of readable and powerful APIs which make a lot of the utils obsolete. Try implementing the same thing inline directly in the test first. Only follow the below guide if that doesn't work for you. Some examples of utils that deserve to be implemented in the `e2e-test-utils-playwright` package include complex browser APIs (like `pageUtils.dragFiles` and `pageUtils.pressKeys`) and APIs that set states (`requestUtils.*`). > **Note** > The `e2e-test-utils-playwright` package is not meant to be a drop-in replacement of the Jest + Puppeteer's `e2e-test-utils` package. Some utils are only created to ease the migration process, but they are not necessarily required. @@ -25,7 +25,7 @@ Before migrating a test utility function, think twice about whether it's necessa Playwright utilities are organized a little differently from those in the `e2e-test-utils` package. The `e2e-test-utils-playwright` package has the following folders that utils are divided up into: - `admin` - Utilities related to WordPress admin or WordPress admin's user interface (e.g. `visitAdminPage`). - `editor` - Utilities for the block editor (e.g. `clickBlockToolbarButton`). -- `pageUtils` - General utilities for interacting with the browser (e.g. `pressKeyWithModifier`). +- `pageUtils` - General utilities for interacting with the browser (e.g. `pressKeys`). - `requestUtils` - Utilities for making REST API requests (e.g. `activatePlugin`). These utilities are used for setup and teardown of tests. 1. Copy the existing file in `e2e-test-utils` and paste it in the `admin`, `editor`, `page` or `request` folder in `e2e-test-utils-playwright` depending on the type of util. diff --git a/packages/e2e-test-utils-playwright/README.md b/packages/e2e-test-utils-playwright/README.md index 52cf8523159db0..50e9c0540b9245 100644 --- a/packages/e2e-test-utils-playwright/README.md +++ b/packages/e2e-test-utils-playwright/README.md @@ -58,7 +58,7 @@ Generic Playwright utilities for interacting with web pages. ```js const pageUtils = new PageUtils( { page } ); -await pageUtils.pressKeyWithModifier( 'primary', 'a' ); +await pageUtils.pressKeys( 'primary+a' ); ``` ### RequestUtils diff --git a/packages/e2e-test-utils-playwright/src/page-utils/index.ts b/packages/e2e-test-utils-playwright/src/page-utils/index.ts index 28fc36c9abeebb..50dbe71033fc6b 100644 --- a/packages/e2e-test-utils-playwright/src/page-utils/index.ts +++ b/packages/e2e-test-utils-playwright/src/page-utils/index.ts @@ -8,11 +8,7 @@ import type { Browser, Page, BrowserContext } from '@playwright/test'; */ import { dragFiles } from './drag-files'; import { isCurrentURL } from './is-current-url'; -import { - setClipboardData, - pressKeyWithModifier, -} from './press-key-with-modifier'; -import { pressKeyTimes } from './press-key-times'; +import { setClipboardData, pressKeys } from './press-keys'; import { setBrowserViewport } from './set-browser-viewport'; type PageUtilConstructorParams = { @@ -34,11 +30,8 @@ class PageUtils { dragFiles: typeof dragFiles = dragFiles.bind( this ); /** @borrows isCurrentURL as this.isCurrentURL */ isCurrentURL: typeof isCurrentURL = isCurrentURL.bind( this ); - /** @borrows pressKeyTimes as this.pressKeyTimes */ - pressKeyTimes: typeof pressKeyTimes = pressKeyTimes.bind( this ); - /** @borrows pressKeyWithModifier as this.pressKeyWithModifier */ - pressKeyWithModifier: typeof pressKeyWithModifier = - pressKeyWithModifier.bind( this ); + /** @borrows pressKeys as this.pressKeys */ + pressKeys: typeof pressKeys = pressKeys.bind( this ); /** @borrows setBrowserViewport as this.setBrowserViewport */ setBrowserViewport: typeof setBrowserViewport = setBrowserViewport.bind( this ); diff --git a/packages/e2e-test-utils-playwright/src/page-utils/press-key-times.js b/packages/e2e-test-utils-playwright/src/page-utils/press-key-times.js deleted file mode 100644 index 5e8f3ea65b99c5..00000000000000 --- a/packages/e2e-test-utils-playwright/src/page-utils/press-key-times.js +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Presses the given keyboard key a number of times in sequence. - * - * @param {string} key Key to press. - * @param {number} count Number of times to press. - */ -export async function pressKeyTimes( key, count ) { - while ( count-- ) { - await this.page.keyboard.press( key ); - } -} diff --git a/packages/e2e-test-utils-playwright/src/page-utils/press-key-with-modifier.ts b/packages/e2e-test-utils-playwright/src/page-utils/press-key-with-modifier.ts deleted file mode 100644 index c5301fd6ba3725..00000000000000 --- a/packages/e2e-test-utils-playwright/src/page-utils/press-key-with-modifier.ts +++ /dev/null @@ -1,132 +0,0 @@ -/** - * External dependencies - */ -import { capitalCase } from 'change-case'; -import type { Page } from '@playwright/test'; - -/** - * WordPress dependencies - */ -import { modifiers, SHIFT, ALT, CTRL } from '@wordpress/keycodes'; -import type { WPKeycodeModifier } from '@wordpress/keycodes'; - -/** - * Internal dependencies - */ -import type { PageUtils } from './index'; - -let clipboardDataHolder: { - plainText: string; - html: string; -} = { - plainText: '', - html: '', -}; - -/** - * Sets the clipboard data that can be pasted with - * `pressKeyWithModifier( 'primary', 'v' )`. - * - * @param this - * @param clipboardData - * @param clipboardData.plainText - * @param clipboardData.html - */ -export function setClipboardData( - this: PageUtils, - { plainText = '', html = '' }: typeof clipboardDataHolder -) { - clipboardDataHolder = { - plainText, - html, - }; -} - -async function emulateClipboard( page: Page, type: 'copy' | 'cut' | 'paste' ) { - clipboardDataHolder = await page.evaluate( - ( [ _type, _clipboardData ] ) => { - const clipboardDataTransfer = new DataTransfer(); - - if ( _type === 'paste' ) { - clipboardDataTransfer.setData( - 'text/plain', - _clipboardData.plainText - ); - clipboardDataTransfer.setData( - 'text/html', - _clipboardData.html - ); - } else { - const selection = window.getSelection()!; - const plainText = selection.toString(); - let html = plainText; - if ( selection.rangeCount ) { - const range = selection.getRangeAt( 0 ); - const fragment = range.cloneContents(); - html = Array.from( fragment.childNodes ) - .map( - ( node ) => - ( node as Element ).outerHTML ?? node.nodeValue - ) - .join( '' ); - } - clipboardDataTransfer.setData( 'text/plain', plainText ); - clipboardDataTransfer.setData( 'text/html', html ); - } - - document.activeElement?.dispatchEvent( - new ClipboardEvent( _type, { - bubbles: true, - cancelable: true, - clipboardData: clipboardDataTransfer, - } ) - ); - - return { - plainText: clipboardDataTransfer.getData( 'text/plain' ), - html: clipboardDataTransfer.getData( 'text/html' ), - }; - }, - [ type, clipboardDataHolder ] as const - ); -} - -/** - * Performs a key press with modifier (Shift, Control, Meta, Alt), where each modifier - * is normalized to platform-specific modifier. - * - * @param this - * @param modifier - * @param key - */ -export async function pressKeyWithModifier( - this: PageUtils, - modifier: WPKeycodeModifier, - key: string -) { - if ( modifier.toLowerCase() === 'primary' && key.toLowerCase() === 'c' ) { - return await emulateClipboard( this.page, 'copy' ); - } - - if ( modifier.toLowerCase() === 'primary' && key.toLowerCase() === 'x' ) { - return await emulateClipboard( this.page, 'cut' ); - } - - if ( modifier.toLowerCase() === 'primary' && key.toLowerCase() === 'v' ) { - return await emulateClipboard( this.page, 'paste' ); - } - - const isAppleOS = () => process.platform === 'darwin'; - const overWrittenModifiers = { - ...modifiers, - shiftAlt: ( _isApple: () => boolean ) => - _isApple() ? [ SHIFT, ALT ] : [ SHIFT, CTRL ], - }; - const mappedModifiers = overWrittenModifiers[ modifier ]( isAppleOS ).map( - ( keycode ) => ( keycode === CTRL ? 'Control' : capitalCase( keycode ) ) - ); - - await this.page.keyboard.press( - `${ mappedModifiers.join( '+' ) }+${ key }` - ); -} diff --git a/packages/e2e-test-utils-playwright/src/page-utils/press-keys.ts b/packages/e2e-test-utils-playwright/src/page-utils/press-keys.ts new file mode 100644 index 00000000000000..2d93a184913bba --- /dev/null +++ b/packages/e2e-test-utils-playwright/src/page-utils/press-keys.ts @@ -0,0 +1,176 @@ +/** + * External dependencies + */ +import { capitalCase } from 'change-case'; +import type { Page } from '@playwright/test'; + +/** + * Internal dependencies + */ +import type { PageUtils } from './'; + +/** + * WordPress dependencies + */ +import { + modifiers as baseModifiers, + SHIFT, + ALT, + CTRL, +} from '@wordpress/keycodes'; + +let clipboardDataHolder: { + plainText: string; + html: string; +} = { + plainText: '', + html: '', +}; + +/** + * Sets the clipboard data that can be pasted with + * `pressKeys( 'primary+v' )`. + * + * @param this + * @param clipboardData + * @param clipboardData.plainText + * @param clipboardData.html + */ +export function setClipboardData( + this: PageUtils, + { plainText = '', html = '' }: typeof clipboardDataHolder +) { + clipboardDataHolder = { + plainText, + html, + }; +} + +async function emulateClipboard( page: Page, type: 'copy' | 'cut' | 'paste' ) { + clipboardDataHolder = await page.evaluate( + ( [ _type, _clipboardData ] ) => { + const clipboardDataTransfer = new DataTransfer(); + + if ( _type === 'paste' ) { + clipboardDataTransfer.setData( + 'text/plain', + _clipboardData.plainText + ); + clipboardDataTransfer.setData( + 'text/html', + _clipboardData.html + ); + } else { + const selection = window.getSelection()!; + const plainText = selection.toString(); + let html = plainText; + if ( selection.rangeCount ) { + const range = selection.getRangeAt( 0 ); + const fragment = range.cloneContents(); + html = Array.from( fragment.childNodes ) + .map( + ( node ) => + ( node as Element ).outerHTML ?? node.nodeValue + ) + .join( '' ); + } + clipboardDataTransfer.setData( 'text/plain', plainText ); + clipboardDataTransfer.setData( 'text/html', html ); + } + + document.activeElement?.dispatchEvent( + new ClipboardEvent( _type, { + bubbles: true, + cancelable: true, + clipboardData: clipboardDataTransfer, + } ) + ); + + return { + plainText: clipboardDataTransfer.getData( 'text/plain' ), + html: clipboardDataTransfer.getData( 'text/html' ), + }; + }, + [ type, clipboardDataHolder ] as const + ); +} + +const isAppleOS = () => process.platform === 'darwin'; + +const isWebkit = ( page: Page ) => + page.context().browser()!.browserType().name() === 'webkit'; + +const browserCache = new WeakMap(); +const getHasNaturalTabNavigation = async ( page: Page ) => { + if ( ! isAppleOS() || ! isWebkit( page ) ) { + return true; + } + if ( browserCache.has( page.context().browser()! ) ) { + return browserCache.get( page.context().browser()! ); + } + const testPage = await page.context().newPage(); + await testPage.setContent( `` ); + await testPage.getByText( '1' ).focus(); + await testPage.keyboard.press( 'Tab' ); + const featureDetected = await testPage + .getByText( '2' ) + .evaluate( ( node ) => node === document.activeElement ); + browserCache.set( page.context().browser()!, featureDetected ); + await testPage.close(); + return featureDetected; +}; + +type Options = { + times?: number; + delay?: number; +}; + +const modifiers = { + ...baseModifiers, + shiftAlt: ( _isApple: () => boolean ) => + _isApple() ? [ SHIFT, ALT ] : [ SHIFT, CTRL ], +}; + +export async function pressKeys( + this: PageUtils, + key: string, + { times, ...pressOptions }: Options = {} +) { + const hasNaturalTabNavigation = await getHasNaturalTabNavigation( + this.page + ); + + let command: () => Promise< void >; + + if ( key.toLowerCase() === 'primary+c' ) { + command = () => emulateClipboard( this.page, 'copy' ); + } else if ( key.toLowerCase() === 'primary+x' ) { + command = () => emulateClipboard( this.page, 'cut' ); + } else if ( key.toLowerCase() === 'primary+v' ) { + command = () => emulateClipboard( this.page, 'paste' ); + } else { + const keys = key.split( '+' ).flatMap( ( keyCode ) => { + if ( Object.prototype.hasOwnProperty.call( modifiers, keyCode ) ) { + return modifiers[ keyCode as keyof typeof modifiers ]( + isAppleOS + ).map( ( modifier ) => + modifier === CTRL ? 'Control' : capitalCase( modifier ) + ); + } else if ( keyCode === 'Tab' && ! hasNaturalTabNavigation ) { + return [ 'Alt', 'Tab' ]; + } + return keyCode; + } ); + const normalizedKeys = keys.join( '+' ); + command = () => this.page.keyboard.press( normalizedKeys ); + } + + times = times ?? 1; + for ( let i = 0; i < times; i += 1 ) { + await command(); + + if ( times > 1 && pressOptions.delay ) { + await this.page.waitForTimeout( pressOptions.delay ); + } + } +} diff --git a/test/e2e/specs/editor/blocks/buttons.spec.js b/test/e2e/specs/editor/blocks/buttons.spec.js index 2362cc23ef6112..9d86e206f02193 100644 --- a/test/e2e/specs/editor/blocks/buttons.spec.js +++ b/test/e2e/specs/editor/blocks/buttons.spec.js @@ -50,7 +50,7 @@ test.describe( 'Buttons', () => { } ) => { // Regression: https://github.com/WordPress/gutenberg/pull/19885 await editor.insertBlock( { name: 'core/buttons' } ); - await pageUtils.pressKeyWithModifier( 'primary', 'k' ); + await pageUtils.pressKeys( 'primary+k' ); await expect( page.locator( 'role=combobox[name="URL"i]' ) ).toBeFocused(); @@ -78,7 +78,7 @@ test.describe( 'Buttons', () => { } ) => { // Regression: https://github.com/WordPress/gutenberg/issues/34307 await editor.insertBlock( { name: 'core/buttons' } ); - await pageUtils.pressKeyWithModifier( 'primary', 'k' ); + await pageUtils.pressKeys( 'primary+k' ); await expect( page.locator( 'role=combobox[name="URL"i]' ) ).toBeFocused(); @@ -107,7 +107,7 @@ test.describe( 'Buttons', () => { } ) => { // Regression: https://github.com/WordPress/gutenberg/issues/34307 await editor.insertBlock( { name: 'core/buttons' } ); - await pageUtils.pressKeyWithModifier( 'primary', 'k' ); + await pageUtils.pressKeys( 'primary+k' ); const urlInput = page.locator( 'role=combobox[name="URL"i]' ); @@ -116,7 +116,7 @@ test.describe( 'Buttons', () => { await page.keyboard.press( 'Enter' ); // Move to "Edit" and switch UI back to edit mode - await page.keyboard.press( 'Tab' ); + await pageUtils.pressKeys( 'Tab' ); await page.keyboard.press( 'Enter' ); // Check the value of the URL input has had http:// prepended. @@ -130,7 +130,7 @@ test.describe( 'Buttons', () => { } ) => { await editor.insertBlock( { name: 'core/buttons' } ); await page.keyboard.type( 'WordPress' ); - await pageUtils.pressKeyWithModifier( 'primary', 'k' ); + await pageUtils.pressKeys( 'primary+k' ); await page.keyboard.type( 'https://www.wordpress.org/' ); await page.keyboard.press( 'Enter' ); // Make sure that the dialog is still opened, and that focus is retained diff --git a/test/e2e/specs/editor/blocks/classic.spec.js b/test/e2e/specs/editor/blocks/classic.spec.js index 73691ac3da2996..559fbc93a1054a 100644 --- a/test/e2e/specs/editor/blocks/classic.spec.js +++ b/test/e2e/specs/editor/blocks/classic.spec.js @@ -32,7 +32,7 @@ test.describe( 'Classic', () => { await page.click( '.mce-content-body' ); await page.keyboard.type( 'test' ); // Move focus away. - await pageUtils.pressKeyWithModifier( 'shift', 'Tab' ); + await pageUtils.pressKeys( 'shift+Tab' ); await expect.poll( editor.getEditedPostContent ).toBe( 'test' ); } ); @@ -73,7 +73,7 @@ test.describe( 'Classic', () => { await createGallery.click(); await page.click( 'role=button[name="Insert gallery"i]' ); - await pageUtils.pressKeyWithModifier( 'shift', 'Tab' ); + await pageUtils.pressKeys( 'shift+Tab' ); await expect .poll( editor.getEditedPostContent ) .toMatch( /\[gallery ids=\"\d+\"\]/ ); @@ -89,7 +89,7 @@ test.describe( 'Classic', () => { await galleryBlock.focus(); // Check that you can undo back to a Classic block gallery in one step. - await pageUtils.pressKeyWithModifier( 'primary', 'z' ); + await pageUtils.pressKeys( 'primary+z' ); await expect( page.locator( 'role=document[name="Block: Classic"i]' ) ).toBeVisible(); @@ -119,7 +119,7 @@ test.describe( 'Classic', () => { await page.click( '.mce-content-body' ); await page.keyboard.type( 'test' ); // Move focus away. - await pageUtils.pressKeyWithModifier( 'shift', 'Tab' ); + await pageUtils.pressKeys( 'shift+Tab' ); await page.click( 'role=button[name="Save draft"i]' ); diff --git a/test/e2e/specs/editor/blocks/code.spec.js b/test/e2e/specs/editor/blocks/code.spec.js index 1fe91e3966f22b..80f41779b9131e 100644 --- a/test/e2e/specs/editor/blocks/code.spec.js +++ b/test/e2e/specs/editor/blocks/code.spec.js @@ -40,7 +40,7 @@ test.describe( 'Code', () => { // Test to see if HTML and white space is kept. pageUtils.setClipboardData( { plainText: '\n\t
' } ); - await pageUtils.pressKeyWithModifier( 'primary', 'v' ); + await pageUtils.pressKeys( 'primary+v' ); expect( await editor.getEditedPostContent() ).toMatchSnapshot(); } ); diff --git a/test/e2e/specs/editor/blocks/gallery.spec.js b/test/e2e/specs/editor/blocks/gallery.spec.js index 68093535b30629..1a71d3e46e6dd4 100644 --- a/test/e2e/specs/editor/blocks/gallery.spec.js +++ b/test/e2e/specs/editor/blocks/gallery.spec.js @@ -52,7 +52,7 @@ test.describe( 'Gallery', () => { } ); await page.click( 'role=button[name="Add default block"i]' ); - await pageUtils.pressKeyWithModifier( 'primary', 'v' ); + await pageUtils.pressKeys( 'primary+v' ); const img = page.locator( 'role=document[name="Block: Image"i] >> role=img' diff --git a/test/e2e/specs/editor/blocks/heading.spec.js b/test/e2e/specs/editor/blocks/heading.spec.js index 9d6e0db52f816a..e1b97d780f2929 100644 --- a/test/e2e/specs/editor/blocks/heading.spec.js +++ b/test/e2e/specs/editor/blocks/heading.spec.js @@ -196,9 +196,9 @@ test.describe( 'Heading', () => { await textAlignButton.click(); // Focus the block content - await page.keyboard.press( 'Tab' ); + await pageUtils.pressKeys( 'Tab' ); - await pageUtils.pressKeyWithModifier( 'access', '4' ); + await pageUtils.pressKeys( 'access+4' ); await expect.poll( editor.getBlocks ).toMatchObject( [ { name: 'core/heading', @@ -227,9 +227,9 @@ test.describe( 'Heading', () => { await textAlignButton.click(); // Focus the block content - await page.keyboard.press( 'Tab' ); + await pageUtils.pressKeys( 'Tab' ); - await pageUtils.pressKeyWithModifier( 'access', '2' ); + await pageUtils.pressKeys( 'access+2' ); await expect.poll( editor.getBlocks ).toMatchObject( [ { name: 'core/heading', @@ -259,9 +259,9 @@ test.describe( 'Heading', () => { await textAlignButton.click(); // Focus the block content - await page.keyboard.press( 'Tab' ); + await pageUtils.pressKeys( 'Tab' ); - await pageUtils.pressKeyWithModifier( 'access', '0' ); + await pageUtils.pressKeys( 'access+0' ); await expect.poll( editor.getBlocks ).toMatchObject( [ { diff --git a/test/e2e/specs/editor/blocks/image.spec.js b/test/e2e/specs/editor/blocks/image.spec.js index c5cee42cad16c5..3684980affb291 100644 --- a/test/e2e/specs/editor/blocks/image.spec.js +++ b/test/e2e/specs/editor/blocks/image.spec.js @@ -219,7 +219,7 @@ test.describe( 'Image', () => { // Add caption and navigate to inline toolbar. await editor.clickBlockToolbarButton( 'Add caption' ); - await pageUtils.pressKeyWithModifier( 'shift', 'Tab' ); + await pageUtils.pressKeys( 'shift+Tab' ); expect( await page.evaluate( () => document.activeElement.getAttribute( 'aria-label' ) @@ -327,12 +327,12 @@ test.describe( 'Image', () => { page.locator( 'role=slider[name="Zoom"i]' ) ).toBeFocused(); - await page.keyboard.press( 'Tab' ); + await pageUtils.pressKeys( 'Tab' ); await expect( page.locator( 'role=spinbutton[name="Zoom"i]' ) ).toBeFocused(); - await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await pageUtils.pressKeys( 'primary+a' ); await page.keyboard.type( '200' ); await page.keyboard.press( 'Escape' ); await editor.clickBlockToolbarButton( 'Apply' ); @@ -530,7 +530,7 @@ test.describe( 'Image', () => { await expect( image ).toHaveAttribute( 'src', new RegExp( filename ) ); await page.focus( '.wp-block-image' ); - await pageUtils.pressKeyWithModifier( 'primary', 'z' ); + await pageUtils.pressKeys( 'primary+z' ); // Expect an empty image block (placeholder) rather than one with a // broken temporary URL. diff --git a/test/e2e/specs/editor/blocks/list.spec.js b/test/e2e/specs/editor/blocks/list.spec.js index e0a6d62d3ccdd8..bdf366fd73f47b 100644 --- a/test/e2e/specs/editor/blocks/list.spec.js +++ b/test/e2e/specs/editor/blocks/list.spec.js @@ -40,7 +40,7 @@ test.describe( 'List (@firefox)', () => { // Create a list with the slash block shortcut. await page.click( 'role=button[name="Add default block"i]' ); await page.keyboard.type( 'test' ); - await pageUtils.pressKeyTimes( 'ArrowLeft', 4 ); + await pageUtils.pressKeys( 'ArrowLeft', { times: 4 } ); await page.keyboard.type( '* ' ); await expect.poll( editor.getEditedPostContent ).toBe( ` @@ -75,7 +75,7 @@ test.describe( 'List (@firefox)', () => { } ) => { await page.click( 'role=button[name="Add default block"i]' ); await page.keyboard.type( '1. ' ); - await pageUtils.pressKeyWithModifier( 'primary', 'z' ); + await pageUtils.pressKeys( 'primary+z' ); await expect.poll( editor.getEditedPostContent ).toBe( ` @@ -261,7 +261,7 @@ test.describe( 'List (@firefox)', () => { } ) => { await page.click( 'role=button[name="Add default block"i]' ); await page.keyboard.type( 'one' ); - await pageUtils.pressKeyWithModifier( 'shift', 'Enter' ); + await pageUtils.pressKeys( 'shift+Enter' ); await page.keyboard.type( 'two' ); await editor.transformBlockTo( 'core/list' ); @@ -285,7 +285,7 @@ test.describe( 'List (@firefox)', () => { } ) => { await page.click( 'role=button[name="Add default block"i]' ); await page.keyboard.type( 'one' ); - await pageUtils.pressKeyWithModifier( 'shift', 'Enter' ); + await pageUtils.pressKeys( 'shift+Enter' ); await page.keyboard.type( '...' ); await page.keyboard.press( 'Enter' ); await page.keyboard.type( 'two' ); @@ -336,7 +336,7 @@ test.describe( 'List (@firefox)', () => { await page.keyboard.press( 'Enter' ); await editor.clickBlockToolbarButton( 'Indent' ); await page.keyboard.type( 'two' ); - await pageUtils.pressKeyTimes( 'ArrowUp', 4 ); + await pageUtils.pressKeys( 'ArrowUp', { times: 4 } ); await editor.transformBlockTo( 'core/paragraph' ); await expect.poll( editor.getEditedPostContent ).toBe( @@ -396,7 +396,7 @@ test.describe( 'List (@firefox)', () => { ); await page.keyboard.type( 'two' ); - await pageUtils.pressKeyTimes( 'ArrowLeft', 'two'.length ); + await pageUtils.pressKeys( 'ArrowLeft', { times: 'two'.length } ); await page.keyboard.press( 'Backspace' ); await expect.poll( editor.getEditedPostContent ).toBe( @@ -465,7 +465,7 @@ test.describe( 'List (@firefox)', () => { // Should merge lists into one. await page.keyboard.press( 'ArrowDown' ); - await pageUtils.pressKeyTimes( 'ArrowLeft', 'two'.length ); + await pageUtils.pressKeys( 'ArrowLeft', { times: 'two'.length } ); await expect.poll( editor.getEditedPostContent ).toBe( ` @@ -759,7 +759,7 @@ test.describe( 'List (@firefox)', () => { ` ); - await pageUtils.pressKeyTimes( 'ArrowUp', 3 ); + await pageUtils.pressKeys( 'ArrowUp', { times: 3 } ); await editor.clickBlockToolbarButton( 'Outdent' ); await expect.poll( editor.getEditedPostContent ).toBe( @@ -786,7 +786,7 @@ test.describe( 'List (@firefox)', () => { } ) => { await editor.insertBlock( { name: 'core/list' } ); await page.keyboard.type( 'a' ); - await pageUtils.pressKeyWithModifier( 'shift', 'Enter' ); + await pageUtils.pressKeys( 'shift+Enter' ); await expect.poll( editor.getEditedPostContent ).toBe( ` @@ -809,7 +809,7 @@ test.describe( 'List (@firefox)', () => { await page.keyboard.press( 'Enter' ); await page.keyboard.type( 'c' ); await page.keyboard.press( 'ArrowUp' ); - await pageUtils.pressKeyWithModifier( 'shift', 'Enter' ); + await pageUtils.pressKeys( 'shift+Enter' ); await expect.poll( editor.getEditedPostContent ).toBe( ` @@ -950,7 +950,7 @@ test.describe( 'List (@firefox)', () => { await page.keyboard.type( '* 1' ); await page.keyboard.press( 'Enter' ); await page.keyboard.type( ' a' ); - await pageUtils.pressKeyTimes( 'ArrowUp', 2 ); + await pageUtils.pressKeys( 'ArrowUp', { times: 2 } ); await page.keyboard.press( 'Enter' ); // The caret should land in the second item. await page.keyboard.type( '2' ); @@ -981,7 +981,7 @@ test.describe( 'List (@firefox)', () => { await page.keyboard.type( '* 1' ); await page.keyboard.press( 'Enter' ); - await pageUtils.pressKeyWithModifier( 'shift', 'Space' ); + await pageUtils.pressKeys( 'shift+Space' ); await expect.poll( editor.getEditedPostContent ).toBe( ` @@ -1166,17 +1166,17 @@ test.describe( 'List (@firefox)', () => { pageUtils, } ) => { // Open code editor - await pageUtils.pressKeyWithModifier( 'secondary', 'M' ); // Emulates CTRL+Shift+Alt + M => toggle code editor + await pageUtils.pressKeys( 'secondary+M' ); // Emulates CTRL+Shift+Alt + M => toggle code editor // Paste empty list block pageUtils.setClipboardData( { plainText: '\n
\n', } ); - await pageUtils.pressKeyWithModifier( 'primary', 'v' ); + await pageUtils.pressKeys( 'primary+v' ); // Go back to normal editor - await pageUtils.pressKeyWithModifier( 'secondary', 'M' ); // Emulates CTRL+Shift+Alt + M => toggle code editor + await pageUtils.pressKeys( 'secondary+M' ); // Emulates CTRL+Shift+Alt + M => toggle code editor // Verify no WSOD and content is proper. expect( await editor.getEditedPostContent() ).toBe( ` diff --git a/test/e2e/specs/editor/blocks/quote.spec.js b/test/e2e/specs/editor/blocks/quote.spec.js index 299d56785d831b..279af60fe20949 100644 --- a/test/e2e/specs/editor/blocks/quote.spec.js +++ b/test/e2e/specs/editor/blocks/quote.spec.js @@ -58,7 +58,7 @@ test.describe( 'Quote', () => { } ) => { await page.click( 'role=button[name="Add default block"i]' ); await page.keyboard.type( 'test' ); - await pageUtils.pressKeyTimes( 'ArrowLeft', 'test'.length ); + await pageUtils.pressKeys( 'ArrowLeft', { times: 'test'.length } ); await page.keyboard.type( '> ' ); expect( await editor.getEditedPostContent() ).toBe( ` @@ -305,7 +305,7 @@ test.describe( 'Quote', () => { ` ); // Move the cursor to the start of the first paragraph of the quoted block. - await pageUtils.pressKeyTimes( 'ArrowLeft', 4 ); + await pageUtils.pressKeys( 'ArrowLeft', { times: 4 } ); await page.keyboard.press( 'Backspace' ); expect( await editor.getEditedPostContent() ).toBe( ` diff --git a/test/e2e/specs/editor/plugins/deprecated-node-matcher.spec.js b/test/e2e/specs/editor/plugins/deprecated-node-matcher.spec.js index fd57954e9905da..1466781bbf59aa 100644 --- a/test/e2e/specs/editor/plugins/deprecated-node-matcher.spec.js +++ b/test/e2e/specs/editor/plugins/deprecated-node-matcher.spec.js @@ -49,7 +49,7 @@ test.describe( 'Deprecated Node Matcher', () => { await page.keyboard.down( 'Shift' ); await page.keyboard.press( 'ArrowLeft' ); await page.keyboard.up( 'Shift' ); - await pageUtils.pressKeyWithModifier( 'primary', 'b' ); + await pageUtils.pressKeys( 'primary+b' ); expect( await editor.getEditedPostContent() ).toMatchSnapshot(); } ); } ); diff --git a/test/e2e/specs/editor/plugins/format-api.spec.js b/test/e2e/specs/editor/plugins/format-api.spec.js index 1b1d3b3d4173ff..21e942ddc2199b 100644 --- a/test/e2e/specs/editor/plugins/format-api.spec.js +++ b/test/e2e/specs/editor/plugins/format-api.spec.js @@ -23,7 +23,7 @@ test.describe( 'Using Format API', () => { } ) => { await page.click( 'role=button[name="Add default block"i]' ); await page.keyboard.type( 'First paragraph' ); - await pageUtils.pressKeyWithModifier( 'shiftAlt', 'ArrowLeft' ); + await pageUtils.pressKeys( 'shiftAlt+ArrowLeft' ); await editor.clickBlockToolbarButton( 'More' ); // Used a regex to tackle the  in name of menuitem.(Custom Link). diff --git a/test/e2e/specs/editor/plugins/post-type-templates.spec.js b/test/e2e/specs/editor/plugins/post-type-templates.spec.js index 3c5a664259f2d2..2caffdf52abe2e 100644 --- a/test/e2e/specs/editor/plugins/post-type-templates.spec.js +++ b/test/e2e/specs/editor/plugins/post-type-templates.spec.js @@ -69,7 +69,7 @@ test.describe( 'Post type templates', () => { 'My Empty Book' ); await page.keyboard.press( 'ArrowDown' ); - await pageUtils.pressKeyWithModifier( 'primary', 'A' ); + await pageUtils.pressKeys( 'primary+A' ); await page.keyboard.press( 'Backspace' ); await page.click( 'role=button[name="Save draft"i]' ); await expect( diff --git a/test/e2e/specs/editor/various/a11y.spec.js b/test/e2e/specs/editor/various/a11y.spec.js index b73ba5469c0d63..5725f216d1cf24 100644 --- a/test/e2e/specs/editor/various/a11y.spec.js +++ b/test/e2e/specs/editor/various/a11y.spec.js @@ -26,18 +26,18 @@ test.describe( 'a11y (@firefox, @webkit)', () => { page.locator( 'role=textbox[name=/Add title/i]' ) ).toBeFocused(); // Navigate to the 'Editor settings' region. - await pageUtils.pressKeyWithModifier( 'ctrl', '`' ); + await pageUtils.pressKeys( 'ctrl+`' ); // Navigate to the 'Editor publish' region. - await pageUtils.pressKeyWithModifier( 'ctrl', '`' ); + await pageUtils.pressKeys( 'ctrl+`' ); // Navigate to the 'Editor footer' region. - await pageUtils.pressKeyWithModifier( 'ctrl', '`' ); + await pageUtils.pressKeys( 'ctrl+`' ); // Navigate to the 'Editor top bar' region. - await pageUtils.pressKeyWithModifier( 'ctrl', '`' ); + await pageUtils.pressKeys( 'ctrl+`' ); // This test assumes the Editor is not in Fullscreen mode. Check the // first tabbable element within the 'Editor top bar' region is the // 'Toggle block inserter' button. - await page.keyboard.press( 'Tab' ); + await pageUtils.pressKeys( 'Tab' ); await expect( page.locator( 'role=button[name=/Toggle block inserter/i]' ) ).toBeFocused(); @@ -48,7 +48,7 @@ test.describe( 'a11y (@firefox, @webkit)', () => { pageUtils, } ) => { // Open keyboard shortcuts modal. - await pageUtils.pressKeyWithModifier( 'access', 'h' ); + await pageUtils.pressKeys( 'access+h' ); const modalContent = page.locator( 'role=dialog[name="Keyboard shortcuts"i] >> role=document' @@ -63,13 +63,13 @@ test.describe( 'a11y (@firefox, @webkit)', () => { await expect( closeButton ).not.toBeFocused(); // Open keyboard shortcuts modal. - await page.keyboard.press( 'Tab' ); + await pageUtils.pressKeys( 'Tab' ); await expect( modalContent ).toBeFocused(); - await page.keyboard.press( 'Tab' ); + await pageUtils.pressKeys( 'Tab' ); await expect( closeButton ).toBeFocused(); - await page.keyboard.press( 'Tab' ); + await pageUtils.pressKeys( 'Tab' ); await expect( modalContent ).toBeFocused(); } ); @@ -77,7 +77,7 @@ test.describe( 'a11y (@firefox, @webkit)', () => { page, pageUtils, } ) => { - await pageUtils.pressKeyWithModifier( 'access', 'h' ); + await pageUtils.pressKeys( 'access+h' ); // Click a non-focusable element after the last tabbable within the modal. const last = page @@ -85,7 +85,7 @@ test.describe( 'a11y (@firefox, @webkit)', () => { .last(); await last.click(); - await page.keyboard.press( 'Tab' ); + await pageUtils.pressKeys( 'Tab' ); await expect( page.locator( @@ -98,12 +98,12 @@ test.describe( 'a11y (@firefox, @webkit)', () => { page, pageUtils, } ) => { - await pageUtils.pressKeyWithModifier( 'access', 'h' ); + await pageUtils.pressKeys( 'access+h' ); // Click a non-focusable element before the first tabbable within the modal. await page.click( 'role=heading[name="Keyboard shortcuts"i]' ); - await pageUtils.pressKeyWithModifier( 'shift', 'Tab' ); + await pageUtils.pressKeys( 'shift+Tab' ); await expect( page.locator( @@ -114,6 +114,7 @@ test.describe( 'a11y (@firefox, @webkit)', () => { test( 'should make the modal content focusable when it is scrollable', async ( { page, + pageUtils, } ) => { // Open the top bar Options menu. await page.click( @@ -162,9 +163,9 @@ test.describe( 'a11y (@firefox, @webkit)', () => { // The General tab panel content is short and not scrollable. // Check it's not focusable. await clickAndFocusTab( generalTab ); - await page.keyboard.press( 'Shift+Tab' ); + await pageUtils.pressKeys( 'Shift+Tab' ); await expect( closeButton ).toBeFocused(); - await page.keyboard.press( 'Shift+Tab' ); + await pageUtils.pressKeys( 'Shift+Tab' ); await expect( preferencesModalContent ).not.toBeFocused(); // The Blocks tab panel content is long and scrollable. @@ -177,9 +178,9 @@ test.describe( 'a11y (@firefox, @webkit)', () => { 'tabindex', '0' ); - await page.keyboard.press( 'Shift+Tab' ); + await pageUtils.pressKeys( 'Shift+Tab' ); await expect( closeButton ).toBeFocused(); - await page.keyboard.press( 'Shift+Tab' ); + await pageUtils.pressKeys( 'Shift+Tab' ); await expect( preferencesModalContent ).toBeFocused(); // Make the Blocks tab panel content shorter by searching for a block @@ -191,17 +192,17 @@ test.describe( 'a11y (@firefox, @webkit)', () => { 'qwerty' ); await clickAndFocusTab( blocksTab ); - await page.keyboard.press( 'Shift+Tab' ); + await pageUtils.pressKeys( 'Shift+Tab' ); await expect( closeButton ).toBeFocused(); - await page.keyboard.press( 'Shift+Tab' ); + await pageUtils.pressKeys( 'Shift+Tab' ); await expect( preferencesModalContent ).not.toBeFocused(); // The Panels tab panel content is short and not scrollable. // Check it's not focusable. await clickAndFocusTab( panelsTab ); - await page.keyboard.press( 'Shift+Tab' ); + await pageUtils.pressKeys( 'Shift+Tab' ); await expect( closeButton ).toBeFocused(); - await page.keyboard.press( 'Shift+Tab' ); + await pageUtils.pressKeys( 'Shift+Tab' ); await expect( preferencesModalContent ).not.toBeFocused(); } ); } ); diff --git a/test/e2e/specs/editor/various/autocomplete-and-mentions.spec.js b/test/e2e/specs/editor/various/autocomplete-and-mentions.spec.js index 180ff719bb71e0..0176fb45982db8 100644 --- a/test/e2e/specs/editor/various/autocomplete-and-mentions.spec.js +++ b/test/e2e/specs/editor/various/autocomplete-and-mentions.spec.js @@ -152,7 +152,7 @@ test.describe( 'Autocomplete (@firefox, @webkit)', () => { 'role=button[name="Add default block"i]' ); await page.keyboard.type( 'Stuck in the middle with you.' ); - await pageUtils.pressKeyTimes( 'ArrowLeft', 'you.'.length ); + await pageUtils.pressKeys( 'ArrowLeft', { times: 'you.'.length } ); await page.keyboard.type( testData.triggerString ); await expect( page.locator( `role=option[name="${ testData.optionText }"i]` ) @@ -276,7 +276,7 @@ test.describe( 'Autocomplete (@firefox, @webkit)', () => { await expect( page.locator( `role=option[name="${ testData.optionText }"i]` ) ).toBeVisible(); - await pageUtils.pressKeyTimes( 'ArrowDown', 6 ); + await pageUtils.pressKeys( 'ArrowDown', { times: 6 } ); await page.keyboard.press( 'Enter' ); await expect @@ -462,9 +462,9 @@ test.describe( 'Autocomplete (@firefox, @webkit)', () => { } ) => { await editor.canvas.click( 'role=button[name="Add default block"i]' ); await page.keyboard.type( '@' ); - await pageUtils.pressKeyWithModifier( 'primary', 'b' ); + await pageUtils.pressKeys( 'primary+b' ); await page.keyboard.type( 'f' ); - await pageUtils.pressKeyWithModifier( 'primary', 'b' ); + await pageUtils.pressKeys( 'primary+b' ); await page.keyboard.type( 'r' ); await expect( page.locator( 'role=option', { hasText: 'Frodo Baggins' } ) diff --git a/test/e2e/specs/editor/various/block-deletion.spec.js b/test/e2e/specs/editor/various/block-deletion.spec.js index 78c63bfc64aac0..6e8329222d4183 100644 --- a/test/e2e/specs/editor/various/block-deletion.spec.js +++ b/test/e2e/specs/editor/various/block-deletion.spec.js @@ -138,7 +138,7 @@ test.describe( 'Block deletion', () => { ).toBeFocused(); // Remove the current paragraph via dedicated keyboard shortcut. - await pageUtils.pressKeyWithModifier( 'access', 'z' ); + await pageUtils.pressKeys( 'access+z' ); // Ensure the last block was removed. await expect.poll( editor.getBlocks ).toMatchObject( [ @@ -274,7 +274,7 @@ test.describe( 'Block deletion', () => { ).toBeFocused(); // Select the last two paragraphs. - await pageUtils.pressKeyWithModifier( 'shift', 'ArrowUp' ); + await pageUtils.pressKeys( 'shift+ArrowUp' ); await expect .poll( () => page.evaluate( () => 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 41626ed986b8f2..c215da33f3ec57 100644 --- a/test/e2e/specs/editor/various/content-only-lock.spec.js +++ b/test/e2e/specs/editor/various/content-only-lock.spec.js @@ -14,7 +14,7 @@ test.describe( 'Content-only lock', () => { pageUtils, } ) => { // Add content only locked block in the code editor - await pageUtils.pressKeyWithModifier( 'secondary', 'M' ); // Emulates CTRL+Shift+Alt + M => toggle 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( ` @@ -22,7 +22,7 @@ test.describe( 'Content-only lock', () => {

Hello

` ); - await pageUtils.pressKeyWithModifier( 'secondary', 'M' ); + await pageUtils.pressKeys( 'secondary+M' ); await page.click( 'role=document[name="Paragraph block"i]' ); await page.keyboard.type( ' World' ); diff --git a/test/e2e/specs/editor/various/copy-cut-paste.spec.js b/test/e2e/specs/editor/various/copy-cut-paste.spec.js index 5936071147e847..cb040504de4c81 100644 --- a/test/e2e/specs/editor/various/copy-cut-paste.spec.js +++ b/test/e2e/specs/editor/various/copy-cut-paste.spec.js @@ -18,11 +18,11 @@ test.describe( 'Copy/cut/paste', () => { await page.keyboard.press( 'Enter' ); await page.keyboard.type( '2' ); await page.keyboard.press( 'ArrowUp' ); - await pageUtils.pressKeyWithModifier( 'primary', 'c' ); + await pageUtils.pressKeys( 'primary+c' ); expect( await editor.getEditedPostContent() ).toMatchSnapshot(); await page.keyboard.press( 'ArrowDown' ); - await pageUtils.pressKeyWithModifier( 'primary', 'v' ); + await pageUtils.pressKeys( 'primary+v' ); expect( await editor.getEditedPostContent() ).toMatchSnapshot(); } ); @@ -36,12 +36,12 @@ test.describe( 'Copy/cut/paste', () => { await page.keyboard.press( 'Enter' ); await page.keyboard.type( '2' ); await page.keyboard.press( 'ArrowUp' ); - await pageUtils.pressKeyWithModifier( 'primary', 'x' ); + await pageUtils.pressKeys( 'primary+x' ); expect( await editor.getEditedPostContent() ).toMatchSnapshot(); - await page.keyboard.press( 'Tab' ); + await pageUtils.pressKeys( 'Tab' ); await page.keyboard.press( 'ArrowDown' ); - await pageUtils.pressKeyWithModifier( 'primary', 'v' ); + await pageUtils.pressKeys( 'primary+v' ); expect( await editor.getEditedPostContent() ).toMatchSnapshot(); } ); @@ -52,7 +52,7 @@ test.describe( 'Copy/cut/paste', () => { } ) => { await editor.insertBlock( { name: 'core/spacer' } ); // At this point the spacer wrapper should be focused. - await pageUtils.pressKeyWithModifier( 'primary', 'c' ); + await pageUtils.pressKeys( 'primary+c' ); expect( await editor.getEditedPostContent() ).toMatchSnapshot(); // The block appender is only visible when there's no selection. @@ -60,7 +60,7 @@ test.describe( 'Copy/cut/paste', () => { window.wp.data.dispatch( 'core/block-editor' ).clearSelectedBlock(); } ); await page.click( 'role=button[name="Add default block"i]' ); - await pageUtils.pressKeyWithModifier( 'primary', 'v' ); + await pageUtils.pressKeys( 'primary+v' ); expect( await editor.getEditedPostContent() ).toMatchSnapshot(); } ); @@ -71,7 +71,7 @@ test.describe( 'Copy/cut/paste', () => { } ) => { await editor.insertBlock( { name: 'core/spacer' } ); // At this point the spacer wrapper should be focused. - await pageUtils.pressKeyWithModifier( 'primary', 'x' ); + await pageUtils.pressKeys( 'primary+x' ); expect( await editor.getEditedPostContent() ).toBe( '' ); // The block appender is only visible when there's no selection. @@ -79,7 +79,7 @@ test.describe( 'Copy/cut/paste', () => { window.wp.data.dispatch( 'core/block-editor' ).clearSelectedBlock(); } ); await page.click( 'role=button[name="Add default block"i]' ); - await pageUtils.pressKeyWithModifier( 'primary', 'v' ); + await pageUtils.pressKeys( 'primary+v' ); expect( await editor.getEditedPostContent() ).toMatchSnapshot(); } ); @@ -94,15 +94,15 @@ test.describe( 'Copy/cut/paste', () => { await page.keyboard.press( 'Enter' ); await page.keyboard.type( 'Second block' ); await page.keyboard.press( 'ArrowUp' ); - await pageUtils.pressKeyWithModifier( 'shift', 'ArrowLeft' ); - await pageUtils.pressKeyWithModifier( 'shift', 'ArrowLeft' ); + await pageUtils.pressKeys( 'shift+ArrowLeft' ); + await pageUtils.pressKeys( 'shift+ArrowLeft' ); - await pageUtils.pressKeyWithModifier( 'primary', 'c' ); + await pageUtils.pressKeys( 'primary+c' ); await page.keyboard.press( 'ArrowRight' ); expect( await editor.getEditedPostContent() ).toMatchSnapshot(); await page.keyboard.press( 'Enter' ); - await pageUtils.pressKeyWithModifier( 'primary', 'v' ); + await pageUtils.pressKeys( 'primary+v' ); expect( await editor.getEditedPostContent() ).toMatchSnapshot(); } ); @@ -113,17 +113,17 @@ test.describe( 'Copy/cut/paste', () => { } ) => { await editor.insertBlock( { name: 'core/shortcode' } ); await page.keyboard.type( '[my-shortcode]' ); - await pageUtils.pressKeyWithModifier( 'shift', 'ArrowLeft' ); - await pageUtils.pressKeyWithModifier( 'shift', 'ArrowLeft' ); + await pageUtils.pressKeys( 'shift+ArrowLeft' ); + await pageUtils.pressKeys( 'shift+ArrowLeft' ); - await pageUtils.pressKeyWithModifier( 'primary', 'c' ); + await pageUtils.pressKeys( 'primary+c' ); await page.keyboard.press( 'ArrowRight' ); await page.keyboard.press( 'ArrowRight' ); expect( await editor.getEditedPostContent() ).toMatchSnapshot(); await editor.insertBlock( { name: 'core/paragraph' } ); await page.keyboard.type( 'Pasted: ' ); - await pageUtils.pressKeyWithModifier( 'primary', 'v' ); + await pageUtils.pressKeys( 'primary+v' ); expect( await editor.getEditedPostContent() ).toMatchSnapshot(); } ); @@ -143,7 +143,7 @@ test.describe( 'Copy/cut/paste', () => { ], } ); // Cut group. - await pageUtils.pressKeyWithModifier( 'primary', 'x' ); + await pageUtils.pressKeys( 'primary+x' ); expect( await editor.getEditedPostContent() ).toBe( '' ); await page.keyboard.press( 'Enter' ); @@ -170,7 +170,7 @@ test.describe( 'Copy/cut/paste', () => { } ); // Paste. - await pageUtils.pressKeyWithModifier( 'primary', 'v' ); + await pageUtils.pressKeys( 'primary+v' ); // Blocks should only be modified once, not twice with new clientIds on a single paste action. const blocksUpdated = await page.evaluate( @@ -197,7 +197,7 @@ test.describe( 'Copy/cut/paste', () => { ], } ); // Cut group. - await pageUtils.pressKeyWithModifier( 'primary', 'x' ); + await pageUtils.pressKeys( 'primary+x' ); expect( await editor.getEditedPostContent() ).toBe( '' ); // Insert a non textual element (a spacer) @@ -224,7 +224,7 @@ test.describe( 'Copy/cut/paste', () => { } ); } ); - await pageUtils.pressKeyWithModifier( 'primary', 'v' ); + await pageUtils.pressKeys( 'primary+v' ); // Paste should be handled on non-textual elements and only handled once. const blocksUpdated = await page.evaluate( @@ -246,10 +246,10 @@ test.describe( 'Copy/cut/paste', () => { await page.keyboard.type( 'B block' ); expect( await editor.getEditedPostContent() ).toMatchSnapshot(); // Partial select from both blocks. - await pageUtils.pressKeyTimes( 'ArrowLeft', 5 ); - await pageUtils.pressKeyWithModifier( 'shift', 'ArrowUp' ); - await pageUtils.pressKeyWithModifier( 'primary', 'c' ); - await pageUtils.pressKeyWithModifier( 'primary', 'ArrowLeft' ); + await pageUtils.pressKeys( 'ArrowLeft', { times: 5 } ); + await pageUtils.pressKeys( 'shift+ArrowUp' ); + await pageUtils.pressKeys( 'primary+c' ); + await pageUtils.pressKeys( 'primary+ArrowLeft' ); // Sometimes the caret has not moved to the correct position before pressing Enter. // @see https://github.com/WordPress/gutenberg/issues/40303#issuecomment-1109434887 await page.waitForFunction( @@ -258,7 +258,7 @@ test.describe( 'Copy/cut/paste', () => { // Create a new block at the top of the document to paste there. await page.keyboard.press( 'Enter' ); await page.keyboard.press( 'ArrowUp' ); - await pageUtils.pressKeyWithModifier( 'primary', 'v' ); + await pageUtils.pressKeys( 'primary+v' ); expect( await editor.getEditedPostContent() ).toMatchSnapshot(); } ); @@ -274,10 +274,10 @@ test.describe( 'Copy/cut/paste', () => { await page.keyboard.type( 'B block' ); expect( await editor.getEditedPostContent() ).toMatchSnapshot(); // Partial select from outer blocks. - await pageUtils.pressKeyTimes( 'ArrowLeft', 5 ); - await pageUtils.pressKeyWithModifier( 'shift', 'ArrowUp' ); - await pageUtils.pressKeyWithModifier( 'primary', 'c' ); - await pageUtils.pressKeyWithModifier( 'primary', 'ArrowLeft' ); + await pageUtils.pressKeys( 'ArrowLeft', { times: 5 } ); + await pageUtils.pressKeys( 'shift+ArrowUp' ); + await pageUtils.pressKeys( 'primary+c' ); + await pageUtils.pressKeys( 'primary+ArrowLeft' ); // Sometimes the caret has not moved to the correct position before pressing Enter. // @see https://github.com/WordPress/gutenberg/issues/40303#issuecomment-1109434887 await page.waitForFunction( @@ -286,7 +286,7 @@ test.describe( 'Copy/cut/paste', () => { // Create a new block at the top of the document to paste there. await page.keyboard.press( 'Enter' ); await page.keyboard.press( 'ArrowUp' ); - await pageUtils.pressKeyWithModifier( 'primary', 'v' ); + await pageUtils.pressKeys( 'primary+v' ); expect( await editor.getEditedPostContent() ).toMatchSnapshot(); } ); @@ -301,10 +301,10 @@ test.describe( 'Copy/cut/paste', () => { await page.keyboard.type( 'B block' ); expect( await editor.getEditedPostContent() ).toMatchSnapshot(); // Partial select from both blocks. - await pageUtils.pressKeyTimes( 'ArrowLeft', 5 ); - await pageUtils.pressKeyWithModifier( 'shift', 'ArrowUp' ); - await pageUtils.pressKeyWithModifier( 'primary', 'x' ); - await pageUtils.pressKeyWithModifier( 'primary', 'ArrowLeft' ); + await pageUtils.pressKeys( 'ArrowLeft', { times: 5 } ); + await pageUtils.pressKeys( 'shift+ArrowUp' ); + await pageUtils.pressKeys( 'primary+x' ); + await pageUtils.pressKeys( 'primary+ArrowLeft' ); // Sometimes the caret has not moved to the correct position before pressing Enter. // @see https://github.com/WordPress/gutenberg/issues/40303#issuecomment-1109434887 await page.waitForFunction( @@ -313,7 +313,7 @@ test.describe( 'Copy/cut/paste', () => { // Create a new block at the top of the document to paste there. await page.keyboard.press( 'Enter' ); await page.keyboard.press( 'ArrowUp' ); - await pageUtils.pressKeyWithModifier( 'primary', 'v' ); + await pageUtils.pressKeys( 'primary+v' ); expect( await editor.getEditedPostContent() ).toMatchSnapshot(); } ); @@ -329,10 +329,10 @@ test.describe( 'Copy/cut/paste', () => { await page.keyboard.type( 'B block' ); expect( await editor.getEditedPostContent() ).toMatchSnapshot(); // Partial select from outer blocks. - await pageUtils.pressKeyTimes( 'ArrowLeft', 5 ); - await pageUtils.pressKeyWithModifier( 'shift', 'ArrowUp' ); - await pageUtils.pressKeyWithModifier( 'primary', 'x' ); - await pageUtils.pressKeyWithModifier( 'primary', 'ArrowLeft' ); + await pageUtils.pressKeys( 'ArrowLeft', { times: 5 } ); + await pageUtils.pressKeys( 'shift+ArrowUp' ); + await pageUtils.pressKeys( 'primary+x' ); + await pageUtils.pressKeys( 'primary+ArrowLeft' ); // Sometimes the caret has not moved to the correct position before pressing Enter. // @see https://github.com/WordPress/gutenberg/issues/40303#issuecomment-1109434887 await page.waitForFunction( @@ -341,7 +341,7 @@ test.describe( 'Copy/cut/paste', () => { // Create a new block at the top of the document to paste there. await page.keyboard.press( 'Enter' ); await page.keyboard.press( 'ArrowUp' ); - await pageUtils.pressKeyWithModifier( 'primary', 'v' ); + await pageUtils.pressKeys( 'primary+v' ); expect( await editor.getEditedPostContent() ).toMatchSnapshot(); } ); @@ -356,10 +356,10 @@ test.describe( 'Copy/cut/paste', () => { await page.keyboard.type( 'Paragraph' ); expect( await editor.getEditedPostContent() ).toMatchSnapshot(); // Partial select from outer blocks. - await pageUtils.pressKeyTimes( 'ArrowLeft', 2 ); - await pageUtils.pressKeyWithModifier( 'shift', 'ArrowUp' ); - await pageUtils.pressKeyWithModifier( 'primary', 'x' ); - await pageUtils.pressKeyWithModifier( 'primary', 'ArrowLeft' ); + await pageUtils.pressKeys( 'ArrowLeft', { times: 2 } ); + await pageUtils.pressKeys( 'shift+ArrowUp' ); + await pageUtils.pressKeys( 'primary+x' ); + await pageUtils.pressKeys( 'primary+ArrowLeft' ); // Sometimes the caret has not moved to the correct position before pressing Enter. // @see https://github.com/WordPress/gutenberg/issues/40303#issuecomment-1109434887 await page.waitForFunction( @@ -368,7 +368,7 @@ test.describe( 'Copy/cut/paste', () => { // Create a new block at the top of the document to paste there. await page.keyboard.press( 'Enter' ); await page.keyboard.press( 'ArrowUp' ); - await pageUtils.pressKeyWithModifier( 'primary', 'v' ); + await pageUtils.pressKeys( 'primary+v' ); expect( await editor.getEditedPostContent() ).toMatchSnapshot(); } ); @@ -383,10 +383,10 @@ test.describe( 'Copy/cut/paste', () => { await page.keyboard.type( 'Paragraph' ); expect( await editor.getEditedPostContent() ).toMatchSnapshot(); // Partial select from outer blocks. - await pageUtils.pressKeyTimes( 'ArrowLeft', 2 ); - await pageUtils.pressKeyWithModifier( 'shift', 'ArrowUp' ); - await pageUtils.pressKeyWithModifier( 'primary', 'c' ); - await pageUtils.pressKeyWithModifier( 'primary', 'ArrowLeft' ); + await pageUtils.pressKeys( 'ArrowLeft', { times: 2 } ); + await pageUtils.pressKeys( 'shift+ArrowUp' ); + await pageUtils.pressKeys( 'primary+c' ); + await pageUtils.pressKeys( 'primary+ArrowLeft' ); // Sometimes the caret has not moved to the correct position before pressing Enter. // @see https://github.com/WordPress/gutenberg/issues/40303#issuecomment-1109434887 await page.waitForFunction( @@ -394,7 +394,7 @@ test.describe( 'Copy/cut/paste', () => { ); // Create a new code block to paste there. await editor.insertBlock( { name: 'core/code' } ); - await pageUtils.pressKeyWithModifier( 'primary', 'v' ); + await pageUtils.pressKeys( 'primary+v' ); expect( await editor.getEditedPostContent() ).toMatchSnapshot(); } ); @@ -410,7 +410,7 @@ test.describe( 'Copy/cut/paste', () => { pageUtils.setClipboardData( { html: 'Hello World', } ); - await pageUtils.pressKeyWithModifier( 'primary', 'v' ); + await pageUtils.pressKeys( 'primary+v' ); // Expect the span to be filtered out. expect( await page.evaluate( () => document.activeElement.innerHTML ) @@ -426,7 +426,7 @@ test.describe( 'Copy/cut/paste', () => { pageUtils.setClipboardData( { html: 'x', } ); - await pageUtils.pressKeyWithModifier( 'primary', 'v' ); + await pageUtils.pressKeys( 'primary+v' ); // Ensure the selection is correct. await page.keyboard.type( 'y' ); expect( @@ -443,7 +443,7 @@ test.describe( 'Copy/cut/paste', () => { html: '
x
', } ); await editor.insertBlock( { name: 'core/list' } ); - await pageUtils.pressKeyWithModifier( 'primary', 'v' ); + await pageUtils.pressKeys( 'primary+v' ); // Ensure the selection is correct. await page.keyboard.type( 'y' ); expect( await editor.getEditedPostContent() ).toMatchSnapshot(); diff --git a/test/e2e/specs/editor/various/duplicating-blocks.spec.js b/test/e2e/specs/editor/various/duplicating-blocks.spec.js index 1878db2013dc44..aed4fae223894c 100644 --- a/test/e2e/specs/editor/various/duplicating-blocks.spec.js +++ b/test/e2e/specs/editor/various/duplicating-blocks.spec.js @@ -19,7 +19,7 @@ test.describe( 'Duplicating blocks', () => { // Select the test we just typed // This doesn't do anything but we previously had a duplicationi bug // When the selection was not collapsed. - await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await pageUtils.pressKeys( 'primary+a' ); await editor.clickBlockToolbarButton( 'Options' ); await page.click( 'role=menuitem[name=/Duplicate/i]' ); @@ -46,10 +46,10 @@ test.describe( 'Duplicating blocks', () => { // Select the test we just typed // This doesn't do anything but we previously had a duplicationi bug // When the selection was not collapsed. - await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await pageUtils.pressKeys( 'primary+a' ); // Duplicate using the keyboard shortccut. - await pageUtils.pressKeyWithModifier( 'primaryShift', 'd' ); + await pageUtils.pressKeys( 'primaryShift+d' ); expect( await editor.getEditedPostContent() ).toBe( ` diff --git a/test/e2e/specs/editor/various/font-size-picker.spec.js b/test/e2e/specs/editor/various/font-size-picker.spec.js index df16108afcf180..e63a5984443bca 100644 --- a/test/e2e/specs/editor/various/font-size-picker.spec.js +++ b/test/e2e/specs/editor/various/font-size-picker.spec.js @@ -58,7 +58,7 @@ test.describe( 'Font Size Picker', () => {

Paragraph reset - custom size

` ); - await pageUtils.pressKeyTimes( 'Backspace', 2 ); + await pageUtils.pressKeys( 'Backspace', { times: 2 } ); await expect.poll( editor.getEditedPostContent ) .toBe( `

Paragraph reset - custom size

@@ -140,7 +140,7 @@ test.describe( 'Font Size Picker', () => { await page.click( 'role=group[name="Font size"i] >> role=button[name="Font size"i]' ); - await pageUtils.pressKeyTimes( 'ArrowDown', 4 ); + await pageUtils.pressKeys( 'ArrowDown', { times: 4 } ); await page.keyboard.press( 'Enter' ); await expect.poll( editor.getEditedPostContent ) @@ -162,7 +162,7 @@ test.describe( 'Font Size Picker', () => { await page.click( 'role=group[name="Font size"i] >> role=button[name="Font size"i]' ); - await pageUtils.pressKeyTimes( 'ArrowDown', 3 ); + await pageUtils.pressKeys( 'ArrowDown', { times: 3 } ); await page.keyboard.press( 'Enter' ); await expect.poll( editor.getEditedPostContent ) @@ -193,7 +193,7 @@ test.describe( 'Font Size Picker', () => { await page.click( 'role=group[name="Font size"i] >> role=button[name="Font size"i]' ); - await pageUtils.pressKeyTimes( 'ArrowDown', 2 ); + await pageUtils.pressKeys( 'ArrowDown', { times: 2 } ); await page.keyboard.press( 'Enter' ); await expect.poll( editor.getEditedPostContent ) @@ -205,7 +205,7 @@ test.describe( 'Font Size Picker', () => { 'role=region[name="Editor settings"i] >> role=button[name="Set custom size"i]' ); await page.click( 'role=spinbutton[name="Custom"i]' ); - await pageUtils.pressKeyWithModifier( 'primary', 'A' ); + await pageUtils.pressKeys( 'primary+A' ); await page.keyboard.press( 'Backspace' ); await expect.poll( editor.getEditedPostContent ) @@ -284,7 +284,7 @@ test.describe( 'Font Size Picker', () => { 'role=region[name="Editor settings"i] >> role=button[name="Set custom size"i]' ); await page.click( 'role=spinbutton[name="Custom"i]' ); - await pageUtils.pressKeyWithModifier( 'primary', 'A' ); + await pageUtils.pressKeys( 'primary+A' ); await page.keyboard.press( 'Backspace' ); await expect.poll( editor.getEditedPostContent ) diff --git a/test/e2e/specs/editor/various/list-view.spec.js b/test/e2e/specs/editor/various/list-view.spec.js index 46e86709c41313..906159c944d1e7 100644 --- a/test/e2e/specs/editor/various/list-view.spec.js +++ b/test/e2e/specs/editor/various/list-view.spec.js @@ -19,7 +19,7 @@ test.describe( 'List View', () => { await editor.insertBlock( { name: 'core/paragraph' } ); // Open List View. - await pageUtils.pressKeyWithModifier( 'access', 'o' ); + await pageUtils.pressKeys( 'access+o' ); const listView = page.getByRole( 'treegrid', { name: 'Block navigation structure', } ); @@ -72,7 +72,7 @@ test.describe( 'List View', () => { await editor.insertBlock( { name: 'core/paragraph' } ); // Open List View. - await pageUtils.pressKeyWithModifier( 'access', 'o' ); + await pageUtils.pressKeys( 'access+o' ); const listView = page.getByRole( 'treegrid', { name: 'Block navigation structure', } ); @@ -86,7 +86,7 @@ test.describe( 'List View', () => { ).toBeVisible(); // Go to the image block in List View. - await pageUtils.pressKeyTimes( 'ArrowUp', 2 ); + await pageUtils.pressKeys( 'ArrowUp', { times: 2 } ); await expect( listView .getByRole( 'gridcell', { @@ -127,7 +127,7 @@ test.describe( 'List View', () => { await editor.insertBlock( { name: 'core/paragraph' } ); // Open List View. - await pageUtils.pressKeyWithModifier( 'access', 'o' ); + await pageUtils.pressKeys( 'access+o' ); const listView = page.getByRole( 'treegrid', { name: 'Block navigation structure', } ); @@ -168,7 +168,7 @@ test.describe( 'List View', () => { await editor.insertBlock( { name: 'core/paragraph' } ); // Open List View. - await pageUtils.pressKeyWithModifier( 'access', 'o' ); + await pageUtils.pressKeys( 'access+o' ); const listView = page.getByRole( 'treegrid', { name: 'Block navigation structure', } ); @@ -182,7 +182,7 @@ test.describe( 'List View', () => { ).toBeVisible(); // Select the image block in List View. - await pageUtils.pressKeyTimes( 'ArrowUp', 2 ); + await pageUtils.pressKeys( 'ArrowUp', { times: 2 } ); await expect( listView .getByRole( 'gridcell', { @@ -221,7 +221,7 @@ test.describe( 'List View', () => { await editor.insertBlock( { name: 'core/heading' } ); // Open List View. - await pageUtils.pressKeyWithModifier( 'access', 'o' ); + await pageUtils.pressKeys( 'access+o' ); const listView = page.getByRole( 'treegrid', { name: 'Block navigation structure', } ); @@ -235,7 +235,7 @@ test.describe( 'List View', () => { ).toBeVisible(); // Select the Image block as well. - await pageUtils.pressKeyWithModifier( 'shift', 'ArrowUp' ); + await pageUtils.pressKeys( 'shift+ArrowUp' ); await expect( listView.getByRole( 'gridcell', { name: 'Image link', @@ -271,7 +271,7 @@ test.describe( 'List View', () => { .click(); // Open List View. - await pageUtils.pressKeyWithModifier( 'access', 'o' ); + await pageUtils.pressKeys( 'access+o' ); const listView = page.getByRole( 'treegrid', { name: 'Block navigation structure', } ); @@ -341,7 +341,7 @@ test.describe( 'List View', () => { await editor.insertBlock( { name: 'core/group' } ); // Open List View. - await pageUtils.pressKeyWithModifier( 'access', 'o' ); + await pageUtils.pressKeys( 'access+o' ); const listView = page.getByRole( 'treegrid', { name: 'Block navigation structure', } ); @@ -409,7 +409,7 @@ test.describe( 'List View', () => { ).toBeFocused(); // Open List View. - await pageUtils.pressKeyWithModifier( 'access', 'o' ); + await pageUtils.pressKeys( 'access+o' ); const listView = page.getByRole( 'treegrid', { name: 'Block navigation structure', } ); @@ -445,12 +445,12 @@ test.describe( 'List View', () => { // Since focus is now at the image block upload button in the canvas, // pressing the list view shortcut should bring focus back to the image // block in the list view. - await pageUtils.pressKeyWithModifier( 'access', 'o' ); + await pageUtils.pressKeys( 'access+o' ); await expect( imageItem ).toBeFocused(); // Since focus is now inside the list view, the shortcut should close // the sidebar. - await pageUtils.pressKeyWithModifier( 'access', 'o' ); + await pageUtils.pressKeys( 'access+o' ); // Focus should now be on the paragraph block since that is // where we opened the list view sidebar. This is not a perfect @@ -464,15 +464,15 @@ test.describe( 'List View', () => { await expect( listView ).not.toBeVisible(); // Open List View. - await pageUtils.pressKeyWithModifier( 'access', 'o' ); + await pageUtils.pressKeys( 'access+o' ); // Focus the list view close button and make sure the shortcut will // close the list view. This is to catch a bug where elements could be // out of range of the sidebar region. Must shift+tab 3 times to reach // close button before tabs. - await pageUtils.pressKeyWithModifier( 'shift', 'Tab' ); - await pageUtils.pressKeyWithModifier( 'shift', 'Tab' ); - await pageUtils.pressKeyWithModifier( 'shift', 'Tab' ); + await pageUtils.pressKeys( 'shift+Tab' ); + await pageUtils.pressKeys( 'shift+Tab' ); + await pageUtils.pressKeys( 'shift+Tab' ); await expect( editor.canvas.getByRole( 'button', { name: 'Close Document Overview Sidebar', @@ -480,15 +480,15 @@ test.describe( 'List View', () => { ).toBeFocused(); // Close List View and ensure it's closed. - await pageUtils.pressKeyWithModifier( 'access', 'o' ); + await pageUtils.pressKeys( 'access+o' ); await expect( listView ).not.toBeVisible(); // Open List View. - await pageUtils.pressKeyWithModifier( 'access', 'o' ); + await pageUtils.pressKeys( 'access+o' ); // Focus the outline tab and select it. This test ensures the outline // tab receives similar focus events based on the shortcut. - await pageUtils.pressKeyWithModifier( 'shift', 'Tab' ); + await pageUtils.pressKeys( 'shift+Tab' ); const outlineButton = editor.canvas.getByRole( 'button', { name: 'Outline', } ); @@ -497,14 +497,14 @@ test.describe( 'List View', () => { // From here, tab in to the editor so focus can be checked on return to // the outline tab in the sidebar. - await pageUtils.pressKeyTimes( 'Tab', 2 ); + await pageUtils.pressKeys( 'Tab', { times: 2 } ); // Focus should be placed on the outline tab button since there is // nothing to focus inside the tab itself. - await pageUtils.pressKeyWithModifier( 'access', 'o' ); + await pageUtils.pressKeys( 'access+o' ); await expect( outlineButton ).toBeFocused(); // Close List View and ensure it's closed. - await pageUtils.pressKeyWithModifier( 'access', 'o' ); + await pageUtils.pressKeys( 'access+o' ); await expect( listView ).not.toBeVisible(); } ); @@ -519,7 +519,7 @@ test.describe( 'List View', () => { await editor.insertBlock( { name: 'core/paragraph' } ); // Open List View. - await pageUtils.pressKeyWithModifier( 'access', 'o' ); + await pageUtils.pressKeys( 'access+o' ); const listView = page.getByRole( 'treegrid', { name: 'Block navigation structure', } ); @@ -533,7 +533,7 @@ test.describe( 'List View', () => { ).toBeVisible(); // Go to the image block in List View. - await pageUtils.pressKeyTimes( 'ArrowUp', 2 ); + await pageUtils.pressKeys( 'ArrowUp', { times: 2 } ); await expect( listView .getByRole( 'gridcell', { @@ -552,7 +552,7 @@ test.describe( 'List View', () => { ).toBeFocused(); // Triggering the List View shortcut should result in the image block gaining focus. - await pageUtils.pressKeyWithModifier( 'access', 'o' ); + await pageUtils.pressKeys( 'access+o' ); await expect( listView .getByRole( 'gridcell', { diff --git a/test/e2e/specs/editor/various/mentions.spec.js b/test/e2e/specs/editor/various/mentions.spec.js index 878cf148e8abf5..b7e75e046c471a 100644 --- a/test/e2e/specs/editor/various/mentions.spec.js +++ b/test/e2e/specs/editor/various/mentions.spec.js @@ -44,7 +44,7 @@ test.describe( 'autocomplete mentions', () => { } ) => { await page.click( 'role=button[name="Add default block"i]' ); await page.keyboard.type( 'Stuck in the middle with you' ); - await pageUtils.pressKeyTimes( 'ArrowLeft', 'you'.length ); + await pageUtils.pressKeys( 'ArrowLeft', { times: 'you'.length } ); await page.keyboard.type( '@j' ); await expect( page.locator( 'role=listbox >> role=option[name=/testuser/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 d651001c8f2640..3e03a0c2805620 100644 --- a/test/e2e/specs/editor/various/multi-block-selection.spec.js +++ b/test/e2e/specs/editor/various/multi-block-selection.spec.js @@ -35,8 +35,8 @@ test.describe( 'Multi-block selection', () => { } // Multiselect via keyboard. - await pageUtils.pressKeyWithModifier( 'primary', 'a' ); - await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await pageUtils.pressKeys( 'primary+a' ); + await pageUtils.pressKeys( 'primary+a' ); await expect .poll( multiBlockSelectionUtils.getSelectedFlatIndices ) @@ -67,10 +67,10 @@ test.describe( 'Multi-block selection', () => { .getByRole( 'document', { name: 'Empty block' } ) .click(); // Move to the middle of the first line. - await pageUtils.pressKeyTimes( 'ArrowUp', 4 ); + await pageUtils.pressKeys( 'ArrowUp', { times: 4 } ); await page.keyboard.press( 'ArrowRight' ); // Select mid line one to mid line four. - await pageUtils.pressKeyTimes( 'Shift+ArrowDown', 3 ); + await pageUtils.pressKeys( 'Shift+ArrowDown', { times: 3 } ); // Delete the text to see if the selection was correct. await page.keyboard.press( 'Backspace' ); @@ -227,8 +227,8 @@ test.describe( 'Multi-block selection', () => { attributes: { content: `${ i }` }, } ); } - await pageUtils.pressKeyWithModifier( 'primary', 'a' ); - await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await pageUtils.pressKeys( 'primary+a' ); + await pageUtils.pressKeys( 'primary+a' ); await expect .poll( multiBlockSelectionUtils.getSelectedFlatIndices ) @@ -308,7 +308,7 @@ test.describe( 'Multi-block selection', () => { await editor.canvas .getByRole( 'document', { name: 'Paragraph block' } ) .click( { modifiers: [ 'Shift' ] } ); - await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await pageUtils.pressKeys( 'primary+a' ); await page.keyboard.type( 'new content' ); await expect.poll( editor.getBlocks ).toMatchObject( [ @@ -468,13 +468,13 @@ test.describe( 'Multi-block selection', () => { attributes: { content: '2' }, } ); - await pageUtils.pressKeyWithModifier( 'primary', 'a' ); - await pageUtils.pressKeyWithModifier( 'primary', 'a' ); - await pageUtils.pressKeyWithModifier( 'primary', 'x' ); + await pageUtils.pressKeys( 'primary+a' ); + await pageUtils.pressKeys( 'primary+a' ); + await pageUtils.pressKeys( 'primary+x' ); await expect.poll( editor.getBlocks ).toEqual( [] ); - await pageUtils.pressKeyWithModifier( 'primary', 'v' ); + await pageUtils.pressKeys( 'primary+v' ); await expect.poll( editor.getBlocks ).toMatchObject( [ { name: 'core/paragraph', attributes: { content: '1' } }, @@ -492,16 +492,16 @@ test.describe( 'Multi-block selection', () => { attributes: { content: 'second paragraph' }, } ); - await pageUtils.pressKeyWithModifier( 'primary', 'a' ); - await pageUtils.pressKeyWithModifier( 'primary', 'a' ); - await pageUtils.pressKeyWithModifier( 'primary', 'c' ); + await pageUtils.pressKeys( 'primary+a' ); + await pageUtils.pressKeys( 'primary+a' ); + await pageUtils.pressKeys( 'primary+c' ); await page.keyboard.press( 'Backspace' ); await expect .poll( editor.getBlocks, 'should select all blocks after copying' ) .toEqual( [] ); - await pageUtils.pressKeyWithModifier( 'primary', 'v' ); + await pageUtils.pressKeys( 'primary+v' ); await expect .poll( editor.getBlocks, 'should copy and paste multiple blocks' ) .toMatchObject( [ @@ -520,9 +520,9 @@ test.describe( 'Multi-block selection', () => { { attributes: { content: 'second paragraph|' } }, ] ); - await pageUtils.pressKeyWithModifier( 'primary', 'a' ); - await pageUtils.pressKeyWithModifier( 'primary', 'a' ); - await pageUtils.pressKeyWithModifier( 'primary', 'v' ); + await pageUtils.pressKeys( 'primary+a' ); + await pageUtils.pressKeys( 'primary+a' ); + await pageUtils.pressKeys( 'primary+v' ); await page.keyboard.type( '|' ); await expect .poll( editor.getBlocks, 'should replace blocks' ) @@ -532,8 +532,8 @@ test.describe( 'Multi-block selection', () => { ] ); await page.keyboard.press( 'Backspace' ); - await pageUtils.pressKeyTimes( 'ArrowLeft', 3 ); - await pageUtils.pressKeyWithModifier( 'primary', 'v' ); + await pageUtils.pressKeys( 'ArrowLeft', { times: 3 } ); + await pageUtils.pressKeys( 'primary+v' ); await page.keyboard.type( '|' ); await expect .poll( editor.getBlocks, 'should paste mid-block' ) @@ -689,8 +689,8 @@ test.describe( 'Multi-block selection', () => { attributes: { content: `${ i }` }, } ); } - await pageUtils.pressKeyWithModifier( 'primary', 'a' ); - await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await pageUtils.pressKeys( 'primary+a' ); + await pageUtils.pressKeys( 'primary+a' ); await page .getByRole( 'toolbar', { name: 'Block tools' } ) @@ -720,8 +720,8 @@ test.describe( 'Multi-block selection', () => { .click(); await page.keyboard.type( '1' ); - await pageUtils.pressKeyWithModifier( 'primary', 'a' ); - await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await pageUtils.pressKeys( 'primary+a' ); + await pageUtils.pressKeys( 'primary+a' ); await page.keyboard.press( 'Enter' ); await expect.poll( editor.getBlocks ).toMatchObject( [ @@ -761,8 +761,8 @@ test.describe( 'Multi-block selection', () => { .nth( 1 ) .click(); - await pageUtils.pressKeyWithModifier( 'primary', 'a' ); - await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await pageUtils.pressKeys( 'primary+a' ); + await pageUtils.pressKeys( 'primary+a' ); await expect .poll( multiBlockSelectionUtils.getSelectedBlocks ) @@ -771,13 +771,13 @@ test.describe( 'Multi-block selection', () => { { name: 'core/paragraph' }, ] ); - await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await pageUtils.pressKeys( 'primary+a' ); await expect .poll( multiBlockSelectionUtils.getSelectedBlocks ) .toMatchObject( [ { name: 'core/column' } ] ); - await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await pageUtils.pressKeys( 'primary+a' ); await expect .poll( multiBlockSelectionUtils.getSelectedBlocks ) @@ -818,17 +818,17 @@ test.describe( 'Multi-block selection', () => { .getByRole( 'textbox' ) .click(); - await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await pageUtils.pressKeys( 'primary+a' ); await expect .poll( multiBlockSelectionUtils.getSelectedBlocks ) .toMatchObject( [ { name: 'core/list-item' } ] ); - await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await pageUtils.pressKeys( 'primary+a' ); await expect .poll( multiBlockSelectionUtils.getSelectedBlocks ) .toMatchObject( [ { name: 'core/list' } ] ); - await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await pageUtils.pressKeys( 'primary+a' ); await expect .poll( multiBlockSelectionUtils.getSelectedBlocks ) .toMatchObject( [ @@ -858,7 +858,7 @@ test.describe( 'Multi-block selection', () => { .poll( multiBlockSelectionUtils.getSelectedBlocks ) .toEqual( [] ); - await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await pageUtils.pressKeys( 'primary+a' ); await page.keyboard.press( 'Backspace' ); @@ -883,7 +883,7 @@ test.describe( 'Multi-block selection', () => { .getByRole( 'textbox', { name: 'Add title' } ) .type( 'Post title' ); - await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await pageUtils.pressKeys( 'primary+a' ); await expect .poll( multiBlockSelectionUtils.getSelectedBlocks ) @@ -958,7 +958,7 @@ test.describe( 'Multi-block selection', () => { navButtons.nth( 3 ).getByRole( 'link', { includeHidden: true } ) ).toBeFocused(); // Press Up twice to highlight the second block. - await pageUtils.pressKeyTimes( 'ArrowUp', 2 ); + await pageUtils.pressKeys( 'ArrowUp', { times: 2 } ); await page.keyboard.press( 'Shift+ArrowDown' ); await expect @@ -976,7 +976,7 @@ test.describe( 'Multi-block selection', () => { ) .toEqual( [ 2, 3, 4 ] ); - await pageUtils.pressKeyTimes( 'Shift+ArrowUp', 3 ); + await pageUtils.pressKeys( 'Shift+ArrowUp', { times: 3 } ); await expect .poll( multiBlockSelectionUtils.getSelectedFlatIndices, @@ -985,7 +985,7 @@ test.describe( 'Multi-block selection', () => { .toEqual( [ 1, 2 ] ); // Navigate to the bottom of the list of blocks. - await pageUtils.pressKeyTimes( 'ArrowDown', 3 ); + await pageUtils.pressKeys( 'ArrowDown', { times: 3 } ); // This tests that shift selecting blocks by keyboard that are not adjacent // to an existing selection resets the selection. @@ -1022,7 +1022,7 @@ test.describe( 'Multi-block selection', () => { await page.keyboard.press( 'ArrowLeft' ); // Select everything between []. - await pageUtils.pressKeyTimes( 'Shift+ArrowLeft', 5 ); + await pageUtils.pressKeys( 'Shift+ArrowLeft', { times: 5 } ); await page.keyboard.press( 'Delete' ); @@ -1049,7 +1049,7 @@ test.describe( 'Multi-block selection', () => { await page.keyboard.type( ']2' ); await page.keyboard.press( 'ArrowLeft' ); // Select everything between []. - await pageUtils.pressKeyTimes( 'Shift+ArrowLeft', 3 ); + await pageUtils.pressKeys( 'Shift+ArrowLeft', { times: 3 } ); // Ensure selection is in the correct place. await page.keyboard.type( '|' ); @@ -1084,7 +1084,7 @@ test.describe( 'Multi-block selection', () => { .click(); await page.keyboard.press( 'ArrowLeft' ); // Select everything between []. - await pageUtils.pressKeyTimes( 'Shift+ArrowLeft', 5 ); + await pageUtils.pressKeys( 'Shift+ArrowLeft', { times: 5 } ); await page.keyboard.press( 'Enter' ); @@ -1127,7 +1127,7 @@ test.describe( 'Multi-block selection', () => { .filter( { hasText: 'a' } ) .click(); - await pageUtils.pressKeyTimes( 'Shift+ArrowDown', 2 ); + await pageUtils.pressKeys( 'Shift+ArrowDown', { times: 2 } ); await page.keyboard.press( 'Backspace' ); // Ensure selection is in the correct place. diff --git a/test/e2e/specs/editor/various/rtl.spec.js b/test/e2e/specs/editor/various/rtl.spec.js index d06c0ec8a43894..899dfd3c87ddec 100644 --- a/test/e2e/specs/editor/various/rtl.spec.js +++ b/test/e2e/specs/editor/various/rtl.spec.js @@ -151,9 +151,9 @@ test.describe( 'RTL', () => { pageUtils, } ) => { await page.click( 'role=button[name="Add default block"i]' ); - await pageUtils.pressKeyWithModifier( 'primary', 'b' ); + await pageUtils.pressKeys( 'primary+b' ); await page.keyboard.type( ARABIC_ONE ); - await pageUtils.pressKeyWithModifier( 'primary', 'b' ); + await pageUtils.pressKeys( 'primary+b' ); await page.keyboard.type( ARABIC_TWO ); // Insert a character at each boundary position. diff --git a/test/e2e/specs/editor/various/splitting-merging.spec.js b/test/e2e/specs/editor/various/splitting-merging.spec.js index 510c1cb46cf3b3..fc6d09fcc3240c 100644 --- a/test/e2e/specs/editor/various/splitting-merging.spec.js +++ b/test/e2e/specs/editor/various/splitting-merging.spec.js @@ -23,7 +23,7 @@ test.describe( 'splitting and merging blocks', () => { // Move caret between 'First' and 'Second' and press Enter to split // paragraph blocks. - await pageUtils.pressKeyTimes( 'ArrowLeft', 6 ); + await pageUtils.pressKeys( 'ArrowLeft', { times: 6 } ); await page.keyboard.press( 'Enter' ); // Assert that there are now two paragraph blocks with correct content. @@ -55,16 +55,16 @@ test.describe( 'splitting and merging blocks', () => { ` ); - await pageUtils.pressKeyTimes( 'Backspace', 7 ); // Delete "Between" + await pageUtils.pressKeys( 'Backspace', { times: 7 } ); // Delete "Between" // Edge case: Without ensuring that the editor still has focus when // restoring a bookmark, the caret may be inadvertently moved back to // an inline boundary after a split occurs. await page.keyboard.press( 'Home' ); await page.keyboard.down( 'Shift' ); - await pageUtils.pressKeyTimes( 'ArrowRight', 5 ); + await pageUtils.pressKeys( 'ArrowRight', { times: 5 } ); await page.keyboard.up( 'Shift' ); - await pageUtils.pressKeyWithModifier( 'primary', 'b' ); + await pageUtils.pressKeys( 'primary+b' ); // Collapse selection, still within inline boundary. await page.keyboard.press( 'ArrowRight' ); await page.keyboard.press( 'Enter' ); @@ -90,13 +90,13 @@ test.describe( 'splitting and merging blocks', () => { // Regression Test: Caret should reset to end of inline boundary when // backspacing to delete second paragraph. await editor.insertBlock( { name: 'core/paragraph' } ); - await pageUtils.pressKeyWithModifier( 'primary', 'b' ); + await pageUtils.pressKeys( 'primary+b' ); await page.keyboard.type( 'Foo' ); await page.keyboard.press( 'Enter' ); await page.keyboard.press( 'Backspace' ); // Replace contents of first paragraph with "Bar". - await pageUtils.pressKeyTimes( 'Backspace', 4 ); + await pageUtils.pressKeys( 'Backspace', { times: 4 } ); await page.keyboard.type( 'Bar' ); const content = await editor.getEditedPostContent(); @@ -161,7 +161,7 @@ test.describe( 'splitting and merging blocks', () => { // Select text. await page.keyboard.down( 'Shift' ); - await pageUtils.pressKeyTimes( 'ArrowLeft', 3 ); + await pageUtils.pressKeys( 'ArrowLeft', { times: 3 } ); await page.keyboard.up( 'Shift' ); // Delete selection. @@ -194,9 +194,9 @@ test.describe( 'splitting and merging blocks', () => { // The regression appeared to only affect paragraphs created while // within an inline boundary. await page.keyboard.down( 'Shift' ); - await pageUtils.pressKeyTimes( 'ArrowLeft', 3 ); + await pageUtils.pressKeys( 'ArrowLeft', { times: 3 } ); await page.keyboard.up( 'Shift' ); - await pageUtils.pressKeyWithModifier( 'primary', 'b' ); + await pageUtils.pressKeys( 'primary+b' ); await page.keyboard.press( 'ArrowRight' ); await page.keyboard.press( 'Enter' ); await page.keyboard.press( 'Enter' ); @@ -331,7 +331,7 @@ test.describe( 'splitting and merging blocks', () => { await page.keyboard.type( '12' ); await page.keyboard.press( 'ArrowLeft' ); await page.keyboard.press( 'Enter' ); - await pageUtils.pressKeyWithModifier( 'primary', 'z' ); + await pageUtils.pressKeys( 'primary+z' ); // Check the content. expect( await editor.getEditedPostContent() ).toMatchSnapshot(); @@ -345,7 +345,7 @@ test.describe( 'splitting and merging blocks', () => { await page.keyboard.press( 'Enter' ); await page.keyboard.type( '1' ); await page.keyboard.press( 'Enter' ); - await pageUtils.pressKeyWithModifier( 'shift', 'Enter' ); + await pageUtils.pressKeys( 'shift+Enter' ); await page.keyboard.type( '2' ); await page.keyboard.press( 'ArrowLeft' ); await page.keyboard.press( 'Backspace' ); @@ -377,7 +377,7 @@ test.describe( 'splitting and merging blocks', () => { await page.keyboard.type( 'item 1' ); await page.keyboard.press( 'Enter' ); await page.keyboard.type( 'item 2' ); - await pageUtils.pressKeyTimes( 'ArrowUp', 3 ); + await pageUtils.pressKeys( 'ArrowUp', { times: 3 } ); await page.keyboard.press( 'Delete' ); expect( await editor.getEditedPostContent() ).toMatchSnapshot(); @@ -398,7 +398,7 @@ test.describe( 'splitting and merging blocks', () => { await page.keyboard.press( 'Enter' ); await page.keyboard.type( 'item 2' ); await page.keyboard.press( 'ArrowUp' ); - await pageUtils.pressKeyTimes( 'ArrowLeft', 6 ); + await pageUtils.pressKeys( 'ArrowLeft', { times: 6 } ); await page.keyboard.press( 'Backspace' ); expect( await editor.getEditedPostContent() ).toMatchSnapshot(); diff --git a/test/e2e/specs/editor/various/toolbar-roving-tabindex.spec.js b/test/e2e/specs/editor/various/toolbar-roving-tabindex.spec.js index 0feb50f1b92330..7026be84c72a3f 100644 --- a/test/e2e/specs/editor/various/toolbar-roving-tabindex.spec.js +++ b/test/e2e/specs/editor/various/toolbar-roving-tabindex.spec.js @@ -93,6 +93,7 @@ test.describe( 'Toolbar roving tabindex', () => { editor, page, ToolbarRovingTabindexUtils, + pageUtils, } ) => { await editor.insertBlock( { name: 'core/table' } ); await ToolbarRovingTabindexUtils.testBlockToolbarKeyboardNavigation( @@ -103,7 +104,7 @@ test.describe( 'Toolbar roving tabindex', () => { await page.keyboard.press( 'Home' ); await ToolbarRovingTabindexUtils.expectLabelToHaveFocus( 'Table' ); await page.click( `role=button[name="Create Table"i]` ); - await page.keyboard.press( 'Tab' ); + await pageUtils.pressKeys( 'Tab' ); await ToolbarRovingTabindexUtils.testBlockToolbarKeyboardNavigation( 'Body cell text', 'Table' @@ -144,8 +145,8 @@ test.describe( 'Toolbar roving tabindex', () => { await ToolbarRovingTabindexUtils.focusBlockToolbar(); await page.keyboard.press( 'ArrowRight' ); await ToolbarRovingTabindexUtils.expectLabelToHaveFocus( 'Move up' ); - await page.keyboard.press( 'Tab' ); - await pageUtils.pressKeyWithModifier( 'shift', 'Tab' ); + await pageUtils.pressKeys( 'Tab' ); + await pageUtils.pressKeys( 'shift+Tab' ); await ToolbarRovingTabindexUtils.expectLabelToHaveFocus( 'Move up' ); } ); @@ -154,7 +155,7 @@ test.describe( 'Toolbar roving tabindex', () => { pageUtils, ToolbarRovingTabindexUtils, } ) => { - await pageUtils.pressKeyWithModifier( 'alt', 'F10' ); + await pageUtils.pressKeys( 'alt+F10' ); await page.keyboard.press( 'ArrowRight' ); await page.keyboard.press( 'ArrowRight' ); await ToolbarRovingTabindexUtils.expectLabelToHaveFocus( 'Bold' ); @@ -168,7 +169,7 @@ class ToolbarRovingTabindexUtils { } async focusBlockToolbar() { - await this.pageUtils.pressKeyWithModifier( 'alt', 'F10' ); + await this.pageUtils.pressKeys( 'alt+F10' ); } async testBlockToolbarKeyboardNavigation( @@ -179,9 +180,9 @@ class ToolbarRovingTabindexUtils { await this.expectLabelToHaveFocus( currentBlockTitle ); await this.page.keyboard.press( 'ArrowRight' ); await this.expectLabelToHaveFocus( 'Move up' ); - await this.page.keyboard.press( 'Tab' ); + await this.pageUtils.pressKeys( 'Tab' ); await this.expectLabelToHaveFocus( currentBlockLabel ); - await this.pageUtils.pressKeyWithModifier( 'shift', 'Tab' ); + await this.pageUtils.pressKeys( 'shift+Tab' ); await this.expectLabelToHaveFocus( 'Move up' ); } @@ -208,7 +209,7 @@ class ToolbarRovingTabindexUtils { await this.expectLabelToHaveFocus( 'Block: Group' ); await this.page.keyboard.press( 'ArrowRight' ); await this.expectLabelToHaveFocus( currentBlockLabel ); - await this.pageUtils.pressKeyWithModifier( 'shift', 'Tab' ); + await this.pageUtils.pressKeys( 'shift+Tab' ); await this.expectLabelToHaveFocus( 'Select Group' ); await this.page.keyboard.press( 'ArrowRight' ); await this.expectLabelToHaveFocus( currentBlockTitle ); diff --git a/test/e2e/specs/editor/various/writing-flow.spec.js b/test/e2e/specs/editor/various/writing-flow.spec.js index 64f28c4f5a0931..80e3fb3b126827 100644 --- a/test/e2e/specs/editor/various/writing-flow.spec.js +++ b/test/e2e/specs/editor/various/writing-flow.spec.js @@ -122,13 +122,13 @@ test.describe( 'Writing Flow', () => { await page.keyboard.type( 'Third' ); // Navigate to second paragraph. - await pageUtils.pressKeyTimes( 'ArrowLeft', 6 ); + await pageUtils.pressKeys( 'ArrowLeft', { times: 6 } ); // Bold second paragraph text. await page.keyboard.down( 'Shift' ); - await pageUtils.pressKeyTimes( 'ArrowLeft', 6 ); + await pageUtils.pressKeys( 'ArrowLeft', { times: 6 } ); await page.keyboard.up( 'Shift' ); - await pageUtils.pressKeyWithModifier( 'primary', 'b' ); + await pageUtils.pressKeys( 'primary+b' ); // Arrow left from selected bold should collapse to before the inline // boundary. Arrow once more to traverse into first paragraph. @@ -138,18 +138,18 @@ test.describe( 'Writing Flow', () => { // Arrow right from end of first should traverse to second, *BEFORE* // the bolded text. Another press should move within inline boundary. - await pageUtils.pressKeyTimes( 'ArrowRight', 2 ); + await pageUtils.pressKeys( 'ArrowRight', { times: 2 } ); await page.keyboard.type( 'Inside' ); // Arrow left from end of beginning of inline boundary should move to // the outside of the inline boundary. - await pageUtils.pressKeyTimes( 'ArrowLeft', 6 ); + await pageUtils.pressKeys( 'ArrowLeft', { times: 6 } ); await page.keyboard.press( 'ArrowLeft' ); // Separate for emphasis. await page.keyboard.type( 'Before' ); // Likewise, test at the end of the inline boundary for same effect. await page.keyboard.press( 'ArrowRight' ); // Move inside - await pageUtils.pressKeyTimes( 'ArrowRight', 12 ); + await pageUtils.pressKeys( 'ArrowRight', { times: 12 } ); await page.keyboard.type( 'Inside' ); await page.keyboard.press( 'ArrowRight' ); @@ -176,18 +176,18 @@ test.describe( 'Writing Flow', () => { pageUtils, } ) => { await page.keyboard.press( 'Enter' ); - await pageUtils.pressKeyWithModifier( 'primary', 'b' ); + await pageUtils.pressKeys( 'primary+b' ); await page.keyboard.type( '1 2' ); await page.keyboard.down( 'Shift' ); await page.keyboard.press( 'ArrowLeft' ); await page.keyboard.up( 'Shift' ); - await pageUtils.pressKeyWithModifier( 'primary', 'i' ); + await pageUtils.pressKeys( 'primary+i' ); await page.keyboard.press( 'ArrowLeft' ); await page.keyboard.press( 'ArrowLeft' ); await page.keyboard.down( 'Shift' ); await page.keyboard.press( 'ArrowLeft' ); await page.keyboard.up( 'Shift' ); - await pageUtils.pressKeyWithModifier( 'primary', 'i' ); + await pageUtils.pressKeys( 'primary+i' ); await page.keyboard.press( 'ArrowLeft' ); await expect.poll( editor.getEditedPostContent ) @@ -228,7 +228,7 @@ test.describe( 'Writing Flow', () => { } ) => { await page.keyboard.press( 'Enter' ); await page.keyboard.type( 'a' ); - await pageUtils.pressKeyWithModifier( 'shift', 'Enter' ); + await pageUtils.pressKeys( 'shift+Enter' ); await expect.poll( editor.getEditedPostContent ) .toBe( `

a

@@ -242,7 +242,7 @@ test.describe( 'Writing Flow', () => { } ) => { await page.keyboard.press( 'Enter' ); await page.keyboard.type( 'a' ); - await pageUtils.pressKeyWithModifier( 'shift', 'Enter' ); + await pageUtils.pressKeys( 'shift+Enter' ); await page.keyboard.type( 'b' ); await expect.poll( editor.getEditedPostContent ) .toBe( ` @@ -258,7 +258,7 @@ test.describe( 'Writing Flow', () => { await page.keyboard.press( 'Enter' ); await page.keyboard.type( 'ab' ); await page.keyboard.press( 'ArrowLeft' ); - await pageUtils.pressKeyWithModifier( 'shift', 'Enter' ); + await pageUtils.pressKeys( 'shift+Enter' ); await expect.poll( editor.getEditedPostContent ) .toBe( `

a
b

@@ -273,7 +273,7 @@ test.describe( 'Writing Flow', () => { await page.keyboard.press( 'Enter' ); await page.keyboard.type( 'a' ); await page.keyboard.press( 'ArrowLeft' ); - await pageUtils.pressKeyWithModifier( 'shift', 'Enter' ); + await pageUtils.pressKeys( 'shift+Enter' ); await expect.poll( editor.getEditedPostContent ) .toBe( `


a

@@ -286,7 +286,7 @@ test.describe( 'Writing Flow', () => { pageUtils, } ) => { await page.keyboard.press( 'Enter' ); - await pageUtils.pressKeyWithModifier( 'shift', 'Enter' ); + await pageUtils.pressKeys( 'shift+Enter' ); await expect.poll( editor.getEditedPostContent ) .toBe( `


@@ -327,7 +327,7 @@ test.describe( 'Writing Flow', () => { ).toHaveClass( /is-selected/ ); // Should remain in title upon modifier + ArrowDown: - await pageUtils.pressKeyWithModifier( 'primary', 'ArrowDown' ); + await pageUtils.pressKeys( 'primary+ArrowDown' ); await expect( page.locator( 'role=document[name="Block: Shortcode"i]' ) ).toHaveClass( /is-selected/ ); @@ -346,7 +346,7 @@ test.describe( 'Writing Flow', () => { } ) => { await page.keyboard.press( 'Enter' ); await page.keyboard.type( '1 2 3' ); - await pageUtils.pressKeyTimes( 'ArrowLeft', ' 3'.length ); + await pageUtils.pressKeys( 'ArrowLeft', { times: ' 3'.length } ); await page.keyboard.press( 'Backspace' ); await expect.poll( editor.getEditedPostContent ) @@ -369,11 +369,10 @@ test.describe( 'Writing Flow', () => { } ) => { await page.keyboard.press( 'Enter' ); await page.keyboard.type( 'alpha beta gamma' ); - await pageUtils.pressKeyTimes( 'ArrowLeft', ' gamma'.length ); + await pageUtils.pressKeys( 'ArrowLeft', { times: ' gamma'.length } ); - await pageUtils.pressKeyWithModifier( - process.platform === 'darwin' ? 'alt' : 'primary', - 'Backspace' + await pageUtils.pressKeys( + `${ process.platform === 'darwin' ? 'Alt' : 'primary' }+Backspace` ); await expect.poll( editor.getEditedPostContent ) @@ -396,9 +395,9 @@ test.describe( 'Writing Flow', () => { } ) => { await page.keyboard.press( 'Enter' ); await page.keyboard.type( 'alpha beta gamma' ); - await pageUtils.pressKeyTimes( 'ArrowLeft', ' gamma'.length ); + await pageUtils.pressKeys( 'ArrowLeft', { times: ' gamma'.length } ); await page.keyboard.down( 'Shift' ); - await pageUtils.pressKeyTimes( 'ArrowLeft', 'beta'.length ); + await pageUtils.pressKeys( 'ArrowLeft', { times: 'beta'.length } ); await page.keyboard.up( 'Shift' ); await page.keyboard.press( 'Backspace' ); @@ -422,7 +421,7 @@ test.describe( 'Writing Flow', () => { pageUtils, } ) => { await page.keyboard.press( 'Enter' ); - await pageUtils.pressKeyTimes( 'Enter', 10 ); + await pageUtils.pressKeys( 'Enter', { times: 10 } ); // Check that none of the paragraph blocks have
in them. expect( await editor.getEditedPostContent() ).toMatchSnapshot(); @@ -486,10 +485,10 @@ test.describe( 'Writing Flow', () => { await page.keyboard.type( '1' ); await page.keyboard.press( 'Enter' ); await page.keyboard.type( '><<' ); - await pageUtils.pressKeyWithModifier( 'shift', 'Enter' ); + await pageUtils.pressKeys( 'shift+Enter' ); await page.keyboard.type( '<<<' ); await page.keyboard.down( 'Shift' ); - await pageUtils.pressKeyTimes( 'ArrowLeft', '<<\n<<<'.length ); + await pageUtils.pressKeys( 'ArrowLeft', { times: '<<\n<<<'.length } ); await page.keyboard.up( 'Shift' ); await page.keyboard.press( 'Backspace' ); @@ -578,8 +577,8 @@ test.describe( 'Writing Flow', () => { await page.keyboard.type( '2' ); await page.keyboard.press( 'Enter' ); await page.keyboard.type( '3' ); - await pageUtils.pressKeyTimes( 'ArrowUp', 2 ); - await pageUtils.pressKeyTimes( 'Delete', 2 ); + await pageUtils.pressKeys( 'ArrowUp', { times: 2 } ); + await pageUtils.pressKeys( 'Delete', { times: 2 } ); await expect.poll( editor.getEditedPostContent ) .toBe( `

1

@@ -629,7 +628,7 @@ test.describe( 'Writing Flow', () => { await page.keyboard.press( 'Enter' ); await page.keyboard.type( '1' ); await page.keyboard.press( 'Enter' ); - await pageUtils.pressKeyWithModifier( 'shift', 'Enter' ); + await pageUtils.pressKeys( 'shift+Enter' ); await page.keyboard.type( '2' ); await page.keyboard.press( 'ArrowUp' ); await page.keyboard.press( 'ArrowUp' ); @@ -675,10 +674,10 @@ test.describe( 'Writing Flow', () => { await page.keyboard.type( 'cd' ); // Selects part of the first list item, although invisible. - await pageUtils.pressKeyWithModifier( 'shift', 'ArrowUp' ); + await pageUtils.pressKeys( 'shift+ArrowUp' ); await page.evaluate( () => new Promise( window.requestIdleCallback ) ); // Extends selection into the first paragraph - await pageUtils.pressKeyWithModifier( 'shift', 'ArrowUp' ); + await pageUtils.pressKeys( 'shift+ArrowUp' ); await page.evaluate( () => new Promise( window.requestIdleCallback ) ); // Mixed selection, so all content will be removed. @@ -731,6 +730,7 @@ test.describe( 'Writing Flow', () => { test( 'should not have a dead zone above an aligned block', async ( { editor, page, + pageUtils, } ) => { await page.keyboard.press( 'Enter' ); await page.keyboard.type( '1' ); @@ -745,7 +745,7 @@ test.describe( 'Writing Flow', () => { await wideButton.click(); // Focus the block content - await page.keyboard.press( 'Tab' ); + await pageUtils.pressKeys( 'Tab' ); // Select the previous block. await page.keyboard.press( 'ArrowUp' ); @@ -791,13 +791,14 @@ test.describe( 'Writing Flow', () => { test( 'should only consider the content as one tab stop', async ( { editor, page, + pageUtils, } ) => { await page.keyboard.press( 'Enter' ); await page.keyboard.type( '/table' ); await page.keyboard.press( 'Enter' ); // Tab to the "Create table" button. - await page.keyboard.press( 'Tab' ); - await page.keyboard.press( 'Tab' ); + await pageUtils.pressKeys( 'Tab' ); + await pageUtils.pressKeys( 'Tab' ); // Create the table. await page.keyboard.press( 'Space' ); await expect( @@ -881,12 +882,12 @@ test.describe( 'Writing Flow', () => { await page.keyboard.type( 'second' ); // Multi select both paragraphs. - await pageUtils.pressKeyTimes( 'ArrowLeft', 2 ); + await pageUtils.pressKeys( 'ArrowLeft', { times: 2 } ); await page.keyboard.down( 'Shift' ); - await pageUtils.pressKeyTimes( 'ArrowLeft', 2 ); + await pageUtils.pressKeys( 'ArrowLeft', { times: 2 } ); await page.keyboard.press( 'ArrowUp' ); await page.keyboard.up( 'Shift' ); - await pageUtils.pressKeyWithModifier( 'primary', 'b' ); + await pageUtils.pressKeys( 'primary+b' ); await expect.poll( editor.getEditedPostContent ) .toBe( ` @@ -989,7 +990,7 @@ test.describe( 'Writing Flow', () => { await page.keyboard.press( 'Enter' ); await page.keyboard.type( 'b' ); await page.keyboard.press( 'ArrowLeft' ); - await pageUtils.pressKeyWithModifier( 'alt', 'ArrowUp' ); + await pageUtils.pressKeys( 'alt+ArrowUp' ); await page.keyboard.type( '.' ); // Expect the "." to be added at the start of the paragraph diff --git a/test/e2e/specs/site-editor/writing-flow.spec.js b/test/e2e/specs/site-editor/writing-flow.spec.js index 2f9a83b4874e5e..d9c63a1da1ddc5 100644 --- a/test/e2e/specs/site-editor/writing-flow.spec.js +++ b/test/e2e/specs/site-editor/writing-flow.spec.js @@ -33,7 +33,7 @@ test.describe( 'Site editor writing flow', () => { await editor.selectBlocks( siteTitleBlock ); // Shift tab to the toolbar. - await pageUtils.pressKeyWithModifier( 'shift', 'Tab' ); + await pageUtils.pressKeys( 'shift+Tab' ); const blockToolbarButton = page.locator( 'role=toolbar[name="Block tools"i] >> role=button[name="Site Title"i]' ); @@ -64,7 +64,7 @@ test.describe( 'Site editor writing flow', () => { await editor.selectBlocks( siteTaglineBlock ); // Tab to the inspector, tabbing three times to go past the two resize handles. - await pageUtils.pressKeyTimes( 'Tab', 3 ); + await pageUtils.pressKeys( 'Tab', { times: 3 } ); const inspectorTemplateTab = page.locator( 'role=region[name="Editor settings"i] >> role=button[name="Template"i]' ); diff --git a/test/e2e/specs/widgets/customizing-widgets.spec.js b/test/e2e/specs/widgets/customizing-widgets.spec.js index 01b6cf7e13ea70..9bf3dcb32b371b 100644 --- a/test/e2e/specs/widgets/customizing-widgets.spec.js +++ b/test/e2e/specs/widgets/customizing-widgets.spec.js @@ -342,6 +342,7 @@ test.describe( 'Widgets Customizer', () => { editor, page, widgetsCustomizerPage, + pageUtils, } ) => { await widgetsCustomizerPage.visitCustomizerPage(); await widgetsCustomizerPage.expandWidgetArea( 'Footer #1' ); @@ -368,7 +369,7 @@ test.describe( 'Widgets Customizer', () => { await titleInput.type( 'Hello Title' ); // Unfocus the current legacy widget. - await page.keyboard.press( 'Tab' ); + await pageUtils.pressKeys( 'Tab' ); const previewFrame = widgetsCustomizerPage.previewFrame; const legacyWidgetPreviewFrame = page.frameLocator( @@ -403,7 +404,7 @@ test.describe( 'Widgets Customizer', () => { await titleInput.type( 'Hello again!' ); // Unfocus the current legacy widget. - await page.keyboard.press( 'Tab' ); + await pageUtils.pressKeys( 'Tab' ); // Expect the preview in block to show when unfocusing the legacy widget block. await expect( From 93a679182efd6244b3656e5a854582333151d358 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Tue, 14 Mar 2023 12:06:49 +0200 Subject: [PATCH 139/910] sprintf requires more than 1 params (#49054) --- packages/block-library/src/comment-template/index.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/block-library/src/comment-template/index.php b/packages/block-library/src/comment-template/index.php index 08620a3ea695fd..45fd993adbdeba 100644 --- a/packages/block-library/src/comment-template/index.php +++ b/packages/block-library/src/comment-template/index.php @@ -58,11 +58,10 @@ function block_core_comment_template_render_comments( $comments, $block ) { $block_content .= sprintf( '
    %1$s
', $inner_content ); --$comment_depth; } else { - $inner_content = block_core_comment_template_render_comments( + $block_content .= block_core_comment_template_render_comments( $children, $block ); - $block_content .= sprintf( $inner_content ); } } From 1308111513f738c62558c8411b5eb7c5da62ba88 Mon Sep 17 00:00:00 2001 From: Michael Day <108079556+mike-day@users.noreply.github.com> Date: Tue, 14 Mar 2023 05:47:21 -0500 Subject: [PATCH 140/910] Refactor/toolbar button component to typescript (#47750) * Add Toolbar types * Initial refactor Toolbar to TS * Initial refactor ToolbarContainer to TS * Update component README * Add @ts-nocheck for untargeted dependencies * Remove src/toolbar from tsconfig exclude * Convert Toolbar test to TS * Convert Toolbar stories to TS * ToolbarGroup test to TS * Update jsdoc, cleanup types * Add changelog entry * Empty commit * Apply suggestions from code review Co-authored-by: Lena Morita * Use @ts-expect-error in stories, remove unknown typing for toggleProps * Remove unneeded explicit typing for WithoutGroup * Simplify ToolbarProps, remove reakit type * Add toolbar button types * Refactor toolbar-button to TS * Refactor toolbar-button-container to TS * Update readme * Add changelog entry * Move changelog entry to Unreleased section * Update packages/components/src/toolbar/toolbar-button/toolbar-button-container.tsx Co-authored-by: Lena Morita * Explictly type className; alphabetize ToolbarButtonContainer params * Remove ts-nocheck from toolbar-button * Add TODO for possible onClick bug * Fixup changelog --------- Co-authored-by: Lena Morita --- packages/components/CHANGELOG.md | 1 + .../src/toolbar/toolbar-button/README.md | 14 +++- .../toolbar-button/{index.js => index.tsx} | 75 ++++++++++++++----- .../toolbar-button-container.js | 6 -- .../toolbar-button-container.tsx | 13 ++++ .../src/toolbar/toolbar-button/types.ts | 46 ++++++++++++ 6 files changed, 128 insertions(+), 27 deletions(-) rename packages/components/src/toolbar/toolbar-button/{index.js => index.tsx} (53%) delete mode 100644 packages/components/src/toolbar/toolbar-button/toolbar-button-container.js create mode 100644 packages/components/src/toolbar/toolbar-button/toolbar-button-container.tsx create mode 100644 packages/components/src/toolbar/toolbar-button/types.ts diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 4031a4712e3e0d..02b83fadfcfc20 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -22,6 +22,7 @@ - `withFocusReturn` HOC: Convert to TypeScript ([#48748](https://github.com/WordPress/gutenberg/pull/48748)). - `navigateRegions` HOC: Convert to TypeScript ([#48632](https://github.com/WordPress/gutenberg/pull/48632)). - `withSpokenMessages`: HOC: Convert to TypeScript ([#48163](https://github.com/WordPress/gutenberg/pull/48163)). +- `ToolbarButton`: Convert to TypeScript ([#47750](https://github.com/WordPress/gutenberg/pull/47750)). - `DimensionControl(Experimental)`: Convert to TypeScript ([#47351](https://github.com/WordPress/gutenberg/pull/47351)). - `PaletteEdit`: Convert to TypeScript ([#47764](https://github.com/WordPress/gutenberg/pull/47764)). diff --git a/packages/components/src/toolbar/toolbar-button/README.md b/packages/components/src/toolbar/toolbar-button/README.md index 92e9e61777aff5..6bde73117b02c5 100644 --- a/packages/components/src/toolbar/toolbar-button/README.md +++ b/packages/components/src/toolbar/toolbar-button/README.md @@ -51,7 +51,19 @@ function Edit() { ## Props -This component accepts [the same API of the Button](/packages/components/src/button/README.md#props) component. +This component accepts [the same API of the Button](/packages/components/src/button/README.md#props) component in addition to: + +#### `containerClassName`: `string` + +An optional additional class name to apply to the button container. + +- Required: No + +#### `subscript`: `string` + +An optional subscript for the button. + +- Required: No ## Related components diff --git a/packages/components/src/toolbar/toolbar-button/index.js b/packages/components/src/toolbar/toolbar-button/index.tsx similarity index 53% rename from packages/components/src/toolbar/toolbar-button/index.js rename to packages/components/src/toolbar/toolbar-button/index.tsx index bdfc44e0d64fbf..00c5c364e94ce9 100644 --- a/packages/components/src/toolbar/toolbar-button/index.js +++ b/packages/components/src/toolbar/toolbar-button/index.tsx @@ -1,13 +1,14 @@ -// @ts-nocheck - /** * External dependencies */ import classnames from 'classnames'; +import type { ForwardedRef } from 'react'; + /** * WordPress dependencies */ import { useContext, forwardRef } from '@wordpress/element'; + /** * Internal dependencies */ @@ -15,19 +16,22 @@ import Button from '../../button'; import ToolbarItem from '../toolbar-item'; import ToolbarContext from '../toolbar-context'; import ToolbarButtonContainer from './toolbar-button-container'; +import type { ToolbarButtonProps } from './types'; +import type { WordPressComponentProps } from '../../ui/context'; +import type React from 'react'; -function ToolbarButton( +function UnforwardedToolbarButton( { - containerClassName, + children, className, + containerClassName, extraProps, - children, - title, isActive, isDisabled, + title, ...props - }, - ref + }: WordPressComponentProps< ToolbarButtonProps, typeof Button, false >, + ref: ForwardedRef< any > ) { const accessibleToolbarState = useContext( ToolbarContext ); @@ -40,8 +44,14 @@ function ToolbarButton( label={ title } shortcut={ props.shortcut } data-subscript={ props.subscript } - onClick={ ( event ) => { + onClick={ ( + event: React.MouseEvent< + HTMLButtonElement & HTMLAnchorElement, + MouseEvent + > + ) => { event.stopPropagation(); + // TODO: Possible bug; maybe use onClick instead of props.onClick. if ( props.onClick ) { props.onClick( event ); } @@ -72,18 +82,43 @@ function ToolbarButton( { ...props } ref={ ref } > - { ( toolbarItemProps ) => ( - - ) } + { + // @ts-expect-error + ( toolbarItemProps ) => ( + + ) + } ); } -export default forwardRef( ToolbarButton ); +/** + * ToolbarButton can be used to add actions to a toolbar, usually inside a Toolbar + * or ToolbarGroup when used to create general interfaces. + * + * ```jsx + * import { Toolbar, ToolbarButton } from '@wordpress/components'; + * import { edit } from '@wordpress/icons'; + * + * function MyToolbar() { + * return ( + * + * alert( 'Editing' ) } + * /> + * + * ); + * } + * ``` + */ +export const ToolbarButton = forwardRef( UnforwardedToolbarButton ); +export default ToolbarButton; diff --git a/packages/components/src/toolbar/toolbar-button/toolbar-button-container.js b/packages/components/src/toolbar/toolbar-button/toolbar-button-container.js deleted file mode 100644 index 5417aeb3e7ba7b..00000000000000 --- a/packages/components/src/toolbar/toolbar-button/toolbar-button-container.js +++ /dev/null @@ -1,6 +0,0 @@ -// @ts-nocheck - -const ToolbarButtonContainer = ( props ) => ( -
{ props.children }
-); -export default ToolbarButtonContainer; diff --git a/packages/components/src/toolbar/toolbar-button/toolbar-button-container.tsx b/packages/components/src/toolbar/toolbar-button/toolbar-button-container.tsx new file mode 100644 index 00000000000000..47594c1e99b58b --- /dev/null +++ b/packages/components/src/toolbar/toolbar-button/toolbar-button-container.tsx @@ -0,0 +1,13 @@ +/** + * Internal dependencies + */ +import type { ToolbarButtonContainerProps } from './types'; + +const ToolbarButtonContainer = ( { + children, + className, +}: ToolbarButtonContainerProps ) => ( +
{ children }
+); + +export default ToolbarButtonContainer; diff --git a/packages/components/src/toolbar/toolbar-button/types.ts b/packages/components/src/toolbar/toolbar-button/types.ts new file mode 100644 index 00000000000000..da7038c213324a --- /dev/null +++ b/packages/components/src/toolbar/toolbar-button/types.ts @@ -0,0 +1,46 @@ +/** + * External dependencies + */ +import type { ReactNode } from 'react'; + +export type ToolbarButtonProps = { + /** + * Children to be rendered inside the button. + */ + children?: ReactNode; + /** + * An optional class name for the button container. + */ + containerClassName?: string; + /** + * Additional props to be passed alongside props. + */ + extraProps?: any; + /** + * Indicates if the button is active. + */ + isActive?: boolean; + /** + * Indicates if the button is disabled. + */ + isDisabled?: boolean; + /** + * An optional subscript for the button. + */ + subscript?: string; + /** + * An optional title/label for the button. + */ + title?: string; +}; + +export type ToolbarButtonContainerProps = { + /** + * Children to be rendered inside the button container. + */ + children?: ReactNode; + /** + * An optional class name for the button container. + */ + className?: string; +}; From cc47da6b559a810c2047e8eb8e2fadd62b2ae191 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Mar 2023 09:52:01 -0400 Subject: [PATCH 141/910] Bump actions/cache from 3.2.5 to 3.3.1 (#49034) * Bump actions/cache from 3.2.5 to 3.3.1 Bumps [actions/cache](https://github.com/actions/cache) from 3.2.5 to 3.3.1. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/6998d139ddd3e68c71e9e398d8e40b71a2f39812...88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Update inline comment that dependabot missed. --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Jonathan Desrosiers --- .github/workflows/pull-request-automation.yml | 2 +- .github/workflows/rnmobile-android-runner.yml | 2 +- .github/workflows/rnmobile-ios-runner.yml | 4 ++-- .github/workflows/unit-test.yml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pull-request-automation.yml b/.github/workflows/pull-request-automation.yml index 5163ae3f0ea0cb..5d38f7965f7b10 100644 --- a/.github/workflows/pull-request-automation.yml +++ b/.github/workflows/pull-request-automation.yml @@ -25,7 +25,7 @@ jobs: node-version: ${{ matrix.node }} - name: Cache NPM packages - uses: actions/cache@6998d139ddd3e68c71e9e398d8e40b71a2f39812 # v3.2.5 + uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 with: # npm cache files are stored in `~/.npm` on Linux/macOS path: ~/.npm diff --git a/.github/workflows/rnmobile-android-runner.yml b/.github/workflows/rnmobile-android-runner.yml index a0e15c960670cc..0f1474c824a208 100644 --- a/.github/workflows/rnmobile-android-runner.yml +++ b/.github/workflows/rnmobile-android-runner.yml @@ -43,7 +43,7 @@ jobs: uses: gradle/gradle-build-action@6095a76664413da4c8c134ee32e8a8ae900f0f1f # v2.4.0 - name: AVD cache - uses: actions/cache@6998d139ddd3e68c71e9e398d8e40b71a2f39812 # v3.2.5 + uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 id: avd-cache with: path: | diff --git a/.github/workflows/rnmobile-ios-runner.yml b/.github/workflows/rnmobile-ios-runner.yml index 52fab007b19173..933b4184957d11 100644 --- a/.github/workflows/rnmobile-ios-runner.yml +++ b/.github/workflows/rnmobile-ios-runner.yml @@ -37,7 +37,7 @@ jobs: run: find package-lock.json packages/react-native-editor/ios packages/react-native-aztec/ios packages/react-native-bridge/ios -type f -print0 | sort -z | xargs -0 shasum | tee ios-checksums.txt - name: Restore build cache - uses: actions/cache@6998d139ddd3e68c71e9e398d8e40b71a2f39812 # v3.2.5 + uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 with: path: | packages/react-native-editor/ios/build/GutenbergDemo/Build/Products/Release-iphonesimulator/GutenbergDemo.app @@ -45,7 +45,7 @@ jobs: key: ${{ runner.os }}-ios-build-${{ matrix.xcode }}-${{ matrix.device }}-${{ hashFiles('ios-checksums.txt') }} - name: Restore pods cache - uses: actions/cache@6998d139ddd3e68c71e9e398d8e40b71a2f39812 # v3.2.5 + uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 with: path: | packages/react-native-editor/ios/Pods diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 6552f2ecf6a2b0..ca9adb75ebca83 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -182,7 +182,7 @@ jobs: run: echo "date=$(/bin/date -u --date='last Mon' "+%F")" >> $GITHUB_OUTPUT - name: Cache PHPCS scan cache - uses: actions/cache@6998d139ddd3e68c71e9e398d8e40b71a2f39812 # v3.0.11 + uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 with: path: .cache/phpcs.json key: ${{ runner.os }}-date-${{ steps.get-date.outputs.date }}-phpcs-cache-${{ hashFiles('**/composer.json', 'phpcs.xml.dist') }} From 0a9b98085a751ec0cd2d717b0165bada3a64a569 Mon Sep 17 00:00:00 2001 From: flootr Date: Tue, 14 Mar 2023 15:07:21 +0100 Subject: [PATCH 142/910] `QueryControls`: refactor away from `lodash.groupBy` (#48779) * `QueryControls`: refactor away from `lodash.groupBy` * clean up a little * clarify usage of custom type guard * add changelog entry --- packages/components/CHANGELOG.md | 1 + .../components/src/query-controls/terms.ts | 47 ++++++++++++------- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 02b83fadfcfc20..a9d18c4ff3040f 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -25,6 +25,7 @@ - `ToolbarButton`: Convert to TypeScript ([#47750](https://github.com/WordPress/gutenberg/pull/47750)). - `DimensionControl(Experimental)`: Convert to TypeScript ([#47351](https://github.com/WordPress/gutenberg/pull/47351)). - `PaletteEdit`: Convert to TypeScript ([#47764](https://github.com/WordPress/gutenberg/pull/47764)). +- `QueryControls`: Refactor away from Lodash (`.groupBy`) ([#48779](https://github.com/WordPress/gutenberg/pull/48779)). ## 23.5.0 (2023-03-01) diff --git a/packages/components/src/query-controls/terms.ts b/packages/components/src/query-controls/terms.ts index d9164dae3a9cd1..7224d3bda6b46e 100644 --- a/packages/components/src/query-controls/terms.ts +++ b/packages/components/src/query-controls/terms.ts @@ -1,8 +1,3 @@ -/** - * External dependencies - */ -import { groupBy } from 'lodash'; - /** * Internal dependencies */ @@ -13,6 +8,11 @@ import type { TermsByParent, } from './types'; +const ensureParentsAreDefined = ( + terms: TermWithParentAndChildren[] +): terms is ( TermWithParentAndChildren & { parent: number } )[] => { + return terms.every( ( term ) => term.parent !== null ); +}; /** * Returns terms in a tree form. * @@ -22,22 +22,33 @@ import type { */ export function buildTermsTree( flatTerms: readonly ( Author | Category )[] ) { const flatTermsWithParentAndChildren: TermWithParentAndChildren[] = - flatTerms.map( ( term ) => { - return { - children: [], - parent: null, - ...term, - id: String( term.id ), - }; - } ); + flatTerms.map( ( term ) => ( { + children: [], + parent: null, + ...term, + id: String( term.id ), + } ) ); - const termsByParent: TermsByParent = groupBy( - flatTermsWithParentAndChildren, - 'parent' - ); - if ( termsByParent.null && termsByParent.null.length ) { + // We use a custom type guard here to ensure that the parent property is + // defined on all terms. The type of the `parent` property is `number | null` + // and we need to ensure that it is `number`. This is because we use the + // `parent` property as a key in the `termsByParent` object. + if ( ! ensureParentsAreDefined( flatTermsWithParentAndChildren ) ) { return flatTermsWithParentAndChildren; } + + const termsByParent = flatTermsWithParentAndChildren.reduce( + ( acc: TermsByParent, term ) => { + const { parent } = term; + if ( ! acc[ parent ] ) { + acc[ parent ] = []; + } + acc[ parent ].push( term ); + return acc; + }, + {} + ); + const fillWithChildren = ( terms: TermWithParentAndChildren[] ): TermWithParentAndChildren[] => { From 979afc85f799d721989c625094b3239af796edaa Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Tue, 14 Mar 2023 18:22:50 +0400 Subject: [PATCH 143/910] Navigation Link: Don't remove 'block_core_navigation_link_build_css_colors' (#49064) --- .../src/navigation-link/index.php | 72 ++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/packages/block-library/src/navigation-link/index.php b/packages/block-library/src/navigation-link/index.php index 4744f7571b6dcd..ab384f8349158c 100644 --- a/packages/block-library/src/navigation-link/index.php +++ b/packages/block-library/src/navigation-link/index.php @@ -5,6 +5,76 @@ * @package WordPress */ +/** + * Build an array with CSS classes and inline styles defining the colors + * which will be applied to the navigation markup in the front-end. + * + * @param array $context Navigation block context. + * @param array $attributes Block attributes. + * @param bool $is_sub_menu Whether the link is part of a sub-menu. + * @return array Colors CSS classes and inline styles. + */ +function block_core_navigation_link_build_css_colors( $context, $attributes, $is_sub_menu = false ) { + $colors = array( + 'css_classes' => array(), + 'inline_styles' => '', + ); + + // Text color. + $named_text_color = null; + $custom_text_color = null; + + if ( $is_sub_menu && array_key_exists( 'customOverlayTextColor', $context ) ) { + $custom_text_color = $context['customOverlayTextColor']; + } elseif ( $is_sub_menu && array_key_exists( 'overlayTextColor', $context ) ) { + $named_text_color = $context['overlayTextColor']; + } elseif ( array_key_exists( 'customTextColor', $context ) ) { + $custom_text_color = $context['customTextColor']; + } elseif ( array_key_exists( 'textColor', $context ) ) { + $named_text_color = $context['textColor']; + } elseif ( isset( $context['style']['color']['text'] ) ) { + $custom_text_color = $context['style']['color']['text']; + } + + // If has text color. + if ( ! is_null( $named_text_color ) ) { + // Add the color class. + array_push( $colors['css_classes'], 'has-text-color', sprintf( 'has-%s-color', $named_text_color ) ); + } elseif ( ! is_null( $custom_text_color ) ) { + // Add the custom color inline style. + $colors['css_classes'][] = 'has-text-color'; + $colors['inline_styles'] .= sprintf( 'color: %s;', $custom_text_color ); + } + + // Background color. + $named_background_color = null; + $custom_background_color = null; + + if ( $is_sub_menu && array_key_exists( 'customOverlayBackgroundColor', $context ) ) { + $custom_background_color = $context['customOverlayBackgroundColor']; + } elseif ( $is_sub_menu && array_key_exists( 'overlayBackgroundColor', $context ) ) { + $named_background_color = $context['overlayBackgroundColor']; + } elseif ( array_key_exists( 'customBackgroundColor', $context ) ) { + $custom_background_color = $context['customBackgroundColor']; + } elseif ( array_key_exists( 'backgroundColor', $context ) ) { + $named_background_color = $context['backgroundColor']; + } elseif ( isset( $context['style']['color']['background'] ) ) { + $custom_background_color = $context['style']['color']['background']; + } + + // If has background color. + if ( ! is_null( $named_background_color ) ) { + // Add the background-color class. + array_push( $colors['css_classes'], 'has-background', sprintf( 'has-%s-background-color', $named_background_color ) ); + } elseif ( ! is_null( $custom_background_color ) ) { + // Add the custom background-color inline style. + $colors['css_classes'][] = 'has-background'; + $colors['inline_styles'] .= sprintf( 'background-color: %s;', $custom_background_color ); + } + + return $colors; +} + /** * Build an array with CSS classes and inline styles defining the font sizes * which will be applied to the navigation markup in the front-end. @@ -107,7 +177,7 @@ function render_block_core_navigation_link( $attributes, $content, $block ) { $classes = array_merge( $font_sizes['css_classes'] ); - $style_attribute = ( $font_sizes['inline_styles'] ); + $style_attribute = $font_sizes['inline_styles']; $css_classes = trim( implode( ' ', $classes ) ); $has_submenu = count( $block->inner_blocks ) > 0; From d7d7c817506e73d2c685e73bf507b6d894fd1298 Mon Sep 17 00:00:00 2001 From: Rich Tabor Date: Tue, 14 Mar 2023 11:18:00 -0400 Subject: [PATCH 144/910] Remove 16:10 from Post Featured Image (#48969) --- .../src/post-featured-image/dimension-controls.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/block-library/src/post-featured-image/dimension-controls.js b/packages/block-library/src/post-featured-image/dimension-controls.js index da66e7f4bda52d..f10872c178b5cc 100644 --- a/packages/block-library/src/post-featured-image/dimension-controls.js +++ b/packages/block-library/src/post-featured-image/dimension-controls.js @@ -96,10 +96,6 @@ const DimensionControls = ( { label: __( 'Square' ), value: '1', }, - { - label: __( '16:10' ), - value: '16/10', - }, { label: __( '16:9' ), value: '16/9', @@ -112,10 +108,6 @@ const DimensionControls = ( { label: __( '3:2' ), value: '3/2', }, - { - label: __( '10:16' ), - value: '10/16', - }, { label: __( '9:16' ), value: '9/16', From dafb94cbf72ac08d744a06d090db8fb9dc12a8f1 Mon Sep 17 00:00:00 2001 From: Michael Day <108079556+mike-day@users.noreply.github.com> Date: Tue, 14 Mar 2023 10:38:07 -0500 Subject: [PATCH 145/910] Refactor ToolbarContext to TS (#49002) * Refactor ToolbarContext to TS * Update changelog --- packages/components/CHANGELOG.md | 1 + .../src/toolbar/toolbar-context/index.js | 10 ---------- .../src/toolbar/toolbar-context/index.ts | 15 +++++++++++++++ 3 files changed, 16 insertions(+), 10 deletions(-) delete mode 100644 packages/components/src/toolbar/toolbar-context/index.js create mode 100644 packages/components/src/toolbar/toolbar-context/index.ts diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index a9d18c4ff3040f..0301bf88e1ddec 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -26,6 +26,7 @@ - `DimensionControl(Experimental)`: Convert to TypeScript ([#47351](https://github.com/WordPress/gutenberg/pull/47351)). - `PaletteEdit`: Convert to TypeScript ([#47764](https://github.com/WordPress/gutenberg/pull/47764)). - `QueryControls`: Refactor away from Lodash (`.groupBy`) ([#48779](https://github.com/WordPress/gutenberg/pull/48779)). +- `ToolbarContext`: Convert to TypeScript ([#49002](https://github.com/WordPress/gutenberg/pull/49002)). ## 23.5.0 (2023-03-01) diff --git a/packages/components/src/toolbar/toolbar-context/index.js b/packages/components/src/toolbar/toolbar-context/index.js deleted file mode 100644 index 1eaeef2b8e243b..00000000000000 --- a/packages/components/src/toolbar/toolbar-context/index.js +++ /dev/null @@ -1,10 +0,0 @@ -// @ts-nocheck - -/** - * WordPress dependencies - */ -import { createContext } from '@wordpress/element'; - -const ToolbarContext = createContext(); - -export default ToolbarContext; diff --git a/packages/components/src/toolbar/toolbar-context/index.ts b/packages/components/src/toolbar/toolbar-context/index.ts new file mode 100644 index 00000000000000..17674798edc31c --- /dev/null +++ b/packages/components/src/toolbar/toolbar-context/index.ts @@ -0,0 +1,15 @@ +/** + * External dependencies + */ +import type { ToolbarStateReturn } from 'reakit/Toolbar'; + +/** + * WordPress dependencies + */ +import { createContext } from '@wordpress/element'; + +const ToolbarContext = createContext< ToolbarStateReturn | undefined >( + undefined +); + +export default ToolbarContext; From 7709c892b1ced5c3a838ee9d842c223da681d0e9 Mon Sep 17 00:00:00 2001 From: Ben Dwyer Date: Tue, 14 Mar 2023 16:18:05 +0000 Subject: [PATCH 146/910] Duotone: Style Engine: Add unit test and associated refactoring (#49033) --- phpunit/style-engine/style-engine-test.php | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/phpunit/style-engine/style-engine-test.php b/phpunit/style-engine/style-engine-test.php index 0588f10ef4fce7..20d817432c0a22 100644 --- a/phpunit/style-engine/style-engine-test.php +++ b/phpunit/style-engine/style-engine-test.php @@ -676,4 +676,30 @@ public function test_should_dedupe_and_merge_css_rules() { $this->assertSame( '.gandalf{color:white;height:190px;border-style:dotted;padding:10px;margin-bottom:100px;}.dumbledore,.rincewind{color:grey;height:90px;border-style:dotted;}', $compiled_stylesheet ); } + + /** + * Tests returning a generated stylesheet from a set of duotone rules. + * + * This is testing this fix: https://github.com/WordPress/gutenberg/pull/49004 + * + * @covers ::gutenberg_style_engine_get_stylesheet_from_css_rules + * @covers WP_Style_Engine_Gutenberg::compile_stylesheet_from_css_rules + */ + public function test_should_return_stylesheet_from_duotone_css_rules() { + $css_rules = array( + array( + 'selector' => '.wp-duotone-ffffff-000000-1', + 'declarations' => array( + // !important is needed because these styles + // render before global styles, + // and they should be overriding the duotone + // filters set by global styles. + 'filter' => "url('#wp-duotone-ffffff-000000-1') !important", + ), + ), + ); + + $compiled_stylesheet = gutenberg_style_engine_get_stylesheet_from_css_rules( $css_rules, array( 'prettify' => false ) ); + $this->assertSame( ".wp-duotone-ffffff-000000-1{filter:url('#wp-duotone-ffffff-000000-1') !important;}", $compiled_stylesheet ); + } } From a2b1c1147b9bb8e53a96908625476ab9e5405be2 Mon Sep 17 00:00:00 2001 From: Rich Tabor Date: Tue, 14 Mar 2023 12:56:49 -0400 Subject: [PATCH 147/910] Use proper active color for block styles control (#46684) * Update style.scss * Update style.scss * Revert "Update style.scss" This reverts commit d11ae3a75e233790e83030b727c619ac4fb9f0e6. --- packages/block-editor/src/components/block-styles/style.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/block-styles/style.scss b/packages/block-editor/src/components/block-styles/style.scss index 794608c5d3c96f..41e8cc18e2653b 100644 --- a/packages/block-editor/src/components/block-styles/style.scss +++ b/packages/block-editor/src/components/block-styles/style.scss @@ -38,7 +38,7 @@ gap: $grid-unit-10; .block-editor-block-styles__item { - color: $gray-800; + color: $gray-900; box-shadow: inset 0 0 0 1px $gray-400; display: inline-block; width: calc(50% - #{$grid-unit-05}); @@ -51,7 +51,7 @@ &.is-active, &.is-active:hover { - background-color: $gray-800; + background-color: $gray-900; box-shadow: none; } From dd9bc7b206adf2c8a17a017945e80f3e97f73407 Mon Sep 17 00:00:00 2001 From: Shelmuk Vladimir Date: Tue, 14 Mar 2023 21:32:23 +0200 Subject: [PATCH 148/910] FormTokenField: Hide suggestions list on blur event if input value is invalid (#48785) * Hide FormTokenField suggestions list on blur event if input value is invalid * Improve test for an empty input * Update CHANGELOG.md * Add validated input example to FormTokeField story * Update Storybook documentation Co-authored-by: Marco Ciampini --------- Co-authored-by: Marco Ciampini --- packages/components/CHANGELOG.md | 1 + .../components/src/form-token-field/index.tsx | 5 +- .../src/form-token-field/stories/index.tsx | 13 +++ .../src/form-token-field/test/index.tsx | 80 +++++++++++++++++++ 4 files changed, 98 insertions(+), 1 deletion(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 0301bf88e1ddec..0fce2b8b6557da 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -5,6 +5,7 @@ ### Enhancements - `FontSizePicker`: Allow custom units for custom font size control ([#48468](https://github.com/WordPress/gutenberg/pull/48468)). +- `FormTokenField`: Hide suggestions list on blur event if the input value is invalid ([#48785](https://github.com/WordPress/gutenberg/pull/48785)). ### Bug Fix diff --git a/packages/components/src/form-token-field/index.tsx b/packages/components/src/form-token-field/index.tsx index a7c8e9d272f8e3..980376e905e782 100644 --- a/packages/components/src/form-token-field/index.tsx +++ b/packages/components/src/form-token-field/index.tsx @@ -153,7 +153,10 @@ export function FormTokenField( props: FormTokenFieldProps ) { } function onBlur() { - if ( inputHasValidValue() ) { + if ( + inputHasValidValue() && + __experimentalValidateInput( incompleteTokenValue ) + ) { setIsActive( false ); } else { // Reset to initial state diff --git a/packages/components/src/form-token-field/stories/index.tsx b/packages/components/src/form-token-field/stories/index.tsx index d4af7209132da2..f120c0dc52211e 100644 --- a/packages/components/src/form-token-field/stories/index.tsx +++ b/packages/components/src/form-token-field/stories/index.tsx @@ -123,3 +123,16 @@ WithCustomRenderItem.args = {
{ `${ item } — a nice place to visit` }
), }; + +/** + * Only values for which the `__experimentalValidateInput` function returns + * `true` will be tokenized. (This is still an experimental feature and is + * subject to change.) + */ +export const WithValidatedInput: ComponentStory< typeof FormTokenField > = + DefaultTemplate.bind( {} ); +WithValidatedInput.args = { + ...Default.args, + __experimentalValidateInput: ( input: string ) => + continents.includes( input ), +}; diff --git a/packages/components/src/form-token-field/test/index.tsx b/packages/components/src/form-token-field/test/index.tsx index 94761d420573b4..a10677f3c10aa1 100644 --- a/packages/components/src/form-token-field/test/index.tsx +++ b/packages/components/src/form-token-field/test/index.tsx @@ -950,6 +950,86 @@ describe( 'FormTokenField', () => { expect( onChangeSpy ).not.toHaveBeenCalled(); } ); + it( 'should hide the suggestion list on an empty input', async () => { + const user = userEvent.setup(); + + const suggestions = [ 'One', 'Two', 'Three' ]; + + render( ); + + const input = screen.getByRole( 'combobox' ); + + await user.type( input, 'on' ); + + expectVisibleSuggestionsToBe( screen.getByRole( 'listbox' ), [ + 'One', + ] ); + + expect( screen.getByRole( 'listbox' ) ).toBeVisible(); + + await user.clear( input ); + + expect( screen.queryByRole( 'listbox' ) ).not.toBeInTheDocument(); + } ); + + it( 'should hide the suggestion list on blur and invalid input', async () => { + const user = userEvent.setup(); + + const suggestions = [ 'One', 'Two', 'Three' ]; + + render( + + suggestions.includes( token ) + } + /> + ); + + const input = screen.getByRole( 'combobox' ); + + await user.type( input, 'on' ); + + expectVisibleSuggestionsToBe( screen.getByRole( 'listbox' ), [ + 'One', + ] ); + + expect( screen.getByRole( 'listbox' ) ).toBeVisible(); + + await user.click( document.body ); + + expect( screen.queryByRole( 'listbox' ) ).not.toBeInTheDocument(); + } ); + + it( 'should not hide the suggestion list on blur and valid input', async () => { + const user = userEvent.setup(); + + const suggestions = [ 'One', 'Two', 'Three' ]; + + render( + + suggestions.includes( token ) + } + /> + ); + + const input = screen.getByRole( 'combobox' ); + + await user.type( input, 'One' ); + + expectVisibleSuggestionsToBe( screen.getByRole( 'listbox' ), [ + 'One', + ] ); + + expect( screen.getByRole( 'listbox' ) ).toBeVisible(); + + await user.click( document.body ); + + expect( screen.getByRole( 'listbox' ) ).toBeVisible(); + } ); + it( 'matches the search text with the suggestions in a case-insensitive way', async () => { const user = userEvent.setup(); From 213bc3fcc2d68abbe5f6b3a3dbbe2b2ec11d60c4 Mon Sep 17 00:00:00 2001 From: Kai Hao Date: Wed, 15 Mar 2023 10:58:06 +0800 Subject: [PATCH 149/910] Site Editor: Add "Added by" description to template part navigation sidebar (#48732) * Add Added by description to template navigation sidebar * Add customized string * Rename to AddedByText * Code review * Keep both descriptions * Concat descriptions * Refactor to postType and postId * Modify into a local hook * Design tweaks * Style adjustments - increase space between icon / label - apply rounded corners to gravatar - colorise fallback icon * Don't show added by active theme --------- Co-authored-by: James Koster --- .../edit-site/src/components/list/added-by.js | 284 +++++++++--------- .../edit-site/src/components/list/table.js | 10 +- .../index.js | 81 ++++- .../style.scss | 25 ++ packages/edit-site/src/style.scss | 1 + 5 files changed, 245 insertions(+), 156 deletions(-) create mode 100644 packages/edit-site/src/components/sidebar-navigation-screen-template/style.scss diff --git a/packages/edit-site/src/components/list/added-by.js b/packages/edit-site/src/components/list/added-by.js index 95107f97edc4e5..ede50d8e22c205 100644 --- a/packages/edit-site/src/components/list/added-by.js +++ b/packages/edit-site/src/components/list/added-by.js @@ -1,3 +1,4 @@ +// @ts-check /** * External dependencies */ @@ -6,7 +7,7 @@ import classnames from 'classnames'; /** * WordPress dependencies */ -import { __experimentalHStack as HStack, Icon } from '@wordpress/components'; +import { Icon, __experimentalHStack as HStack } from '@wordpress/components'; import { store as coreStore } from '@wordpress/core-data'; import { useSelect } from '@wordpress/data'; import { useState } from '@wordpress/element'; @@ -18,25 +19,154 @@ import { } from '@wordpress/icons'; import { _x } from '@wordpress/i18n'; +/** @typedef {'wp_template'|'wp_template_part'} TemplateType */ + +/** @type {TemplateType} */ const TEMPLATE_POST_TYPE_NAMES = [ 'wp_template', 'wp_template_part' ]; -function BaseAddedBy( { text, icon, imageUrl, isCustomized, templateType } ) { +/** + * @typedef {'theme'|'plugin'|'site'|'user'} AddedByType + * + * @typedef AddedByData + * @type {Object} + * @property {AddedByType} type The type of the data. + * @property {JSX.Element} icon The icon to display. + * @property {string} [imageUrl] The optional image URL to display. + * @property {string} [text] The text to display. + * @property {boolean} isCustomized Whether the template has been customized. + * + * @param {TemplateType} postType The template post type. + * @param {number} postId The template post id. + * @return {AddedByData} The added by object or null. + */ +export function useAddedBy( postType, postId ) { + return useSelect( + ( select ) => { + const { + getTheme, + getPlugin, + getEntityRecord, + getMedia, + getUser, + getEditedEntityRecord, + } = select( coreStore ); + const template = getEditedEntityRecord( + 'postType', + postType, + postId + ); + + if ( TEMPLATE_POST_TYPE_NAMES.includes( template.type ) ) { + // Added by theme. + // Template originally provided by a theme, but customized by a user. + // Templates originally didn't have the 'origin' field so identify + // older customized templates by checking for no origin and a 'theme' + // or 'custom' source. + if ( + template.has_theme_file && + ( template.origin === 'theme' || + ( ! template.origin && + [ 'theme', 'custom' ].includes( + template.source + ) ) ) + ) { + return { + type: 'theme', + icon: themeIcon, + text: + getTheme( template.theme )?.name?.rendered || + template.theme, + isCustomized: template.source === 'custom', + }; + } + + // Added by plugin. + if ( template.has_theme_file && template.origin === 'plugin' ) { + return { + type: 'plugin', + icon: pluginIcon, + text: + getPlugin( template.theme )?.name || template.theme, + isCustomized: template.source === 'custom', + }; + } + + // Added by site. + // Template was created from scratch, but has no author. Author support + // was only added to templates in WordPress 5.9. Fallback to showing the + // site logo and title. + if ( + ! template.has_theme_file && + template.source === 'custom' && + ! template.author + ) { + const siteData = getEntityRecord( + 'root', + '__unstableBase' + ); + return { + type: 'site', + icon: globeIcon, + imageUrl: siteData?.site_logo + ? getMedia( siteData.site_logo )?.source_url + : undefined, + text: siteData?.name, + isCustomized: false, + }; + } + } + + // Added by user. + const user = getUser( template.author ); + return { + type: 'user', + icon: authorIcon, + imageUrl: user?.avatar_urls?.[ 48 ], + text: user?.nickname, + isCustomized: false, + }; + }, + [ postType, postId ] + ); +} + +/** + * @param {Object} props + * @param {string} props.imageUrl + */ +function AvatarImage( { imageUrl } ) { const [ isImageLoaded, setIsImageLoaded ] = useState( false ); + return ( +
+ setIsImageLoaded( true ) } + alt="" + src={ imageUrl } + /> +
+ ); +} + +/** + * @param {Object} props + * @param {TemplateType} props.postType The template post type. + * @param {number} props.postId The template post id. + */ +export default function AddedBy( { postType, postId } ) { + const { text, icon, imageUrl, isCustomized } = useAddedBy( + postType, + postId + ); + return ( { imageUrl ? ( -
- setIsImageLoaded( true ) } - alt="" - src={ imageUrl } - /> -
+ ) : (
@@ -46,7 +176,7 @@ function BaseAddedBy( { text, icon, imageUrl, isCustomized, templateType } ) { { text } { isCustomized && ( - { templateType === 'wp_template' + { postType === 'wp_template' ? _x( 'Customized', 'template' ) : _x( 'Customized', 'template part' ) } @@ -55,129 +185,3 @@ function BaseAddedBy( { text, icon, imageUrl, isCustomized, templateType } ) { ); } - -function AddedByTheme( { slug, isCustomized, templateType } ) { - const theme = useSelect( - ( select ) => select( coreStore ).getTheme( slug ), - [ slug ] - ); - - return ( - - ); -} - -function AddedByPlugin( { slug, isCustomized, templateType } ) { - const plugin = useSelect( - ( select ) => select( coreStore ).getPlugin( slug ), - [ slug ] - ); - - return ( - - ); -} - -function AddedByAuthor( { id, templateType } ) { - const user = useSelect( - ( select ) => select( coreStore ).getUser( id ), - [ id ] - ); - - return ( - - ); -} - -function AddedBySite( { templateType } ) { - const { name, logoURL } = useSelect( ( select ) => { - const { getEntityRecord, getMedia } = select( coreStore ); - const siteData = getEntityRecord( 'root', '__unstableBase' ); - - return { - name: siteData?.name, - logoURL: siteData?.site_logo - ? getMedia( siteData.site_logo )?.source_url - : undefined, - }; - }, [] ); - - return ( - - ); -} - -export default function AddedBy( { templateType, template } ) { - if ( ! template ) { - return; - } - - if ( TEMPLATE_POST_TYPE_NAMES.includes( templateType ) ) { - // Template originally provided by a theme, but customized by a user. - // Templates originally didn't have the 'origin' field so identify - // older customized templates by checking for no origin and a 'theme' - // or 'custom' source. - if ( - template.has_theme_file && - ( template.origin === 'theme' || - ( ! template.origin && - [ 'theme', 'custom' ].includes( template.source ) ) ) - ) { - return ( - - ); - } - - // Template originally provided by a plugin, but customized by a user. - if ( template.has_theme_file && template.origin === 'plugin' ) { - return ( - - ); - } - - // Template was created from scratch, but has no author. Author support - // was only added to templates in WordPress 5.9. Fallback to showing the - // site logo and title. - if ( - ! template.has_theme_file && - template.source === 'custom' && - ! template.author - ) { - return ; - } - } - - // Simply show the author for templates created from scratch that have an - // author or for any other post type. - return ( - - ); -} diff --git a/packages/edit-site/src/components/list/table.js b/packages/edit-site/src/components/list/table.js index 1f2b4dc3766816..245f22ea784268 100644 --- a/packages/edit-site/src/components/list/table.js +++ b/packages/edit-site/src/components/list/table.js @@ -102,10 +102,12 @@ export default function Table( { templateType } ) { - + { template ? ( + + ) : null } diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-template/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-template/index.js index b4d1572c3393c2..99139b55d87a5c 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-template/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-template/index.js @@ -1,10 +1,14 @@ /** * WordPress dependencies */ -import { __, sprintf } from '@wordpress/i18n'; -import { useDispatch } from '@wordpress/data'; +import { __, sprintf, _x } from '@wordpress/i18n'; +import { useDispatch, useSelect } from '@wordpress/data'; import { pencil } from '@wordpress/icons'; -import { __experimentalUseNavigator as useNavigator } from '@wordpress/components'; +import { + __experimentalUseNavigator as useNavigator, + Icon, +} from '@wordpress/components'; +import { store as coreStore } from '@wordpress/core-data'; /** * Internal dependencies @@ -14,23 +18,30 @@ import useEditedEntityRecord from '../use-edited-entity-record'; import { unlock } from '../../private-apis'; import { store as editSiteStore } from '../../store'; import SidebarButton from '../sidebar-button'; +import { useAddedBy } from '../list/added-by'; -export default function SidebarNavigationScreenTemplate() { - const { params } = useNavigator(); - const { postType, postId } = params; - const { setCanvasMode } = unlock( useDispatch( editSiteStore ) ); +function useTemplateTitleAndDescription( postType, postId ) { const { getDescription, getTitle, record } = useEditedEntityRecord( postType, postId ); - let description = getDescription(); - if ( ! description ) { + const currentTheme = useSelect( + ( select ) => select( coreStore ).getCurrentTheme(), + [] + ); + const addedBy = useAddedBy( postType, postId ); + const isAddedByActiveTheme = + addedBy.type === 'theme' && record.theme === currentTheme?.stylesheet; + const title = getTitle(); + let descriptionText = getDescription(); + + if ( ! descriptionText && addedBy.text ) { if ( record.type === 'wp_template' && record.is_custom ) { - description = __( + descriptionText = __( 'This is a custom template that can be applied manually to any Post or Page.' ); } else if ( record.type === 'wp_template_part' ) { - description = sprintf( + descriptionText = sprintf( // translators: %s: template part title e.g: "Header". __( 'This is your %s template part.' ), getTitle() @@ -38,9 +49,55 @@ export default function SidebarNavigationScreenTemplate() { } } + const description = ( + <> + { descriptionText } + + { addedBy.text && ! isAddedByActiveTheme && ( + + + + { addedBy.imageUrl ? ( + + ) : ( + + ) } + + { addedBy.text } + + + { addedBy.isCustomized && ( + + { postType === 'wp_template' + ? _x( '(Customized)', 'template' ) + : _x( '(Customized)', 'template part' ) } + + ) } + + ) } + + ); + + return { title, description }; +} + +export default function SidebarNavigationScreenTemplate() { + const { params } = useNavigator(); + const { postType, postId } = params; + const { setCanvasMode } = unlock( useDispatch( editSiteStore ) ); + const { title, description } = useTemplateTitleAndDescription( + postType, + postId + ); + return ( setCanvasMode( 'edit' ) } diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-template/style.scss b/packages/edit-site/src/components/sidebar-navigation-screen-template/style.scss new file mode 100644 index 00000000000000..e86c137a574d5f --- /dev/null +++ b/packages/edit-site/src/components/sidebar-navigation-screen-template/style.scss @@ -0,0 +1,25 @@ +.edit-site-sidebar-navigation-screen-template__added-by-description { + display: flex; + align-items: center; + justify-content: space-between; + margin-top: $grid-unit-30; + + &-author { + display: inline-flex; + align-items: center; + + img { + border-radius: $grid-unit-15; + } + + svg { + fill: $gray-600; + } + + &-icon { + width: 24px; + height: 24px; + margin-right: $grid-unit-10; + } + } +} diff --git a/packages/edit-site/src/style.scss b/packages/edit-site/src/style.scss index 8c3713a988cafb..a68971f9011578 100644 --- a/packages/edit-site/src/style.scss +++ b/packages/edit-site/src/style.scss @@ -25,6 +25,7 @@ @import "./components/sidebar-button/style.scss"; @import "./components/sidebar-navigation-item/style.scss"; @import "./components/sidebar-navigation-screen/style.scss"; +@import "./components/sidebar-navigation-screen-template/style.scss"; @import "./components/sidebar-navigation-screen-templates/style.scss"; @import "./components/site-hub/style.scss"; @import "./components/sidebar-navigation-screen-navigation-menus/style.scss"; From 814a087305b30e906fe54f6e30c6fe4f81795241 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 15 Mar 2023 11:59:34 +0800 Subject: [PATCH 150/910] BlockInvalidWarning: Prefer `canInsertBlockType` and refactor to hooks. (#49052) * Refactor to hooks and fix convert to classic action visible when classic block not insertable * Use a memo instead of a callback --- .../block-list/block-invalid-warning.js | 143 +++++++++--------- 1 file changed, 72 insertions(+), 71 deletions(-) diff --git a/packages/block-editor/src/components/block-list/block-invalid-warning.js b/packages/block-editor/src/components/block-list/block-invalid-warning.js index 922d7080f26ebc..a3ece994916a43 100644 --- a/packages/block-editor/src/components/block-list/block-invalid-warning.js +++ b/packages/block-editor/src/components/block-list/block-invalid-warning.js @@ -4,9 +4,8 @@ import { __, _x } from '@wordpress/i18n'; import { Button, Modal } from '@wordpress/components'; import { useState, useCallback, useMemo } from '@wordpress/element'; -import { getBlockType, createBlock, rawHandler } from '@wordpress/blocks'; -import { compose } from '@wordpress/compose'; -import { withDispatch, withSelect } from '@wordpress/data'; +import { createBlock, rawHandler } from '@wordpress/blocks'; +import { useDispatch, useSelect } from '@wordpress/data'; /** * Internal dependencies @@ -15,45 +14,86 @@ import Warning from '../warning'; import BlockCompare from '../block-compare'; import { store as blockEditorStore } from '../../store'; -export function BlockInvalidWarning( { - convertToHTML, - convertToBlocks, - convertToClassic, - attemptBlockRecovery, - block, -} ) { - const hasHTMLBlock = !! getBlockType( 'core/html' ); - const hasClassicBlock = !! getBlockType( 'core/freeform' ); - const [ compare, setCompare ] = useState( false ); +const blockToBlocks = ( block ) => + rawHandler( { + HTML: block.originalContent, + } ); + +export default function BlockInvalidWarning( { clientId } ) { + const { block, canInsertHTMLBlock, canInsertClassicBlock } = useSelect( + ( select ) => { + const { canInsertBlockType, getBlock, getBlockRootClientId } = + select( blockEditorStore ); - const onCompare = useCallback( () => setCompare( true ), [] ); + const rootClientId = getBlockRootClientId( clientId ); + + return { + block: getBlock( clientId ), + canInsertHTMLBlock: canInsertBlockType( + 'core/html', + rootClientId + ), + canInsertClassicBlock: canInsertBlockType( + 'core/freeform', + rootClientId + ), + }; + }, + [ clientId ] + ); + const { replaceBlock } = useDispatch( blockEditorStore ); + + const [ compare, setCompare ] = useState( false ); const onCompareClose = useCallback( () => setCompare( false ), [] ); - // We memo the array here to prevent the children components from being updated unexpectedly. - const hiddenActions = useMemo( + const convert = useMemo( + () => ( { + toClassic() { + const classicBlock = createBlock( 'core/freeform', { + content: block.originalContent, + } ); + return replaceBlock( block.clientId, classicBlock ); + }, + toHTML() { + const htmlBlock = createBlock( 'core/html', { + content: block.originalContent, + } ); + return replaceBlock( block.clientId, htmlBlock ); + }, + toBlocks() { + const newBlocks = blockToBlocks( block ); + return replaceBlock( block.clientId, newBlocks ); + }, + toRecoveredBlock() { + const recoveredBlock = createBlock( + block.name, + block.attributes, + block.innerBlocks + ); + return replaceBlock( block.clientId, recoveredBlock ); + }, + } ), + [ block, replaceBlock ] + ); + + const secondaryActions = useMemo( () => [ { // translators: Button to fix block content title: _x( 'Resolve', 'imperative verb' ), - onClick: onCompare, + onClick: () => setCompare( true ), }, - hasHTMLBlock && { + canInsertHTMLBlock && { title: __( 'Convert to HTML' ), - onClick: convertToHTML, + onClick: convert.toHTML, }, - hasClassicBlock && { + canInsertClassicBlock && { title: __( 'Convert to Classic Block' ), - onClick: convertToClassic, + onClick: convert.toClassic, }, ].filter( Boolean ), - [ - onCompare, - hasHTMLBlock, - convertToHTML, - hasClassicBlock, - convertToClassic, - ] + [ canInsertHTMLBlock, canInsertClassicBlock, convert ] ); return ( @@ -62,13 +102,13 @@ export function BlockInvalidWarning( { actions={ [ , ] } - secondaryActions={ hiddenActions } + secondaryActions={ secondaryActions } > { __( 'This block contains unexpected or invalid content.' ) } @@ -83,8 +123,8 @@ export function BlockInvalidWarning( { > @@ -93,42 +133,3 @@ export function BlockInvalidWarning( { ); } - -const blockToClassic = ( block ) => - createBlock( 'core/freeform', { - content: block.originalContent, - } ); -const blockToHTML = ( block ) => - createBlock( 'core/html', { - content: block.originalContent, - } ); -const blockToBlocks = ( block ) => - rawHandler( { - HTML: block.originalContent, - } ); -const recoverBlock = ( { name, attributes, innerBlocks } ) => - createBlock( name, attributes, innerBlocks ); - -export default compose( [ - withSelect( ( select, { clientId } ) => ( { - block: select( blockEditorStore ).getBlock( clientId ), - } ) ), - withDispatch( ( dispatch, { block } ) => { - const { replaceBlock } = dispatch( blockEditorStore ); - - return { - convertToClassic() { - replaceBlock( block.clientId, blockToClassic( block ) ); - }, - convertToHTML() { - replaceBlock( block.clientId, blockToHTML( block ) ); - }, - convertToBlocks() { - replaceBlock( block.clientId, blockToBlocks( block ) ); - }, - attemptBlockRecovery() { - replaceBlock( block.clientId, recoverBlock( block ) ); - }, - }; - } ), -] )( BlockInvalidWarning ); From 987f80bddc2f23926ff183365000c0426d109417 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Wed, 15 Mar 2023 14:50:05 +1000 Subject: [PATCH 151/910] Block Deprecations: Provide extra data for isEligible check (#48815) Co-authored-by: Daniel Richards --- .../block-api/block-deprecation.md | 21 +++++++++++++++---- .../parser/apply-block-deprecated-versions.js | 5 ++++- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/docs/reference-guides/block-api/block-deprecation.md b/docs/reference-guides/block-api/block-deprecation.md index 0314939ee8f649..d3773948112c41 100644 --- a/docs/reference-guides/block-api/block-deprecation.md +++ b/docs/reference-guides/block-api/block-deprecation.md @@ -37,8 +37,21 @@ Deprecations are defined on a block type as its `deprecated` property, an array - `attributes` (Object): The [attributes definition](/docs/reference-guides/block-api/block-attributes.md) of the deprecated form of the block. - `supports` (Object): The [supports definition](/docs/reference-guides/block-api/block-registration.md) of the deprecated form of the block. - `save` (Function): The [save implementation](/docs/reference-guides/block-api/block-edit-save.md) of the deprecated form of the block. -- `migrate` (Function, Optional): A function which, given the old attributes and inner blocks is expected to return either the new attributes or a tuple array of `[ attributes, innerBlocks ]` compatible with the block. As mentioned above, a deprecation's `migrate` will not be run if its `save` function does not return a valid block so you will need to make sure your migrations are available in all the deprecations where they are relevant. -- `isEligible` (Function, Optional): A function which, given the attributes and inner blocks of the parsed block, returns true if the deprecation can handle the block migration even if the block is valid. This is particularly useful in cases where a block is technically valid even once deprecated, but still requires updates to its attributes or inner blocks. This function is not called when the results of all previous deprecations' `save` functions were invalid. +- `migrate`: (Function, Optional). A function which, given the old attributes and inner blocks is expected to return either the new attributes or a tuple array of attributes and inner blocks compatible with the block. As mentioned above, a deprecation's `migrate` will not be run if its `save` function does not return a valid block so you will need to make sure your migrations are available in all the deprecations where they are relevant. + - _Parameters_ + - `attributes`: The block's old attributes. + - `innerBlocks`: The block's old inner blocks. + - _Return_ + - `Object | Array`: Either the updated block attributes or tuple array `[attributes, innerBlocks]`. +- `isEligible`: (Function, Optional). A function which returns `true` if the deprecation can handle the block migration even if the block is valid. It is particularly useful in cases where a block is technically valid even once deprecated, but still requires updates to its attributes or inner blocks. This function is **not** called when the results of all previous deprecations' save functions were invalid. + - _Parameters_ + - `attributes`: The raw block attributes as parsed from the serialized HTML, and before the block type code is applied. + - `innerBlocks`: The block's current inner blocks. + - `data`: An object containing properties representing the block node and its resulting block object. + - `data.blockNode`: The raw form of the block as a result of parsing the serialized HTML. + - `data.block`: The block object, which is the result of applying the block type to the `blockNode`. + - _Return_ + - `boolean`: Whether or not this otherwise valid block is eligible to be migrated by this deprecation. It's important to note that `attributes`, `supports`, and `save` are not automatically inherited from the current version, since they can impact parsing and serialization of a block, so they must be defined on the deprecated object in order to be processed during a migration. @@ -63,7 +76,7 @@ registerBlockType( 'gutenberg/block-with-deprecated-version', { // ... other block properties go here attributes, - + supports, save( props ) { @@ -73,7 +86,7 @@ registerBlockType( 'gutenberg/block-with-deprecated-version', { deprecated: [ { attributes, - + supports, save( props ) { diff --git a/packages/blocks/src/api/parser/apply-block-deprecated-versions.js b/packages/blocks/src/api/parser/apply-block-deprecated-versions.js index 2a3e880a3f7460..6c9d81d47b0316 100644 --- a/packages/blocks/src/api/parser/apply-block-deprecated-versions.js +++ b/packages/blocks/src/api/parser/apply-block-deprecated-versions.js @@ -50,7 +50,10 @@ export function applyBlockDeprecatedVersions( block, rawBlock, blockType ) { const { isEligible = stubFalse } = deprecatedDefinitions[ i ]; if ( block.isValid && - ! isEligible( parsedAttributes, block.innerBlocks ) + ! isEligible( parsedAttributes, block.innerBlocks, { + blockNode: rawBlock, + block, + } ) ) { continue; } From 170076a453a8c6f4d148baba81830f620381842d Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 15 Mar 2023 06:41:43 +0100 Subject: [PATCH 152/910] Preferences: Remove `types` field from `package.json` (#49053) * Preferences: Remove `types` field from `package.json` The preferences package does not actually provide any TypeScript types, nor is it written in TypeScript. Adding a `types` field incorrectly declares the opposite. * Fix typing for persistence layer set function --------- Co-authored-by: Daniel Richards --- packages/preferences/package.json | 1 - packages/preferences/src/store/actions.js | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/preferences/package.json b/packages/preferences/package.json index ba1a95190bb2bc..b51571bc870e51 100644 --- a/packages/preferences/package.json +++ b/packages/preferences/package.json @@ -26,7 +26,6 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", - "types": "build-types", "sideEffects": false, "dependencies": { "@babel/runtime": "^7.16.0", diff --git a/packages/preferences/src/store/actions.js b/packages/preferences/src/store/actions.js index 7fcedd49d35297..10b5b45567a7fc 100644 --- a/packages/preferences/src/store/actions.js +++ b/packages/preferences/src/store/actions.js @@ -49,12 +49,12 @@ export function setDefaults( scope, defaults ) { } /** @typedef {() => Promise} WPPreferencesPersistenceLayerGet */ -/** @typedef {(*) => void} WPPreferencesPersistenceLayerSet */ +/** @typedef {(Object) => void} WPPreferencesPersistenceLayerSet */ /** * @typedef WPPreferencesPersistenceLayer * * @property {WPPreferencesPersistenceLayerGet} get An async function that gets data from the persistence layer. - * @property {WPPreferencesPersistenceLayerSet} set A function that sets data in the persistence layer. + * @property {WPPreferencesPersistenceLayerSet} set A function that sets data in the persistence layer. */ /** From 822b035e7d9f161673adce1bfad1331d9633a8bb Mon Sep 17 00:00:00 2001 From: Rich Tabor Date: Wed, 15 Mar 2023 02:45:51 -0400 Subject: [PATCH 153/910] Tweak Latest Posts block PanelBody labels (#49079) * Update labels, edit * Update labels, edit.native --- packages/block-library/src/latest-posts/edit.js | 6 +++--- packages/block-library/src/latest-posts/edit.native.js | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/block-library/src/latest-posts/edit.js b/packages/block-library/src/latest-posts/edit.js index 307bf855bacf27..ad07e48986058c 100644 --- a/packages/block-library/src/latest-posts/edit.js +++ b/packages/block-library/src/latest-posts/edit.js @@ -199,7 +199,7 @@ export default function LatestPostsEdit( { attributes, setAttributes } ) { const hasPosts = !! latestPosts?.length; const inspectorControls = ( - + - + - + - + - + - + Date: Wed, 15 Mar 2023 02:49:18 -0400 Subject: [PATCH 154/910] Tweak label for Latest Posts excerpt control(#49077) --- packages/block-library/src/latest-posts/edit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/latest-posts/edit.js b/packages/block-library/src/latest-posts/edit.js index ad07e48986058c..2e10cb19de0685 100644 --- a/packages/block-library/src/latest-posts/edit.js +++ b/packages/block-library/src/latest-posts/edit.js @@ -229,7 +229,7 @@ export default function LatestPostsEdit( { attributes, setAttributes } ) { displayPostContentRadio === 'excerpt' && ( setAttributes( { excerptLength: value } ) From a071744736766ee8c55f7fdade052f0d33aabd6d Mon Sep 17 00:00:00 2001 From: Rich Tabor Date: Wed, 15 Mar 2023 02:54:47 -0400 Subject: [PATCH 155/910] Tweak PanelBody label (#49076) --- packages/block-library/src/post-featured-image/edit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/post-featured-image/edit.js b/packages/block-library/src/post-featured-image/edit.js index 950dbd57667b3d..cc0b2cfc6cd0cb 100644 --- a/packages/block-library/src/post-featured-image/edit.js +++ b/packages/block-library/src/post-featured-image/edit.js @@ -139,7 +139,7 @@ export default function PostFeaturedImageEdit( { imageSizeOptions={ imageSizeOptions } /> - + Date: Wed, 15 Mar 2023 06:56:21 +0000 Subject: [PATCH 156/910] Adds link to post on the developer blog to the deprecation page (#49069) * Adds deprecation page link to post on the developer blog. * Re-wording of the text --- docs/reference-guides/block-api/block-deprecation.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/reference-guides/block-api/block-deprecation.md b/docs/reference-guides/block-api/block-deprecation.md index d3773948112c41..7154f84072e61f 100644 --- a/docs/reference-guides/block-api/block-deprecation.md +++ b/docs/reference-guides/block-api/block-deprecation.md @@ -1,5 +1,7 @@ # Deprecation +> This page provides a comprehensive guide to the principles and usage of the Deprecation API. For an introduction check out the [tutorial on the basics of block deprecation](https://developer.wordpress.org/news/2023/03/block-deprecation-a-tutorial/) which can be found on the [Developer Blog](https://developer.wordpress.org/news/). + When updating static blocks markup and attributes, block authors need to consider existing posts using the old versions of their block. To provide a good upgrade path, you can choose one of the following strategies: - Do not deprecate the block and create a new one (a different name) From 88073042516767f620367914b87b0dda06c038f6 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Wed, 15 Mar 2023 11:33:36 +0000 Subject: [PATCH 157/910] Fix: Global styles forces a white background. (#49042) --- .../src/components/global-styles/screen-root.js | 2 +- .../global-styles/screen-style-variations.js | 6 +++++- .../edit-site/src/components/global-styles/style.scss | 10 ++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/packages/edit-site/src/components/global-styles/screen-root.js b/packages/edit-site/src/components/global-styles/screen-root.js index 3ec02647b62826..3372826869b3b9 100644 --- a/packages/edit-site/src/components/global-styles/screen-root.js +++ b/packages/edit-site/src/components/global-styles/screen-root.js @@ -51,7 +51,7 @@ function ScreenRoot() { }, [] ); return ( - + diff --git a/packages/edit-site/src/components/global-styles/screen-style-variations.js b/packages/edit-site/src/components/global-styles/screen-style-variations.js index 10a5b66aa27bc6..b7992f10ba351f 100644 --- a/packages/edit-site/src/components/global-styles/screen-style-variations.js +++ b/packages/edit-site/src/components/global-styles/screen-style-variations.js @@ -172,7 +172,11 @@ function ScreenStyleVariations() { ) } /> - + { withEmptyVariation?.map( ( variation, index ) => ( diff --git a/packages/edit-site/src/components/global-styles/style.scss b/packages/edit-site/src/components/global-styles/style.scss index 22634292a6bab1..a348bd72173b3d 100644 --- a/packages/edit-site/src/components/global-styles/style.scss +++ b/packages/edit-site/src/components/global-styles/style.scss @@ -241,3 +241,13 @@ height: 24px; width: 24px; } + +.edit-site-global-styles-screen-root.edit-site-global-styles-screen-root, +.edit-site-global-styles-screen-style-variations.edit-site-global-styles-screen-style-variations { + background: unset; + color: inherit; +} + +.edit-site-global-styles-sidebar__panel .block-editor-block-icon svg { + fill: currentColor; +} From 352b8361447ff275822f0da9a89e862d183dd3b7 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 15 Mar 2023 14:18:01 +0100 Subject: [PATCH 158/910] `Navigator`: disable initial animation (#49062) * Navigator: disable initial animation * Fix snapshot * Remove framer-motion mocks from Navigator tests * Add unit tests for skipping initial animation * Add inline comment * CHANGELOG --- packages/components/CHANGELOG.md | 1 + .../navigator/navigator-screen/component.tsx | 20 +++-- .../components/src/navigator/test/index.tsx | 87 ++++++++++++++----- .../test/__snapshots__/index.js.snap | 2 +- 4 files changed, 80 insertions(+), 30 deletions(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 0fce2b8b6557da..52fee76a975da5 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -5,6 +5,7 @@ ### Enhancements - `FontSizePicker`: Allow custom units for custom font size control ([#48468](https://github.com/WordPress/gutenberg/pull/48468)). +- `Navigator`: Disable initial screen animation ([#49062](https://github.com/WordPress/gutenberg/pull/49062)). - `FormTokenField`: Hide suggestions list on blur event if the input value is invalid ([#48785](https://github.com/WordPress/gutenberg/pull/48785)). ### Bug Fix diff --git a/packages/components/src/navigator/navigator-screen/component.tsx b/packages/components/src/navigator/navigator-screen/component.tsx index 15cec834d8b8ad..2d4588d91b14d6 100644 --- a/packages/components/src/navigator/navigator-screen/component.tsx +++ b/packages/components/src/navigator/navigator-screen/component.tsx @@ -172,13 +172,19 @@ function UnconnectedNavigatorScreen( }, x: 0, }; - const initial = { - opacity: 0, - x: - ( isRTL() && location.isBack ) || ( ! isRTL() && ! location.isBack ) - ? 50 - : -50, - }; + // Disable the initial animation if the screen is the very first screen to be + // rendered within the current `NavigatorProvider`. + const initial = + location.isInitial && ! location.isBack + ? false + : { + opacity: 0, + x: + ( isRTL() && location.isBack ) || + ( ! isRTL() && ! location.isBack ) + ? 50 + : -50, + }; const exit = { delay: animationExitDelay, opacity: 0, diff --git a/packages/components/src/navigator/test/index.tsx b/packages/components/src/navigator/test/index.tsx index 927f6f3abf0e04..a27be5475d8028 100644 --- a/packages/components/src/navigator/test/index.tsx +++ b/packages/components/src/navigator/test/index.tsx @@ -1,7 +1,7 @@ /** * External dependencies */ -import type { ReactNode, ForwardedRef, ComponentPropsWithoutRef } from 'react'; +import type { ComponentPropsWithoutRef } from 'react'; import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; @@ -23,27 +23,6 @@ import { useNavigator, } from '..'; -jest.mock( 'framer-motion', () => { - const actual = jest.requireActual( 'framer-motion' ); - return { - __esModule: true, - ...actual, - AnimatePresence: - ( { children }: { children?: ReactNode } ) => - () => -
{ children }
, - motion: { - ...actual.motion, - div: require( 'react' ).forwardRef( - ( - { children }: { children?: ReactNode }, - ref: ForwardedRef< HTMLDivElement > - ) =>
{ children }
- ), - }, - }; -} ); - const INVALID_HTML_ATTRIBUTE = { raw: ' "\'><=invalid_path', escaped: " "'><=invalid_path", @@ -738,4 +717,68 @@ describe( 'Navigator', () => { expect( getNavigationButton( 'toChildScreen' ) ).toHaveFocus(); } ); } ); + + describe( 'animation', () => { + it( 'should not animate the initial screen', async () => { + const onHomeAnimationStartSpy = jest.fn(); + + render( + + + + To child + + + + ); + + expect( onHomeAnimationStartSpy ).not.toHaveBeenCalled(); + } ); + + it( 'should animate all other screens (including the initial screen when navigating back)', async () => { + const user = userEvent.setup(); + + const onHomeAnimationStartSpy = jest.fn(); + const onChildAnimationStartSpy = jest.fn(); + + render( + + + + To child + + + + + Back to home + + + + ); + + expect( onHomeAnimationStartSpy ).not.toHaveBeenCalled(); + expect( onChildAnimationStartSpy ).not.toHaveBeenCalled(); + + await user.click( + screen.getByRole( 'button', { name: 'To child' } ) + ); + expect( onChildAnimationStartSpy ).toHaveBeenCalledTimes( 1 ); + expect( onHomeAnimationStartSpy ).not.toHaveBeenCalled(); + + await user.click( + screen.getByRole( 'button', { name: 'Back to home' } ) + ); + expect( onChildAnimationStartSpy ).toHaveBeenCalledTimes( 1 ); + expect( onHomeAnimationStartSpy ).toHaveBeenCalledTimes( 1 ); + } ); + } ); } ); diff --git a/packages/edit-post/src/components/preferences-modal/test/__snapshots__/index.js.snap b/packages/edit-post/src/components/preferences-modal/test/__snapshots__/index.js.snap index 67d5ee4e184366..c666ab81e0724f 100644 --- a/packages/edit-post/src/components/preferences-modal/test/__snapshots__/index.js.snap +++ b/packages/edit-post/src/components/preferences-modal/test/__snapshots__/index.js.snap @@ -733,7 +733,7 @@ exports[`EditPostPreferencesModal should match snapshot when the modal is active class="emotion-2 components-navigator-screen" data-wp-c16t="true" data-wp-component="NavigatorScreen" - style="opacity: 0; transform: translateX(50px) translateZ(0);" + style="opacity: 1; transform: none;" >
Date: Wed, 15 Mar 2023 22:19:36 +0900 Subject: [PATCH 159/910] withNotices: Convert to TypeScript (#49088) * withNotices: Convert to TypeScript * Make type exportable * Add changelog * Add `noticeList` to readme --- packages/components/CHANGELOG.md | 1 + .../src/higher-order/with-notices/README.md | 5 +- .../src/higher-order/with-notices/index.js | 104 ---------------- .../src/higher-order/with-notices/index.tsx | 116 ++++++++++++++++++ .../with-notices/test/{index.js => index.tsx} | 17 ++- .../src/higher-order/with-notices/types.ts | 35 ++++++ packages/components/tsconfig.json | 3 +- 7 files changed, 168 insertions(+), 113 deletions(-) delete mode 100644 packages/components/src/higher-order/with-notices/index.js create mode 100644 packages/components/src/higher-order/with-notices/index.tsx rename packages/components/src/higher-order/with-notices/test/{index.js => index.tsx} (91%) create mode 100644 packages/components/src/higher-order/with-notices/types.ts diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 52fee76a975da5..65261f11ced412 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -24,6 +24,7 @@ - `withFocusReturn` HOC: Convert to TypeScript ([#48748](https://github.com/WordPress/gutenberg/pull/48748)). - `navigateRegions` HOC: Convert to TypeScript ([#48632](https://github.com/WordPress/gutenberg/pull/48632)). - `withSpokenMessages`: HOC: Convert to TypeScript ([#48163](https://github.com/WordPress/gutenberg/pull/48163)). +- `withNotices`: HOC: Convert to TypeScript ([#49088](https://github.com/WordPress/gutenberg/pull/49088)). - `ToolbarButton`: Convert to TypeScript ([#47750](https://github.com/WordPress/gutenberg/pull/47750)). - `DimensionControl(Experimental)`: Convert to TypeScript ([#47351](https://github.com/WordPress/gutenberg/pull/47351)). - `PaletteEdit`: Convert to TypeScript ([#47764](https://github.com/WordPress/gutenberg/pull/47764)). diff --git a/packages/components/src/higher-order/with-notices/README.md b/packages/components/src/higher-order/with-notices/README.md index 84c484eba101b2..17e1b0e12113b9 100644 --- a/packages/components/src/higher-order/with-notices/README.md +++ b/packages/components/src/higher-order/with-notices/README.md @@ -2,7 +2,7 @@ `withNotices` is a React [higher-order component](https://facebook.github.io/react/docs/higher-order-components.html) used typically in adding the ability to post notice messages within the original component. -Wrapping the original component with `withNotices` encapsulates the component with the additional props `noticeOperations` and `noticeUI`. +Wrapping the original component with `withNotices` encapsulates the component with the additional props `noticeOperations`, `noticeUI`, and `noticeList`. **noticeOperations** Contains a number of useful functions to add notices to your site. @@ -34,6 +34,9 @@ _Parameters_ #**noticeUi** The rendered `NoticeList`. +#**noticeList** +The array of notice objects to be displayed. + ## Usage ```jsx diff --git a/packages/components/src/higher-order/with-notices/index.js b/packages/components/src/higher-order/with-notices/index.js deleted file mode 100644 index c4057e6b6fff3e..00000000000000 --- a/packages/components/src/higher-order/with-notices/index.js +++ /dev/null @@ -1,104 +0,0 @@ -/** - * External dependencies - */ -import { v4 as uuid } from 'uuid'; - -/** - * WordPress dependencies - */ -import { forwardRef, useState, useMemo } from '@wordpress/element'; -import { createHigherOrderComponent } from '@wordpress/compose'; - -/** - * Internal dependencies - */ -import NoticeList from '../../notice/list'; - -/** - * Override the default edit UI to include notices if supported. - * - * @param {WPComponent} OriginalComponent Original component. - * - * @return {WPComponent} Wrapped component. - */ -export default createHigherOrderComponent( ( OriginalComponent ) => { - function Component( props, ref ) { - const [ noticeList, setNoticeList ] = useState( [] ); - - const noticeOperations = useMemo( () => { - /** - * Function passed down as a prop that adds a new notice. - * - * @param {Object} notice Notice to add. - */ - const createNotice = ( notice ) => { - const noticeToAdd = notice.id - ? notice - : { ...notice, id: uuid() }; - setNoticeList( ( current ) => [ ...current, noticeToAdd ] ); - }; - - return { - createNotice, - - /** - * Function passed as a prop that adds a new error notice. - * - * @param {string} msg Error message of the notice. - */ - createErrorNotice: ( msg ) => { - createNotice( { - status: 'error', - content: msg, - } ); - }, - - /** - * Removes a notice by id. - * - * @param {string} id Id of the notice to remove. - */ - removeNotice: ( id ) => { - setNoticeList( ( current ) => - current.filter( ( notice ) => notice.id !== id ) - ); - }, - - /** - * Removes all notices - */ - removeAllNotices: () => { - setNoticeList( [] ); - }, - }; - }, [] ); - - const propsOut = { - ...props, - noticeList, - noticeOperations, - noticeUI: noticeList.length > 0 && ( - - ), - }; - - return isForwardRef ? ( - - ) : ( - - ); - } - - let isForwardRef; - const { render } = OriginalComponent; - // Returns a forwardRef if OriginalComponent appears to be a forwardRef. - if ( typeof render === 'function' ) { - isForwardRef = true; - return forwardRef( Component ); - } - return Component; -} ); diff --git a/packages/components/src/higher-order/with-notices/index.tsx b/packages/components/src/higher-order/with-notices/index.tsx new file mode 100644 index 00000000000000..734fd8b2b1056c --- /dev/null +++ b/packages/components/src/higher-order/with-notices/index.tsx @@ -0,0 +1,116 @@ +/** + * External dependencies + */ +import { v4 as uuid } from 'uuid'; + +/** + * WordPress dependencies + */ +import { forwardRef, useState, useMemo } from '@wordpress/element'; +import { createHigherOrderComponent } from '@wordpress/compose'; + +/** + * Internal dependencies + */ +import NoticeList from '../../notice/list'; +import type { WithNoticeProps } from './types'; + +/** + * Override the default edit UI to include notices if supported. + * + * Wrapping the original component with `withNotices` encapsulates the component + * with the additional props `noticeOperations` and `noticeUI`. + * + * ```jsx + * import { withNotices, Button } from '@wordpress/components'; + * + * const MyComponentWithNotices = withNotices( + * ( { noticeOperations, noticeUI } ) => { + * const addError = () => + * noticeOperations.createErrorNotice( 'Error message' ); + * return ( + *
+ * { noticeUI } + * + *
+ * ); + * } + * ); + * ``` + * + * @param OriginalComponent Original component. + * + * @return Wrapped component. + */ +export default createHigherOrderComponent( ( OriginalComponent ) => { + function Component( + props: { [ key: string ]: any }, + ref: React.ForwardedRef< any > + ) { + const [ noticeList, setNoticeList ] = useState< + WithNoticeProps[ 'noticeList' ] + >( [] ); + + const noticeOperations = useMemo< + WithNoticeProps[ 'noticeOperations' ] + >( () => { + const createNotice: WithNoticeProps[ 'noticeOperations' ][ 'createNotice' ] = + ( notice ) => { + const noticeToAdd = notice.id + ? notice + : { ...notice, id: uuid() }; + setNoticeList( ( current ) => [ ...current, noticeToAdd ] ); + }; + + return { + createNotice, + createErrorNotice: ( msg ) => { + // @ts-expect-error TODO: Missing `id`, potentially a bug + createNotice( { + status: 'error', + content: msg, + } ); + }, + removeNotice: ( id ) => { + setNoticeList( ( current ) => + current.filter( ( notice ) => notice.id !== id ) + ); + }, + removeAllNotices: () => { + setNoticeList( [] ); + }, + }; + }, [] ); + + const propsOut = { + ...props, + noticeList, + noticeOperations, + noticeUI: noticeList.length > 0 && ( + + ), + }; + + return isForwardRef ? ( + + ) : ( + + ); + } + + let isForwardRef: boolean; + // @ts-expect-error - `render` will only be present when OriginalComponent was wrapped with forwardRef(). + const { render } = OriginalComponent; + // Returns a forwardRef if OriginalComponent appears to be a forwardRef. + if ( typeof render === 'function' ) { + isForwardRef = true; + return forwardRef( Component ); + } + return Component; +}, 'withNotices' ); diff --git a/packages/components/src/higher-order/with-notices/test/index.js b/packages/components/src/higher-order/with-notices/test/index.tsx similarity index 91% rename from packages/components/src/higher-order/with-notices/test/index.js rename to packages/components/src/higher-order/with-notices/test/index.tsx index a403f3c3a80942..cbad7270414e3e 100644 --- a/packages/components/src/higher-order/with-notices/test/index.js +++ b/packages/components/src/higher-order/with-notices/test/index.tsx @@ -24,25 +24,30 @@ import { * Internal dependencies */ import withNotices from '..'; +import type { WithNoticeProps } from '../types'; // Implementation detail of Notice component used to query the dismissal button. const stockDismissText = 'Dismiss this notice'; -function noticesFrom( list ) { +function noticesFrom( list: string[] ) { return list.map( ( item ) => ( { id: item, content: item } ) ); } -function isComponentLike( object ) { +function isComponentLike( object: any ) { return typeof object === 'function'; } -function isForwardRefLike( { render: renderMethod } ) { +function isForwardRefLike( { render: renderMethod }: any ) { return typeof renderMethod === 'function'; } const content = 'Base content'; -const BaseComponent = ( { noticeOperations, noticeUI, notifications } ) => { +const BaseComponent = ( { + noticeOperations, + noticeUI, + notifications, +}: WithNoticeProps & { notifications: ReturnType< typeof noticesFrom > } ) => { useEffect( () => { if ( notifications ) { notifications.forEach( ( item ) => @@ -78,8 +83,8 @@ describe( 'withNotices return type', () => { } ); describe( 'withNotices operations', () => { - let handle; - const Handle = ( props ) => { + let handle: React.MutableRefObject< any >; + const Handle = ( props: any ) => { handle = useRef(); return ; }; diff --git a/packages/components/src/higher-order/with-notices/types.ts b/packages/components/src/higher-order/with-notices/types.ts new file mode 100644 index 00000000000000..e914d21d82df7d --- /dev/null +++ b/packages/components/src/higher-order/with-notices/types.ts @@ -0,0 +1,35 @@ +/** + * Internal dependencies + */ +import type { NoticeListProps } from '../../notice/types'; + +export type WithNoticeProps = { + noticeList: NoticeListProps[ 'notices' ]; + noticeOperations: { + /** + * Function passed down as a prop that adds a new notice. + * + * @param notice Notice to add. + */ + createNotice: ( + notice: NoticeListProps[ 'notices' ][ number ] + ) => void; + /** + * Function passed as a prop that adds a new error notice. + * + * @param msg Error message of the notice. + */ + createErrorNotice: ( msg: string ) => void; + /** + * Removes a notice by id. + * + * @param id Id of the notice to remove. + */ + removeNotice: ( id: string ) => void; + /** + * Removes all notices + */ + removeAllNotices: () => void; + }; + noticeUI: false | JSX.Element; +}; diff --git a/packages/components/tsconfig.json b/packages/components/tsconfig.json index aa2434d026ca17..6aaadae65b49cc 100644 --- a/packages/components/tsconfig.json +++ b/packages/components/tsconfig.json @@ -45,7 +45,6 @@ "src/**/stories/**/*.js", // only exclude js files, tsx files should be checked "src/**/test/**/*.js", // only exclude js files, ts{x} files should be checked "src/index.js", - "src/duotone-picker", - "src/higher-order/with-notices" + "src/duotone-picker" ] } From ff2661493d0e287090d192afecddc2bb36c6932b Mon Sep 17 00:00:00 2001 From: Andrei Draganescu Date: Wed, 15 Mar 2023 15:49:00 +0200 Subject: [PATCH 160/910] manage selection on block sync (#48979) --- .../src/components/provider/use-block-sync.js | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/packages/block-editor/src/components/provider/use-block-sync.js b/packages/block-editor/src/components/provider/use-block-sync.js index 6e9b2c0474e15a..f7392f99035a95 100644 --- a/packages/block-editor/src/components/provider/use-block-sync.js +++ b/packages/block-editor/src/components/provider/use-block-sync.js @@ -76,10 +76,18 @@ export default function useBlockSync( { resetBlocks, resetSelection, replaceInnerBlocks, + selectBlock, setHasControlledInnerBlocks, __unstableMarkNextChangeAsNotPersistent, } = registry.dispatch( blockEditorStore ); - const { getBlockName, getBlocks } = registry.select( blockEditorStore ); + const { + hasSelectedBlock, + getBlockName, + getBlocks, + getSelectionStart, + getSelectionEnd, + getBlock, + } = registry.select( blockEditorStore ); const isControlled = useSelect( ( select ) => { return ( @@ -159,6 +167,9 @@ export default function useBlockSync( { // bound sync, unset the outbound value to avoid considering it in // subsequent renders. pendingChanges.current.outgoing = []; + const hadSelecton = hasSelectedBlock(); + const selectionAnchor = getSelectionStart(); + const selectionFocus = getSelectionEnd(); setControlledBlocks(); if ( controlledSelection ) { @@ -167,6 +178,15 @@ export default function useBlockSync( { controlledSelection.selectionEnd, controlledSelection.initialPosition ); + } else { + const selectionStillExists = getBlock( + selectionAnchor.clientId + ); + if ( hadSelecton && ! selectionStillExists ) { + selectBlock( clientId ); + } else { + resetSelection( selectionAnchor, selectionFocus ); + } } } }, [ controlledBlocks, clientId ] ); @@ -182,8 +202,6 @@ export default function useBlockSync( { useEffect( () => { const { - getSelectionStart, - getSelectionEnd, getSelectedBlocksInitialCaretPosition, isLastBlockChangePersistent, __unstableIsLastBlockChangeIgnored, @@ -220,7 +238,6 @@ export default function useBlockSync( { const newBlocks = getBlocks( clientId ); const areBlocksDifferent = newBlocks !== blocks; blocks = newBlocks; - if ( areBlocksDifferent && ( pendingChanges.current.incoming || From 88f12f4ba274ffd08229e2256f6eb31272a363b9 Mon Sep 17 00:00:00 2001 From: Rich Tabor Date: Wed, 15 Mar 2023 10:19:37 -0400 Subject: [PATCH 161/910] Add help text to Gallery Image Size control (#49074) * Add help text to SelectControl * Make images plural --- packages/block-library/src/gallery/edit.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/block-library/src/gallery/edit.js b/packages/block-library/src/gallery/edit.js index 3a58a291738af9..7ab1b1307056f5 100644 --- a/packages/block-library/src/gallery/edit.js +++ b/packages/block-library/src/gallery/edit.js @@ -591,6 +591,9 @@ function GalleryEdit( props ) { Date: Wed, 15 Mar 2023 09:25:01 -0500 Subject: [PATCH 162/910] Duotone: Limit SVG filter output to used filters (#48995) Adds the needed duotone code to the frontend output if that duotone definition is being used. * Adds a new WP_Duotone class that handles what filters need to be added to the page based on what duotone filters, if any, are in use. * Removes all duotone output initially via removing the actions that add them * Registers all duotone filters from global styles and store in the WP_Duotone class * Registers all block-level duotone filters from global styles and stores in the WP_Duotone class * Process all blocks via `render_block` filter to determine the correct duotone output needed. This is the same as before, but we're only outputting the duotone code if needed. * On the 'wp_enqueue_scripts' action, output all the necessary CSS for duotone filters collected from WP_Duotone::$output`. * On the 'wp_footer' action, output all necessary SVG for duotone filters from WP_Duotone::$output --- lib/block-supports/duotone.php | 190 ++++++++++++++------- lib/class-wp-duotone.php | 150 ++++++++++++++++ lib/class-wp-theme-json-gutenberg.php | 37 +++- lib/compat/wordpress-6.2/script-loader.php | 4 + lib/load.php | 1 + phpunit/block-supports/duotone-test.php | 41 ++++- 6 files changed, 358 insertions(+), 65 deletions(-) create mode 100644 lib/class-wp-duotone.php diff --git a/lib/block-supports/duotone.php b/lib/block-supports/duotone.php index ef950f80ce9448..afb521f5ea73eb 100644 --- a/lib/block-supports/duotone.php +++ b/lib/block-supports/duotone.php @@ -436,54 +436,67 @@ function gutenberg_render_duotone_support( $block_content, $block ) { $duotone_support = _wp_array_get( $block_type->supports, array( 'color', '__experimentalDuotone' ), false ); } - $has_duotone_attribute = isset( $block['attrs']['style']['color']['duotone'] ); + // The block should have a duotone attribute or have duotone defined in its theme.json to be processed. + $has_duotone_attribute = isset( $block['attrs']['style']['color']['duotone'] ); + $has_global_styles_duotone = array_key_exists( $block['blockName'], WP_Duotone::$global_styles_block_names ); if ( + empty( $block_content ) || ! $duotone_support || - ! $has_duotone_attribute + ( ! $has_duotone_attribute && ! $has_global_styles_duotone ) ) { return $block_content; } - // Possible values for duotone attribute: - // 1. Array of colors - e.g. array('#000000', '#ffffff'). - // 2. Variable for an existing Duotone preset - e.g. 'var:preset|duotone|green-blue'. - // 3. A CSS string - e.g. 'unset' to remove globally applied duotone. - $duotone_attr = $block['attrs']['style']['color']['duotone']; + // Generate the pieces needed for rendering a duotone to the page. + if ( $has_duotone_attribute ) { - $is_preset = is_string( $duotone_attr ) && strpos( $duotone_attr, 'var:preset|duotone|' ) === 0; - $is_css = is_string( $duotone_attr ) && strpos( $duotone_attr, 'var:preset|duotone|' ) === false; - $is_custom = is_array( $duotone_attr ); + // Possible values for duotone attribute: + // 1. Array of colors - e.g. array('#000000', '#ffffff'). + // 2. Variable for an existing Duotone preset - e.g. 'var:preset|duotone|green-blue' or 'var(--wp--preset--duotone--green-blue)'' + // 3. A CSS string - e.g. 'unset' to remove globally applied duotone. - // Generate the pieces needed for rendering a duotone to the page. - if ( $is_preset ) { - // Extract the slug from the preset variable string. - $slug = str_replace( 'var:preset|duotone|', '', $duotone_attr ); + $duotone_attr = $block['attrs']['style']['color']['duotone']; + $is_preset = is_string( $duotone_attr ) && WP_Duotone::is_preset( $duotone_attr ); + $is_css = is_string( $duotone_attr ) && ! $is_preset; + $is_custom = is_array( $duotone_attr ); + + if ( $is_preset ) { + + // Extract the slug from the preset variable string. + $slug = WP_Duotone::gutenberg_get_slug_from_attr( $duotone_attr ); + + // Utilize existing preset CSS custom property. + $filter_property = "var(--wp--preset--duotone--$slug)"; + + WP_Duotone::$output[ $slug ] = WP_Duotone::$global_styles_presets[ $slug ]; + + } elseif ( $is_css ) { + // Build a unique slug for the filter based on the CSS value. + $slug = wp_unique_id( sanitize_key( $duotone_attr . '-' ) ); + + // Pass through the CSS value. + $filter_property = $duotone_attr; + } elseif ( $is_custom ) { + // Build a unique slug for the filter based on the array of colors. + $slug = wp_unique_id( sanitize_key( implode( '-', $duotone_attr ) . '-' ) ); + + $filter_data = array( + 'slug' => $slug, + 'colors' => $duotone_attr, + ); + // Build a customized CSS filter property for unique slug. + $filter_property = gutenberg_get_duotone_filter_property( $filter_data ); + + WP_Duotone::$output[ $slug ] = $filter_data; + } + } elseif ( $has_global_styles_duotone ) { + $slug = WP_Duotone::$global_styles_block_names[ $block['blockName'] ]; // Utilize existing preset CSS custom property. $filter_property = "var(--wp--preset--duotone--$slug)"; - } elseif ( $is_css ) { - // Build a unique slug for the filter based on the CSS value. - $slug = wp_unique_id( sanitize_key( $duotone_attr . '-' ) ); - - // Pass through the CSS value. - $filter_property = $duotone_attr; - } elseif ( $is_custom ) { - // Build a unique slug for the filter based on the array of colors. - $slug = wp_unique_id( sanitize_key( implode( '-', $duotone_attr ) . '-' ) ); - - // This has the same shape as a preset, so it can be used in place of a - // preset when getting the filter property and SVG filter. - $filter_data = array( - 'slug' => $slug, - 'colors' => $duotone_attr, - ); - // Build a customized CSS filter property for unique slug. - $filter_property = gutenberg_get_duotone_filter_property( $filter_data ); - - // SVG will be output on the page later. - $filter_svg = gutenberg_get_duotone_filter_svg( $filter_data ); + WP_Duotone::$output[ $slug ] = WP_Duotone::$global_styles_presets[ $slug ]; } // - Applied as a class attribute to the block wrapper. @@ -493,6 +506,11 @@ function gutenberg_render_duotone_support( $block_content, $block ) { // Build the CSS selectors to which the filter will be applied. $selector = WP_Theme_JSON_Gutenberg::scope_selector( '.' . $filter_id, $duotone_support ); + // We only want to add the selector if we have it in the output already, essentially skipping 'unset'. + if ( array_key_exists( $slug, WP_Duotone::$output ) ) { + WP_Duotone::$output[ $slug ]['selector'] = $selector; + } + // Calling gutenberg_style_engine_get_stylesheet_from_css_rules ensures that // the styles are rendered in an inline for block supports because we're // using the `context` option to instruct it so. @@ -514,33 +532,6 @@ function gutenberg_render_duotone_support( $block_content, $block ) { ) ); - // If we needed to generate an SVG, output it on the page. - if ( isset( $filter_svg ) ) { - add_action( - 'wp_footer', - static function () use ( $filter_svg, $selector ) { - echo $filter_svg; - - /* - * Safari renders elements incorrectly on first paint when the - * SVG filter comes after the content that it is filtering, so - * we force a repaint with a WebKit hack which solves the issue. - */ - global $is_safari; - if ( $is_safari ) { - /* - * Simply accessing el.offsetHeight flushes layout and style - * changes in WebKit without having to wait for setTimeout. - */ - printf( - '', - wp_json_encode( $selector ) - ); - } - } - ); - } - // Like the layout hook, this assumes the hook only applies to blocks with a single wrapper. return preg_replace( '/' . preg_quote( 'class="', '/' ) . '/', @@ -550,6 +541,81 @@ static function () use ( $filter_svg, $selector ) { ); } + +add_action( + 'wp_footer', + static function () { + + foreach ( WP_Duotone::$output as $filter_data ) { + + $filter_property = gutenberg_get_duotone_filter_property( $filter_data ); + // SVG will be output on the page later. + $filter_svg = gutenberg_get_duotone_filter_svg( $filter_data ); + + echo $filter_svg; + + // This is for classic themes - in block themes, the CSS is added in the head via the value_func. + if ( ! wp_is_block_theme() ) { + $duotone_preset_css_var = WP_Theme_JSON_Gutenberg::get_preset_css_var( array( 'color', 'duotone' ), $filter_data['slug'] ); + wp_add_inline_style( 'core-block-supports', 'body{' . $duotone_preset_css_var . ' :' . $filter_property . ';}' ); + } + + global $is_safari; + if ( $is_safari ) { + duotone_safari_rerender_hack( $filter_data['selector'] ); + } + } + } +); + +/** + * Appends the used duotone fitler CSS Vars to the inline global styles CSS + */ +add_action( + 'wp_enqueue_scripts', + static function() { + + if ( empty( WP_Duotone::$output ) ) { + return; + } + + $duotone_css_vars = ''; + + foreach ( WP_Duotone::$output as $filter_data ) { + if ( ! array_key_exists( $filter_data['slug'], WP_Duotone::$global_styles_presets ) ) { + continue; + } + + $filter_property = gutenberg_get_duotone_filter_property( $filter_data ); + + $duotone_preset_css_var = WP_Theme_JSON_Gutenberg::get_preset_css_var( array( 'color', 'duotone' ), $filter_data['slug'] ); + $duotone_css_vars .= $duotone_preset_css_var . ': ' . $filter_property . ';'; + } + + if ( ! empty( $duotone_css_vars ) ) { + wp_add_inline_style( 'global-styles', 'body{' . $duotone_css_vars . '}' ); + } + }, + 11 +); + +/** + * Safari renders elements incorrectly on first paint when the SVG filter comes after the content that it is filtering, + * so we force a repaint with a WebKit hack which solves the issue. + * + * @param string $selector The selector to apply the hack for. + */ +function duotone_safari_rerender_hack( $selector ) { + /* + * Simply accessing el.offsetHeight flushes layout and style + * changes in WebKit without having to wait for setTimeout. + */ + printf( + '', + wp_json_encode( $selector ) + ); +} + // Register the block support. WP_Block_Supports::get_instance()->register( 'duotone', diff --git a/lib/class-wp-duotone.php b/lib/class-wp-duotone.php new file mode 100644 index 00000000000000..8f15efbecf0ad3 --- /dev/null +++ b/lib/class-wp-duotone.php @@ -0,0 +1,150 @@ + + * [ + * 'slug' => 'blue-orange', + * 'colors' => [ '#0000ff', '#ffcc00' ], + * ] + * ], + * … + * ] + * + * @since 6.3.0 + * @var array + */ + static $global_styles_presets = array(); + + /** + * An array of block names from global, theme, and custom styles that have duotone presets. We'll use this to quickly + * check if a block being rendered needs to have duotone applied, and which duotone preset to use. + * + * Example: + * [ + * 'core/featured-image' => 'blue-orange', + * … + * ] + * + * @since 6.3.0 + * @var array + */ + static $global_styles_block_names = array(); + + /** + * An array of Duotone SVG and CSS ouput needed for the frontend duotone rendering based on what is + * being ouptput on the page. Organized by a slug of the preset/color group and the information needed + * to generate the SVG and CSS at render. + * + * Example: + * [ + * 'blue-orange' => [ + * 'slug' => 'blue-orange', + * 'colors' => [ '#0000ff', '#ffcc00' ], + * ], + * 'wp-duotone-000000-ffffff-2' => [ + * 'slug' => 'wp-duotone-000000-ffffff-2', + * 'colors' => [ '#000000', '#ffffff' ], + * ], + * ] + * + * @since 6.3.0 + * @var array + */ + static $output = array(); + + /** + * Get all possible duotone presets from global and theme styles and store as slug => [ colors array ] + * We only want to process this one time. On block render we'll access and output only the needed presets for that page. + */ + static function set_global_styles_presets() { + // Get the per block settings from the theme.json. + $tree = gutenberg_get_global_settings(); + $presets_by_origin = _wp_array_get( $tree, array( 'color', 'duotone' ), array() ); + + foreach ( $presets_by_origin as $presets ) { + foreach ( $presets as $preset ) { + self::$global_styles_presets[ _wp_to_kebab_case( $preset['slug'] ) ] = array( + 'slug' => $preset['slug'], + 'colors' => $preset['colors'], + ); + } + } + } + + /** + * Scrape all block names from global styles and store in WP_Duotone::$global_styles_block_names + */ + static function set_global_style_block_names() { + // Get the per block settings from the theme.json. + $tree = WP_Theme_JSON_Resolver::get_merged_data(); + $block_nodes = $tree->get_styles_block_nodes(); + $theme_json = $tree->get_raw_data(); + + foreach ( $block_nodes as $block_node ) { + // This block definition doesn't include any duotone settings. Skip it. + if ( empty( $block_node['duotone'] ) ) { + continue; + } + + // Value looks like this: 'var(--wp--preset--duotone--blue-orange)' or 'var:preset|duotone|default-filter'. + $duotone_attr_path = array_merge( $block_node['path'], array( 'filter', 'duotone' ) ); + $duotone_attr = _wp_array_get( $theme_json, $duotone_attr_path, array() ); + + if ( empty( $duotone_attr ) ) { + continue; + } + // If it has a duotone filter preset, save the block name and the preset slug. + $slug = self::gutenberg_get_slug_from_attr( $duotone_attr ); + + if ( $slug && $slug !== $duotone_attr ) { + self::$global_styles_block_names[ $block_node['name'] ] = $slug; + } + } + } + + /** + * Take the inline CSS duotone variable from a block and return the slug. Handles styles slugs like: + * var:preset|duotone|default-filter + * var(--wp--preset--duotone--blue-orange) + * + * @param string $duotone_attr The duotone attribute from a block. + * @return string The slug of the duotone preset or an empty string if no slug is found. + */ + static function gutenberg_get_slug_from_attr( $duotone_attr ) { + // Uses Branch Reset Groups `(?|…)` to return one capture group. + preg_match( '/(?|var:preset\|duotone\|(\S+)|var\(--wp--preset--duotone--(\S+)\))/', $duotone_attr, $matches ); + + return ! empty( $matches[1] ) ? $matches[1] : ''; + } + + /** + * Check if we have a valid duotone preset. + * + * @param string $duotone_attr The duotone attribute from a block. + * @return bool True if the duotone preset present and valid. + */ + static function is_preset( $duotone_attr ) { + $slug = WP_Duotone::gutenberg_get_slug_from_attr( $duotone_attr ); + + return array_key_exists( $slug, WP_Duotone::$global_styles_presets ); + } +} + +add_action( 'wp_loaded', array( 'WP_Duotone', 'set_global_styles_presets' ), 10 ); +add_action( 'wp_loaded', array( 'WP_Duotone', 'set_global_style_block_names' ), 10 ); diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 673e97d2b84e5c..c13bd36baaf479 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -144,7 +144,7 @@ class WP_Theme_JSON_Gutenberg { 'path' => array( 'color', 'duotone' ), 'prevent_override' => array( 'color', 'defaultDuotone' ), 'use_default_names' => false, - 'value_func' => 'gutenberg_get_duotone_filter_property', + 'value_func' => null, // Don't output CSS Custom Properties for duotone. 'css_vars' => '--wp--preset--duotone--$slug', 'classes' => array(), 'properties' => array( 'filter' ), @@ -3480,4 +3480,39 @@ public function set_spacing_sizes() { _wp_array_set( $this->theme_json, array( 'settings', 'spacing', 'spacingSizes', 'default' ), $spacing_sizes ); } + + /** + * Returns the CSS variable for a preset. + * + * @since 6.3.0 + * + * @param array $path Path to the preset. + * @param string $slug Slug of the preset. + * @return string CSS variable. + */ + public static function get_preset_css_var( $path, $slug ) { + $duotone_preset_metadata = static::get_preset_metadata_from_path( $path ); + return static::replace_slug_in_string( $duotone_preset_metadata['css_vars'], $slug ); + } + + /** + * Returns the metadata for a preset. + * + * @since 6.3.0 + * + * @param array $path Path to the preset. + * @return array Preset metadata. + */ + static function get_preset_metadata_from_path( $path ) { + $preset_metadata = array_filter( + static::PRESETS_METADATA, + function( $preset ) use ( &$path ) { + if ( $preset['path'] === $path ) { + return $preset; + } + } + ); + + return reset( $preset_metadata ); + } } diff --git a/lib/compat/wordpress-6.2/script-loader.php b/lib/compat/wordpress-6.2/script-loader.php index 149a6a18e14507..876d14902fdcb2 100644 --- a/lib/compat/wordpress-6.2/script-loader.php +++ b/lib/compat/wordpress-6.2/script-loader.php @@ -191,3 +191,7 @@ function gutenberg_enqueue_global_styles_custom_css() { } } add_action( 'wp_enqueue_scripts', 'gutenberg_enqueue_global_styles_custom_css' ); + + +remove_action( 'wp_body_open', 'wp_global_styles_render_svg_filters' ); +remove_action( 'in_admin_header', 'wp_global_styles_render_svg_filters' ); diff --git a/lib/load.php b/lib/load.php index a5be015320652f..5d8babd0525683 100644 --- a/lib/load.php +++ b/lib/load.php @@ -131,6 +131,7 @@ function gutenberg_is_experiment_enabled( $name ) { // Plugin specific code. require __DIR__ . '/class-wp-theme-json-gutenberg.php'; require __DIR__ . '/class-wp-theme-json-resolver-gutenberg.php'; +require __DIR__ . '/class-wp-duotone.php'; require __DIR__ . '/blocks.php'; require __DIR__ . '/client-assets.php'; require __DIR__ . '/demo.php'; diff --git a/phpunit/block-supports/duotone-test.php b/phpunit/block-supports/duotone-test.php index 3588950468aba4..306ad977fba5db 100644 --- a/phpunit/block-supports/duotone-test.php +++ b/phpunit/block-supports/duotone-test.php @@ -10,10 +10,10 @@ class WP_Block_Supports_Duotone_Test extends WP_UnitTestCase { public function test_gutenberg_render_duotone_support_preset() { $block = array( 'blockName' => 'core/image', - 'attrs' => array( 'style' => array( 'color' => array( 'duotone' => 'var:preset|duotone|slug' ) ) ), + 'attrs' => array( 'style' => array( 'color' => array( 'duotone' => 'var:preset|duotone|blue-orange' ) ) ), ); $block_content = '
'; - $expected = '
'; + $expected = '
'; $this->assertSame( $expected, gutenberg_render_duotone_support( $block_content, $block ) ); } @@ -37,4 +37,41 @@ public function test_gutenberg_render_duotone_support_custom() { $this->assertMatchesRegularExpression( $expected, gutenberg_render_duotone_support( $block_content, $block ) ); } + public function data_gutenberg_get_slug_from_attr() { + return array( + 'pipe-slug' => array( 'var:preset|duotone|blue-orange', 'blue-orange' ), + 'css-var' => array( 'var(--wp--preset--duotone--blue-orange)', 'blue-orange' ), + 'css-var-weird-chars' => array( 'var(--wp--preset--duotone--.)', '.' ), + 'css-var-missing-end-parenthesis' => array( 'var(--wp--preset--duotone--blue-orange', '' ), + 'invalid' => array( 'not a valid attribute', '' ), + 'css-var-no-value' => array( 'var(--wp--preset--duotone--)', '' ), + 'pipe-slug-no-value' => array( 'var:preset|duotone|', '' ), + 'css-var-spaces' => array( 'var(--wp--preset--duotone-- ', '' ), + 'pipe-slug-spaces' => array( 'var:preset|duotone| ', '' ), + ); + } + + /** + * @dataProvider data_gutenberg_get_slug_from_attr + */ + public function test_gutenberg_get_slug_from_attr( $data_attr, $expected ) { + $this->assertSame( $expected, WP_Duotone::gutenberg_get_slug_from_attr( $data_attr ) ); + } + + public function data_is_preset() { + return array( + 'pipe-slug' => array( 'var:preset|duotone|blue-orange', true ), + 'css-var' => array( 'var(--wp--preset--duotone--blue-orange)', true ), + 'css-var-weird-chars' => array( 'var(--wp--preset--duotone--.)', false ), + 'css-var-missing-end-parenthesis' => array( 'var(--wp--preset--duotone--blue-orange', false ), + 'invalid' => array( 'not a valid attribute', false ), + ); + } + + /** + * @dataProvider data_is_preset + */ + public function test_is_preset( $data_attr, $expected ) { + $this->assertSame( $expected, WP_Duotone::is_preset( $data_attr ) ); + } } From 2ad87aa5a3196a7e4b46cf7a38495d108f31e302 Mon Sep 17 00:00:00 2001 From: Felix Arntz Date: Wed, 15 Mar 2023 07:52:58 -0700 Subject: [PATCH 163/910] Avoid declaring a function inside another function (#49049) * Avoid declaring a function inside another function. * Update variable name. --- lib/experimental/block-editor-settings.php | 48 +++++++++++----------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/lib/experimental/block-editor-settings.php b/lib/experimental/block-editor-settings.php index 7888595eba0f40..db7070dbd0b17b 100644 --- a/lib/experimental/block-editor-settings.php +++ b/lib/experimental/block-editor-settings.php @@ -5,6 +5,30 @@ * @package gutenberg */ +/** + * Finds the first occurrence of a specific block in an array of blocks. + * + * @param string $block_name Name of the block to find. + * @param array $blocks Array of blocks. + * @return array Found block, or empty array if none found. + */ +function gutenberg_find_first_block( $block_name, $blocks ) { + foreach ( $blocks as $block ) { + if ( $block_name === $block['blockName'] ) { + return $block; + } + if ( ! empty( $block['innerBlocks'] ) ) { + $found_block = gutenberg_find_first_block( $block_name, $block['innerBlocks'] ); + + if ( ! empty( $found_block ) ) { + return $found_block; + } + } + } + + return array(); +} + /** * Adds styles and __experimentalFeatures to the block editor settings. * @@ -50,31 +74,9 @@ function gutenberg_get_block_editor_settings_experimental( $settings ) { $current_template = gutenberg_get_block_templates( array( 'slug__in' => array( $template_slug ) ) ); - /** - * Finds Post Content in an array of blocks - * - * @param array $blocks Array of blocks. - * - * @return array Post Content block. - */ - function get_post_content_block( $blocks ) { - foreach ( $blocks as $block ) { - if ( 'core/post-content' === $block['blockName'] ) { - return $block; - } - if ( ! empty( $block['innerBlocks'] ) ) { - $post_content = get_post_content_block( $block['innerBlocks'] ); - - if ( ! empty( $post_content ) ) { - return $post_content; - } - } - } - } - if ( ! empty( $current_template ) ) { $template_blocks = parse_blocks( $current_template[0]->content ); - $post_content_block = get_post_content_block( $template_blocks ); + $post_content_block = gutenberg_find_first_block( 'core/post-content', $template_blocks ); if ( ! empty( $post_content_block['attrs'] ) ) { $settings['postContentAttributes'] = $post_content_block['attrs']; From f64bdefc178b9cb47cae6a55a8a6c2b2dfef4c99 Mon Sep 17 00:00:00 2001 From: flootr Date: Wed, 15 Mar 2023 15:58:27 +0100 Subject: [PATCH 164/910] `Icon`: refactor tests to TypeScript (#49066) --- packages/components/src/icon/test/{index.js => index.tsx} | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) rename packages/components/src/icon/test/{index.js => index.tsx} (96%) diff --git a/packages/components/src/icon/test/index.js b/packages/components/src/icon/test/index.tsx similarity index 96% rename from packages/components/src/icon/test/index.js rename to packages/components/src/icon/test/index.tsx index 9faff9e74116df..fd9c1650fb3b5b 100644 --- a/packages/components/src/icon/test/index.js +++ b/packages/components/src/icon/test/index.tsx @@ -3,11 +3,15 @@ */ import { render, screen } from '@testing-library/react'; +/** + * WordPress dependencies + */ +import { Path, SVG } from '@wordpress/primitives'; + /** * Internal dependencies */ -import Icon from '../'; -import { Path, SVG } from '../../'; +import Icon from '..'; describe( 'Icon', () => { const testId = 'icon'; From 0f38d66765ea2297d8bbd0caf788d134f69b7c1c Mon Sep 17 00:00:00 2001 From: flootr Date: Wed, 15 Mar 2023 15:58:52 +0100 Subject: [PATCH 165/910] `context/getStyledClassName`: refactor away from `lodash.kebabCase` (#48688) --- .../components/src/ui/context/get-styled-class-name-from-key.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/ui/context/get-styled-class-name-from-key.ts b/packages/components/src/ui/context/get-styled-class-name-from-key.ts index 76dba53001d822..c2f5ebec5df6ec 100644 --- a/packages/components/src/ui/context/get-styled-class-name-from-key.ts +++ b/packages/components/src/ui/context/get-styled-class-name-from-key.ts @@ -1,7 +1,7 @@ /** * External dependencies */ -import { kebabCase } from 'lodash'; +import { paramCase as kebabCase } from 'change-case'; import memoize from 'memize'; /** From d9a96d928165209e585e2f23658ccb37ec345926 Mon Sep 17 00:00:00 2001 From: Alex Lende Date: Wed, 15 Mar 2023 10:20:30 -0600 Subject: [PATCH 166/910] Revert "Duotone: Limit SVG filter output to used filters" (#49102) --- lib/block-supports/duotone.php | 190 +++++++-------------- lib/class-wp-duotone.php | 150 ---------------- lib/class-wp-theme-json-gutenberg.php | 37 +--- lib/compat/wordpress-6.2/script-loader.php | 4 - lib/load.php | 1 - phpunit/block-supports/duotone-test.php | 41 +---- 6 files changed, 65 insertions(+), 358 deletions(-) delete mode 100644 lib/class-wp-duotone.php diff --git a/lib/block-supports/duotone.php b/lib/block-supports/duotone.php index afb521f5ea73eb..ef950f80ce9448 100644 --- a/lib/block-supports/duotone.php +++ b/lib/block-supports/duotone.php @@ -436,67 +436,54 @@ function gutenberg_render_duotone_support( $block_content, $block ) { $duotone_support = _wp_array_get( $block_type->supports, array( 'color', '__experimentalDuotone' ), false ); } - // The block should have a duotone attribute or have duotone defined in its theme.json to be processed. - $has_duotone_attribute = isset( $block['attrs']['style']['color']['duotone'] ); - $has_global_styles_duotone = array_key_exists( $block['blockName'], WP_Duotone::$global_styles_block_names ); + $has_duotone_attribute = isset( $block['attrs']['style']['color']['duotone'] ); if ( - empty( $block_content ) || ! $duotone_support || - ( ! $has_duotone_attribute && ! $has_global_styles_duotone ) + ! $has_duotone_attribute ) { return $block_content; } - // Generate the pieces needed for rendering a duotone to the page. - if ( $has_duotone_attribute ) { - - // Possible values for duotone attribute: - // 1. Array of colors - e.g. array('#000000', '#ffffff'). - // 2. Variable for an existing Duotone preset - e.g. 'var:preset|duotone|green-blue' or 'var(--wp--preset--duotone--green-blue)'' - // 3. A CSS string - e.g. 'unset' to remove globally applied duotone. - - $duotone_attr = $block['attrs']['style']['color']['duotone']; - $is_preset = is_string( $duotone_attr ) && WP_Duotone::is_preset( $duotone_attr ); - $is_css = is_string( $duotone_attr ) && ! $is_preset; - $is_custom = is_array( $duotone_attr ); - - if ( $is_preset ) { - - // Extract the slug from the preset variable string. - $slug = WP_Duotone::gutenberg_get_slug_from_attr( $duotone_attr ); - - // Utilize existing preset CSS custom property. - $filter_property = "var(--wp--preset--duotone--$slug)"; - - WP_Duotone::$output[ $slug ] = WP_Duotone::$global_styles_presets[ $slug ]; + // Possible values for duotone attribute: + // 1. Array of colors - e.g. array('#000000', '#ffffff'). + // 2. Variable for an existing Duotone preset - e.g. 'var:preset|duotone|green-blue'. + // 3. A CSS string - e.g. 'unset' to remove globally applied duotone. + $duotone_attr = $block['attrs']['style']['color']['duotone']; - } elseif ( $is_css ) { - // Build a unique slug for the filter based on the CSS value. - $slug = wp_unique_id( sanitize_key( $duotone_attr . '-' ) ); + $is_preset = is_string( $duotone_attr ) && strpos( $duotone_attr, 'var:preset|duotone|' ) === 0; + $is_css = is_string( $duotone_attr ) && strpos( $duotone_attr, 'var:preset|duotone|' ) === false; + $is_custom = is_array( $duotone_attr ); - // Pass through the CSS value. - $filter_property = $duotone_attr; - } elseif ( $is_custom ) { - // Build a unique slug for the filter based on the array of colors. - $slug = wp_unique_id( sanitize_key( implode( '-', $duotone_attr ) . '-' ) ); - - $filter_data = array( - 'slug' => $slug, - 'colors' => $duotone_attr, - ); - // Build a customized CSS filter property for unique slug. - $filter_property = gutenberg_get_duotone_filter_property( $filter_data ); - - WP_Duotone::$output[ $slug ] = $filter_data; - } - } elseif ( $has_global_styles_duotone ) { - $slug = WP_Duotone::$global_styles_block_names[ $block['blockName'] ]; + // Generate the pieces needed for rendering a duotone to the page. + if ( $is_preset ) { + // Extract the slug from the preset variable string. + $slug = str_replace( 'var:preset|duotone|', '', $duotone_attr ); // Utilize existing preset CSS custom property. $filter_property = "var(--wp--preset--duotone--$slug)"; + } elseif ( $is_css ) { + // Build a unique slug for the filter based on the CSS value. + $slug = wp_unique_id( sanitize_key( $duotone_attr . '-' ) ); + + // Pass through the CSS value. + $filter_property = $duotone_attr; + } elseif ( $is_custom ) { + // Build a unique slug for the filter based on the array of colors. + $slug = wp_unique_id( sanitize_key( implode( '-', $duotone_attr ) . '-' ) ); + + // This has the same shape as a preset, so it can be used in place of a + // preset when getting the filter property and SVG filter. + $filter_data = array( + 'slug' => $slug, + 'colors' => $duotone_attr, + ); - WP_Duotone::$output[ $slug ] = WP_Duotone::$global_styles_presets[ $slug ]; + // Build a customized CSS filter property for unique slug. + $filter_property = gutenberg_get_duotone_filter_property( $filter_data ); + + // SVG will be output on the page later. + $filter_svg = gutenberg_get_duotone_filter_svg( $filter_data ); } // - Applied as a class attribute to the block wrapper. @@ -506,11 +493,6 @@ function gutenberg_render_duotone_support( $block_content, $block ) { // Build the CSS selectors to which the filter will be applied. $selector = WP_Theme_JSON_Gutenberg::scope_selector( '.' . $filter_id, $duotone_support ); - // We only want to add the selector if we have it in the output already, essentially skipping 'unset'. - if ( array_key_exists( $slug, WP_Duotone::$output ) ) { - WP_Duotone::$output[ $slug ]['selector'] = $selector; - } - // Calling gutenberg_style_engine_get_stylesheet_from_css_rules ensures that // the styles are rendered in an inline for block supports because we're // using the `context` option to instruct it so. @@ -532,6 +514,33 @@ function gutenberg_render_duotone_support( $block_content, $block ) { ) ); + // If we needed to generate an SVG, output it on the page. + if ( isset( $filter_svg ) ) { + add_action( + 'wp_footer', + static function () use ( $filter_svg, $selector ) { + echo $filter_svg; + + /* + * Safari renders elements incorrectly on first paint when the + * SVG filter comes after the content that it is filtering, so + * we force a repaint with a WebKit hack which solves the issue. + */ + global $is_safari; + if ( $is_safari ) { + /* + * Simply accessing el.offsetHeight flushes layout and style + * changes in WebKit without having to wait for setTimeout. + */ + printf( + '', + wp_json_encode( $selector ) + ); + } + } + ); + } + // Like the layout hook, this assumes the hook only applies to blocks with a single wrapper. return preg_replace( '/' . preg_quote( 'class="', '/' ) . '/', @@ -541,81 +550,6 @@ function gutenberg_render_duotone_support( $block_content, $block ) { ); } - -add_action( - 'wp_footer', - static function () { - - foreach ( WP_Duotone::$output as $filter_data ) { - - $filter_property = gutenberg_get_duotone_filter_property( $filter_data ); - // SVG will be output on the page later. - $filter_svg = gutenberg_get_duotone_filter_svg( $filter_data ); - - echo $filter_svg; - - // This is for classic themes - in block themes, the CSS is added in the head via the value_func. - if ( ! wp_is_block_theme() ) { - $duotone_preset_css_var = WP_Theme_JSON_Gutenberg::get_preset_css_var( array( 'color', 'duotone' ), $filter_data['slug'] ); - wp_add_inline_style( 'core-block-supports', 'body{' . $duotone_preset_css_var . ' :' . $filter_property . ';}' ); - } - - global $is_safari; - if ( $is_safari ) { - duotone_safari_rerender_hack( $filter_data['selector'] ); - } - } - } -); - -/** - * Appends the used duotone fitler CSS Vars to the inline global styles CSS - */ -add_action( - 'wp_enqueue_scripts', - static function() { - - if ( empty( WP_Duotone::$output ) ) { - return; - } - - $duotone_css_vars = ''; - - foreach ( WP_Duotone::$output as $filter_data ) { - if ( ! array_key_exists( $filter_data['slug'], WP_Duotone::$global_styles_presets ) ) { - continue; - } - - $filter_property = gutenberg_get_duotone_filter_property( $filter_data ); - - $duotone_preset_css_var = WP_Theme_JSON_Gutenberg::get_preset_css_var( array( 'color', 'duotone' ), $filter_data['slug'] ); - $duotone_css_vars .= $duotone_preset_css_var . ': ' . $filter_property . ';'; - } - - if ( ! empty( $duotone_css_vars ) ) { - wp_add_inline_style( 'global-styles', 'body{' . $duotone_css_vars . '}' ); - } - }, - 11 -); - -/** - * Safari renders elements incorrectly on first paint when the SVG filter comes after the content that it is filtering, - * so we force a repaint with a WebKit hack which solves the issue. - * - * @param string $selector The selector to apply the hack for. - */ -function duotone_safari_rerender_hack( $selector ) { - /* - * Simply accessing el.offsetHeight flushes layout and style - * changes in WebKit without having to wait for setTimeout. - */ - printf( - '', - wp_json_encode( $selector ) - ); -} - // Register the block support. WP_Block_Supports::get_instance()->register( 'duotone', diff --git a/lib/class-wp-duotone.php b/lib/class-wp-duotone.php deleted file mode 100644 index 8f15efbecf0ad3..00000000000000 --- a/lib/class-wp-duotone.php +++ /dev/null @@ -1,150 +0,0 @@ - - * [ - * 'slug' => 'blue-orange', - * 'colors' => [ '#0000ff', '#ffcc00' ], - * ] - * ], - * … - * ] - * - * @since 6.3.0 - * @var array - */ - static $global_styles_presets = array(); - - /** - * An array of block names from global, theme, and custom styles that have duotone presets. We'll use this to quickly - * check if a block being rendered needs to have duotone applied, and which duotone preset to use. - * - * Example: - * [ - * 'core/featured-image' => 'blue-orange', - * … - * ] - * - * @since 6.3.0 - * @var array - */ - static $global_styles_block_names = array(); - - /** - * An array of Duotone SVG and CSS ouput needed for the frontend duotone rendering based on what is - * being ouptput on the page. Organized by a slug of the preset/color group and the information needed - * to generate the SVG and CSS at render. - * - * Example: - * [ - * 'blue-orange' => [ - * 'slug' => 'blue-orange', - * 'colors' => [ '#0000ff', '#ffcc00' ], - * ], - * 'wp-duotone-000000-ffffff-2' => [ - * 'slug' => 'wp-duotone-000000-ffffff-2', - * 'colors' => [ '#000000', '#ffffff' ], - * ], - * ] - * - * @since 6.3.0 - * @var array - */ - static $output = array(); - - /** - * Get all possible duotone presets from global and theme styles and store as slug => [ colors array ] - * We only want to process this one time. On block render we'll access and output only the needed presets for that page. - */ - static function set_global_styles_presets() { - // Get the per block settings from the theme.json. - $tree = gutenberg_get_global_settings(); - $presets_by_origin = _wp_array_get( $tree, array( 'color', 'duotone' ), array() ); - - foreach ( $presets_by_origin as $presets ) { - foreach ( $presets as $preset ) { - self::$global_styles_presets[ _wp_to_kebab_case( $preset['slug'] ) ] = array( - 'slug' => $preset['slug'], - 'colors' => $preset['colors'], - ); - } - } - } - - /** - * Scrape all block names from global styles and store in WP_Duotone::$global_styles_block_names - */ - static function set_global_style_block_names() { - // Get the per block settings from the theme.json. - $tree = WP_Theme_JSON_Resolver::get_merged_data(); - $block_nodes = $tree->get_styles_block_nodes(); - $theme_json = $tree->get_raw_data(); - - foreach ( $block_nodes as $block_node ) { - // This block definition doesn't include any duotone settings. Skip it. - if ( empty( $block_node['duotone'] ) ) { - continue; - } - - // Value looks like this: 'var(--wp--preset--duotone--blue-orange)' or 'var:preset|duotone|default-filter'. - $duotone_attr_path = array_merge( $block_node['path'], array( 'filter', 'duotone' ) ); - $duotone_attr = _wp_array_get( $theme_json, $duotone_attr_path, array() ); - - if ( empty( $duotone_attr ) ) { - continue; - } - // If it has a duotone filter preset, save the block name and the preset slug. - $slug = self::gutenberg_get_slug_from_attr( $duotone_attr ); - - if ( $slug && $slug !== $duotone_attr ) { - self::$global_styles_block_names[ $block_node['name'] ] = $slug; - } - } - } - - /** - * Take the inline CSS duotone variable from a block and return the slug. Handles styles slugs like: - * var:preset|duotone|default-filter - * var(--wp--preset--duotone--blue-orange) - * - * @param string $duotone_attr The duotone attribute from a block. - * @return string The slug of the duotone preset or an empty string if no slug is found. - */ - static function gutenberg_get_slug_from_attr( $duotone_attr ) { - // Uses Branch Reset Groups `(?|…)` to return one capture group. - preg_match( '/(?|var:preset\|duotone\|(\S+)|var\(--wp--preset--duotone--(\S+)\))/', $duotone_attr, $matches ); - - return ! empty( $matches[1] ) ? $matches[1] : ''; - } - - /** - * Check if we have a valid duotone preset. - * - * @param string $duotone_attr The duotone attribute from a block. - * @return bool True if the duotone preset present and valid. - */ - static function is_preset( $duotone_attr ) { - $slug = WP_Duotone::gutenberg_get_slug_from_attr( $duotone_attr ); - - return array_key_exists( $slug, WP_Duotone::$global_styles_presets ); - } -} - -add_action( 'wp_loaded', array( 'WP_Duotone', 'set_global_styles_presets' ), 10 ); -add_action( 'wp_loaded', array( 'WP_Duotone', 'set_global_style_block_names' ), 10 ); diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index c13bd36baaf479..673e97d2b84e5c 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -144,7 +144,7 @@ class WP_Theme_JSON_Gutenberg { 'path' => array( 'color', 'duotone' ), 'prevent_override' => array( 'color', 'defaultDuotone' ), 'use_default_names' => false, - 'value_func' => null, // Don't output CSS Custom Properties for duotone. + 'value_func' => 'gutenberg_get_duotone_filter_property', 'css_vars' => '--wp--preset--duotone--$slug', 'classes' => array(), 'properties' => array( 'filter' ), @@ -3480,39 +3480,4 @@ public function set_spacing_sizes() { _wp_array_set( $this->theme_json, array( 'settings', 'spacing', 'spacingSizes', 'default' ), $spacing_sizes ); } - - /** - * Returns the CSS variable for a preset. - * - * @since 6.3.0 - * - * @param array $path Path to the preset. - * @param string $slug Slug of the preset. - * @return string CSS variable. - */ - public static function get_preset_css_var( $path, $slug ) { - $duotone_preset_metadata = static::get_preset_metadata_from_path( $path ); - return static::replace_slug_in_string( $duotone_preset_metadata['css_vars'], $slug ); - } - - /** - * Returns the metadata for a preset. - * - * @since 6.3.0 - * - * @param array $path Path to the preset. - * @return array Preset metadata. - */ - static function get_preset_metadata_from_path( $path ) { - $preset_metadata = array_filter( - static::PRESETS_METADATA, - function( $preset ) use ( &$path ) { - if ( $preset['path'] === $path ) { - return $preset; - } - } - ); - - return reset( $preset_metadata ); - } } diff --git a/lib/compat/wordpress-6.2/script-loader.php b/lib/compat/wordpress-6.2/script-loader.php index 876d14902fdcb2..149a6a18e14507 100644 --- a/lib/compat/wordpress-6.2/script-loader.php +++ b/lib/compat/wordpress-6.2/script-loader.php @@ -191,7 +191,3 @@ function gutenberg_enqueue_global_styles_custom_css() { } } add_action( 'wp_enqueue_scripts', 'gutenberg_enqueue_global_styles_custom_css' ); - - -remove_action( 'wp_body_open', 'wp_global_styles_render_svg_filters' ); -remove_action( 'in_admin_header', 'wp_global_styles_render_svg_filters' ); diff --git a/lib/load.php b/lib/load.php index 5d8babd0525683..a5be015320652f 100644 --- a/lib/load.php +++ b/lib/load.php @@ -131,7 +131,6 @@ function gutenberg_is_experiment_enabled( $name ) { // Plugin specific code. require __DIR__ . '/class-wp-theme-json-gutenberg.php'; require __DIR__ . '/class-wp-theme-json-resolver-gutenberg.php'; -require __DIR__ . '/class-wp-duotone.php'; require __DIR__ . '/blocks.php'; require __DIR__ . '/client-assets.php'; require __DIR__ . '/demo.php'; diff --git a/phpunit/block-supports/duotone-test.php b/phpunit/block-supports/duotone-test.php index 306ad977fba5db..3588950468aba4 100644 --- a/phpunit/block-supports/duotone-test.php +++ b/phpunit/block-supports/duotone-test.php @@ -10,10 +10,10 @@ class WP_Block_Supports_Duotone_Test extends WP_UnitTestCase { public function test_gutenberg_render_duotone_support_preset() { $block = array( 'blockName' => 'core/image', - 'attrs' => array( 'style' => array( 'color' => array( 'duotone' => 'var:preset|duotone|blue-orange' ) ) ), + 'attrs' => array( 'style' => array( 'color' => array( 'duotone' => 'var:preset|duotone|slug' ) ) ), ); $block_content = '
'; - $expected = '
'; + $expected = '
'; $this->assertSame( $expected, gutenberg_render_duotone_support( $block_content, $block ) ); } @@ -37,41 +37,4 @@ public function test_gutenberg_render_duotone_support_custom() { $this->assertMatchesRegularExpression( $expected, gutenberg_render_duotone_support( $block_content, $block ) ); } - public function data_gutenberg_get_slug_from_attr() { - return array( - 'pipe-slug' => array( 'var:preset|duotone|blue-orange', 'blue-orange' ), - 'css-var' => array( 'var(--wp--preset--duotone--blue-orange)', 'blue-orange' ), - 'css-var-weird-chars' => array( 'var(--wp--preset--duotone--.)', '.' ), - 'css-var-missing-end-parenthesis' => array( 'var(--wp--preset--duotone--blue-orange', '' ), - 'invalid' => array( 'not a valid attribute', '' ), - 'css-var-no-value' => array( 'var(--wp--preset--duotone--)', '' ), - 'pipe-slug-no-value' => array( 'var:preset|duotone|', '' ), - 'css-var-spaces' => array( 'var(--wp--preset--duotone-- ', '' ), - 'pipe-slug-spaces' => array( 'var:preset|duotone| ', '' ), - ); - } - - /** - * @dataProvider data_gutenberg_get_slug_from_attr - */ - public function test_gutenberg_get_slug_from_attr( $data_attr, $expected ) { - $this->assertSame( $expected, WP_Duotone::gutenberg_get_slug_from_attr( $data_attr ) ); - } - - public function data_is_preset() { - return array( - 'pipe-slug' => array( 'var:preset|duotone|blue-orange', true ), - 'css-var' => array( 'var(--wp--preset--duotone--blue-orange)', true ), - 'css-var-weird-chars' => array( 'var(--wp--preset--duotone--.)', false ), - 'css-var-missing-end-parenthesis' => array( 'var(--wp--preset--duotone--blue-orange', false ), - 'invalid' => array( 'not a valid attribute', false ), - ); - } - - /** - * @dataProvider data_is_preset - */ - public function test_is_preset( $data_attr, $expected ) { - $this->assertSame( $expected, WP_Duotone::is_preset( $data_attr ) ); - } } From f33f123bb5202c5e45f9b835952ee968fa632a70 Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Wed, 15 Mar 2023 16:44:04 +0000 Subject: [PATCH 167/910] Bump plugin version to 15.4.0-rc.1 --- gutenberg.php | 2 +- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gutenberg.php b/gutenberg.php index 22ad77d55bf2b4..ce3d4b810f83a5 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.0 * Requires PHP: 5.6 - * Version: 15.3.1 + * Version: 15.4.0-rc.1 * Author: Gutenberg Team * Text Domain: gutenberg * diff --git a/package-lock.json b/package-lock.json index 3d82cbdb65efe7..0ad5466ceb1068 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "15.3.1", + "version": "15.4.0-rc.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index d27815e98a2e8c..16b1e5478354cd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "15.3.1", + "version": "15.4.0-rc.1", "private": true, "description": "A new WordPress editor experience.", "author": "The WordPress Contributors", From 99aa689b4b27ac04883e667f129f6fa9b22e7f03 Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Wed, 15 Mar 2023 17:21:47 +0000 Subject: [PATCH 168/910] Update changelog files --- packages/a11y/CHANGELOG.md | 2 ++ packages/a11y/package.json | 2 +- packages/annotations/CHANGELOG.md | 2 ++ packages/annotations/package.json | 2 +- packages/api-fetch/CHANGELOG.md | 2 ++ packages/api-fetch/package.json | 2 +- packages/autop/CHANGELOG.md | 2 ++ packages/autop/package.json | 2 +- packages/babel-plugin-import-jsx-pragma/CHANGELOG.md | 2 ++ packages/babel-plugin-import-jsx-pragma/package.json | 2 +- packages/babel-plugin-makepot/CHANGELOG.md | 2 ++ packages/babel-plugin-makepot/package.json | 2 +- packages/babel-preset-default/CHANGELOG.md | 2 ++ packages/babel-preset-default/package.json | 2 +- packages/base-styles/CHANGELOG.md | 2 ++ packages/base-styles/package.json | 2 +- packages/blob/CHANGELOG.md | 2 ++ packages/blob/package.json | 2 +- packages/block-directory/CHANGELOG.md | 2 ++ packages/block-directory/package.json | 2 +- packages/block-editor/CHANGELOG.md | 2 ++ packages/block-editor/package.json | 2 +- packages/block-library/CHANGELOG.md | 2 ++ packages/block-library/package.json | 2 +- packages/block-serialization-default-parser/CHANGELOG.md | 2 ++ packages/block-serialization-default-parser/package.json | 2 +- packages/block-serialization-spec-parser/CHANGELOG.md | 2 ++ packages/block-serialization-spec-parser/package.json | 2 +- packages/blocks/CHANGELOG.md | 2 ++ packages/blocks/package.json | 2 +- packages/browserslist-config/CHANGELOG.md | 2 ++ packages/browserslist-config/package.json | 2 +- packages/components/CHANGELOG.md | 2 ++ packages/components/package.json | 2 +- packages/compose/CHANGELOG.md | 2 ++ packages/compose/package.json | 2 +- packages/core-data/CHANGELOG.md | 2 ++ packages/core-data/package.json | 2 +- packages/create-block-tutorial-template/CHANGELOG.md | 2 ++ packages/create-block-tutorial-template/package.json | 2 +- packages/create-block/CHANGELOG.md | 2 ++ packages/create-block/package.json | 2 +- packages/customize-widgets/CHANGELOG.md | 2 ++ packages/customize-widgets/package.json | 2 +- packages/data-controls/CHANGELOG.md | 2 ++ packages/data-controls/package.json | 2 +- packages/data/CHANGELOG.md | 2 ++ packages/data/package.json | 2 +- packages/date/CHANGELOG.md | 2 ++ packages/date/package.json | 2 +- packages/dependency-extraction-webpack-plugin/CHANGELOG.md | 2 ++ packages/dependency-extraction-webpack-plugin/package.json | 2 +- packages/deprecated/CHANGELOG.md | 2 ++ packages/deprecated/package.json | 2 +- packages/docgen/CHANGELOG.md | 2 ++ packages/docgen/package.json | 2 +- packages/dom-ready/CHANGELOG.md | 2 ++ packages/dom-ready/package.json | 2 +- packages/dom/CHANGELOG.md | 2 ++ packages/dom/package.json | 2 +- packages/e2e-test-utils/CHANGELOG.md | 2 ++ packages/e2e-test-utils/package.json | 2 +- packages/e2e-tests/CHANGELOG.md | 2 ++ packages/e2e-tests/package.json | 2 +- packages/edit-post/CHANGELOG.md | 2 ++ packages/edit-post/package.json | 2 +- packages/edit-site/CHANGELOG.md | 2 ++ packages/edit-site/package.json | 2 +- packages/edit-widgets/CHANGELOG.md | 2 ++ packages/edit-widgets/package.json | 2 +- packages/editor/CHANGELOG.md | 2 ++ packages/editor/package.json | 2 +- packages/element/CHANGELOG.md | 2 ++ packages/element/package.json | 2 +- packages/env/CHANGELOG.md | 2 ++ packages/env/package.json | 2 +- packages/escape-html/CHANGELOG.md | 2 ++ packages/escape-html/package.json | 2 +- packages/eslint-plugin/CHANGELOG.md | 2 ++ packages/eslint-plugin/package.json | 2 +- packages/format-library/CHANGELOG.md | 2 ++ packages/format-library/package.json | 2 +- packages/hooks/CHANGELOG.md | 2 ++ packages/hooks/package.json | 2 +- packages/html-entities/CHANGELOG.md | 2 ++ packages/html-entities/package.json | 2 +- packages/i18n/CHANGELOG.md | 2 ++ packages/i18n/package.json | 2 +- packages/icons/CHANGELOG.md | 2 ++ packages/icons/package.json | 2 +- packages/interface/CHANGELOG.md | 2 ++ packages/interface/package.json | 2 +- packages/is-shallow-equal/CHANGELOG.md | 2 ++ packages/is-shallow-equal/package.json | 2 +- packages/jest-console/CHANGELOG.md | 2 ++ packages/jest-console/package.json | 2 +- packages/jest-preset-default/CHANGELOG.md | 2 ++ packages/jest-preset-default/package.json | 2 +- packages/jest-puppeteer-axe/CHANGELOG.md | 2 ++ packages/jest-puppeteer-axe/package.json | 2 +- packages/keyboard-shortcuts/CHANGELOG.md | 2 ++ packages/keyboard-shortcuts/package.json | 2 +- packages/keycodes/CHANGELOG.md | 2 ++ packages/keycodes/package.json | 2 +- packages/lazy-import/CHANGELOG.md | 2 ++ packages/lazy-import/package.json | 2 +- packages/list-reusable-blocks/CHANGELOG.md | 2 ++ packages/list-reusable-blocks/package.json | 2 +- packages/media-utils/CHANGELOG.md | 2 ++ packages/media-utils/package.json | 2 +- packages/notices/CHANGELOG.md | 2 ++ packages/notices/package.json | 2 +- packages/npm-package-json-lint-config/CHANGELOG.md | 2 ++ packages/npm-package-json-lint-config/package.json | 2 +- packages/plugins/CHANGELOG.md | 2 ++ packages/plugins/package.json | 2 +- packages/postcss-plugins-preset/CHANGELOG.md | 2 ++ packages/postcss-plugins-preset/package.json | 2 +- packages/postcss-themes/CHANGELOG.md | 2 ++ packages/postcss-themes/package.json | 2 +- packages/preferences-persistence/CHANGELOG.md | 2 ++ packages/preferences-persistence/package.json | 2 +- packages/preferences/CHANGELOG.md | 2 ++ packages/preferences/package.json | 2 +- packages/prettier-config/CHANGELOG.md | 2 ++ packages/prettier-config/package.json | 2 +- packages/primitives/CHANGELOG.md | 2 ++ packages/primitives/package.json | 2 +- packages/priority-queue/CHANGELOG.md | 2 ++ packages/priority-queue/package.json | 2 +- packages/private-apis/CHANGELOG.md | 2 ++ packages/private-apis/package.json | 2 +- packages/project-management-automation/CHANGELOG.md | 2 ++ packages/project-management-automation/package.json | 2 +- packages/react-i18n/CHANGELOG.md | 2 ++ packages/react-i18n/package.json | 2 +- packages/readable-js-assets-webpack-plugin/CHANGELOG.md | 2 ++ packages/readable-js-assets-webpack-plugin/package.json | 2 +- packages/redux-routine/CHANGELOG.md | 2 ++ packages/redux-routine/package.json | 2 +- packages/reusable-blocks/CHANGELOG.md | 2 ++ packages/reusable-blocks/package.json | 2 +- packages/rich-text/CHANGELOG.md | 2 ++ packages/rich-text/package.json | 2 +- packages/scripts/CHANGELOG.md | 2 ++ packages/scripts/package.json | 2 +- packages/server-side-render/CHANGELOG.md | 2 ++ packages/server-side-render/package.json | 2 +- packages/shortcode/CHANGELOG.md | 2 ++ packages/shortcode/package.json | 2 +- packages/style-engine/CHANGELOG.md | 2 ++ packages/style-engine/package.json | 2 +- packages/stylelint-config/CHANGELOG.md | 2 ++ packages/stylelint-config/package.json | 2 +- packages/token-list/CHANGELOG.md | 2 ++ packages/token-list/package.json | 2 +- packages/url/CHANGELOG.md | 2 ++ packages/url/package.json | 2 +- packages/viewport/CHANGELOG.md | 2 ++ packages/viewport/package.json | 2 +- packages/warning/CHANGELOG.md | 2 ++ packages/warning/package.json | 2 +- packages/widgets/CHANGELOG.md | 2 ++ packages/widgets/package.json | 2 +- packages/wordcount/CHANGELOG.md | 2 ++ packages/wordcount/package.json | 2 +- 166 files changed, 249 insertions(+), 83 deletions(-) diff --git a/packages/a11y/CHANGELOG.md b/packages/a11y/CHANGELOG.md index 66d81ca508afea..d02bd555a5a7e6 100644 --- a/packages/a11y/CHANGELOG.md +++ b/packages/a11y/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.29.0 (2023-03-15) + ## 3.28.0 (2023-03-01) ## 3.27.0 (2023-02-15) diff --git a/packages/a11y/package.json b/packages/a11y/package.json index 02896fef16545e..de040fb5ccf90a 100644 --- a/packages/a11y/package.json +++ b/packages/a11y/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/a11y", - "version": "3.28.0", + "version": "3.29.0-prerelease", "description": "Accessibility (a11y) utilities for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/annotations/CHANGELOG.md b/packages/annotations/CHANGELOG.md index 9deba25a935d80..62f20010e2cf44 100644 --- a/packages/annotations/CHANGELOG.md +++ b/packages/annotations/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.29.0 (2023-03-15) + ## 2.28.0 (2023-03-01) ## 2.27.0 (2023-02-15) diff --git a/packages/annotations/package.json b/packages/annotations/package.json index 0c38f073ff07ae..93c1a5289eb629 100644 --- a/packages/annotations/package.json +++ b/packages/annotations/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/annotations", - "version": "2.28.0", + "version": "2.29.0-prerelease", "description": "Annotate content in the Gutenberg editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/api-fetch/CHANGELOG.md b/packages/api-fetch/CHANGELOG.md index 5d443e417653ea..425c31d68106ab 100644 --- a/packages/api-fetch/CHANGELOG.md +++ b/packages/api-fetch/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 6.26.0 (2023-03-15) + ## 6.25.0 (2023-03-01) ## 6.24.0 (2023-02-15) diff --git a/packages/api-fetch/package.json b/packages/api-fetch/package.json index e8fb48ed00ad71..0859876b32e890 100644 --- a/packages/api-fetch/package.json +++ b/packages/api-fetch/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/api-fetch", - "version": "6.25.0", + "version": "6.26.0-prerelease", "description": "Utility to make WordPress REST API requests.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/autop/CHANGELOG.md b/packages/autop/CHANGELOG.md index 701333b280daac..52f06d284edb29 100644 --- a/packages/autop/CHANGELOG.md +++ b/packages/autop/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.29.0 (2023-03-15) + ## 3.28.0 (2023-03-01) ## 3.27.0 (2023-02-15) diff --git a/packages/autop/package.json b/packages/autop/package.json index fa1dd1cdcf9bfe..db102336b53000 100644 --- a/packages/autop/package.json +++ b/packages/autop/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/autop", - "version": "3.28.0", + "version": "3.29.0-prerelease", "description": "WordPress's automatic paragraph functions `autop` and `removep`.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/babel-plugin-import-jsx-pragma/CHANGELOG.md b/packages/babel-plugin-import-jsx-pragma/CHANGELOG.md index 1f1044072f49f8..c22949112de768 100644 --- a/packages/babel-plugin-import-jsx-pragma/CHANGELOG.md +++ b/packages/babel-plugin-import-jsx-pragma/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.12.0 (2023-03-15) + ## 4.11.0 (2023-03-01) ## 4.10.0 (2023-02-15) diff --git a/packages/babel-plugin-import-jsx-pragma/package.json b/packages/babel-plugin-import-jsx-pragma/package.json index 297b6fa64a5865..735c0eaecea9c8 100644 --- a/packages/babel-plugin-import-jsx-pragma/package.json +++ b/packages/babel-plugin-import-jsx-pragma/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/babel-plugin-import-jsx-pragma", - "version": "4.11.0", + "version": "4.12.0-prerelease", "description": "Babel transform plugin for automatically injecting an import to be used as the pragma for the React JSX Transform plugin.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/babel-plugin-makepot/CHANGELOG.md b/packages/babel-plugin-makepot/CHANGELOG.md index e30c919aea400c..3f82f6f9bdc731 100644 --- a/packages/babel-plugin-makepot/CHANGELOG.md +++ b/packages/babel-plugin-makepot/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.13.0 (2023-03-15) + ## 5.12.0 (2023-03-01) ### Internal diff --git a/packages/babel-plugin-makepot/package.json b/packages/babel-plugin-makepot/package.json index d10c3197b8966a..654c10c078d2bf 100644 --- a/packages/babel-plugin-makepot/package.json +++ b/packages/babel-plugin-makepot/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/babel-plugin-makepot", - "version": "5.12.0", + "version": "5.13.0-prerelease", "description": "WordPress Babel internationalization (i18n) plugin.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/babel-preset-default/CHANGELOG.md b/packages/babel-preset-default/CHANGELOG.md index c8bbe1c7c889c7..61974d2d8b47f6 100644 --- a/packages/babel-preset-default/CHANGELOG.md +++ b/packages/babel-preset-default/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 7.13.0 (2023-03-15) + ## 7.12.0 (2023-03-01) ## 7.11.0 (2023-02-15) diff --git a/packages/babel-preset-default/package.json b/packages/babel-preset-default/package.json index 6ea730363f9bca..ca3eeeb0211b4c 100644 --- a/packages/babel-preset-default/package.json +++ b/packages/babel-preset-default/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/babel-preset-default", - "version": "7.12.0", + "version": "7.13.0-prerelease", "description": "Default Babel preset for WordPress development.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/base-styles/CHANGELOG.md b/packages/base-styles/CHANGELOG.md index ec9a2c80ac1fcf..482467ff4223b8 100644 --- a/packages/base-styles/CHANGELOG.md +++ b/packages/base-styles/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.20.0 (2023-03-15) + ## 4.19.0 (2023-03-01) ## 4.18.0 (2023-02-15) diff --git a/packages/base-styles/package.json b/packages/base-styles/package.json index fe3384d39fee09..d7e6d67c8f01d2 100644 --- a/packages/base-styles/package.json +++ b/packages/base-styles/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/base-styles", - "version": "4.19.0", + "version": "4.20.0-prerelease", "description": "Base SCSS utilities and variables for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/blob/CHANGELOG.md b/packages/blob/CHANGELOG.md index 2f5dec45333d4d..0f57aa69c51375 100644 --- a/packages/blob/CHANGELOG.md +++ b/packages/blob/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.29.0 (2023-03-15) + ## 3.28.0 (2023-03-01) ## 3.27.0 (2023-02-15) diff --git a/packages/blob/package.json b/packages/blob/package.json index 7b0ff41d294deb..b624c172ab6d66 100644 --- a/packages/blob/package.json +++ b/packages/blob/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/blob", - "version": "3.28.0", + "version": "3.29.0-prerelease", "description": "Blob utilities for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-directory/CHANGELOG.md b/packages/block-directory/CHANGELOG.md index fd214b64014c02..b250f1728c208e 100644 --- a/packages/block-directory/CHANGELOG.md +++ b/packages/block-directory/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.6.0 (2023-03-15) + ## 4.5.0 (2023-03-01) ## 4.4.0 (2023-02-15) diff --git a/packages/block-directory/package.json b/packages/block-directory/package.json index 2c6ada5f02ffdc..291565cf6aaadc 100644 --- a/packages/block-directory/package.json +++ b/packages/block-directory/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-directory", - "version": "4.5.0", + "version": "4.6.0-prerelease", "description": "Extend editor with block directory features to search, download and install blocks.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-editor/CHANGELOG.md b/packages/block-editor/CHANGELOG.md index 1ed77a8b3804bc..e33f743838286f 100644 --- a/packages/block-editor/CHANGELOG.md +++ b/packages/block-editor/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 11.6.0 (2023-03-15) + ## 11.5.0 (2023-03-01) ### Bug Fix diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json index 9874171ffc95cf..ebb778bc45cf99 100644 --- a/packages/block-editor/package.json +++ b/packages/block-editor/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-editor", - "version": "11.5.0", + "version": "11.6.0-prerelease", "description": "Generic block editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-library/CHANGELOG.md b/packages/block-library/CHANGELOG.md index d9b31f00171a9c..a65fe6601c49f1 100644 --- a/packages/block-library/CHANGELOG.md +++ b/packages/block-library/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 8.6.0 (2023-03-15) + ## 8.5.0 (2023-03-01) ## 8.4.0 (2023-02-15) diff --git a/packages/block-library/package.json b/packages/block-library/package.json index fdacfcf629fa4f..66ff3bb67f43db 100644 --- a/packages/block-library/package.json +++ b/packages/block-library/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-library", - "version": "8.5.0", + "version": "8.6.0-prerelease", "description": "Block library for the WordPress editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-serialization-default-parser/CHANGELOG.md b/packages/block-serialization-default-parser/CHANGELOG.md index 38e52550d81743..fc2b74a8c2ee0b 100644 --- a/packages/block-serialization-default-parser/CHANGELOG.md +++ b/packages/block-serialization-default-parser/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.29.0 (2023-03-15) + ## 4.28.0 (2023-03-01) ## 4.27.0 (2023-02-15) diff --git a/packages/block-serialization-default-parser/package.json b/packages/block-serialization-default-parser/package.json index d7d0a7bde376d4..28fbdd1e1b979b 100644 --- a/packages/block-serialization-default-parser/package.json +++ b/packages/block-serialization-default-parser/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-serialization-default-parser", - "version": "4.28.0", + "version": "4.29.0-prerelease", "description": "Block serialization specification parser for WordPress posts.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-serialization-spec-parser/CHANGELOG.md b/packages/block-serialization-spec-parser/CHANGELOG.md index 20f95922952257..d39fdfa3233444 100644 --- a/packages/block-serialization-spec-parser/CHANGELOG.md +++ b/packages/block-serialization-spec-parser/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.29.0 (2023-03-15) + ## 4.28.0 (2023-03-01) ## 4.27.0 (2023-02-15) diff --git a/packages/block-serialization-spec-parser/package.json b/packages/block-serialization-spec-parser/package.json index 86004639166842..98119306ed3edf 100644 --- a/packages/block-serialization-spec-parser/package.json +++ b/packages/block-serialization-spec-parser/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-serialization-spec-parser", - "version": "4.28.0", + "version": "4.29.0-prerelease", "description": "Block serialization specification parser for WordPress posts.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/blocks/CHANGELOG.md b/packages/blocks/CHANGELOG.md index 2989f8694ca137..6a0c9f0b3d18a2 100644 --- a/packages/blocks/CHANGELOG.md +++ b/packages/blocks/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 12.6.0 (2023-03-15) + ## 12.5.0 (2023-03-01) ## 12.4.0 (2023-02-15) diff --git a/packages/blocks/package.json b/packages/blocks/package.json index de9c211bed1678..21f6cf577fefaa 100644 --- a/packages/blocks/package.json +++ b/packages/blocks/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/blocks", - "version": "12.5.0", + "version": "12.6.0-prerelease", "description": "Block API for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/browserslist-config/CHANGELOG.md b/packages/browserslist-config/CHANGELOG.md index 6bda778e8490de..d12f486b7f69f6 100644 --- a/packages/browserslist-config/CHANGELOG.md +++ b/packages/browserslist-config/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.12.0 (2023-03-15) + ## 5.11.0 (2023-03-01) ## 5.10.0 (2023-02-15) diff --git a/packages/browserslist-config/package.json b/packages/browserslist-config/package.json index 4b3e57823c89ed..7b015d2d11033c 100644 --- a/packages/browserslist-config/package.json +++ b/packages/browserslist-config/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/browserslist-config", - "version": "5.11.0", + "version": "5.12.0-prerelease", "description": "WordPress Browserslist shared configuration.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 65261f11ced412..eae4f3355741a0 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 23.6.0 (2023-03-15) + ### Enhancements - `FontSizePicker`: Allow custom units for custom font size control ([#48468](https://github.com/WordPress/gutenberg/pull/48468)). diff --git a/packages/components/package.json b/packages/components/package.json index 153ea7778c3888..098d4bcd1ef641 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/components", - "version": "23.5.0", + "version": "23.6.0-prerelease", "description": "UI components for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/compose/CHANGELOG.md b/packages/compose/CHANGELOG.md index e2e6111ab55fb5..d86599847cd7db 100644 --- a/packages/compose/CHANGELOG.md +++ b/packages/compose/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 6.6.0 (2023-03-15) + ## 6.5.0 (2023-03-01) ## 6.4.0 (2023-02-15) diff --git a/packages/compose/package.json b/packages/compose/package.json index 1cfa13e8d58d27..8beb82a9eccf5f 100644 --- a/packages/compose/package.json +++ b/packages/compose/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/compose", - "version": "6.5.0", + "version": "6.6.0-prerelease", "description": "WordPress higher-order components (HOCs).", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/core-data/CHANGELOG.md b/packages/core-data/CHANGELOG.md index b2d26b858cb16b..910783630990de 100644 --- a/packages/core-data/CHANGELOG.md +++ b/packages/core-data/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 6.6.0 (2023-03-15) + ## 6.5.0 (2023-03-01) ## 6.4.0 (2023-02-15) diff --git a/packages/core-data/package.json b/packages/core-data/package.json index 072ea2f3a82a9b..27a0b3e5fbef7b 100644 --- a/packages/core-data/package.json +++ b/packages/core-data/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/core-data", - "version": "6.5.0", + "version": "6.6.0-prerelease", "description": "Access to and manipulation of core WordPress entities.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/create-block-tutorial-template/CHANGELOG.md b/packages/create-block-tutorial-template/CHANGELOG.md index a4bec5eed6595a..1238aed11498c7 100644 --- a/packages/create-block-tutorial-template/CHANGELOG.md +++ b/packages/create-block-tutorial-template/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.17.0 (2023-03-15) + ## 2.16.0 (2023-03-01) ## 2.15.0 (2023-02-15) diff --git a/packages/create-block-tutorial-template/package.json b/packages/create-block-tutorial-template/package.json index 9984652389ebf6..13f34e7bb73a92 100644 --- a/packages/create-block-tutorial-template/package.json +++ b/packages/create-block-tutorial-template/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/create-block-tutorial-template", - "version": "2.16.0", + "version": "2.17.0-prerelease", "description": "Template for @wordpress/create-block used in the official WordPress tutorial.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/create-block/CHANGELOG.md b/packages/create-block/CHANGELOG.md index d26539bef55d3f..736e78d2aab462 100644 --- a/packages/create-block/CHANGELOG.md +++ b/packages/create-block/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.13.0 (2023-03-15) + ## 4.12.0 (2023-03-01) ## 4.11.0 (2023-02-15) diff --git a/packages/create-block/package.json b/packages/create-block/package.json index 6198bc1726016c..0154ff64fc3aa6 100644 --- a/packages/create-block/package.json +++ b/packages/create-block/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/create-block", - "version": "4.12.0", + "version": "4.13.0-prerelease", "description": "Generates PHP, JS and CSS code for registering a block for a WordPress plugin.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/customize-widgets/CHANGELOG.md b/packages/customize-widgets/CHANGELOG.md index 159471b28c4631..5bb335c8aa1618 100644 --- a/packages/customize-widgets/CHANGELOG.md +++ b/packages/customize-widgets/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.6.0 (2023-03-15) + ## 4.5.0 (2023-03-01) ## 4.4.0 (2023-02-15) diff --git a/packages/customize-widgets/package.json b/packages/customize-widgets/package.json index 198a7d38e3e5a5..c8bfc96814d732 100644 --- a/packages/customize-widgets/package.json +++ b/packages/customize-widgets/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/customize-widgets", - "version": "4.5.0", + "version": "4.6.0-prerelease", "description": "Widgets blocks in Customizer Module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/data-controls/CHANGELOG.md b/packages/data-controls/CHANGELOG.md index a4ceca774cb81d..aafd7f5d8fb5f7 100644 --- a/packages/data-controls/CHANGELOG.md +++ b/packages/data-controls/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.29.0 (2023-03-15) + ## 2.28.0 (2023-03-01) ## 2.27.0 (2023-02-15) diff --git a/packages/data-controls/package.json b/packages/data-controls/package.json index d47ed462f2e499..3439a768e13ef3 100644 --- a/packages/data-controls/package.json +++ b/packages/data-controls/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/data-controls", - "version": "2.28.0", + "version": "2.29.0-prerelease", "description": "A set of common controls for the @wordpress/data api.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/data/CHANGELOG.md b/packages/data/CHANGELOG.md index 2358aad98e22c1..60592bd2ee53f4 100644 --- a/packages/data/CHANGELOG.md +++ b/packages/data/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 8.6.0 (2023-03-15) + ## 8.5.0 (2023-03-01) ## 8.4.0 (2023-02-15) diff --git a/packages/data/package.json b/packages/data/package.json index fa9484c6a714ad..9d3e75a4b4a236 100644 --- a/packages/data/package.json +++ b/packages/data/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/data", - "version": "8.5.0", + "version": "8.6.0-prerelease", "description": "Data module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/date/CHANGELOG.md b/packages/date/CHANGELOG.md index c5ff0a69c8a981..51463172268686 100644 --- a/packages/date/CHANGELOG.md +++ b/packages/date/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.29.0 (2023-03-15) + ## 4.28.0 (2023-03-01) ## 4.27.0 (2023-02-15) diff --git a/packages/date/package.json b/packages/date/package.json index 902726f611ea2f..09aa90ffb591b1 100644 --- a/packages/date/package.json +++ b/packages/date/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/date", - "version": "4.28.0", + "version": "4.29.0-prerelease", "description": "Date module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/dependency-extraction-webpack-plugin/CHANGELOG.md b/packages/dependency-extraction-webpack-plugin/CHANGELOG.md index 621955ed4af91e..9ba386d2a64247 100644 --- a/packages/dependency-extraction-webpack-plugin/CHANGELOG.md +++ b/packages/dependency-extraction-webpack-plugin/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.12.0 (2023-03-15) + ## 4.11.0 (2023-03-01) ## 4.10.0 (2023-02-15) diff --git a/packages/dependency-extraction-webpack-plugin/package.json b/packages/dependency-extraction-webpack-plugin/package.json index 3cab139d593aee..2b13918107cdbe 100644 --- a/packages/dependency-extraction-webpack-plugin/package.json +++ b/packages/dependency-extraction-webpack-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/dependency-extraction-webpack-plugin", - "version": "4.11.0", + "version": "4.12.0-prerelease", "description": "Extract WordPress script dependencies from webpack bundles.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/deprecated/CHANGELOG.md b/packages/deprecated/CHANGELOG.md index 5cdd011a213b61..327f1418a6d2a6 100644 --- a/packages/deprecated/CHANGELOG.md +++ b/packages/deprecated/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.29.0 (2023-03-15) + ## 3.28.0 (2023-03-01) ## 3.27.0 (2023-02-15) diff --git a/packages/deprecated/package.json b/packages/deprecated/package.json index 4ed9b6902e36e6..50a59ab2eebb22 100644 --- a/packages/deprecated/package.json +++ b/packages/deprecated/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/deprecated", - "version": "3.28.0", + "version": "3.29.0-prerelease", "description": "Deprecation utility for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/docgen/CHANGELOG.md b/packages/docgen/CHANGELOG.md index 3382a0b30cdbee..83dea69985f13c 100644 --- a/packages/docgen/CHANGELOG.md +++ b/packages/docgen/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 1.38.0 (2023-03-15) + ## 1.37.0 (2023-03-01) ## 1.36.0 (2023-02-15) diff --git a/packages/docgen/package.json b/packages/docgen/package.json index a4e0c7f8698fe6..3160ff5b365155 100644 --- a/packages/docgen/package.json +++ b/packages/docgen/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/docgen", - "version": "1.37.0", + "version": "1.38.0-prerelease", "description": "Autogenerate public API documentation from exports and JSDoc comments.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/dom-ready/CHANGELOG.md b/packages/dom-ready/CHANGELOG.md index 4b65ce7eac6a97..82729de514c868 100644 --- a/packages/dom-ready/CHANGELOG.md +++ b/packages/dom-ready/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.29.0 (2023-03-15) + ## 3.28.0 (2023-03-01) ## 3.27.0 (2023-02-15) diff --git a/packages/dom-ready/package.json b/packages/dom-ready/package.json index f7e7d1c616b978..159cf4dd9d3eb7 100644 --- a/packages/dom-ready/package.json +++ b/packages/dom-ready/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/dom-ready", - "version": "3.28.0", + "version": "3.29.0-prerelease", "description": "Execute callback after the DOM is loaded.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/dom/CHANGELOG.md b/packages/dom/CHANGELOG.md index 33d35b2d7b60bd..7149b9caa0ab24 100644 --- a/packages/dom/CHANGELOG.md +++ b/packages/dom/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.29.0 (2023-03-15) + ## 3.28.0 (2023-03-01) ## 3.27.0 (2023-02-15) diff --git a/packages/dom/package.json b/packages/dom/package.json index d17f339a79ca1a..d2a93a1959e048 100644 --- a/packages/dom/package.json +++ b/packages/dom/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/dom", - "version": "3.28.0", + "version": "3.29.0-prerelease", "description": "DOM utilities module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/e2e-test-utils/CHANGELOG.md b/packages/e2e-test-utils/CHANGELOG.md index a1900e2da966ba..297d8f8718fd79 100644 --- a/packages/e2e-test-utils/CHANGELOG.md +++ b/packages/e2e-test-utils/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 10.0.0 (2023-03-15) + ### Breaking Changes - Started requiring Jest v29 instead of v27 as a peer dependency. See [breaking changes in Jest 28](https://jestjs.io/blog/2022/04/25/jest-28) and [in jest 29](https://jestjs.io/blog/2022/08/25/jest-29) ([#47388](https://github.com/WordPress/gutenberg/pull/47388)) diff --git a/packages/e2e-test-utils/package.json b/packages/e2e-test-utils/package.json index a3c5ab86f5bd5e..00626f38e9be25 100644 --- a/packages/e2e-test-utils/package.json +++ b/packages/e2e-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/e2e-test-utils", - "version": "9.5.0", + "version": "10.0.0-prerelease", "description": "End-To-End (E2E) test utils for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/e2e-tests/CHANGELOG.md b/packages/e2e-tests/CHANGELOG.md index 04e3f064e3c4cb..c3e7dd2e252196 100644 --- a/packages/e2e-tests/CHANGELOG.md +++ b/packages/e2e-tests/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 7.0.0 (2023-03-15) + ### Breaking Changes - Started requiring Jest v29 instead of v27 as a peer dependency. See [breaking changes in Jest 28](https://jestjs.io/blog/2022/04/25/jest-28) and [in jest 29](https://jestjs.io/blog/2022/08/25/jest-29) ([#47388](https://github.com/WordPress/gutenberg/pull/47388)) diff --git a/packages/e2e-tests/package.json b/packages/e2e-tests/package.json index ed148fc1721fa0..5c331bda77e4eb 100644 --- a/packages/e2e-tests/package.json +++ b/packages/e2e-tests/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/e2e-tests", - "version": "6.5.1", + "version": "7.0.0-prerelease", "description": "End-To-End (E2E) tests for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/edit-post/CHANGELOG.md b/packages/edit-post/CHANGELOG.md index 0787e11012a81c..a63155a306a99f 100644 --- a/packages/edit-post/CHANGELOG.md +++ b/packages/edit-post/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 7.6.0 (2023-03-15) + ## 7.5.0 (2023-03-01) ## 7.4.0 (2023-02-15) diff --git a/packages/edit-post/package.json b/packages/edit-post/package.json index 2ec53ca879fa15..0b26d57ae7191f 100644 --- a/packages/edit-post/package.json +++ b/packages/edit-post/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/edit-post", - "version": "7.5.0", + "version": "7.6.0-prerelease", "description": "Edit Post module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/edit-site/CHANGELOG.md b/packages/edit-site/CHANGELOG.md index 19986e08753673..456e35855e76a4 100644 --- a/packages/edit-site/CHANGELOG.md +++ b/packages/edit-site/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.6.0 (2023-03-15) + ## 5.5.0 (2023-03-01) ## 5.4.0 (2023-02-15) diff --git a/packages/edit-site/package.json b/packages/edit-site/package.json index 5208c81fe066d7..7fcecbef5ce3bb 100644 --- a/packages/edit-site/package.json +++ b/packages/edit-site/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/edit-site", - "version": "5.5.0", + "version": "5.6.0-prerelease", "description": "Edit Site Page module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/edit-widgets/CHANGELOG.md b/packages/edit-widgets/CHANGELOG.md index f288ecea66b8ed..4ab45397be63c8 100644 --- a/packages/edit-widgets/CHANGELOG.md +++ b/packages/edit-widgets/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.6.0 (2023-03-15) + ## 5.5.0 (2023-03-01) ## 5.4.0 (2023-02-15) diff --git a/packages/edit-widgets/package.json b/packages/edit-widgets/package.json index 45ef2192c19dee..1320febeb147e6 100644 --- a/packages/edit-widgets/package.json +++ b/packages/edit-widgets/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/edit-widgets", - "version": "5.5.0", + "version": "5.6.0-prerelease", "description": "Widgets Page module for WordPress..", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/editor/CHANGELOG.md b/packages/editor/CHANGELOG.md index 44de6703c38d37..174dc89c2a7c62 100644 --- a/packages/editor/CHANGELOG.md +++ b/packages/editor/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 13.6.0 (2023-03-15) + ## 13.5.0 (2023-03-01) ## 13.4.0 (2023-02-15) diff --git a/packages/editor/package.json b/packages/editor/package.json index a5affa4021839d..d045a6aa5bd80d 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/editor", - "version": "13.5.0", + "version": "13.6.0-prerelease", "description": "Enhanced block editor for WordPress posts.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/element/CHANGELOG.md b/packages/element/CHANGELOG.md index de7d9a7d9397a6..52a2d5b8073a55 100644 --- a/packages/element/CHANGELOG.md +++ b/packages/element/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.6.0 (2023-03-15) + ## 5.5.0 (2023-03-01) ### New Features diff --git a/packages/element/package.json b/packages/element/package.json index 9fff71c8e17a45..e0995c7b85e67f 100644 --- a/packages/element/package.json +++ b/packages/element/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/element", - "version": "5.5.0", + "version": "5.6.0-prerelease", "description": "Element React module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/env/CHANGELOG.md b/packages/env/CHANGELOG.md index 9e43be3f6c249c..ebda4e399cb043 100644 --- a/packages/env/CHANGELOG.md +++ b/packages/env/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.14.0 (2023-03-15) + ## 5.13.0 (2023-03-01) ## 5.12.0 (2023-02-15) diff --git a/packages/env/package.json b/packages/env/package.json index 6832e443d709a2..8ad22dff6b1163 100644 --- a/packages/env/package.json +++ b/packages/env/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/env", - "version": "5.13.0", + "version": "5.14.0-prerelease", "description": "A zero-config, self contained local WordPress environment for development and testing.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/escape-html/CHANGELOG.md b/packages/escape-html/CHANGELOG.md index 39873fb4ec68c2..41e4eb7731609f 100644 --- a/packages/escape-html/CHANGELOG.md +++ b/packages/escape-html/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.29.0 (2023-03-15) + ## 2.28.0 (2023-03-01) ## 2.27.0 (2023-02-15) diff --git a/packages/escape-html/package.json b/packages/escape-html/package.json index d1cb8b2d136d63..302773e22da5b8 100644 --- a/packages/escape-html/package.json +++ b/packages/escape-html/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/escape-html", - "version": "2.28.0", + "version": "2.29.0-prerelease", "description": "Escape HTML utils.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md index 850b62d463c4c3..4fe1e1ae4e7781 100644 --- a/packages/eslint-plugin/CHANGELOG.md +++ b/packages/eslint-plugin/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 14.2.0 (2023-03-15) + ## 14.1.0 (2023-03-01) ## 14.0.0 (2023-02-15) diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 58600bde1be820..202168dd261354 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/eslint-plugin", - "version": "14.1.0", + "version": "14.2.0-prerelease", "description": "ESLint plugin for WordPress development.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/format-library/CHANGELOG.md b/packages/format-library/CHANGELOG.md index a282b87fbb9107..fac245e012645d 100644 --- a/packages/format-library/CHANGELOG.md +++ b/packages/format-library/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.6.0 (2023-03-15) + ## 4.5.0 (2023-03-01) ## 4.4.0 (2023-02-15) diff --git a/packages/format-library/package.json b/packages/format-library/package.json index 3c16cde810a25b..5b221bff0a047e 100644 --- a/packages/format-library/package.json +++ b/packages/format-library/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/format-library", - "version": "4.5.0", + "version": "4.6.0-prerelease", "description": "Format library for the WordPress editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/hooks/CHANGELOG.md b/packages/hooks/CHANGELOG.md index e9b0973f019d8b..ab8d9c4d5f6905 100644 --- a/packages/hooks/CHANGELOG.md +++ b/packages/hooks/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.29.0 (2023-03-15) + ## 3.28.0 (2023-03-01) ## 3.27.0 (2023-02-15) diff --git a/packages/hooks/package.json b/packages/hooks/package.json index 589631cb2aed24..607419859d5434 100644 --- a/packages/hooks/package.json +++ b/packages/hooks/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/hooks", - "version": "3.28.0", + "version": "3.29.0-prerelease", "description": "WordPress hooks library.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/html-entities/CHANGELOG.md b/packages/html-entities/CHANGELOG.md index 8ced9643347714..0c9b03696580cc 100644 --- a/packages/html-entities/CHANGELOG.md +++ b/packages/html-entities/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.29.0 (2023-03-15) + ## 3.28.0 (2023-03-01) ## 3.27.0 (2023-02-15) diff --git a/packages/html-entities/package.json b/packages/html-entities/package.json index 7d8a35fcb36d8c..ea2e1f369627d8 100644 --- a/packages/html-entities/package.json +++ b/packages/html-entities/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/html-entities", - "version": "3.28.0", + "version": "3.29.0-prerelease", "description": "HTML entity utilities for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/i18n/CHANGELOG.md b/packages/i18n/CHANGELOG.md index e383fa0b7336bb..92bbcc1c659fc8 100644 --- a/packages/i18n/CHANGELOG.md +++ b/packages/i18n/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.29.0 (2023-03-15) + ## 4.28.0 (2023-03-01) ## 4.27.0 (2023-02-15) diff --git a/packages/i18n/package.json b/packages/i18n/package.json index e7943e3414f1ec..e4da706505aa10 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/i18n", - "version": "4.28.0", + "version": "4.29.0-prerelease", "description": "WordPress internationalization (i18n) library.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/icons/CHANGELOG.md b/packages/icons/CHANGELOG.md index bace96c15b0cd8..31833ed9682d87 100644 --- a/packages/icons/CHANGELOG.md +++ b/packages/icons/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 9.20.0 (2023-03-15) + ## 9.19.0 (2023-03-01) ## 9.18.0 (2023-02-15) diff --git a/packages/icons/package.json b/packages/icons/package.json index 30f3caad3d5e34..6941e0014e4571 100644 --- a/packages/icons/package.json +++ b/packages/icons/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/icons", - "version": "9.19.0", + "version": "9.20.0-prerelease", "description": "WordPress Icons package, based on dashicon.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/interface/CHANGELOG.md b/packages/interface/CHANGELOG.md index 01b42e3ce473e1..3fb7b64173687c 100644 --- a/packages/interface/CHANGELOG.md +++ b/packages/interface/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.6.0 (2023-03-15) + ## 5.5.0 (2023-03-01) ## 5.4.0 (2023-02-15) diff --git a/packages/interface/package.json b/packages/interface/package.json index 239c22629865e8..dfd5aa30954dbd 100644 --- a/packages/interface/package.json +++ b/packages/interface/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/interface", - "version": "5.5.0", + "version": "5.6.0-prerelease", "description": "Interface module for WordPress. The package contains shared functionality across the modern JavaScript-based WordPress screens.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/is-shallow-equal/CHANGELOG.md b/packages/is-shallow-equal/CHANGELOG.md index c017918f07d485..4f7ff2c98bf6c1 100644 --- a/packages/is-shallow-equal/CHANGELOG.md +++ b/packages/is-shallow-equal/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.29.0 (2023-03-15) + ## 4.28.0 (2023-03-01) ## 4.27.0 (2023-02-15) diff --git a/packages/is-shallow-equal/package.json b/packages/is-shallow-equal/package.json index ef4583dc9fec77..e2865397142b3a 100644 --- a/packages/is-shallow-equal/package.json +++ b/packages/is-shallow-equal/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/is-shallow-equal", - "version": "4.28.0", + "version": "4.29.0-prerelease", "description": "Test for shallow equality between two objects or arrays.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/jest-console/CHANGELOG.md b/packages/jest-console/CHANGELOG.md index 8ddcefcae3c2ed..134ef9338b697a 100644 --- a/packages/jest-console/CHANGELOG.md +++ b/packages/jest-console/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 7.0.0 (2023-03-15) + ### Breaking Changes - Started requiring Jest v29 instead of v27 as a peer dependency. See [breaking changes in Jest 28](https://jestjs.io/blog/2022/04/25/jest-28) and [in jest 29](https://jestjs.io/blog/2022/08/25/jest-29) ([#47388](https://github.com/WordPress/gutenberg/pull/47388)) diff --git a/packages/jest-console/package.json b/packages/jest-console/package.json index 71c38bdc4e4b53..53eab0fdf132a4 100644 --- a/packages/jest-console/package.json +++ b/packages/jest-console/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/jest-console", - "version": "6.11.0", + "version": "7.0.0-prerelease", "description": "Custom Jest matchers for the Console object.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/jest-preset-default/CHANGELOG.md b/packages/jest-preset-default/CHANGELOG.md index 5bb51185c754eb..015b5b309d40d8 100644 --- a/packages/jest-preset-default/CHANGELOG.md +++ b/packages/jest-preset-default/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 11.0.0 (2023-03-15) + ### Breaking Changes - Started requiring Jest v29 instead of v27 as a peer dependency. See [breaking changes in Jest 28](https://jestjs.io/blog/2022/04/25/jest-28) and [in jest 29](https://jestjs.io/blog/2022/08/25/jest-29) ([#47388](https://github.com/WordPress/gutenberg/pull/47388)) diff --git a/packages/jest-preset-default/package.json b/packages/jest-preset-default/package.json index 219373abea38d2..a1d08c7f16e071 100644 --- a/packages/jest-preset-default/package.json +++ b/packages/jest-preset-default/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/jest-preset-default", - "version": "10.9.0", + "version": "11.0.0-prerelease", "description": "Default Jest preset for WordPress development.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/jest-puppeteer-axe/CHANGELOG.md b/packages/jest-puppeteer-axe/CHANGELOG.md index 7b1df947da8ddc..18bf9b20382f51 100644 --- a/packages/jest-puppeteer-axe/CHANGELOG.md +++ b/packages/jest-puppeteer-axe/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 6.0.0 (2023-03-15) + ### Breaking Changes - Started requiring Jest v29 instead of v27 as a peer dependency. See [breaking changes in Jest 28](https://jestjs.io/blog/2022/04/25/jest-28) and [in jest 29](https://jestjs.io/blog/2022/08/25/jest-29) ([#47388](https://github.com/WordPress/gutenberg/pull/47388)) diff --git a/packages/jest-puppeteer-axe/package.json b/packages/jest-puppeteer-axe/package.json index 5fe6df55bb1069..937fa87f8ea3a9 100644 --- a/packages/jest-puppeteer-axe/package.json +++ b/packages/jest-puppeteer-axe/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/jest-puppeteer-axe", - "version": "5.11.0", + "version": "6.0.0-prerelease", "description": "Axe API integration with Jest and Puppeteer.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/keyboard-shortcuts/CHANGELOG.md b/packages/keyboard-shortcuts/CHANGELOG.md index 34739090ce1fd5..efbc9950ffdefa 100644 --- a/packages/keyboard-shortcuts/CHANGELOG.md +++ b/packages/keyboard-shortcuts/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.6.0 (2023-03-15) + ## 4.5.0 (2023-03-01) ## 4.4.0 (2023-02-15) diff --git a/packages/keyboard-shortcuts/package.json b/packages/keyboard-shortcuts/package.json index 355de35832ad74..dfa9f07f89cb48 100644 --- a/packages/keyboard-shortcuts/package.json +++ b/packages/keyboard-shortcuts/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/keyboard-shortcuts", - "version": "4.5.0", + "version": "4.6.0-prerelease", "description": "Handling keyboard shortcuts.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/keycodes/CHANGELOG.md b/packages/keycodes/CHANGELOG.md index fd6251843ca9c9..f161135abef3ac 100644 --- a/packages/keycodes/CHANGELOG.md +++ b/packages/keycodes/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.29.0 (2023-03-15) + ## 3.28.0 (2023-03-01) ## 3.27.0 (2023-02-15) diff --git a/packages/keycodes/package.json b/packages/keycodes/package.json index c9839ef01e8902..5019a12ab89b8d 100644 --- a/packages/keycodes/package.json +++ b/packages/keycodes/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/keycodes", - "version": "3.28.0", + "version": "3.29.0-prerelease", "description": "Keycodes utilities for WordPress. Used to check for keyboard events across browsers/operating systems.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/lazy-import/CHANGELOG.md b/packages/lazy-import/CHANGELOG.md index ab498422ecdc0b..21e55b850cf170 100644 --- a/packages/lazy-import/CHANGELOG.md +++ b/packages/lazy-import/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 1.16.0 (2023-03-15) + ## 1.15.0 (2023-03-01) ## 1.14.0 (2023-02-15) diff --git a/packages/lazy-import/package.json b/packages/lazy-import/package.json index 032d7225efe8e6..b6227b3e191541 100644 --- a/packages/lazy-import/package.json +++ b/packages/lazy-import/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/lazy-import", - "version": "1.15.0", + "version": "1.16.0-prerelease", "description": "Lazily import a module, installing it automatically if missing.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/list-reusable-blocks/CHANGELOG.md b/packages/list-reusable-blocks/CHANGELOG.md index 011cf09dd6e3bb..962d32431b02da 100644 --- a/packages/list-reusable-blocks/CHANGELOG.md +++ b/packages/list-reusable-blocks/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.6.0 (2023-03-15) + ## 4.5.0 (2023-03-01) ## 4.4.0 (2023-02-15) diff --git a/packages/list-reusable-blocks/package.json b/packages/list-reusable-blocks/package.json index e7cdaa998f2865..698ae7548a7554 100644 --- a/packages/list-reusable-blocks/package.json +++ b/packages/list-reusable-blocks/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/list-reusable-blocks", - "version": "4.5.0", + "version": "4.6.0-prerelease", "description": "Adding Export/Import support to the reusable blocks listing.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/media-utils/CHANGELOG.md b/packages/media-utils/CHANGELOG.md index 3b18a6cfd800e3..4022d13e9e9d28 100644 --- a/packages/media-utils/CHANGELOG.md +++ b/packages/media-utils/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.20.0 (2023-03-15) + ## 4.19.0 (2023-03-01) ## 4.18.0 (2023-02-15) diff --git a/packages/media-utils/package.json b/packages/media-utils/package.json index a5e9267dad8a26..3d65c793b157f4 100644 --- a/packages/media-utils/package.json +++ b/packages/media-utils/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/media-utils", - "version": "4.19.0", + "version": "4.20.0-prerelease", "description": "WordPress Media Upload Utils.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/notices/CHANGELOG.md b/packages/notices/CHANGELOG.md index 933de96f6ff69b..b6ffb160abaae6 100644 --- a/packages/notices/CHANGELOG.md +++ b/packages/notices/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.29.0 (2023-03-15) + ## 3.28.0 (2023-03-01) ## 3.27.0 (2023-02-15) diff --git a/packages/notices/package.json b/packages/notices/package.json index 6dfb2184c76d37..eeb2e9d32f1756 100644 --- a/packages/notices/package.json +++ b/packages/notices/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/notices", - "version": "3.28.0", + "version": "3.29.0-prerelease", "description": "State management for notices.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/npm-package-json-lint-config/CHANGELOG.md b/packages/npm-package-json-lint-config/CHANGELOG.md index a28d15c8efca37..b26e95f4138aa0 100644 --- a/packages/npm-package-json-lint-config/CHANGELOG.md +++ b/packages/npm-package-json-lint-config/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.14.0 (2023-03-15) + ## 4.13.0 (2023-03-01) ## 4.12.0 (2023-02-15) diff --git a/packages/npm-package-json-lint-config/package.json b/packages/npm-package-json-lint-config/package.json index 9672cca84bc6ca..cb398dac24b327 100644 --- a/packages/npm-package-json-lint-config/package.json +++ b/packages/npm-package-json-lint-config/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/npm-package-json-lint-config", - "version": "4.13.0", + "version": "4.14.0-prerelease", "description": "WordPress npm-package-json-lint shareable configuration.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/plugins/CHANGELOG.md b/packages/plugins/CHANGELOG.md index 8943265cea36f5..601e8c6a4f3c30 100644 --- a/packages/plugins/CHANGELOG.md +++ b/packages/plugins/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.6.0 (2023-03-15) + ## 5.5.0 (2023-03-01) ## 5.4.0 (2023-02-15) diff --git a/packages/plugins/package.json b/packages/plugins/package.json index c079950a41d2a8..9e894a0a0680ec 100644 --- a/packages/plugins/package.json +++ b/packages/plugins/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/plugins", - "version": "5.5.0", + "version": "5.6.0-prerelease", "description": "Plugins module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/postcss-plugins-preset/CHANGELOG.md b/packages/postcss-plugins-preset/CHANGELOG.md index e0411bc49b5931..ad51fd7bba1767 100644 --- a/packages/postcss-plugins-preset/CHANGELOG.md +++ b/packages/postcss-plugins-preset/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.13.0 (2023-03-15) + ## 4.12.0 (2023-03-01) ## 4.11.0 (2023-02-15) diff --git a/packages/postcss-plugins-preset/package.json b/packages/postcss-plugins-preset/package.json index f5f6c77cfa82f7..e9f3f2e916d51e 100644 --- a/packages/postcss-plugins-preset/package.json +++ b/packages/postcss-plugins-preset/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/postcss-plugins-preset", - "version": "4.12.0", + "version": "4.13.0-prerelease", "description": "PostCSS sharable plugins preset for WordPress development.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/postcss-themes/CHANGELOG.md b/packages/postcss-themes/CHANGELOG.md index 9c672d51e60c53..b6efed8f6d4d40 100644 --- a/packages/postcss-themes/CHANGELOG.md +++ b/packages/postcss-themes/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.12.0 (2023-03-15) + ## 5.11.0 (2023-03-01) ## 5.10.0 (2023-02-15) diff --git a/packages/postcss-themes/package.json b/packages/postcss-themes/package.json index cba6039021634f..948046184a2ee5 100644 --- a/packages/postcss-themes/package.json +++ b/packages/postcss-themes/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/postcss-themes", - "version": "5.11.0", + "version": "5.12.0-prerelease", "description": "PostCSS plugin to generate theme colors.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/preferences-persistence/CHANGELOG.md b/packages/preferences-persistence/CHANGELOG.md index 775b56249962f0..f4df5c7f1dceeb 100644 --- a/packages/preferences-persistence/CHANGELOG.md +++ b/packages/preferences-persistence/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 1.21.0 (2023-03-15) + ## 1.20.0 (2023-03-01) ## 1.19.0 (2023-02-15) diff --git a/packages/preferences-persistence/package.json b/packages/preferences-persistence/package.json index cec26c8f6d0cd9..bbe9774d1e20cd 100644 --- a/packages/preferences-persistence/package.json +++ b/packages/preferences-persistence/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/preferences-persistence", - "version": "1.20.0", + "version": "1.21.0-prerelease", "description": "Persistence utilities for `wordpress/preferences`.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/preferences/CHANGELOG.md b/packages/preferences/CHANGELOG.md index 48d46889b6be8e..01c8941f1b971b 100644 --- a/packages/preferences/CHANGELOG.md +++ b/packages/preferences/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.6.0 (2023-03-15) + ## 3.5.0 (2023-03-01) ## 3.4.0 (2023-02-15) diff --git a/packages/preferences/package.json b/packages/preferences/package.json index b51571bc870e51..13631e4ecf15a5 100644 --- a/packages/preferences/package.json +++ b/packages/preferences/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/preferences", - "version": "3.5.0", + "version": "3.6.0-prerelease", "description": "Utilities for managing WordPress preferences.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/prettier-config/CHANGELOG.md b/packages/prettier-config/CHANGELOG.md index d588816e2c1b03..bb3eb0b0157930 100644 --- a/packages/prettier-config/CHANGELOG.md +++ b/packages/prettier-config/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.12.0 (2023-03-15) + ## 2.11.0 (2023-03-01) ## 2.10.0 (2023-02-15) diff --git a/packages/prettier-config/package.json b/packages/prettier-config/package.json index 708db2b6135cfd..325988eadd2425 100644 --- a/packages/prettier-config/package.json +++ b/packages/prettier-config/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/prettier-config", - "version": "2.11.0", + "version": "2.12.0-prerelease", "description": "WordPress Prettier shared configuration.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/primitives/CHANGELOG.md b/packages/primitives/CHANGELOG.md index c30d3111e8a478..ee5602fb26df62 100644 --- a/packages/primitives/CHANGELOG.md +++ b/packages/primitives/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.27.0 (2023-03-15) + ## 3.26.0 (2023-03-01) ## 3.25.0 (2023-02-15) diff --git a/packages/primitives/package.json b/packages/primitives/package.json index ceac4a7dcc0d38..3c7cea92669204 100644 --- a/packages/primitives/package.json +++ b/packages/primitives/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/primitives", - "version": "3.26.0", + "version": "3.27.0-prerelease", "description": "WordPress cross-platform primitives.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/priority-queue/CHANGELOG.md b/packages/priority-queue/CHANGELOG.md index 48e001113925fd..1e996ae82f8038 100644 --- a/packages/priority-queue/CHANGELOG.md +++ b/packages/priority-queue/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.29.0 (2023-03-15) + ## 2.28.0 (2023-03-01) ## 2.27.0 (2023-02-15) diff --git a/packages/priority-queue/package.json b/packages/priority-queue/package.json index ab133302d8820b..d655278a8ef2dd 100644 --- a/packages/priority-queue/package.json +++ b/packages/priority-queue/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/priority-queue", - "version": "2.28.0", + "version": "2.29.0-prerelease", "description": "Generic browser priority queue.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/private-apis/CHANGELOG.md b/packages/private-apis/CHANGELOG.md index e1e36ba1944dc5..1190d7a1993d68 100644 --- a/packages/private-apis/CHANGELOG.md +++ b/packages/private-apis/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 0.11.0 (2023-03-15) + ## 0.10.0 (2023-03-01) ## 0.9.0 (2023-02-15) diff --git a/packages/private-apis/package.json b/packages/private-apis/package.json index c9def433e19251..51934f30af9ed0 100644 --- a/packages/private-apis/package.json +++ b/packages/private-apis/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/private-apis", - "version": "0.10.0", + "version": "0.11.0-prerelease", "description": "Internal experimental APIs for WordPress core.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/project-management-automation/CHANGELOG.md b/packages/project-management-automation/CHANGELOG.md index 67e8696667d653..0904845df18d15 100644 --- a/packages/project-management-automation/CHANGELOG.md +++ b/packages/project-management-automation/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 1.28.0 (2023-03-15) + ## 1.27.0 (2023-03-01) ## 1.26.0 (2023-02-15) diff --git a/packages/project-management-automation/package.json b/packages/project-management-automation/package.json index 8ecc6013747859..cd2dda8a882af6 100644 --- a/packages/project-management-automation/package.json +++ b/packages/project-management-automation/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/project-management-automation", - "version": "1.27.0", + "version": "1.28.0-prerelease", "description": "GitHub Action that implements various automation to assist with managing the Gutenberg GitHub repository.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/react-i18n/CHANGELOG.md b/packages/react-i18n/CHANGELOG.md index ae54e6e16a8337..9c8cdb97596057 100644 --- a/packages/react-i18n/CHANGELOG.md +++ b/packages/react-i18n/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.27.0 (2023-03-15) + ## 3.26.0 (2023-03-01) ## 3.25.0 (2023-02-15) diff --git a/packages/react-i18n/package.json b/packages/react-i18n/package.json index eefa99b7c73ace..f4434ee3aed307 100644 --- a/packages/react-i18n/package.json +++ b/packages/react-i18n/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/react-i18n", - "version": "3.26.0", + "version": "3.27.0-prerelease", "description": "React bindings for @wordpress/i18n.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/readable-js-assets-webpack-plugin/CHANGELOG.md b/packages/readable-js-assets-webpack-plugin/CHANGELOG.md index ce12c070252f62..c98191949074bc 100644 --- a/packages/readable-js-assets-webpack-plugin/CHANGELOG.md +++ b/packages/readable-js-assets-webpack-plugin/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.12.0 (2023-03-15) + ## 2.11.0 (2023-03-01) ## 2.10.0 (2023-02-15) diff --git a/packages/readable-js-assets-webpack-plugin/package.json b/packages/readable-js-assets-webpack-plugin/package.json index cb52c8eea8168c..9daee05837c0a1 100644 --- a/packages/readable-js-assets-webpack-plugin/package.json +++ b/packages/readable-js-assets-webpack-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/readable-js-assets-webpack-plugin", - "version": "2.11.0", + "version": "2.12.0-prerelease", "description": "Generate a readable JS file for each JS asset.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/redux-routine/CHANGELOG.md b/packages/redux-routine/CHANGELOG.md index 9f5f616947fe40..fda08cb4638156 100644 --- a/packages/redux-routine/CHANGELOG.md +++ b/packages/redux-routine/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.29.0 (2023-03-15) + ## 4.28.0 (2023-03-01) ## 4.27.0 (2023-02-15) diff --git a/packages/redux-routine/package.json b/packages/redux-routine/package.json index 6aa8ef85cbf3ad..25e04ea7b204fb 100644 --- a/packages/redux-routine/package.json +++ b/packages/redux-routine/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/redux-routine", - "version": "4.28.0", + "version": "4.29.0-prerelease", "description": "Redux middleware for generator coroutines.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/reusable-blocks/CHANGELOG.md b/packages/reusable-blocks/CHANGELOG.md index 605bdf915dd96b..88778b64e6a611 100644 --- a/packages/reusable-blocks/CHANGELOG.md +++ b/packages/reusable-blocks/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.6.0 (2023-03-15) + ## 4.5.0 (2023-03-01) ## 4.4.0 (2023-02-15) diff --git a/packages/reusable-blocks/package.json b/packages/reusable-blocks/package.json index 46cf438836bf4d..2e616f5f0fd376 100644 --- a/packages/reusable-blocks/package.json +++ b/packages/reusable-blocks/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/reusable-blocks", - "version": "4.5.0", + "version": "4.6.0-prerelease", "description": "Reusable blocks utilities.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/rich-text/CHANGELOG.md b/packages/rich-text/CHANGELOG.md index 5562d06695c715..670c5637041da8 100644 --- a/packages/rich-text/CHANGELOG.md +++ b/packages/rich-text/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 6.6.0 (2023-03-15) + ## 6.5.0 (2023-03-01) ## 6.4.0 (2023-02-15) diff --git a/packages/rich-text/package.json b/packages/rich-text/package.json index 9dd26370d68d84..f51ae10bcda7fa 100644 --- a/packages/rich-text/package.json +++ b/packages/rich-text/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/rich-text", - "version": "6.5.0", + "version": "6.6.0-prerelease", "description": "Rich text value and manipulation API.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/scripts/CHANGELOG.md b/packages/scripts/CHANGELOG.md index 6ab314d5c7e100..afe22e868a63ab 100644 --- a/packages/scripts/CHANGELOG.md +++ b/packages/scripts/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 26.0.0 (2023-03-15) + ### Breaking Changes - Started using Jest v29 instead of v27 as a dependency. See [breaking changes in Jest 28](https://jestjs.io/blog/2022/04/25/jest-28) and [in jest 29](https://jestjs.io/blog/2022/08/25/jest-29) ([#47388](https://github.com/WordPress/gutenberg/pull/47388)) diff --git a/packages/scripts/package.json b/packages/scripts/package.json index 43ff79652a552c..de810311c89632 100644 --- a/packages/scripts/package.json +++ b/packages/scripts/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/scripts", - "version": "25.5.1", + "version": "26.0.0-prerelease", "description": "Collection of reusable scripts for WordPress development.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/server-side-render/CHANGELOG.md b/packages/server-side-render/CHANGELOG.md index a08695da991d38..c795fb17a25580 100644 --- a/packages/server-side-render/CHANGELOG.md +++ b/packages/server-side-render/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.6.0 (2023-03-15) + ## 4.5.0 (2023-03-01) ## 4.4.0 (2023-02-15) diff --git a/packages/server-side-render/package.json b/packages/server-side-render/package.json index c94a41aa832706..e099ac763701c6 100644 --- a/packages/server-side-render/package.json +++ b/packages/server-side-render/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/server-side-render", - "version": "4.5.0", + "version": "4.6.0-prerelease", "description": "The component used with WordPress to server-side render a preview of dynamic blocks to display in the editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/shortcode/CHANGELOG.md b/packages/shortcode/CHANGELOG.md index f40b9b013e1c83..9446da8b4b126d 100644 --- a/packages/shortcode/CHANGELOG.md +++ b/packages/shortcode/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.29.0 (2023-03-15) + ## 3.28.0 (2023-03-01) ## 3.27.0 (2023-02-15) diff --git a/packages/shortcode/package.json b/packages/shortcode/package.json index b5131a016fb298..7499df29102dc9 100644 --- a/packages/shortcode/package.json +++ b/packages/shortcode/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/shortcode", - "version": "3.28.0", + "version": "3.29.0-prerelease", "description": "Shortcode module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/style-engine/CHANGELOG.md b/packages/style-engine/CHANGELOG.md index 8b57d9ddca9bda..4450ec6b40e721 100644 --- a/packages/style-engine/CHANGELOG.md +++ b/packages/style-engine/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 1.12.0 (2023-03-15) + ## 1.11.0 (2023-03-01) ## 1.10.0 (2023-02-15) diff --git a/packages/style-engine/package.json b/packages/style-engine/package.json index 23ed8f6074f1ab..0bddfb983f4e5d 100644 --- a/packages/style-engine/package.json +++ b/packages/style-engine/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/style-engine", - "version": "1.11.0", + "version": "1.12.0-prerelease", "description": "A suite of parsers and compilers for WordPress styles.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/stylelint-config/CHANGELOG.md b/packages/stylelint-config/CHANGELOG.md index f70c84438b1731..aad1eaa342921b 100644 --- a/packages/stylelint-config/CHANGELOG.md +++ b/packages/stylelint-config/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 21.12.0 (2023-03-15) + ## 21.11.0 (2023-03-01) ## 21.10.0 (2023-02-15) diff --git a/packages/stylelint-config/package.json b/packages/stylelint-config/package.json index 956822cfa01e20..1e9ed7c2a658e3 100644 --- a/packages/stylelint-config/package.json +++ b/packages/stylelint-config/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/stylelint-config", - "version": "21.11.0", + "version": "21.12.0-prerelease", "description": "stylelint config for WordPress development.", "author": "The WordPress Contributors", "license": "MIT", diff --git a/packages/token-list/CHANGELOG.md b/packages/token-list/CHANGELOG.md index 768ccc08821d98..2ad9975dc0eb8d 100644 --- a/packages/token-list/CHANGELOG.md +++ b/packages/token-list/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.29.0 (2023-03-15) + ## 2.28.0 (2023-03-01) ## 2.27.0 (2023-02-15) diff --git a/packages/token-list/package.json b/packages/token-list/package.json index 52c82fb034b564..1cc16fb61230c1 100644 --- a/packages/token-list/package.json +++ b/packages/token-list/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/token-list", - "version": "2.28.0", + "version": "2.29.0-prerelease", "description": "Constructable, plain JavaScript DOMTokenList implementation, supporting non-browser runtimes.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/url/CHANGELOG.md b/packages/url/CHANGELOG.md index 105f105a927716..88815e24ce6b9b 100644 --- a/packages/url/CHANGELOG.md +++ b/packages/url/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.30.0 (2023-03-15) + ## 3.29.0 (2023-03-01) ## 3.28.0 (2023-02-15) diff --git a/packages/url/package.json b/packages/url/package.json index 85d2606f3ac16e..315a80c1ca59f2 100644 --- a/packages/url/package.json +++ b/packages/url/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/url", - "version": "3.29.0", + "version": "3.30.0-prerelease", "description": "WordPress URL utilities.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/viewport/CHANGELOG.md b/packages/viewport/CHANGELOG.md index 95b7cdaa684b0e..08c7be4a6d5c04 100644 --- a/packages/viewport/CHANGELOG.md +++ b/packages/viewport/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.6.0 (2023-03-15) + ## 5.5.0 (2023-03-01) ## 5.4.0 (2023-02-15) diff --git a/packages/viewport/package.json b/packages/viewport/package.json index 20ec4c1d63d615..0eb4688b2b3b0e 100644 --- a/packages/viewport/package.json +++ b/packages/viewport/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/viewport", - "version": "5.5.0", + "version": "5.6.0-prerelease", "description": "Viewport module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/warning/CHANGELOG.md b/packages/warning/CHANGELOG.md index 3444412e68aeca..4bc45851a45dad 100644 --- a/packages/warning/CHANGELOG.md +++ b/packages/warning/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.29.0 (2023-03-15) + ## 2.28.0 (2023-03-01) ## 2.27.0 (2023-02-15) diff --git a/packages/warning/package.json b/packages/warning/package.json index 42dece8997ad99..ab44fd13eb26c1 100644 --- a/packages/warning/package.json +++ b/packages/warning/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/warning", - "version": "2.28.0", + "version": "2.29.0-prerelease", "description": "Warning utility for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/widgets/CHANGELOG.md b/packages/widgets/CHANGELOG.md index 6ceecf3cdeec77..fc437a1ec60a41 100644 --- a/packages/widgets/CHANGELOG.md +++ b/packages/widgets/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.6.0 (2023-03-15) + ## 3.5.0 (2023-03-01) ## 3.4.0 (2023-02-15) diff --git a/packages/widgets/package.json b/packages/widgets/package.json index 8ba30280656a43..7188bfe21b6d9b 100644 --- a/packages/widgets/package.json +++ b/packages/widgets/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/widgets", - "version": "3.5.0", + "version": "3.6.0-prerelease", "description": "Functionality used by the widgets block editor in the Widgets screen and the Customizer.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/wordcount/CHANGELOG.md b/packages/wordcount/CHANGELOG.md index 0782f3ada428b1..2bccb30a004c2c 100644 --- a/packages/wordcount/CHANGELOG.md +++ b/packages/wordcount/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.29.0 (2023-03-15) + ## 3.28.0 (2023-03-01) ## 3.27.0 (2023-02-15) diff --git a/packages/wordcount/package.json b/packages/wordcount/package.json index 14bef22989ae7e..a1684b8ead274c 100644 --- a/packages/wordcount/package.json +++ b/packages/wordcount/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/wordcount", - "version": "3.28.0", + "version": "3.29.0-prerelease", "description": "WordPress word count utility.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", From 352056bd77a9764d94dc635f41cae18b09782da1 Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Wed, 15 Mar 2023 17:24:11 +0000 Subject: [PATCH 169/910] chore(release): publish - @wordpress/a11y@3.29.0 - @wordpress/annotations@2.29.0 - @wordpress/api-fetch@6.26.0 - @wordpress/autop@3.29.0 - @wordpress/babel-plugin-import-jsx-pragma@4.12.0 - @wordpress/babel-plugin-makepot@5.13.0 - @wordpress/babel-preset-default@7.13.0 - @wordpress/base-styles@4.20.0 - @wordpress/blob@3.29.0 - @wordpress/block-directory@4.6.0 - @wordpress/block-editor@11.6.0 - @wordpress/block-library@8.6.0 - @wordpress/block-serialization-default-parser@4.29.0 - @wordpress/block-serialization-spec-parser@4.29.0 - @wordpress/blocks@12.6.0 - @wordpress/browserslist-config@5.12.0 - @wordpress/components@23.6.0 - @wordpress/compose@6.6.0 - @wordpress/core-data@6.6.0 - @wordpress/create-block-tutorial-template@2.17.0 - @wordpress/create-block@4.13.0 - @wordpress/customize-widgets@4.6.0 - @wordpress/data-controls@2.29.0 - @wordpress/data@8.6.0 - @wordpress/date@4.29.0 - @wordpress/dependency-extraction-webpack-plugin@4.12.0 - @wordpress/deprecated@3.29.0 - @wordpress/docgen@1.38.0 - @wordpress/dom-ready@3.29.0 - @wordpress/dom@3.29.0 - @wordpress/e2e-test-utils@10.0.0 - @wordpress/e2e-tests@7.0.0 - @wordpress/edit-post@7.6.0 - @wordpress/edit-site@5.6.0 - @wordpress/edit-widgets@5.6.0 - @wordpress/editor@13.6.0 - @wordpress/element@5.6.0 - @wordpress/env@5.14.0 - @wordpress/escape-html@2.29.0 - @wordpress/eslint-plugin@14.2.0 - @wordpress/format-library@4.6.0 - @wordpress/hooks@3.29.0 - @wordpress/html-entities@3.29.0 - @wordpress/i18n@4.29.0 - @wordpress/icons@9.20.0 - @wordpress/interface@5.6.0 - @wordpress/is-shallow-equal@4.29.0 - @wordpress/jest-console@7.0.0 - @wordpress/jest-preset-default@11.0.0 - @wordpress/jest-puppeteer-axe@6.0.0 - @wordpress/keyboard-shortcuts@4.6.0 - @wordpress/keycodes@3.29.0 - @wordpress/lazy-import@1.16.0 - @wordpress/list-reusable-blocks@4.6.0 - @wordpress/media-utils@4.20.0 - @wordpress/notices@3.29.0 - @wordpress/npm-package-json-lint-config@4.14.0 - @wordpress/plugins@5.6.0 - @wordpress/postcss-plugins-preset@4.13.0 - @wordpress/postcss-themes@5.12.0 - @wordpress/preferences-persistence@1.21.0 - @wordpress/preferences@3.6.0 - @wordpress/prettier-config@2.12.0 - @wordpress/primitives@3.27.0 - @wordpress/priority-queue@2.29.0 - @wordpress/private-apis@0.11.0 - @wordpress/project-management-automation@1.28.0 - @wordpress/react-i18n@3.27.0 - @wordpress/readable-js-assets-webpack-plugin@2.12.0 - @wordpress/redux-routine@4.29.0 - @wordpress/reusable-blocks@4.6.0 - @wordpress/rich-text@6.6.0 - @wordpress/scripts@26.0.0 - @wordpress/server-side-render@4.6.0 - @wordpress/shortcode@3.29.0 - @wordpress/style-engine@1.12.0 - @wordpress/stylelint-config@21.12.0 - @wordpress/token-list@2.29.0 - @wordpress/url@3.30.0 - @wordpress/viewport@5.6.0 - @wordpress/warning@2.29.0 - @wordpress/widgets@3.6.0 - @wordpress/wordcount@3.29.0 --- packages/a11y/package.json | 2 +- packages/annotations/package.json | 2 +- packages/api-fetch/package.json | 2 +- packages/autop/package.json | 2 +- packages/babel-plugin-import-jsx-pragma/package.json | 2 +- packages/babel-plugin-makepot/package.json | 2 +- packages/babel-preset-default/package.json | 2 +- packages/base-styles/package.json | 2 +- packages/blob/package.json | 2 +- packages/block-directory/package.json | 2 +- packages/block-editor/package.json | 2 +- packages/block-library/package.json | 2 +- packages/block-serialization-default-parser/package.json | 2 +- packages/block-serialization-spec-parser/package.json | 2 +- packages/blocks/package.json | 2 +- packages/browserslist-config/package.json | 2 +- packages/components/package.json | 2 +- packages/compose/package.json | 2 +- packages/core-data/package.json | 2 +- packages/create-block-tutorial-template/package.json | 2 +- packages/create-block/package.json | 2 +- packages/customize-widgets/package.json | 2 +- packages/data-controls/package.json | 2 +- packages/data/package.json | 2 +- packages/date/package.json | 2 +- packages/dependency-extraction-webpack-plugin/package.json | 2 +- packages/deprecated/package.json | 2 +- packages/docgen/package.json | 2 +- packages/dom-ready/package.json | 2 +- packages/dom/package.json | 2 +- packages/e2e-test-utils/package.json | 2 +- packages/e2e-tests/package.json | 2 +- packages/edit-post/package.json | 2 +- packages/edit-site/package.json | 2 +- packages/edit-widgets/package.json | 2 +- packages/editor/package.json | 2 +- packages/element/package.json | 2 +- packages/env/package.json | 2 +- packages/escape-html/package.json | 2 +- packages/eslint-plugin/package.json | 2 +- packages/format-library/package.json | 2 +- packages/hooks/package.json | 2 +- packages/html-entities/package.json | 2 +- packages/i18n/package.json | 2 +- packages/icons/package.json | 2 +- packages/interface/package.json | 2 +- packages/is-shallow-equal/package.json | 2 +- packages/jest-console/package.json | 2 +- packages/jest-preset-default/package.json | 2 +- packages/jest-puppeteer-axe/package.json | 2 +- packages/keyboard-shortcuts/package.json | 2 +- packages/keycodes/package.json | 2 +- packages/lazy-import/package.json | 2 +- packages/list-reusable-blocks/package.json | 2 +- packages/media-utils/package.json | 2 +- packages/notices/package.json | 2 +- packages/npm-package-json-lint-config/package.json | 2 +- packages/plugins/package.json | 2 +- packages/postcss-plugins-preset/package.json | 2 +- packages/postcss-themes/package.json | 2 +- packages/preferences-persistence/package.json | 2 +- packages/preferences/package.json | 2 +- packages/prettier-config/package.json | 2 +- packages/primitives/package.json | 2 +- packages/priority-queue/package.json | 2 +- packages/private-apis/package.json | 2 +- packages/project-management-automation/package.json | 2 +- packages/react-i18n/package.json | 2 +- packages/readable-js-assets-webpack-plugin/package.json | 2 +- packages/redux-routine/package.json | 2 +- packages/reusable-blocks/package.json | 2 +- packages/rich-text/package.json | 2 +- packages/scripts/package.json | 2 +- packages/server-side-render/package.json | 2 +- packages/shortcode/package.json | 2 +- packages/style-engine/package.json | 2 +- packages/stylelint-config/package.json | 2 +- packages/token-list/package.json | 2 +- packages/url/package.json | 2 +- packages/viewport/package.json | 2 +- packages/warning/package.json | 2 +- packages/widgets/package.json | 2 +- packages/wordcount/package.json | 2 +- 83 files changed, 83 insertions(+), 83 deletions(-) diff --git a/packages/a11y/package.json b/packages/a11y/package.json index de040fb5ccf90a..942e1416b91a1c 100644 --- a/packages/a11y/package.json +++ b/packages/a11y/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/a11y", - "version": "3.29.0-prerelease", + "version": "3.29.0", "description": "Accessibility (a11y) utilities for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/annotations/package.json b/packages/annotations/package.json index 93c1a5289eb629..366d70371eb2b2 100644 --- a/packages/annotations/package.json +++ b/packages/annotations/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/annotations", - "version": "2.29.0-prerelease", + "version": "2.29.0", "description": "Annotate content in the Gutenberg editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/api-fetch/package.json b/packages/api-fetch/package.json index 0859876b32e890..3bf3447118afdb 100644 --- a/packages/api-fetch/package.json +++ b/packages/api-fetch/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/api-fetch", - "version": "6.26.0-prerelease", + "version": "6.26.0", "description": "Utility to make WordPress REST API requests.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/autop/package.json b/packages/autop/package.json index db102336b53000..70d776353096c0 100644 --- a/packages/autop/package.json +++ b/packages/autop/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/autop", - "version": "3.29.0-prerelease", + "version": "3.29.0", "description": "WordPress's automatic paragraph functions `autop` and `removep`.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/babel-plugin-import-jsx-pragma/package.json b/packages/babel-plugin-import-jsx-pragma/package.json index 735c0eaecea9c8..fbe2fd8f092e6f 100644 --- a/packages/babel-plugin-import-jsx-pragma/package.json +++ b/packages/babel-plugin-import-jsx-pragma/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/babel-plugin-import-jsx-pragma", - "version": "4.12.0-prerelease", + "version": "4.12.0", "description": "Babel transform plugin for automatically injecting an import to be used as the pragma for the React JSX Transform plugin.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/babel-plugin-makepot/package.json b/packages/babel-plugin-makepot/package.json index 654c10c078d2bf..f45237c808c8af 100644 --- a/packages/babel-plugin-makepot/package.json +++ b/packages/babel-plugin-makepot/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/babel-plugin-makepot", - "version": "5.13.0-prerelease", + "version": "5.13.0", "description": "WordPress Babel internationalization (i18n) plugin.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/babel-preset-default/package.json b/packages/babel-preset-default/package.json index ca3eeeb0211b4c..2ed7be8482c776 100644 --- a/packages/babel-preset-default/package.json +++ b/packages/babel-preset-default/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/babel-preset-default", - "version": "7.13.0-prerelease", + "version": "7.13.0", "description": "Default Babel preset for WordPress development.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/base-styles/package.json b/packages/base-styles/package.json index d7e6d67c8f01d2..8e320316bcd61b 100644 --- a/packages/base-styles/package.json +++ b/packages/base-styles/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/base-styles", - "version": "4.20.0-prerelease", + "version": "4.20.0", "description": "Base SCSS utilities and variables for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/blob/package.json b/packages/blob/package.json index b624c172ab6d66..0de06bb05cf864 100644 --- a/packages/blob/package.json +++ b/packages/blob/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/blob", - "version": "3.29.0-prerelease", + "version": "3.29.0", "description": "Blob utilities for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-directory/package.json b/packages/block-directory/package.json index 291565cf6aaadc..9071c55c4d98c4 100644 --- a/packages/block-directory/package.json +++ b/packages/block-directory/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-directory", - "version": "4.6.0-prerelease", + "version": "4.6.0", "description": "Extend editor with block directory features to search, download and install blocks.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json index ebb778bc45cf99..e3a873fe65d68e 100644 --- a/packages/block-editor/package.json +++ b/packages/block-editor/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-editor", - "version": "11.6.0-prerelease", + "version": "11.6.0", "description": "Generic block editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-library/package.json b/packages/block-library/package.json index 66ff3bb67f43db..431ea2cedc4442 100644 --- a/packages/block-library/package.json +++ b/packages/block-library/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-library", - "version": "8.6.0-prerelease", + "version": "8.6.0", "description": "Block library for the WordPress editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-serialization-default-parser/package.json b/packages/block-serialization-default-parser/package.json index 28fbdd1e1b979b..ef21ccb083bead 100644 --- a/packages/block-serialization-default-parser/package.json +++ b/packages/block-serialization-default-parser/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-serialization-default-parser", - "version": "4.29.0-prerelease", + "version": "4.29.0", "description": "Block serialization specification parser for WordPress posts.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-serialization-spec-parser/package.json b/packages/block-serialization-spec-parser/package.json index 98119306ed3edf..d5544fc80da8a9 100644 --- a/packages/block-serialization-spec-parser/package.json +++ b/packages/block-serialization-spec-parser/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-serialization-spec-parser", - "version": "4.29.0-prerelease", + "version": "4.29.0", "description": "Block serialization specification parser for WordPress posts.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/blocks/package.json b/packages/blocks/package.json index 21f6cf577fefaa..73f8f272c3f098 100644 --- a/packages/blocks/package.json +++ b/packages/blocks/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/blocks", - "version": "12.6.0-prerelease", + "version": "12.6.0", "description": "Block API for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/browserslist-config/package.json b/packages/browserslist-config/package.json index 7b015d2d11033c..6488d5b1879dc1 100644 --- a/packages/browserslist-config/package.json +++ b/packages/browserslist-config/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/browserslist-config", - "version": "5.12.0-prerelease", + "version": "5.12.0", "description": "WordPress Browserslist shared configuration.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/components/package.json b/packages/components/package.json index 098d4bcd1ef641..5cd52073b412af 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/components", - "version": "23.6.0-prerelease", + "version": "23.6.0", "description": "UI components for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/compose/package.json b/packages/compose/package.json index 8beb82a9eccf5f..2f50793e113440 100644 --- a/packages/compose/package.json +++ b/packages/compose/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/compose", - "version": "6.6.0-prerelease", + "version": "6.6.0", "description": "WordPress higher-order components (HOCs).", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/core-data/package.json b/packages/core-data/package.json index 27a0b3e5fbef7b..3f89274586f41f 100644 --- a/packages/core-data/package.json +++ b/packages/core-data/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/core-data", - "version": "6.6.0-prerelease", + "version": "6.6.0", "description": "Access to and manipulation of core WordPress entities.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/create-block-tutorial-template/package.json b/packages/create-block-tutorial-template/package.json index 13f34e7bb73a92..226620aef1ae2d 100644 --- a/packages/create-block-tutorial-template/package.json +++ b/packages/create-block-tutorial-template/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/create-block-tutorial-template", - "version": "2.17.0-prerelease", + "version": "2.17.0", "description": "Template for @wordpress/create-block used in the official WordPress tutorial.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/create-block/package.json b/packages/create-block/package.json index 0154ff64fc3aa6..7ce7c750d4f979 100644 --- a/packages/create-block/package.json +++ b/packages/create-block/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/create-block", - "version": "4.13.0-prerelease", + "version": "4.13.0", "description": "Generates PHP, JS and CSS code for registering a block for a WordPress plugin.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/customize-widgets/package.json b/packages/customize-widgets/package.json index c8bfc96814d732..226aa4e9c7ecba 100644 --- a/packages/customize-widgets/package.json +++ b/packages/customize-widgets/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/customize-widgets", - "version": "4.6.0-prerelease", + "version": "4.6.0", "description": "Widgets blocks in Customizer Module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/data-controls/package.json b/packages/data-controls/package.json index 3439a768e13ef3..c84eae4c305b00 100644 --- a/packages/data-controls/package.json +++ b/packages/data-controls/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/data-controls", - "version": "2.29.0-prerelease", + "version": "2.29.0", "description": "A set of common controls for the @wordpress/data api.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/data/package.json b/packages/data/package.json index 9d3e75a4b4a236..a8d689f058c851 100644 --- a/packages/data/package.json +++ b/packages/data/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/data", - "version": "8.6.0-prerelease", + "version": "8.6.0", "description": "Data module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/date/package.json b/packages/date/package.json index 09aa90ffb591b1..b4b00b80cab3d2 100644 --- a/packages/date/package.json +++ b/packages/date/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/date", - "version": "4.29.0-prerelease", + "version": "4.29.0", "description": "Date module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/dependency-extraction-webpack-plugin/package.json b/packages/dependency-extraction-webpack-plugin/package.json index 2b13918107cdbe..e2e27a2a05a27d 100644 --- a/packages/dependency-extraction-webpack-plugin/package.json +++ b/packages/dependency-extraction-webpack-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/dependency-extraction-webpack-plugin", - "version": "4.12.0-prerelease", + "version": "4.12.0", "description": "Extract WordPress script dependencies from webpack bundles.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/deprecated/package.json b/packages/deprecated/package.json index 50a59ab2eebb22..0fb97cf76d6ff0 100644 --- a/packages/deprecated/package.json +++ b/packages/deprecated/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/deprecated", - "version": "3.29.0-prerelease", + "version": "3.29.0", "description": "Deprecation utility for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/docgen/package.json b/packages/docgen/package.json index 3160ff5b365155..ff42699dff03b0 100644 --- a/packages/docgen/package.json +++ b/packages/docgen/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/docgen", - "version": "1.38.0-prerelease", + "version": "1.38.0", "description": "Autogenerate public API documentation from exports and JSDoc comments.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/dom-ready/package.json b/packages/dom-ready/package.json index 159cf4dd9d3eb7..f5fb38777d5063 100644 --- a/packages/dom-ready/package.json +++ b/packages/dom-ready/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/dom-ready", - "version": "3.29.0-prerelease", + "version": "3.29.0", "description": "Execute callback after the DOM is loaded.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/dom/package.json b/packages/dom/package.json index d2a93a1959e048..0996360214435c 100644 --- a/packages/dom/package.json +++ b/packages/dom/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/dom", - "version": "3.29.0-prerelease", + "version": "3.29.0", "description": "DOM utilities module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/e2e-test-utils/package.json b/packages/e2e-test-utils/package.json index 00626f38e9be25..3dd71211e76b5c 100644 --- a/packages/e2e-test-utils/package.json +++ b/packages/e2e-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/e2e-test-utils", - "version": "10.0.0-prerelease", + "version": "10.0.0", "description": "End-To-End (E2E) test utils for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/e2e-tests/package.json b/packages/e2e-tests/package.json index 5c331bda77e4eb..3b1e030561684a 100644 --- a/packages/e2e-tests/package.json +++ b/packages/e2e-tests/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/e2e-tests", - "version": "7.0.0-prerelease", + "version": "7.0.0", "description": "End-To-End (E2E) tests for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/edit-post/package.json b/packages/edit-post/package.json index 0b26d57ae7191f..cd4ff218bfa794 100644 --- a/packages/edit-post/package.json +++ b/packages/edit-post/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/edit-post", - "version": "7.6.0-prerelease", + "version": "7.6.0", "description": "Edit Post module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/edit-site/package.json b/packages/edit-site/package.json index 7fcecbef5ce3bb..02f50e3294a7b9 100644 --- a/packages/edit-site/package.json +++ b/packages/edit-site/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/edit-site", - "version": "5.6.0-prerelease", + "version": "5.6.0", "description": "Edit Site Page module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/edit-widgets/package.json b/packages/edit-widgets/package.json index 1320febeb147e6..2cc103c4605742 100644 --- a/packages/edit-widgets/package.json +++ b/packages/edit-widgets/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/edit-widgets", - "version": "5.6.0-prerelease", + "version": "5.6.0", "description": "Widgets Page module for WordPress..", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/editor/package.json b/packages/editor/package.json index d045a6aa5bd80d..b06cf73b10e325 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/editor", - "version": "13.6.0-prerelease", + "version": "13.6.0", "description": "Enhanced block editor for WordPress posts.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/element/package.json b/packages/element/package.json index e0995c7b85e67f..1a456276c6813f 100644 --- a/packages/element/package.json +++ b/packages/element/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/element", - "version": "5.6.0-prerelease", + "version": "5.6.0", "description": "Element React module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/env/package.json b/packages/env/package.json index 8ad22dff6b1163..1644e55c7deca6 100644 --- a/packages/env/package.json +++ b/packages/env/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/env", - "version": "5.14.0-prerelease", + "version": "5.14.0", "description": "A zero-config, self contained local WordPress environment for development and testing.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/escape-html/package.json b/packages/escape-html/package.json index 302773e22da5b8..b8abf679b7df49 100644 --- a/packages/escape-html/package.json +++ b/packages/escape-html/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/escape-html", - "version": "2.29.0-prerelease", + "version": "2.29.0", "description": "Escape HTML utils.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 202168dd261354..4b06e571eef461 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/eslint-plugin", - "version": "14.2.0-prerelease", + "version": "14.2.0", "description": "ESLint plugin for WordPress development.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/format-library/package.json b/packages/format-library/package.json index 5b221bff0a047e..a11861ef9635a7 100644 --- a/packages/format-library/package.json +++ b/packages/format-library/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/format-library", - "version": "4.6.0-prerelease", + "version": "4.6.0", "description": "Format library for the WordPress editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/hooks/package.json b/packages/hooks/package.json index 607419859d5434..10468b5d5a5e68 100644 --- a/packages/hooks/package.json +++ b/packages/hooks/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/hooks", - "version": "3.29.0-prerelease", + "version": "3.29.0", "description": "WordPress hooks library.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/html-entities/package.json b/packages/html-entities/package.json index ea2e1f369627d8..34300a6ad31f94 100644 --- a/packages/html-entities/package.json +++ b/packages/html-entities/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/html-entities", - "version": "3.29.0-prerelease", + "version": "3.29.0", "description": "HTML entity utilities for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/i18n/package.json b/packages/i18n/package.json index e4da706505aa10..5c69e87d92dec5 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/i18n", - "version": "4.29.0-prerelease", + "version": "4.29.0", "description": "WordPress internationalization (i18n) library.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/icons/package.json b/packages/icons/package.json index 6941e0014e4571..c00706de61a37f 100644 --- a/packages/icons/package.json +++ b/packages/icons/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/icons", - "version": "9.20.0-prerelease", + "version": "9.20.0", "description": "WordPress Icons package, based on dashicon.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/interface/package.json b/packages/interface/package.json index dfd5aa30954dbd..e5662ed7657a92 100644 --- a/packages/interface/package.json +++ b/packages/interface/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/interface", - "version": "5.6.0-prerelease", + "version": "5.6.0", "description": "Interface module for WordPress. The package contains shared functionality across the modern JavaScript-based WordPress screens.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/is-shallow-equal/package.json b/packages/is-shallow-equal/package.json index e2865397142b3a..92afef2a12c241 100644 --- a/packages/is-shallow-equal/package.json +++ b/packages/is-shallow-equal/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/is-shallow-equal", - "version": "4.29.0-prerelease", + "version": "4.29.0", "description": "Test for shallow equality between two objects or arrays.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/jest-console/package.json b/packages/jest-console/package.json index 53eab0fdf132a4..97561fd7915b71 100644 --- a/packages/jest-console/package.json +++ b/packages/jest-console/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/jest-console", - "version": "7.0.0-prerelease", + "version": "7.0.0", "description": "Custom Jest matchers for the Console object.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/jest-preset-default/package.json b/packages/jest-preset-default/package.json index a1d08c7f16e071..d1546db68755d3 100644 --- a/packages/jest-preset-default/package.json +++ b/packages/jest-preset-default/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/jest-preset-default", - "version": "11.0.0-prerelease", + "version": "11.0.0", "description": "Default Jest preset for WordPress development.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/jest-puppeteer-axe/package.json b/packages/jest-puppeteer-axe/package.json index 937fa87f8ea3a9..aaf57585f73273 100644 --- a/packages/jest-puppeteer-axe/package.json +++ b/packages/jest-puppeteer-axe/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/jest-puppeteer-axe", - "version": "6.0.0-prerelease", + "version": "6.0.0", "description": "Axe API integration with Jest and Puppeteer.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/keyboard-shortcuts/package.json b/packages/keyboard-shortcuts/package.json index dfa9f07f89cb48..c68b68697e3667 100644 --- a/packages/keyboard-shortcuts/package.json +++ b/packages/keyboard-shortcuts/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/keyboard-shortcuts", - "version": "4.6.0-prerelease", + "version": "4.6.0", "description": "Handling keyboard shortcuts.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/keycodes/package.json b/packages/keycodes/package.json index 5019a12ab89b8d..3faf69a0629360 100644 --- a/packages/keycodes/package.json +++ b/packages/keycodes/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/keycodes", - "version": "3.29.0-prerelease", + "version": "3.29.0", "description": "Keycodes utilities for WordPress. Used to check for keyboard events across browsers/operating systems.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/lazy-import/package.json b/packages/lazy-import/package.json index b6227b3e191541..10dde616c80559 100644 --- a/packages/lazy-import/package.json +++ b/packages/lazy-import/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/lazy-import", - "version": "1.16.0-prerelease", + "version": "1.16.0", "description": "Lazily import a module, installing it automatically if missing.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/list-reusable-blocks/package.json b/packages/list-reusable-blocks/package.json index 698ae7548a7554..6bec74d7b83801 100644 --- a/packages/list-reusable-blocks/package.json +++ b/packages/list-reusable-blocks/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/list-reusable-blocks", - "version": "4.6.0-prerelease", + "version": "4.6.0", "description": "Adding Export/Import support to the reusable blocks listing.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/media-utils/package.json b/packages/media-utils/package.json index 3d65c793b157f4..eb1f24613b6543 100644 --- a/packages/media-utils/package.json +++ b/packages/media-utils/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/media-utils", - "version": "4.20.0-prerelease", + "version": "4.20.0", "description": "WordPress Media Upload Utils.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/notices/package.json b/packages/notices/package.json index eeb2e9d32f1756..2209b84bb34a26 100644 --- a/packages/notices/package.json +++ b/packages/notices/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/notices", - "version": "3.29.0-prerelease", + "version": "3.29.0", "description": "State management for notices.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/npm-package-json-lint-config/package.json b/packages/npm-package-json-lint-config/package.json index cb398dac24b327..882e654bbf4902 100644 --- a/packages/npm-package-json-lint-config/package.json +++ b/packages/npm-package-json-lint-config/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/npm-package-json-lint-config", - "version": "4.14.0-prerelease", + "version": "4.14.0", "description": "WordPress npm-package-json-lint shareable configuration.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/plugins/package.json b/packages/plugins/package.json index 9e894a0a0680ec..881e6fdf4530a0 100644 --- a/packages/plugins/package.json +++ b/packages/plugins/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/plugins", - "version": "5.6.0-prerelease", + "version": "5.6.0", "description": "Plugins module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/postcss-plugins-preset/package.json b/packages/postcss-plugins-preset/package.json index e9f3f2e916d51e..d807e51bb2aa4f 100644 --- a/packages/postcss-plugins-preset/package.json +++ b/packages/postcss-plugins-preset/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/postcss-plugins-preset", - "version": "4.13.0-prerelease", + "version": "4.13.0", "description": "PostCSS sharable plugins preset for WordPress development.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/postcss-themes/package.json b/packages/postcss-themes/package.json index 948046184a2ee5..b48d9d83eb8031 100644 --- a/packages/postcss-themes/package.json +++ b/packages/postcss-themes/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/postcss-themes", - "version": "5.12.0-prerelease", + "version": "5.12.0", "description": "PostCSS plugin to generate theme colors.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/preferences-persistence/package.json b/packages/preferences-persistence/package.json index bbe9774d1e20cd..00846517084e87 100644 --- a/packages/preferences-persistence/package.json +++ b/packages/preferences-persistence/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/preferences-persistence", - "version": "1.21.0-prerelease", + "version": "1.21.0", "description": "Persistence utilities for `wordpress/preferences`.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/preferences/package.json b/packages/preferences/package.json index 13631e4ecf15a5..a6ab3648a270bb 100644 --- a/packages/preferences/package.json +++ b/packages/preferences/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/preferences", - "version": "3.6.0-prerelease", + "version": "3.6.0", "description": "Utilities for managing WordPress preferences.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/prettier-config/package.json b/packages/prettier-config/package.json index 325988eadd2425..b03437d316d348 100644 --- a/packages/prettier-config/package.json +++ b/packages/prettier-config/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/prettier-config", - "version": "2.12.0-prerelease", + "version": "2.12.0", "description": "WordPress Prettier shared configuration.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/primitives/package.json b/packages/primitives/package.json index 3c7cea92669204..01d455201698db 100644 --- a/packages/primitives/package.json +++ b/packages/primitives/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/primitives", - "version": "3.27.0-prerelease", + "version": "3.27.0", "description": "WordPress cross-platform primitives.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/priority-queue/package.json b/packages/priority-queue/package.json index d655278a8ef2dd..ea152c422a6493 100644 --- a/packages/priority-queue/package.json +++ b/packages/priority-queue/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/priority-queue", - "version": "2.29.0-prerelease", + "version": "2.29.0", "description": "Generic browser priority queue.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/private-apis/package.json b/packages/private-apis/package.json index 51934f30af9ed0..8fb0f0d828b630 100644 --- a/packages/private-apis/package.json +++ b/packages/private-apis/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/private-apis", - "version": "0.11.0-prerelease", + "version": "0.11.0", "description": "Internal experimental APIs for WordPress core.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/project-management-automation/package.json b/packages/project-management-automation/package.json index cd2dda8a882af6..d9cb592ac4b2e5 100644 --- a/packages/project-management-automation/package.json +++ b/packages/project-management-automation/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/project-management-automation", - "version": "1.28.0-prerelease", + "version": "1.28.0", "description": "GitHub Action that implements various automation to assist with managing the Gutenberg GitHub repository.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/react-i18n/package.json b/packages/react-i18n/package.json index f4434ee3aed307..dd14a22c23a15b 100644 --- a/packages/react-i18n/package.json +++ b/packages/react-i18n/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/react-i18n", - "version": "3.27.0-prerelease", + "version": "3.27.0", "description": "React bindings for @wordpress/i18n.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/readable-js-assets-webpack-plugin/package.json b/packages/readable-js-assets-webpack-plugin/package.json index 9daee05837c0a1..2026cfa38e39ec 100644 --- a/packages/readable-js-assets-webpack-plugin/package.json +++ b/packages/readable-js-assets-webpack-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/readable-js-assets-webpack-plugin", - "version": "2.12.0-prerelease", + "version": "2.12.0", "description": "Generate a readable JS file for each JS asset.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/redux-routine/package.json b/packages/redux-routine/package.json index 25e04ea7b204fb..c679e00a7d55ff 100644 --- a/packages/redux-routine/package.json +++ b/packages/redux-routine/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/redux-routine", - "version": "4.29.0-prerelease", + "version": "4.29.0", "description": "Redux middleware for generator coroutines.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/reusable-blocks/package.json b/packages/reusable-blocks/package.json index 2e616f5f0fd376..b1fba090ccb7f4 100644 --- a/packages/reusable-blocks/package.json +++ b/packages/reusable-blocks/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/reusable-blocks", - "version": "4.6.0-prerelease", + "version": "4.6.0", "description": "Reusable blocks utilities.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/rich-text/package.json b/packages/rich-text/package.json index f51ae10bcda7fa..23fa4251f2f4fc 100644 --- a/packages/rich-text/package.json +++ b/packages/rich-text/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/rich-text", - "version": "6.6.0-prerelease", + "version": "6.6.0", "description": "Rich text value and manipulation API.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/scripts/package.json b/packages/scripts/package.json index de810311c89632..5c565a16e41058 100644 --- a/packages/scripts/package.json +++ b/packages/scripts/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/scripts", - "version": "26.0.0-prerelease", + "version": "26.0.0", "description": "Collection of reusable scripts for WordPress development.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/server-side-render/package.json b/packages/server-side-render/package.json index e099ac763701c6..12f9582febc470 100644 --- a/packages/server-side-render/package.json +++ b/packages/server-side-render/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/server-side-render", - "version": "4.6.0-prerelease", + "version": "4.6.0", "description": "The component used with WordPress to server-side render a preview of dynamic blocks to display in the editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/shortcode/package.json b/packages/shortcode/package.json index 7499df29102dc9..36de997fb9ed16 100644 --- a/packages/shortcode/package.json +++ b/packages/shortcode/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/shortcode", - "version": "3.29.0-prerelease", + "version": "3.29.0", "description": "Shortcode module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/style-engine/package.json b/packages/style-engine/package.json index 0bddfb983f4e5d..b8c01527e04ebc 100644 --- a/packages/style-engine/package.json +++ b/packages/style-engine/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/style-engine", - "version": "1.12.0-prerelease", + "version": "1.12.0", "description": "A suite of parsers and compilers for WordPress styles.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/stylelint-config/package.json b/packages/stylelint-config/package.json index 1e9ed7c2a658e3..31a8cf47d86bb6 100644 --- a/packages/stylelint-config/package.json +++ b/packages/stylelint-config/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/stylelint-config", - "version": "21.12.0-prerelease", + "version": "21.12.0", "description": "stylelint config for WordPress development.", "author": "The WordPress Contributors", "license": "MIT", diff --git a/packages/token-list/package.json b/packages/token-list/package.json index 1cc16fb61230c1..d802e3e96b78ac 100644 --- a/packages/token-list/package.json +++ b/packages/token-list/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/token-list", - "version": "2.29.0-prerelease", + "version": "2.29.0", "description": "Constructable, plain JavaScript DOMTokenList implementation, supporting non-browser runtimes.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/url/package.json b/packages/url/package.json index 315a80c1ca59f2..537a1a9426fa4f 100644 --- a/packages/url/package.json +++ b/packages/url/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/url", - "version": "3.30.0-prerelease", + "version": "3.30.0", "description": "WordPress URL utilities.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/viewport/package.json b/packages/viewport/package.json index 0eb4688b2b3b0e..44b5eb17e770dd 100644 --- a/packages/viewport/package.json +++ b/packages/viewport/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/viewport", - "version": "5.6.0-prerelease", + "version": "5.6.0", "description": "Viewport module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/warning/package.json b/packages/warning/package.json index ab44fd13eb26c1..f9c0e2a45b4dde 100644 --- a/packages/warning/package.json +++ b/packages/warning/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/warning", - "version": "2.29.0-prerelease", + "version": "2.29.0", "description": "Warning utility for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/widgets/package.json b/packages/widgets/package.json index 7188bfe21b6d9b..a3bfcb9c8d88fb 100644 --- a/packages/widgets/package.json +++ b/packages/widgets/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/widgets", - "version": "3.6.0-prerelease", + "version": "3.6.0", "description": "Functionality used by the widgets block editor in the Widgets screen and the Customizer.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/wordcount/package.json b/packages/wordcount/package.json index a1684b8ead274c..65e77d4c4fac44 100644 --- a/packages/wordcount/package.json +++ b/packages/wordcount/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/wordcount", - "version": "3.29.0-prerelease", + "version": "3.29.0", "description": "WordPress word count utility.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", From 4e343bd3896b913e0357c2ba7c473821432f1763 Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Wed, 15 Mar 2023 17:48:14 +0000 Subject: [PATCH 170/910] Update Changelog for 15.4.0-rc.1 --- changelog.txt | 321 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 321 insertions(+) diff --git a/changelog.txt b/changelog.txt index a0ce9ad1790f03..f5abba7bd8cb79 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,326 @@ == Changelog == += 15.4.0-rc.1 = + +## Changelog + +### Enhancements + +#### Site Editor + +- Adjust whileHover effect to be a bit subtler and less pronounced. ([48928](https://github.com/WordPress/gutenberg/pull/48928)) +- Go direct to edit from manage all templates list. ([48764](https://github.com/WordPress/gutenberg/pull/48764)) +- Move "Add Template"'s descriptions to tooltips. ([48710](https://github.com/WordPress/gutenberg/pull/48710)) +- Add descriptions to all panels in the Site Editor's dark side. ([48739](https://github.com/WordPress/gutenberg/pull/48739)) +- Add hover animation to site editor canvas. ([48575](https://github.com/WordPress/gutenberg/pull/48575)) +- Fix non-us spelling in sidebar. ([48976](https://github.com/WordPress/gutenberg/pull/48976)) +- Prevent the saving button from showing when renaming templates. ([48399](https://github.com/WordPress/gutenberg/pull/48399)) +- Navigation Sidebar: Change the logic about which navigation gets selected for the sidebar. ([48689](https://github.com/WordPress/gutenberg/pull/48689)) +- Add "Added by" description to template part navigation sidebar. ([48732](https://github.com/WordPress/gutenberg/pull/48732)) +- Add border radius to off canvas navigation menu items. ([48798](https://github.com/WordPress/gutenberg/pull/48798)) +- Add page details when viewing a specific page. ([48650](https://github.com/WordPress/gutenberg/pull/48650)) +- Duotone: Limit SVG filter output to used filters. ([48995](https://github.com/WordPress/gutenberg/pull/48995)) +- Hide navigation screen in site editor. ([49043](https://github.com/WordPress/gutenberg/pull/49043)) + +#### Block Library + +- Open convert to links modal on select of a page item. ([48723](https://github.com/WordPress/gutenberg/pull/48723)) +- Query Block: Add tests for `getValueFromObjectPath()` util. ([48956](https://github.com/WordPress/gutenberg/pull/48956)) +- Post Featured Image: Remove 16:10. ([48969](https://github.com/WordPress/gutenberg/pull/48969)) +- Cover: Add constrained/flow layout. ([45326](https://github.com/WordPress/gutenberg/pull/45326)) + +#### Components + +- FontSizePicker: Allow custom units. ([48468](https://github.com/WordPress/gutenberg/pull/48468)) +- `Navigator`: Disable initial animation. ([49062](https://github.com/WordPress/gutenberg/pull/49062)) +- Try: Update Tertiary Button appearance. ([48888](https://github.com/WordPress/gutenberg/pull/48888)) +- FormTokenField: Hide suggestions list on blur event if input value is invalid. ([48785](https://github.com/WordPress/gutenberg/pull/48785)) + +#### Design Tools + +- Block Supports: Add text columns (column count) to typography block supports. ([33587](https://github.com/WordPress/gutenberg/pull/33587)) +- Cover: Add text color block support. ([41572](https://github.com/WordPress/gutenberg/pull/41572)) + +#### Global Styles + +- Move the global styles provider to the app level component. ([49011](https://github.com/WordPress/gutenberg/pull/49011)) +- Add support for `:link` and `:Any-link` in `theme.json`. ([48634](https://github.com/WordPress/gutenberg/pull/48634)) +- Add compound class to layout wrapper for global spacing styles. ([47952](https://github.com/WordPress/gutenberg/pull/47952)) + +#### Block API + +- Block Deprecations: Provide extra data for isEligible check. ([48815](https://github.com/WordPress/gutenberg/pull/48815)) + +#### Post Editor + +- Provide static native editor help article slugs. ([48802](https://github.com/WordPress/gutenberg/pull/48802)) +- Try getting Post Content layout on server before editor loads. ([45299](https://github.com/WordPress/gutenberg/pull/45299)) + +#### Packages + +- Introduce prependHTTPS URL util. ([47648](https://github.com/WordPress/gutenberg/pull/47648)) + +### Bug Fixes + +#### Block Library + +- Embed Block: Fix Aspect Ratio Classes #29641. ([41141](https://github.com/WordPress/gutenberg/pull/41141)) +- Ensure aspect ratio is applied when Post Featured Image block is linked. ([48495](https://github.com/WordPress/gutenberg/pull/48495)) +- Fix PostContent initial render by waiting for the canEdit request. ([48642](https://github.com/WordPress/gutenberg/pull/48642)) +- Fix classic menu fallback race condition. ([48811](https://github.com/WordPress/gutenberg/pull/48811)) +- Fix navigation block off-canvas appender for empty menus. ([48907](https://github.com/WordPress/gutenberg/pull/48907)) +- Fixes extra UI in navigation block inspector. ([48679](https://github.com/WordPress/gutenberg/pull/48679)) +- Import Classic Menu using the menu name as the block menu title. ([48771](https://github.com/WordPress/gutenberg/pull/48771)) +- Navigation Link: Remove color generation code. ([48927](https://github.com/WordPress/gutenberg/pull/48927)) +- Navigation: Fix missing state for MenuControls. ([48921](https://github.com/WordPress/gutenberg/pull/48921)) +- Update missing translation from label. ([48760](https://github.com/WordPress/gutenberg/pull/48760)) +- Widget Importer: Fix Widget Group block imports. ([48669](https://github.com/WordPress/gutenberg/pull/48669)) +- Query Loop: Show variant patterns even if there are no patterns for the Query Loop block. ([48793](https://github.com/WordPress/gutenberg/pull/48793)) +- Comments: Fix 'sprintf requires more than 1 params' error. ([49054](https://github.com/WordPress/gutenberg/pull/49054)) +- Adjust Post Featured Image PanelBody label to "Settings". ([49076](https://github.com/WordPress/gutenberg/pull/49076)) +- Add help text to Gallery Image Size control. ([49074](https://github.com/WordPress/gutenberg/pull/49074)) +- Comments Block (Legacy): Update missing translation. ([48820](https://github.com/WordPress/gutenberg/pull/48820)) +- I18n of created Navigation menu title. ([48773](https://github.com/WordPress/gutenberg/pull/48773)) +- Make sure the directly inserted block in the Nav block is a Page link. ([48740](https://github.com/WordPress/gutenberg/pull/48740)) +- Navigation Link: Don't remove 'block_core_navigation_link_build_css_colors'. ([49064](https://github.com/WordPress/gutenberg/pull/49064)) +- Navigation: Don't save the level of the link in an attribute. ([48219](https://github.com/WordPress/gutenberg/pull/48219)) +- Refactor away state in Nav menu selector. ([45464](https://github.com/WordPress/gutenberg/pull/45464)) +- Revert: Navigation: Always create a fallback menu. ([48602](https://github.com/WordPress/gutenberg/pull/48602)) +- Tweak Latest Posts block PanelBody labels. ([49079](https://github.com/WordPress/gutenberg/pull/49079)) +- Tweak label for Latest Posts excerpt control. ([49077](https://github.com/WordPress/gutenberg/pull/49077)) +- [Page list Block] Show untitled pages on page list on the editor. ([48772](https://github.com/WordPress/gutenberg/pull/48772)) + +#### Site Editor + +- Don't offer Classic block as a recovery action when not registered. ([49051](https://github.com/WordPress/gutenberg/pull/49051)) +- Fix browser history when synchronising state with urls. ([48731](https://github.com/WordPress/gutenberg/pull/48731)) +- Fix lingering insertion point within template parts. ([48913](https://github.com/WordPress/gutenberg/pull/48913)) +- Fix template part actions in List View. ([48905](https://github.com/WordPress/gutenberg/pull/48905)) +- Fix text alignment in the Site Editor sidebar. ([48959](https://github.com/WordPress/gutenberg/pull/48959)) +- Fix typo in template parts description. ([48781](https://github.com/WordPress/gutenberg/pull/48781)) +- Fix browse mode descriptions margin. ([48778](https://github.com/WordPress/gutenberg/pull/48778)) +- Fix scrollbar in site editor. ([48822](https://github.com/WordPress/gutenberg/pull/48822)) +- Site Editor Navigation panel: Update appearance of non-link blocks. ([48933](https://github.com/WordPress/gutenberg/pull/48933)) +- Navigation sidebar shows a wrong submenu popover. ([48941](https://github.com/WordPress/gutenberg/pull/48941)) +- Show creation popover on empty page links in the navigation sidebar. ([48746](https://github.com/WordPress/gutenberg/pull/48746)) +- Site button metrics. ([48918](https://github.com/WordPress/gutenberg/pull/48918)) +- Remove actions from SidebarNavigationScreenWrapper. ([48935](https://github.com/WordPress/gutenberg/pull/48935)) +- Update template descriptions with more detail. ([48934](https://github.com/WordPress/gutenberg/pull/48934)) + +#### Global Styles + +- Fix typo: Use WP_Theme_JSON_Gutenberg instead of WP_Theme_JSON class name. ([48648](https://github.com/WordPress/gutenberg/pull/48648)) +- Fix: Crashes on getNodesWithSettings and getNodesWithStyles. ([49023](https://github.com/WordPress/gutenberg/pull/49023)) +- Fix: Global Styles crash in updateConfigWithSeparator when not block styles are passed. ([49045](https://github.com/WordPress/gutenberg/pull/49045)) +- Fix: Global Styles getNodesWithStyles expects an object with elements. ([49044](https://github.com/WordPress/gutenberg/pull/49044)) +- Fix: Global Styles getPresetsClasses crashes if no selector is passed. ([49024](https://github.com/WordPress/gutenberg/pull/49024)) +- Fix: Global styles forces a white background. ([49042](https://github.com/WordPress/gutenberg/pull/49042)) +- Style Book: Move iframe to root of content area to support styles that overflow block previews. ([48664](https://github.com/WordPress/gutenberg/pull/48664)) +- `WP_Theme_JSON`: Sync indirect properties changes from core. ([48646](https://github.com/WordPress/gutenberg/pull/48646)) + +#### Components + +- Fix HStack and VStack alignment prop. ([47914](https://github.com/WordPress/gutenberg/pull/47914)) +- ResizeTooltip: Use default.fontFamily on tooltip. ([48805](https://github.com/WordPress/gutenberg/pull/48805)) +- ResponsiveWrapper: Use aspect-ratio CSS prop and support SVG elements. ([48573](https://github.com/WordPress/gutenberg/pull/48573)) + +#### Accessibility + +- Make sure useFocusOnMount runs when all the children tabbable elements have mounted. ([42187](https://github.com/WordPress/gutenberg/pull/42187)) +- Manage selection on block sync. ([48979](https://github.com/WordPress/gutenberg/pull/48979)) + +#### Post Editor + +- Distraction Free Mode: Don't show the metaboxes. ([48947](https://github.com/WordPress/gutenberg/pull/48947)) +- Don't add Post Content layout styles to title in the post editor. ([48663](https://github.com/WordPress/gutenberg/pull/48663)) +- Fix animation and browser console error when returning from template edit mode. ([48930](https://github.com/WordPress/gutenberg/pull/48930)) + +#### Block Editor + +- LinkControl: Remove HTML from suggestion title before passing it to TextHighlight component. ([48685](https://github.com/WordPress/gutenberg/pull/48685)) +- Order initial block items in Navigation with PrivateInserter. ([48752](https://github.com/WordPress/gutenberg/pull/48752)) +- BlockInvalidWarning: Prefer `canInsertBlockType` and refactor to hooks. ([49052](https://github.com/WordPress/gutenberg/pull/49052)) +- Fix grouping actions in List View. ([48910](https://github.com/WordPress/gutenberg/pull/48910)) +- Fix typo in the media-categories component. ([49047](https://github.com/WordPress/gutenberg/pull/49047)) +- Custom link UI does appears outside canvas on the sidebar navigation. ([48633](https://github.com/WordPress/gutenberg/pull/48633)) +- Use proper color for block styles control. ([46684](https://github.com/WordPress/gutenberg/pull/46684)) +- Update Welcome Guide article links to avoid redirect. ([48582](https://github.com/WordPress/gutenberg/pull/48582)) +- Columns Block: Don't show the column count change UI when `templateLock` is `all`. ([48691](https://github.com/WordPress/gutenberg/pull/48691)) +- Remove border from quick inserter child elements. ([48794](https://github.com/WordPress/gutenberg/pull/48794)) + +#### Inspector Controls +- Fix settings tab active state border in block inspector. ([48945](https://github.com/WordPress/gutenberg/pull/48945)) + +#### Testing + +- Playwright Utils: Fix the 'publishPost' address locator. ([48729](https://github.com/WordPress/gutenberg/pull/48729)) + +#### CSS & Styling +- Fix duplication of block classname in feature selectors for style variations. ([48662](https://github.com/WordPress/gutenberg/pull/48662)) + +#### Experimental + +- Fix KSES filter for un-prettified filters. ([49004](https://github.com/WordPress/gutenberg/pull/49004)) + +#### Packages + +- Rich text: Fix range equality checks for Safari. ([48733](https://github.com/WordPress/gutenberg/pull/48733)) +- Preferences Modal: Fix double focus outline in tab item. ([48996](https://github.com/WordPress/gutenberg/pull/48996)) + +#### Tools + +- Scripts: Fix `render.php` isn't copied in Windows OS. ([48735](https://github.com/WordPress/gutenberg/pull/48735)) + +#### Mobile + +- Mobile - Fix parsing of CSS units for null matched values. ([48484](https://github.com/WordPress/gutenberg/pull/48484)) + +### Performance + +#### Block Editor + +- Rich text: useAnchor: Remove value dependency. ([48715](https://github.com/WordPress/gutenberg/pull/48715)) + +#### Post Editor + +- Lodash: Refactor away from `_.kebabCase()` in `EditorHelpTopics`. ([48776](https://github.com/WordPress/gutenberg/pull/48776)) +- Lodash: Refactor away from `edit-post` package. ([48786](https://github.com/WordPress/gutenberg/pull/48786)) + +#### Site Editor + +- Improve the Navigation panel's menu query. ([48908](https://github.com/WordPress/gutenberg/pull/48908)) +- Improve Site Editor performance tests. ([48138](https://github.com/WordPress/gutenberg/pull/48138)) + +#### Testing + +- Lodash: Remove from e2e-tests package. ([48775](https://github.com/WordPress/gutenberg/pull/48775)) + +#### Themes + +- Fix: Incorrect selector generated by `append_to_selector` method. ([48759](https://github.com/WordPress/gutenberg/pull/48759)) + +#### Block Library + +- Lodash: Remove `_.get()` from various blocks. ([48491](https://github.com/WordPress/gutenberg/pull/48491)) + +#### Data Layer + +- Lodash: Refactor away from `_.set()` in core-data. ([48784](https://github.com/WordPress/gutenberg/pull/48784)) + +#### GitHub Actions + +- Prefer committer over author date for perf results timestamp. ([48673](https://github.com/WordPress/gutenberg/pull/48673)) + +### Documentation + +- Add links to hook documentation in curation doc. ([48653](https://github.com/WordPress/gutenberg/pull/48653)) +- Add missing playwright end-to-end documentation to toc.json. ([48447](https://github.com/WordPress/gutenberg/pull/48447)) +- Adding examples of how to programmatically remove the panels in Document sidebar. ([48895](https://github.com/WordPress/gutenberg/pull/48895)) +- Adds link to post on the developer blog to the deprecation page. ([49069](https://github.com/WordPress/gutenberg/pull/49069)) +- Add position: Sticky to the Opt-in into UI controls appearanceTools section. ([48763](https://github.com/WordPress/gutenberg/pull/48763)) +- Fix broken Lerna documentation link. ([48890](https://github.com/WordPress/gutenberg/pull/48890)) +- Table of styles keys with since versions. ([48265](https://github.com/WordPress/gutenberg/pull/48265)) +- Fix URL mismatch. ([48931](https://github.com/WordPress/gutenberg/pull/48931)) +- Theme JSON schema: Add sticky position to settings, minHeight to styles. ([48948](https://github.com/WordPress/gutenberg/pull/48948)) +- Update the end-to-end tests documentation. ([48951](https://github.com/WordPress/gutenberg/pull/48951)) +- jest-preset-default: Update README to reflect current status. ([48925](https://github.com/WordPress/gutenberg/pull/48925)) + +### Code Quality + +#### Components + +- Autocomplete: Refactor to TypeScript. ([47751](https://github.com/WordPress/gutenberg/pull/47751)) +- Navigation: Refactor to TypeScript. ([48742](https://github.com/WordPress/gutenberg/pull/48742)) +- SelectControl: Improve prop types for single vs multiple selection. ([47390](https://github.com/WordPress/gutenberg/pull/47390)) +- `DimensionControl(Experimental)`: Refactor to TypeScript. ([47351](https://github.com/WordPress/gutenberg/pull/47351)) +- `Guide`: Refactor to TypeScript. ([47493](https://github.com/WordPress/gutenberg/pull/47493)) +- `Icon`: Refactor tests to TypeScript. ([49066](https://github.com/WordPress/gutenberg/pull/49066)) +- `PaletteEdit`: Refactor away from `lodash.kebabCase`. ([48637](https://github.com/WordPress/gutenberg/pull/48637)) +- `QueryControls`: Refactor away from `lodash.groupBy`. ([48779](https://github.com/WordPress/gutenberg/pull/48779)) +- components/utils/font: Refactor away from lodash `.get`. ([48629](https://github.com/WordPress/gutenberg/pull/48629)) +- remove lodash from `context/getStyledClassName`:. ([48688](https://github.com/WordPress/gutenberg/pull/48688)) +- withSpokenMessages: Change js files to typescript. ([48163](https://github.com/WordPress/gutenberg/pull/48163)) + +#### Block Library + +- Add Nav block files to those triggering error for exhaustive deps. ([48821](https://github.com/WordPress/gutenberg/pull/48821)) +- Fix Nav block exhaustive deps warnings. ([48680](https://github.com/WordPress/gutenberg/pull/48680)) +- Media Text: Refactored constants to it's designated file. ([48480](https://github.com/WordPress/gutenberg/pull/48480)) +- Navigation: Simplify the method for finding the fallback menu. ([48916](https://github.com/WordPress/gutenberg/pull/48916)) +- Duotone.php code cleanup. ([48607](https://github.com/WordPress/gutenberg/pull/48607)) +- Revert "Duotone: Limit SVG filter output to used filters". ([49102](https://github.com/WordPress/gutenberg/pull/49102)) + +#### Block Editor + +- Inserter: Remove outer scope values dependencies. ([48961](https://github.com/WordPress/gutenberg/pull/48961)) +- Inserter: Remove unnecessary dependency 'delayedFilterValue'. ([48960](https://github.com/WordPress/gutenberg/pull/48960)) +- Remove unused CSS from LinkControl Apply button. ([48431](https://github.com/WordPress/gutenberg/pull/48431)) +- Custom Classname block support: Update code comments to remove reference to anchor id. ([48709](https://github.com/WordPress/gutenberg/pull/48709)) +- List View: Remove unused selector from the 'useBlockSelection' hook. ([48984](https://github.com/WordPress/gutenberg/pull/48984)) +- Renames parent selection boolean param and improves documentation. ([48677](https://github.com/WordPress/gutenberg/pull/48677)) +- Tests: Cleanup unnecessary jest timers setup. ([49030](https://github.com/WordPress/gutenberg/pull/49030)) +- Avoid declaring a function inside another function. ([49049](https://github.com/WordPress/gutenberg/pull/49049)) + +#### Global Styles + +- Theme JSON: Clarify status of fixed position opt-in in appearance tools. ([48660](https://github.com/WordPress/gutenberg/pull/48660)) +- Extract a BorderPanel component as a reusable component between Global Styles and Block Inspector. ([48636](https://github.com/WordPress/gutenberg/pull/48636)) + +#### Data Layer + +- Data: Use real timers for private APIs tests. ([49029](https://github.com/WordPress/gutenberg/pull/49029)) + +#### Packages + +- Preferences: Remove `types` field from `package.json`. ([49053](https://github.com/WordPress/gutenberg/pull/49053)) +- Upgrade `typescript` to 4.9.5. ([48299](https://github.com/WordPress/gutenberg/pull/48299)) +- Compose: Remove useAsyncList from mobile exports. ([48241](https://github.com/WordPress/gutenberg/pull/48241)) +- Animation: Refactor to TypeScript. ([47042](https://github.com/WordPress/gutenberg/pull/47042)) +- PanelBody: Convert to TypeScript. ([47702](https://github.com/WordPress/gutenberg/pull/47702)) +- Refactor ToolbarContext to TS. ([49002](https://github.com/WordPress/gutenberg/pull/49002)) +- Refactor/toolbar button component to typescript. ([47750](https://github.com/WordPress/gutenberg/pull/47750)) +- `PaletteEdit`: Convert to TypeScript. ([47764](https://github.com/WordPress/gutenberg/pull/47764)) +- navigateRegions: Convert to TypeScript. ([48632](https://github.com/WordPress/gutenberg/pull/48632)) +- withFallbackStyles: Convert to TypeScript. ([48720](https://github.com/WordPress/gutenberg/pull/48720)) +- withFilters: Convert to TypeScript. ([48721](https://github.com/WordPress/gutenberg/pull/48721)) +- withFocusReturn: Convert to TypeScript. ([48748](https://github.com/WordPress/gutenberg/pull/48748)) +- withNotices: Convert to TypeScript. ([49088](https://github.com/WordPress/gutenberg/pull/49088)) +- Packages: Remove completely two deprecated webpack plugins. ([48770](https://github.com/WordPress/gutenberg/pull/48770)) + +### Tools + +- Env: Fix typo / grammar README.md. ([48952](https://github.com/WordPress/gutenberg/pull/48952)) +- ci: Add Rich Text code owner. ([48727](https://github.com/WordPress/gutenberg/pull/48727)) + +#### Testing + +- Add `pageUtils.pressKeys` to playwright utils. ([49009](https://github.com/WordPress/gutenberg/pull/49009)) +- Add artifacts upload for the performance tests. ([48243](https://github.com/WordPress/gutenberg/pull/48243)) +- Fix flaky block hierarchy navigation test by better inserter selection. ([48780](https://github.com/WordPress/gutenberg/pull/48780)) +- Migrate multi-block selection end-to-end tests to Playwright. ([48035](https://github.com/WordPress/gutenberg/pull/48035)) +- Navigation block end-to-end tests: Default to my most recently created menu. ([48132](https://github.com/WordPress/gutenberg/pull/48132)) +- Upgrade Jest from 27 to 29.5.0. ([47388](https://github.com/WordPress/gutenberg/pull/47388)) +- Duotone: Style Engine: Add unit test and associated refactoring. ([49033](https://github.com/WordPress/gutenberg/pull/49033)) + +## First time contributors + +The following PRs were merged by first time contributors: + +- @anver: Env: Fix typo / grammar README.md. ([48952](https://github.com/WordPress/gutenberg/pull/48952)) +- @bhavz-10: withSpokenMessages: Change js files to typescript. ([48163](https://github.com/WordPress/gutenberg/pull/48163)) +- @krishneup: Update missing translation from label. ([48760](https://github.com/WordPress/gutenberg/pull/48760)) +- @mike-day: Refactor/toolbar button component to typescript. ([47750](https://github.com/WordPress/gutenberg/pull/47750)) +- @shvlv: FormTokenField: Hide suggestions list on blur event if input value is invalid. ([48785](https://github.com/WordPress/gutenberg/pull/48785)) +- @TylerB24890: Embed Block: Fix Aspect Ratio Classes #29641. ([41141](https://github.com/WordPress/gutenberg/pull/41141)) + + +## Contributors + +The following contributors merged PRs in this release: + +@aaronrobertshaw @afercia @ajlende @andrewserong @anver @aristath @bhavz-10 @brookewp @chad1008 @ciampo @DaisyOlsen @dcalhoun @draganescu @ellatrix @fabiankaegy @felixarntz @flootr @fluiddot @geriux @getdave @glendaviesnz @gvgvgvijayan @gziolo @hideokamoto @jameskoster @jasmussen @jeryj @jorgefilipecosta @jsnajdr @kevin940726 @kienstra @krishneup @MaggieCabrera @Mamaduka @mburridge @mike-day @mirka @mtias @ndiego @ntsekouras @oandregal @Rahmon @richtabor @ryanwelcher @SavPhill @scruffian @shvlv @SiobhyB @swissspidy @t-hamano @talldan @tellthemachines @tomdevisser @TylerB24890 @tyxla @WunderBart @youknowriad + + = 15.3.1 = From fe5b8a76252591e37ddcf08cf094e746fbfd676f Mon Sep 17 00:00:00 2001 From: Bart Kalisz Date: Wed, 15 Mar 2023 23:32:11 +0100 Subject: [PATCH 171/910] Fix release perf job + running locally via CLI (#49068) --- bin/plugin/commands/performance.js | 20 ++++++++++++++++---- bin/plugin/lib/utils.js | 1 + 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/bin/plugin/commands/performance.js b/bin/plugin/commands/performance.js index 75ab95a4cc4a10..0d91947da80212 100644 --- a/bin/plugin/commands/performance.js +++ b/bin/plugin/commands/performance.js @@ -3,7 +3,7 @@ */ const fs = require( 'fs' ); const path = require( 'path' ); -const { mapValues, kebabCase } = require( 'lodash' ); +const { mapValues } = require( 'lodash' ); const SimpleGit = require( 'simple-git' ); /** @@ -83,6 +83,17 @@ const config = require( '../config' ); * @property {number=} maxListViewOpen Max time to open list view. */ +/** + * Sanitizes branch name to be used in a path or a filename. + * + * @param {string} branch + * + * @return {string} Sanitized branch name. + */ +function sanitizeBranchName( branch ) { + return branch.replace( /[^a-zA-Z0-9-]/g, '-' ); +} + /** * Computes the average number from an array numbers. * @@ -299,8 +310,8 @@ async function runPerformanceTests( branches, options ) { const branchDirectories = {}; for ( const branch of branches ) { log( ` >> Branch: ${ branch }` ); - const environmentDirectory = - rootDirectory + '/envs/' + kebabCase( branch ); + const sanitizedBranch = sanitizeBranchName( branch ); + const environmentDirectory = rootDirectory + '/envs/' + sanitizedBranch; // @ts-ignore branchDirectories[ branch ] = environmentDirectory; const buildPath = `${ environmentDirectory }/plugin`; @@ -401,7 +412,8 @@ async function runPerformanceTests( branches, options ) { for ( let i = 0; i < TEST_ROUNDS; i++ ) { rawResults[ i ] = {}; for ( const branch of branches ) { - const runKey = `${ branch }_${ testSuite }_run-${ i }`; + const sanitizedBranch = sanitizeBranchName( branch ); + const runKey = `${ sanitizedBranch }_${ testSuite }_run-${ i }`; // @ts-ignore const environmentDirectory = branchDirectories[ branch ]; log( ` >> Branch: ${ branch }, Suite: ${ testSuite }` ); diff --git a/bin/plugin/lib/utils.js b/bin/plugin/lib/utils.js index 4a75437a60694e..c50094321710ca 100644 --- a/bin/plugin/lib/utils.js +++ b/bin/plugin/lib/utils.js @@ -33,6 +33,7 @@ function runShellScript( script, cwd, env = {} ) { NO_CHECKS: 'true', PATH: process.env.PATH, HOME: process.env.HOME, + USER: process.env.USER, ...env, }, }, From f9950bed9b9c81d00c834e4be787ebd577bf9363 Mon Sep 17 00:00:00 2001 From: Bart Kalisz Date: Wed, 15 Mar 2023 23:36:51 +0100 Subject: [PATCH 172/910] Install perf testing themes in the correct wp-env context (#49063) --- .github/workflows/performance.yml | 7 ------- bin/plugin/utils/.wp-env.performance.json | 6 +++++- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/.github/workflows/performance.yml b/.github/workflows/performance.yml index ed2dac34ddc939..fa388e76d32f15 100644 --- a/.github/workflows/performance.yml +++ b/.github/workflows/performance.yml @@ -42,13 +42,6 @@ jobs: run: | npm ci - - name: Install specific versions of the themes used in tests - run: | - npm run wp-env start - npm run wp-env -- run tests-cli "wp theme update twentytwentyone --version=1.7" - npm run wp-env -- run tests-cli "wp theme update twentytwentythree --version=1.0" - npm run wp-env stop - - name: Compare performance with trunk if: github.event_name == 'pull_request' run: ./bin/plugin/cli.js perf $GITHUB_SHA trunk --tests-branch $GITHUB_SHA diff --git a/bin/plugin/utils/.wp-env.performance.json b/bin/plugin/utils/.wp-env.performance.json index 112c2684f64a48..3f80b794ddddd6 100644 --- a/bin/plugin/utils/.wp-env.performance.json +++ b/bin/plugin/utils/.wp-env.performance.json @@ -1,7 +1,11 @@ { "core": "WordPress/WordPress", "plugins": [ "./plugin" ], - "themes": [ "../../tests/test/emptytheme" ], + "themes": [ + "../../tests/test/emptytheme", + "https://downloads.wordpress.org/theme/twentytwentyone.1.7.zip", + "https://downloads.wordpress.org/theme/twentytwentythree.1.0.zip" + ], "env": { "tests": { "mappings": { From cc11957c3021ce084cbdddde3a84a2a4763aba3e Mon Sep 17 00:00:00 2001 From: Kai Hao Date: Thu, 16 Mar 2023 10:49:37 +0800 Subject: [PATCH 173/910] Speed up `npm ci` by caching `node_modules` (#45932) * Try custom setup-node composite action for aggressive caching * Add comment about GHA bug * Try without rebuild and ignore-scripts * Include npm version to the cache key * Try reverting the change of graceful-fs * Update step name * Code review * Add some comments --- .github/setup-node/action.yml | 46 +++++++++++++++++++ .github/workflows/build-plugin-zip.yml | 4 +- .github/workflows/bundle-size.yml | 2 +- .github/workflows/create-block.yml | 13 ++---- .github/workflows/end2end-test.yml | 39 +++++----------- .github/workflows/performance.yml | 11 +---- .github/workflows/publish-npm-packages.yml | 2 +- .github/workflows/pull-request-automation.yml | 2 +- .github/workflows/rnmobile-android-runner.yml | 9 +--- .github/workflows/rnmobile-ios-runner.yml | 9 +--- .github/workflows/static-checks.yml | 2 +- .github/workflows/storybook-pages.yml | 10 +--- .github/workflows/unit-test.yml | 37 +++++---------- 13 files changed, 89 insertions(+), 97 deletions(-) create mode 100644 .github/setup-node/action.yml diff --git a/.github/setup-node/action.yml b/.github/setup-node/action.yml new file mode 100644 index 00000000000000..22cb81618a1efb --- /dev/null +++ b/.github/setup-node/action.yml @@ -0,0 +1,46 @@ +name: 'Setup Node.js and install npm dependencies' +description: 'Configure Node.js and install npm dependencies while managing all aspects of caching.' +inputs: + node-version: + description: 'Optional. The Node.js version to use. When not specified, the version specified in .nvmrc will be used.' + required: false + type: string + +runs: + using: 'composite' + steps: + - name: Use desired version of Node.js + uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 + with: + node-version-file: '.nvmrc' + node-version: ${{ inputs.node-version }} + cache: npm + + - name: Get Node.js and npm version + id: node-version + run: | + echo "NODE_VERSION=$(node -v)" >> $GITHUB_OUTPUT + shell: bash + + - name: Cache node_modules + id: cache-node_modules + uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 + with: + path: '**/node_modules' + key: node_modules-${{ runner.os }}-${{ steps.node-version.outputs.NODE_VERSION }}-${{ hashFiles('package-lock.json') }} + + - name: Install npm dependencies + if: ${{ steps.cache-node_modules.outputs.cache-hit != 'true' }} + run: npm ci + shell: bash + + # On cache hit, we run the post-install script to match the native `npm ci` behavior. + # An example of this is to patch `node_modules` using patch-package. + - name: Post-install + if: ${{ steps.cache-node_modules.outputs.cache-hit == 'true' }} + run: | + # Run the post-install script for the root project. + npm run postinstall + # Run the post-install scripts for workspaces. + npx lerna run postinstall + shell: bash diff --git a/.github/workflows/build-plugin-zip.yml b/.github/workflows/build-plugin-zip.yml index 6e915d0ffddd27..9e30cc1c1cfc67 100644 --- a/.github/workflows/build-plugin-zip.yml +++ b/.github/workflows/build-plugin-zip.yml @@ -168,7 +168,7 @@ jobs: with: ref: ${{ needs.bump-version.outputs.release_branch || github.ref }} - - name: Use desired version of NodeJS + - name: Use desired version of Node.js uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 with: node-version-file: '.nvmrc' @@ -326,7 +326,7 @@ jobs: git config user.name "Gutenberg Repository Automation" git config user.email gutenberg@wordpress.org - - name: Setup Node (for CLI) + - name: Setup Node.js uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 with: node-version-file: 'main/.nvmrc' diff --git a/.github/workflows/bundle-size.yml b/.github/workflows/bundle-size.yml index 9ae0116ef1c79b..ea1facacdf236a 100644 --- a/.github/workflows/bundle-size.yml +++ b/.github/workflows/bundle-size.yml @@ -41,7 +41,7 @@ jobs: with: fetch-depth: 1 - - name: Use desired version of NodeJS + - name: Use desired version of Node.js uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 with: node-version-file: '.nvmrc' diff --git a/.github/workflows/create-block.yml b/.github/workflows/create-block.yml index 304052d4297b87..d0eae662c90978 100644 --- a/.github/workflows/create-block.yml +++ b/.github/workflows/create-block.yml @@ -20,20 +20,17 @@ jobs: strategy: fail-fast: false matrix: - node: [14] + node: ['14'] os: [macos-latest, ubuntu-latest, windows-latest] steps: - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 - - name: Use desired version of NodeJS - uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 + - name: Setup Node.js and install dependencies + uses: ./.github/setup-node with: node-version: ${{ matrix.node }} - cache: npm - - name: npm install, build, format and lint + - name: Create block shell: bash - run: | - npm ci - bash ./bin/test-create-block.sh + run: bash ./bin/test-create-block.sh diff --git a/.github/workflows/end2end-test.yml b/.github/workflows/end2end-test.yml index 24256e4266a46e..9bc1224fa11e7c 100644 --- a/.github/workflows/end2end-test.yml +++ b/.github/workflows/end2end-test.yml @@ -29,16 +29,11 @@ jobs: steps: - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 - - name: Use desired version of NodeJS - uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 - with: - node-version-file: '.nvmrc' - cache: npm + - name: Setup Node.js and install dependencies + uses: ./.github/setup-node - - name: Npm install and build - run: | - npm ci - npm run build + - name: Npm build + run: npm run build - name: Install WordPress run: | @@ -78,16 +73,11 @@ jobs: steps: - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 - - name: Use desired version of NodeJS - uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 - with: - node-version-file: '.nvmrc' - cache: npm + - name: Setup Node.js and install dependencies + uses: ./.github/setup-node - - name: Npm install and build - run: | - npm ci - npm run build + - name: Npm build + run: npm run build - name: Install Playwright dependencies run: | @@ -137,19 +127,14 @@ jobs: name: flaky-tests-report path: flaky-tests - - name: Use desired version of NodeJS + - name: Setup Node.js and install dependencies if: ${{ steps.download_artifact.outcome == 'success' }} - uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 - with: - node-version-file: '.nvmrc' - cache: npm + uses: ./.github/setup-node - - name: Npm install and build + - name: Npm build if: ${{ steps.download_artifact.outcome == 'success' }} # TODO: We don't have to build the entire project, just the action itself. - run: | - npm ci - npm run build:packages + run: npm run build:packages - name: Report flaky tests if: ${{ steps.download_artifact.outcome == 'success' }} diff --git a/.github/workflows/performance.yml b/.github/workflows/performance.yml index fa388e76d32f15..18f11e55b3de88 100644 --- a/.github/workflows/performance.yml +++ b/.github/workflows/performance.yml @@ -32,15 +32,8 @@ jobs: steps: - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 - - name: Use desired version of NodeJS - uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 - with: - node-version-file: '.nvmrc' - cache: npm - - - name: Npm install - run: | - npm ci + - name: Setup Node.js and install dependencies + uses: ./.github/setup-node - name: Compare performance with trunk if: github.event_name == 'pull_request' diff --git a/.github/workflows/publish-npm-packages.yml b/.github/workflows/publish-npm-packages.yml index 6568d46989fe65..bc6782ae3ed8f9 100644 --- a/.github/workflows/publish-npm-packages.yml +++ b/.github/workflows/publish-npm-packages.yml @@ -49,7 +49,7 @@ jobs: git config user.name "Gutenberg Repository Automation" git config user.email gutenberg@wordpress.org - - name: Setup Node (for CLI) + - name: Setup Node.js uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 with: node-version-file: 'main/.nvmrc' diff --git a/.github/workflows/pull-request-automation.yml b/.github/workflows/pull-request-automation.yml index 5d38f7965f7b10..b39a54f084c602 100644 --- a/.github/workflows/pull-request-automation.yml +++ b/.github/workflows/pull-request-automation.yml @@ -19,7 +19,7 @@ jobs: with: ref: trunk - - name: Use desired version of NodeJS + - name: Use desired version of Node.js uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 with: node-version: ${{ matrix.node }} diff --git a/.github/workflows/rnmobile-android-runner.yml b/.github/workflows/rnmobile-android-runner.yml index 0f1474c824a208..55d29accbcb8fb 100644 --- a/.github/workflows/rnmobile-android-runner.yml +++ b/.github/workflows/rnmobile-android-runner.yml @@ -31,13 +31,8 @@ jobs: distribution: 'temurin' java-version: '11' - - name: Use desired version of NodeJS - uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 - with: - node-version-file: '.nvmrc' - cache: npm - - - run: npm ci + - name: Setup Node.js and install dependencies + uses: ./.github/setup-node - name: Gradle cache uses: gradle/gradle-build-action@6095a76664413da4c8c134ee32e8a8ae900f0f1f # v2.4.0 diff --git a/.github/workflows/rnmobile-ios-runner.yml b/.github/workflows/rnmobile-ios-runner.yml index 933b4184957d11..1a4b1db788371b 100644 --- a/.github/workflows/rnmobile-ios-runner.yml +++ b/.github/workflows/rnmobile-ios-runner.yml @@ -25,13 +25,8 @@ jobs: steps: - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 - - name: Use desired version of NodeJS - uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 - with: - node-version-file: '.nvmrc' - cache: npm - - - run: npm ci + - name: Setup Node.js and install dependencies + uses: ./.github/setup-node - name: Prepare build cache key run: find package-lock.json packages/react-native-editor/ios packages/react-native-aztec/ios packages/react-native-bridge/ios -type f -print0 | sort -z | xargs -0 shasum | tee ios-checksums.txt diff --git a/.github/workflows/static-checks.yml b/.github/workflows/static-checks.yml index 00002aaf52f191..8935ef5e393258 100644 --- a/.github/workflows/static-checks.yml +++ b/.github/workflows/static-checks.yml @@ -24,7 +24,7 @@ jobs: steps: - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 - - name: Use desired version of NodeJS + - name: Use desired version of Node.js uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 with: node-version-file: '.nvmrc' diff --git a/.github/workflows/storybook-pages.yml b/.github/workflows/storybook-pages.yml index 616fad4e55b5aa..ad49e3274f9ba8 100644 --- a/.github/workflows/storybook-pages.yml +++ b/.github/workflows/storybook-pages.yml @@ -16,14 +16,8 @@ jobs: with: ref: trunk - - name: Use desired version of NodeJS - uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 - with: - node-version-file: '.nvmrc' - cache: npm - - - name: Install Dependencies - run: npm ci + - name: Setup Node.js and install dependencies + uses: ./.github/setup-node - name: Build Storybook run: npm run storybook:build diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index ca9adb75ebca83..a78440bf5ef52e 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -33,19 +33,16 @@ jobs: steps: - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 - - name: Use desired version of NodeJS - uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 + - name: Setup Node.js and install dependencies + uses: ./.github/setup-node with: node-version: ${{ matrix.node }} - cache: npm - - name: Npm install and build + - name: Npm build # It's not necessary to run the full build, since Jest can interpret # source files with `babel-jest`. Some packages have their own custom # build tasks, however. These must be run. - run: | - npm ci - npx lerna run build + run: npx lerna run build - name: Running the tests run: npm run test:unit -- --ci --maxWorkers=2 --cacheDirectory="$HOME/.jest-cache" @@ -79,11 +76,8 @@ jobs: steps: - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 - - name: Set up Node.js - uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 - with: - node-version-file: '.nvmrc' - cache: npm + - name: Setup Node.js and install dependencies + uses: ./.github/setup-node ## # This allows Composer dependencies to be installed using a single step. @@ -116,10 +110,8 @@ jobs: with: custom-cache-suffix: $(/bin/date -u --date='last Mon' "+%F") - - name: Install npm dependencies - run: | - npm ci - npm run build + - name: Npm build + run: npm run build - name: Docker debug information run: | @@ -235,19 +227,14 @@ jobs: steps: - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 - - name: Use desired version of NodeJS - uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 - with: - node-version-file: '.nvmrc' - cache: npm + - name: Setup Node.js and install dependencies + uses: ./.github/setup-node - - name: Npm install and build + - name: Npm build # It's not necessary to run the full build, since Jest can interpret # source files with `babel-jest`. Some packages have their own custom # build tasks, however. These must be run. - run: | - npm ci - npx lerna run build + run: npx lerna run build - name: Running the tests run: npm run native test -- --ci --maxWorkers=2 --cacheDirectory="$HOME/.jest-cache" From ced52228a39ba68fea86c1e3e8d36aa7947685db Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Thu, 16 Mar 2023 15:58:45 +1000 Subject: [PATCH 174/910] Media & Text: Switch default alignment to `none` (#48404) --- .../__snapshots__/transforms.native.js.snap | 4 +- .../__snapshots__/transforms.native.js.snap | 2 +- .../block-library/src/media-text/block.json | 2 +- .../src/media-text/deprecated.js | 238 +++++++++++++++++- .../__snapshots__/transforms.native.js.snap | 8 +- .../__snapshots__/transforms.native.js.snap | 2 +- .../fixtures/blocks/core__media-text.html | 2 +- .../fixtures/blocks/core__media-text.json | 2 +- .../blocks/core__media-text.parsed.json | 4 +- .../blocks/core__media-text.serialized.html | 2 +- ..._media-text__deprecated-v5.serialized.html | 2 +- ...ext__deprecated-v6-wide-default-align.html | 12 + ...ext__deprecated-v6-wide-default-align.json | 28 +++ ...precated-v6-wide-default-align.parsed.json | 28 +++ ...ated-v6-wide-default-align.serialized.html | 5 + ...a-text__image-alt-no-align.serialized.html | 2 +- ...t__image-fill-no-focal-point-selected.json | 2 +- ...ll-no-focal-point-selected.serialized.html | 2 +- ..._image-fill-with-focal-point-selected.json | 2 +- ...-with-focal-point-selected.serialized.html | 2 +- ...ore__media-text__is-stacked-on-mobile.json | 4 +- ...text__is-stacked-on-mobile.serialized.html | 2 +- ...re__media-text__vertical-align-bottom.json | 2 +- ...ext__vertical-align-bottom.serialized.html | 2 +- .../blocks/core__media-text__video.json | 4 +- .../core__media-text__video.serialized.html | 2 +- 26 files changed, 336 insertions(+), 31 deletions(-) create mode 100644 test/integration/fixtures/blocks/core__media-text__deprecated-v6-wide-default-align.html create mode 100644 test/integration/fixtures/blocks/core__media-text__deprecated-v6-wide-default-align.json create mode 100644 test/integration/fixtures/blocks/core__media-text__deprecated-v6-wide-default-align.parsed.json create mode 100644 test/integration/fixtures/blocks/core__media-text__deprecated-v6-wide-default-align.serialized.html diff --git a/packages/block-library/src/cover/test/__snapshots__/transforms.native.js.snap b/packages/block-library/src/cover/test/__snapshots__/transforms.native.js.snap index 252edf24172231..dc4ba0fbb2b1f8 100644 --- a/packages/block-library/src/cover/test/__snapshots__/transforms.native.js.snap +++ b/packages/block-library/src/cover/test/__snapshots__/transforms.native.js.snap @@ -30,7 +30,7 @@ exports[`Cover block transformations with Image to Image block 1`] = ` exports[`Cover block transformations with Image to Media & Text block 1`] = ` " -
+

Cool cover

" @@ -60,7 +60,7 @@ exports[`Cover block transformations with Video to Group block 1`] = ` exports[`Cover block transformations with Video to Media & Text block 1`] = ` " -
+

Cool cover

" diff --git a/packages/block-library/src/image/test/__snapshots__/transforms.native.js.snap b/packages/block-library/src/image/test/__snapshots__/transforms.native.js.snap index 61c8de02bd03a2..56a5fe3c259120 100644 --- a/packages/block-library/src/image/test/__snapshots__/transforms.native.js.snap +++ b/packages/block-library/src/image/test/__snapshots__/transforms.native.js.snap @@ -42,7 +42,7 @@ exports[`Image block transformations to Group block 1`] = ` exports[`Image block transformations to Media & Text block 1`] = ` " -
+

" diff --git a/packages/block-library/src/media-text/block.json b/packages/block-library/src/media-text/block.json index 1c9fc0b6820f1e..ded230a2eb1006 100644 --- a/packages/block-library/src/media-text/block.json +++ b/packages/block-library/src/media-text/block.json @@ -10,7 +10,7 @@ "attributes": { "align": { "type": "string", - "default": "wide" + "default": "none" }, "mediaAlt": { "type": "string", diff --git a/packages/block-library/src/media-text/deprecated.js b/packages/block-library/src/media-text/deprecated.js index 95595f8da6424b..2d061213dface8 100644 --- a/packages/block-library/src/media-text/deprecated.js +++ b/packages/block-library/src/media-text/deprecated.js @@ -13,6 +13,7 @@ import { useInnerBlocksProps, useBlockProps, } from '@wordpress/block-editor'; +import { compose } from '@wordpress/compose'; /** * Internal dependencies @@ -30,6 +31,19 @@ const v1ToV5ImageFillStyles = ( url, focalPoint ) => { : {}; }; +const v6ImageFillStyles = ( url, focalPoint ) => { + return url + ? { + backgroundImage: `url(${ url })`, + backgroundPosition: focalPoint + ? `${ Math.round( focalPoint.x * 100 ) }% ${ Math.round( + focalPoint.y * 100 + ) }%` + : `50% 50%`, + } + : {}; +}; + const DEFAULT_MEDIA_WIDTH = 50; const noop = () => {}; @@ -49,6 +63,20 @@ const migrateCustomColors = ( attributes ) => { }; }; +// After align attribute's default was updated this function explicitly sets +// the align value for deprecated blocks to the `wide` value which was default +// for their versions of this block. +const migrateDefaultAlign = ( attributes ) => { + if ( attributes.align ) { + return attributes; + } + + return { + ...attributes, + align: 'wide', + }; +}; + const baseAttributes = { align: { type: 'string', @@ -133,6 +161,40 @@ const v4ToV5BlockAttributes = { }, }; +const v6Attributes = { + ...v4ToV5BlockAttributes, + mediaAlt: { + type: 'string', + source: 'attribute', + selector: 'figure img', + attribute: 'alt', + default: '', + __experimentalRole: 'content', + }, + mediaId: { + type: 'number', + __experimentalRole: 'content', + }, + mediaUrl: { + type: 'string', + source: 'attribute', + selector: 'figure video,figure img', + attribute: 'src', + __experimentalRole: 'content', + }, + href: { + type: 'string', + source: 'attribute', + selector: 'figure a', + attribute: 'href', + __experimentalRole: 'content', + }, + mediaType: { + type: 'string', + __experimentalRole: 'content', + }, +}; + const v4ToV5Supports = { anchor: true, align: [ 'wide', 'full' ], @@ -143,6 +205,166 @@ const v4ToV5Supports = { }, }; +const v6Supports = { + ...v4ToV5Supports, + color: { + gradients: true, + link: true, + __experimentalDefaultControls: { + background: true, + text: true, + }, + }, + spacing: { + margin: true, + padding: true, + }, + typography: { + fontSize: true, + lineHeight: true, + __experimentalFontFamily: true, + __experimentalFontWeight: true, + __experimentalFontStyle: true, + __experimentalTextTransform: true, + __experimentalTextDecoration: true, + __experimentalLetterSpacing: true, + __experimentalDefaultControls: { + fontSize: true, + }, + }, +}; + +// Version with wide as the default alignment. +// See: https://github.com/WordPress/gutenberg/pull/48404 +const v6 = { + attributes: v6Attributes, + supports: v6Supports, + save( { attributes } ) { + const { + isStackedOnMobile, + mediaAlt, + mediaPosition, + mediaType, + mediaUrl, + mediaWidth, + mediaId, + verticalAlignment, + imageFill, + focalPoint, + linkClass, + href, + linkTarget, + rel, + } = attributes; + const mediaSizeSlug = + attributes.mediaSizeSlug || DEFAULT_MEDIA_SIZE_SLUG; + const newRel = isEmpty( rel ) ? undefined : rel; + + const imageClasses = classnames( { + [ `wp-image-${ mediaId }` ]: mediaId && mediaType === 'image', + [ `size-${ mediaSizeSlug }` ]: mediaId && mediaType === 'image', + } ); + + let image = ( + { + ); + + if ( href ) { + image = ( + + { image } + + ); + } + + const mediaTypeRenders = { + image: () => image, + video: () =>