Skip to content

Commit 13e4efa

Browse files
authored
[MOO-2437]: Add accessibility label to color picker (#686)
2 parents 03cb9f5 + a2ae49b commit 13e4efa

10 files changed

Lines changed: 98 additions & 11 deletions

File tree

‎packages/pluggableWidgets/color-picker-native/CHANGELOG.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66

77
## [Unreleased]
88

9+
### Added
10+
11+
- We added screen reader label properties for the hue, saturation, brightness and transparency sliders.
12+
913
### Changed
1014

1115
- We replaced react-native-slider and react-native-color with built-in components, and removed deprecated-react-native-prop-types.

‎packages/pluggableWidgets/color-picker-native/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "color-picker-native",
33
"widgetName": "ColorPicker",
4-
"version": "2.4.0",
4+
"version": "2.4.1",
55
"license": "Apache-2.0",
66
"repository": {
77
"type": "git",

‎packages/pluggableWidgets/color-picker-native/src/ColorPicker.editorConfig.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { StructurePreviewProps } from "@mendix/piw-utils-internal";
2+
import { hidePropertiesIn, Properties } from "@mendix/pluggable-widgets-tools";
23

34
import { ColorPickerPreviewProps } from "../typings/ColorPickerProps";
45
import colorPickerSvgNoPreviewDark from "./assets/ColorPicker.nopreview.dark.svg";
@@ -22,3 +23,10 @@ export function getPreview(values: ColorPickerPreviewProps, isDarkMode: boolean)
2223
width: 188
2324
};
2425
}
26+
27+
export function getProperties(values: ColorPickerPreviewProps, defaultProperties: Properties): Properties {
28+
if (!values.enableAccessibility) {
29+
hidePropertiesIn(defaultProperties, values, ["hueLabel", "saturationLabel", "lightnessLabel", "alphaLabel"]);
30+
}
31+
return defaultProperties;
32+
}

