mirror of
https://github.com/zoriya/react-native-web.git
synced 2026-08-15 18:45:06 +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', () => {
|
describe('disabled', () => {
|
||||||
test('when "false" a default checkbox is rendered', () => {
|
test('when "false" a default checkbox is rendered', () => {
|
||||||
const component = shallow(<CheckBox />);
|
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', () => {
|
test('when "true" a disabled checkbox is rendered', () => {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ describe('components/Switch', () => {
|
|||||||
describe('disabled', () => {
|
describe('disabled', () => {
|
||||||
test('when "false" a default checkbox is rendered', () => {
|
test('when "false" a default checkbox is rendered', () => {
|
||||||
const component = shallow(<Switch />);
|
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', () => {
|
test('when "true" a disabled checkbox is rendered', () => {
|
||||||
|
|||||||
@@ -10,11 +10,12 @@
|
|||||||
|
|
||||||
import StyleSheetPropType from '../../modules/StyleSheetPropType';
|
import StyleSheetPropType from '../../modules/StyleSheetPropType';
|
||||||
import TextStylePropTypes from './TextStylePropTypes';
|
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 = {
|
const TextPropTypes = {
|
||||||
accessibilityLabel: string,
|
accessibilityLabel: string,
|
||||||
accessibilityLiveRegion: oneOf(['assertive', 'none', 'polite']),
|
accessibilityLiveRegion: oneOf(['assertive', 'none', 'polite']),
|
||||||
|
accessibilityRelationship: object,
|
||||||
accessibilityRole: oneOf([
|
accessibilityRole: oneOf([
|
||||||
'button',
|
'button',
|
||||||
'header',
|
'header',
|
||||||
@@ -26,6 +27,7 @@ const TextPropTypes = {
|
|||||||
'text'
|
'text'
|
||||||
]),
|
]),
|
||||||
accessible: bool,
|
accessible: bool,
|
||||||
|
accessibilityState: object,
|
||||||
children: any,
|
children: any,
|
||||||
importantForAccessibility: oneOf(['auto', 'no', 'no-hide-descendants', 'yes']),
|
importantForAccessibility: oneOf(['auto', 'no', 'no-hide-descendants', 'yes']),
|
||||||
maxFontSizeMultiplier: number,
|
maxFontSizeMultiplier: number,
|
||||||
|
|||||||
+26
-15
@@ -11,7 +11,7 @@
|
|||||||
import EdgeInsetsPropType, { type EdgeInsetsProp } from '../EdgeInsetsPropType';
|
import EdgeInsetsPropType, { type EdgeInsetsProp } from '../EdgeInsetsPropType';
|
||||||
import StyleSheetPropType from '../../modules/StyleSheetPropType';
|
import StyleSheetPropType from '../../modules/StyleSheetPropType';
|
||||||
import ViewStylePropTypes from './ViewStylePropTypes';
|
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';
|
import { type StyleObj } from '../StyleSheet/StyleSheetTypes';
|
||||||
|
|
||||||
const stylePropType = StyleSheetPropType(ViewStylePropTypes);
|
const stylePropType = StyleSheetPropType(ViewStylePropTypes);
|
||||||
@@ -33,8 +33,30 @@ export type ViewProps = {
|
|||||||
accessibilityComponentType?: string,
|
accessibilityComponentType?: string,
|
||||||
accessibilityLabel?: string,
|
accessibilityLabel?: string,
|
||||||
accessibilityLiveRegion?: 'none' | 'polite' | 'assertive',
|
accessibilityLiveRegion?: 'none' | 'polite' | 'assertive',
|
||||||
|
accessibilityRelationship?: {
|
||||||
|
activedescendant?: ?string,
|
||||||
|
controls?: ?string,
|
||||||
|
describedby?: ?string,
|
||||||
|
details?: ?string,
|
||||||
|
haspopup?: ?string,
|
||||||
|
labelledby?: ?string,
|
||||||
|
owns?: ?string
|
||||||
|
},
|
||||||
accessibilityRole?: 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,
|
accessible?: boolean,
|
||||||
children?: any,
|
children?: any,
|
||||||
className?: string,
|
className?: string,
|
||||||
@@ -90,20 +112,9 @@ const ViewPropTypes = {
|
|||||||
accessibilityComponentType: string,
|
accessibilityComponentType: string,
|
||||||
accessibilityLabel: string,
|
accessibilityLabel: string,
|
||||||
accessibilityLiveRegion: oneOf(['assertive', 'none', 'polite']),
|
accessibilityLiveRegion: oneOf(['assertive', 'none', 'polite']),
|
||||||
|
accessibilityRelationship: object,
|
||||||
accessibilityRole: string,
|
accessibilityRole: string,
|
||||||
accessibilityStates: arrayOf(
|
accessibilityState: object,
|
||||||
oneOf([
|
|
||||||
'disabled',
|
|
||||||
'selected',
|
|
||||||
/* web-only */
|
|
||||||
'busy',
|
|
||||||
'checked',
|
|
||||||
'expanded',
|
|
||||||
'grabbed',
|
|
||||||
'invalid',
|
|
||||||
'pressed'
|
|
||||||
])
|
|
||||||
),
|
|
||||||
accessible: bool,
|
accessible: bool,
|
||||||
children: any,
|
children: any,
|
||||||
hitSlop: EdgeInsetsPropType,
|
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 1`] = `"css-cursor-18t94o4"`;
|
||||||
|
|
||||||
exports[`modules/createDOMProps includes cursor style for pressable roles 2`] = `"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');
|
expect(props.role).toEqual('button');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('prop "accessibilityStates" becomes ARIA states', () => {
|
describe('prop "accessibilityState"', () => {
|
||||||
const accessibilityStates = ['disabled', 'selected'];
|
function createAccessibilityState(value) {
|
||||||
const props = createProps({ accessibilityStates });
|
return {
|
||||||
expect(props['aria-disabled']).toEqual(true);
|
busy: value,
|
||||||
expect(props['aria-selected']).toEqual(true);
|
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', () => {
|
test('prop "className" is preserved', () => {
|
||||||
|
|||||||
+45
-16
@@ -63,9 +63,11 @@ const createDOMProps = (component, props, styleResolver) => {
|
|||||||
const {
|
const {
|
||||||
accessibilityLabel,
|
accessibilityLabel,
|
||||||
accessibilityLiveRegion,
|
accessibilityLiveRegion,
|
||||||
accessibilityStates,
|
accessibilityRelationship,
|
||||||
|
accessibilityState,
|
||||||
classList,
|
classList,
|
||||||
className: deprecatedClassName,
|
className: deprecatedClassName,
|
||||||
|
disabled: providedDisabled,
|
||||||
importantForAccessibility,
|
importantForAccessibility,
|
||||||
nativeID,
|
nativeID,
|
||||||
placeholderTextColor,
|
placeholderTextColor,
|
||||||
@@ -79,32 +81,59 @@ const createDOMProps = (component, props, styleResolver) => {
|
|||||||
...domProps
|
...domProps
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const disabled = AccessibilityUtil.isDisabled(props);
|
const disabled =
|
||||||
|
(accessibilityState != null && accessibilityState.disabled === true) || providedDisabled;
|
||||||
const role = AccessibilityUtil.propsToAriaRole(props);
|
const role = AccessibilityUtil.propsToAriaRole(props);
|
||||||
|
|
||||||
// GENERAL ACCESSIBILITY
|
// accessibilityLabel
|
||||||
if (importantForAccessibility === 'no-hide-descendants') {
|
if (accessibilityLabel != null) {
|
||||||
domProps['aria-hidden'] = true;
|
|
||||||
}
|
|
||||||
if (accessibilityLabel && accessibilityLabel.constructor === String) {
|
|
||||||
domProps['aria-label'] = accessibilityLabel;
|
domProps['aria-label'] = accessibilityLabel;
|
||||||
}
|
}
|
||||||
if (accessibilityLiveRegion && accessibilityLiveRegion.constructor === String) {
|
|
||||||
|
// accessibilityLiveRegion
|
||||||
|
if (accessibilityLiveRegion != null) {
|
||||||
domProps['aria-live'] = accessibilityLiveRegion === 'none' ? 'off' : accessibilityLiveRegion;
|
domProps['aria-live'] = accessibilityLiveRegion === 'none' ? 'off' : accessibilityLiveRegion;
|
||||||
}
|
}
|
||||||
if (Array.isArray(accessibilityStates)) {
|
|
||||||
for (let i = 0; i < accessibilityStates.length; i += 1) {
|
// accessibilityRelationship
|
||||||
domProps[`aria-${accessibilityStates[i]}`] = true;
|
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;
|
domProps.role = role;
|
||||||
}
|
}
|
||||||
|
|
||||||
// DISABLED
|
// accessibilityState
|
||||||
if (disabled) {
|
if (accessibilityState != null) {
|
||||||
domProps['aria-disabled'] = disabled;
|
for (const prop in accessibilityState) {
|
||||||
domProps.disabled = disabled;
|
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
|
// FOCUS
|
||||||
|
|||||||
Reference in New Issue
Block a user