diff --git a/packages/block-editor/src/components/color-palette/test/__snapshots__/control.js.snap b/packages/block-editor/src/components/color-palette/test/__snapshots__/control.js.snap index 3d082a14a92bff..eb665ced62079f 100644 --- a/packages/block-editor/src/components/color-palette/test/__snapshots__/control.js.snap +++ b/packages/block-editor/src/components/color-palette/test/__snapshots__/control.js.snap @@ -203,7 +203,7 @@ exports[`ColorPaletteControl matches the snapshot 1`] = ` class="components-circular-option-picker" >
diff --git a/packages/block-library/src/cover/edit/index.js b/packages/block-library/src/cover/edit/index.js index 1eafe99e283eb4..b09093e312211d 100644 --- a/packages/block-library/src/cover/edit/index.js +++ b/packages/block-library/src/cover/edit/index.js @@ -514,6 +514,8 @@ function CoverEdit( { value={ overlayColor.color } onChange={ onSetOverlayColor } clearable={ false } + asButtons + aria-label={ __( 'Overlay color' ) } />
diff --git a/packages/block-library/src/cover/test/edit.js b/packages/block-library/src/cover/test/edit.js index 0a18d2cf3f9f8e..16695f53f67466 100644 --- a/packages/block-library/src/cover/test/edit.js +++ b/packages/block-library/src/cover/test/edit.js @@ -47,7 +47,7 @@ async function setup( attributes, useCoreBlocks, customSettings ) { async function createAndSelectBlock() { await userEvent.click( - screen.getByRole( 'option', { + screen.getByRole( 'button', { name: 'Black', } ) ); @@ -72,7 +72,7 @@ describe( 'Cover block', () => { test( 'can set overlay color using color picker on block placeholder', async () => { const { container } = await setup(); - const colorPicker = screen.getByRole( 'option', { + const colorPicker = screen.getByRole( 'button', { name: 'Black', } ); await userEvent.click( colorPicker ); @@ -96,7 +96,7 @@ describe( 'Cover block', () => { await setup(); await userEvent.click( - screen.getByRole( 'option', { + screen.getByRole( 'button', { name: 'Black', } ) ); @@ -389,7 +389,7 @@ describe( 'Cover block', () => { describe( 'isDark settings', () => { test( 'should toggle is-light class if background changed from light to dark', async () => { await setup(); - const colorPicker = screen.getByRole( 'option', { + const colorPicker = screen.getByRole( 'button', { name: 'White', } ); await userEvent.click( colorPicker ); @@ -413,7 +413,7 @@ describe( 'Cover block', () => { } ); test( 'should remove is-light class if overlay color is removed', async () => { await setup(); - const colorPicker = screen.getByRole( 'option', { + const colorPicker = screen.getByRole( 'button', { name: 'White', } ); await userEvent.click( colorPicker ); @@ -426,7 +426,7 @@ describe( 'Cover block', () => { } ) ); await userEvent.click( screen.getByText( 'Overlay' ) ); - // The default color is black, so clicking the black color option will remove the background color, + // The default color is black, so clicking the black color button will remove the background color, // which should remove the isDark setting and assign the is-light class. const popupColorPicker = screen.getByRole( 'option', { name: 'White', diff --git a/packages/components/src/border-box-control/test/index.tsx b/packages/components/src/border-box-control/test/index.tsx index fb536656453f4d..74501f7fccc7ac 100644 --- a/packages/components/src/border-box-control/test/index.tsx +++ b/packages/components/src/border-box-control/test/index.tsx @@ -202,7 +202,7 @@ describe( 'BorderBoxControl', () => { await waitFor( () => expect( screen.getByRole( 'button', { - name: 'Custom color picker.', + name: 'Custom color picker', } ) ).toBeVisible() ); diff --git a/packages/components/src/border-control/test/index.js b/packages/components/src/border-control/test/index.js index ff9007be28f1a2..c3e3987ed13517 100644 --- a/packages/components/src/border-control/test/index.js +++ b/packages/components/src/border-control/test/index.js @@ -138,7 +138,7 @@ describe( 'BorderControl', () => { const customColorPicker = getButton( /Custom color picker/ ); const circularOptionPicker = screen.getByRole( 'listbox', { - name: 'Custom color picker.', + name: 'Custom color picker', } ); const colorSwatchButtons = within( circularOptionPicker ).getAllByRole( 'option' ); diff --git a/packages/components/src/circular-option-picker/README.md b/packages/components/src/circular-option-picker/README.md index b6db6f06daf456..8a4d5ac3cf5ca3 100644 --- a/packages/components/src/circular-option-picker/README.md +++ b/packages/components/src/circular-option-picker/README.md @@ -93,6 +93,19 @@ Prevents keyboard interaction from wrapping around. Only used when `asButtons` i - Required: No - Default: `true` +### `aria-labelledby`: `string` + +The ID reference list of one or more elements that label the wrapper element. + +- Required: No + +### `aria-label`: `string` + +The label for the wrapper element. Not used if an 'aria-labelledby' is provided. + +- Required: No +- Default: `Custom color picker` + ## Subcomponents ### `CircularOptionPicker.ButtonAction` diff --git a/packages/components/src/circular-option-picker/circular-option-picker.tsx b/packages/components/src/circular-option-picker/circular-option-picker.tsx index 8b6be8cd2215f0..c4309ecf4dda3d 100644 --- a/packages/components/src/circular-option-picker/circular-option-picker.tsx +++ b/packages/components/src/circular-option-picker/circular-option-picker.tsx @@ -132,7 +132,7 @@ function ButtonsCircularOptionPicker( ); return ( -
+
{ options } { children } diff --git a/packages/components/src/circular-option-picker/index.tsx b/packages/components/src/circular-option-picker/index.tsx index ef975c21ee6545..ef379994b476f5 100644 --- a/packages/components/src/circular-option-picker/index.tsx +++ b/packages/components/src/circular-option-picker/index.tsx @@ -9,5 +9,6 @@ export { ButtonAction, DropdownLinkAction, } from './circular-option-picker-actions'; +export { getComputeCircularOptionPickerCommonProps } from './utils'; export default CircularOptionPicker; diff --git a/packages/components/src/circular-option-picker/stories/index.story.tsx b/packages/components/src/circular-option-picker/stories/index.story.tsx index 9d45c9bb92f7d0..6b564929fd8eb9 100644 --- a/packages/components/src/circular-option-picker/stories/index.story.tsx +++ b/packages/components/src/circular-option-picker/stories/index.story.tsx @@ -131,7 +131,7 @@ WithLoopingDisabled.parameters = { docs: { source: { code: `} />`, diff --git a/packages/components/src/circular-option-picker/test/index.tsx b/packages/components/src/circular-option-picker/test/index.tsx index a6e9f2c45a05ce..7d58ed3920f9bd 100644 --- a/packages/components/src/circular-option-picker/test/index.tsx +++ b/packages/components/src/circular-option-picker/test/index.tsx @@ -57,6 +57,7 @@ describe( 'CircularOptionPicker', () => { expect( screen.queryByRole( 'listbox' ) ).not.toBeInTheDocument(); expect( screen.queryByRole( 'option' ) ).not.toBeInTheDocument(); + expect( screen.getByRole( 'group' ) ).toBeInTheDocument(); expect( screen.getByRole( 'button' ) ).toBeInTheDocument(); } ); } ); diff --git a/packages/components/src/circular-option-picker/types.ts b/packages/components/src/circular-option-picker/types.ts index 411782aed575b1..54fae3ab2e798a 100644 --- a/packages/components/src/circular-option-picker/types.ts +++ b/packages/components/src/circular-option-picker/types.ts @@ -40,6 +40,16 @@ type CommonCircularOptionPickerProps = { * The child elements. */ children?: ReactNode; + /** + * The ID reference list of one or more elements that label the wrapper + * element. + */ + 'aria-labelledby'?: string; + /** + * The label for the wrapper element. Defaults to 'Custom color picker'. Not + * used if an 'aria-labelledby' is provided. + */ + 'aria-label'?: string; }; type WithBaseId = { @@ -59,16 +69,7 @@ type FullListboxCircularOptionPickerProps = CommonCircularOptionPickerProps & { * @default true */ loop?: boolean; -} & ( - | { - 'aria-label': string; - 'aria-labelledby'?: never; - } - | { - 'aria-label'?: never; - 'aria-labelledby': string; - } - ); +}; export type ListboxCircularOptionPickerProps = WithBaseId & Omit< FullListboxCircularOptionPickerProps, 'asButtons' >; diff --git a/packages/components/src/circular-option-picker/utils.tsx b/packages/components/src/circular-option-picker/utils.tsx new file mode 100644 index 00000000000000..fcb3b2bcac3695 --- /dev/null +++ b/packages/components/src/circular-option-picker/utils.tsx @@ -0,0 +1,27 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +/** + * Computes the common props for the CircularOptionPicker. + */ +export function getComputeCircularOptionPickerCommonProps( + asButtons?: boolean, + loop?: boolean, + ariaLabel?: string, + ariaLabelledby?: string +) { + const metaProps = asButtons + ? { asButtons: true } + : { asButtons: false, loop }; + + const labelProps = { + 'aria-labelledby': ariaLabelledby, + 'aria-label': ariaLabelledby + ? undefined + : ariaLabel || __( 'Custom color picker' ), + }; + + return { metaProps, labelProps }; +} diff --git a/packages/components/src/color-palette/index.tsx b/packages/components/src/color-palette/index.tsx index de4e4f4206fe3a..eb981e8b9acc70 100644 --- a/packages/components/src/color-palette/index.tsx +++ b/packages/components/src/color-palette/index.tsx @@ -19,7 +19,9 @@ import { useCallback, useMemo, useState, forwardRef } from '@wordpress/element'; */ import Dropdown from '../dropdown'; import { ColorPicker } from '../color-picker'; -import CircularOptionPicker from '../circular-option-picker'; +import CircularOptionPicker, { + getComputeCircularOptionPickerCommonProps, +} from '../circular-option-picker'; import { VStack } from '../v-stack'; import { Truncate } from '../truncate'; import { ColorHeading } from './styles'; @@ -233,7 +235,7 @@ function UnforwardedColorPalette( buttonLabelName, displayValue ) - : __( 'Custom color picker.' ); + : __( 'Custom color picker' ); const paletteCommonProps = { clearColor, @@ -251,33 +253,12 @@ function UnforwardedColorPalette( ); - let metaProps: - | { asButtons: false; loop?: boolean; 'aria-label': string } - | { asButtons: false; loop?: boolean; 'aria-labelledby': string } - | { asButtons: true }; - - if ( asButtons ) { - metaProps = { asButtons: true }; - } else { - const _metaProps: { asButtons: false; loop?: boolean } = { - asButtons: false, - loop, - }; - - if ( ariaLabel ) { - metaProps = { ..._metaProps, 'aria-label': ariaLabel }; - } else if ( ariaLabelledby ) { - metaProps = { - ..._metaProps, - 'aria-labelledby': ariaLabelledby, - }; - } else { - metaProps = { - ..._metaProps, - 'aria-label': __( 'Custom color picker.' ), - }; - } - } + const { metaProps, labelProps } = getComputeCircularOptionPickerCommonProps( + asButtons, + loop, + ariaLabel, + ariaLabelledby + ); return ( @@ -335,6 +316,7 @@ function UnforwardedColorPalette( { ( colors.length > 0 || actions ) && ( { expect( screen.queryByText( colorCode ) ).not.toBeInTheDocument(); expect( screen.getByRole( 'button', { - name: /^Custom color picker.$/, + name: /^Custom color picker$/, } ) ).toBeInTheDocument(); } ); diff --git a/packages/components/src/duotone-picker/duotone-picker.tsx b/packages/components/src/duotone-picker/duotone-picker.tsx index 8764b401c38296..a21d12b73a65c4 100644 --- a/packages/components/src/duotone-picker/duotone-picker.tsx +++ b/packages/components/src/duotone-picker/duotone-picker.tsx @@ -13,7 +13,9 @@ import { __, sprintf } from '@wordpress/i18n'; * Internal dependencies */ import ColorListPicker from './color-list-picker'; -import CircularOptionPicker from '../circular-option-picker'; +import CircularOptionPicker, { + getComputeCircularOptionPickerCommonProps, +} from '../circular-option-picker'; import { VStack } from '../v-stack'; import CustomDuotoneBar from './custom-duotone-bar'; @@ -127,33 +129,12 @@ function DuotonePicker( { ); } ); - let metaProps: - | { asButtons: false; loop?: boolean; 'aria-label': string } - | { asButtons: false; loop?: boolean; 'aria-labelledby': string } - | { asButtons: true }; - - if ( asButtons ) { - metaProps = { asButtons: true }; - } else { - const _metaProps: { asButtons: false; loop?: boolean } = { - asButtons: false, - loop, - }; - - if ( ariaLabel ) { - metaProps = { ..._metaProps, 'aria-label': ariaLabel }; - } else if ( ariaLabelledby ) { - metaProps = { - ..._metaProps, - 'aria-labelledby': ariaLabelledby, - }; - } else { - metaProps = { - ..._metaProps, - 'aria-label': __( 'Custom color picker.' ), - }; - } - } + const { metaProps, labelProps } = getComputeCircularOptionPickerCommonProps( + asButtons, + loop, + ariaLabel, + ariaLabelledby + ); const options = unsetable ? [ unsetOption, ...duotoneOptions ] @@ -163,6 +144,7 @@ function DuotonePicker( { ) { ); - let metaProps: - | { asButtons: false; loop?: boolean; 'aria-label': string } - | { asButtons: false; loop?: boolean; 'aria-labelledby': string } - | { asButtons: true }; - - if ( asButtons ) { - metaProps = { asButtons: true }; - } else { - const _metaProps: { asButtons: false; loop?: boolean } = { - asButtons: false, - loop, - }; - - if ( ariaLabel ) { - metaProps = { ..._metaProps, 'aria-label': ariaLabel }; - } else if ( ariaLabelledby ) { - metaProps = { - ..._metaProps, - 'aria-labelledby': ariaLabelledby, - }; - } else { - metaProps = { - ..._metaProps, - 'aria-label': __( 'Custom color picker.' ), - }; - } - } + const { metaProps, labelProps } = getComputeCircularOptionPickerCommonProps( + asButtons, + loop, + ariaLabel, + ariaLabelledby + ); return ( diff --git a/test/e2e/specs/editor/blocks/buttons.spec.js b/test/e2e/specs/editor/blocks/buttons.spec.js index 554bd8947f0bf5..7830a934529aa4 100644 --- a/test/e2e/specs/editor/blocks/buttons.spec.js +++ b/test/e2e/specs/editor/blocks/buttons.spec.js @@ -324,13 +324,13 @@ test.describe( 'Buttons', () => { await page.click( 'role=region[name="Editor settings"i] >> role=button[name="Text"i]' ); - await page.click( 'role=button[name="Custom color picker."i]' ); + await page.click( 'role=button[name="Custom color picker"i]' ); await page.fill( 'role=textbox[name="Hex color"i]', 'ff0000' ); await page.click( 'role=region[name="Editor settings"i] >> role=button[name="Background"i]' ); - await page.click( 'role=button[name="Custom color picker."i]' ); + await page.click( 'role=button[name="Custom color picker"i]' ); await page.fill( 'role=textbox[name="Hex color"i]', '00ff00' ); // Check the content. diff --git a/test/e2e/specs/editor/blocks/cover.spec.js b/test/e2e/specs/editor/blocks/cover.spec.js index 87c244a7306dc6..bee2548c2305d2 100644 --- a/test/e2e/specs/editor/blocks/cover.spec.js +++ b/test/e2e/specs/editor/blocks/cover.spec.js @@ -33,7 +33,7 @@ test.describe( 'Cover', () => { } ); // Locate the Black color swatch. - const blackColorSwatch = coverBlock.getByRole( 'option', { + const blackColorSwatch = coverBlock.getByRole( 'button', { name: 'Black', } ); await expect( blackColorSwatch ).toBeVisible(); @@ -105,7 +105,7 @@ test.describe( 'Cover', () => { // Choose a color swatch to transform the placeholder block into // a functioning block. await coverBlock - .getByRole( 'option', { + .getByRole( 'button', { name: 'Black', } ) .click(); @@ -128,7 +128,7 @@ test.describe( 'Cover', () => { name: 'Block: Cover', } ); await coverBlock - .getByRole( 'option', { + .getByRole( 'button', { name: 'Black', } ) .click(); @@ -240,7 +240,7 @@ test.describe( 'Cover', () => { // Choose a color swatch to transform the placeholder block into // a functioning block. await coverBlock - .getByRole( 'option', { + .getByRole( 'button', { name: 'Black', } ) .click(); @@ -266,7 +266,7 @@ test.describe( 'Cover', () => { // Choose a color swatch to transform the placeholder block into // a functioning block. await secondCoverBlock - .getByRole( 'option', { + .getByRole( 'button', { name: 'Black', } ) .click(); diff --git a/test/e2e/specs/editor/blocks/heading.spec.js b/test/e2e/specs/editor/blocks/heading.spec.js index 906095cad9d080..6ff7e11bb334e0 100644 --- a/test/e2e/specs/editor/blocks/heading.spec.js +++ b/test/e2e/specs/editor/blocks/heading.spec.js @@ -184,7 +184,7 @@ test.describe( 'Heading', () => { await textColor.click(); await page - .getByRole( 'button', { name: /Custom color picker./i } ) + .getByRole( 'button', { name: /Custom color picker/i } ) .click(); await page diff --git a/test/e2e/specs/editor/various/list-view.spec.js b/test/e2e/specs/editor/various/list-view.spec.js index 988683c8d11aa3..98dfe5e304f802 100644 --- a/test/e2e/specs/editor/various/list-view.spec.js +++ b/test/e2e/specs/editor/various/list-view.spec.js @@ -162,10 +162,10 @@ test.describe( 'List View', () => { // make the inner blocks appear. await editor.canvas .getByRole( 'document', { name: 'Block: Cover' } ) - .getByRole( 'listbox', { - name: 'Custom color picker.', + .getByRole( 'group', { + name: 'Overlay color', } ) - .getByRole( 'option' ) + .getByRole( 'button' ) .first() .click();