From afbb659ed61a07e0b8e98cdd57e64363d1d62910 Mon Sep 17 00:00:00 2001 From: Dani Guardiola Date: Tue, 17 Sep 2024 13:48:05 +0200 Subject: [PATCH 01/49] Tabs: fix vertical indicator (#65385) * Fix vertical indicator * Add changelog entry Co-authored-by: DaniGuardiola Co-authored-by: tyxla --- packages/components/CHANGELOG.md | 1 + packages/components/src/tabs/styles.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 1473cf5f5df40e..2f9f0fd03458ec 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -4,6 +4,7 @@ ### Bug Fixes +- `Tabs`: restore vertical indicator ([#65385](https://github.com/WordPress/gutenberg/pull/65385)). - `Tabs`: indicator positioning under RTL direction ([#64926](https://github.com/WordPress/gutenberg/pull/64926)). - `Popover`: Update `toolbar` variant radius to match block toolbar ([#65263](https://github.com/WordPress/gutenberg/pull/65263)). diff --git a/packages/components/src/tabs/styles.ts b/packages/components/src/tabs/styles.ts index a6425f9ab31e45..c00943b180f637 100644 --- a/packages/components/src/tabs/styles.ts +++ b/packages/components/src/tabs/styles.ts @@ -76,7 +76,7 @@ export const TabListWrapper = styled.div` top: 0; left: 0; width: 100%; - width: calc( var( --antialiasing-factor ) * 1px ); + height: calc( var( --antialiasing-factor ) * 1px ); transform: translateY( calc( var( --indicator-top ) * 1px ) ) scaleY( calc( var( --indicator-height ) / var( --antialiasing-factor ) ) From 99fefd79c2644f65fb885fad82e0c11e7593eca0 Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Tue, 17 Sep 2024 20:59:21 +0900 Subject: [PATCH 02/49] Don't force iframe editor when gutenberg plugin and block theme are enabled (#65372) Co-authored-by: t-hamano Co-authored-by: fabiankaegy Co-authored-by: stokesman --- .../components/layout/use-should-iframe.js | 43 +++++++------------ 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/packages/edit-post/src/components/layout/use-should-iframe.js b/packages/edit-post/src/components/layout/use-should-iframe.js index e36a4773c4a1fd..9fed30c07b37e3 100644 --- a/packages/edit-post/src/components/layout/use-should-iframe.js +++ b/packages/edit-post/src/components/layout/use-should-iframe.js @@ -6,33 +6,22 @@ import { useSelect } from '@wordpress/data'; import { store as blocksStore } from '@wordpress/blocks'; import { store as blockEditorStore } from '@wordpress/block-editor'; -const isGutenbergPlugin = globalThis.IS_GUTENBERG_PLUGIN ? true : false; - export function useShouldIframe() { - const { - isBlockBasedTheme, - hasV3BlocksOnly, - isEditingTemplate, - isZoomOutMode, - } = useSelect( ( select ) => { - const { getEditorSettings, getCurrentPostType } = select( editorStore ); - const { __unstableGetEditorMode } = select( blockEditorStore ); - const { getBlockTypes } = select( blocksStore ); - const editorSettings = getEditorSettings(); - return { - isBlockBasedTheme: editorSettings.__unstableIsBlockBasedTheme, - hasV3BlocksOnly: getBlockTypes().every( ( type ) => { - return type.apiVersion >= 3; - } ), - isEditingTemplate: getCurrentPostType() === 'wp_template', - isZoomOutMode: __unstableGetEditorMode() === 'zoom-out', - }; - }, [] ); - - return ( - hasV3BlocksOnly || - ( isGutenbergPlugin && isBlockBasedTheme ) || - isEditingTemplate || - isZoomOutMode + const { hasV3BlocksOnly, isEditingTemplate, isZoomOutMode } = useSelect( + ( select ) => { + const { getCurrentPostType } = select( editorStore ); + const { __unstableGetEditorMode } = select( blockEditorStore ); + const { getBlockTypes } = select( blocksStore ); + return { + hasV3BlocksOnly: getBlockTypes().every( ( type ) => { + return type.apiVersion >= 3; + } ), + isEditingTemplate: getCurrentPostType() === 'wp_template', + isZoomOutMode: __unstableGetEditorMode() === 'zoom-out', + }; + }, + [] ); + + return hasV3BlocksOnly || isEditingTemplate || isZoomOutMode; } From be8f9b62dbab28974f84c438a27d26880abb5e7a Mon Sep 17 00:00:00 2001 From: Luis Felipe Zaguini <26530524+zaguiini@users.noreply.github.com> Date: Tue, 17 Sep 2024 09:17:05 -0300 Subject: [PATCH 03/49] Combobox Control: Add placeholder attribute (#65254) * Combobox Control: Add placeholder attribute * Remove placeholder from default story * Change placeholder type description and add it to README * Add CHANGELOG entry --- packages/components/CHANGELOG.md | 1 + packages/components/src/combobox-control/README.md | 7 +++++++ packages/components/src/combobox-control/index.tsx | 2 ++ packages/components/src/combobox-control/types.ts | 4 ++++ 4 files changed, 14 insertions(+) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 2f9f0fd03458ec..8d9ee39833577b 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -15,6 +15,7 @@ ### New Features - `Composite`: add stable version of the component ([#63569](https://github.com/WordPress/gutenberg/pull/63569)). +- `ComboboxControl`: add support for `placeholder` attribute ([#65254](https://github.com/WordPress/gutenberg/pull/65254)). ### Enhancements diff --git a/packages/components/src/combobox-control/README.md b/packages/components/src/combobox-control/README.md index 61611f82fee233..5831c5ec2832c2 100644 --- a/packages/components/src/combobox-control/README.md +++ b/packages/components/src/combobox-control/README.md @@ -111,6 +111,13 @@ If the control is clicked, the dropdown will expand regardless of this prop. - Required: No - Default: `true` +### placeholder + +If passed, the combobox input will show a placeholder string if no values are present. + +- Type: `string` +- Required: No + #### __experimentalRenderItem Custom renderer invoked for each option in the suggestion list. The render prop receives as its argument an object containing, under the `item` key, the single option's data (directly from the array of data passed to the `options` prop). diff --git a/packages/components/src/combobox-control/index.tsx b/packages/components/src/combobox-control/index.tsx index fc3ecccf0b6599..5c6725a51b435d 100644 --- a/packages/components/src/combobox-control/index.tsx +++ b/packages/components/src/combobox-control/index.tsx @@ -129,6 +129,7 @@ function ComboboxControl( props: ComboboxControlProps ) { }, __experimentalRenderItem, expandOnFocus = true, + placeholder, } = useDeprecated36pxDefaultSizeProp( props ); const [ value, setValue ] = useControlledValue( { @@ -340,6 +341,7 @@ function ComboboxControl( props: ComboboxControlProps ) { className="components-combobox-control__input" instanceId={ instanceId } ref={ inputContainer } + placeholder={ placeholder } value={ isExpanded ? inputValue : currentLabel } onFocus={ onFocus } onBlur={ onBlur } diff --git a/packages/components/src/combobox-control/types.ts b/packages/components/src/combobox-control/types.ts index 10adf8c0ff0ef6..470b38c35e2c8a 100644 --- a/packages/components/src/combobox-control/types.ts +++ b/packages/components/src/combobox-control/types.ts @@ -82,4 +82,8 @@ export type ComboboxControlProps = Pick< * The current value of the control. */ value?: string | null; + /** + * If passed, the combobox input will show a placeholder string if no values are present. + */ + placeholder?: string; }; From 05660fe9b2abf4e0f1cd74e9821cb7742772e327 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Tue, 17 Sep 2024 15:33:11 +0200 Subject: [PATCH 04/49] a11y: Print script module live regions in page HTML (#65380) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix issues where @wordpress/a11y script module `speak` may not be detected and announced by accessibility software, in particular when dynamically loading the module. Accessibility software needs to detect the live regions in order to announce them. The `@wordpress/a11y` script module was adding the live regions to the DOM correctly when the module was loaded, but when loaded dynamically (via `import( '@wordpress/a11y' ).then( /*…*/ )` ) the live region changes would happen before the live region was detected by accessibility software and the announcements would not happen. The `@wordpress/a11y` script module is updated so that it does not inject the live regions into the DOM but expects them to be present on the page. A hook is added to inject the necessary HTML for the live regions when the @wordpress/a11y script module may be present (enqueued or in the import map). This has a few additional advantages: - Accessibility software correctly detects the live regions. - The DOM does not need to be modified immediately on page load. - The (gzipped) size of the script module is reduced by 416B (~46%). The `wp-a11y` script shares the same DOM elements. It includes protection to prevent injecting duplicate elements. --- Co-authored-by: sirreal Co-authored-by: gziolo Co-authored-by: afercia --- lib/experimental/script-modules.php | 49 ++++++++++++++++--- packages/a11y/src/index.js | 25 ++++++++-- packages/a11y/src/module/index.ts | 22 ++------- .../src/{shared => script}/add-container.js | 0 .../src/{shared => script}/add-intro-text.ts | 10 ++-- .../test/add-container.test.js | 0 packages/a11y/src/shared/index.js | 28 ----------- 7 files changed, 75 insertions(+), 59 deletions(-) rename packages/a11y/src/{shared => script}/add-container.js (100%) rename packages/a11y/src/{shared => script}/add-intro-text.ts (83%) rename packages/a11y/src/{shared => script}/test/add-container.test.js (100%) diff --git a/lib/experimental/script-modules.php b/lib/experimental/script-modules.php index a113df02b9d758..f65bc1704dd890 100644 --- a/lib/experimental/script-modules.php +++ b/lib/experimental/script-modules.php @@ -201,6 +201,45 @@ function gutenberg_dequeue_module( $module_identifier ) { wp_script_modules()->dequeue( $module_identifier ); } +/** + * Prints HTML for the a11y Script Module. + * + * a11y relies on some DOM elements to use as ARIA live regions. + * Ideally, these elements are part of the initial HTML of the page + * so that accessibility tools can find them and observe updates. + */ +function gutenberg_a11y_script_module_html() { + $a11y_module_available = false; + + $get_marked_for_enqueue = new ReflectionMethod( 'WP_Script_Modules', 'get_marked_for_enqueue' ); + $get_marked_for_enqueue->setAccessible( true ); + $get_import_map = new ReflectionMethod( 'WP_Script_Modules', 'get_import_map' ); + $get_import_map->setAccessible( true ); + + foreach ( array_keys( $get_marked_for_enqueue->invoke( wp_script_modules() ) ) as $id ) { + if ( '@wordpress/a11y' === $id ) { + $a11y_module_available = true; + break; + } + } + if ( ! $a11y_module_available ) { + foreach ( array_keys( $get_import_map->invoke( wp_script_modules() )['imports'] ) as $id ) { + if ( '@wordpress/a11y' === $id ) { + $a11y_module_available = true; + break; + } + } + } + if ( ! $a11y_module_available ) { + return; + } + echo '
' + . '' + . '
' + . '
' + . '
'; +} + /** * Registers Gutenberg Script Modules. * @@ -218,12 +257,8 @@ function gutenberg_register_script_modules() { array(), $default_version ); - add_filter( - 'script_module_data_@wordpress/a11y', - function ( $data ) { - $data['i18n'] = array( 'Notifications' => __( 'Notifications', 'default' ) ); - return $data; - } - ); + + add_action( 'wp_footer', 'gutenberg_a11y_script_module_html' ); + add_action( 'admin_footer', 'gutenberg_a11y_script_module_html' ); } add_action( 'init', 'gutenberg_register_script_modules' ); diff --git a/packages/a11y/src/index.js b/packages/a11y/src/index.js index 59e93da780bd81..cdc3be10545155 100644 --- a/packages/a11y/src/index.js +++ b/packages/a11y/src/index.js @@ -2,18 +2,37 @@ * WordPress dependencies */ import domReady from '@wordpress/dom-ready'; -import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import { makeSetupFunction } from './shared/index'; +import addContainer from './script/add-container'; +import addIntroText from './script/add-intro-text'; + export { speak } from './shared/index'; /** * Create the live regions. */ -export const setup = makeSetupFunction( __( 'Notifications' ) ); +export function setup() { + const introText = document.getElementById( 'a11y-speak-intro-text' ); + const containerAssertive = document.getElementById( + 'a11y-speak-assertive' + ); + const containerPolite = document.getElementById( 'a11y-speak-polite' ); + + if ( introText === null ) { + addIntroText(); + } + + if ( containerAssertive === null ) { + addContainer( 'assertive' ); + } + + if ( containerPolite === null ) { + addContainer( 'polite' ); + } +} /** * Run setup on domReady. diff --git a/packages/a11y/src/module/index.ts b/packages/a11y/src/module/index.ts index a2c87f397f4875..a06882f068dd3a 100644 --- a/packages/a11y/src/module/index.ts +++ b/packages/a11y/src/module/index.ts @@ -1,25 +1,11 @@ /** * Internal dependencies */ -import { makeSetupFunction } from '../shared/index'; export { speak } from '../shared/index'; -// Without an i18n Script Module, "Notifications" (the only localized text used in this module) -// will be translated on the server and provided as script-module data. -let notificationsText = 'Notifications'; -try { - const textContent = document.getElementById( - 'wp-script-module-data-@wordpress/a11y' - )?.textContent; - if ( textContent ) { - const parsed = JSON.parse( textContent ); - notificationsText = parsed?.i18n?.Notifications ?? notificationsText; - } -} catch {} - /** - * Create the live regions. + * This no-op function is exported to provide compatibility with the `wp-a11y` Script. + * + * Filters should inject the relevant HTML on page load instead of requiring setup. */ -export const setup = makeSetupFunction( notificationsText ); - -setup(); +export const setup = () => {}; diff --git a/packages/a11y/src/shared/add-container.js b/packages/a11y/src/script/add-container.js similarity index 100% rename from packages/a11y/src/shared/add-container.js rename to packages/a11y/src/script/add-container.js diff --git a/packages/a11y/src/shared/add-intro-text.ts b/packages/a11y/src/script/add-intro-text.ts similarity index 83% rename from packages/a11y/src/shared/add-intro-text.ts rename to packages/a11y/src/script/add-intro-text.ts index 6bd97c887664d3..2bcf453ec44c8e 100644 --- a/packages/a11y/src/shared/add-intro-text.ts +++ b/packages/a11y/src/script/add-intro-text.ts @@ -1,18 +1,22 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + /** * Build the explanatory text to be placed before the aria live regions. * * This text is initially hidden from assistive technologies by using a `hidden` * HTML attribute which is then removed once a message fills the aria-live regions. * - * @param {string} introTextContent The translated intro text content. * @return {HTMLParagraphElement} The explanatory text HTML element. */ -export default function addIntroText( introTextContent: string ) { +export default function addIntroText() { const introText = document.createElement( 'p' ); introText.id = 'a11y-speak-intro-text'; introText.className = 'a11y-speak-intro-text'; - introText.textContent = introTextContent; + introText.textContent = __( 'Notifications' ); introText.setAttribute( 'style', diff --git a/packages/a11y/src/shared/test/add-container.test.js b/packages/a11y/src/script/test/add-container.test.js similarity index 100% rename from packages/a11y/src/shared/test/add-container.test.js rename to packages/a11y/src/script/test/add-container.test.js diff --git a/packages/a11y/src/shared/index.js b/packages/a11y/src/shared/index.js index a05f891f428561..2b6353720d0add 100644 --- a/packages/a11y/src/shared/index.js +++ b/packages/a11y/src/shared/index.js @@ -1,37 +1,9 @@ /** * Internal dependencies */ -import addContainer from './add-container'; -import addIntroText from './add-intro-text'; import clear from './clear'; import filterMessage from './filter-message'; -/** - * Create the live regions. - * @param {string} introTextContent The intro text content. - */ -export function makeSetupFunction( introTextContent ) { - return function setup() { - const introText = document.getElementById( 'a11y-speak-intro-text' ); - const containerAssertive = document.getElementById( - 'a11y-speak-assertive' - ); - const containerPolite = document.getElementById( 'a11y-speak-polite' ); - - if ( introText === null ) { - addIntroText( introTextContent ); - } - - if ( containerAssertive === null ) { - addContainer( 'assertive' ); - } - - if ( containerPolite === null ) { - addContainer( 'polite' ); - } - }; -} - /** * Allows you to easily announce dynamic interface updates to screen readers using ARIA live regions. * This module is inspired by the `speak` function in `wp-a11y.js`. From 59595f6d0f44d7cce27c20df8f8b58a72f021afa Mon Sep 17 00:00:00 2001 From: Andrea Fercia Date: Tue, 17 Sep 2024 15:54:27 +0200 Subject: [PATCH 05/49] Fix the shadows Range control accessibility and usability (#63908) * Use real labels instaed of headings or aria-labels. * Simplify by removing RangeControl. * Remove unnecessary HStack and className. * Remove no longer used hasNegativeRange prop. Co-authored-by: afercia Co-authored-by: t-hamano Co-authored-by: jameskoster Co-authored-by: richtabor --- .../global-styles/shadows-edit-panel.js | 69 ++----------------- 1 file changed, 7 insertions(+), 62 deletions(-) diff --git a/packages/edit-site/src/components/global-styles/shadows-edit-panel.js b/packages/edit-site/src/components/global-styles/shadows-edit-panel.js index cf49d8c28ebcb9..ec1dd1a900c3bf 100644 --- a/packages/edit-site/src/components/global-styles/shadows-edit-panel.js +++ b/packages/edit-site/src/components/global-styles/shadows-edit-panel.js @@ -13,7 +13,6 @@ import { __experimentalItemGroup as ItemGroup, __experimentalInputControl as InputControl, __experimentalUnitControl as UnitControl, - __experimentalParseQuantityAndUnitFromRawValue as parseQuantityAndUnitFromRawValue, __experimentalGrid as Grid, __experimentalDropdownContentWrapper as DropdownContentWrapper, __experimentalUseNavigator as useNavigator, @@ -21,7 +20,6 @@ import { __experimentalToggleGroupControlOption as ToggleGroupControlOption, __experimentalConfirmDialog as ConfirmDialog, Dropdown, - RangeControl, Button, Flex, FlexItem, @@ -35,7 +33,6 @@ import { plus, shadow as shadowIcon, reset, - settings, moreVertical, } from '@wordpress/icons'; import { useState, useMemo } from '@wordpress/element'; @@ -51,7 +48,6 @@ import { getShadowParts, shadowStringToObject, shadowObjectToString, - CUSTOM_VALUE_SETTINGS, } from './shadow-utils'; const { useGlobalSetting } = unlock( blockEditorPrivateApis ); @@ -464,13 +460,11 @@ function ShadowPopover( { shadowObj, onChange } ) { onShadowChange( 'x', value ) } /> onShadowChange( 'y', value ) } /> onShadowChange( 'spread', value ) } /> @@ -489,16 +482,7 @@ function ShadowPopover( { shadowObj, onChange } ) { ); } -function ShadowInputControl( { label, value, onChange, hasNegativeRange } ) { - const [ isCustomInput, setIsCustomInput ] = useState( false ); - const [ parsedQuantity, parsedUnit ] = - parseQuantityAndUnitFromRawValue( value ); - - const sliderOnChange = ( next ) => { - onChange( - next !== undefined ? [ next, parsedUnit || 'px' ].join( '' ) : '0px' - ); - }; +function ShadowInputControl( { label, value, onChange } ) { const onValueChange = ( next ) => { const isNumeric = next !== undefined && ! isNaN( parseFloat( next ) ); const nextValue = isNumeric ? next : '0px'; @@ -506,50 +490,11 @@ function ShadowInputControl( { label, value, onChange, hasNegativeRange } ) { }; return ( - - - { label } -