Skip to content

Commit 6650445

Browse files
committed
feat(ui-color-picker): add colorScreenReaderLabel prop to provide more color information
INSTUI-4273
1 parent 51cd81a commit 6650445

File tree

4 files changed

+103
-35
lines changed

4 files changed

+103
-35
lines changed

packages/ui-color-picker/src/ColorPreset/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ A component for picking a color from a list of colors. Supports adding new color
3434
colors={this.state.colors}
3535
selected={this.state.selected}
3636
onSelect={(selected) => this.setState({ selected })}
37+
colorScreenReaderLabel={(hexCode, isSelected) => {
38+
return `color with hex code ${hexCode}${
39+
isSelected ? ' selected' : ''
40+
}`
41+
}}
3742
/>
3843
</div>
3944
)
@@ -65,6 +70,11 @@ A component for picking a color from a list of colors. Supports adding new color
6570
colors={colors}
6671
selected={selected}
6772
onSelect={setSelected}
73+
colorScreenReaderLabel={(hexCode, isSelected) => {
74+
return `color with hex code ${hexCode}${
75+
isSelected ? ' selected' : ''
76+
}`
77+
}}
6878
/>
6979
</div>
7080
)
@@ -131,6 +141,11 @@ A component for picking a color from a list of colors. Supports adding new color
131141
secondColorLabel: 'Foreground'
132142
}
133143
}}
144+
colorScreenReaderLabel={(hexCode, isSelected) => {
145+
return `color with hex code ${hexCode}${
146+
isSelected ? ' selected' : ''
147+
}`
148+
}}
134149
/>
135150
</div>
136151
)
@@ -189,6 +204,11 @@ A component for picking a color from a list of colors. Supports adding new color
189204
secondColorLabel: 'Foreground'
190205
}
191206
}}
207+
colorScreenReaderLabel={(hexCode, isSelected) => {
208+
return `color with hex code ${hexCode}${
209+
isSelected ? ' selected' : ''
210+
}`
211+
}}
192212
/>
193213
</div>
194214
)

packages/ui-color-picker/src/ColorPreset/__tests__/ColorPreset.test.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,32 @@ const testColorMixerSettings: ColorPresetProps['colorMixerSettings'] = {
7575
}
7676

