From fab909c13ec2f98fa160dd8cdc9f4d95dd6066c0 Mon Sep 17 00:00:00 2001 From: Nick Diego Date: Wed, 9 Nov 2022 10:52:46 -0600 Subject: [PATCH 01/66] Plugin: Updates tested up to version to 6.1 (#45630) --- readme.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.txt b/readme.txt index dcc9e7c3598a52..c87aff308e1c52 100644 --- a/readme.txt +++ b/readme.txt @@ -1,6 +1,6 @@ === Gutenberg === Contributors: matveb, joen, karmatosed -Tested up to: 6.0 +Tested up to: 6.1 Stable tag: V.V.V License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html From 2d8c0ee7c17a895c64925d9afbcb7f8950771711 Mon Sep 17 00:00:00 2001 From: flootr Date: Wed, 9 Nov 2022 11:19:09 -0700 Subject: [PATCH 02/66] Components: improve tests for `ToggleGroupControl` (#45627) * Components: refactor `ToggleGroupControl` away from `fireEvents` * use `.focus()` directly instead of `userEvent` * use `userEvent.hover()` to resemble real user interaction --- .../src/toggle-group-control/test/index.tsx | 44 ++++++++++++------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/packages/components/src/toggle-group-control/test/index.tsx b/packages/components/src/toggle-group-control/test/index.tsx index 361cbb789f64c7..28b3812fe8dafa 100644 --- a/packages/components/src/toggle-group-control/test/index.tsx +++ b/packages/components/src/toggle-group-control/test/index.tsx @@ -1,7 +1,7 @@ /** * External dependencies */ -import { render, fireEvent, screen } from '@testing-library/react'; +import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; /** @@ -73,7 +73,10 @@ describe( 'ToggleGroupControl', () => { expect( container ).toMatchSnapshot(); } ); } ); - it( 'should call onChange with proper value', () => { + it( 'should call onChange with proper value', async () => { + const user = userEvent.setup( { + advanceTimers: jest.advanceTimersByTime, + } ); const mockOnChange = jest.fn(); render( @@ -86,13 +89,15 @@ describe( 'ToggleGroupControl', () => { ); - const firstRadio = screen.getByRole( 'radio', { name: 'R' } ); - - fireEvent.click( firstRadio ); + await user.click( screen.getByRole( 'radio', { name: 'R' } ) ); expect( mockOnChange ).toHaveBeenCalledWith( 'rigas' ); } ); - it( 'should render tooltip where `showTooltip` === `true`', () => { + + it( 'should render tooltip where `showTooltip` === `true`', async () => { + const user = userEvent.setup( { + advanceTimers: jest.advanceTimersByTime, + } ); render( { optionsWithTooltip } @@ -103,14 +108,19 @@ describe( 'ToggleGroupControl', () => { 'Click for Delicious Gnocchi' ); - fireEvent.focus( firstRadio ); + await user.hover( firstRadio ); - expect( - screen.getByText( 'Click for Delicious Gnocchi' ) - ).toBeInTheDocument(); + await waitFor( () => + expect( + screen.getByText( 'Click for Delicious Gnocchi' ) + ).toBeVisible() + ); } ); - it( 'should not render tooltip', () => { + it( 'should not render tooltip', async () => { + const user = userEvent.setup( { + advanceTimers: jest.advanceTimersByTime, + } ); render( { optionsWithTooltip } @@ -121,11 +131,13 @@ describe( 'ToggleGroupControl', () => { 'Click for Sumptuous Caponata' ); - fireEvent.focus( secondRadio ); + await user.hover( secondRadio ); - expect( - screen.queryByText( 'Click for Sumptuous Caponata' ) - ).not.toBeInTheDocument(); + await waitFor( () => + expect( + screen.queryByText( 'Click for Sumptuous Caponata' ) + ).not.toBeInTheDocument() + ); } ); describe( 'isDeselectable', () => { @@ -208,7 +220,7 @@ describe( 'ToggleGroupControl', () => { name: 'R', pressed: false, } ) - ).toBeInTheDocument(); + ).toBeVisible(); } ); it( 'should tab to the next option button', async () => { From c8cd482c86d34a921f30ae32cd820292b6fe7707 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Wed, 9 Nov 2022 18:31:51 +0000 Subject: [PATCH 03/66] Fix: Contrast checker appears unexpectedly on some blocks (#45639) --- packages/block-editor/src/hooks/color.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/block-editor/src/hooks/color.js b/packages/block-editor/src/hooks/color.js index ad6a14028d3f28..593eea881e3107 100644 --- a/packages/block-editor/src/hooks/color.js +++ b/packages/block-editor/src/hooks/color.js @@ -459,6 +459,8 @@ export function ColorEdit( props ) { Platform.OS === 'web' && ! gradient && ! style?.color?.gradient && + hasBackgroundColor && + ( hasLinkColor || hasTextColor ) && // Contrast checking is enabled by default. // Deactivating it requires `enableContrastChecker` to have // an explicit value of `false`. From f59441ba606009d42e941653363ec018901e61db Mon Sep 17 00:00:00 2001 From: Tim Broddin Date: Wed, 9 Nov 2022 11:35:32 -0700 Subject: [PATCH 04/66] Fix alignment of create new post link (#45638) Padding left should be 52px (16px + 24px + 12px) --- packages/block-library/src/query/editor.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/query/editor.scss b/packages/block-library/src/query/editor.scss index e6eead11f152c0..f6de35bc04f69b 100644 --- a/packages/block-library/src/query/editor.scss +++ b/packages/block-library/src/query/editor.scss @@ -3,7 +3,7 @@ } .wp-block-query__create-new-link { - padding: 0 $grid-unit-20 $grid-unit-20 56px; + padding: 0 $grid-unit-20 $grid-unit-20 52px; } .block-library-query__pattern-selection-content .block-editor-block-patterns-list { From 503e152269600a7acf7a3c4f633baff08b33405f Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Wed, 9 Nov 2022 18:37:21 +0000 Subject: [PATCH 05/66] Tidy and minor refactor of Link UI code (#37833) * Put similar variable types together * Normalise to function expressions * Refactor input value handling to hook * Use clearning variable naming for internal inputs * Extract input value hook to file * Simplify and avoid unnecessary effect Co-authored-by: Haz * Remove unused dep * Restore space * Fix bug with input value not updating Co-authored-by: Haz --- .../src/components/link-control/index.js | 62 +++++++------------ .../link-control/use-internal-input-value.js | 22 +++++++ 2 files changed, 45 insertions(+), 39 deletions(-) create mode 100644 packages/block-editor/src/components/link-control/use-internal-input-value.js diff --git a/packages/block-editor/src/components/link-control/index.js b/packages/block-editor/src/components/link-control/index.js index ee8b8db855acd6..52048cdc25b056 100644 --- a/packages/block-editor/src/components/link-control/index.js +++ b/packages/block-editor/src/components/link-control/index.js @@ -20,6 +20,7 @@ import LinkControlSettingsDrawer from './settings-drawer'; import LinkControlSearchInput from './search-input'; import LinkPreview from './link-preview'; import useCreatePage from './use-create-page'; +import useInternalInputValue from './use-internal-input-value'; import { ViewerFill } from './viewer-slot'; import { DEFAULT_LINK_SETTINGS } from './constants'; @@ -132,22 +133,19 @@ function LinkControl( { const isMounting = useRef( true ); const wrapperNode = useRef(); const textInputRef = useRef(); + const isEndingEditWithFocus = useRef( false ); + + const [ internalUrlInputValue, setInternalUrlInputValue ] = + useInternalInputValue( value?.url || '' ); + + const [ internalTextInputValue, setInternalTextInputValue ] = + useInternalInputValue( value?.title || '' ); - const [ internalInputValue, setInternalInputValue ] = useState( - value?.url || '' - ); - const [ internalTextValue, setInternalTextValue ] = useState( - value?.title || '' - ); - const currentInputValue = propInputValue || internalInputValue; const [ isEditingLink, setIsEditingLink ] = useState( forceIsEditingLink !== undefined ? forceIsEditingLink : ! value || ! value.url ); - const isEndingEditWithFocus = useRef( false ); - - const currentInputIsEmpty = ! currentInputValue?.trim()?.length; const { createPage, isCreatingPage, errorMessage } = useCreatePage( createSuggestion ); @@ -191,53 +189,35 @@ function LinkControl( { isEndingEditWithFocus.current = false; }, [ isEditingLink, isCreatingPage ] ); - useEffect( () => { - /** - * If the value's `text` property changes then sync this - * back up with state. - */ - if ( value?.title && value.title !== internalTextValue ) { - setInternalTextValue( value.title ); - } - - /** - * Update the state value internalInputValue if the url value changes - * for example when clicking on another anchor - */ - if ( value?.url ) { - setInternalInputValue( value.url ); - } - }, [ value ] ); - /** * Cancels editing state and marks that focus may need to be restored after * the next render, if focus was within the wrapper when editing finished. */ - function stopEditing() { + const stopEditing = () => { isEndingEditWithFocus.current = !! wrapperNode.current?.contains( wrapperNode.current.ownerDocument.activeElement ); setIsEditingLink( false ); - } + }; const handleSelectSuggestion = ( updatedValue ) => { onChange( { ...updatedValue, - title: internalTextValue || updatedValue?.title, + title: internalTextInputValue || updatedValue?.title, } ); stopEditing(); }; const handleSubmit = () => { if ( - currentInputValue !== value?.url || - internalTextValue !== value?.title + currentUrlInputValue !== value?.url || + internalTextInputValue !== value?.title ) { onChange( { ...value, - url: currentInputValue, - title: internalTextValue, + url: currentUrlInputValue, + title: internalTextInputValue, } ); } stopEditing(); @@ -254,6 +234,10 @@ function LinkControl( { } }; + const currentUrlInputValue = propInputValue || internalUrlInputValue; + + const currentInputIsEmpty = ! currentUrlInputValue?.trim()?.length; + const shownUnlinkControl = onRemove && value && ! isEditingLink && ! isCreatingPage; @@ -289,8 +273,8 @@ function LinkControl( { ref={ textInputRef } className="block-editor-link-control__field block-editor-link-control__text-content" label="Text" - value={ internalTextValue } - onChange={ setInternalTextValue } + value={ internalTextInputValue } + onChange={ setInternalTextInputValue } onKeyDown={ handleSubmitWithEnter } /> ) } @@ -299,10 +283,10 @@ function LinkControl( { currentLink={ value } className="block-editor-link-control__field block-editor-link-control__search-input" placeholder={ searchInputPlaceholder } - value={ currentInputValue } + value={ currentUrlInputValue } withCreateSuggestion={ withCreateSuggestion } onCreateSuggestion={ createPage } - onChange={ setInternalInputValue } + onChange={ setInternalUrlInputValue } onSelect={ handleSelectSuggestion } showInitialSuggestions={ showInitialSuggestions } allowDirectEntry={ ! noDirectEntry } diff --git a/packages/block-editor/src/components/link-control/use-internal-input-value.js b/packages/block-editor/src/components/link-control/use-internal-input-value.js new file mode 100644 index 00000000000000..c69c8d06bcb2f5 --- /dev/null +++ b/packages/block-editor/src/components/link-control/use-internal-input-value.js @@ -0,0 +1,22 @@ +/** + * WordPress dependencies + */ +import { useState, useEffect } from '@wordpress/element'; + +export default function useInternalInputValue( value ) { + const [ internalInputValue, setInternalInputValue ] = useState( + value || '' + ); + + useEffect( () => { + /** + * If the value's `text` property changes then sync this + * back up with state. + */ + if ( value?.title && value.title !== internalInputValue ) { + setInternalInputValue( value.title ); + } + }, [ value ] ); + + return [ internalInputValue, setInternalInputValue ]; +} From 776912a5f41ee984286ea1179b5e8a369230f947 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Wed, 9 Nov 2022 18:49:37 +0000 Subject: [PATCH 06/66] Fix: Updated names from List View to Document Overview (#45524) --- packages/e2e-test-utils/src/list-view.js | 6 ++++-- packages/e2e-tests/specs/editor/blocks/cover.test.js | 4 +++- .../editor/various/block-hierarchy-navigation.test.js | 8 ++++++-- .../src/components/header/header-toolbar/index.js | 4 ++-- packages/edit-post/src/components/header/style.scss | 2 +- packages/edit-post/src/components/layout/index.js | 2 +- .../src/components/secondary-sidebar/list-view-sidebar.js | 8 ++++---- .../edit-post/src/components/secondary-sidebar/style.scss | 6 +++--- test/e2e/specs/editor/blocks/columns.spec.js | 2 +- 9 files changed, 25 insertions(+), 17 deletions(-) diff --git a/packages/e2e-test-utils/src/list-view.js b/packages/e2e-test-utils/src/list-view.js index 3ac16475bd9541..58b857652f5899 100644 --- a/packages/e2e-test-utils/src/list-view.js +++ b/packages/e2e-test-utils/src/list-view.js @@ -1,13 +1,15 @@ async function toggleListView() { + // selector .edit-post-header-toolbar__list-view-toggle is still required because the performance tests also execute against older versions that still use that selector. await page.click( - '.edit-post-header-toolbar__list-view-toggle, .edit-site-header-edit-mode__list-view-toggle, .edit-widgets-header-toolbar__list-view-toggle' + '.edit-post-header-toolbar__document-overview-toggle, .edit-post-header-toolbar__list-view-toggle, .edit-site-header-edit-mode__list-view-toggle, .edit-widgets-header-toolbar__list-view-toggle' ); } async function isListViewOpen() { return await page.evaluate( () => { + // selector .edit-post-header-toolbar__list-view-toggle is still required because the performance tests also execute against older versions that still use that selector. return !! document.querySelector( - '.edit-post-header-toolbar__list-view-toggle.is-pressed, .edit-site-header-edit-mode__list-view-toggle.is-pressed, .edit-widgets-header-toolbar__list-view-toggle.is-pressed' + '.edit-post-header-toolbar__document-overview-toggle.is-pressed, .edit-post-header-toolbar__list-view-toggle.is-pressed, .edit-site-header-edit-mode__list-view-toggle.is-pressed, .edit-widgets-header-toolbar__list-view-toggle.is-pressed' ); } ); } diff --git a/packages/e2e-tests/specs/editor/blocks/cover.test.js b/packages/e2e-tests/specs/editor/blocks/cover.test.js index 715ed555631a49..4366bad799bc21 100644 --- a/packages/e2e-tests/specs/editor/blocks/cover.test.js +++ b/packages/e2e-tests/specs/editor/blocks/cover.test.js @@ -127,7 +127,9 @@ describe( 'Cover', () => { ); // Select the cover block.By default the child paragraph gets selected. - await page.click( '.edit-post-header-toolbar__list-view-toggle' ); + await page.click( + '.edit-post-header-toolbar__document-overview-toggle' + ); await page.click( '.block-editor-list-view-block__contents-container a' ); diff --git a/packages/e2e-tests/specs/editor/various/block-hierarchy-navigation.test.js b/packages/e2e-tests/specs/editor/various/block-hierarchy-navigation.test.js index ea16ec75ffda2b..fef0f11eca67a9 100644 --- a/packages/e2e-tests/specs/editor/various/block-hierarchy-navigation.test.js +++ b/packages/e2e-tests/specs/editor/various/block-hierarchy-navigation.test.js @@ -49,7 +49,9 @@ describe( 'Navigating the block hierarchy', () => { await page.keyboard.type( 'First column' ); // Navigate to the columns blocks. - await page.click( '.edit-post-header-toolbar__list-view-toggle' ); + await page.click( + '.edit-post-header-toolbar__document-overview-toggle' + ); const firstColumnsBlockMenuItem = ( await getListViewBlocks( 'Columns' ) @@ -183,7 +185,9 @@ describe( 'Navigating the block hierarchy', () => { await page.click( '.editor-post-title' ); // Try selecting the group block using the Outline. - await page.click( '.edit-post-header-toolbar__list-view-toggle' ); + await page.click( + '.edit-post-header-toolbar__document-overview-toggle' + ); const groupMenuItem = ( await getListViewBlocks( 'Group' ) )[ 0 ]; await groupMenuItem.click(); diff --git a/packages/edit-post/src/components/header/header-toolbar/index.js b/packages/edit-post/src/components/header/header-toolbar/index.js index bf22dec0822887..b4d30adc20244a 100644 --- a/packages/edit-post/src/components/header/header-toolbar/index.js +++ b/packages/edit-post/src/components/header/header-toolbar/index.js @@ -78,12 +78,12 @@ function HeaderToolbar() { <> .edit-post-header__toolbar .edit-post-header-toolbar__inserter-toggle, - & > .edit-post-header__toolbar .edit-post-header-toolbar__list-view-toggle, + & > .edit-post-header__toolbar .edit-post-header-toolbar__document-overview-toggle, & > .edit-post-header__settings > .block-editor-post-preview__dropdown, & > .edit-post-header__settings > .interface-pinned-items { display: none; diff --git a/packages/edit-post/src/components/layout/index.js b/packages/edit-post/src/components/layout/index.js index 3385d2624c4513..7ab7be65e54f26 100644 --- a/packages/edit-post/src/components/layout/index.js +++ b/packages/edit-post/src/components/layout/index.js @@ -164,7 +164,7 @@ function Layout( { styles } ) { ); const secondarySidebarLabel = isListViewOpened - ? __( 'List View' ) + ? __( 'Document Overview' ) : __( 'Block Library' ); const secondarySidebar = () => { diff --git a/packages/edit-post/src/components/secondary-sidebar/list-view-sidebar.js b/packages/edit-post/src/components/secondary-sidebar/list-view-sidebar.js index 56f6fce6f17bf1..447fcbd1a70db2 100644 --- a/packages/edit-post/src/components/secondary-sidebar/list-view-sidebar.js +++ b/packages/edit-post/src/components/secondary-sidebar/list-view-sidebar.js @@ -43,17 +43,17 @@ export default function ListViewSidebar() { return ( // eslint-disable-next-line jsx-a11y/no-static-element-interactions
); diff --git a/packages/block-editor/src/components/inserter/style.scss b/packages/block-editor/src/components/inserter/style.scss index 210f2db211c00a..12a3690f71e4de 100644 --- a/packages/block-editor/src/components/inserter/style.scss +++ b/packages/block-editor/src/components/inserter/style.scss @@ -174,11 +174,15 @@ $block-inserter-tabs-height: 44px; text-align: right; } -.block-editor-inserter__manage-reusable-blocks { - display: inline-block; +.block-editor-inserter__manage-reusable-blocks-container { margin: $grid-unit-20; } +.block-editor-inserter__manage-reusable-blocks { + justify-content: center; + width: 100%; +} + .block-editor-inserter__no-results { padding: $grid-unit-40; text-align: center; @@ -341,10 +345,6 @@ $block-inserter-tabs-height: 44px; position: relative; // prevents overscroll when block library is open } -.block-editor-inserter__manage-reusable-blocks-container { - padding: $grid-unit-20; -} - .block-editor-inserter__quick-inserter { width: 100%; From 0fd58491d5c3801750665aa2b031c3cc75577aca Mon Sep 17 00:00:00 2001 From: Enej Bajgoric Date: Wed, 9 Nov 2022 11:14:42 -0800 Subject: [PATCH 08/66] Update: lock icon to outline (#45645) --- .../src/components/block-lock/menu-item.js | 7 +++++-- packages/icons/src/index.js | 1 + packages/icons/src/library/lock-outline.js | 12 ++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 packages/icons/src/library/lock-outline.js diff --git a/packages/block-editor/src/components/block-lock/menu-item.js b/packages/block-editor/src/components/block-lock/menu-item.js index 665d9bfbc91a40..65607c646e9684 100644 --- a/packages/block-editor/src/components/block-lock/menu-item.js +++ b/packages/block-editor/src/components/block-lock/menu-item.js @@ -4,7 +4,7 @@ import { __ } from '@wordpress/i18n'; import { useReducer } from '@wordpress/element'; import { MenuItem } from '@wordpress/components'; -import { lock, unlock } from '@wordpress/icons'; +import { lockOutline, unlock } from '@wordpress/icons'; /** * Internal dependencies @@ -28,7 +28,10 @@ export default function BlockLockMenuItem( { clientId } ) { return ( <> - + { label } { isModalOpen && ( diff --git a/packages/icons/src/index.js b/packages/icons/src/index.js index f5071f80d1a1e3..c7f891a5292dbf 100644 --- a/packages/icons/src/index.js +++ b/packages/icons/src/index.js @@ -125,6 +125,7 @@ export { default as list } from './library/list'; export { default as listItem } from './library/list-item'; export { default as listView } from './library/list-view'; export { default as lock } from './library/lock'; +export { default as lockOutline } from './library/lock-outline'; export { default as login } from './library/login'; export { default as loop } from './library/loop'; export { default as mapMarker } from './library/map-marker'; diff --git a/packages/icons/src/library/lock-outline.js b/packages/icons/src/library/lock-outline.js new file mode 100644 index 00000000000000..dfc21768f55740 --- /dev/null +++ b/packages/icons/src/library/lock-outline.js @@ -0,0 +1,12 @@ +/** + * WordPress dependencies + */ +import { SVG, Path } from '@wordpress/primitives'; + +const lockOutline = ( + + + +); + +export default lockOutline; From 9ef0dd9525c2aaa071b78cbf38aa109f5633b2a0 Mon Sep 17 00:00:00 2001 From: Birgit Pauli-Haack Date: Wed, 9 Nov 2022 12:24:14 -0700 Subject: [PATCH 09/66] Change Title: How to use JavaScript with Gutenberg (#45323) * Updated heard from a few developers, that the title is not accurate. They didn't think they needed to get started on JavaScript would have shortened their discovery if the title were a bit more descriptive. * Update title to be more accurate * copy changes to the first paragraph * updated toc.json * better wording after feedback * updated manifest Props @ramonjd @glendaviesnz @alexstine @skorasaurus for review --- docs/how-to-guides/javascript/README.md | 4 ++-- docs/manifest.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/how-to-guides/javascript/README.md b/docs/how-to-guides/javascript/README.md index b31b0fa4d9989b..4b2d123c8ab136 100644 --- a/docs/how-to-guides/javascript/README.md +++ b/docs/how-to-guides/javascript/README.md @@ -1,6 +1,6 @@ -# Getting Started with JavaScript +# How to use JavaScript with the Block Editor -The purpose of this tutorial is to step through getting started with JavaScript and WordPress, specifically around the new block editor. The Block Editor Handbook contains information on the APIs available for working with this new setup. The goal of this tutorial is to get you comfortable on how to use the API reference and snippets of code found within. +The Block Editor Handbook contains information on the APIs available for working with this new setup. The goal of this tutorial is to get you comfortable using the API reference and snippets of code found within. ### What is JavaScript diff --git a/docs/manifest.json b/docs/manifest.json index 3db21b179331ef..8cc39af57531e4 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -234,7 +234,7 @@ "parent": "how-to-guides" }, { - "title": "Getting Started with JavaScript", + "title": "How to use JavaScript with the Block Editor", "slug": "javascript", "markdown_source": "../docs/how-to-guides/javascript/README.md", "parent": "how-to-guides" From bba2cb6021f1becc7a622e0c352c2ff2a0b0d63d Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Wed, 9 Nov 2022 20:28:27 +0000 Subject: [PATCH 10/66] Update: Improve performance of block template object retrieval. (#45646) --- .../wordpress-6.1/block-template-utils.php | 107 +++++++++++------- 1 file changed, 67 insertions(+), 40 deletions(-) diff --git a/lib/compat/wordpress-6.1/block-template-utils.php b/lib/compat/wordpress-6.1/block-template-utils.php index d1814b6f50e538..088333990a0d5f 100644 --- a/lib/compat/wordpress-6.1/block-template-utils.php +++ b/lib/compat/wordpress-6.1/block-template-utils.php @@ -264,51 +264,65 @@ function gutenberg_get_block_template( $id, $template_type = 'wp_template' ) { function _gutenberg_build_title_and_description_for_single_post_type_block_template( $post_type, $slug, WP_Block_Template $template ) { $post_type_object = get_post_type_object( $post_type ); - $posts = get_posts( - array( - 'name' => $slug, - 'post_type' => $post_type, - ) + $default_args = array( + 'post_type' => $post_type, + 'post_status' => 'publish', + 'posts_per_page' => 1, + 'update_post_meta_cache' => false, + 'update_post_term_cache' => false, + 'ignore_sticky_posts' => true, + 'no_found_rows' => true, ); - if ( empty( $posts ) ) { + + $args = array( + 'name' => $slug, + ); + $args = wp_parse_args( $args, $default_args ); + + $posts_query = new WP_Query( $args ); + + if ( empty( $posts_query->posts ) ) { $template->title = sprintf( - // translators: Represents the title of a user's custom template in the Site Editor referencing a post that was not found, where %1$s is the singular name of a post type and %2$s is the slug of the deleted post, e.g. "Not found: Page(hello)". - __( 'Not found: %1$s(%2$s)', 'gutenberg' ), + /* translators: Custom template title in the Site Editor referencing a post that was not found. 1: Post type singular name, 2: Post type slug. */ + __( 'Not found: %1$s (%2$s)', 'gutenberg' ), $post_type_object->labels->singular_name, $slug ); + return false; } - $post_title = $posts[0]->post_title; + $post_title = $posts_query->posts[0]->post_title; $template->title = sprintf( - // translators: Represents the title of a user's custom template in the Site Editor, where %1$s is the singular name of a post type and %2$s is the name of the post, e.g. "Page: Hello". + /* translators: Custom template title in the Site Editor. 1: Post type singular name, 2: Post title. */ __( '%1$s: %2$s', 'gutenberg' ), $post_type_object->labels->singular_name, $post_title ); + $template->description = sprintf( - // translators: Represents the description of a user's custom template in the Site Editor, e.g. "Template for Page: Hello". + /* translators: Custom template description in the Site Editor. %s: Post title. */ __( 'Template for %s', 'gutenberg' ), $post_title ); - $posts_with_same_title = get_posts( - array( - 'title' => $post_title, - 'post_type' => $post_type, - 'post_status' => 'publish', - ) + $args = array( + 'title' => $post_title, ); - if ( count( $posts_with_same_title ) > 1 ) { + $args = wp_parse_args( $args, $default_args ); + + $posts_with_same_title_query = new WP_Query( $args ); + + if ( count( $posts_with_same_title_query->posts ) > 1 ) { $template->title = sprintf( - // translators: Represents the title of a user's custom template in the Site Editor, where %1$s is the template title and %2$s is the slug of the post type, e.g. "Project: Hello (project_type)". + /* translators: Custom template title in the Site Editor. 1: Template title, 2: Post type slug. */ __( '%1$s (%2$s)', 'gutenberg' ), $template->title, $slug ); } + return true; } @@ -328,53 +342,66 @@ function _gutenberg_build_title_and_description_for_single_post_type_block_templ function _gutenberg_build_title_and_description_for_taxonomy_block_template( $taxonomy, $slug, WP_Block_Template $template ) { $taxonomy_object = get_taxonomy( $taxonomy ); - $terms = get_terms( - array( - 'taxonomy' => $taxonomy, - 'hide_empty' => false, - 'slug' => $slug, - ) + $default_args = array( + 'taxonomy' => $taxonomy, + 'hide_empty' => false, + 'update_term_meta_cache' => false, ); - if ( empty( $terms ) ) { + $term_query = new WP_Term_Query(); + + $args = array( + 'number' => 1, + 'slug' => $slug, + ); + $args = wp_parse_args( $args, $default_args ); + + $terms_query = $term_query->query( $args ); + + if ( empty( $terms_query ) ) { $template->title = sprintf( - // translators: Represents the title of a user's custom template in the Site Editor referencing a taxonomy term that was not found, where %1$s is the singular name of a taxonomy and %2$s is the slug of the deleted term, e.g. "Not found: Category(shoes)". - __( 'Not found: %1$s(%2$s)', 'gutenberg' ), + /* translators: Custom template title in the Site Editor, referencing a taxonomy term that was not found. 1: Taxonomy singular name, 2: Term slug. */ + __( 'Not found: %1$s (%2$s)', 'gutenberg' ), $taxonomy_object->labels->singular_name, $slug ); return false; } - $term_title = $terms[0]->name; + $term_title = $terms_query[0]->name; $template->title = sprintf( - // translators: Represents the title of a user's custom template in the Site Editor, where %1$s is the singular name of a taxonomy and %2$s is the name of the term, e.g. "Category: shoes". + /* translators: Custom template title in the Site Editor. 1: Taxonomy singular name, 2: Term title. */ __( '%1$s: %2$s', 'gutenberg' ), $taxonomy_object->labels->singular_name, $term_title ); + $template->description = sprintf( - // translators: Represents the description of a user's custom template in the Site Editor, e.g. "Template for Category: shoes". - __( 'Template for %1$s', 'gutenberg' ), + /* translators: Custom template description in the Site Editor. %s: Term title. */ + __( 'Template for %s', 'gutenberg' ), $term_title ); - $terms_with_same_title = get_terms( - array( - 'taxonomy' => $taxonomy, - 'hide_empty' => false, - 'name' => $term_title, - ) + $term_query = new WP_Term_Query(); + + $args = array( + 'number' => 2, + 'name' => $term_title, ); - if ( count( $terms_with_same_title ) > 1 ) { + $args = wp_parse_args( $args, $default_args ); + + $terms_with_same_title_query = $term_query->query( $args ); + + if ( count( $terms_with_same_title_query ) > 1 ) { $template->title = sprintf( - // translators: Represents the title of a user's custom template in the Site Editor, where %1$s is the template title and %2$s is the slug of the taxonomy, e.g. "Category: shoes (product_tag)". + /* translators: Custom template title in the Site Editor. 1: Template title, 2: Term slug. */ __( '%1$s (%2$s)', 'gutenberg' ), $template->title, $slug ); } + return true; } From a8cddf9dc4f98869d3433eb0a198dc42fb001294 Mon Sep 17 00:00:00 2001 From: Miguel Torres Date: Wed, 9 Nov 2022 13:34:25 -0700 Subject: [PATCH 11/66] Global Styles: Use low-level cache for get_user_data_from_wp_global_styles (#45634) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: André <583546+oandregal@users.noreply.github.com> --- .../class-wp-theme-json-resolver-6-0.php | 74 -------------- .../class-wp-theme-json-resolver-6-1.php | 68 +++++++++++++ phpunit/class-wp-theme-json-resolver-test.php | 96 +++++++++++++++++-- 3 files changed, 155 insertions(+), 83 deletions(-) diff --git a/lib/compat/wordpress-6.0/class-wp-theme-json-resolver-6-0.php b/lib/compat/wordpress-6.0/class-wp-theme-json-resolver-6-0.php index 45779d395e6291..07b83049cfeaad 100644 --- a/lib/compat/wordpress-6.0/class-wp-theme-json-resolver-6-0.php +++ b/lib/compat/wordpress-6.0/class-wp-theme-json-resolver-6-0.php @@ -146,80 +146,6 @@ public static function get_style_variations() { return $variations; } - /** - * Returns the custom post type that contains the user's origin config - * for the current theme or a void array if none are found. - * - * This can also create and return a new draft custom post type. - * - * @param WP_Theme $theme The theme object. If empty, it - * defaults to the current theme. - * @param bool $create_post Optional. Whether a new custom post - * type should be created if none are - * found. False by default. - * @param array $post_status_filter Filter Optional. custom post type by - * post status. ['publish'] by default, - * so it only fetches published posts. - * @return array Custom Post Type for the user's origin config. - */ - public static function get_user_data_from_wp_global_styles( $theme, $create_post = false, $post_status_filter = array( 'publish' ) ) { - if ( ! $theme instanceof WP_Theme ) { - $theme = wp_get_theme(); - } - $user_cpt = array(); - $post_type_filter = 'wp_global_styles'; - $args = array( - 'numberposts' => 1, - 'orderby' => 'date', - 'order' => 'desc', - 'post_type' => $post_type_filter, - 'post_status' => $post_status_filter, - 'tax_query' => array( - array( - 'taxonomy' => 'wp_theme', - 'field' => 'name', - 'terms' => $theme->get_stylesheet(), - ), - ), - ); - - $cache_key = sprintf( 'wp_global_styles_%s', md5( serialize( $args ) ) ); - $post_id = wp_cache_get( $cache_key ); - - if ( (int) $post_id > 0 ) { - return get_post( $post_id, ARRAY_A ); - } - - // Special case: '-1' is a results not found. - if ( -1 === $post_id && ! $create_post ) { - return $user_cpt; - } - - $recent_posts = wp_get_recent_posts( $args ); - if ( is_array( $recent_posts ) && ( count( $recent_posts ) === 1 ) ) { - $user_cpt = $recent_posts[0]; - } elseif ( $create_post ) { - $cpt_post_id = wp_insert_post( - array( - 'post_content' => '{"version": ' . WP_Theme_JSON_Gutenberg::LATEST_SCHEMA . ', "isGlobalStylesUserThemeJSON": true }', - 'post_status' => 'publish', - 'post_title' => __( 'Custom Styles', 'default' ), - 'post_type' => $post_type_filter, - 'post_name' => 'wp-global-styles-' . urlencode( wp_get_theme()->get_stylesheet() ), - 'tax_input' => array( - 'wp_theme' => array( wp_get_theme()->get_stylesheet() ), - ), - ), - true - ); - $user_cpt = get_post( $cpt_post_id, ARRAY_A ); - } - $cache_expiration = $user_cpt ? DAY_IN_SECONDS : HOUR_IN_SECONDS; - wp_cache_set( $cache_key, $user_cpt ? $user_cpt['ID'] : -1, '', $cache_expiration ); - - return $user_cpt; - } - /** * Returns the user's origin config. * diff --git a/lib/compat/wordpress-6.1/class-wp-theme-json-resolver-6-1.php b/lib/compat/wordpress-6.1/class-wp-theme-json-resolver-6-1.php index d006f25382bbdc..73e012f33d1c7e 100644 --- a/lib/compat/wordpress-6.1/class-wp-theme-json-resolver-6-1.php +++ b/lib/compat/wordpress-6.1/class-wp-theme-json-resolver-6-1.php @@ -120,4 +120,72 @@ public static function get_user_data() { return static::$user; } + + /** + * Returns the custom post type that contains the user's origin config + * for the active theme or a void array if none are found. + * + * This can also create and return a new draft custom post type. + * + * @since 5.9.0 + * + * @param WP_Theme $theme The theme object. If empty, it + * defaults to the active theme. + * @param bool $create_post Optional. Whether a new custom post + * type should be created if none are + * found. Default false. + * @param array $post_status_filter Optional. Filter custom post type by + * post status. Default `array( 'publish' )`, + * so it only fetches published posts. + * @return array Custom Post Type for the user's origin config. + */ + public static function get_user_data_from_wp_global_styles( $theme, $create_post = false, $post_status_filter = array( 'publish' ) ) { + if ( ! $theme instanceof WP_Theme ) { + $theme = wp_get_theme(); + } + $user_cpt = array(); + $post_type_filter = 'wp_global_styles'; + $stylesheet = $theme->get_stylesheet(); + $args = array( + 'posts_per_page' => 1, + 'orderby' => 'date', + 'order' => 'desc', + 'post_type' => $post_type_filter, + 'post_status' => $post_status_filter, + 'ignore_sticky_posts' => true, + 'no_found_rows' => true, + 'tax_query' => array( + array( + 'taxonomy' => 'wp_theme', + 'field' => 'name', + 'terms' => $stylesheet, + ), + ), + ); + + $global_style_query = new WP_Query(); + $recent_posts = $global_style_query->query( $args ); + if ( count( $recent_posts ) === 1 ) { + $user_cpt = get_post( $recent_posts[0], ARRAY_A ); + } elseif ( $create_post ) { + $cpt_post_id = wp_insert_post( + array( + 'post_content' => '{"version": ' . WP_Theme_JSON_Gutenberg::LATEST_SCHEMA . ', "isGlobalStylesUserThemeJSON": true }', + 'post_status' => 'publish', + 'post_title' => 'Custom Styles', // Do not make string translatable, see https://core.trac.wordpress.org/ticket/54518. + 'post_type' => $post_type_filter, + 'post_name' => sprintf( 'wp-global-styles-%s', urlencode( $stylesheet ) ), + 'tax_input' => array( + 'wp_theme' => array( $stylesheet ), + ), + ), + true + ); + if ( ! is_wp_error( $cpt_post_id ) ) { + $user_cpt = get_post( $cpt_post_id, ARRAY_A ); + } + } + + return $user_cpt; + } } diff --git a/phpunit/class-wp-theme-json-resolver-test.php b/phpunit/class-wp-theme-json-resolver-test.php index 3746d158f20169..153db8c5606791 100644 --- a/phpunit/class-wp-theme-json-resolver-test.php +++ b/phpunit/class-wp-theme-json-resolver-test.php @@ -8,6 +8,60 @@ class WP_Theme_JSON_Resolver_Gutenberg_Test extends WP_UnitTestCase { + /** + * Administrator ID. + * + * @var int + */ + protected static $administrator_id; + + /** + * WP_Theme_JSON_Resolver::$blocks_cache property. + * + * @var ReflectionProperty + */ + private static $property_blocks_cache; + + /** + * Original value of the WP_Theme_JSON_Resolver::$blocks_cache property. + * + * @var array + */ + private static $property_blocks_cache_orig_value; + + /** + * WP_Theme_JSON_Resolver::$core property. + * + * @var ReflectionProperty + */ + private static $property_core; + + /** + * Original value of the WP_Theme_JSON_Resolver::$core property. + * + * @var WP_Theme_JSON + */ + private static $property_core_orig_value; + + public static function set_up_before_class() { + parent::set_up_before_class(); + + self::$administrator_id = self::factory()->user->create( + array( + 'role' => 'administrator', + 'user_email' => 'administrator@example.com', + ) + ); + + static::$property_blocks_cache = new ReflectionProperty( WP_Theme_JSON_Resolver_Gutenberg::class, 'blocks_cache' ); + static::$property_blocks_cache->setAccessible( true ); + static::$property_blocks_cache_orig_value = static::$property_blocks_cache->getValue(); + + static::$property_core = new ReflectionProperty( WP_Theme_JSON_Resolver_Gutenberg::class, 'core' ); + static::$property_core->setAccessible( true ); + static::$property_core_orig_value = static::$property_core->getValue(); + } + public function set_up() { parent::set_up(); $this->theme_root = realpath( __DIR__ . '/data/themedir1' ); @@ -256,28 +310,52 @@ public function test_merges_child_theme_json_into_parent_theme_json() { } public function test_get_user_data_from_wp_global_styles_does_not_use_uncached_queries() { + // Switch to a theme that does have support. + switch_theme( 'block-theme' ); + wp_set_current_user( self::$administrator_id ); + $theme = wp_get_theme(); + WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( $theme ); add_filter( 'query', array( $this, 'filter_db_query' ) ); $query_count = count( $this->queries ); for ( $i = 0; $i < 3; $i++ ) { - WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( wp_get_theme() ); + WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( $theme ); WP_Theme_JSON_Resolver_Gutenberg::clean_cached_data(); } $query_count = count( $this->queries ) - $query_count; - $this->assertEquals( 1, $query_count, 'Only one SQL query should be peformed for multiple invocations of WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles()' ); + $this->assertSame( 0, $query_count, 'Unexpected SQL queries detected for the wp_global_style post type prior to creation.' ); - $user_cpt = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( wp_get_theme() ); - $this->assertEmpty( $user_cpt ); + $user_cpt = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( $theme ); + $this->assertEmpty( $user_cpt, 'User CPT is expected to be empty.' ); - $user_cpt = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( wp_get_theme(), true ); - $this->assertNotEmpty( $user_cpt ); + $user_cpt = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( $theme, true ); + $this->assertNotEmpty( $user_cpt, 'User CPT is expected not to be empty.' ); + $query_count = count( $this->queries ); + for ( $i = 0; $i < 3; $i ++ ) { + $new_user_cpt = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( $theme ); + WP_Theme_JSON_Resolver_Gutenberg::clean_cached_data(); + $this->assertSameSets( $user_cpt, $new_user_cpt, "User CPTs do not match on run {$i}." ); + } + $query_count = count( $this->queries ) - $query_count; + $this->assertSame( 1, $query_count, 'Unexpected SQL queries detected for the wp_global_style post type after creation.' ); + } + + /** + * @covers WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles + */ + public function test_get_user_data_from_wp_global_styles_does_not_use_uncached_queries_for_logged_out_users() { + $theme = wp_get_theme(); + WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( $theme ); + add_filter( 'query', array( $this, 'filter_db_query' ) ); $query_count = count( $this->queries ); for ( $i = 0; $i < 3; $i++ ) { - WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( wp_get_theme() ); + WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( $theme ); WP_Theme_JSON_Resolver_Gutenberg::clean_cached_data(); } $query_count = count( $this->queries ) - $query_count; - $this->assertEquals( 0, $query_count, 'Unexpected SQL queries detected for the wp_global_style post type' ); - remove_filter( 'query', array( $this, 'filter_db_query' ) ); + $this->assertSame( 0, $query_count, 'Unexpected SQL queries detected for the wp_global_style post type prior to creation.' ); + + $user_cpt = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( $theme ); + $this->assertEmpty( $user_cpt, 'User CPT is expected to be empty.' ); } } From a607db31a1644c237ddacf0fecc00be3b46ff831 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petter=20Walb=C3=B8=20Johnsg=C3=A5rd?= Date: Wed, 9 Nov 2022 21:36:01 +0100 Subject: [PATCH 12/66] DateTime: Remove unused types (#45615) --- packages/components/src/date-time/types.ts | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/packages/components/src/date-time/types.ts b/packages/components/src/date-time/types.ts index 5e4f6ec5339a6a..ae99a7082dcd27 100644 --- a/packages/components/src/date-time/types.ts +++ b/packages/components/src/date-time/types.ts @@ -1,8 +1,3 @@ -/** - * External dependencies - */ -import type { Moment } from 'moment'; - export type TimePickerProps = { /** * The initial current time the time picker should render. @@ -30,20 +25,6 @@ export type DatePickerEvent = { date: Date; }; -export type DatePickerDayProps = { - /** - * The day to display. - */ - day: Moment; - - /** - * List of events to show on this day. - * - * @default [] - */ - events?: DatePickerEvent[]; -}; - export type DatePickerProps = { /** * The current date and time at initialization. Optionally pass in a `null` From bdd3fd7789ec136e216d8527c2b21726c21efa41 Mon Sep 17 00:00:00 2001 From: brookewp <35543432+brookewp@users.noreply.github.com> Date: Wed, 9 Nov 2022 12:51:43 -0800 Subject: [PATCH 13/66] CheckboxControl: move icons out of labels (#45535) * Move icons out of BlockLockModal labels * Move icon out of BlockTypesChecklist label * Add CHANGELOG entries * fix checklist centering and add class for lock icon * Add flex-shrink to ensure lock icons maintain size with longer labels --- packages/block-editor/CHANGELOG.md | 4 ++ .../src/components/block-lock/modal.js | 51 ++++++------------- .../src/components/block-lock/style.scss | 23 +++------ packages/edit-post/CHANGELOG.md | 4 ++ .../src/components/block-manager/checklist.js | 8 +-- .../src/components/block-manager/style.scss | 18 ++----- 6 files changed, 37 insertions(+), 71 deletions(-) diff --git a/packages/block-editor/CHANGELOG.md b/packages/block-editor/CHANGELOG.md index e3d5cab8586f85..3a99a7fec0f1e4 100644 --- a/packages/block-editor/CHANGELOG.md +++ b/packages/block-editor/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Enhancement + +- `BlockLockModal`: Move Icon component out of CheckboxControl label ([#45535](https://github.com/WordPress/gutenberg/pull/45535)) + ## 10.4.0 (2022-11-02) ### Bug Fix diff --git a/packages/block-editor/src/components/block-lock/modal.js b/packages/block-editor/src/components/block-lock/modal.js index fec05139360810..00429c3a9b6992 100644 --- a/packages/block-editor/src/components/block-lock/modal.js +++ b/packages/block-editor/src/components/block-lock/modal.js @@ -136,18 +136,7 @@ export default function BlockLockModal( { clientId, onClose } ) {
  • - { __( 'Restrict editing' ) } - - - } + label={ __( 'Restrict editing' ) } checked={ !! lock.edit } onChange={ ( edit ) => setLock( ( prevLock ) => ( { @@ -156,23 +145,16 @@ export default function BlockLockModal( { clientId, onClose } ) { } ) ) } /> +
  • ) }
  • - { __( 'Disable movement' ) } - - - } + label={ __( 'Disable movement' ) } checked={ lock.move } onChange={ ( move ) => setLock( ( prevLock ) => ( { @@ -181,22 +163,15 @@ export default function BlockLockModal( { clientId, onClose } ) { } ) ) } /> +
  • - { __( 'Prevent removal' ) } - - - } + label={ __( 'Prevent removal' ) } checked={ lock.remove } onChange={ ( remove ) => setLock( ( prevLock ) => ( { @@ -205,6 +180,10 @@ export default function BlockLockModal( { clientId, onClose } ) { } ) ) } /> +
  • { hasTemplateLock && ( diff --git a/packages/block-editor/src/components/block-lock/style.scss b/packages/block-editor/src/components/block-lock/style.scss index 4152168e3e8154..b833ec72d95a59 100644 --- a/packages/block-editor/src/components/block-lock/style.scss +++ b/packages/block-editor/src/components/block-lock/style.scss @@ -25,24 +25,17 @@ } } .block-editor-block-lock-modal__checklist-item { + display: flex; + justify-content: space-between; + align-items: center; + gap: $grid-unit-15; margin-bottom: 0; padding: $grid-unit-15 0 $grid-unit-15 $grid-unit-40; - .components-base-control__field { - align-items: center; - display: flex; - } - - .components-checkbox-control__label { - display: flex; - align-items: center; - justify-content: space-between; - flex-grow: 1; - - svg { - margin-right: $grid-unit-15; - fill: $gray-900; - } + .block-editor-block-lock-modal__lock-icon { + flex-shrink: 0; + margin-right: $grid-unit-15; + fill: $gray-900; } &:hover { diff --git a/packages/edit-post/CHANGELOG.md b/packages/edit-post/CHANGELOG.md index 488878796586e3..5cf850cdc4bcdd 100644 --- a/packages/edit-post/CHANGELOG.md +++ b/packages/edit-post/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Enhancement + +- ` BlockTypesChecklist`: Move BlockIcon component out of CheckboxControl label ([#45535](https://github.com/WordPress/gutenberg/pull/45535)) + ## 6.18.0 (2022-11-02) ## 6.17.0 (2022-10-19) diff --git a/packages/edit-post/src/components/block-manager/checklist.js b/packages/edit-post/src/components/block-manager/checklist.js index 02481f0995e2e0..aa21fefb1c8180 100644 --- a/packages/edit-post/src/components/block-manager/checklist.js +++ b/packages/edit-post/src/components/block-manager/checklist.js @@ -14,17 +14,13 @@ function BlockTypesChecklist( { blockTypes, value, onItemChange } ) { > - { blockType.title } - - - } + label={ blockType.title } checked={ value.includes( blockType.name ) } onChange={ ( ...args ) => onItemChange( blockType.name, ...args ) } /> + ) ) } diff --git a/packages/edit-post/src/components/block-manager/style.scss b/packages/edit-post/src/components/block-manager/style.scss index 17e98948ac7c28..d8f9b78fe5a391 100644 --- a/packages/edit-post/src/components/block-manager/style.scss +++ b/packages/edit-post/src/components/block-manager/style.scss @@ -51,29 +51,19 @@ .edit-post-block-manager__category-title, .edit-post-block-manager__checklist-item { border-bottom: 1px solid $gray-300; - - .components-base-control__field { - align-items: center; - display: flex; - } } .edit-post-block-manager__checklist-item { + display: flex; + justify-content: space-between; + align-items: center; margin-bottom: 0; - padding-left: $grid-unit-20; + padding: $grid-unit-10 0 $grid-unit-10 $grid-unit-20; .components-modal__content &.components-checkbox-control__input-container { margin: 0 $grid-unit-10; } - .components-checkbox-control__label { - display: flex; - align-items: center; - justify-content: space-between; - flex-grow: 1; - padding: $grid-unit-10 0; - } - .block-editor-block-icon { margin-right: 10px; fill: $gray-900; From 84b5393c58099b7884ca85efaf7e6f4a2ccca0da Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Wed, 9 Nov 2022 14:01:31 -0700 Subject: [PATCH 14/66] Query Pagination: Fix positioning of next link in editor when parent is selected (#45651) --- packages/block-library/src/query-pagination/style.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/query-pagination/style.scss b/packages/block-library/src/query-pagination/style.scss index db5e90232532d0..a7f2832af48c7a 100644 --- a/packages/block-library/src/query-pagination/style.scss +++ b/packages/block-library/src/query-pagination/style.scss @@ -18,7 +18,7 @@ $pagination-margin: 0.5em; // which is important when it's the only block displayed // and the block has a "space-between" justification. &.is-content-justification-space-between { - > .wp-block-query-pagination-next:last-child { + > .wp-block-query-pagination-next:last-of-type { margin-inline-start: auto; } > .wp-block-query-pagination-previous:first-child { From 18b64f59a2edebd1bed41b26740c78f16f183ee0 Mon Sep 17 00:00:00 2001 From: Ramon Date: Thu, 10 Nov 2022 08:07:01 +1100 Subject: [PATCH 15/66] Replacing button fieldsets with ToggleGroupControl component to improve consistency (#45637) --- packages/block-editor/src/layouts/flex.js | 81 ++++++++++++----------- 1 file changed, 43 insertions(+), 38 deletions(-) diff --git a/packages/block-editor/src/layouts/flex.js b/packages/block-editor/src/layouts/flex.js index 57dbf1005d5283..948eceb6624d60 100644 --- a/packages/block-editor/src/layouts/flex.js +++ b/packages/block-editor/src/layouts/flex.js @@ -10,7 +10,14 @@ import { arrowRight, arrowDown, } from '@wordpress/icons'; -import { Button, ToggleControl, Flex, FlexItem } from '@wordpress/components'; +import { + Button, + ToggleControl, + Flex, + FlexItem, + __experimentalToggleGroupControl as ToggleGroupControl, + __experimentalToggleGroupControlOptionIcon as ToggleGroupControlOptionIcon, +} from '@wordpress/components'; /** * Internal dependencies @@ -289,22 +296,23 @@ function FlexLayoutJustifyContentControl( { } return ( -
    - { __( 'Justification' ) } -
    - { justificationOptions.map( ( { value, icon, label } ) => { - return ( -
    -
    + + { justificationOptions.map( ( { value, icon, label } ) => { + return ( + + ); + } ) } + ); } @@ -327,30 +335,27 @@ function FlexWrapControl( { layout, onChange } ) { function OrientationControl( { layout, onChange } ) { const { orientation = 'horizontal' } = layout; return ( -
    - { __( 'Orientation' ) } -
    + ); } From 5411c05858914f3e6b0572a82832ad1fc76de715 Mon Sep 17 00:00:00 2001 From: flootr Date: Wed, 9 Nov 2022 14:26:05 -0700 Subject: [PATCH 16/66] Components: fix `useCx` story to satisfy `exhaustive-deps` eslint rule (#45614) * move `eslint-ignore-*` to the correct line * update CHANGELOG.md --- packages/components/CHANGELOG.md | 1 + packages/components/src/utils/hooks/stories/use-cx.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 848f00a8e270be..76ed17d09c30c1 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -17,6 +17,7 @@ - `withNotices`: Update to pass `exhaustive-deps` eslint rule ([#45530](https://github.com/WordPress/gutenberg/pull/45530)). - `ItemGroup`: Update to pass `exhaustive-deps` eslint rule ([#45531](https://github.com/WordPress/gutenberg/pull/45531)). - `Draggable`: Convert to TypeScript ([#45471](https://github.com/WordPress/gutenberg/pull/45471)). +- `useCx`: fix story to satisfy the `react-hooks/exhaustive-deps` eslint rule ([#45614](https://github.com/WordPress/gutenberg/pull/45614)) ### Experimental diff --git a/packages/components/src/utils/hooks/stories/use-cx.js b/packages/components/src/utils/hooks/stories/use-cx.js index b8a85f0834dbd7..e7054a8f53952b 100644 --- a/packages/components/src/utils/hooks/stories/use-cx.js +++ b/packages/components/src/utils/hooks/stories/use-cx.js @@ -32,9 +32,9 @@ const Example = ( { serializedStyles, children } ) => { const ExampleWithUseMemoWrong = ( { serializedStyles, children } ) => { const cx = useCx(); // Wrong: using 'useMemo' without adding 'cx' to the dependency list. - // eslint-disable-next-line react-hooks/exhaustive-deps const classes = useMemo( () => cx( serializedStyles ), + // eslint-disable-next-line react-hooks/exhaustive-deps [ serializedStyles ] ); return { children }; From 94055cec911a3a617d8e16025a910ef0b227f925 Mon Sep 17 00:00:00 2001 From: Wojtek Naruniec Date: Wed, 9 Nov 2022 22:47:12 +0100 Subject: [PATCH 17/66] Make Author block selector to display all users instead of just 10 (#45640) * Make Author block selector to display all users instead of just 10 * Change from unlimited to 100 I am changing the limit from -1 to 100 as for now, to fix issue for most cases, and at the same time, to avoid introducing performance problems. Additionally, I include support for Combobox control for cases with more than 25 users to keep it aligned with other parts of the interface. --- .../block-library/src/post-author/edit.js | 64 +++++++++++++------ 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/packages/block-library/src/post-author/edit.js b/packages/block-library/src/post-author/edit.js index ee8f0722e82bf1..1fc13ea42add9f 100644 --- a/packages/block-library/src/post-author/edit.js +++ b/packages/block-library/src/post-author/edit.js @@ -13,11 +13,23 @@ import { RichText, useBlockProps, } from '@wordpress/block-editor'; -import { PanelBody, SelectControl, ToggleControl } from '@wordpress/components'; +import { + ComboboxControl, + PanelBody, + SelectControl, + ToggleControl, +} from '@wordpress/components'; import { useSelect, useDispatch } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; import { store as coreStore } from '@wordpress/core-data'; +const minimumUsersForCombobox = 25; + +const AUTHORS_QUERY = { + who: 'authors', + per_page: 100, +}; + function PostAuthorEdit( { isSelected, context: { postType, postId, queryId }, @@ -38,7 +50,7 @@ function PostAuthorEdit( { return { authorId: _authorId, authorDetails: _authorId ? getUser( _authorId ) : null, - authors: getUsers( { who: 'authors' } ), + authors: getUsers( AUTHORS_QUERY ), }; }, [ postType, postId ] @@ -65,34 +77,46 @@ function PostAuthorEdit( { } ), } ); + const authorOptions = authors?.length + ? authors.map( ( { id, name } ) => { + return { + value: id, + label: name, + }; + } ) + : []; + + const handleSelect = ( nextAuthorId ) => { + editEntityRecord( 'postType', postType, postId, { + author: nextAuthorId, + } ); + }; + + const showCombobox = authorOptions.length >= minimumUsersForCombobox; + return ( <> { !! postId && ! isDescendentOfQueryLoop && - !! authors?.length && ( + authorOptions.length && + ( ( showCombobox && ( + + ) ) || ( { - return { - value: id, - label: name, - }; - } ) } - onChange={ ( nextAuthorId ) => { - editEntityRecord( - 'postType', - postType, - postId, - { - author: nextAuthorId, - } - ); - } } + options={ authorOptions } + onChange={ handleSelect } /> - ) } + ) ) } Date: Wed, 9 Nov 2022 14:50:01 -0700 Subject: [PATCH 18/66] Remove hover style to button on dark block tool ui (#45653) --- packages/block-editor/src/components/block-tools/style.scss | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/block-editor/src/components/block-tools/style.scss b/packages/block-editor/src/components/block-tools/style.scss index ea6cba19639cdb..e301859c165491 100644 --- a/packages/block-editor/src/components/block-tools/style.scss +++ b/packages/block-editor/src/components/block-tools/style.scss @@ -175,6 +175,12 @@ &:active { color: $white; } + + // Make sure the button has no hover style when it's disabled. + &[aria-disabled="true"]:hover { + color: $white; + } + display: flex; } .block-selection-button_select-button.components-button { From a2f6e21b9ae2f047015f706dce914c30f15c83b3 Mon Sep 17 00:00:00 2001 From: JuanMa Date: Wed, 9 Nov 2022 14:50:52 -0700 Subject: [PATCH 19/66] Move "Available commands" section of create-package to a subpage (#45636) * move available commands to a subpage * added manifest.json * removing unnecesary extra file * removed unnecesary descriptions of sscripts and linked to the source of truth for those scripts * improved description to include some clarifications useful for non-JS developers * improved readability * improved readability --- packages/create-block/README.md | 48 ++++----------------------------- 1 file changed, 5 insertions(+), 43 deletions(-) diff --git a/packages/create-block/README.md b/packages/create-block/README.md index 36d0d632cc391b..46be7b914450bc 100644 --- a/packages/create-block/README.md +++ b/packages/create-block/README.md @@ -95,54 +95,16 @@ $ npx @wordpress/create-block --no-plugin When you scaffold a block, you must provide at least a `slug` name, the `namespace` which usually corresponds to either the `theme` or `plugin` name. In most cases, we recommended pairing blocks with WordPress plugins rather than themes, because only using plugin ensures that all blocks still work when your theme changes. -## Available Commands +## Available commands in the scaffolded project -When bootstrapped with the `static` template (or any other project template with `wpScripts` flag enabled), you can run several commands inside the directory: +The plugin folder created when executing this command, is a node package with a modern build setup that requires no configuration. -```bash -$ npm start -``` - -Starts the build for development. [Learn more](https://github.com/WordPress/gutenberg/tree/HEAD/packages/scripts#start). - -```bash -$ npm run build -``` - -Builds the code for production. [Learn more](https://github.com/WordPress/gutenberg/tree/HEAD/packages/scripts#build). - -```bash -$ npm run format -``` - -Formats files. [Learn more](https://github.com/WordPress/gutenberg/tree/HEAD/packages/scripts#format). - -```bash -$ npm run lint:css -``` - -Lints CSS files. [Learn more](https://github.com/WordPress/gutenberg/tree/HEAD/packages/scripts#lint-style). - -```bash -$ npm run lint:js -``` - -Lints JavaScript files. [Learn more](https://github.com/WordPress/gutenberg/tree/HEAD/packages/scripts#lint-js). - -```bash -$ npm run plugin-zip -``` - -Creates a zip file for a WordPress plugin. [Learn more](https://github.com/WordPress/gutenberg/tree/HEAD/packages/scripts#plugin-zip). - -```bash -$ npm run packages-update -``` - -Updates WordPress packages to the latest version. [Learn more](https://github.com/WordPress/gutenberg/tree/HEAD/packages/scripts#packages-update). +A set of scripts is available from inside that folder (provided by the `scripts` package) to make your work easier. [Click here](https://github.com/WordPress/gutenberg/tree/HEAD/packages/scripts#available-scripts) for a full description of these commands. _Note: You don’t need to install or configure tools like [webpack](https://webpack.js.org), [Babel](https://babeljs.io) or [ESLint](https://eslint.org) yourself. They are preconfigured and hidden so that you can focus on coding._ +For example, running the `start` script from inside the generated folder (`npm start`) would automatically start the build for development. + ## External Project Templates [Click here](https://github.com/WordPress/gutenberg/tree/HEAD/packages/create-block/docs/external-template.md) for information on External Project Templates From 3284f343a4c69a74a1e29eac03a14d0d2c6c9cc1 Mon Sep 17 00:00:00 2001 From: Daisy Olsen Date: Wed, 9 Nov 2022 14:54:18 -0700 Subject: [PATCH 20/66] Change block style references to omit "varations" (#45650) --- packages/block-editor/src/components/block-styles/utils.js | 6 +++--- packages/blocks/README.md | 4 ++-- packages/blocks/src/api/registration.js | 4 ++-- packages/blocks/src/store/reducer.js | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/block-editor/src/components/block-styles/utils.js b/packages/block-editor/src/components/block-styles/utils.js index 7f738ab0fa923c..f60a23a4287b21 100644 --- a/packages/block-editor/src/components/block-styles/utils.js +++ b/packages/block-editor/src/components/block-styles/utils.js @@ -11,7 +11,7 @@ import { _x } from '@wordpress/i18n'; /** * Returns the active style from the given className. * - * @param {Array} styles Block style variations. + * @param {Array} styles Block styles. * @param {string} className Class name * * @return {Object?} The active style. @@ -59,7 +59,7 @@ export function replaceActiveStyle( className, activeStyle, newStyle ) { * act as a fallback for when there is no active style applied to a block. The default item also serves * as a switch on the frontend to deactivate non-default styles. * - * @param {Array} styles Block style variations. + * @param {Array} styles Block styles. * * @return {Array} The style collection. */ @@ -83,7 +83,7 @@ export function getRenderedStyles( styles ) { /** * Returns a style object from a collection of styles where that style object is the default block style. * - * @param {Array} styles Block style variations. + * @param {Array} styles Block styles. * * @return {Object?} The default style object, if found. */ diff --git a/packages/blocks/README.md b/packages/blocks/README.md index 20930df1a3a9f5..f78783f7283392 100644 --- a/packages/blocks/README.md +++ b/packages/blocks/README.md @@ -692,7 +692,7 @@ _Parameters_ ### registerBlockStyle -Registers a new block style variation for the given block. +Registers a new block style for the given block. For more information on connecting the styles with CSS [the official documentation](/docs/reference-guides/block-api/block-styles.md#styles) @@ -970,7 +970,7 @@ _Returns_ ### unregisterBlockStyle -Unregisters a block style variation for the given block. +Unregisters a block style for the given block. _Usage_ diff --git a/packages/blocks/src/api/registration.js b/packages/blocks/src/api/registration.js index 903d9e7e00e32a..f8f5ac996173b6 100644 --- a/packages/blocks/src/api/registration.js +++ b/packages/blocks/src/api/registration.js @@ -656,7 +656,7 @@ export const hasChildBlocksWithInserterSupport = ( blockName ) => { }; /** - * Registers a new block style variation for the given block. + * Registers a new block style for the given block. * * For more information on connecting the styles with CSS [the official documentation](/docs/reference-guides/block-api/block-styles.md#styles) * @@ -691,7 +691,7 @@ export const registerBlockStyle = ( blockName, styleVariation ) => { }; /** - * Unregisters a block style variation for the given block. + * Unregisters a block style for the given block. * * @param {string} blockName Name of block (example: “core/latest-posts”). * @param {string} styleVariationName Name of class applied to the block. diff --git a/packages/blocks/src/store/reducer.js b/packages/blocks/src/store/reducer.js index 242876476f14ff..6770dd6b67f26b 100644 --- a/packages/blocks/src/store/reducer.js +++ b/packages/blocks/src/store/reducer.js @@ -105,7 +105,7 @@ export function blockTypes( state = {}, action ) { } /** - * Reducer managing the block style variations. + * Reducer managing the block styles. * * @param {Object} state Current state. * @param {Object} action Dispatched action. From 7dd31ab2a3a4460493d97c3356c585879b9a1292 Mon Sep 17 00:00:00 2001 From: Ben Dwyer Date: Wed, 9 Nov 2022 15:02:27 -0700 Subject: [PATCH 21/66] Global Styles: Elements: Add a text decoration control to link elements (#45643) * Global Styles: Elements: Add a text decoration control to link elements * Update packages/edit-site/src/components/global-styles/typography-panel.js Co-authored-by: Robert Anderson Co-authored-by: Robert Anderson --- .../global-styles/typography-panel.js | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/packages/edit-site/src/components/global-styles/typography-panel.js b/packages/edit-site/src/components/global-styles/typography-panel.js index 3e53486af2121c..82f7dc796d92f1 100644 --- a/packages/edit-site/src/components/global-styles/typography-panel.js +++ b/packages/edit-site/src/components/global-styles/typography-panel.js @@ -7,6 +7,7 @@ import { __experimentalFontAppearanceControl as FontAppearanceControl, __experimentalLetterSpacingControl as LetterSpacingControl, __experimentalTextTransformControl as TextTransformControl, + __experimentalTextDecorationControl as TextDecorationControl, } from '@wordpress/block-editor'; import { FontSizePicker, @@ -101,6 +102,13 @@ function useHasTextTransformControl( name, element ) { return supports.includes( 'textTransform' ); } +function useHasTextDecorationControl( name, element ) { + // This is an exception for link elements. + // We shouldn't allow other blocks or elements to set textDecoration + // because this will be inherited by their children. + return ! name && element === 'link'; +} + function useStyleWithReset( path, blockName ) { const [ style, setStyle ] = useStyle( path, blockName ); const [ userStyle ] = useStyle( path, blockName, 'user' ); @@ -190,6 +198,10 @@ export default function TypographyPanel( { name, element, headingLevel } ) { const appearanceControlLabel = useAppearanceControlLabel( name ); const hasLetterSpacingControl = useHasLetterSpacingControl( name, element ); const hasTextTransformControl = useHasTextTransformControl( name, element ); + const hasTextDecorationControl = useHasTextDecorationControl( + name, + element + ); /* Disable font size controls when the option to style all headings is selected. */ let hasFontSizeEnabled = supports.includes( 'fontSize' ); @@ -223,6 +235,12 @@ export default function TypographyPanel( { name, element, headingLevel } ) { hasTextTransform, resetTextTransform, ] = useStyleWithReset( prefix + 'typography.textTransform', name ); + const [ + textDecoration, + setTextDecoration, + hasTextDecoration, + resetTextDecoration, + ] = useStyleWithReset( prefix + 'typography.textDecoration', name ); const resetAll = () => { resetFontFamily(); @@ -347,6 +365,22 @@ export default function TypographyPanel( { name, element, headingLevel } ) { /> ) } + { hasTextDecorationControl && ( + + + + ) } ); } From 1073bbb3cab6101881536944460e4a9005e94a75 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Wed, 9 Nov 2022 22:03:09 +0000 Subject: [PATCH 22/66] Fix: Button block text alignment. (#45663) --- docs/reference-guides/core-blocks.md | 4 ++-- packages/block-library/src/button/block.json | 5 ++++- .../block-library/src/button/deprecated.js | 20 +++++++++++++++++-- packages/block-library/src/button/edit.js | 10 +++++++++- packages/block-library/src/button/save.js | 14 +++++++++++-- .../core__button__center__deprecated.json | 3 ++- ...button__center__deprecated.serialized.html | 2 +- 7 files changed, 48 insertions(+), 10 deletions(-) diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index a543e36babb11c..4ed71b8527b61b 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -50,8 +50,8 @@ Prompt visitors to take action with a button-style link. ([Source](https://githu - **Name:** core/button - **Category:** design -- **Supports:** align, anchor, color (background, gradients, text), spacing (padding), typography (fontSize, lineHeight), ~~alignWide~~, ~~reusable~~ -- **Attributes:** backgroundColor, gradient, linkTarget, placeholder, rel, text, textColor, title, url, width +- **Supports:** anchor, color (background, gradients, text), spacing (padding), typography (fontSize, lineHeight), ~~alignWide~~, ~~align~~, ~~reusable~~ +- **Attributes:** backgroundColor, gradient, linkTarget, placeholder, rel, text, textAlign, textColor, title, url, width ## Buttons diff --git a/packages/block-library/src/button/block.json b/packages/block-library/src/button/block.json index 6076db9b8feec3..f34437f74b573e 100644 --- a/packages/block-library/src/button/block.json +++ b/packages/block-library/src/button/block.json @@ -9,6 +9,9 @@ "keywords": [ "link" ], "textdomain": "default", "attributes": { + "textAlign": { + "type": "string" + }, "url": { "type": "string", "source": "attribute", @@ -56,7 +59,7 @@ }, "supports": { "anchor": true, - "align": true, + "align": false, "alignWide": false, "color": { "__experimentalSkipSerialization": true, diff --git a/packages/block-library/src/button/deprecated.js b/packages/block-library/src/button/deprecated.js index 2da17df69cde51..1ec6fde3fe4a48 100644 --- a/packages/block-library/src/button/deprecated.js +++ b/packages/block-library/src/button/deprecated.js @@ -51,6 +51,20 @@ const migrateBorderRadius = ( attributes ) => { }; }; +function migrateAlign( attributes ) { + if ( ! attributes.align ) { + return attributes; + } + const { align, ...otherAttributes } = attributes; + return { + ...otherAttributes, + className: classnames( + otherAttributes.className, + `align${ attributes.align }` + ), + }; +} + const migrateCustomColorsAndGradients = ( attributes ) => { if ( ! attributes.customTextColor && @@ -780,10 +794,12 @@ const deprecated = [ isEligible: ( attributes ) => !! attributes.customTextColor || !! attributes.customBackgroundColor || - !! attributes.customGradient, + !! attributes.customGradient || + !! attributes.align, migrate: compose( migrateBorderRadius, - migrateCustomColorsAndGradients + migrateCustomColorsAndGradients, + migrateAlign ), save( { attributes } ) { const { diff --git a/packages/block-library/src/button/edit.js b/packages/block-library/src/button/edit.js index c222b55943c1b4..fd38379636faf3 100644 --- a/packages/block-library/src/button/edit.js +++ b/packages/block-library/src/button/edit.js @@ -17,6 +17,7 @@ import { Popover, } from '@wordpress/components'; import { + AlignmentControl, BlockControls, InspectorControls, RichText, @@ -76,7 +77,7 @@ function ButtonEdit( props ) { onReplace, mergeBlocks, } = props; - const { linkTarget, placeholder, rel, style, text, url, width } = + const { textAlign, linkTarget, placeholder, rel, style, text, url, width } = attributes; function onToggleOpenInNewTab( value ) { @@ -170,6 +171,7 @@ function ButtonEdit( props ) { colorProps.className, borderProps.className, { + [ `has-text-align-${ textAlign }` ]: textAlign, // For backwards compatibility add style that isn't // provided via block support. 'no-border-radius': style?.border?.radius === 0, @@ -193,6 +195,12 @@ function ButtonEdit( props ) { />
    + { + setAttributes( { textAlign: nextAlign } ); + } } + /> { ! isURLSet && ( + From edafbbf66bfd1aae50b390dcf7d73471a3e62aa3 Mon Sep 17 00:00:00 2001 From: Yuliyan Slavchev Date: Thu, 10 Nov 2022 00:24:04 +0200 Subject: [PATCH 23/66] Navigation: Fix overflowing menu name in the navigation selector dropdown (#45647) * Navigation: Fix overflowing menu name in the navigation selector dropdown * Force the navigation selector dropdown label to be on a single line * Revert unrelated CSS linting fixes --- .../src/navigation/edit/navigation-menu-selector.js | 6 +++++- packages/block-library/src/navigation/editor.scss | 11 +++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/block-library/src/navigation/edit/navigation-menu-selector.js b/packages/block-library/src/navigation/edit/navigation-menu-selector.js index 8103dfeef3d1ba..9817934c3a2b16 100644 --- a/packages/block-library/src/navigation/edit/navigation-menu-selector.js +++ b/packages/block-library/src/navigation/edit/navigation-menu-selector.js @@ -170,7 +170,11 @@ function NavigationMenuSelector( { : 'wp-block-navigation__navigation-selector' } label={ selectorLabel } - text={ isOffCanvasNavigationEditorEnabled ? '' : selectorLabel } + text={ + + { isOffCanvasNavigationEditorEnabled ? '' : selectorLabel } + + } icon={ isOffCanvasNavigationEditorEnabled ? moreVertical : null } toggleProps={ isOffCanvasNavigationEditorEnabled diff --git a/packages/block-library/src/navigation/editor.scss b/packages/block-library/src/navigation/editor.scss index 68802b67adf812..8b2dd93e3010be 100644 --- a/packages/block-library/src/navigation/editor.scss +++ b/packages/block-library/src/navigation/editor.scss @@ -639,6 +639,17 @@ body.editor-styles-wrapper width: 100%; } +.wp-block-navigation__navigation-selector-button__icon { + flex: 0 0 auto; +} + +.wp-block-navigation__navigation-selector-button__label { + flex: 0 1 auto; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + .wp-block-navigation__navigation-selector-button--createnew { border: 1px solid; margin-bottom: $grid-unit-20; From 171ca59ef8c612467bacb632170dd4a32b02d816 Mon Sep 17 00:00:00 2001 From: Ramon Date: Thu, 10 Nov 2022 09:34:50 +1100 Subject: [PATCH 24/66] Converting View component's index.js > index.ts (#45667) --- packages/components/src/view/{index.js => index.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/components/src/view/{index.js => index.ts} (100%) diff --git a/packages/components/src/view/index.js b/packages/components/src/view/index.ts similarity index 100% rename from packages/components/src/view/index.js rename to packages/components/src/view/index.ts From 7fd1dc58eaa357c1079201007a263bf1f9e60dde Mon Sep 17 00:00:00 2001 From: flootr Date: Wed, 9 Nov 2022 15:53:06 -0700 Subject: [PATCH 25/66] `NavigatorScreen`: satisfy `exhaustive-deps` eslint rule (#45648) * `NavigatorScreen`: satisfy `exhaustive-deps` eslint rule * CHANGELOG Co-authored-by: Marco Ciampini --- packages/components/CHANGELOG.md | 1 + .../src/navigator/navigator-screen/component.tsx | 11 ++++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 76ed17d09c30c1..b25cb955aa6768 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -16,6 +16,7 @@ - `Flex`: Update to pass `exhaustive-deps` eslint rule ([#45528](https://github.com/WordPress/gutenberg/pull/45528)). - `withNotices`: Update to pass `exhaustive-deps` eslint rule ([#45530](https://github.com/WordPress/gutenberg/pull/45530)). - `ItemGroup`: Update to pass `exhaustive-deps` eslint rule ([#45531](https://github.com/WordPress/gutenberg/pull/45531)). +- `NavigatorScreen`: Update to pass `exhaustive-deps` eslint rule ([#45648](https://github.com/WordPress/gutenberg/pull/45648)). - `Draggable`: Convert to TypeScript ([#45471](https://github.com/WordPress/gutenberg/pull/45471)). - `useCx`: fix story to satisfy the `react-hooks/exhaustive-deps` eslint rule ([#45614](https://github.com/WordPress/gutenberg/pull/45614)) diff --git a/packages/components/src/navigator/navigator-screen/component.tsx b/packages/components/src/navigator/navigator-screen/component.tsx index 7365bf9817e81f..266bd553e0a8d2 100644 --- a/packages/components/src/navigator/navigator-screen/component.tsx +++ b/packages/components/src/navigator/navigator-screen/component.tsx @@ -75,6 +75,12 @@ function UnconnectedNavigatorScreen( [ className, cx ] ); + const locationRef = useRef( location ); + + useEffect( () => { + locationRef.current = location; + }, [ location ] ); + // Focus restoration const isInitialLocation = location.isInitial && ! location.isBack; useEffect( () => { @@ -87,7 +93,7 @@ function UnconnectedNavigatorScreen( isInitialLocation || ! isMatch || ! wrapperRef.current || - location.hasRestoredFocus + locationRef.current.hasRestoredFocus ) { return; } @@ -119,12 +125,11 @@ function UnconnectedNavigatorScreen( elementToFocus = firstTabbable ?? wrapperRef.current; } - location.hasRestoredFocus = true; + locationRef.current.hasRestoredFocus = true; elementToFocus.focus(); }, [ isInitialLocation, isMatch, - location.hasRestoredFocus, location.isBack, previousLocation?.focusTargetSelector, ] ); From f8c3f1f4cf05dd18cb67e58f7d9a692fbf7b5ea7 Mon Sep 17 00:00:00 2001 From: Erick Danzer Date: Wed, 9 Nov 2022 16:18:00 -0700 Subject: [PATCH 26/66] Adjust template part block isActive method (#45672) --- packages/block-library/src/template-part/variations.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/block-library/src/template-part/variations.js b/packages/block-library/src/template-part/variations.js index d39b3e5e8a6bc5..866cf15d56c125 100644 --- a/packages/block-library/src/template-part/variations.js +++ b/packages/block-library/src/template-part/variations.js @@ -40,6 +40,10 @@ export function enhanceTemplatePartVariations( settings, name ) { 'wp_template_part', `${ theme }//${ slug }` ); + + if ( entity?.slug ) { + return entity.slug === variationAttributes.slug; + } return entity?.area === variationAttributes.area; }; From 07c948dfa56f51923c668c430b3bdff9d14e6a1b Mon Sep 17 00:00:00 2001 From: Dean Sas Date: Wed, 9 Nov 2022 23:25:31 +0000 Subject: [PATCH 27/66] Columns: Add transform to unwrap the contents (#45666) Add a transform to move the column contents out into standalone blocks. --- packages/block-library/src/columns/transforms.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/block-library/src/columns/transforms.js b/packages/block-library/src/columns/transforms.js index 9fb80e49e4295c..04fc932f529643 100644 --- a/packages/block-library/src/columns/transforms.js +++ b/packages/block-library/src/columns/transforms.js @@ -105,6 +105,16 @@ const transforms = { }, }, ], + to: [ + { + type: 'block', + blocks: [ '*' ], + transform: ( attributes, innerBlocks ) => + innerBlocks + .map( ( innerBlock ) => innerBlock.innerBlocks ) + .flat(), + }, + ], }; export default transforms; From 6d804a95b69dd5dbbaa522dfe87ea3dffd0f29fc Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Wed, 9 Nov 2022 23:38:13 +0000 Subject: [PATCH 28/66] Strip HTML from Post Title when pasting (#35825) * Strip HTML from the post title field portion of the blocks * Also strip HTML if the content is a string Co-authored-by: Ben Dwyer --- package-lock.json | 1 + packages/editor/package.json | 1 + packages/editor/src/components/post-title/index.js | 8 ++++++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index f51b791101fc10..e00ad245d20c9f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18110,6 +18110,7 @@ "@wordpress/data": "file:packages/data", "@wordpress/date": "file:packages/date", "@wordpress/deprecated": "file:packages/deprecated", + "@wordpress/dom": "file:packages/dom", "@wordpress/element": "file:packages/element", "@wordpress/hooks": "file:packages/hooks", "@wordpress/html-entities": "file:packages/html-entities", diff --git a/packages/editor/package.json b/packages/editor/package.json index 0fb5506b6e05c2..288a0d427b582f 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -42,6 +42,7 @@ "@wordpress/data": "file:../data", "@wordpress/date": "file:../date", "@wordpress/deprecated": "file:../deprecated", + "@wordpress/dom": "file:../dom", "@wordpress/element": "file:../element", "@wordpress/hooks": "file:../hooks", "@wordpress/html-entities": "file:../html-entities", diff --git a/packages/editor/src/components/post-title/index.js b/packages/editor/src/components/post-title/index.js index 1092ee5959c0bd..b9143a29ff3c05 100644 --- a/packages/editor/src/components/post-title/index.js +++ b/packages/editor/src/components/post-title/index.js @@ -26,6 +26,7 @@ import { insert, } from '@wordpress/rich-text'; import { useMergeRefs } from '@wordpress/compose'; +import { __unstableStripHTML as stripHTML } from '@wordpress/dom'; /** * Internal dependencies @@ -166,7 +167,7 @@ function PostTitle( _, forwardedRef ) { ( firstBlock.name === 'core/heading' || firstBlock.name === 'core/paragraph' ) ) { - onUpdate( firstBlock.attributes.content ); + onUpdate( stripHTML( firstBlock.attributes.content ) ); onInsertBlockAfter( content.slice( 1 ) ); } else { onInsertBlockAfter( content ); @@ -176,7 +177,10 @@ function PostTitle( _, forwardedRef ) { ...create( { html: title } ), ...selection, }; - const newValue = insert( value, create( { html: content } ) ); + const newValue = insert( + value, + create( { html: stripHTML( content ) } ) + ); onUpdate( toHTMLString( { value: newValue } ) ); setSelection( { start: newValue.start, From 1db30dfdd59ffcf22c9677f385d38063d406f0cc Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Wed, 9 Nov 2022 16:50:23 -0700 Subject: [PATCH 29/66] Table block: Fix error in margin value (#45674) --- packages/block-library/src/table/theme.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/table/theme.scss b/packages/block-library/src/table/theme.scss index 81558143627876..3adc4d3e452ba7 100644 --- a/packages/block-library/src/table/theme.scss +++ b/packages/block-library/src/table/theme.scss @@ -1,5 +1,5 @@ .wp-block-table { - margin: "0 0 1em 0"; + margin: 0 0 1em 0; thead { border-bottom: 3px solid; From 0636851b0e3857b973626318ea4101a80bea3ba1 Mon Sep 17 00:00:00 2001 From: Vicente Canales <1157901+vcanales@users.noreply.github.com> Date: Wed, 9 Nov 2022 16:57:59 -0700 Subject: [PATCH 30/66] Remove width from block mover button focus style (#45665) --- packages/block-editor/src/components/block-mover/style.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/block-editor/src/components/block-mover/style.scss b/packages/block-editor/src/components/block-mover/style.scss index eba7bbed683045..cf4061b3789dfb 100644 --- a/packages/block-editor/src/components/block-mover/style.scss +++ b/packages/block-editor/src/components/block-mover/style.scss @@ -15,7 +15,6 @@ // Focus style. &::before { height: calc(100% - 4px); - width: calc(100% - 4px); } } From d2ab8ffcf4c3f1e860cb7159c49bcbfdf51043a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petter=20Walb=C3=B8=20Johnsg=C3=A5rd?= Date: Thu, 10 Nov 2022 01:04:55 +0100 Subject: [PATCH 31/66] MenuGroup: Convert component to TypeScript (#45617) --- packages/components/CHANGELOG.md | 3 ++- .../src/menu-group/{index.js => index.tsx} | 25 ++++++++++++++++--- .../menu-group/test/{index.js => index.tsx} | 0 packages/components/src/menu-group/types.ts | 23 +++++++++++++++++ .../core__button__center__deprecated.json | 1 - 5 files changed, 47 insertions(+), 5 deletions(-) rename packages/components/src/menu-group/{index.js => index.tsx} (60%) rename packages/components/src/menu-group/test/{index.js => index.tsx} (100%) create mode 100644 packages/components/src/menu-group/types.ts diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index b25cb955aa6768..8cb09113d05bcf 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -18,6 +18,7 @@ - `ItemGroup`: Update to pass `exhaustive-deps` eslint rule ([#45531](https://github.com/WordPress/gutenberg/pull/45531)). - `NavigatorScreen`: Update to pass `exhaustive-deps` eslint rule ([#45648](https://github.com/WordPress/gutenberg/pull/45648)). - `Draggable`: Convert to TypeScript ([#45471](https://github.com/WordPress/gutenberg/pull/45471)). +- `MenuGroup`: Convert to TypeScript ([#45617](https://github.com/WordPress/gutenberg/pull/45617)). - `useCx`: fix story to satisfy the `react-hooks/exhaustive-deps` eslint rule ([#45614](https://github.com/WordPress/gutenberg/pull/45614)) ### Experimental @@ -91,8 +92,8 @@ - `NumberControl`: Replace `hideHTMLArrows` prop with `spinControls` prop. Allow custom spin controls via `spinControls="custom"` ([#45333](https://github.com/WordPress/gutenberg/pull/45333)). ### Experimental -- Theming: updated Components package to utilize the new `accent` prop of the experimental `Theme` component. +- Theming: updated Components package to utilize the new `accent` prop of the experimental `Theme` component. ## 21.3.0 (2022-10-19) diff --git a/packages/components/src/menu-group/index.js b/packages/components/src/menu-group/index.tsx similarity index 60% rename from packages/components/src/menu-group/index.js rename to packages/components/src/menu-group/index.tsx index 6054bfcb3e4e29..00938928969507 100644 --- a/packages/components/src/menu-group/index.js +++ b/packages/components/src/menu-group/index.tsx @@ -1,4 +1,3 @@ -// @ts-nocheck /** * External dependencies */ @@ -10,7 +9,27 @@ import classnames from 'classnames'; import { Children } from '@wordpress/element'; import { useInstanceId } from '@wordpress/compose'; -export function MenuGroup( props ) { +/** + * Internal dependencies + */ +import type { MenuGroupProps } from './types'; + +/** + * `MenuGroup` wraps a series of related `MenuItem` components into a common + * section. + * + * ```jsx + * import { MenuGroup, MenuItem } from '@wordpress/components'; + * + * const MyMenuGroup = () => ( + * + * Setting 1 + * Setting 2 + * + * ); + * ``` + */ +export function MenuGroup( props: MenuGroupProps ) { const { children, className = '', label, hideSeparator } = props; const instanceId = useInstanceId( MenuGroup ); @@ -34,7 +53,7 @@ export function MenuGroup( props ) { { label } ) } -
    +
    { children }
    diff --git a/packages/components/src/menu-group/test/index.js b/packages/components/src/menu-group/test/index.tsx similarity index 100% rename from packages/components/src/menu-group/test/index.js rename to packages/components/src/menu-group/test/index.tsx diff --git a/packages/components/src/menu-group/types.ts b/packages/components/src/menu-group/types.ts new file mode 100644 index 00000000000000..e73538eea68f24 --- /dev/null +++ b/packages/components/src/menu-group/types.ts @@ -0,0 +1,23 @@ +/** + * External dependencies + */ +import type { ReactNode } from 'react'; + +export type MenuGroupProps = { + /** + * A CSS `class` to give to the container element. + */ + className?: string; + /** + * Hide the top border on the container. + */ + hideSeparator?: boolean; + /** + * Text to be displayed as the menu group header. + */ + label?: string; + /** + * The children elements. + */ + children?: ReactNode; +}; diff --git a/test/integration/fixtures/blocks/core__button__center__deprecated.json b/test/integration/fixtures/blocks/core__button__center__deprecated.json index a8f30ca58b4d7f..458eebb841b877 100644 --- a/test/integration/fixtures/blocks/core__button__center__deprecated.json +++ b/test/integration/fixtures/blocks/core__button__center__deprecated.json @@ -6,7 +6,6 @@ "url": "https://github.com/WordPress/gutenberg", "text": "Help build Gutenberg", "className": "aligncenter" - }, "innerBlocks": [] } From 0fc74ada1d49416430b87bf7dabf909682f8c57b Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Wed, 9 Nov 2022 17:16:54 -0700 Subject: [PATCH 32/66] ToolsPanel: Prevent calling deselect when panel remounts (#45673) * ToolsPanel: Prevent calling deselect when panel remounts * Add link to comment * Add changelog --- packages/components/CHANGELOG.md | 1 + .../components/src/tools-panel/tools-panel-item/hook.ts | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 8cb09113d05bcf..58d5275fd750d5 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -9,6 +9,7 @@ ### Bug Fix - `Autocomplete`: Fix unexpected block insertion during IME composition ([#45510](https://github.com/WordPress/gutenberg/pull/45510)). +- `ToolsPanelItem`: Prevent unintended calls to onDeselect when parent panel is remounted and item is rendered via SlotFill ([#45673](https://github.com/WordPress/gutenberg/pull/45673)) ### Internal diff --git a/packages/components/src/tools-panel/tools-panel-item/hook.ts b/packages/components/src/tools-panel/tools-panel-item/hook.ts index 582ff88113dba5..4e0db5b9881d3e 100644 --- a/packages/components/src/tools-panel/tools-panel-item/hook.ts +++ b/packages/components/src/tools-panel/tools-panel-item/hook.ts @@ -108,11 +108,15 @@ export function useToolsPanelItem( const menuGroup = isShownByDefault ? 'default' : 'optional'; const isMenuItemChecked = menuItems?.[ menuGroup ]?.[ label ]; const wasMenuItemChecked = usePrevious( isMenuItemChecked ); + const isRegistered = menuItems?.[ menuGroup ]?.[ label ] !== undefined; // Determine if the panel item's corresponding menu is being toggled and // trigger appropriate callback if it is. useEffect( () => { - if ( isResetting || ! hasMatchingPanel ) { + // We check whether this item is currently registered as items rendered + // via fills can persist through the parent panel being remounted. + // See: https://github.com/WordPress/gutenberg/pull/45673 + if ( ! isRegistered || isResetting || ! hasMatchingPanel ) { return; } From 3f5416f1ae88fa090535c38f2a2d01e8c07a04ab Mon Sep 17 00:00:00 2001 From: Jacopo Tomasone Date: Wed, 9 Nov 2022 17:36:25 -0700 Subject: [PATCH 33/66] Color Picker: Prevent all number fields to become 0 when one of them is an empty string (#45649) * Prevent zero-ing all ColorPicker fields when one of them is an empty string * Tighten up the type checks --- .../src/color-picker/input-with-slider.tsx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/components/src/color-picker/input-with-slider.tsx b/packages/components/src/color-picker/input-with-slider.tsx index 8e4e16030ccae3..be09e789d7902e 100644 --- a/packages/components/src/color-picker/input-with-slider.tsx +++ b/packages/components/src/color-picker/input-with-slider.tsx @@ -25,6 +25,18 @@ export const InputWithSlider = ( { onChange, value, }: InputWithSliderProps ) => { + const onNumberControlChange = ( newValue?: number | string ) => { + if ( ! newValue ) { + onChange( 0 ); + return; + } + if ( typeof newValue === 'string' ) { + onChange( parseInt( newValue, 10 ) ); + return; + } + onChange( newValue ); + }; + return ( Date: Wed, 9 Nov 2022 17:55:03 -0700 Subject: [PATCH 34/66] Read More block: add aria-label and screen reader text (#45490) * Add aria label + screen reader text to the link. * Use only screen reader text. * Make the screen reader text more universal. * Make the screen reader text explicit when the post is untitled. * Make linter happy. * Move translators note and fix Yoda. --- packages/block-library/src/read-more/index.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/block-library/src/read-more/index.php b/packages/block-library/src/read-more/index.php index 93013b2517d141..7bfd22e6d4c4df 100644 --- a/packages/block-library/src/read-more/index.php +++ b/packages/block-library/src/read-more/index.php @@ -19,15 +19,22 @@ function render_block_core_read_more( $attributes, $content, $block ) { } $post_ID = $block->context['postId']; + $post_title = get_the_title( $post_ID ); + $screen_reader_text = sprintf( + /* translators: %s is either the post title or post ID to describe the link for screen readers. */ + __( ': %s' ), + '' !== $post_title ? $post_title : __( 'untitled post ' ) . $post_ID + ); $justify_class_name = empty( $attributes['justifyContent'] ) ? '' : "is-justified-{$attributes['justifyContent']}"; $wrapper_attributes = get_block_wrapper_attributes( array( 'class' => $justify_class_name ) ); $more_text = ! empty( $attributes['content'] ) ? wp_kses_post( $attributes['content'] ) : __( 'Read more' ); return sprintf( - '%4s', + '%4s%5s', $wrapper_attributes, get_the_permalink( $post_ID ), esc_attr( $attributes['linkTarget'] ), - $more_text + $more_text, + $screen_reader_text ); } From 141ee2485b612a93b04faac9f3aa083a6331b75a Mon Sep 17 00:00:00 2001 From: Marin Atanasov <8436925+tyxla@users.noreply.github.com> Date: Wed, 9 Nov 2022 18:28:38 -0700 Subject: [PATCH 35/66] Components: Fix no-container violations in FormGroup tests (#45662) --- .../src/ui/form-group/test/index.js | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/packages/components/src/ui/form-group/test/index.js b/packages/components/src/ui/form-group/test/index.js index a9c0edb70efadb..b6bfcd4d8f2110 100644 --- a/packages/components/src/ui/form-group/test/index.js +++ b/packages/components/src/ui/form-group/test/index.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { render } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; /** * Internal dependencies @@ -20,44 +20,43 @@ const TextInput = ( { id: idProp, ...props } ) => { /* eslint-disable no-restricted-syntax */ describe( 'props', () => { test( 'should render correctly', () => { - const { container } = render( + render( ); - const label = container.querySelector( 'label' ); - expect( label ).toHaveAttribute( 'for', 'fname' ); - expect( label ).toContainHTML( 'First name' ); - - const input = container.querySelector( 'input' ); - expect( input ).toHaveAttribute( 'id', 'fname' ); + expect( + screen.getByRole( 'textbox', { name: 'First name' } ) + ).toBeVisible(); } ); test( 'should render label without prop correctly', () => { - const { container } = render( + render( First name ); - const label = container.querySelector( 'label' ); - expect( label ).toHaveAttribute( 'for', 'fname' ); - expect( label ).toContainHTML( 'First name' ); + expect( + screen.getByRole( 'textbox', { name: 'First name' } ) + ).toBeVisible(); } ); test( 'should render labelHidden', () => { - const { container } = render( + render( ); - const label = container.querySelector( 'label' ); - expect( label ).toContainHTML( 'First name' ); - // @todo: Refactor this after adding next VisuallyHidden. - expect( label ).toHaveClass( 'components-visually-hidden' ); + expect( + screen.getByRole( 'textbox', { name: 'First name' } ) + ).toBeVisible(); + expect( screen.getByText( 'First name' ) ).toHaveClass( + 'components-visually-hidden' + ); } ); test( 'should render alignLabel', () => { From e7eb6a761850eb35d69cd09d4138f2ae0559d8c3 Mon Sep 17 00:00:00 2001 From: Nik Tsekouras Date: Wed, 9 Nov 2022 18:50:01 -0700 Subject: [PATCH 36/66] Make Manage Reusable blocks match similar links (#45689) --- packages/block-editor/src/components/inserter/style.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/inserter/style.scss b/packages/block-editor/src/components/inserter/style.scss index 12a3690f71e4de..5aa9b688f25021 100644 --- a/packages/block-editor/src/components/inserter/style.scss +++ b/packages/block-editor/src/components/inserter/style.scss @@ -175,7 +175,7 @@ $block-inserter-tabs-height: 44px; } .block-editor-inserter__manage-reusable-blocks-container { - margin: $grid-unit-20; + margin: auto $grid-unit-20 $grid-unit-20; } .block-editor-inserter__manage-reusable-blocks { From a7da8be312b3603ae28964f4e0b308ce20cf970d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petter=20Walb=C3=B8=20Johnsg=C3=A5rd?= Date: Thu, 10 Nov 2022 05:58:32 +0100 Subject: [PATCH 37/66] Truncate: Remove unnecessary `.firstChild` from tests (#45694) * Truncate: Remove unnecessary `.firstChild` from tests * Apply suggestions from code review Co-authored-by: Marin Atanasov <8436925+tyxla@users.noreply.github.com> Co-authored-by: Marin Atanasov <8436925+tyxla@users.noreply.github.com> --- .../components/src/truncate/test/index.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/components/src/truncate/test/index.tsx b/packages/components/src/truncate/test/index.tsx index 366f1980238c8d..082b6aa232d477 100644 --- a/packages/components/src/truncate/test/index.tsx +++ b/packages/components/src/truncate/test/index.tsx @@ -1,7 +1,7 @@ /** * External dependencies */ -import { render } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; /** * Internal dependencies @@ -10,34 +10,34 @@ import { Truncate } from '..'; describe( 'props', () => { test( 'should render correctly', () => { - const { container } = render( Lorem ipsum. ); - expect( container.firstChild ).toHaveTextContent( 'Lorem ipsum.' ); + render( Lorem ipsum. ); + expect( screen.getByText( 'Lorem ipsum.' ) ).toBeVisible(); } ); test( 'should render limit', () => { - const { container } = render( + render( Lorem ipsum. ); - expect( container.firstChild ).toHaveTextContent( 'L…' ); + expect( screen.getByText( 'L…' ) ).toBeVisible(); } ); test( 'should render custom ellipsis', () => { - const { container } = render( + render( Lorem ipsum. ); - expect( container.firstChild ).toHaveTextContent( 'Lorem!!!' ); + expect( screen.getByText( 'Lorem!!!' ) ).toBeVisible(); } ); test( 'should render custom ellipsizeMode', () => { - const { container } = render( + render( Lorem ipsum. ); - expect( container.firstChild ).toHaveTextContent( 'Lo!!!m.' ); + expect( screen.getByText( 'Lo!!!m.' ) ).toBeVisible(); } ); } ); From 6b6fe623a75653caccedf76674197528bc916128 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petter=20Walb=C3=B8=20Johnsg=C3=A5rd?= Date: Thu, 10 Nov 2022 05:58:50 +0100 Subject: [PATCH 38/66] BaseField: Remove unnecessary `.firstChild` from tests (#45687) --- .../test/__snapshots__/index.js.snap | 12 +++--- .../components/src/base-field/test/index.js | 42 ++++++++++++------- 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/packages/components/src/base-field/test/__snapshots__/index.js.snap b/packages/components/src/base-field/test/__snapshots__/index.js.snap index 273234b79c1f9a..99582f8d40879f 100644 --- a/packages/components/src/base-field/test/__snapshots__/index.js.snap +++ b/packages/components/src/base-field/test/__snapshots__/index.js.snap @@ -131,9 +131,11 @@ exports[`base field should render correctly 1`] = ` box-shadow: 0 0 0 0.5px var(--wp-components-color-accent, var(--wp-admin-theme-color, #007cba)); } -
    +
    +
    +
    `; diff --git a/packages/components/src/base-field/test/index.js b/packages/components/src/base-field/test/index.js index 8204163c2b6c40..871bd8b3f595ee 100644 --- a/packages/components/src/base-field/test/index.js +++ b/packages/components/src/base-field/test/index.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { render } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; /** * Internal dependencies @@ -15,33 +15,45 @@ const TestField = ( props ) => { describe( 'base field', () => { it( 'should render correctly', () => { - const base = render( ).container; - expect( base.firstChild ).toMatchSnapshot(); + const { container } = render( ); + expect( container ).toMatchSnapshot(); } ); describe( 'props', () => { it( 'should render error styles', () => { - const base = render( ).container; - const { container } = render( ); - expect( container.firstChild ).toMatchStyleDiffSnapshot( - base.firstChild + render( + <> + + + ); + expect( + screen.getByTestId( 'base-field-error' ) + ).toMatchStyleDiffSnapshot( screen.getByTestId( 'base-field' ) ); } ); it( 'should render inline styles', () => { - const base = render( ).container; - const { container } = render( ); - expect( container.firstChild ).toMatchStyleDiffSnapshot( - base.firstChild + render( + <> + + + ); + expect( + screen.getByTestId( 'base-field-inline' ) + ).toMatchStyleDiffSnapshot( screen.getByTestId( 'base-field' ) ); } ); it( 'should render subtle styles', () => { - const base = render( ).container; - const { container } = render( ); - expect( container.firstChild ).toMatchStyleDiffSnapshot( - base.firstChild + render( + <> + + + ); + expect( + screen.getByTestId( 'base-field-subtle' ) + ).toMatchStyleDiffSnapshot( screen.getByTestId( 'base-field' ) ); } ); } ); From ad82bab62617ff135373c3f5d6287a937060f828 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Thu, 10 Nov 2022 06:58:19 +0100 Subject: [PATCH 39/66] Add missing CHANGELOG entry (#45691) --- packages/components/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 58d5275fd750d5..a5cf55e2ac9edb 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -10,6 +10,7 @@ - `Autocomplete`: Fix unexpected block insertion during IME composition ([#45510](https://github.com/WordPress/gutenberg/pull/45510)). - `ToolsPanelItem`: Prevent unintended calls to onDeselect when parent panel is remounted and item is rendered via SlotFill ([#45673](https://github.com/WordPress/gutenberg/pull/45673)) +- `ColorPicker`: Prevent all number fields from becoming "0" when one of them is an empty string ([#45649](https://github.com/WordPress/gutenberg/pull/45649)). ### Internal From 22c2e0a1fcd0970995ad89388f707e5cb865d99c Mon Sep 17 00:00:00 2001 From: Dean Sas Date: Thu, 10 Nov 2022 07:20:07 +0000 Subject: [PATCH 40/66] Make unwrapping columns slighly more efficient (#45684) Use flatMap rather than map().flat(); --- packages/block-library/src/columns/transforms.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/block-library/src/columns/transforms.js b/packages/block-library/src/columns/transforms.js index 04fc932f529643..fda65df8659d8f 100644 --- a/packages/block-library/src/columns/transforms.js +++ b/packages/block-library/src/columns/transforms.js @@ -110,9 +110,7 @@ const transforms = { type: 'block', blocks: [ '*' ], transform: ( attributes, innerBlocks ) => - innerBlocks - .map( ( innerBlock ) => innerBlock.innerBlocks ) - .flat(), + innerBlocks.flatMap( ( innerBlock ) => innerBlock.innerBlocks ), }, ], }; From 0c556c87588c14ff1993cd53f7e1b170fe5950d8 Mon Sep 17 00:00:00 2001 From: Carolina Nymark Date: Thu, 10 Nov 2022 09:46:27 +0100 Subject: [PATCH 41/66] Add color block support to latest posts (#41874) * Add color block support to latest posts * Adjust spacing after merge conflict * Adjust spacing after merge conflict. --- docs/reference-guides/core-blocks.md | 2 +- packages/block-library/src/latest-posts/block.json | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index 4ed71b8527b61b..65e3cb1f65c8fc 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -329,7 +329,7 @@ Display a list of your most recent posts. ([Source](https://github.com/WordPress - **Name:** core/latest-posts - **Category:** widgets -- **Supports:** align, spacing (margin, padding), typography (fontSize, lineHeight), ~~html~~ +- **Supports:** align, color (background, gradients, link, text), spacing (margin, padding), typography (fontSize, lineHeight), ~~html~~ - **Attributes:** addLinkToFeaturedImage, categories, columns, displayAuthor, displayFeaturedImage, displayPostContent, displayPostContentRadio, displayPostDate, excerptLength, featuredImageAlign, featuredImageSizeHeight, featuredImageSizeSlug, featuredImageSizeWidth, order, orderBy, postLayout, postsToShow, selectedAuthor ## List diff --git a/packages/block-library/src/latest-posts/block.json b/packages/block-library/src/latest-posts/block.json index cef1a64967f1b2..13a01280c4d1d2 100644 --- a/packages/block-library/src/latest-posts/block.json +++ b/packages/block-library/src/latest-posts/block.json @@ -85,6 +85,15 @@ "supports": { "align": true, "html": false, + "color": { + "gradients": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true, + "link": true + } + }, "spacing": { "margin": true, "padding": true From 3373e97b01cb75929edb1d4e4e66890d138a11d6 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Thu, 10 Nov 2022 13:26:38 +0400 Subject: [PATCH 42/66] Cover: Avoid content loss when the templateLock value is all or contentOnly (#45632) * Cover: Avoid content loss when the templateLock value is all or contentOnly * Add inline comment --- packages/block-library/src/cover/edit/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/block-library/src/cover/edit/index.js b/packages/block-library/src/cover/edit/index.js index 4d7aba03dc7fa3..88f97297a5d93d 100644 --- a/packages/block-library/src/cover/edit/index.js +++ b/packages/block-library/src/cover/edit/index.js @@ -188,7 +188,9 @@ function CoverEdit( { className: 'wp-block-cover__inner-container', }, { - template: innerBlocksTemplate, + // Avoid template sync when the `templateLock` value is `all` or `contentOnly`. + // See: https://github.com/WordPress/gutenberg/pull/45632 + template: ! hasInnerBlocks ? innerBlocksTemplate : undefined, templateInsertUpdatesSelection: true, allowedBlocks, templateLock, From 9e4bc29df32ba98637095234b09707e8ba771283 Mon Sep 17 00:00:00 2001 From: Ella <4710635+ellatrix@users.noreply.github.com> Date: Thu, 10 Nov 2022 03:40:42 -0700 Subject: [PATCH 43/66] List: disable nested list drop zone so dropping list items works (#45321) --- .../block-editor/src/components/inner-blocks/index.js | 11 +++++++---- packages/block-library/src/list-item/edit.js | 1 + 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/block-editor/src/components/inner-blocks/index.js b/packages/block-editor/src/components/inner-blocks/index.js index 8afc6482eb33fa..c03a8616fed657 100644 --- a/packages/block-editor/src/components/inner-blocks/index.js +++ b/packages/block-editor/src/components/inner-blocks/index.js @@ -150,7 +150,8 @@ const ForwardedInnerBlocks = forwardRef( ( props, ref ) => { * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/inner-blocks/README.md */ export function useInnerBlocksProps( props = {}, options = {} ) { - const { __unstableDisableLayoutClassNames } = options; + const { __unstableDisableLayoutClassNames, __unstableDisableDropZone } = + options; const { clientId, __unstableLayoutClassNames: layoutClassNames = '' } = useBlockEditContext(); const isSmallScreen = useViewportMatch( 'medium', '<' ); @@ -187,11 +188,13 @@ export function useInnerBlocksProps( props = {}, options = {} ) { [ clientId, isSmallScreen ] ); + const blockDropZoneRef = useBlockDropZone( { + rootClientId: clientId, + } ); + const ref = useMergeRefs( [ props.ref, - useBlockDropZone( { - rootClientId: clientId, - } ), + __unstableDisableDropZone ? null : blockDropZoneRef, ] ); const innerBlocksProps = { diff --git a/packages/block-library/src/list-item/edit.js b/packages/block-library/src/list-item/edit.js index 5168dbeb60cfef..738a8ab397adf3 100644 --- a/packages/block-library/src/list-item/edit.js +++ b/packages/block-library/src/list-item/edit.js @@ -67,6 +67,7 @@ export default function ListItemEdit( { const innerBlocksProps = useInnerBlocksProps( blockProps, { allowedBlocks: [ 'core/list' ], renderAppender: false, + __unstableDisableDropZone: true, } ); const useEnterRef = useEnter( { content, clientId } ); const useSpaceRef = useSpace( clientId ); From 8c9af511be57af4364db63eb47dc175712b4177d Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Thu, 10 Nov 2022 15:25:45 +0400 Subject: [PATCH 44/66] Replace FSE with Site Editor (#45699) --- .../class-wp-rest-block-editor-settings-controller.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/experimental/class-wp-rest-block-editor-settings-controller.php b/lib/experimental/class-wp-rest-block-editor-settings-controller.php index c0850a8a29f4cf..4a258a70102bb5 100644 --- a/lib/experimental/class-wp-rest-block-editor-settings-controller.php +++ b/lib/experimental/class-wp-rest-block-editor-settings-controller.php @@ -115,7 +115,7 @@ public function get_item_schema() { 'type' => 'object', 'properties' => array( '__unstableEnableFullSiteEditingBlocks' => array( - 'description' => __( 'Enables experimental Full Site Editing blocks', 'gutenberg' ), + 'description' => __( 'Enables experimental Site Editor blocks', 'gutenberg' ), 'type' => 'boolean', 'context' => array( 'post-editor', 'site-editor', 'widgets-editor' ), ), @@ -127,7 +127,7 @@ public function get_item_schema() { ), 'supportsTemplateMode' => array( - 'description' => __( 'Returns if the current theme is full site editing-enabled or not.', 'gutenberg' ), + 'description' => __( 'Indicates whether the current theme supports block-based templates.', 'gutenberg' ), 'type' => 'boolean', 'context' => array( 'post-editor', 'site-editor', 'widgets-editor' ), ), From e57efa8c352b54fd5f7940697134df6d3813a07d Mon Sep 17 00:00:00 2001 From: Matias Ventura Date: Thu, 10 Nov 2022 15:11:39 +0100 Subject: [PATCH 45/66] Update the design of the focal point handle (#45053) * Update the design of the focal point handle. * Simplify markup structure. --- .../src/focal-point-picker/focal-point.tsx | 26 +----------- .../styles/focal-point-style.ts | 40 ++++--------------- 2 files changed, 10 insertions(+), 56 deletions(-) diff --git a/packages/components/src/focal-point-picker/focal-point.tsx b/packages/components/src/focal-point-picker/focal-point.tsx index fbc90ab113c67f..21a25f5a832f34 100644 --- a/packages/components/src/focal-point-picker/focal-point.tsx +++ b/packages/components/src/focal-point-picker/focal-point.tsx @@ -1,12 +1,7 @@ /** * Internal dependencies */ -import { - FocalPointWrapper, - PointerIconPathFill, - PointerIconPathOutline, - PointerIconSVG, -} from './styles/focal-point-style'; +import { PointerCircle } from './styles/focal-point-style'; /** * External dependencies @@ -26,22 +21,5 @@ export default function FocalPoint( { const style = { left, top }; - return ( - - - - - - - ); + return ; } diff --git a/packages/components/src/focal-point-picker/styles/focal-point-style.ts b/packages/components/src/focal-point-picker/styles/focal-point-style.ts index 0f46d7c5738574..201924ec6f6c6e 100644 --- a/packages/components/src/focal-point-picker/styles/focal-point-style.ts +++ b/packages/components/src/focal-point-picker/styles/focal-point-style.ts @@ -3,45 +3,21 @@ */ import styled from '@emotion/styled'; -/** - * WordPress dependencies - */ -import { Path, SVG } from '@wordpress/primitives'; - -/** - * Internal dependencies - */ -import { COLORS } from '../../utils'; - -export const FocalPointWrapper = styled.div` +export const PointerCircle = styled.div` background-color: transparent; cursor: grab; - height: 30px; - margin: -15px 0 0 -15px; - opacity: 0.8; + height: 48px; + margin: -24px 0 0 -24px; position: absolute; user-select: none; - width: 30px; + width: 48px; will-change: transform; z-index: 10000; + background: rgba( 255, 255, 255, 0.6 ); + border-radius: 50%; + backdrop-filter: blur( 4px ); + box-shadow: rgb( 0 0 0 / 20% ) 0px 0px 10px; ${ ( { isDragging }: { isDragging: boolean } ) => isDragging && 'cursor: grabbing;' } `; - -export const PointerIconSVG = styled( SVG )` - display: block; - height: 100%; - left: 0; - position: absolute; - top: 0; - width: 100%; -`; - -export const PointerIconPathOutline = styled( Path )` - fill: white; -`; - -export const PointerIconPathFill = styled( Path )` - fill: ${ COLORS.ui.theme }; -`; From cb8ed512c11bf5714cb3d681a49a334260df2984 Mon Sep 17 00:00:00 2001 From: Vicente Canales <1157901+vcanales@users.noreply.github.com> Date: Thu, 10 Nov 2022 09:18:44 -0700 Subject: [PATCH 46/66] Inherit font from theme on overlay close button (#45635) --- packages/block-library/src/navigation/style.scss | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/block-library/src/navigation/style.scss b/packages/block-library/src/navigation/style.scss index 27638cd5d84cc7..59622a7039f491 100644 --- a/packages/block-library/src/navigation/style.scss +++ b/packages/block-library/src/navigation/style.scss @@ -676,6 +676,14 @@ button.wp-block-navigation-item__content { top: 0; right: 0; z-index: 2; // Needs to be above the modal z index itself. + + // When set to collapse into a text button, it should inherit the parent font. + // This needs specificity to override inherited properties by the button element and component. + &.wp-block-navigation__responsive-container-close.wp-block-navigation__responsive-container-close { + font-family: inherit; + font-weight: inherit; + font-size: inherit; + } } // The menu adds wrapping containers. From 3086d49e6816d333a2cd9d9c4153403c33b82a2c Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Thu, 10 Nov 2022 17:06:02 +0000 Subject: [PATCH 47/66] Fix Link UI popover positioning when inspector control input is focused (#45661) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Use editable element as popover “anchor” point * Reverse tenary --- packages/rich-text/src/component/use-anchor.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/rich-text/src/component/use-anchor.js b/packages/rich-text/src/component/use-anchor.js index dd6172a1661db3..075515c1521cfc 100644 --- a/packages/rich-text/src/component/use-anchor.js +++ b/packages/rich-text/src/component/use-anchor.js @@ -45,13 +45,18 @@ export function useAnchor( { editableContentElement, value, settings = {} } ) { return; } + const selectionWithinEditableContentElement = + editableContentElement?.contains( selection?.anchorNode ); + const range = selection.getRangeAt( 0 ); if ( ! activeFormat ) { return { ownerDocument: range.startContainer.ownerDocument, getBoundingClientRect() { - return range.getBoundingClientRect(); + return selectionWithinEditableContentElement + ? range.getBoundingClientRect() + : editableContentElement.getBoundingClientRect(); }, }; } From e112e26f7b8e6f9adc499a4a357aef18811a8796 Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Wed, 26 Oct 2022 20:04:37 -0700 Subject: [PATCH 48/66] Perf Tests: Refactor use of ENV and git in test setup In this patch we're refactoring how the performance tests are configured, relying on the ENV values set by Github Actions and adding new semantic terms for existing implicit conditions. Additionally, the custom `git` abstraction has been removed and replaced with `SimpleGit` for a more concrete represetnation of what is being performed during the test setup. This change will enable more complicated uses of `git` for optimizing the run script, and by moving out of the abstraction it will minimize any changes based on that kind of `git` usage. There should be no functional changes in this patch and only the code surrounding test setup should change. The term "branch" is replaced in a few places with "ref" since this script is designed to accept any `git` ref pointing to commits, and in fact has been in daily use with a mix of tags, commit SHAs, and branch names. --- bin/plugin/commands/common.js | 60 +-------- bin/plugin/commands/packages.js | 148 ++++++++++++++------- bin/plugin/commands/performance.js | 91 +++++++------ bin/plugin/lib/git.js | 202 ----------------------------- bin/tsconfig.json | 1 - 5 files changed, 159 insertions(+), 343 deletions(-) delete mode 100644 bin/plugin/lib/git.js diff --git a/bin/plugin/commands/common.js b/bin/plugin/commands/common.js index fc540b073a1ff4..59abfa93447ef3 100644 --- a/bin/plugin/commands/common.js +++ b/bin/plugin/commands/common.js @@ -1,72 +1,26 @@ /** * External dependencies */ -const fs = require( 'fs' ); -const rimraf = require( 'rimraf' ); const semver = require( 'semver' ); +const SimpleGit = require( 'simple-git' ); /** * Internal dependencies */ -const { log, formats } = require( '../lib/logger' ); -const { runStep, readJSONFile } = require( '../lib/utils' ); -const git = require( '../lib/git' ); -const config = require( '../config' ); - -/** - * Clone the repository and returns the working directory. - * - * @param {string} abortMessage Abort message. - * - * @return {Promise} Repository local path. - */ -async function runGitRepositoryCloneStep( abortMessage ) { - // Cloning the repository. - let gitWorkingDirectoryPath; - await runStep( 'Cloning the Git repository', abortMessage, async () => { - log( '>> Cloning the Git repository' ); - gitWorkingDirectoryPath = await git.clone( config.gitRepositoryURL ); - log( - '>> The Git repository has been successfully cloned in the following temporary folder: ' + - formats.success( gitWorkingDirectoryPath ) - ); - } ); - - return gitWorkingDirectoryPath; -} - -/** - * Clean the working directories. - * - * @param {string[]} folders Folders to clean. - * @param {string} abortMessage Abort message. - */ -async function runCleanLocalFoldersStep( folders, abortMessage ) { - await runStep( 'Cleaning the temporary folders', abortMessage, async () => { - await Promise.all( - folders.map( async ( directoryPath ) => { - if ( fs.existsSync( directoryPath ) ) { - await rimraf( directoryPath, ( err ) => { - if ( err ) { - throw err; - } - } ); - } - } ) - ); - } ); -} +const { readJSONFile } = require( '../lib/utils' ); /** * Finds the name of the current plugin release branch based on the version in - * the package.json file. + * the package.json file and the latest `trunk` branch in `git`. * * @param {string} gitWorkingDirectoryPath Path to the project's working directory. * * @return {string} Name of the plugin release branch. */ async function findPluginReleaseBranchName( gitWorkingDirectoryPath ) { - await git.checkoutRemoteBranch( gitWorkingDirectoryPath, 'trunk' ); + await SimpleGit( gitWorkingDirectoryPath ) + .fetch( 'origin', 'trunk' ) + .checkout( 'trunk' ); const packageJsonPath = gitWorkingDirectoryPath + '/package.json'; const mainPackageJson = readJSONFile( packageJsonPath ); @@ -141,6 +95,4 @@ function calculateVersionBumpFromChangelog( module.exports = { calculateVersionBumpFromChangelog, findPluginReleaseBranchName, - runGitRepositoryCloneStep, - runCleanLocalFoldersStep, }; diff --git a/bin/plugin/commands/packages.js b/bin/plugin/commands/packages.js index e715ee69345ad0..c085201a235001 100644 --- a/bin/plugin/commands/packages.js +++ b/bin/plugin/commands/packages.js @@ -6,20 +6,24 @@ const path = require( 'path' ); const glob = require( 'fast-glob' ); const fs = require( 'fs' ); const { inc: semverInc } = require( 'semver' ); +const rimraf = require( 'rimraf' ); const readline = require( 'readline' ); +const SimpleGit = require( 'simple-git' ); /** * Internal dependencies */ const { log, formats } = require( '../lib/logger' ); -const { askForConfirmation, runStep, readJSONFile } = require( '../lib/utils' ); +const { + askForConfirmation, + runStep, + readJSONFile, + getRandomTemporaryPath, +} = require( '../lib/utils' ); const { calculateVersionBumpFromChangelog, findPluginReleaseBranchName, - runGitRepositoryCloneStep, - runCleanLocalFoldersStep, } = require( './common' ); -const git = require( '../lib/git' ); const { join } = require( 'path' ); /** @@ -55,6 +59,17 @@ const { join } = require( 'path' ); * @property {ReleaseType} releaseType The selected release type. */ +/** + * Throws if given an error in the node.js callback style. + * + * @param {any|null} error If callback failed, this will hold a value. + */ +const rethrow = ( error ) => { + if ( error ) { + throw error; + } +}; + /** * Checks out the npm release branch. * @@ -64,9 +79,28 @@ async function checkoutNpmReleaseBranch( { gitWorkingDirectoryPath, npmReleaseBranch, } ) { - // Creating the release branch. - await git.checkoutRemoteBranch( gitWorkingDirectoryPath, npmReleaseBranch ); - await git.fetch( gitWorkingDirectoryPath, [ '--depth=100' ] ); + /* + * Create the release branch. + * + * Note that we are grabbing an arbitrary depth of commits + * during the fetch. When `lerna` attempts to determine if + * a package needs an update, it looks at `git` history, + * and if we have pruned that history it will pre-emptively + * publish when it doesn't need to. + * + * We could set a different arbitrary depth if this isn't + * long enough or if it's excessive. We could also try and + * find a way to more specifically fetch what we expect to + * change. For example, if we knew we'll be performing + * updates every two weeks, we might be conservative and + * use `--shallow-since=4.weeks.ago`. + * + * At the time of writing, a depth of 100 pulls in all + * `trunk` commits from within the past week. + */ + await SimpleGit( gitWorkingDirectoryPath ) + .fetch( npmReleaseBranch, [ '--depth=100' ] ) + .checkout( npmReleaseBranch ); log( '>> The local npm release branch ' + formats.success( npmReleaseBranch ) + @@ -105,13 +139,19 @@ async function runNpmReleaseBranchSyncStep( pluginReleaseBranch, config ) { `>> Syncing the latest plugin release to "${ pluginReleaseBranch }".` ); - await git.replaceContentFromRemoteBranch( - gitWorkingDirectoryPath, - pluginReleaseBranch - ); + const repo = SimpleGit( gitWorkingDirectoryPath ); + + /* + * Replace content from remote branch. + * + * @TODO: What is our goal here? Could `git reset --hard origin/${pluginReleaseBranch}` work? + * Why are we manually removing and then adding files back in? + */ + await repo + .raw( 'rm', '-r', '.' ) + .raw( 'checkout', `origin/${ pluginReleaseBranch }`, '--', '.' ); - const commitHash = await git.commit( - gitWorkingDirectoryPath, + const { commit: commitHash } = await repo.commit( `Merge changes published in the Gutenberg plugin "${ pluginReleaseBranch }" branch` ); @@ -223,6 +263,7 @@ async function updatePackages( config ) { '>> Recommended version bumps based on the changes detected in CHANGELOG files:' ); + // e.g. "2022-11-01T00:13:26.102Z" -> "2022-11-01" const publishDate = new Date().toISOString().split( 'T' )[ 0 ]; await Promise.all( packagesToUpdate.map( @@ -234,11 +275,8 @@ async function updatePackages( config ) { version, } ) => { // Update changelog. - const content = await fs.promises.readFile( - changelogPath, - 'utf8' - ); - await fs.promises.writeFile( + const content = fs.readFileSync( changelogPath, 'utf8' ); + fs.writeFileSync( changelogPath, content.replace( '## Unreleased', @@ -280,11 +318,10 @@ async function updatePackages( config ) { ); } - const commitHash = await git.commit( - gitWorkingDirectoryPath, - 'Update changelog files', - [ './*' ] - ); + const { commit: commitHash } = await SimpleGit( gitWorkingDirectoryPath ) + .add( [ './*' ] ) + .commit( 'Update changelog files' ); + if ( commitHash ) { await runPushGitChangesStep( config ); } @@ -313,8 +350,8 @@ async function runPushGitChangesStep( { abortMessage ); } - await git.pushBranchToOrigin( - gitWorkingDirectoryPath, + await SimpleGit( gitWorkingDirectoryPath ).push( + 'origin', npmReleaseBranch ); } ); @@ -345,9 +382,9 @@ async function publishPackagesToNpm( { stdio: 'inherit', } ); - const beforeCommitHash = await git.getLastCommitHash( + const beforeCommitHash = await SimpleGit( gitWorkingDirectoryPath - ); + ).revparse( [ '--short', 'HEAD' ] ); const yesFlag = interactive ? '' : '--yes'; const noVerifyAccessFlag = interactive ? '' : '--no-verify-access'; @@ -403,8 +440,8 @@ async function publishPackagesToNpm( { ); } - const afterCommitHash = await git.getLastCommitHash( - gitWorkingDirectoryPath + const afterCommitHash = await SimpleGit( gitWorkingDirectoryPath ).revparse( + [ '--short', 'HEAD' ] ); if ( afterCommitHash === beforeCommitHash ) { return; @@ -439,18 +476,23 @@ async function backportCommitsToBranch( log( `>> Backporting commits to "${ branchName }".` ); - await git.resetLocalBranchAgainstOrigin( - gitWorkingDirectoryPath, - branchName - ); + const repo = SimpleGit( gitWorkingDirectoryPath ); + + /* + * Reset any local changes and replace them with the origin branch's copy. + * + * Perform an additional fetch to ensure that when we push our changes that + * it's very unlikely that new commits could have appeared at the origin + * HEAD between when we started running this script and now when we're + * pushing our changes back upstream. + */ + await repo.fetch().checkout( branchName ).pull( 'origin', branchName ); + for ( const commitHash of commits ) { - await git.cherrypickCommitIntoBranch( - gitWorkingDirectoryPath, - branchName, - commitHash - ); + await repo.raw( 'cherry-pick', commitHash ); } - await git.pushBranchToOrigin( gitWorkingDirectoryPath, branchName ); + + await repo.push( 'origin', branchName ); log( `>> Backporting successfully finished.` ); } @@ -478,11 +520,20 @@ async function runPackagesRelease( config, customMessages ) { const temporaryFolders = []; if ( ! config.gitWorkingDirectoryPath ) { - // Cloning the Git repository. - config.gitWorkingDirectoryPath = await runGitRepositoryCloneStep( - config.abortMessage + const gitPath = getRandomTemporaryPath(); + config.gitWorkingDirectoryPath = gitPath; + fs.mkdirSync( gitPath, { recursive: true } ); + temporaryFolders.push( gitPath ); + + await runStep( + 'Cloning the Git repository', + config.abortMessage, + async () => { + log( '>> Cloning the Git repository' ); + await SimpleGit( gitPath ).clone( config.gitRepositoryURL ); + log( ` >> successfully clone into: ${ gitPath }` ); + } ); - temporaryFolders.push( config.gitWorkingDirectoryPath ); } let pluginReleaseBranch; @@ -518,7 +569,16 @@ async function runPackagesRelease( config, customMessages ) { } } - await runCleanLocalFoldersStep( temporaryFolders, 'Cleaning failed.' ); + await runStep( + 'Cleaning the temporary folders', + 'Cleaning failed', + async () => + await Promise.all( + temporaryFolders + .filter( ( tempDir ) => fs.existsSync( tempDir ) ) + .map( ( tempDir ) => rimraf( tempDir, rethrow ) ) + ) + ); log( '\n>> 🎉 WordPress packages are now published!\n\n', diff --git a/bin/plugin/commands/performance.js b/bin/plugin/commands/performance.js index 0571a0e72955fe..2aa8a2c9a7f5c6 100644 --- a/bin/plugin/commands/performance.js +++ b/bin/plugin/commands/performance.js @@ -4,6 +4,7 @@ const fs = require( 'fs' ); const path = require( 'path' ); const { mapValues, kebabCase } = require( 'lodash' ); +const SimpleGit = require( 'simple-git' ); /** * Internal dependencies @@ -15,7 +16,6 @@ const { askForConfirmation, getRandomTemporaryPath, } = require( '../lib/utils' ); -const git = require( '../lib/git' ); const config = require( '../config' ); /** @@ -146,24 +146,6 @@ function curateResults( results ) { }; } -/** - * Set up the given branch for testing. - * - * @param {string} branch Branch name. - * @param {string} environmentDirectory Path to the plugin environment's clone. - */ -async function setUpGitBranch( branch, environmentDirectory ) { - // Restore clean working directory (e.g. if `package-lock.json` has local - // changes after install). - await git.discardLocalChanges( environmentDirectory ); - - log( ' >> Fetching the ' + formats.success( branch ) + ' branch' ); - await git.checkoutRemoteBranch( environmentDirectory, branch ); - - log( ' >> Building the ' + formats.success( branch ) + ' branch' ); - await runShellScript( 'npm ci && npm run build', environmentDirectory ); -} - /** * Runs the performance tests on the current branch. * @@ -214,24 +196,47 @@ async function runPerformanceTests( branches, options ) { // 1- Preparing the tests directory. log( '\n>> Preparing the tests directories' ); log( ' >> Cloning the repository' ); - const baseDirectory = await git.clone( config.gitRepositoryURL ); + + /** + * @type {string[]} git refs against which to run tests; + * could be commit SHA, branch name, tag, etc... + */ + if ( branches.length < 2 ) { + throw new Error( `Need at least two git refs to run` ); + } + + const baseDirectory = getRandomTemporaryPath(); + fs.mkdirSync( baseDirectory, { recursive: true } ); + + // @ts-ignore + const git = SimpleGit( baseDirectory ); + await git + .raw( 'init' ) + .raw( 'remote', 'add', 'origin', config.gitRepositoryURL ); + + for ( const branch of branches ) { + await git.raw( 'fetch', '--depth=1', 'origin', branch ); + } + + await git.raw( 'checkout', branches[ 0 ] ); + const rootDirectory = getRandomTemporaryPath(); const performanceTestDirectory = rootDirectory + '/tests'; await runShellScript( 'mkdir -p ' + rootDirectory ); await runShellScript( 'cp -R ' + baseDirectory + ' ' + performanceTestDirectory ); + if ( !! options.testsBranch ) { - log( - ' >> Fetching the test branch: ' + - formats.success( options.testsBranch ) + - ' branch' - ); - await git.checkoutRemoteBranch( - performanceTestDirectory, - options.testsBranch - ); + const branchName = formats.success( options.testsBranch ); + log( ` >> Fetching the test-runner branch: ${ branchName }` ); + + // @ts-ignore + await SimpleGit( performanceTestDirectory ) + .raw( 'fetch', '--depth=1', 'origin', options.testsBranch ) + .raw( 'checkout', options.testsBranch ); } + log( ' >> Installing dependencies and building packages' ); await runShellScript( 'npm ci && npm run build:packages', @@ -244,16 +249,22 @@ async function runPerformanceTests( branches, options ) { log( '\n>> Preparing an environment directory per branch' ); const branchDirectories = {}; for ( const branch of branches ) { - log( ' >> Branch: ' + branch ); + log( ` >> Branch: ${ branch }` ); const environmentDirectory = rootDirectory + '/envs/' + kebabCase( branch ); // @ts-ignore branchDirectories[ branch ] = environmentDirectory; + const buildPath = `${ environmentDirectory }/plugin`; await runShellScript( 'mkdir ' + environmentDirectory ); - await runShellScript( - 'cp -R ' + baseDirectory + ' ' + environmentDirectory + '/plugin' - ); - await setUpGitBranch( branch, environmentDirectory + '/plugin' ); + await runShellScript( `cp -R ${ baseDirectory } ${ buildPath }` ); + + log( ` >> Fetching the ${ formats.success( branch ) } branch` ); + // @ts-ignore + await SimpleGit( buildPath ).reset( 'hard' ).checkout( branch ); + + log( ` >> Building the ${ formats.success( branch ) } branch` ); + await runShellScript( 'npm ci && npm run build', buildPath ); + await runShellScript( 'cp ' + path.resolve( @@ -302,13 +313,9 @@ async function runPerformanceTests( branches, options ) { formats.success( performanceTestDirectory ) ); for ( const branch of branches ) { - log( - '>> Environment Directory (' + - branch + - ') : ' + - // @ts-ignore - formats.success( branchDirectories[ branch ] ) - ); + // @ts-ignore + const envPath = formats.success( branchDirectories[ branch ] ); + log( `>> Environment Directory (${ branch }) : ${ envPath }` ); } // 4- Running the tests. @@ -328,7 +335,7 @@ async function runPerformanceTests( branches, options ) { for ( const branch of branches ) { // @ts-ignore const environmentDirectory = branchDirectories[ branch ]; - log( ' >> Branch: ' + branch + ', Suite: ' + testSuite ); + log( ` >> Branch: ${ branch }, Suite: ${ testSuite }` ); log( ' >> Starting the environment.' ); await runShellScript( '../../tests/node_modules/.bin/wp-env start', diff --git a/bin/plugin/lib/git.js b/bin/plugin/lib/git.js deleted file mode 100644 index 8208efa7164d1a..00000000000000 --- a/bin/plugin/lib/git.js +++ /dev/null @@ -1,202 +0,0 @@ -// @ts-nocheck -/** - * External dependencies - */ -const SimpleGit = require( 'simple-git' ); - -/** - * Internal dependencies - */ -const { getRandomTemporaryPath } = require( './utils' ); - -/** - * Clones a GitHub repository. - * - * @param {string} repositoryUrl - * - * @return {Promise} Repository local Path - */ -async function clone( repositoryUrl ) { - const gitWorkingDirectoryPath = getRandomTemporaryPath(); - const simpleGit = SimpleGit(); - await simpleGit.clone( repositoryUrl, gitWorkingDirectoryPath, [ - '--depth=1', - '--no-single-branch', - ] ); - return gitWorkingDirectoryPath; -} - -/** - * Fetches changes from the repository. - * - * @param {string} gitWorkingDirectoryPath Local repository path. - * @param {string[]|Object} options Git options to apply. - */ -async function fetch( gitWorkingDirectoryPath, options = [] ) { - const simpleGit = SimpleGit( gitWorkingDirectoryPath ); - await simpleGit.fetch( options ); -} - -/** - * Commits changes to the repository. - * - * @param {string} gitWorkingDirectoryPath Local repository path. - * @param {string} message Commit message. - * @param {string[]} filesToAdd Files to add. - * - * @return {Promise} Commit Hash - */ -async function commit( gitWorkingDirectoryPath, message, filesToAdd = [] ) { - const simpleGit = SimpleGit( gitWorkingDirectoryPath ); - await simpleGit.add( filesToAdd ); - const commitData = await simpleGit.commit( message ); - const commitHash = commitData.commit; - - return commitHash; -} - -/** - * Creates a local branch. - * - * @param {string} gitWorkingDirectoryPath Local repository path. - * @param {string} branchName Branch Name - */ -async function createLocalBranch( gitWorkingDirectoryPath, branchName ) { - const simpleGit = SimpleGit( gitWorkingDirectoryPath ); - await simpleGit.checkoutLocalBranch( branchName ); -} - -/** - * Checkout a local branch. - * - * @param {string} gitWorkingDirectoryPath Local repository path. - * @param {string} branchName Branch Name - */ -async function checkoutRemoteBranch( gitWorkingDirectoryPath, branchName ) { - const simpleGit = SimpleGit( gitWorkingDirectoryPath ); - await simpleGit.fetch( 'origin', branchName ); - await simpleGit.checkout( branchName ); -} - -/** - * Creates a local tag. - * - * @param {string} gitWorkingDirectoryPath Local repository path. - * @param {string} tagName Tag Name - */ -async function createLocalTag( gitWorkingDirectoryPath, tagName ) { - const simpleGit = SimpleGit( gitWorkingDirectoryPath ); - await simpleGit.addTag( tagName ); -} - -/** - * Pushes a local branch to the origin. - * - * @param {string} gitWorkingDirectoryPath Local repository path. - * @param {string} branchName Branch Name - */ -async function pushBranchToOrigin( gitWorkingDirectoryPath, branchName ) { - const simpleGit = SimpleGit( gitWorkingDirectoryPath ); - await simpleGit.push( 'origin', branchName ); -} - -/** - * Pushes tags to the origin. - * - * @param {string} gitWorkingDirectoryPath Local repository path. - */ -async function pushTagsToOrigin( gitWorkingDirectoryPath ) { - const simpleGit = SimpleGit( gitWorkingDirectoryPath ); - await simpleGit.pushTags( 'origin' ); -} - -/** - * Discard local changes. - * - * @param {string} gitWorkingDirectoryPath Local repository path. - */ -async function discardLocalChanges( gitWorkingDirectoryPath ) { - const simpleGit = SimpleGit( gitWorkingDirectoryPath ); - await simpleGit.reset( 'hard' ); -} - -/** - * Reset local branch against the origin. - * - * @param {string} gitWorkingDirectoryPath Local repository path. - * @param {string} branchName Branch Name - */ -async function resetLocalBranchAgainstOrigin( - gitWorkingDirectoryPath, - branchName -) { - const simpleGit = SimpleGit( gitWorkingDirectoryPath ); - await simpleGit.fetch(); - await simpleGit.checkout( branchName ); - await simpleGit.pull( 'origin', branchName ); -} - -/** - * Gets the commit hash for the last commit in the current branch. - * - * @param {string} gitWorkingDirectoryPath Local repository path. - * - * @return {string} Commit hash. - */ -async function getLastCommitHash( gitWorkingDirectoryPath ) { - const simpleGit = SimpleGit( gitWorkingDirectoryPath ); - return await simpleGit.revparse( [ '--short', 'HEAD' ] ); -} - -/** - * Cherry-picks a commit into trunk - * - * @param {string} gitWorkingDirectoryPath Local repository path. - * @param {string} branchName Branch name. - * @param {string} commitHash Commit hash. - */ -async function cherrypickCommitIntoBranch( - gitWorkingDirectoryPath, - branchName, - commitHash -) { - const simpleGit = SimpleGit( gitWorkingDirectoryPath ); - await simpleGit.checkout( branchName ); - await simpleGit.raw( [ 'cherry-pick', commitHash ] ); -} - -/** - * Replaces the local branch's content with the content from another branch. - * - * @param {string} gitWorkingDirectoryPath Local repository path. - * @param {string} sourceBranchName Branch Name - */ -async function replaceContentFromRemoteBranch( - gitWorkingDirectoryPath, - sourceBranchName -) { - const simpleGit = SimpleGit( gitWorkingDirectoryPath ); - await simpleGit.raw( [ 'rm', '-r', '.' ] ); - await simpleGit.raw( [ - 'checkout', - `origin/${ sourceBranchName }`, - '--', - '.', - ] ); -} - -module.exports = { - clone, - commit, - checkoutRemoteBranch, - createLocalBranch, - createLocalTag, - fetch, - pushBranchToOrigin, - pushTagsToOrigin, - discardLocalChanges, - resetLocalBranchAgainstOrigin, - getLastCommitHash, - cherrypickCommitIntoBranch, - replaceContentFromRemoteBranch, -}; diff --git a/bin/tsconfig.json b/bin/tsconfig.json index 86d2a07c742f8c..f3d576c178d0c0 100644 --- a/bin/tsconfig.json +++ b/bin/tsconfig.json @@ -25,7 +25,6 @@ "./plugin/lib/version.js", "./plugin/lib/logger.js", "./plugin/lib/utils.js", - "./plugin/lib/git.js", "./validate-package-lock.js" ] } From 6ab84d5ab63beaac3f630ed20ed53516b077ca7e Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Thu, 10 Nov 2022 21:27:06 +0100 Subject: [PATCH 49/66] Popover: fix exhaustive-deps warning (#45656) * Extract hook dependencies to separate variables * Add refs.flaoting to deps list * CHANGELOG --- packages/components/CHANGELOG.md | 1 + packages/components/src/popover/index.tsx | 25 ++++++++++++++++------- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index a5cf55e2ac9edb..eb3df7f985f750 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -15,6 +15,7 @@ ### Internal - `PaletteEditListView`: Update to ignore `exhaustive-deps` eslint rule ([#45467](https://github.com/WordPress/gutenberg/pull/45467)). +- `Popover`: Update to pass `exhaustive-deps` eslint rule ([#45656](https://github.com/WordPress/gutenberg/pull/45656)). - `Flex`: Update to pass `exhaustive-deps` eslint rule ([#45528](https://github.com/WordPress/gutenberg/pull/45528)). - `withNotices`: Update to pass `exhaustive-deps` eslint rule ([#45530](https://github.com/WordPress/gutenberg/pull/45530)). - `ItemGroup`: Update to pass `exhaustive-deps` eslint rule ([#45531](https://github.com/WordPress/gutenberg/pull/45531)). diff --git a/packages/components/src/popover/index.tsx b/packages/components/src/popover/index.tsx index b960edef57c3c6..1d3b2af1dec1ae 100644 --- a/packages/components/src/popover/index.tsx +++ b/packages/components/src/popover/index.tsx @@ -379,6 +379,17 @@ const UnforwardedPopover = ( // When any of the possible anchor "sources" change, // recompute the reference element (real or virtual) and its owner document. + + const anchorRefTop = ( anchorRef as PopoverAnchorRefTopBottom | undefined ) + ?.top; + const anchorRefBottom = ( + anchorRef as PopoverAnchorRefTopBottom | undefined + )?.bottom; + const anchorRefStartContainer = ( anchorRef as Range | undefined ) + ?.startContainer; + const anchorRefCurrent = ( anchorRef as PopoverAnchorRefReference ) + ?.current; + useLayoutEffect( () => { const resultingReferenceOwnerDoc = getReferenceOwnerDocument( { anchor, @@ -401,11 +412,11 @@ const UnforwardedPopover = ( setReferenceOwnerDocument( resultingReferenceOwnerDoc ); }, [ anchor, - anchorRef as Element | undefined, - ( anchorRef as PopoverAnchorRefTopBottom | undefined )?.top, - ( anchorRef as PopoverAnchorRefTopBottom | undefined )?.bottom, - ( anchorRef as Range | undefined )?.startContainer, - ( anchorRef as PopoverAnchorRefReference )?.current, + anchorRef, + anchorRefTop, + anchorRefBottom, + anchorRefStartContainer, + anchorRefCurrent, anchorRect, getAnchorRect, fallbackReferenceElement, @@ -420,7 +431,7 @@ const UnforwardedPopover = ( // Reference and root documents are the same. referenceOwnerDocument === document || // Reference and floating are in the same document. - referenceOwnerDocument === refs?.floating?.current?.ownerDocument || + referenceOwnerDocument === refs.floating.current?.ownerDocument || // The reference's document has no view (i.e. window) // or frame element (ie. it's not an iframe). ! referenceOwnerDocument?.defaultView?.frameElement @@ -442,7 +453,7 @@ const UnforwardedPopover = ( return () => { defaultView.removeEventListener( 'resize', updateFrameOffset ); }; - }, [ referenceOwnerDocument, update ] ); + }, [ referenceOwnerDocument, update, refs.floating ] ); const mergedFloatingRef = useMergeRefs( [ floating, From 1b209f7faa5641879174ca97b88f6e0d7c6bc5a1 Mon Sep 17 00:00:00 2001 From: Marin Atanasov <8436925+tyxla@users.noreply.github.com> Date: Thu, 10 Nov 2022 14:03:41 -0700 Subject: [PATCH 50/66] Block Editor: Improve ReusableBlocksTab tests (#45652) --- .../inserter/test/reusable-blocks-tab.js | 71 ++++--------------- 1 file changed, 14 insertions(+), 57 deletions(-) diff --git a/packages/block-editor/src/components/inserter/test/reusable-blocks-tab.js b/packages/block-editor/src/components/inserter/test/reusable-blocks-tab.js index 90042d1df5d410..ce47db3d7f4079 100644 --- a/packages/block-editor/src/components/inserter/test/reusable-blocks-tab.js +++ b/packages/block-editor/src/components/inserter/test/reusable-blocks-tab.js @@ -1,13 +1,12 @@ /** * External dependencies */ -import { render, fireEvent } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; /** * WordPress dependencies */ import { registerBlockType, unregisterBlockType } from '@wordpress/blocks'; -import { useSelect } from '@wordpress/data'; /** * Internal dependencies @@ -22,35 +21,6 @@ jest.mock( '../hooks/use-block-types-state', () => { return mock; } ); -jest.mock( '@wordpress/data/src/components/use-select', () => { - // This allows us to tweak the returned value on each test. - const mock = jest.fn(); - return mock; -} ); - -jest.mock( '@wordpress/data/src/components/use-dispatch', () => { - return { - useDispatch: () => ( {} ), - }; -} ); - -const debouncedSpeak = jest.fn(); - -function InserterBlockList( props ) { - return ; -} - -const initializeAllClosedMenuState = ( propOverrides ) => { - const { container } = render( ); - const activeTabs = container.querySelectorAll( - '.components-panel__body.is-opened button.components-panel__body-toggle' - ); - activeTabs.forEach( ( tab ) => { - fireEvent.click( tab ); - } ); - return container; -}; - describe( 'InserterMenu', () => { beforeAll( () => { registerBlockType( 'core/block', { @@ -59,19 +29,17 @@ describe( 'InserterMenu', () => { edit: () => {}, } ); } ); + afterAll( () => { unregisterBlockType( 'core/block' ); } ); - beforeEach( () => { - debouncedSpeak.mockClear(); + beforeEach( () => { useBlockTypesState.mockImplementation( () => [ items, categories, collections, ] ); - - useSelect.mockImplementation( () => false ); } ); it( 'should show nothing if there are no items', () => { @@ -81,36 +49,25 @@ describe( 'InserterMenu', () => { categories, collections, ] ); - const { container } = render( - - ); - const visibleBlocks = container.querySelector( - '.block-editor-block-types-list__item' - ); - expect( visibleBlocks ).toBe( null ); + render( ); + + expect( screen.queryByRole( 'option' ) ).not.toBeInTheDocument(); } ); it( 'should list reusable blocks', () => { - const container = initializeAllClosedMenuState(); - const blocks = container.querySelectorAll( - '.block-editor-block-types-list__item-title' - ); + render( ); - expect( blocks ).toHaveLength( 1 ); - expect( blocks[ 0 ] ).toHaveTextContent( 'My reusable block' ); + expect( + screen.getByRole( 'option', { name: 'My reusable block' } ) + ).toBeVisible(); } ); it( 'should trim whitespace of search terms', () => { - const { container } = render( - - ); - - const blocks = container.querySelectorAll( - '.block-editor-block-types-list__item-title' - ); + render( ); - expect( blocks ).toHaveLength( 1 ); - expect( blocks[ 0 ] ).toHaveTextContent( 'My reusable block' ); + expect( + screen.getByRole( 'option', { name: 'My reusable block' } ) + ).toBeVisible(); } ); } ); From 9928e2c3c7955eaa1f607c3f60bb1f6e5f45b2ce Mon Sep 17 00:00:00 2001 From: Ramon Date: Fri, 11 Nov 2022 08:11:52 +1100 Subject: [PATCH 51/66] Fluid typography: adjust font size min and max rules (#45536) * Initial commit. No tests. * Update PHP unit tests * Updating JS tests Harmonizing test descriptions across PHP and JS Updated CHANGELOG.md and README.md * Updating global styles JS tests * Update typography props JS units tests to reflect new max value * Aligning comments acrossing PHP and JS and making sure they're clearer. Bumping the lower bound from 14px to 16px Updating tests * Updating global styles tests * Revert "Updating global styles tests" This reverts commit 72d4a38b9afc638cf9d96a16ee6185b406504b86. * Revert "Aligning comments acrossing PHP and JS and making sure they're clearer." This reverts commit 863d9aefb96ca02275638311d81b4738628c2461. --- lib/block-supports/typography.php | 55 +++--- packages/block-editor/CHANGELOG.md | 1 + packages/block-editor/README.md | 1 - .../src/components/font-sizes/fluid-utils.js | 101 ++++------ .../components/font-sizes/test/fluid-utils.js | 10 +- .../src/hooks/test/use-typography-props.js | 2 +- .../global-styles/test/typography-utils.js | 180 ++++++++---------- .../test/use-global-styles-output.js | 12 +- phpunit/block-supports/typography-test.php | 130 ++++++------- 9 files changed, 218 insertions(+), 274 deletions(-) diff --git a/lib/block-supports/typography.php b/lib/block-supports/typography.php index b1adaf1a679b81..01d223b84281eb 100644 --- a/lib/block-supports/typography.php +++ b/lib/block-supports/typography.php @@ -461,7 +461,6 @@ function gutenberg_get_typography_font_size_value( $preset, $should_use_fluid_ty $default_maximum_viewport_width = '1600px'; $default_minimum_viewport_width = '768px'; $default_minimum_font_size_factor = 0.75; - $default_maximum_font_size_factor = 1.5; $default_scale_factor = 1; $default_minimum_font_size_limit = '14px'; @@ -480,21 +479,11 @@ function gutenberg_get_typography_font_size_value( $preset, $should_use_fluid_ty // Font sizes. $preferred_size = gutenberg_get_typography_value_and_unit( $preset['size'] ); - // Protect against unsupported units. + // Protects against unsupported units. if ( empty( $preferred_size['unit'] ) ) { return $preset['size']; } - // If no fluid max font size is available, create one using max font size factor. - if ( ! $maximum_font_size_raw ) { - $maximum_font_size_raw = round( $preferred_size['value'] * $default_maximum_font_size_factor, 3 ) . $preferred_size['unit']; - } - - // If no fluid min font size is available, create one using min font size factor. - if ( ! $minimum_font_size_raw ) { - $minimum_font_size_raw = round( $preferred_size['value'] * $default_minimum_font_size_factor, 3 ) . $preferred_size['unit']; - } - // Parses the minimum font size limit, so we can perform checks using it. $minimum_font_size_limit = gutenberg_get_typography_value_and_unit( $default_minimum_font_size_limit, @@ -503,29 +492,35 @@ function gutenberg_get_typography_font_size_value( $preset, $should_use_fluid_ty ) ); - if ( ! empty( $minimum_font_size_limit ) ) { + // Don't enforce minimum font size if a font size has explicitly set a min and max value. + if ( ! empty( $minimum_font_size_limit ) && ( ! $minimum_font_size_raw && ! $maximum_font_size_raw ) ) { /* * If a minimum size was not passed to this function * and the user-defined font size is lower than $minimum_font_size_limit, - * then uses the user-defined font size as the minimum font-size. + * do not calculate a fluid value. */ - if ( ! isset( $fluid_font_size_settings['min'] ) && $preferred_size['value'] < $minimum_font_size_limit['value'] ) { - $minimum_font_size_raw = implode( '', $preferred_size ); + if ( $preferred_size['value'] <= $minimum_font_size_limit['value'] ) { + return $preset['size']; + } + } + + // If no fluid max font size is available use the incoming value. + if ( ! $maximum_font_size_raw ) { + $maximum_font_size_raw = $preferred_size['value'] . $preferred_size['unit']; + } + + /* + * If no minimumFontSize is provided, create one using + * the given font size multiplied by the min font size scale factor. + */ + if ( ! $minimum_font_size_raw ) { + $calculated_minimum_font_size = round( $preferred_size['value'] * $default_minimum_font_size_factor, 3 ); + + // Only use calculated min font size if it's > $minimum_font_size_limit value. + if ( ! empty( $minimum_font_size_limit ) && $calculated_minimum_font_size <= $minimum_font_size_limit['value'] ) { + $minimum_font_size_raw = $minimum_font_size_limit['value'] . $minimum_font_size_limit['unit']; } else { - $minimum_font_size_parsed = gutenberg_get_typography_value_and_unit( - $minimum_font_size_raw, - array( - 'coerce_to' => $preferred_size['unit'], - ) - ); - - /* - * If the passed or calculated minimum font size is lower than $minimum_font_size_limit - * use $minimum_font_size_limit instead. - */ - if ( ! empty( $minimum_font_size_parsed ) && $minimum_font_size_parsed['value'] < $minimum_font_size_limit['value'] ) { - $minimum_font_size_raw = implode( '', $minimum_font_size_limit ); - } + $minimum_font_size_raw = $calculated_minimum_font_size . $preferred_size['unit']; } } diff --git a/packages/block-editor/CHANGELOG.md b/packages/block-editor/CHANGELOG.md index 3a99a7fec0f1e4..54af4cdb6e603d 100644 --- a/packages/block-editor/CHANGELOG.md +++ b/packages/block-editor/CHANGELOG.md @@ -5,6 +5,7 @@ ### Enhancement - `BlockLockModal`: Move Icon component out of CheckboxControl label ([#45535](https://github.com/WordPress/gutenberg/pull/45535)) +- Fluid typography: adjust font size min and max rules ([#45536](https://github.com/WordPress/gutenberg/pull/45536)). ## 10.4.0 (2022-11-02) diff --git a/packages/block-editor/README.md b/packages/block-editor/README.md index 03155e0f4ddd89..5760f1584c5db8 100644 --- a/packages/block-editor/README.md +++ b/packages/block-editor/README.md @@ -428,7 +428,6 @@ _Parameters_ - _args.minimumFontSize_ `?string`: Minimum font size for any clamp() calculation. Optional. - _args.scaleFactor_ `?number`: A scale factor to determine how fast a font scales within boundaries. Optional. - _args.minimumFontSizeFactor_ `?number`: How much to scale defaultFontSize by to derive minimumFontSize. Optional. -- _args.maximumFontSizeFactor_ `?number`: How much to scale defaultFontSize by to derive maximumFontSize. Optional. _Returns_ diff --git a/packages/block-editor/src/components/font-sizes/fluid-utils.js b/packages/block-editor/src/components/font-sizes/fluid-utils.js index 2c6540f89d0494..de8a27e3014e88 100644 --- a/packages/block-editor/src/components/font-sizes/fluid-utils.js +++ b/packages/block-editor/src/components/font-sizes/fluid-utils.js @@ -9,7 +9,6 @@ const DEFAULT_MAXIMUM_VIEWPORT_WIDTH = '1600px'; const DEFAULT_MINIMUM_VIEWPORT_WIDTH = '768px'; const DEFAULT_SCALE_FACTOR = 1; const DEFAULT_MINIMUM_FONT_SIZE_FACTOR = 0.75; -const DEFAULT_MAXIMUM_FONT_SIZE_FACTOR = 1.5; const DEFAULT_MINIMUM_FONT_SIZE_LIMIT = '14px'; /** @@ -41,7 +40,6 @@ const DEFAULT_MINIMUM_FONT_SIZE_LIMIT = '14px'; * @param {?string} args.minimumFontSize Minimum font size for any clamp() calculation. Optional. * @param {?number} args.scaleFactor A scale factor to determine how fast a font scales within boundaries. Optional. * @param {?number} args.minimumFontSizeFactor How much to scale defaultFontSize by to derive minimumFontSize. Optional. - * @param {?number} args.maximumFontSizeFactor How much to scale defaultFontSize by to derive maximumFontSize. Optional. * * @return {string|null} A font-size value using clamp(). */ @@ -53,15 +51,8 @@ export function getComputedFluidTypographyValue( { maximumViewPortWidth = DEFAULT_MAXIMUM_VIEWPORT_WIDTH, scaleFactor = DEFAULT_SCALE_FACTOR, minimumFontSizeFactor = DEFAULT_MINIMUM_FONT_SIZE_FACTOR, - maximumFontSizeFactor = DEFAULT_MAXIMUM_FONT_SIZE_FACTOR, minimumFontSizeLimit = DEFAULT_MINIMUM_FONT_SIZE_LIMIT, } ) { - /* - * Caches minimumFontSize in minimumFontSizeValue - * so we can check if minimumFontSize exists later. - */ - let minimumFontSizeValue = minimumFontSize; - /* * Calculates missing minimumFontSize and maximumFontSize from * defaultFontSize if provided. @@ -75,15 +66,6 @@ export function getComputedFluidTypographyValue( { return null; } - // If no minimumFontSize is provided, derive using min scale factor. - if ( ! minimumFontSizeValue ) { - minimumFontSizeValue = - roundToPrecision( - fontSizeParsed.value * minimumFontSizeFactor, - 3 - ) + fontSizeParsed.unit; - } - // Parses the minimum font size limit, so we can perform checks using it. const minimumFontSizeLimitParsed = getTypographyValueAndUnit( minimumFontSizeLimit, @@ -92,57 +74,51 @@ export function getComputedFluidTypographyValue( { } ); - if ( !! minimumFontSizeLimitParsed?.value ) { + // Don't enforce minimum font size if a font size has explicitly set a min and max value. + if ( + !! minimumFontSizeLimitParsed?.value && + ! minimumFontSize && + ! maximumFontSize + ) { /* * If a minimum size was not passed to this function - * and the user-defined font size is lower than `minimumFontSizeLimit`, - * then uses the user-defined font size as the minimum font-size. + * and the user-defined font size is lower than $minimum_font_size_limit, + * do not calculate a fluid value. */ - if ( - ! minimumFontSize && - fontSizeParsed?.value < minimumFontSizeLimitParsed?.value - ) { - minimumFontSizeValue = `${ fontSizeParsed.value }${ fontSizeParsed.unit }`; - } else { - const minimumFontSizeParsed = getTypographyValueAndUnit( - minimumFontSizeValue, - { - coerceTo: fontSizeParsed.unit, - } - ); - - /* - * Otherwise, if the passed or calculated minimum font size is lower than `minimumFontSizeLimit` - * use `minimumFontSizeLimit` instead. - */ - if ( - !! minimumFontSizeParsed?.value && - minimumFontSizeParsed.value < - minimumFontSizeLimitParsed.value - ) { - minimumFontSizeValue = `${ minimumFontSizeLimitParsed.value }${ minimumFontSizeLimitParsed.unit }`; - } + if ( fontSizeParsed?.value <= minimumFontSizeLimitParsed?.value ) { + return null; } } - // If no maximumFontSize is provided, derive using max scale factor. + // If no fluid max font size is available use the incoming value. if ( ! maximumFontSize ) { - maximumFontSize = - roundToPrecision( - fontSizeParsed.value * maximumFontSizeFactor, - 3 - ) + fontSizeParsed.unit; + maximumFontSize = `${ fontSizeParsed.value }${ fontSizeParsed.unit }`; } - } - // Return early if one of the provided inputs is not provided. - if ( ! minimumFontSizeValue || ! maximumFontSize ) { - return null; + /* + * If no minimumFontSize is provided, create one using + * the given font size multiplied by the min font size scale factor. + */ + if ( ! minimumFontSize ) { + const calculatedMinimumFontSize = roundToPrecision( + fontSizeParsed.value * minimumFontSizeFactor, + 3 + ); + + // Only use calculated min font size if it's > $minimum_font_size_limit value. + if ( + !! minimumFontSizeLimitParsed?.value && + calculatedMinimumFontSize < minimumFontSizeLimitParsed?.value + ) { + minimumFontSize = `${ minimumFontSizeLimitParsed.value }${ minimumFontSizeLimitParsed.unit }`; + } else { + minimumFontSize = `${ calculatedMinimumFontSize }${ fontSizeParsed.unit }`; + } + } } // Grab the minimum font size and normalize it in order to use the value for calculations. - const minimumFontSizeParsed = - getTypographyValueAndUnit( minimumFontSizeValue ); + const minimumFontSizeParsed = getTypographyValueAndUnit( minimumFontSize ); // We get a 'preferred' unit to keep units consistent when calculating, // otherwise the result will not be accurate. @@ -159,12 +135,9 @@ export function getComputedFluidTypographyValue( { } // Uses rem for accessible fluid target font scaling. - const minimumFontSizeRem = getTypographyValueAndUnit( - minimumFontSizeValue, - { - coerceTo: 'rem', - } - ); + const minimumFontSizeRem = getTypographyValueAndUnit( minimumFontSize, { + coerceTo: 'rem', + } ); // Viewport widths defined for fluid typography. Normalize units const maximumViewPortWidthParsed = getTypographyValueAndUnit( @@ -205,7 +178,7 @@ export function getComputedFluidTypographyValue( { ); const fluidTargetFontSize = `${ minimumFontSizeRem.value }${ minimumFontSizeRem.unit } + ((1vw - ${ viewPortWidthOffset }) * ${ linearFactorScaled })`; - return `clamp(${ minimumFontSizeValue }, ${ fluidTargetFontSize }, ${ maximumFontSize })`; + return `clamp(${ minimumFontSize }, ${ fluidTargetFontSize }, ${ maximumFontSize })`; } /** diff --git a/packages/block-editor/src/components/font-sizes/test/fluid-utils.js b/packages/block-editor/src/components/font-sizes/test/fluid-utils.js index aa268d04d7f1f2..15805431a959fb 100644 --- a/packages/block-editor/src/components/font-sizes/test/fluid-utils.js +++ b/packages/block-editor/src/components/font-sizes/test/fluid-utils.js @@ -33,7 +33,7 @@ describe( 'getComputedFluidTypographyValue()', () => { fontSize: '30px', } ); expect( fluidTypographyValues ).toBe( - 'clamp(22.5px, 1.406rem + ((1vw - 7.68px) * 2.704), 45px)' + 'clamp(22.5px, 1.406rem + ((1vw - 7.68px) * 0.901), 30px)' ); } ); @@ -42,7 +42,7 @@ describe( 'getComputedFluidTypographyValue()', () => { fontSize: '30px', } ); expect( fluidTypographyValues ).toBe( - 'clamp(22.5px, 1.406rem + ((1vw - 7.68px) * 2.704), 45px)' + 'clamp(22.5px, 1.406rem + ((1vw - 7.68px) * 0.901), 30px)' ); } ); @@ -53,7 +53,7 @@ describe( 'getComputedFluidTypographyValue()', () => { maximumViewPortWidth: '1000px', } ); expect( fluidTypographyValues ).toBe( - 'clamp(22.5px, 1.406rem + ((1vw - 5px) * 4.5), 45px)' + 'clamp(22.5px, 1.406rem + ((1vw - 5px) * 1.5), 30px)' ); } ); @@ -63,7 +63,7 @@ describe( 'getComputedFluidTypographyValue()', () => { scaleFactor: '2', } ); expect( fluidTypographyValues ).toBe( - 'clamp(22.5px, 1.406rem + ((1vw - 7.68px) * 5.409), 45px)' + 'clamp(22.5px, 1.406rem + ((1vw - 7.68px) * 1.803), 30px)' ); } ); @@ -74,7 +74,7 @@ describe( 'getComputedFluidTypographyValue()', () => { maximumFontSizeFactor: '2', } ); expect( fluidTypographyValues ).toBe( - 'clamp(15px, 0.938rem + ((1vw - 7.68px) * 5.409), 60px)' + 'clamp(15px, 0.938rem + ((1vw - 7.68px) * 1.803), 30px)' ); } ); diff --git a/packages/block-editor/src/hooks/test/use-typography-props.js b/packages/block-editor/src/hooks/test/use-typography-props.js index 52f7bf97328ee3..00557881467ca8 100644 --- a/packages/block-editor/src/hooks/test/use-typography-props.js +++ b/packages/block-editor/src/hooks/test/use-typography-props.js @@ -42,7 +42,7 @@ describe( 'getTypographyClassesAndStyles', () => { style: { letterSpacing: '22px', fontSize: - 'clamp(1.5rem, 1.5rem + ((1vw - 0.48rem) * 2.885), 3rem)', + 'clamp(1.5rem, 1.5rem + ((1vw - 0.48rem) * 0.962), 2rem)', textTransform: 'uppercase', }, } ); diff --git a/packages/edit-site/src/components/global-styles/test/typography-utils.js b/packages/edit-site/src/components/global-styles/test/typography-utils.js index e0c29a37ea9811..647b02cb4be1fb 100644 --- a/packages/edit-site/src/components/global-styles/test/typography-utils.js +++ b/packages/edit-site/src/components/global-styles/test/typography-utils.js @@ -7,7 +7,7 @@ describe( 'typography utils', () => { describe( 'getTypographyFontSizeValue', () => { [ { - message: 'Default return non-fluid value.', + message: 'returns value when fluid typography is deactivated', preset: { size: '28px', }, @@ -16,7 +16,7 @@ describe( 'typography utils', () => { }, { - message: 'Default return value where font size is 0.', + message: 'returns value where font size is 0', preset: { size: 0, }, @@ -25,7 +25,7 @@ describe( 'typography utils', () => { }, { - message: "Default return value where font size is '0'.", + message: "returns value where font size is '0'", preset: { size: '0', }, @@ -34,17 +34,16 @@ describe( 'typography utils', () => { }, { - message: - 'Default return non-fluid value where `size` is undefined.', + message: 'returns value where `size` is `null`.', preset: { - size: undefined, + size: null, }, - typographySettings: undefined, - expected: undefined, + typographySettings: null, + expected: null, }, { - message: 'return non-fluid value when fluid is `false`.', + message: 'returns value when fluid is `false`', preset: { size: '28px', fluid: false, @@ -56,84 +55,94 @@ describe( 'typography utils', () => { }, { - message: - 'Should coerce integer to `px` and return fluid value.', + message: 'returns already clamped value', preset: { - size: 33, - fluid: true, + size: 'clamp(21px, 1.313rem + ((1vw - 7.68px) * 2.524), 42px)', + fluid: false, }, typographySettings: { fluid: true, }, expected: - 'clamp(24.75px, 1.547rem + ((1vw - 7.68px) * 2.975), 49.5px)', + 'clamp(21px, 1.313rem + ((1vw - 7.68px) * 2.524), 42px)', }, { - message: 'coerce float to `px` and return fluid value.', + message: 'returns value with unsupported unit', preset: { - size: 100.23, + size: '1000%', + fluid: false, + }, + typographySettings: { fluid: true, }, + expected: '1000%', + }, + + { + message: 'returns clamp value with rem min and max units', + preset: { + size: '1.75rem', + }, typographySettings: { fluid: true, }, expected: - 'clamp(75.173px, 4.698rem + ((1vw - 7.68px) * 9.035), 150.345px)', + 'clamp(1.313rem, 1.313rem + ((1vw - 0.48rem) * 0.84), 1.75rem)', }, { - message: 'return incoming value when already clamped.', + message: 'returns clamp value with eem min and max units', preset: { - size: 'clamp(21px, 1.313rem + ((1vw - 7.68px) * 2.524), 42px)', - fluid: false, + size: '1.75em', }, typographySettings: { fluid: true, }, expected: - 'clamp(21px, 1.313rem + ((1vw - 7.68px) * 2.524), 42px)', + 'clamp(1.313em, 1.313rem + ((1vw - 0.48em) * 0.84), 1.75em)', }, { - message: 'return incoming value with unsupported unit.', + message: 'returns clamp value for floats', preset: { - size: '1000%', - fluid: false, + size: '100.175px', }, typographySettings: { fluid: true, }, - expected: '1000%', + expected: + 'clamp(75.131px, 4.696rem + ((1vw - 7.68px) * 3.01), 100.175px)', }, { - message: 'return fluid value.', + message: 'coerces integer to `px` and returns clamp value', preset: { - size: '1.75rem', + size: 33, + fluid: true, }, typographySettings: { fluid: true, }, expected: - 'clamp(1.313rem, 1.313rem + ((1vw - 0.48rem) * 2.523), 2.625rem)', + 'clamp(24.75px, 1.547rem + ((1vw - 7.68px) * 0.992), 33px)', }, { - message: 'return fluid value for floats with units.', + message: 'coerces float to `px` and returns clamp value', preset: { - size: '100.175px', + size: 100.23, + fluid: true, }, typographySettings: { fluid: true, }, expected: - 'clamp(75.131px, 4.696rem + ((1vw - 7.68px) * 9.03), 150.263px)', + 'clamp(75.173px, 4.698rem + ((1vw - 7.68px) * 3.012), 100.23px)', }, { - message: - 'Should return default fluid values with empty fluid array.', + message: 'returns clamp value when `fluid` is empty array', preset: { size: '28px', fluid: [], @@ -142,11 +151,11 @@ describe( 'typography utils', () => { fluid: true, }, expected: - 'clamp(21px, 1.313rem + ((1vw - 7.68px) * 2.524), 42px)', + 'clamp(21px, 1.313rem + ((1vw - 7.68px) * 0.841), 28px)', }, { - message: 'return default fluid values with null value.', + message: 'returns clamp value when `fluid` is `null`', preset: { size: '28px', fluid: null, @@ -155,12 +164,12 @@ describe( 'typography utils', () => { fluid: true, }, expected: - 'clamp(21px, 1.313rem + ((1vw - 7.68px) * 2.524), 42px)', + 'clamp(21px, 1.313rem + ((1vw - 7.68px) * 0.841), 28px)', }, { message: - 'return clamped value if min font size is greater than max.', + 'returns clamp value if min font size is greater than max', preset: { size: '3rem', fluid: { @@ -176,7 +185,7 @@ describe( 'typography utils', () => { }, { - message: 'return size with invalid fluid units.', + message: 'returns value with invalid min/max fluid units', preset: { size: '10em', fluid: { @@ -192,20 +201,30 @@ describe( 'typography utils', () => { { message: - 'return clamped using font size where no min is given and size is less than default min size.', + 'returns value when size is < lower bounds and no fluid min/max set', preset: { size: '3px', }, typographySettings: { fluid: true, }, - expected: - 'clamp(3px, 0.188rem + ((1vw - 7.68px) * 0.18), 4.5px)', + expected: '3px', }, { message: - 'return fluid clamp value with different min max units.', + 'returns value when size is equal to lower bounds and no fluid min/max set', + preset: { + size: '14px', + }, + typographySettings: { + fluid: true, + }, + expected: '14px', + }, + + { + message: 'returns clamp value with different min max units', preset: { size: '28px', fluid: { @@ -219,10 +238,9 @@ describe( 'typography utils', () => { expected: 'clamp(20px, 1.25rem + ((1vw - 7.68px) * 93.75), 50rem)', }, - // + { - message: - ' Should return clamp value with default fluid max value.', + message: 'returns clamp value where no fluid max size is set', preset: { size: '28px', fluid: { @@ -233,12 +251,11 @@ describe( 'typography utils', () => { fluid: true, }, expected: - 'clamp(2.6rem, 2.6rem + ((1vw - 0.48rem) * 0.048), 42px)', + 'clamp(2.6rem, 2.6rem + ((1vw - 0.48rem) * -1.635), 28px)', }, { - message: - 'Should return clamp value with default fluid min value.', + message: 'returns clamp value where no fluid min size is set', preset: { size: '28px', fluid: { @@ -253,90 +270,57 @@ describe( 'typography utils', () => { }, { - message: 'adjust computed min in px to min limit.', - preset: { - size: '14px', - }, - typographySettings: { - fluid: true, - }, - expected: - 'clamp(14px, 0.875rem + ((1vw - 7.68px) * 0.841), 21px)', - }, - - { - message: 'adjust computed min in rem to min limit.', - preset: { - size: '1.1rem', - }, - typographySettings: { - fluid: true, - }, - expected: - 'clamp(0.875rem, 0.875rem + ((1vw - 0.48rem) * 1.49), 1.65rem)', - }, - - { - message: 'adjust computed min in em to min limit.', - preset: { - size: '1.1em', - }, - typographySettings: { - fluid: true, - }, - expected: - 'clamp(0.875em, 0.875rem + ((1vw - 0.48em) * 1.49), 1.65em)', - }, - - { - message: 'adjust fluid min value in px to min limit', + message: + 'should not apply lower bound test when fluid values are set', preset: { - size: '20px', + size: '1.5rem', fluid: { - min: '12px', + min: '0.5rem', + max: '5rem', }, }, typographySettings: { fluid: true, }, expected: - 'clamp(14px, 0.875rem + ((1vw - 7.68px) * 1.923), 30px)', + 'clamp(0.5rem, 0.5rem + ((1vw - 0.48rem) * 8.654), 5rem)', }, { - message: 'adjust fluid min value in rem to min limit.', + message: + 'should not apply lower bound test when only fluid min is set', preset: { - size: '1.5rem', + size: '20px', fluid: { - min: '0.5rem', + min: '12px', }, }, typographySettings: { fluid: true, }, expected: - 'clamp(0.875rem, 0.875rem + ((1vw - 0.48rem) * 2.644), 2.25rem)', + 'clamp(12px, 0.75rem + ((1vw - 7.68px) * 0.962), 20px)', }, { - message: 'adjust fluid min value but honor max value.', + message: + 'should not apply lower bound test when only fluid max is set', preset: { - size: '1.5rem', + size: '0.875rem', fluid: { - min: '0.5rem', - max: '5rem', + max: '20rem', }, }, typographySettings: { fluid: true, }, expected: - 'clamp(0.875rem, 0.875rem + ((1vw - 0.48rem) * 7.933), 5rem)', + 'clamp(0.875rem, 0.875rem + ((1vw - 0.48rem) * 36.779), 20rem)', }, { message: - 'return a fluid font size a min and max font sizes are equal.', + 'returns clamp value when min and max font sizes are equal', preset: { size: '4rem', fluid: { diff --git a/packages/edit-site/src/components/global-styles/test/use-global-styles-output.js b/packages/edit-site/src/components/global-styles/test/use-global-styles-output.js index a6bd55ff86ec8f..a26221247dc9b9 100644 --- a/packages/edit-site/src/components/global-styles/test/use-global-styles-output.js +++ b/packages/edit-site/src/components/global-styles/test/use-global-styles-output.js @@ -711,7 +711,7 @@ describe( 'global styles renderer', () => { }, typography: { fontFamily: 'sans-serif', - fontSize: '14px', + fontSize: '15px', }, }; @@ -725,7 +725,7 @@ describe( 'global styles renderer', () => { '--wp--style--root--padding-left: 33px', 'background-color: var(--wp--preset--color--light-green-cyan)', 'font-family: sans-serif', - 'font-size: 14px', + 'font-size: 15px', ] ); } ); @@ -739,7 +739,7 @@ describe( 'global styles renderer', () => { 'padding-bottom: 33px', 'padding-left: 33px', 'font-family: sans-serif', - 'font-size: 14px', + 'font-size: 15px', ] ); } ); @@ -757,7 +757,7 @@ describe( 'global styles renderer', () => { 'padding-bottom: 33px', 'padding-left: 33px', 'font-family: sans-serif', - 'font-size: 14px', + 'font-size: 15px', ] ); } ); @@ -782,7 +782,7 @@ describe( 'global styles renderer', () => { 'padding-bottom: 33px', 'padding-left: 33px', 'font-family: sans-serif', - 'font-size: clamp(14px, 0.875rem + ((1vw - 7.68px) * 0.841), 21px)', + 'font-size: clamp(14px, 0.875rem + ((1vw - 7.68px) * 0.12), 15px)', ] ); } ); @@ -807,7 +807,7 @@ describe( 'global styles renderer', () => { 'padding-bottom: 33px', 'padding-left: 33px', 'font-family: sans-serif', - 'font-size: 14px', + 'font-size: 15px', ] ); } ); } ); diff --git a/phpunit/block-supports/typography-test.php b/phpunit/block-supports/typography-test.php index 6ecac215b0f61c..63fd98d1e524ff 100644 --- a/phpunit/block-supports/typography-test.php +++ b/phpunit/block-supports/typography-test.php @@ -315,7 +315,7 @@ public function test_gutenberg_get_typography_font_size_value( $font_size, $shou */ public function data_generate_font_size_preset_fixtures() { return array( - 'default_return_value' => array( + 'returns value when fluid typography is deactivated' => array( 'font_size' => array( 'size' => '28px', ), @@ -323,7 +323,7 @@ public function data_generate_font_size_preset_fixtures() { 'expected_output' => '28px', ), - 'size: int 0' => array( + 'returns value where font size is 0' => array( 'font_size' => array( 'size' => 0, ), @@ -331,7 +331,7 @@ public function data_generate_font_size_preset_fixtures() { 'expected_output' => 0, ), - 'size: string 0' => array( + "returns value where font size is '0'" => array( 'font_size' => array( 'size' => '0', ), @@ -339,7 +339,7 @@ public function data_generate_font_size_preset_fixtures() { 'expected_output' => '0', ), - 'default_return_value_when_size_is_undefined' => array( + 'returns value where `size` is `null`' => array( 'font_size' => array( 'size' => null, ), @@ -347,7 +347,7 @@ public function data_generate_font_size_preset_fixtures() { 'expected_output' => null, ), - 'default_return_value_when_fluid_is_false' => array( + 'returns value when fluid is `false`' => array( 'font_size' => array( 'size' => '28px', 'fluid' => false, @@ -356,7 +356,7 @@ public function data_generate_font_size_preset_fixtures() { 'expected_output' => '28px', ), - 'default_return_value_when_value_is_already_clamped' => array( + 'returns already clamped value' => array( 'font_size' => array( 'size' => 'clamp(21px, 1.313rem + ((1vw - 7.68px) * 2.524), 42px)', 'fluid' => false, @@ -365,7 +365,7 @@ public function data_generate_font_size_preset_fixtures() { 'expected_output' => 'clamp(21px, 1.313rem + ((1vw - 7.68px) * 2.524), 42px)', ), - 'default_return_value_with_unsupported_unit' => array( + 'returns value with unsupported unit' => array( 'font_size' => array( 'size' => '1000%', 'fluid' => false, @@ -374,57 +374,65 @@ public function data_generate_font_size_preset_fixtures() { 'expected_output' => '1000%', ), - 'return_fluid_value' => array( + 'returns clamp value with rem min and max units' => array( 'font_size' => array( 'size' => '1.75rem', ), 'should_use_fluid_typography' => true, - 'expected_output' => 'clamp(1.313rem, 1.313rem + ((1vw - 0.48rem) * 2.523), 2.625rem)', + 'expected_output' => 'clamp(1.313rem, 1.313rem + ((1vw - 0.48rem) * 0.84), 1.75rem)', ), - 'return_fluid_value_with_floats_with_units' => array( + 'returns clamp value with em min and max units' => array( + 'font_size' => array( + 'size' => '1.75em', + ), + 'should_use_fluid_typography' => true, + 'expected_output' => 'clamp(1.313em, 1.313rem + ((1vw - 0.48em) * 0.84), 1.75em)', + ), + + 'returns clamp value for floats' => array( 'font_size' => array( 'size' => '100.175px', ), 'should_use_fluid_typography' => true, - 'expected_output' => 'clamp(75.131px, 4.696rem + ((1vw - 7.68px) * 9.03), 150.263px)', + 'expected_output' => 'clamp(75.131px, 4.696rem + ((1vw - 7.68px) * 3.01), 100.175px)', ), - 'return_fluid_value_with_integer_coerced_to_px' => array( + 'coerces integer to `px` and returns clamp value' => array( 'font_size' => array( 'size' => 33, ), 'should_use_fluid_typography' => true, - 'expected_output' => 'clamp(24.75px, 1.547rem + ((1vw - 7.68px) * 2.975), 49.5px)', + 'expected_output' => 'clamp(24.75px, 1.547rem + ((1vw - 7.68px) * 0.992), 33px)', ), - 'return_fluid_value_with_float_coerced_to_px' => array( + 'coerces float to `px` and returns clamp value' => array( 'font_size' => array( 'size' => 100.23, ), 'should_use_fluid_typography' => true, - 'expected_output' => 'clamp(75.173px, 4.698rem + ((1vw - 7.68px) * 9.035), 150.345px)', + 'expected_output' => 'clamp(75.173px, 4.698rem + ((1vw - 7.68px) * 3.012), 100.23px)', ), - 'return_default_fluid_values_with_empty_fluid_array' => array( + 'returns clamp value when `fluid` is empty array' => array( 'font_size' => array( 'size' => '28px', 'fluid' => array(), ), 'should_use_fluid_typography' => true, - 'expected_output' => 'clamp(21px, 1.313rem + ((1vw - 7.68px) * 2.524), 42px)', + 'expected_output' => 'clamp(21px, 1.313rem + ((1vw - 7.68px) * 0.841), 28px)', ), - 'return_default_fluid_values_with_null_value' => array( + 'returns clamp value when `fluid` is `null`' => array( 'font_size' => array( 'size' => '28px', 'fluid' => null, ), 'should_use_fluid_typography' => true, - 'expected_output' => 'clamp(21px, 1.313rem + ((1vw - 7.68px) * 2.524), 42px)', + 'expected_output' => 'clamp(21px, 1.313rem + ((1vw - 7.68px) * 0.841), 28px)', ), - 'return_clamped_value_if_min_font_size_is_greater_than_max' => array( + 'returns clamp value if min font size is greater than max' => array( 'font_size' => array( 'size' => '3rem', 'fluid' => array( @@ -436,7 +444,7 @@ public function data_generate_font_size_preset_fixtures() { 'expected_output' => 'clamp(5rem, 5rem + ((1vw - 0.48rem) * -5.769), 32px)', ), - 'return_size_with_invalid_fluid_units' => array( + 'returns value with invalid min/max fluid units' => array( 'font_size' => array( 'size' => '10em', 'fluid' => array( @@ -448,15 +456,23 @@ public function data_generate_font_size_preset_fixtures() { 'expected_output' => '10em', ), - 'return_clamped_size_where_no_min_is_given_and_less_than_default_min_size' => array( + 'returns value when size is < lower bounds and no fluid min/max set' => array( 'font_size' => array( 'size' => '3px', ), 'should_use_fluid_typography' => true, - 'expected_output' => 'clamp(3px, 0.188rem + ((1vw - 7.68px) * 0.18), 4.5px)', + 'expected_output' => '3px', ), - 'return_fluid_clamp_value_with_different_min_max_units' => array( + 'returns value when size is equal to lower bounds and no fluid min/max set' => array( + 'font_size' => array( + 'size' => '14px', + ), + 'should_use_fluid_typography' => true, + 'expected_output' => '14px', + ), + + 'returns clamp value with different min max units' => array( 'font_size' => array( 'size' => '28px', 'fluid' => array( @@ -468,7 +484,7 @@ public function data_generate_font_size_preset_fixtures() { 'expected_output' => 'clamp(20px, 1.25rem + ((1vw - 7.68px) * 93.75), 50rem)', ), - 'return_clamp_value_with_default_fluid_max_value' => array( + 'returns clamp value where no fluid max size is set' => array( 'font_size' => array( 'size' => '28px', 'fluid' => array( @@ -476,10 +492,10 @@ public function data_generate_font_size_preset_fixtures() { ), ), 'should_use_fluid_typography' => true, - 'expected_output' => 'clamp(2.6rem, 2.6rem + ((1vw - 0.48rem) * 0.048), 42px)', + 'expected_output' => 'clamp(2.6rem, 2.6rem + ((1vw - 0.48rem) * -1.635), 28px)', ), - 'default_return_clamp_value_with_default_fluid_min_value' => array( + 'returns clamp value where no fluid min size is set' => array( 'font_size' => array( 'size' => '28px', 'fluid' => array( @@ -490,65 +506,41 @@ public function data_generate_font_size_preset_fixtures() { 'expected_output' => 'clamp(21px, 1.313rem + ((1vw - 7.68px) * 7.091), 80px)', ), - 'should_adjust_computed_min_in_px_to_min_limit' => array( - 'font_size' => array( - 'size' => '14px', - ), - 'should_use_fluid_typography' => true, - 'expected_output' => 'clamp(14px, 0.875rem + ((1vw - 7.68px) * 0.841), 21px)', - ), - - 'should_adjust_computed_min_in_rem_to_min_limit' => array( + 'should not apply lower bound test when fluid values are set' => array( 'font_size' => array( - 'size' => '1.1rem', - ), - 'should_use_fluid_typography' => true, - 'expected_output' => 'clamp(0.875rem, 0.875rem + ((1vw - 0.48rem) * 1.49), 1.65rem)', - ), - - 'default_return_clamp_value_with_replaced_fluid_min_value_in_em' => array( - 'font_size' => array( - 'size' => '1.1em', - ), - 'should_use_fluid_typography' => true, - 'expected_output' => 'clamp(0.875em, 0.875rem + ((1vw - 0.48em) * 1.49), 1.65em)', - ), - - 'should_adjust_fluid_min_value_in_px_to_min_limit' => array( - 'font_size' => array( - 'size' => '20px', + 'size' => '1.5rem', 'fluid' => array( - 'min' => '12px', + 'min' => '0.5rem', + 'max' => '5rem', ), ), 'should_use_fluid_typography' => true, - 'expected_output' => 'clamp(14px, 0.875rem + ((1vw - 7.68px) * 1.923), 30px)', + 'expected_output' => 'clamp(0.5rem, 0.5rem + ((1vw - 0.48rem) * 8.654), 5rem)', ), - 'should_adjust_fluid_min_value_in_rem_to_min_limit' => array( + 'should not apply lower bound test when only fluid min is set' => array( 'font_size' => array( - 'size' => '1.5rem', + 'size' => '20px', 'fluid' => array( - 'min' => '0.5rem', + 'min' => '12px', ), ), 'should_use_fluid_typography' => true, - 'expected_output' => 'clamp(0.875rem, 0.875rem + ((1vw - 0.48rem) * 2.644), 2.25rem)', + 'expected_output' => 'clamp(12px, 0.75rem + ((1vw - 7.68px) * 0.962), 20px)', ), - 'should_adjust_fluid_min_value_but_honor_max_value' => array( + 'should not apply lower bound test when only fluid max is set' => array( 'font_size' => array( - 'size' => '1.5rem', + 'size' => '0.875rem', 'fluid' => array( - 'min' => '0.5rem', - 'max' => '5rem', + 'max' => '20rem', ), ), 'should_use_fluid_typography' => true, - 'expected_output' => 'clamp(0.875rem, 0.875rem + ((1vw - 0.48rem) * 7.933), 5rem)', + 'expected_output' => 'clamp(0.875rem, 0.875rem + ((1vw - 0.48rem) * 36.779), 20rem)', ), - 'should_return_fluid_value_when_min_and_max_font_sizes_are_equal' => array( + 'returns clamp value when min and max font sizes are equal' => array( 'font_size' => array( 'size' => '4rem', 'fluid' => array( @@ -630,7 +622,7 @@ public function data_generate_block_supports_font_size_fixtures() { 'return_value_with_fluid_typography' => array( 'font_size_value' => '50px', 'should_use_fluid_typography' => true, - 'expected_output' => 'font-size:clamp(37.5px, 2.344rem + ((1vw - 7.68px) * 4.507), 75px);', + 'expected_output' => 'font-size:clamp(37.5px, 2.344rem + ((1vw - 7.68px) * 1.502), 50px);', ), ); } @@ -688,7 +680,7 @@ public function data_generate_replace_inline_font_styles_with_fluid_values_fixtu 'block_content' => '', 'font_size_value' => '4rem', 'should_use_fluid_typography' => true, - 'expected_output' => '', + 'expected_output' => '', ), 'return_content_if_no_inline_font_size_found' => array( 'block_content' => '

    A paragraph inside a group

    ', @@ -706,13 +698,13 @@ public function data_generate_replace_inline_font_styles_with_fluid_values_fixtu 'block_content' => '

    A paragraph inside a group

    ', 'font_size_value' => '20px', 'should_use_fluid_typography' => true, - 'expected_output' => '

    A paragraph inside a group

    ', + 'expected_output' => '

    A paragraph inside a group

    ', ), 'return_content_with_first_match_replace_only' => array( 'block_content' => "
    \n \n

    A paragraph inside a group

    ", 'font_size_value' => '1.5em', 'should_use_fluid_typography' => true, - 'expected_output' => "
    \n \n

    A paragraph inside a group

    ", + 'expected_output' => "
    \n \n

    A paragraph inside a group

    ", ), ); } From 148f08966752f2a50460a9c80eac4056c359c838 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Thu, 10 Nov 2022 22:27:15 +0100 Subject: [PATCH 52/66] TabPanel: fix the `exhaustive-deps` warning (#45660) * TabPanel: add `handleTabSelection` to the list of hook deps * CHANGELOG --- packages/components/CHANGELOG.md | 1 + packages/components/src/tab-panel/index.tsx | 15 +++++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index eb3df7f985f750..c363ddaca6285a 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -19,6 +19,7 @@ - `Flex`: Update to pass `exhaustive-deps` eslint rule ([#45528](https://github.com/WordPress/gutenberg/pull/45528)). - `withNotices`: Update to pass `exhaustive-deps` eslint rule ([#45530](https://github.com/WordPress/gutenberg/pull/45530)). - `ItemGroup`: Update to pass `exhaustive-deps` eslint rule ([#45531](https://github.com/WordPress/gutenberg/pull/45531)). +- `TabPanel`: Update to pass `exhaustive-deps` eslint rule ([#45660](https://github.com/WordPress/gutenberg/pull/45660)). - `NavigatorScreen`: Update to pass `exhaustive-deps` eslint rule ([#45648](https://github.com/WordPress/gutenberg/pull/45648)). - `Draggable`: Convert to TypeScript ([#45471](https://github.com/WordPress/gutenberg/pull/45471)). - `MenuGroup`: Convert to TypeScript ([#45617](https://github.com/WordPress/gutenberg/pull/45617)). diff --git a/packages/components/src/tab-panel/index.tsx b/packages/components/src/tab-panel/index.tsx index 4e0b0394bd7409..206c2b8aab0a71 100644 --- a/packages/components/src/tab-panel/index.tsx +++ b/packages/components/src/tab-panel/index.tsx @@ -7,7 +7,7 @@ import { find } from 'lodash'; /** * WordPress dependencies */ -import { useState, useEffect } from '@wordpress/element'; +import { useState, useEffect, useCallback } from '@wordpress/element'; import { useInstanceId } from '@wordpress/compose'; /** @@ -85,10 +85,13 @@ export function TabPanel( { const instanceId = useInstanceId( TabPanel, 'tab-panel' ); const [ selected, setSelected ] = useState< string >(); - const handleTabSelection = ( tabKey: string ) => { - setSelected( tabKey ); - onSelect?.( tabKey ); - }; + const handleTabSelection = useCallback( + ( tabKey: string ) => { + setSelected( tabKey ); + onSelect?.( tabKey ); + }, + [ onSelect ] + ); const onNavigate = ( _childIndex: number, child: HTMLButtonElement ) => { child.click(); @@ -100,7 +103,7 @@ export function TabPanel( { if ( ! selectedTab?.name && tabs.length > 0 ) { handleTabSelection( initialTabName || tabs[ 0 ].name ); } - }, [ tabs, selectedTab?.name, initialTabName ] ); + }, [ tabs, selectedTab?.name, initialTabName, handleTabSelection ] ); return (
    From 8771f4d5602715e98c84a8d58b110b69b6d096e8 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Fri, 11 Nov 2022 15:41:43 +0400 Subject: [PATCH 53/66] Site Editor: Decode entities in template title and description (#45716) --- .../add-new-template/add-custom-template-modal.js | 6 +++++- .../components/header-edit-mode/document-actions/index.js | 3 ++- packages/edit-site/src/components/list/table.js | 2 +- .../src/components/sidebar-edit-mode/template-card/index.js | 5 +++-- packages/edit-site/src/components/template-details/index.js | 5 +++-- 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/edit-site/src/components/add-new-template/add-custom-template-modal.js b/packages/edit-site/src/components/add-new-template/add-custom-template-modal.js index e686e612405269..aece3595575f0f 100644 --- a/packages/edit-site/src/components/add-new-template/add-custom-template-modal.js +++ b/packages/edit-site/src/components/add-new-template/add-custom-template-modal.js @@ -17,6 +17,7 @@ import { } from '@wordpress/components'; import { useDebounce } from '@wordpress/compose'; import { useEntityRecords } from '@wordpress/core-data'; +import { decodeEntities } from '@wordpress/html-entities'; /** * Internal dependencies @@ -50,7 +51,10 @@ function SuggestionListItem( { } > - + { suggestion.link && ( diff --git a/packages/edit-site/src/components/header-edit-mode/document-actions/index.js b/packages/edit-site/src/components/header-edit-mode/document-actions/index.js index c822dda69560ff..2bf1ba13a36a1e 100644 --- a/packages/edit-site/src/components/header-edit-mode/document-actions/index.js +++ b/packages/edit-site/src/components/header-edit-mode/document-actions/index.js @@ -24,6 +24,7 @@ import { store as blockEditorStore } from '@wordpress/block-editor'; import { store as coreStore } from '@wordpress/core-data'; import { store as editorStore } from '@wordpress/editor'; import { store as preferencesStore } from '@wordpress/preferences'; +import { decodeEntities } from '@wordpress/html-entities'; /** * Internal dependencies @@ -147,7 +148,7 @@ export default function DocumentActions() { entityLabel ) } - { entityTitle } + { decodeEntities( entityTitle ) } - { template.description } + { decodeEntities( template.description ) } diff --git a/packages/edit-site/src/components/sidebar-edit-mode/template-card/index.js b/packages/edit-site/src/components/sidebar-edit-mode/template-card/index.js index eec45cba850adc..96333e3ab01f26 100644 --- a/packages/edit-site/src/components/sidebar-edit-mode/template-card/index.js +++ b/packages/edit-site/src/components/sidebar-edit-mode/template-card/index.js @@ -5,6 +5,7 @@ import { useSelect } from '@wordpress/data'; import { Icon } from '@wordpress/components'; import { store as editorStore } from '@wordpress/editor'; import { store as coreStore } from '@wordpress/core-data'; +import { decodeEntities } from '@wordpress/html-entities'; /** * Internal dependencies @@ -42,12 +43,12 @@ export default function TemplateCard() {

    - { title } + { decodeEntities( title ) }

    - { description } + { decodeEntities( description ) }
    diff --git a/packages/edit-site/src/components/template-details/index.js b/packages/edit-site/src/components/template-details/index.js index 7b9326deaab797..94dda53f75b22f 100644 --- a/packages/edit-site/src/components/template-details/index.js +++ b/packages/edit-site/src/components/template-details/index.js @@ -11,6 +11,7 @@ import { } from '@wordpress/components'; import { useDispatch, useSelect } from '@wordpress/data'; import { store as editorStore } from '@wordpress/editor'; +import { decodeEntities } from '@wordpress/html-entities'; /** * Internal dependencies @@ -65,7 +66,7 @@ export default function TemplateDetails( { template, onClose } ) { className="edit-site-template-details__title" as="p" > - { title } + { decodeEntities( title ) }
    ) } @@ -75,7 +76,7 @@ export default function TemplateDetails( { template, onClose } ) { className="edit-site-template-details__description" as="p" > - { description } + { decodeEntities( description ) } ) } From 8b6e1c50b5df4b71edb4f27e593006921b3c12d7 Mon Sep 17 00:00:00 2001 From: Andrea Fercia Date: Fri, 11 Nov 2022 15:58:59 +0100 Subject: [PATCH 54/66] Fix focus return when closing the Post publish panel (#45623) * Use setTimeout to fix the publish panel focus return. * Add tests. * Fix typo in the publishPost playwright utility. --- .../src/editor/publish-post.ts | 2 +- .../components/post-publish-button/index.js | 12 +++- .../editor/various/publish-panel.spec.js | 60 +++++++++++++++++++ 3 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 test/e2e/specs/editor/various/publish-panel.spec.js diff --git a/packages/e2e-test-utils-playwright/src/editor/publish-post.ts b/packages/e2e-test-utils-playwright/src/editor/publish-post.ts index 2760a8ff69cefb..552a0cef37233e 100644 --- a/packages/e2e-test-utils-playwright/src/editor/publish-post.ts +++ b/packages/e2e-test-utils-playwright/src/editor/publish-post.ts @@ -12,7 +12,7 @@ import type { Editor } from './index'; export async function publishPost( this: Editor ) { await this.page.click( 'role=button[name="Publish"i]' ); const publishEditorPanel = this.page.locator( - 'role=region[name="Publish editor"i]' + 'role=region[name="Editor publish"i]' ); const isPublishEditorVisible = await publishEditorPanel.isVisible(); diff --git a/packages/editor/src/components/post-publish-button/index.js b/packages/editor/src/components/post-publish-button/index.js index 86d026c8798b2a..73254e99e9556e 100644 --- a/packages/editor/src/components/post-publish-button/index.js +++ b/packages/editor/src/components/post-publish-button/index.js @@ -34,12 +34,22 @@ export class PostPublishButton extends Component { entitiesSavedStatesCallback: false, }; } + componentDidMount() { if ( this.props.focusOnMount ) { - this.buttonNode.current.focus(); + // This timeout is necessary to make sure the `useEffect` hook of + // `useFocusReturn` gets the correct element (the button that opens the + // PostPublishPanel) otherwise it will get this button. + this.timeoutID = setTimeout( () => { + this.buttonNode.current.focus(); + }, 0 ); } } + componentWillUnmount() { + clearTimeout( this.timeoutID ); + } + createOnClick( callback ) { return ( ...args ) => { const { hasNonPostEntityChanges, setEntitiesSavedStatesCallback } = diff --git a/test/e2e/specs/editor/various/publish-panel.spec.js b/test/e2e/specs/editor/various/publish-panel.spec.js new file mode 100644 index 00000000000000..1ea72d7eb11a22 --- /dev/null +++ b/test/e2e/specs/editor/various/publish-panel.spec.js @@ -0,0 +1,60 @@ +/** + * WordPress dependencies + */ +const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); + +test.describe( 'Post publish panel', () => { + test.beforeEach( async ( { admin } ) => { + await admin.createNewPost(); + } ); + + test( 'should move focus back to the Publish panel toggle button when canceling', async ( { + editor, + page, + } ) => { + // Add a paragraph block. + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: 'Dummy text' }, + } ); + + // Find and click the Publish panel toggle button. + const publishPanelToggleButton = page.locator( + 'role=region[name="Editor top bar"i] >> role=button[name="Publish"i]' + ); + await publishPanelToggleButton.click(); + + // Click the Cancel button. + await page.click( + 'role=region[name="Editor publish"i] >> role=button[name="Cancel"i]' + ); + + // Test focus is moved back to the Publish panel toggle button. + await expect( publishPanelToggleButton ).toBeFocused(); + } ); + + test( 'should move focus back to the Publish panel toggle button after publishing and closing the panel', async ( { + editor, + page, + } ) => { + // Insert a paragraph block. + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: 'Dummy text' }, + } ); + + await editor.publishPost(); + + // Close the publish panel. + await page.click( + 'role=region[name="Editor publish"i] >> role=button[name="Close panel"i]' + ); + + // Test focus is moved back to the Publish panel toggle button. + await expect( + page.locator( + 'role=region[name="Editor top bar"i] >> role=button[name="Update"i]' + ) + ).toBeFocused(); + } ); +} ); From 9e9d4d3148e00441ff8e93222fa23e0cb34a8132 Mon Sep 17 00:00:00 2001 From: JuanMa Date: Fri, 11 Nov 2022 11:24:35 -0700 Subject: [PATCH 55/66] [create-block] Reorganized sections to provide a better learning experience of this package (#45676) --- packages/create-block/README.md | 84 +++++++++++++++++++++++---------- 1 file changed, 59 insertions(+), 25 deletions(-) diff --git a/packages/create-block/README.md b/packages/create-block/README.md index 46be7b914450bc..d6e77b9576388b 100644 --- a/packages/create-block/README.md +++ b/packages/create-block/README.md @@ -1,18 +1,26 @@ # Create Block -Create Block is an officially supported tool for scaffolding WordPress plugins with blocks. It generates PHP, JS, CSS code, and everything you need to start the project. It integrates a modern build setup with no configuration. +Create Block is an **officially supported tool for scaffolding a WordPress plugin that registers a block**. It generates PHP, JS, CSS code, and everything you need to start the project. It also integrates a modern build setup with no configuration. -It is largely inspired by [create-react-app](https://create-react-app.dev/docs/getting-started). Major kudos to [@gaearon](https://github.com/gaearon), the whole Facebook team, and the React community. +_It is largely inspired by [create-react-app](https://create-react-app.dev/docs/getting-started). Major kudos to [@gaearon](https://github.com/gaearon), the whole Facebook team, and the React community._ -## Description +> **Blocks are the fundamental elements of modern WordPress sites**. Introduced in [WordPress 5.0](https://wordpress.org/news/2018/12/bebo/), they allow [page and post builder-like functionality](https://wordpress.org/gutenberg/) to every up-to-date WordPress website. -Blocks are the fundamental element of the WordPress block editor. They are the primary way in which plugins can register their functionality and extend the editor's capabilities. +> _Learn more about the [Block API at the Gutenberg HandBook](https://developer.wordpress.org/block-editor/developers/block-api/block-registration/)._ -Visit the [Gutenberg handbook](https://developer.wordpress.org/block-editor/developers/block-api/block-registration/) to learn more about Block API. +## Table of Contents + +- [Quick start](#quick-start) +- [Usage](#usage) + - [Interactive Mode](#interactive-mode) + - [`slug`](#slug) + - [`options`](#options) +- [Available Commands](#available-commands) +- [External Project Templates](#external-project-templates) +- [Contributing to this package](#contributing-to-this-package) -## Quick start -You only need to provide the `slug` – the target location for scaffolded plugin files and the internal block name. +## Quick start ```bash $ npx @wordpress/create-block todo-list @@ -20,15 +28,17 @@ $ cd todo-list $ npm start ``` +The `slug` provided (`todo-list` in the example) defines the folder name for the scaffolded plugin and the internal block name. The WordPress plugin generated must [be installed manually](https://wordpress.org/support/article/managing-plugins/#manual-plugin-installation). + + _(requires `node` version `14.0.0` or above, and `npm` version `6.14.4` or above)_ -It creates a WordPress plugin that you need to [install manually](https://wordpress.org/support/article/managing-plugins/#manual-plugin-installation). -[Watch a video introduction to create-block on Learn.wordpress.org](https://learn.wordpress.org/tutorial/using-the-create-block-tool/) +> [Watch a video introduction to create-block on Learn.wordpress.org](https://learn.wordpress.org/tutorial/using-the-create-block-tool/) ## Usage -The following command generates a project with PHP, JS, and CSS code for registering a block with a WordPress plugin. +The `create-block` command generates a project with PHP, JS, and CSS code for registering a block with a WordPress plugin. ```bash $ npx @wordpress/create-block [options] [slug] @@ -36,9 +46,28 @@ $ npx @wordpress/create-block [options] [slug] ![Demo](https://user-images.githubusercontent.com/699132/103872910-4de15f00-50cf-11eb-8c74-67ca91a8c1a4.gif) -`[slug]` is optional. When provided, it triggers the quick mode where it is used as the block slug used for its identification, the output location for scaffolded files, and the name of the WordPress plugin. The rest of the configuration is set to all default values unless overridden with some options listed below. +> The name for a block is a unique string that identifies a block. Block Names are structured as `namespace`/`slug`, where namespace is the name of your plugin or theme. + +> In most cases, we recommended pairing blocks with WordPress plugins rather than themes, because only using plugin ensures that all blocks still work when your theme changes. + +### Interactive Mode + +When no `slug` is provided, the script will run in interactive mode and will start prompting for the input required (`slug`, title, namespace...) to scaffold the project. + + +### `slug` + +The use of `slug` is optional. + +When provided it triggers the _quick mode_, where this `slug` is used: +- as the block slug (required for its identification) +- as the output location (folder name) for scaffolded files +- as the name of the WordPress plugin. + +The rest of the configuration is set to all default values unless overridden with some options listed below. + +### `options` -Options: ```bash -V, --version output the version number @@ -55,45 +84,50 @@ Options: --variant choose a block variant as defined by the template ``` -More examples: - -1. Interactive mode - without giving a project name, the script will run in interactive mode giving a chance to customize the important options before generating the files. - -```bash -$ npx @wordpress/create-block -``` +#### `--template` -2. External npm package – it is also possible to select an external npm package as a template. +This argument specifies an _external npm package_ as a template. ```bash $ npx @wordpress/create-block --template my-template-package ``` -3. Local template directory – it is also possible to pick a local directory as a template. +This argument also allows to pick a _local directory_ as a template. ```bash $ npx @wordpress/create-block --template ./path/to/template-directory ``` -4. Generating a dynamic block based on the built-in template. +#### `--variant` + +With this argument, `create-block` will generate a [dynamic block](https://developer.wordpress.org/block-editor/explanations/glossary/#dynamic-block) based on the built-in template. ```bash $ npx @wordpress/create-block --variant dynamic ``` -5. Help – you need to use `npx` to output usage information. +#### `--help` + +With this argument, the `create-block` package outputs usage information. ```bash $ npx @wordpress/create-block --help ``` -5. No plugin mode – it is also possible to scaffold only block files into the current directory. +#### `--no-plugin` + +With this argument, the `create-block` package runs in _No plugin mode_ which only scaffolds block files into the current directory. ```bash $ npx @wordpress/create-block --no-plugin ``` +#### `--wp-env` -When you scaffold a block, you must provide at least a `slug` name, the `namespace` which usually corresponds to either the `theme` or `plugin` name. In most cases, we recommended pairing blocks with WordPress plugins rather than themes, because only using plugin ensures that all blocks still work when your theme changes. +With this argument, the `create-block` package will add to the generated plugin the configuration and the script to run [`wp-env` package](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-env/) within the plugin. This will allow you to easily set up a local WordPress environment (via Docker) for building and testing the generated plugin. + +```bash +$ npx @wordpress/create-block --wp-env +``` ## Available commands in the scaffolded project From 7822ef818daeb267e8f675fda8d901eca376bc00 Mon Sep 17 00:00:00 2001 From: Matias Benedetto Date: Fri, 11 Nov 2022 15:43:33 -0300 Subject: [PATCH 56/66] Making size prop work for icon components using dash icon strings (#45593) * Making size work for icon components using dash icon strings * adding changelog * merging custom size styles with the style prop --- packages/components/CHANGELOG.md | 1 + packages/components/src/dashicon/index.js | 23 ++++++++++++++++++++-- packages/components/src/icon/index.tsx | 5 +++-- packages/components/src/icon/test/index.js | 10 ++++++++++ 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index c363ddaca6285a..5670b53ba2f8f3 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -9,6 +9,7 @@ ### Bug Fix - `Autocomplete`: Fix unexpected block insertion during IME composition ([#45510](https://github.com/WordPress/gutenberg/pull/45510)). +- `Icon`: Making size prop work for icon components using dash icon strings ([#45593](https://github.com/WordPress/gutenberg/pull/45593)) - `ToolsPanelItem`: Prevent unintended calls to onDeselect when parent panel is remounted and item is rendered via SlotFill ([#45673](https://github.com/WordPress/gutenberg/pull/45673)) - `ColorPicker`: Prevent all number fields from becoming "0" when one of them is an empty string ([#45649](https://github.com/WordPress/gutenberg/pull/45649)). diff --git a/packages/components/src/dashicon/index.js b/packages/components/src/dashicon/index.js index c57ec0255bf628..9fc5fddae5b316 100644 --- a/packages/components/src/dashicon/index.js +++ b/packages/components/src/dashicon/index.js @@ -3,6 +3,7 @@ * * @property {import('./types').IconKey} icon Icon name * @property {string} [className] Class name + * @property {number} [size] Size of the icon */ /** @typedef {import('react').ComponentPropsWithoutRef<'span'> & OwnProps} Props */ @@ -10,7 +11,8 @@ * @param {Props} props * @return {JSX.Element} Element */ -function Dashicon( { icon, className, ...extraProps } ) { + +function Dashicon( { icon, className, size = 20, style = {}, ...extraProps } ) { const iconClass = [ 'dashicon', 'dashicons', @@ -20,7 +22,24 @@ function Dashicon( { icon, className, ...extraProps } ) { .filter( Boolean ) .join( ' ' ); - return ; + // For retro-compatibility reasons (for example if people are overriding icon size with CSS), we add inline styles just if the size is different to the default + const sizeStyles = + // using `!=` to catch both 20 and "20" + // eslint-disable-next-line eqeqeq + 20 != size + ? { + fontSize: `${ size }px`, + width: `${ size }px`, + height: `${ size }px`, + } + : {}; + + const styles = { + ...sizeStyles, + ...style, + }; + + return ; } export default Dashicon; diff --git a/packages/components/src/icon/index.tsx b/packages/components/src/icon/index.tsx index 504a30444e16ed..15b41063fdb1fc 100644 --- a/packages/components/src/icon/index.tsx +++ b/packages/components/src/icon/index.tsx @@ -33,7 +33,7 @@ interface BaseProps< P > { /** * The size (width and height) of the icon. * - * @default 24 + * @default `20` when a Dashicon is rendered, `24` for all other icons. */ size?: number; } @@ -48,13 +48,14 @@ export type Props< P > = BaseProps< P > & AdditionalProps< IconType< P > >; function Icon< P >( { icon = null, - size = 24, + size = 'string' === typeof icon ? 20 : 24, ...additionalProps }: Props< P > ) { if ( 'string' === typeof icon ) { return ( ) } /> ); diff --git a/packages/components/src/icon/test/index.js b/packages/components/src/icon/test/index.js index 5fc897576db1c7..9faff9e74116df 100644 --- a/packages/components/src/icon/test/index.js +++ b/packages/components/src/icon/test/index.js @@ -32,6 +32,16 @@ describe( 'Icon', () => { ); } ); + it( 'renders a dashicon with custom size', () => { + render( + + ); + + expect( screen.getByTestId( testId ) ).toHaveStyle( 'width:10px' ); + expect( screen.getByTestId( testId ) ).toHaveStyle( 'height:10px' ); + expect( screen.getByTestId( testId ) ).toHaveStyle( 'font-size:10px' ); + } ); + it( 'renders a function', () => { render( } /> ); From c72ef2826cca400d73ff5b03eb1544377e6134ec Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Sat, 12 Nov 2022 12:47:20 +0900 Subject: [PATCH 57/66] FormTokenField: Fix duplicate input in IME composition (#45607) * FormTokenField: Fix duplicate input in IME composition * Update readme * Fix wrong event.key case value * Add composition session check Co-authored-by: Lena Morita * Add workaround for Mac Safari Co-authored-by: Lena Morita Co-authored-by: Lena Morita --- packages/components/CHANGELOG.md | 1 + .../components/src/form-token-field/index.tsx | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 5670b53ba2f8f3..89dfebc110c0ea 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -8,6 +8,7 @@ ### Bug Fix +- `FormTokenField`: Fix duplicate input in IME composition ([#45607](https://github.com/WordPress/gutenberg/pull/45607)). - `Autocomplete`: Fix unexpected block insertion during IME composition ([#45510](https://github.com/WordPress/gutenberg/pull/45510)). - `Icon`: Making size prop work for icon components using dash icon strings ([#45593](https://github.com/WordPress/gutenberg/pull/45593)) - `ToolsPanelItem`: Prevent unintended calls to onDeselect when parent panel is remounted and item is rendered via SlotFill ([#45673](https://github.com/WordPress/gutenberg/pull/45673)) diff --git a/packages/components/src/form-token-field/index.tsx b/packages/components/src/form-token-field/index.tsx index cca98fd5e6d827..a7c8e9d272f8e3 100644 --- a/packages/components/src/form-token-field/index.tsx +++ b/packages/components/src/form-token-field/index.tsx @@ -169,10 +169,18 @@ export function FormTokenField( props: FormTokenFieldProps ) { function onKeyDown( event: KeyboardEvent ) { let preventDefault = false; - if ( event.defaultPrevented ) { + if ( + event.defaultPrevented || + // Ignore keydowns from IMEs + event.nativeEvent.isComposing || + // Workaround for Mac Safari where the final Enter/Backspace of an IME composition + // is `isComposing=false`, even though it's technically still part of the composition. + // These can only be detected by keyCode. + event.keyCode === 229 + ) { return; } - switch ( event.code ) { + switch ( event.key ) { case 'Backspace': preventDefault = handleDeleteKey( deleteTokenBeforeInput ); break; @@ -213,9 +221,9 @@ export function FormTokenField( props: FormTokenFieldProps ) { function onKeyPress( event: KeyboardEvent ) { let preventDefault = false; - // TODO: replace to event.code; - switch ( event.charCode ) { - case 44: // Comma. + + switch ( event.key ) { + case ',': preventDefault = handleCommaKey(); break; default: From 92d28ca4096457135dc7f213a900ec19ced1c8b6 Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Sat, 12 Nov 2022 15:38:29 +0900 Subject: [PATCH 58/66] Autocomplete: Check key events more strictly in IME composition (#45626) * Autocomplete: Check key events more strictly in IME composition * Add workaround for Mac Safari * Add changelog --- packages/components/CHANGELOG.md | 1 + packages/components/src/autocomplete/index.js | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 89dfebc110c0ea..0ae877646abdab 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -9,6 +9,7 @@ ### Bug Fix - `FormTokenField`: Fix duplicate input in IME composition ([#45607](https://github.com/WordPress/gutenberg/pull/45607)). +- `Autocomplete`: Check key events more strictly in IME composition ([#45626](https://github.com/WordPress/gutenberg/pull/45626)). - `Autocomplete`: Fix unexpected block insertion during IME composition ([#45510](https://github.com/WordPress/gutenberg/pull/45510)). - `Icon`: Making size prop work for icon components using dash icon strings ([#45593](https://github.com/WordPress/gutenberg/pull/45593)) - `ToolsPanelItem`: Prevent unintended calls to onDeselect when parent panel is remounted and item is rendered via SlotFill ([#45673](https://github.com/WordPress/gutenberg/pull/45673)) diff --git a/packages/components/src/autocomplete/index.js b/packages/components/src/autocomplete/index.js index 862dc4225ecd1a..f96bd01750c01d 100644 --- a/packages/components/src/autocomplete/index.js +++ b/packages/components/src/autocomplete/index.js @@ -220,7 +220,16 @@ function useAutocomplete( { if ( filteredOptions.length === 0 ) { return; } - if ( event.defaultPrevented ) { + + if ( + event.defaultPrevented || + // Ignore keydowns from IMEs + event.isComposing || + // Workaround for Mac Safari where the final Enter/Backspace of an IME composition + // is `isComposing=false`, even though it's technically still part of the composition. + // These can only be detected by keyCode. + event.keyCode === 229 + ) { return; } switch ( event.key ) { From 4394e9b9e3370624d04df7bb318de5547fbbb3b6 Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Sun, 13 Nov 2022 21:20:13 +0900 Subject: [PATCH 59/66] LinkControl: Suppress errors on null values (#45742) --- .../block-editor/src/components/link-control/search-input.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/link-control/search-input.js b/packages/block-editor/src/components/link-control/search-input.js index 5d4e9328f283e6..cabe52f907469e 100644 --- a/packages/block-editor/src/components/link-control/search-input.js +++ b/packages/block-editor/src/components/link-control/search-input.js @@ -111,7 +111,7 @@ const LinkControlSearchInput = forwardRef( allowDirectEntry || ( suggestion && Object.keys( suggestion ).length >= 1 ) ) { - const { id, url, ...restLinkProps } = currentLink; + const { id, url, ...restLinkProps } = currentLink ?? {}; onSelect( // Some direct entries don't have types or IDs, and we still need to clear the previous ones. { ...restLinkProps, ...suggestion }, From f463286e0638763ff24ecdde320da7759418de86 Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Sun, 13 Nov 2022 22:50:59 +0100 Subject: [PATCH 60/66] Navigation Toggle unit test: unmount synchronously to cancel popover positioning (#45726) --- .../test/__snapshots__/index.js.snap | 41 ----------------- .../navigation-toggle/test/index.js | 44 +++++++++---------- 2 files changed, 20 insertions(+), 65 deletions(-) delete mode 100644 packages/edit-site/src/components/navigation-sidebar/navigation-toggle/test/__snapshots__/index.js.snap diff --git a/packages/edit-site/src/components/navigation-sidebar/navigation-toggle/test/__snapshots__/index.js.snap b/packages/edit-site/src/components/navigation-sidebar/navigation-toggle/test/__snapshots__/index.js.snap deleted file mode 100644 index f08a738cdcaedf..00000000000000 --- a/packages/edit-site/src/components/navigation-sidebar/navigation-toggle/test/__snapshots__/index.js.snap +++ /dev/null @@ -1,41 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`NavigationToggle when in full screen mode should display a default site icon if no user uploaded site icon exists 1`] = ` -
    -
    - -
    -
    -`; diff --git a/packages/edit-site/src/components/navigation-sidebar/navigation-toggle/test/index.js b/packages/edit-site/src/components/navigation-sidebar/navigation-toggle/test/index.js index 159a48b2d7989e..9888593a8eca92 100644 --- a/packages/edit-site/src/components/navigation-sidebar/navigation-toggle/test/index.js +++ b/packages/edit-site/src/components/navigation-sidebar/navigation-toggle/test/index.js @@ -15,55 +15,51 @@ import NavigationToggle from '..'; jest.mock( '@wordpress/data/src/components/use-select', () => { // This allows us to tweak the returned value on each test. - const mock = jest.fn(); - return mock; + return jest.fn(); } ); -jest.mock( '@wordpress/data/src/components/use-dispatch', () => ( { - useDispatch: () => ( { setNavigationPanelActiveMenu: jest.fn() } ), -} ) ); - -jest.mock( '@wordpress/core-data' ); describe( 'NavigationToggle', () => { describe( 'when in full screen mode', () => { it( 'should display a user uploaded site icon if it exists', () => { - useSelect.mockImplementation( ( cb ) => { - return cb( () => ( { - getCurrentTemplateNavigationPanelSubMenu: () => 'root', + useSelect.mockImplementation( ( cb ) => + cb( () => ( { getEntityRecord: () => ( { site_icon_url: 'https://fakeUrl.com', } ), isResolving: () => false, isNavigationOpened: () => false, - } ) ); - } ); + } ) ) + ); - render( ); + const { unmount } = render( ); const siteIcon = screen.getByAltText( 'Site Icon' ); - expect( siteIcon ).toBeVisible(); + + // Unmount the UI synchronously so that any async effects, like the on-mount focus + // that shows and positions a tooltip, are cancelled right away and never run. + unmount(); } ); it( 'should display a default site icon if no user uploaded site icon exists', () => { - useSelect.mockImplementation( ( cb ) => { - return cb( () => ( { - getCurrentTemplateNavigationPanelSubMenu: () => 'root', + useSelect.mockImplementation( ( cb ) => + cb( () => ( { getEntityRecord: () => ( { site_icon_url: '', } ), isResolving: () => false, isNavigationOpened: () => false, - } ) ); - } ); + } ) ) + ); - const { container } = render( ); + const { unmount } = render( ); - expect( - screen.queryByAltText( 'Site Icon' ) - ).not.toBeInTheDocument(); + const siteIcon = screen.queryByAltText( 'Site Icon' ); + expect( siteIcon ).not.toBeInTheDocument(); - expect( container ).toMatchSnapshot(); + // Unmount the UI synchronously so that any async effects, like the on-mount focus + // that shows and positions a tooltip, are cancelled right away and never run. + unmount(); } ); } ); } ); From cff6d70d6ff5a26e212958623dc3130569f95685 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Mon, 14 Nov 2022 02:53:57 +0100 Subject: [PATCH 61/66] ToolsPanel: fix exhaustive-deps hook warning (#45715) --- packages/components/CHANGELOG.md | 1 + packages/components/src/tools-panel/tools-panel-item/hook.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 0ae877646abdab..11bae78e235e2c 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -17,6 +17,7 @@ ### Internal +- `ToolsPanel`: Update to fix `exhaustive-deps` eslint rule ([#45715](https://github.com/WordPress/gutenberg/pull/45715)). - `PaletteEditListView`: Update to ignore `exhaustive-deps` eslint rule ([#45467](https://github.com/WordPress/gutenberg/pull/45467)). - `Popover`: Update to pass `exhaustive-deps` eslint rule ([#45656](https://github.com/WordPress/gutenberg/pull/45656)). - `Flex`: Update to pass `exhaustive-deps` eslint rule ([#45528](https://github.com/WordPress/gutenberg/pull/45528)). diff --git a/packages/components/src/tools-panel/tools-panel-item/hook.ts b/packages/components/src/tools-panel/tools-panel-item/hook.ts index 4e0db5b9881d3e..dcc6c6416e0ce9 100644 --- a/packages/components/src/tools-panel/tools-panel-item/hook.ts +++ b/packages/components/src/tools-panel/tools-panel-item/hook.ts @@ -130,6 +130,7 @@ export function useToolsPanelItem( }, [ hasMatchingPanel, isMenuItemChecked, + isRegistered, isResetting, isValueSet, wasMenuItemChecked, From 00b334f4f597d817279570322b74735daa778c11 Mon Sep 17 00:00:00 2001 From: Vicente Canales <1157901+vcanales@users.noreply.github.com> Date: Mon, 14 Nov 2022 04:21:29 -0600 Subject: [PATCH 62/66] Try generating random color palettes (#40988) * add color randomizer * add color randomizer to gutenberg expriments * move randomize colors button to colors gs panel --- lib/experimental/editor-settings.php | 18 ++++------ lib/experiments-page.php | 12 +++++++ package-lock.json | 8 +++++ packages/edit-site/package.json | 1 + .../src/components/global-styles/hooks.js | 36 +++++++++++++++++++ .../src/components/global-styles/palette.js | 16 ++++++++- 6 files changed, 78 insertions(+), 13 deletions(-) diff --git a/lib/experimental/editor-settings.php b/lib/experimental/editor-settings.php index 408c5f7aecfa30..b0c05544d6b199 100644 --- a/lib/experimental/editor-settings.php +++ b/lib/experimental/editor-settings.php @@ -73,25 +73,19 @@ function gutenberg_initialize_editor( $editor_name, $editor_script_handle, $sett } /** - * Sets a global JS variable used to trigger the availability of zoomed out view. + * Sets a global JS variable used to trigger the availability of each Gutenberg Experiment. */ -function gutenberg_enable_zoomed_out_view() { +function gutenberg_enable_experiments() { $gutenberg_experiments = get_option( 'gutenberg-experiments' ); if ( $gutenberg_experiments && array_key_exists( 'gutenberg-zoomed-out-view', $gutenberg_experiments ) ) { wp_add_inline_script( 'wp-block-editor', 'window.__experimentalEnableZoomedOutView = true', 'before' ); } -} - -add_action( 'admin_init', 'gutenberg_enable_zoomed_out_view' ); - -/** - * Sets a global JS variable used to trigger the availability of the Navigation List View experiment. - */ -function gutenberg_enable_off_canvas_navigation_editor() { - $gutenberg_experiments = get_option( 'gutenberg-experiments' ); + if ( $gutenberg_experiments && array_key_exists( 'gutenberg-color-randomizer', $gutenberg_experiments ) ) { + wp_add_inline_script( 'wp-block-editor', 'window.__experimentalEnableColorRandomizer = true', 'before' ); + } if ( $gutenberg_experiments && array_key_exists( 'gutenberg-off-canvas-navigation-editor', $gutenberg_experiments ) ) { wp_add_inline_script( 'wp-block-editor', 'window.__experimentalEnableOffCanvasNavigationEditor = true', 'before' ); } } -add_action( 'admin_init', 'gutenberg_enable_off_canvas_navigation_editor' ); +add_action( 'admin_init', 'gutenberg_enable_experiments' ); diff --git a/lib/experiments-page.php b/lib/experiments-page.php index aa5103f3bb7a11..309612664cb9c9 100644 --- a/lib/experiments-page.php +++ b/lib/experiments-page.php @@ -63,6 +63,18 @@ function gutenberg_initialize_experiments_settings() { 'id' => 'gutenberg-off-canvas-navigation-editor', ) ); + add_settings_field( + 'gutenberg-color-randomizer', + __( 'Color randomizer ', 'gutenberg' ), + 'gutenberg_display_experiment_field', + 'gutenberg-experiments', + 'gutenberg_experiments_section', + array( + 'label' => __( 'Test the Global Styles color randomizer; a utility that lets you mix the current color palette pseudo-randomly.', 'gutenberg' ), + 'id' => 'gutenberg-color-randomizer', + ) + ); + register_setting( 'gutenberg-experiments', 'gutenberg-experiments' diff --git a/package-lock.json b/package-lock.json index e00ad245d20c9f..ad772d88dd1256 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18057,11 +18057,19 @@ "@wordpress/url": "file:packages/url", "@wordpress/viewport": "file:packages/viewport", "classnames": "^2.3.1", + "colord": "^2.9.2", "downloadjs": "^1.4.7", "history": "^5.1.0", "lodash": "^4.17.21", "react-autosize-textarea": "^7.1.0", "rememo": "^4.0.0" + }, + "dependencies": { + "colord": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz", + "integrity": "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==" + } } }, "@wordpress/edit-widgets": { diff --git a/packages/edit-site/package.json b/packages/edit-site/package.json index 9c6388a947c2c7..13c859b42935b5 100644 --- a/packages/edit-site/package.json +++ b/packages/edit-site/package.json @@ -55,6 +55,7 @@ "@wordpress/url": "file:../url", "@wordpress/viewport": "file:../viewport", "classnames": "^2.3.1", + "colord": "^2.9.2", "downloadjs": "^1.4.7", "history": "^5.1.0", "lodash": "^4.17.21", diff --git a/packages/edit-site/src/components/global-styles/hooks.js b/packages/edit-site/src/components/global-styles/hooks.js index f6df419a46d008..2de36fbb60b728 100644 --- a/packages/edit-site/src/components/global-styles/hooks.js +++ b/packages/edit-site/src/components/global-styles/hooks.js @@ -2,6 +2,8 @@ * External dependencies */ import { get, set, isEqual } from 'lodash'; +import { colord, extend } from 'colord'; +import a11yPlugin from 'colord/plugins/a11y'; /** * WordPress dependencies @@ -20,6 +22,9 @@ import { import { getValueFromVariable, getPresetVariableFromValue } from './utils'; import { GlobalStylesContext } from './context'; +// Enable colord's a11y plugin. +extend( [ a11yPlugin ] ); + const EMPTY_CONFIG = { settings: {}, styles: {} }; export const useGlobalStylesReset = () => { @@ -323,3 +328,34 @@ export function useGradientsPerOrigin( name ) { return result; }, [ customGradients, themeGradients, defaultGradients ] ); } + +export function useColorRandomizer( name ) { + const [ themeColors, setThemeColors ] = useSetting( + 'color.palette.theme', + name + ); + + function randomizeColors() { + /* eslint-disable no-restricted-syntax */ + const randomRotationValue = Math.floor( Math.random() * 225 ); + /* eslint-enable no-restricted-syntax */ + + const newColors = themeColors.map( ( colorObject ) => { + const { color } = colorObject; + const newColor = colord( color ) + .rotate( randomRotationValue ) + .toHex(); + + return { + ...colorObject, + color: newColor, + }; + } ); + + setThemeColors( newColors ); + } + + return window.__experimentalEnableColorRandomizer + ? [ randomizeColors ] + : []; +} diff --git a/packages/edit-site/src/components/global-styles/palette.js b/packages/edit-site/src/components/global-styles/palette.js index d29a04638b275a..4d37b234eb9afe 100644 --- a/packages/edit-site/src/components/global-styles/palette.js +++ b/packages/edit-site/src/components/global-styles/palette.js @@ -8,8 +8,10 @@ import { __experimentalZStack as ZStack, __experimentalVStack as VStack, ColorIndicator, + Button, } from '@wordpress/components'; import { __, _n, sprintf } from '@wordpress/i18n'; +import { shuffle } from '@wordpress/icons'; import { useMemo } from '@wordpress/element'; /** @@ -17,7 +19,7 @@ import { useMemo } from '@wordpress/element'; */ import Subtitle from './subtitle'; import { NavigationButtonAsItem } from './navigation-button'; -import { useSetting } from './hooks'; +import { useColorRandomizer, useSetting } from './hooks'; import ColorIndicatorWrapper from './color-indicator-wrapper'; const EMPTY_COLORS = []; @@ -31,6 +33,9 @@ function Palette( { name } ) { 'color.defaultPalette', name ); + + const [ randomizeThemeColors ] = useColorRandomizer(); + const colors = useMemo( () => [ ...( customColors || EMPTY_COLORS ), @@ -82,6 +87,15 @@ function Palette( { name } ) { + { randomizeThemeColors && ( + + ) } ); } From 4fedac58679eb568588f269efebdf2860c71cc22 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Mon, 14 Nov 2022 11:20:18 +0000 Subject: [PATCH 63/66] Fix: Contrast checker does not updates properly. (#45686) --- packages/block-editor/src/hooks/color-panel.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/hooks/color-panel.js b/packages/block-editor/src/hooks/color-panel.js index b1bf5b8206a79b..d8b804c300eb04 100644 --- a/packages/block-editor/src/hooks/color-panel.js +++ b/packages/block-editor/src/hooks/color-panel.js @@ -29,7 +29,19 @@ export default function ColorPanel( { const definedColors = settings.filter( ( setting ) => setting?.colorValue ); useEffect( () => { - if ( ! enableContrastChecking || ! definedColors.length ) { + if ( ! enableContrastChecking ) { + return; + } + if ( ! definedColors.length ) { + if ( detectedBackgroundColor ) { + setDetectedBackgroundColor(); + } + if ( detectedColor ) { + setDetectedColor(); + } + if ( detectedLinkColor ) { + setDetectedColor(); + } return; } From c4b5770dc039afae01dedba670ce165a27a302b1 Mon Sep 17 00:00:00 2001 From: Ben Dwyer Date: Mon, 14 Nov 2022 06:45:25 -0700 Subject: [PATCH 64/66] Navigation Link: Add the URL field to the Navigation Link inspector controls (#45751) --- packages/block-library/src/navigation-link/edit.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/block-library/src/navigation-link/edit.js b/packages/block-library/src/navigation-link/edit.js index 1b1b530dcdcdfd..ccd0aae57bcbbf 100644 --- a/packages/block-library/src/navigation-link/edit.js +++ b/packages/block-library/src/navigation-link/edit.js @@ -716,6 +716,14 @@ export default function NavigationLinkEdit( { + { + setAttributes( { url: urlValue } ); + } } + label={ __( 'URL' ) } + autoComplete="off" + /> { From db22cc77acefd365f7b4fb7e05d418823b1541d8 Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Tue, 15 Nov 2022 00:22:48 +0900 Subject: [PATCH 65/66] Patterns: Add padding to the pattern explorer list (#45730) --- packages/block-editor/src/components/inserter/style.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/inserter/style.scss b/packages/block-editor/src/components/inserter/style.scss index 5aa9b688f25021..785b9a414a5d79 100644 --- a/packages/block-editor/src/components/inserter/style.scss +++ b/packages/block-editor/src/components/inserter/style.scss @@ -442,7 +442,8 @@ $block-inserter-tabs-height: 44px; } &__list { - margin-left: $sidebar-width - $grid-unit-40; + margin-left: $sidebar-width; + padding: $grid-unit-30 0 $grid-unit-40; } .block-editor-block-patterns-list { From 36b6a1996bfbac563167cd319aa454ebb9e316b2 Mon Sep 17 00:00:00 2001 From: Andrei Draganescu Date: Mon, 14 Nov 2022 17:54:03 +0200 Subject: [PATCH 66/66] Converts paragraphs to headings with keyboard shortcuts (#44681) * adds shortcuts for heading transforms to the paragraph block * Implements feedback on hook location, UX and usage. - Moves the functionality from the paragraph block to the block editor package - Implements the shortcuts for paragraph and heading allowing cycling between them - Refactors the code in a less repetitive shape - Adds access+0 as a way to convert headings to paragraphs - Implements cycling through heading levels via access+[1-6] - Registers shortcuts only once - Adds a new prop to RichText allowing for onKeyDown custom handlers - Removes the keyboard-shortcuts dependency from block libary Co-authored-by: Daniel Richards <677833+talldan@users.noreply.github.com> Co-authored-by: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Co-authored-by: Dave Smith <444434+getdave@users.noreply.github.com> * Refactors so that setting up the text level shortcuts is handled by the post editor. * removes the old hook * adds the text level keyboard shortcuts to the help modal of the post editor * Removes the hook based approach and inlines every shortcut definition and handler into edit post shortcut conf file. Co-authored-by: Daniel Richards <677833+talldan@users.noreply.github.com> * updated snapshot for keyboard help modal * Refactores the shortcut handler to be a bit quicker returning early for multiselection and avoiding an extra state select. Co-authored-by: Daniel Richards <677833+talldan@users.noreply.github.com> Co-authored-by: Daniel Richards <677833+talldan@users.noreply.github.com> Co-authored-by: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Co-authored-by: Dave Smith <444434+getdave@users.noreply.github.com> --- .../keyboard-shortcut-help-modal/config.js | 10 +++ .../test/__snapshots__/index.js.snap | 70 +++++++++++++++++++ .../components/keyboard-shortcuts/index.js | 67 ++++++++++++++++++ packages/keycodes/src/index.js | 2 +- 4 files changed, 148 insertions(+), 1 deletion(-) diff --git a/packages/edit-post/src/components/keyboard-shortcut-help-modal/config.js b/packages/edit-post/src/components/keyboard-shortcut-help-modal/config.js index 7e098a87be2cd9..fd11d14e08cde0 100644 --- a/packages/edit-post/src/components/keyboard-shortcut-help-modal/config.js +++ b/packages/edit-post/src/components/keyboard-shortcut-help-modal/config.js @@ -36,4 +36,14 @@ export const textFormattingShortcuts = [ keyCombination: { modifier: 'access', character: 'x' }, description: __( 'Make the selected text inline code.' ), }, + { + keyCombination: { modifier: 'access', character: '0' }, + description: __( 'Convert the current heading to a paragraph.' ), + }, + { + keyCombination: { modifier: 'access', character: '1-6' }, + description: __( + 'Convert the current paragraph or heading to a heading of level 1 to 6.' + ), + }, ]; diff --git a/packages/edit-post/src/components/keyboard-shortcut-help-modal/test/__snapshots__/index.js.snap b/packages/edit-post/src/components/keyboard-shortcut-help-modal/test/__snapshots__/index.js.snap index 7cc2c124e6fbbd..7b2ba852ad5aab 100644 --- a/packages/edit-post/src/components/keyboard-shortcut-help-modal/test/__snapshots__/index.js.snap +++ b/packages/edit-post/src/components/keyboard-shortcut-help-modal/test/__snapshots__/index.js.snap @@ -821,6 +821,76 @@ exports[`KeyboardShortcutHelpModal should match snapshot when the modal is activ
    +
  • +
    + Convert the current heading to a paragraph. +
    +
    + + + Shift + + + + + Alt + + + + + 0 + + +
    +
  • +
  • +
    + Convert the current paragraph or heading to a heading of level 1 to 6. +
    +
    + + + Shift + + + + + Alt + + + + + 1-6 + + +
    +
  • diff --git a/packages/edit-post/src/components/keyboard-shortcuts/index.js b/packages/edit-post/src/components/keyboard-shortcuts/index.js index df5205de1c94a7..d3d6569e398a45 100644 --- a/packages/edit-post/src/components/keyboard-shortcuts/index.js +++ b/packages/edit-post/src/components/keyboard-shortcuts/index.js @@ -12,6 +12,7 @@ import { store as editorStore } from '@wordpress/editor'; import { store as blockEditorStore } from '@wordpress/block-editor'; import { store as noticesStore } from '@wordpress/notices'; import { store as preferencesStore } from '@wordpress/preferences'; +import { createBlock } from '@wordpress/blocks'; /** * Internal dependencies @@ -53,6 +54,35 @@ function KeyboardShortcuts() { closeGeneralSidebar(); }; + const { replaceBlocks } = useDispatch( blockEditorStore ); + const { getBlockName, getSelectedBlockClientId, getBlockAttributes } = + useSelect( blockEditorStore ); + + const handleTextLevelShortcut = ( event, level ) => { + event.preventDefault(); + const destinationBlockName = + level === 0 ? 'core/paragraph' : 'core/heading'; + const currentClientId = getSelectedBlockClientId(); + if ( currentClientId === null ) { + return; + } + const blockName = getBlockName( currentClientId ); + if ( blockName !== 'core/paragraph' && blockName !== 'core/heading' ) { + return; + } + const currentAttributes = getBlockAttributes( currentClientId ); + const { content: currentContent, align: currentAlign } = + currentAttributes; + replaceBlocks( + currentClientId, + createBlock( destinationBlockName, { + level, + content: currentContent, + align: currentAlign, + } ) + ); + }; + useEffect( () => { registerShortcut( { name: 'core/edit-post/toggle-mode', @@ -149,6 +179,28 @@ function KeyboardShortcuts() { character: 'h', }, } ); + + registerShortcut( { + name: `core/block-editor/transform-heading-to-paragraph`, + category: 'block-library', + description: __( 'Transform heading to paragraph.' ), + keyCombination: { + modifier: 'access', + character: `0`, + }, + } ); + + [ 1, 2, 3, 4, 5, 6 ].forEach( ( level ) => { + registerShortcut( { + name: `core/block-editor/transform-paragraph-to-heading-${ level }`, + category: 'block-library', + description: __( 'Transform paragraph to heading.' ), + keyCombination: { + modifier: 'access', + character: `${ level }`, + }, + } ); + } ); }, [] ); useShortcut( @@ -202,6 +254,21 @@ function KeyboardShortcuts() { setIsListViewOpened( ! isListViewOpened() ) ); + useShortcut( + 'core/block-editor/transform-heading-to-paragraph', + ( event ) => handleTextLevelShortcut( event, 0 ) + ); + + [ 1, 2, 3, 4, 5, 6 ].forEach( ( level ) => { + //the loop is based off on a constant therefore + //the hook will execute the same way every time + //eslint-disable-next-line react-hooks/rules-of-hooks + useShortcut( + `core/block-editor/transform-paragraph-to-heading-${ level }`, + ( event ) => handleTextLevelShortcut( event, level ) + ); + } ); + return null; } diff --git a/packages/keycodes/src/index.js b/packages/keycodes/src/index.js index 4d902f4ba4e278..5bb776127d95e7 100644 --- a/packages/keycodes/src/index.js +++ b/packages/keycodes/src/index.js @@ -237,7 +237,7 @@ export const displayShortcutList = mapValues( modifiers, ( modifier ) => { // so override the rule to allow symbols used for shortcuts. // see: https://github.com/blakeembrey/change-case#options const capitalizedCharacter = capitalCase( character, { - stripRegexp: /[^A-Z0-9`,\.\\]/gi, + stripRegexp: /[^A-Z0-9`,\.\\\-]/gi, } ); return [ ...modifierKeys, capitalizedCharacter ];