diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 7ad6becdb8aef6..52c40d766956b7 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -17,6 +17,7 @@ - `ToggleGroupControl`: Improve controlled value detection ([#57770](https://github.com/WordPress/gutenberg/pull/57770)). - `Tooltip`: Improve props forwarding to children of nested `Tooltip` components ([#57878](https://github.com/WordPress/gutenberg/pull/57878)). +- `PaletteEdit`: Fix palette item accessibility in details view ([#58214](https://github.com/WordPress/gutenberg/pull/58214)). - `Tooltip`: revert prop types to only accept component-specific props ([#58125](https://github.com/WordPress/gutenberg/pull/58125)). - `Button`: prevent the component from trashing and re-creating the HTML element ([#56490](https://github.com/WordPress/gutenberg/pull/56490)). diff --git a/packages/components/src/palette-edit/index.tsx b/packages/components/src/palette-edit/index.tsx index 87df1894dc36fc..694b7ad9511908 100644 --- a/packages/components/src/palette-edit/index.tsx +++ b/packages/components/src/palette-edit/index.tsx @@ -198,23 +198,22 @@ function Option< T extends Color | Gradient >( { return ( - - - + { isEditing && ! canOnlyChangeValues ? ( ( { } /> ) : ( - { element.name } + + { element.name.trim().length + ? element.name + : /* Fall back to non-breaking space to maintain height */ + '\u00A0' } + ) } { isEditing && ! canOnlyChangeValues && ( diff --git a/packages/components/src/palette-edit/stories/index.story.tsx b/packages/components/src/palette-edit/stories/index.story.tsx index dd2ab92978c923..e78bc04d5b9ec9 100644 --- a/packages/components/src/palette-edit/stories/index.story.tsx +++ b/packages/components/src/palette-edit/stories/index.story.tsx @@ -56,6 +56,7 @@ Default.args = { colors: [ { color: '#1a4548', name: 'Primary', slug: 'primary' }, { color: '#0000ff', name: 'Secondary', slug: 'secondary' }, + { color: '#fb326b', name: 'Tertiary', slug: 'tertiary' }, ], paletteLabel: 'Colors', emptyMessage: 'Colors are empty', diff --git a/packages/components/src/palette-edit/styles.js b/packages/components/src/palette-edit/styles.ts similarity index 63% rename from packages/components/src/palette-edit/styles.js rename to packages/components/src/palette-edit/styles.ts index 4beeb6e7c2213d..2c4e05a02a7ed2 100644 --- a/packages/components/src/palette-edit/styles.js +++ b/packages/components/src/palette-edit/styles.ts @@ -2,6 +2,7 @@ * External dependencies */ import styled from '@emotion/styled'; +import { css } from '@emotion/react'; /** * Internal dependencies @@ -10,7 +11,7 @@ import Button from '../button'; import { Heading } from '../heading'; import { HStack } from '../h-stack'; import { space } from '../utils/space'; -import { COLORS, CONFIG } from '../utils'; +import { COLORS, CONFIG, font } from '../utils'; import { View } from '../view'; import InputControl from '../input-control'; import { @@ -18,12 +19,14 @@ import { Input, BackdropUI as InputBackdropUI, } from '../input-control/styles/input-control-styles'; -import CircularOptionPicker from '../circular-option-picker'; +import ColorIndicator from '../color-indicator'; -export const IndicatorStyled = styled( CircularOptionPicker.Option )` - width: ${ space( 6 ) }; - height: ${ space( 6 ) }; - pointer-events: none; +export const IndicatorStyled = styled( ColorIndicator )` + && { + flex-shrink: 0; + width: ${ space( 6 ) }; + height: ${ space( 6 ) }; + } `; export const NameInputControl = styled( InputControl )` @@ -40,20 +43,66 @@ export const NameInputControl = styled( InputControl )` } `; +const buttonStyleReset = ( { + as, +}: { + as: React.ComponentProps< typeof View >[ 'as' ]; +} ) => { + if ( as === 'button' ) { + return css` + display: flex; + align-items: center; + width: 100%; + appearance: none; + background: transparent; + border: none; + border-radius: 0; + padding: 0; + cursor: pointer; + + &:hover { + color: ${ COLORS.theme.accent }; + } + `; + } + return null; +}; + export const PaletteItem = styled( View )` + ${ buttonStyleReset } + padding-block: 3px; padding-inline-start: ${ space( 3 ) }; border: 1px solid ${ CONFIG.surfaceBorderColor }; border-bottom-color: transparent; - &:first-of-type { - border-top-left-radius: ${ CONFIG.controlBorderRadius }; - border-top-right-radius: ${ CONFIG.controlBorderRadius }; + font-size: ${ font( 'default.fontSize' ) }; + + &:focus-visible { + border-color: transparent; + box-shadow: 0 0 0 var( --wp-admin-border-width-focus ) + var( + --wp-components-color-accent, + var( --wp-admin-theme-color, ${ COLORS.theme.accent } ) + ); + // Windows high contrast mode. + outline: 2px solid transparent; + outline-offset: 0; + } + + border-top-left-radius: ${ CONFIG.controlBorderRadius }; + border-top-right-radius: ${ CONFIG.controlBorderRadius }; + + & + & { + border-top-left-radius: 0; + border-top-right-radius: 0; } - &:last-of-type { + + &:last-child { border-bottom-left-radius: ${ CONFIG.controlBorderRadius }; border-bottom-right-radius: ${ CONFIG.controlBorderRadius }; border-bottom-color: ${ CONFIG.surfaceBorderColor }; } + &.is-selected + & { border-top-color: transparent; } @@ -68,9 +117,6 @@ export const NameContainer = styled.div` margin-right: ${ space( 2 ) }; white-space: nowrap; overflow: hidden; - ${ PaletteItem }:hover & { - color: ${ COLORS.theme.accent }; - } `; export const PaletteHeading = styled( Heading )` diff --git a/packages/components/src/palette-edit/test/index.tsx b/packages/components/src/palette-edit/test/index.tsx index b37db91607f8e9..36f2949b3fbd37 100644 --- a/packages/components/src/palette-edit/test/index.tsx +++ b/packages/components/src/palette-edit/test/index.tsx @@ -295,7 +295,7 @@ describe( 'PaletteEdit', () => { name: 'Show details', } ) ); - await click( screen.getByText( 'Primary' ) ); + await click( screen.getByRole( 'button', { name: 'Edit: Primary' } ) ); await click( screen.getByRole( 'button', { name: 'Remove color', @@ -328,7 +328,7 @@ describe( 'PaletteEdit', () => { name: 'Show details', } ) ); - await click( screen.getByText( 'Primary' ) ); + await click( screen.getByRole( 'button', { name: 'Edit: Primary' } ) ); const nameInput = screen.getByRole( 'textbox', { name: 'Color name', } );