7777
describe('<ColorPreset />', () => {
78+
it('should provide aria-label through the colorScreenReaderLabel prop', async () => {
79+
const mockScreenReaderLabel = vi.fn((hexCode) => `${hexCode}, hex code`)
80+
const props = {
81+
...testValue,
82+
colorScreenReaderLabel: mockScreenReaderLabel
83+
}
84+
85+
render(<ColorPreset {...props} />)
86+
const buttons = screen.getAllByRole('button')
87+
88+
buttons.forEach((button, index) => {
89+
const expectedColor = testValue.colors[index]
90+
expect(button).toHaveAttribute('aria-label', `${expectedColor}, hex code`)
91+
})
92+
})
93+
94+
it('should default to using the hex code as aria-label when colorScreenReaderLabel is not provided ', async () => {
95+
render(<ColorPreset {...testValue} />)
96+
const buttons = screen.getAllByRole('button')
97+
98+
buttons.forEach((button, index) => {
99+
const expectedColor = testValue.colors[index]
100+
expect(button).toHaveAttribute('aria-label', `${expectedColor}`)
101+
})
102+
})
103+
78104
describe('elementRef prop', () => {
79105
it('should provide ref', async () => {
80106
const elementRef = vi.fn()

packages/ui-color-picker/src/ColorPreset/index.tsx

Lines changed: 42 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -250,40 +250,48 @@ class ColorPreset extends Component<ColorPresetProps, ColorPresetState> {
250250
: this.renderIndicatorTooltip(indicatorBase, color)
251251
}
252252

253-
renderIndicatorBase = (color: string, selectOnClick?: boolean) => (
254-
<View
255-
disabled={this.props.disabled}
256-
position="relative"
257-
width="2.375rem"
258-
height="2.375rem"
259-
background="transparent"
260-
margin="xx-small"
261-
display="inline-block"
262-
borderRadius="medium"
263-
borderWidth="0"
264-
padding="0"
265-
cursor={this.props.disabled ? 'not-allowed' : 'auto'}
266-
as="button"
267-
{...(selectOnClick ? { onClick: () => this.props.onSelect(color) } : {})}
268-
{...{
269-
'aria-label': `${color}${
270-
this.isSelectedColor(color) ? ' selected' : ''
271-
}`
272-
}}
273-
>
274-
<div>
275-
<ColorIndicator color={color} shape="rectangle" role="presentation" />
276-
{this.isSelectedColor(color) && (
277-
<div css={this.props?.styles?.selectedIndicator}>
278-
<IconCheckDarkSolid
279-
themeOverride={{ sizeXSmall: '0.8rem' }}
280-
size="x-small"
281-
/>
282-
</div>
283-
)}
284-
</div>
285-
</View>
286-
)
253+
renderIndicatorBase = (color: string, selectOnClick?: boolean) => {
254+
const hexCode = color
255+
const isSelected = this.isSelectedColor(color)
256+
257+
const screenReaderLabel =
258+
typeof this.props.colorScreenReaderLabel === 'function'
259+
? this.props.colorScreenReaderLabel(hexCode, isSelected)
260+
: hexCode
261+
262+
return (
263+
<View
264+
disabled={this.props.disabled}
265+
position="relative"
266+
width="2.375rem"
267+
height="2.375rem"
268+
background="transparent"
269+
margin="xx-small"
270+
display="inline-block"
271+
borderRadius="medium"
272+
borderWidth="0"
273+
padding="0"
274+
cursor={this.props.disabled ? 'not-allowed' : 'auto'}
275+
as="button"
276+
aria-label={screenReaderLabel}
277+
{...(selectOnClick
278+
? { onClick: () => this.props.onSelect(color) }
279+
: {})}
280+
>
281+
<div>
282+
<ColorIndicator color={color} shape="rectangle" role="presentation" />
283+
{this.isSelectedColor(color) && (
284+
<div css={this.props?.styles?.selectedIndicator}>
285+
<IconCheckDarkSolid
286+
themeOverride={{ sizeXSmall: '0.8rem' }}
287+
size="x-small"
288+
/>
289+
</div>
290+
)}
291+
</div>
292+
</View>
293+
)
294+
}
287295

288296
renderIndicatorTooltip = (child: React.ReactElement, color: string) => {
289297
return (

packages/ui-color-picker/src/ColorPreset/props.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,18 @@ type ColorPresetOwnProps = {
103103
* The currently selected HEX string
104104
*/
105105
selected?: string
106+
/**
107+
* A function for formatting the text provided to screen readers about the color.
108+
*
109+
* @param {string} hexCode - The hexadecimal color code (e.g., "#FFFFFF") of the current color option. Provided by the component - treat as read-only.
110+
*
111+
* @param {boolean} isSelected - Indicates whether this color is currently selected. Provided by the component - treat as read-only.
112+
*
113+
* Sets the aria-label attribute of the color.
114+
*
115+
* If not set, aria-label defaults to the hex code of the color.
116+
*/
117+
colorScreenReaderLabel?: (hexCode: string, isSelected: boolean) => string
106118
}
107119

108120
type ColorPresetState = {
@@ -138,6 +150,7 @@ const propTypes: PropValidators<PropKeys> = {
138150
elementRef: PropTypes.func,
139151
label: PropTypes.string,
140152
popoverScreenReaderLabel: PropTypes.string,
153+
colorScreenReaderLabel: PropTypes.func,
141154
colorMixerSettings: PropTypes.shape({
142155
addNewPresetButtonScreenReaderLabel: PropTypes.string.isRequired,
143156
selectColorLabel: PropTypes.string.isRequired,
@@ -181,7 +194,8 @@ const allowedProps: AllowedPropKeys = [
181194
'colorMixerSettings',
182195
'onSelect',
183196
'popoverScreenReaderLabel',
184-
'selected'
197+
'selected',
198+
'colorScreenReaderLabel'
185199
]
186200

187201
export type {

0 commit comments

Comments
 (0)