‎packages/pluggableWidgets/color-picker-native/src/ColorPicker.tsx‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ export function ColorPicker(props: Props): ReactElement | null {
110110
)}
111111
<PickerSlider
112112
testID={`${props.name}$hue`}
113+
accessibilityLabel={(props.enableAccessibility && props.hueLabel?.value) || "Hue"}
113114
value={color.h}
114115
onValueChange={onChangeHue}
115116
onValueChangeComplete={onChangeComplete}
@@ -128,6 +129,7 @@ export function ColorPicker(props: Props): ReactElement | null {
128129
{props.showSaturation && !colorValue.readOnly && (
129130
<PickerSlider
130131
testID={`${props.name}$saturation`}
132+
accessibilityLabel={(props.enableAccessibility && props.saturationLabel?.value) || "Saturation"}
131133
value={color.s}
132134
onValueChange={onChangeSaturation}
133135
onValueChangeComplete={onChangeComplete}
@@ -142,6 +144,7 @@ export function ColorPicker(props: Props): ReactElement | null {
142144
{props.showLightness && !colorValue.readOnly && (
143145
<PickerSlider
144146
testID={`${props.name}$lightness`}
147+
accessibilityLabel={(props.enableAccessibility && props.lightnessLabel?.value) || "Brightness"}
145148
value={color.l}
146149
onValueChange={onChangeLightness}
147150
onValueChangeComplete={onChangeComplete}
@@ -156,6 +159,7 @@ export function ColorPicker(props: Props): ReactElement | null {
156159
{format !== "hex" && props.showAlpha && !colorValue.readOnly && (
157160
<PickerSlider
158161
testID={`${props.name}$alpha`}
162+
accessibilityLabel={(props.enableAccessibility && props.alphaLabel?.value) || "Transparency"}
159163
value={color.a}
160164
onValueChange={onChangeAlpha}
161165
onValueChangeComplete={onChangeComplete}

‎packages/pluggableWidgets/color-picker-native/src/ColorPicker.xml‎

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
<?xml version="1.0" encoding="utf-8"?>
2-
<widget id="com.mendix.widget.native.colorpicker.ColorPicker" supportedPlatform="Native" needsEntityContext="true"
3-
offlineCapable="true" pluginWidget="true" xmlns="http://www.mendix.com/widget/1.0/"
4-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5-
xsi:schemaLocation="http://www.mendix.com/widget/1.0/ ../../../node_modules/mendix/custom_widget.xsd">
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<widget id="com.mendix.widget.native.colorpicker.ColorPicker" supportedPlatform="Native" needsEntityContext="true" offlineCapable="true" pluginWidget="true" xmlns="http://www.mendix.com/widget/1.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mendix.com/widget/1.0/ ../../../node_modules/mendix/custom_widget.xsd">
63
<name>Color picker</name>
74
<description>Pick a color from a circular color input</description>
85
<studioProCategory>Input elements</studioProCategory>
@@ -14,7 +11,7 @@
1411
<caption>Color</caption>
1512
<description>Attribute containing a valid color like #d0d0d0, red, rgba(255,255,255,0).</description>
1613
<attributeTypes>
17-
<attributeType name="String"/>
14+
<attributeType name="String" />
1815
</attributeTypes>
1916
</property>
2017
<property key="format" type="enumeration" defaultValue="hex">
@@ -47,6 +44,28 @@
4744
<description>Enables a slider to make the color more transparent or opaque.</description>
4845
</property>
4946
</propertyGroup>
47+
<propertyGroup caption="Accessibility">
48+
<property key="enableAccessibility" type="boolean" defaultValue="false">
49+
<caption>Custom accessibility labels</caption>
50+
<description>Enables setting custom screen reader labels for the sliders.</description>
51+
</property>
52+
<property key="hueLabel" type="textTemplate" required="false">
53+
<caption>Hue label</caption>
54+
<description>Screen reader label of the hue slider. Defaults to "Hue".</description>
55+
</property>
56+
<property key="saturationLabel" type="textTemplate" required="false">
57+
<caption>Saturation label</caption>
58+
<description>Screen reader label of the saturation slider. Defaults to "Saturation".</description>
59+
</property>
60+
<property key="lightnessLabel" type="textTemplate" required="false">
61+
<caption>Brightness label</caption>
62+
<description>Screen reader label of the brightness slider. Defaults to "Brightness".</description>
63+
</property>
64+
<property key="alphaLabel" type="textTemplate" required="false">
65+
<caption>Transparency label</caption>
66+
<description>Screen reader label of the transparency slider. Defaults to "Transparency".</description>
67+
</property>
68+
</propertyGroup>
5069
<propertyGroup caption="Events">
5170
<property key="onChange" type="action" required="false">
5271
<caption>On change</caption>
@@ -55,7 +74,7 @@
5574
</propertyGroup>
5675
<propertyGroup caption="Common">
5776
<systemProperty key="Visibility" />
58-
<systemProperty key="Name"/>
77+
<systemProperty key="Name" />
5978
</propertyGroup>
6079
</propertyGroup>
6180
</properties>

‎packages/pluggableWidgets/color-picker-native/src/__tests__/ColorPicker.spec.tsx‎

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { actionValue, EditableValueBuilder } from "@mendix/piw-utils-internal";
2+
import { DynamicValue, ValueStatus } from "mendix";
23
import { View } from "react-native";
34
import { fireEvent, render, RenderAPI } from "@testing-library/react-native";
45
import { ReactTestInstance } from "react-test-renderer";
@@ -17,7 +18,8 @@ describe("Color Picker", () => {
1718
showPreview: true,
1819
showSaturation: true,
1920
showLightness: true,
20-
showAlpha: false
21+
showAlpha: false,
22+
enableAccessibility: false
2123
};
2224
});
2325

@@ -222,6 +224,34 @@ describe("Color Picker", () => {
222224
});
223225
});
224226

227+
it("labels the sliders for screen readers", () => {
228+
const label = (value: string): DynamicValue<string> => ({ status: ValueStatus.Available, value });
229+
const component = render(
230+
<ColorPicker
231+
{...defaultProps}
232+
format="rgb"
233+
showAlpha
234+
enableAccessibility
235+
hueLabel={label("Tint")}
236+
saturationLabel={label("Intensity")}
237+
/>
238+
);
239+
240+
const labelOf = (name: string): string =>
241+
component.getByTestId(`color-picker-test$${name}`).props.accessibilityLabel;
242+
expect(labelOf("hue")).toBe("Tint");
243+
expect(labelOf("saturation")).toBe("Intensity");
244+
expect(labelOf("lightness")).toBe("Brightness");
245+
expect(labelOf("alpha")).toBe("Transparency");
246+
});
247+
248+
it("ignores custom labels when accessibility is not enabled", () => {
249+
const label = (value: string): DynamicValue<string> => ({ status: ValueStatus.Available, value });
250+
const component = render(<ColorPicker {...defaultProps} hueLabel={label("Tint")} />);
251+
252+
expect(component.getByTestId("color-picker-test$hue").props.accessibilityLabel).toBe("Hue");
253+
});
254+
225255
it("changes the value with screen reader actions", () => {
226256
const onChangeAction = actionValue();
227257
const component = render(<ColorPicker {...defaultProps} onChange={onChangeAction} />);

‎packages/pluggableWidgets/color-picker-native/src/__tests__/__snapshots__/ColorPicker.spec.tsx.snap‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1118,6 +1118,7 @@ exports[`Color Picker renders 1`] = `
11181118
},
11191119
]
11201120
}
1121+
accessibilityLabel="Hue"
11211122
accessibilityRole="adjustable"
11221123
accessibilityState={
11231124
{
@@ -2330,6 +2331,7 @@ exports[`Color Picker renders 1`] = `
23302331
},
23312332
]
23322333
}
2334+
accessibilityLabel="Saturation"
23332335
accessibilityRole="adjustable"
23342336
accessibilityState={
23352337
{
@@ -3542,6 +3544,7 @@ exports[`Color Picker renders 1`] = `
35423544
},
35433545
]
35443546
}
3547+
accessibilityLabel="Brightness"
35453548
accessibilityRole="adjustable"
35463549
accessibilityState={
35473550
{
@@ -4776,6 +4779,7 @@ exports[`Color Picker renders with alpha slider 1`] = `
47764779
},
47774780
]
47784781
}
4782+
accessibilityLabel="Hue"
47794783
accessibilityRole="adjustable"
47804784
accessibilityState={
47814785
{
@@ -5988,6 +5992,7 @@ exports[`Color Picker renders with alpha slider 1`] = `
59885992
},
59895993
]
59905994
}
5995+
accessibilityLabel="Saturation"
59915996
accessibilityRole="adjustable"
59925997
accessibilityState={
59935998
{
@@ -7200,6 +7205,7 @@ exports[`Color Picker renders with alpha slider 1`] = `
72007205
},
72017206
]
72027207
}
7208+
accessibilityLabel="Brightness"
72037209
accessibilityRole="adjustable"
72047210
accessibilityState={
72057211
{
@@ -8412,6 +8418,7 @@ exports[`Color Picker renders with alpha slider 1`] = `
84128418
},
84138419
]
84148420
}
8421+
accessibilityLabel="Transparency"
84158422
accessibilityRole="adjustable"
84168423
accessibilityState={
84178424
{
@@ -9647,6 +9654,7 @@ exports[`Color Picker renders with custom style 1`] = `
96479654
},
96489655
]
96499656
}
9657+
accessibilityLabel="Hue"
96509658
accessibilityRole="adjustable"
96519659
accessibilityState={
96529660
{
@@ -10859,6 +10867,7 @@ exports[`Color Picker renders with custom style 1`] = `
1085910867
},
1086010868
]
1086110869
}
10870+
accessibilityLabel="Saturation"
1086210871
accessibilityRole="adjustable"
1086310872
accessibilityState={
1086410873
{
@@ -12071,6 +12080,7 @@ exports[`Color Picker renders with custom style 1`] = `
1207112080
},
1207212081
]
1207312082
}
12083+
accessibilityLabel="Brightness"
1207412084
accessibilityRole="adjustable"
1207512085
accessibilityState={
1207612086
{

‎packages/pluggableWidgets/color-picker-native/src/components/PickerSlider.tsx‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ interface PickerSlidersProps {
1414
trackStyle?: ViewStyle;
1515
disabled?: boolean;
1616
testID?: string;
17+
accessibilityLabel?: string;
1718
}
1819

1920
export function PickerSlider(props: PropsWithChildren<PickerSlidersProps>): ReactElement {
@@ -22,6 +23,7 @@ export function PickerSlider(props: PropsWithChildren<PickerSlidersProps>): Reac
2223
<View style={styles.gradient}>{props.children}</View>
2324
<Slider
2425
testID={props.testID}
26+
accessibilityLabel={props.accessibilityLabel}
2527
value={props.value}
2628
step={props.step}
2729
trackClickable

‎packages/pluggableWidgets/color-picker-native/src/package.xml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="utf-8" ?>
22
<package xmlns="http://www.mendix.com/package/1.0/">
3-
<clientModule name="ColorPicker" version="2.4.0" xmlns="http://www.mendix.com/clientModule/1.0/">
3+
<clientModule name="ColorPicker" version="2.4.1" xmlns="http://www.mendix.com/clientModule/1.0/">
44
<widgetFiles>
55
<widgetFile path="ColorPicker.xml" />
66
</widgetFiles>

‎packages/pluggableWidgets/color-picker-native/typings/ColorPickerProps.d.ts‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* WARNING: All changes made to this file will be overwritten
44
* @author Mendix Widgets Framework Team
55
*/
6-
import { ActionValue, EditableValue } from "mendix";
6+
import { ActionValue, DynamicValue, EditableValue } from "mendix";
77
import { CSSProperties } from "react";
88

99
export type FormatEnum = "hex" | "hsl" | "hsv" | "rgb";
@@ -17,6 +17,11 @@ export interface ColorPickerProps<Style> {
1717
showSaturation: boolean;
1818
showLightness: boolean;
1919
showAlpha: boolean;
20+
enableAccessibility: boolean;
21+
hueLabel?: DynamicValue<string>;
22+
saturationLabel?: DynamicValue<string>;
23+
lightnessLabel?: DynamicValue<string>;
24+
alphaLabel?: DynamicValue<string>;
2025
onChange?: ActionValue;
2126
}
2227

@@ -37,5 +42,10 @@ export interface ColorPickerPreviewProps {
3742
showSaturation: boolean;
3843
showLightness: boolean;
3944
showAlpha: boolean;
45+
enableAccessibility: boolean;
46+
hueLabel: string;
47+
saturationLabel: string;
48+
lightnessLabel: string;
49+
alphaLabel: string;
4050
onChange: {} | null;
4151
}

0 commit comments

Comments
 (0)