Skip to content

Commit 8ccf3c4

Browse files
committed
feat: aria-value* props support
1 parent 5dbd04f commit 8ccf3c4

File tree

7 files changed

+128
-10
lines changed

7 files changed

+128
-10
lines changed

src/helpers/__tests__/format-default.test.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ describe('mapPropsForQueryError', () => {
2121
'aria-labelledby': 'ARIA_LABELLED_BY',
2222
'aria-modal': true,
2323
'aria-selected': 'ARIA-SELECTED',
24+
'aria-valuemax': 'ARIA-VALUEMAX',
25+
'aria-valuemin': 'ARIA-VALUEMIN',
26+
'aria-valuenow': 'ARIA-VALUENOW',
27+
'aria-valuetext': 'ARIA-VALUETEXT',
2428
placeholder: 'PLACEHOLDER',
2529
value: 'VALUE',
2630
defaultValue: 'DEFAULT_VALUE',

src/helpers/accessiblity.ts

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,9 @@ export function isAccessibilityElement(
112112
);
113113
}
114114

115-
export function getAccessibilityRole(element: ReactTestInstance) {
115+
export function getAccessibilityRole(
116+
element: ReactTestInstance
117+
): string | undefined {
116118
return element.props.role ?? element.props.accessibilityRole;
117119
}
118120

@@ -134,7 +136,9 @@ export function getAccessibilityLabelledBy(
134136
);
135137
}
136138

137-
export function getAccessibilityState(element: ReactTestInstance) {
139+
export function getAccessibilityState(
140+
element: ReactTestInstance
141+
): AccessibilityState | undefined {
138142
const {
139143
accessibilityState,
140144
'aria-busy': ariaBusy,
@@ -171,3 +175,33 @@ export function getAccessibilityCheckedState(
171175
const { accessibilityState, 'aria-checked': ariaChecked } = element.props;
172176
return ariaChecked ?? accessibilityState?.checked;
173177
}
178+
179+
export function getAccessibilityValue(
180+
element: ReactTestInstance
181+
): AccessibilityValue | undefined {
182+
const {
183+
accessibilityValue,
184+
'aria-valuemax': ariaValueMax,
185+
'aria-valuemin': ariaValueMin,
186+
'aria-valuenow': ariaValueNow,
187+
'aria-valuetext': ariaValueText,
188+
} = element.props;
189+
190+
const hasAnyAccessibilityValueProps =
191+
accessibilityValue != null ||
192+
ariaValueMax != null ||
193+
ariaValueMin != null ||
194+
ariaValueNow != null ||
195+
ariaValueText != null;
196+
197+
if (!hasAnyAccessibilityValueProps) {
198+
return undefined;
199+
}
200+
201+
return {
202+
max: ariaValueMax ?? accessibilityValue?.max,
203+
min: ariaValueMin ?? accessibilityValue?.min,
204+
now: ariaValueNow ?? accessibilityValue?.now,
205+
text: ariaValueText ?? accessibilityValue?.text,
206+
};
207+
}

src/helpers/format-default.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ const propsToDisplay = [
1717
'aria-labelledby',
1818
'aria-modal',
1919
'aria-selected',
20+
'aria-valuemax',
21+
'aria-valuemin',
22+
'aria-valuenow',
23+
'aria-valuetext',
2024
'defaultValue',
2125
'importantForAccessibility',
2226
'nativeID',

src/helpers/matchers/accessibilityValue.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { AccessibilityValue } from 'react-native';
21
import { ReactTestInstance } from 'react-test-renderer';
2+
import { getAccessibilityValue } from '../accessiblity';
33
import { TextMatch } from '../../matches';
44
import { matchStringProp } from './matchStringProp';
55

@@ -14,11 +14,11 @@ export function matchAccessibilityValue(
1414
node: ReactTestInstance,
1515
matcher: AccessibilityValueMatcher
1616
): boolean {
17-
const value: AccessibilityValue = node.props.accessibilityValue ?? {};
17+
const value = getAccessibilityValue(node);
1818
return (
19-
(matcher.min === undefined || matcher.min === value.min) &&
20-
(matcher.max === undefined || matcher.max === value.max) &&
21-
(matcher.now === undefined || matcher.now === value.now) &&
22-
(matcher.text === undefined || matchStringProp(value.text, matcher.text))
19+
(matcher.min === undefined || matcher.min === value?.min) &&
20+
(matcher.max === undefined || matcher.max === value?.max) &&
21+
(matcher.now === undefined || matcher.now === value?.now) &&
22+
(matcher.text === undefined || matchStringProp(value?.text, matcher.text))
2323
);
2424
}

src/queries/__tests__/a11yValue.test.tsx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,3 +307,37 @@ test('error message renders the element tree, preserving only helpful props', as
307307
/>"
308308
`);
309309
});
310+
311+
describe('getByAccessibilityValue "aria-value*" props', () => {
312+
test('supports aria-valuemax', () => {
313+
const screen = render(<View aria-valuemax={10} />);
314+
expect(screen.getByAccessibilityValue({ max: 10 })).toBeTruthy();
315+
});
316+
317+
test('supports aria-valuemin', () => {
318+
const screen = render(<View aria-valuemin={20} />);
319+
expect(screen.getByAccessibilityValue({ min: 20 })).toBeTruthy();
320+
});
321+
322+
test('supports aria-valuenow', () => {
323+
const screen = render(<View aria-valuenow={30} />);
324+
expect(screen.getByAccessibilityValue({ now: 30 })).toBeTruthy();
325+
});
326+
327+
test('supports aria-valuetext', () => {
328+
const screen = render(<View aria-valuetext="Hello World" />);
329+
expect(
330+
screen.getByAccessibilityValue({ text: 'Hello World' })
331+
).toBeTruthy();
332+
expect(screen.getByAccessibilityValue({ text: /hello/i })).toBeTruthy();
333+
});
334+
335+
test('supports multiple aria-value* props', () => {
336+
const screen = render(
337+
<View aria-valuenow={50} aria-valuemin={0} aria-valuemax={100} />
338+
);
339+
expect(
340+
screen.getByAccessibilityValue({ now: 50, min: 0, max: 100 })
341+
).toBeTruthy();
342+
});
343+
});

src/queries/__tests__/role-value.test.tsx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,4 +173,46 @@ describe('accessibility value', () => {
173173
</Text>"
174174
`);
175175
});
176+
177+
test('supports "aria-valuemax" prop', () => {
178+
const screen = render(<View accessible role="slider" aria-valuemax={10} />);
179+
expect(screen.getByRole('slider', { value: { max: 10 } })).toBeTruthy();
180+
});
181+
182+
test('supports "aria-valuemin" prop', () => {
183+
const screen = render(<View accessible role="slider" aria-valuemin={20} />);
184+
expect(screen.getByRole('slider', { value: { min: 20 } })).toBeTruthy();
185+
});
186+
187+
test('supports "aria-valuenow" prop', () => {
188+
const screen = render(<View accessible role="slider" aria-valuenow={30} />);
189+
expect(screen.getByRole('slider', { value: { now: 30 } })).toBeTruthy();
190+
});
191+
192+
test('supports "aria-valuetext" prop', () => {
193+
const screen = render(
194+
<View accessible role="slider" aria-valuetext="Hello World" />
195+
);
196+
expect(
197+
screen.getByRole('slider', { value: { text: 'Hello World' } })
198+
).toBeTruthy();
199+
expect(
200+
screen.getByRole('slider', { value: { text: /hello/i } })
201+
).toBeTruthy();
202+
});
203+
204+
test('supports multiple aria-value* props', () => {
205+
const screen = render(
206+
<View
207+
accessible
208+
role="slider"
209+
aria-valuenow={50}
210+
aria-valuemin={0}
211+
aria-valuemax={100}
212+
/>
213+
);
214+
expect(
215+
screen.getByRole('slider', { value: { now: 50, min: 0, max: 100 } })
216+
).toBeTruthy();
217+
});
176218
});

website/docs/Queries.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ const element3 = screen.getByRole('button', { name: 'Hello', disabled: true });
121121

122122
`expanded`: You can filter elements by their expanded state (coming either from `aria-expanded` prop or `accessbilityState.expanded` prop). The possible values are `true` or `false`. See [React Native's accessibilityState](https://reactnative.dev/docs/accessibility#accessibilitystate) docs to learn more about the `expanded` state.
123123

124-
`value`: Filter elements by their accessibility, available value entries include numeric `min`, `max` & `now`, as well as string or regex `text` key. See React Native [accessibilityValue](https://reactnative.dev/docs/accessibility#accessibilityvalue) docs to learn more about this prop.
124+
`value`: Filter elements by their accessibility value, based on either `aria-valuemin`, `aria-valuemax`, `aria-valuenow`, `aria-valuetext` or accessibilityValue props. Accessiblity value conceptually consist of numeric `min`, `max` and `now` values, as well as string `text` value. See React Native [accessibilityValue](https://reactnative.dev/docs/accessibility#accessibilityvalue) docs to learn more about the accessibility value.
125125

126126
### `ByText`
127127

@@ -371,7 +371,7 @@ getByA11yValue(
371371
): ReactTestInstance;
372372
```
373373

374-
Returns a host element with matching `accessibilityValue` prop entries. Only entires provided to the query will be used to match elements. Element might have additional accessibility value entries and still be matched.
374+
Returns a host element with matching accessibility value based on `aria-valuemin`, `aria-valuemax`, `aria-valuenow`, `aria-valuetext` & `accessibilityValue` props. Only value entires provided to the query will be used to match elements. Element might have additional accessibility value entries and still be matched.
375375

376376
When querying by `text` entry a string or regex might be used.
377377

0 commit comments

Comments
 (0)