mirror of
https://github.com/zoriya/react-native-web.git
synced 2026-06-10 13:10:12 +00:00
[change] accessibilityRelationship and accessibilityState props
Adds the accessibilityState and accessibilityRelationship object props that map to ARIA props. Removes the accessibilityStates array prop that is not compatible with web accessibility services. Ref #1172
This commit is contained in:
@@ -10,7 +10,7 @@ describe('CheckBox', () => {
|
||||
describe('disabled', () => {
|
||||
test('when "false" a default checkbox is rendered', () => {
|
||||
const component = shallow(<CheckBox />);
|
||||
expect(component.find(checkboxSelector).prop('disabled')).toBe(false);
|
||||
expect(component.find(checkboxSelector).prop('disabled')).toBe(undefined);
|
||||
});
|
||||
|
||||
test('when "true" a disabled checkbox is rendered', () => {
|
||||
|
||||
@@ -15,7 +15,7 @@ describe('components/Switch', () => {
|
||||
describe('disabled', () => {
|
||||
test('when "false" a default checkbox is rendered', () => {
|
||||
const component = shallow(<Switch />);
|
||||
expect(component.find(checkboxSelector).prop('disabled')).toBe(false);
|
||||
expect(component.find(checkboxSelector).prop('disabled')).toBe(undefined);
|
||||
});
|
||||
|
||||
test('when "true" a disabled checkbox is rendered', () => {
|
||||
|
||||
@@ -10,11 +10,12 @@
|
||||
|
||||
import StyleSheetPropType from '../../modules/StyleSheetPropType';
|
||||
import TextStylePropTypes from './TextStylePropTypes';
|
||||
import { any, bool, func, number, oneOf, string } from 'prop-types';
|
||||
import { any, bool, func, number, object, oneOf, string } from 'prop-types';
|
||||
|
||||
const TextPropTypes = {
|
||||
accessibilityLabel: string,
|
||||
accessibilityLiveRegion: oneOf(['assertive', 'none', 'polite']),
|
||||
accessibilityRelationship: object,
|
||||
accessibilityRole: oneOf([
|
||||
'button',
|
||||
'header',
|
||||
@@ -26,6 +27,7 @@ const TextPropTypes = {
|
||||
'text'
|
||||
]),
|
||||
accessible: bool,
|
||||
accessibilityState: object,
|
||||
children: any,
|
||||
importantForAccessibility: oneOf(['auto', 'no', 'no-hide-descendants', 'yes']),
|
||||
maxFontSizeMultiplier: number,
|
||||
|
||||
+26
-15
@@ -11,7 +11,7 @@
|
||||
import EdgeInsetsPropType, { type EdgeInsetsProp } from '../EdgeInsetsPropType';
|
||||
import StyleSheetPropType from '../../modules/StyleSheetPropType';
|
||||
import ViewStylePropTypes from './ViewStylePropTypes';
|
||||
import { any, arrayOf, bool, func, object, oneOf, string } from 'prop-types';
|
||||
import { any, bool, func, object, oneOf, string } from 'prop-types';
|
||||
import { type StyleObj } from '../StyleSheet/StyleSheetTypes';
|
||||
|
||||
const stylePropType = StyleSheetPropType(ViewStylePropTypes);
|
||||
@@ -33,8 +33,30 @@ export type ViewProps = {
|
||||
accessibilityComponentType?: string,
|
||||
accessibilityLabel?: string,
|
||||
accessibilityLiveRegion?: 'none' | 'polite' | 'assertive',
|
||||
accessibilityRelationship?: {
|
||||
activedescendant?: ?string,
|
||||
controls?: ?string,
|
||||
describedby?: ?string,
|
||||
details?: ?string,
|
||||
haspopup?: ?string,
|
||||
labelledby?: ?string,
|
||||
owns?: ?string
|
||||
},
|
||||
accessibilityRole?: string,
|
||||
accessibilityStates?: Array<string>,
|
||||
accessibilityState?: {
|
||||
busy?: ?boolean,
|
||||
checked?: ?boolean | 'mixed',
|
||||
disabled?: ?boolean,
|
||||
expanded?: ?boolean,
|
||||
grabbed?: ?boolean,
|
||||
hidden?: ?boolean,
|
||||
invalid?: ?boolean,
|
||||
modal?: ?boolean,
|
||||
pressed?: ?boolean,
|
||||
readonly?: ?boolean,
|
||||
required?: ?boolean,
|
||||
selected?: ?boolean
|
||||
},
|
||||
accessible?: boolean,
|
||||
children?: any,
|
||||
className?: string,
|
||||
@@ -90,20 +112,9 @@ const ViewPropTypes = {
|
||||
accessibilityComponentType: string,
|
||||
accessibilityLabel: string,
|
||||
accessibilityLiveRegion: oneOf(['assertive', 'none', 'polite']),
|
||||
accessibilityRelationship: object,
|
||||
accessibilityRole: string,
|
||||
accessibilityStates: arrayOf(
|
||||
oneOf([
|
||||
'disabled',
|
||||
'selected',
|
||||
/* web-only */
|
||||
'busy',
|
||||
'checked',
|
||||
'expanded',
|
||||
'grabbed',
|
||||
'invalid',
|
||||
'pressed'
|
||||
])
|
||||
),
|
||||
accessibilityState: object,
|
||||
accessible: bool,
|
||||
children: any,
|
||||
hitSlop: EdgeInsetsPropType,
|
||||
|
||||
+50
@@ -13,3 +13,53 @@ exports[`modules/createDOMProps includes base reset style for browser-styled ele
|
||||
exports[`modules/createDOMProps includes cursor style for pressable roles 1`] = `"css-cursor-18t94o4"`;
|
||||
|
||||
exports[`modules/createDOMProps includes cursor style for pressable roles 2`] = `"css-cursor-18t94o4"`;
|
||||
|
||||
exports[`modules/createDOMProps prop "accessibilityRelationship" values are "id" string 1`] = `
|
||||
Object {
|
||||
"aria-activedescendant": "id",
|
||||
"aria-controls": "id",
|
||||
"aria-describedby": "id",
|
||||
"aria-details": "id",
|
||||
"aria-haspopup": "id",
|
||||
"aria-labelledby": "id",
|
||||
"aria-owns": "id",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`modules/createDOMProps prop "accessibilityRelationship" values are "undefined" 1`] = `Object {}`;
|
||||
|
||||
exports[`modules/createDOMProps prop "accessibilityState" values are "false" 1`] = `
|
||||
Object {
|
||||
"aria-busy": false,
|
||||
"aria-checked": false,
|
||||
"aria-expanded": false,
|
||||
"aria-grabbed": false,
|
||||
"aria-invalid": false,
|
||||
"aria-modal": false,
|
||||
"aria-pressed": false,
|
||||
"aria-readonly": false,
|
||||
"aria-required": false,
|
||||
"aria-selected": false,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`modules/createDOMProps prop "accessibilityState" values are "true" 1`] = `
|
||||
Object {
|
||||
"aria-busy": true,
|
||||
"aria-checked": true,
|
||||
"aria-disabled": true,
|
||||
"aria-expanded": true,
|
||||
"aria-grabbed": true,
|
||||
"aria-hidden": true,
|
||||
"aria-invalid": true,
|
||||
"aria-modal": true,
|
||||
"aria-pressed": true,
|
||||
"aria-readonly": true,
|
||||
"aria-required": true,
|
||||
"aria-selected": true,
|
||||
"disabled": true,
|
||||
"hidden": true,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`modules/createDOMProps prop "accessibilityState" values are "undefined" 1`] = `Object {}`;
|
||||
|
||||
+61
-5
@@ -158,11 +158,67 @@ describe('modules/createDOMProps', () => {
|
||||
expect(props.role).toEqual('button');
|
||||
});
|
||||
|
||||
test('prop "accessibilityStates" becomes ARIA states', () => {
|
||||
const accessibilityStates = ['disabled', 'selected'];
|
||||
const props = createProps({ accessibilityStates });
|
||||
expect(props['aria-disabled']).toEqual(true);
|
||||
expect(props['aria-selected']).toEqual(true);
|
||||
describe('prop "accessibilityState"', () => {
|
||||
function createAccessibilityState(value) {
|
||||
return {
|
||||
busy: value,
|
||||
checked: value,
|
||||
disabled: value,
|
||||
expanded: value,
|
||||
grabbed: value,
|
||||
hidden: value,
|
||||
invalid: value,
|
||||
modal: value,
|
||||
pressed: value,
|
||||
readonly: value,
|
||||
required: value,
|
||||
selected: value
|
||||
};
|
||||
}
|
||||
|
||||
test('values are "undefined"', () => {
|
||||
const accessibilityState = createAccessibilityState(undefined);
|
||||
const props = createProps({ accessibilityState });
|
||||
expect(props).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('values are "false"', () => {
|
||||
const accessibilityState = createAccessibilityState(false);
|
||||
const props = createProps({ accessibilityState });
|
||||
expect(props).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('values are "true"', () => {
|
||||
const accessibilityState = createAccessibilityState(true);
|
||||
const props = createProps({ accessibilityState });
|
||||
expect(props).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('prop "accessibilityRelationship"', () => {
|
||||
function createAccessibilityRelationship(value) {
|
||||
return {
|
||||
activedescendant: value,
|
||||
controls: value,
|
||||
describedby: value,
|
||||
details: value,
|
||||
haspopup: value,
|
||||
labelledby: value,
|
||||
owns: value
|
||||
};
|
||||
}
|
||||
|
||||
test('values are "undefined"', () => {
|
||||
const accessibilityRelationship = createAccessibilityRelationship(undefined);
|
||||
const props = createProps({ accessibilityRelationship });
|
||||
expect(props).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('values are "id" string', () => {
|
||||
const accessibilityRelationship = createAccessibilityRelationship('id');
|
||||
const props = createProps({ accessibilityRelationship });
|
||||
expect(props).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
test('prop "className" is preserved', () => {
|
||||
|
||||
+45
-16
@@ -63,9 +63,11 @@ const createDOMProps = (component, props, styleResolver) => {
|
||||
const {
|
||||
accessibilityLabel,
|
||||
accessibilityLiveRegion,
|
||||
accessibilityStates,
|
||||
accessibilityRelationship,
|
||||
accessibilityState,
|
||||
classList,
|
||||
className: deprecatedClassName,
|
||||
disabled: providedDisabled,
|
||||
importantForAccessibility,
|
||||
nativeID,
|
||||
placeholderTextColor,
|
||||
@@ -79,32 +81,59 @@ const createDOMProps = (component, props, styleResolver) => {
|
||||
...domProps
|
||||
} = props;
|
||||
|
||||
const disabled = AccessibilityUtil.isDisabled(props);
|
||||
const disabled =
|
||||
(accessibilityState != null && accessibilityState.disabled === true) || providedDisabled;
|
||||
const role = AccessibilityUtil.propsToAriaRole(props);
|
||||
|
||||
// GENERAL ACCESSIBILITY
|
||||
if (importantForAccessibility === 'no-hide-descendants') {
|
||||
domProps['aria-hidden'] = true;
|
||||
}
|
||||
if (accessibilityLabel && accessibilityLabel.constructor === String) {
|
||||
// accessibilityLabel
|
||||
if (accessibilityLabel != null) {
|
||||
domProps['aria-label'] = accessibilityLabel;
|
||||
}
|
||||
if (accessibilityLiveRegion && accessibilityLiveRegion.constructor === String) {
|
||||
|
||||
// accessibilityLiveRegion
|
||||
if (accessibilityLiveRegion != null) {
|
||||
domProps['aria-live'] = accessibilityLiveRegion === 'none' ? 'off' : accessibilityLiveRegion;
|
||||
}
|
||||
if (Array.isArray(accessibilityStates)) {
|
||||
for (let i = 0; i < accessibilityStates.length; i += 1) {
|
||||
domProps[`aria-${accessibilityStates[i]}`] = true;
|
||||
|
||||
// accessibilityRelationship
|
||||
if (accessibilityRelationship != null) {
|
||||
for (const prop in accessibilityRelationship) {
|
||||
const value = accessibilityRelationship[prop];
|
||||
if (value != null) {
|
||||
domProps[`aria-${prop}`] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (role && role.constructor === String) {
|
||||
|
||||
// accessibilityRole
|
||||
if (role != null) {
|
||||
domProps.role = role;
|
||||
}
|
||||
|
||||
// DISABLED
|
||||
if (disabled) {
|
||||
domProps['aria-disabled'] = disabled;
|
||||
domProps.disabled = disabled;
|
||||
// accessibilityState
|
||||
if (accessibilityState != null) {
|
||||
for (const prop in accessibilityState) {
|
||||
const value = accessibilityState[prop];
|
||||
if (value != null) {
|
||||
if (prop === 'disabled' || prop === 'hidden') {
|
||||
if (value === true) {
|
||||
domProps[`aria-${prop}`] = value;
|
||||
// also set prop directly to pick up host component behaviour
|
||||
domProps[prop] = value;
|
||||
}
|
||||
} else {
|
||||
domProps[`aria-${prop}`] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// legacy fallbacks
|
||||
if (importantForAccessibility === 'no-hide-descendants') {
|
||||
domProps['aria-hidden'] = true;
|
||||
}
|
||||
if (disabled === true) {
|
||||
domProps['aria-disabled'] = true;
|
||||
domProps.disabled = true;
|
||||
}
|
||||
|
||||
// FOCUS
|
||||
|
||||
Reference in New Issue
Block a user