mirror of
https://github.com/zhigang1992/react-native-web.git
synced 2026-01-12 22:51:09 +08:00
[add] mark focusable DOM nodes with 'data-focusable'
Focus-based UIs can use out-of-tree focus algorithms to manage focus using remote control devices. This patch marks DOM nodes that React Native considers "focusable". Close #827
This commit is contained in:
@@ -11,6 +11,7 @@ exports[`components/Picker prop "children" items 1`] = `
|
||||
exports[`components/Picker prop "children" renders items 1`] = `
|
||||
<select
|
||||
className="rn-fontFamily-14xgk7a rn-fontSize-7cikom rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw"
|
||||
data-focusable={true}
|
||||
onChange={[Function]}
|
||||
>
|
||||
<PickerItem
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
exports[`components/Text prop "onPress" 1`] = `
|
||||
<div
|
||||
className="rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-homxoj rn-cursor-1loqt21 rn-display-1471scf rn-fontFamily-14xgk7a rn-fontSize-1b43r93 rn-fontStyle-o11vmf rn-fontVariant-ebii48 rn-fontWeight-gul640 rn-lineHeight-t9a87b rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-textDecoration-bauka4 rn-whiteSpace-q42fyq rn-wordWrap-qvutc0"
|
||||
data-focusable={true}
|
||||
dir="auto"
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import propsToTabIndex from '../propsToTabIndex';
|
||||
|
||||
describe('modules/AccessibilityUtil/propsToTabIndex', () => {
|
||||
test('with no accessibility props', () => {
|
||||
expect(propsToTabIndex({})).toBeUndefined();
|
||||
});
|
||||
|
||||
describe('"accessibilityRole" of "link"', () => {
|
||||
const accessibilityRole = 'link';
|
||||
|
||||
test('default case', () => {
|
||||
expect(propsToTabIndex({ accessibilityRole })).toBeUndefined();
|
||||
});
|
||||
|
||||
test('when "accessible" is true', () => {
|
||||
expect(propsToTabIndex({ accessibilityRole, accessible: true })).toBeUndefined();
|
||||
});
|
||||
|
||||
test('when "accessible" is false', () => {
|
||||
expect(propsToTabIndex({ accessibilityRole, accessible: false })).toEqual('-1');
|
||||
});
|
||||
|
||||
test('when "disabled" is true', () => {
|
||||
expect(propsToTabIndex({ accessibilityRole, disabled: true })).toEqual('-1');
|
||||
expect(propsToTabIndex({ accessibilityRole, 'aria-disabled': true })).toEqual('-1');
|
||||
});
|
||||
|
||||
test('when "disabled" is false', () => {
|
||||
expect(propsToTabIndex({ accessibilityRole, disabled: false })).toBeUndefined();
|
||||
expect(propsToTabIndex({ accessibilityRole, 'aria-disabled': false })).toBeUndefined();
|
||||
});
|
||||
|
||||
test('when "importantForAccessibility" is "no"', () => {
|
||||
expect(propsToTabIndex({ accessibilityRole, importantForAccessibility: 'no' })).toEqual('-1');
|
||||
});
|
||||
|
||||
test('when "importantForAccessibility" is "no-hide-descendants"', () => {
|
||||
expect(
|
||||
propsToTabIndex({
|
||||
accessibilityRole,
|
||||
importantForAccessibility: 'no-hide-descendants'
|
||||
})
|
||||
).toEqual('-1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('"accessibilityRole" of "button"', () => {
|
||||
const accessibilityRole = 'button';
|
||||
|
||||
test('default case', () => {
|
||||
expect(propsToTabIndex({ accessibilityRole })).toEqual('0');
|
||||
});
|
||||
|
||||
test('when "accessible" is true', () => {
|
||||
expect(propsToTabIndex({ accessibilityRole, accessible: true })).toEqual('0');
|
||||
});
|
||||
|
||||
test('when "accessible" is false', () => {
|
||||
expect(propsToTabIndex({ accessibilityRole, accessible: false })).toBeUndefined();
|
||||
});
|
||||
|
||||
test('when "disabled" is true', () => {
|
||||
expect(propsToTabIndex({ accessibilityRole, disabled: true })).toBeUndefined();
|
||||
expect(propsToTabIndex({ accessibilityRole, 'aria-disabled': true })).toBeUndefined();
|
||||
});
|
||||
|
||||
test('when "disabled" is false', () => {
|
||||
expect(propsToTabIndex({ accessibilityRole, disabled: false })).toEqual('0');
|
||||
expect(propsToTabIndex({ accessibilityRole, 'aria-disabled': false })).toEqual('0');
|
||||
});
|
||||
|
||||
test('when "importantForAccessibility" is "no"', () => {
|
||||
expect(
|
||||
propsToTabIndex({ accessibilityRole, importantForAccessibility: 'no' })
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
test('when "importantForAccessibility" is "no-hide-descendants"', () => {
|
||||
expect(
|
||||
propsToTabIndex({
|
||||
accessibilityRole,
|
||||
importantForAccessibility: 'no-hide-descendants'
|
||||
})
|
||||
).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('with unfocusable accessibilityRole', () => {
|
||||
test('default case', () => {
|
||||
expect(propsToTabIndex({})).toBeUndefined();
|
||||
});
|
||||
|
||||
test('when "accessible" is true', () => {
|
||||
expect(propsToTabIndex({ accessible: true })).toEqual('0');
|
||||
});
|
||||
|
||||
test('when "accessible" is false', () => {
|
||||
expect(propsToTabIndex({ accessible: false })).toBeUndefined();
|
||||
});
|
||||
|
||||
test('when "importantForAccessibility" is "no"', () => {
|
||||
expect(propsToTabIndex({ importantForAccessibility: 'no' })).toBeUndefined();
|
||||
expect(
|
||||
propsToTabIndex({ accessible: true, importantForAccessibility: 'no' })
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
test('when "importantForAccessibility" is "no-hide-descendants"', () => {
|
||||
expect(
|
||||
propsToTabIndex({ accessible: true, importantForAccessibility: 'no-hide-descendants' })
|
||||
).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -10,13 +10,11 @@
|
||||
import isDisabled from './isDisabled';
|
||||
import propsToAccessibilityComponent from './propsToAccessibilityComponent';
|
||||
import propsToAriaRole from './propsToAriaRole';
|
||||
import propsToTabIndex from './propsToTabIndex';
|
||||
|
||||
const AccessibilityUtil = {
|
||||
isDisabled,
|
||||
propsToAccessibilityComponent,
|
||||
propsToAriaRole,
|
||||
propsToTabIndex
|
||||
propsToAriaRole
|
||||
};
|
||||
|
||||
export default AccessibilityUtil;
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Nicolas Gallagher.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import isDisabled from './isDisabled';
|
||||
import propsToAriaRole from './propsToAriaRole';
|
||||
|
||||
const propsToTabIndex = (props: Object) => {
|
||||
const role = propsToAriaRole(props);
|
||||
const focusable =
|
||||
!isDisabled(props) &&
|
||||
props.importantForAccessibility !== 'no' &&
|
||||
props.importantForAccessibility !== 'no-hide-descendants';
|
||||
|
||||
// Assume that 'link' is focusable by default (uses <a>).
|
||||
// Assume that 'button' is not (uses <div role='button'>) but must be treated as such.
|
||||
if (role === 'link') {
|
||||
if (props.accessible === false || !focusable) {
|
||||
return '-1';
|
||||
}
|
||||
} else if (role === 'button') {
|
||||
if (props.accessible !== false && focusable) {
|
||||
return '0';
|
||||
}
|
||||
} else {
|
||||
if (props.accessible === true && focusable) {
|
||||
return '0';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default propsToTabIndex;
|
||||
@@ -2,28 +2,10 @@
|
||||
|
||||
exports[`modules/createDOMProps includes "rel" values for "a" elements (to securely open external links) 1`] = `" noopener noreferrer"`;
|
||||
|
||||
exports[`modules/createDOMProps includes cursor style for "button" role 1`] = `
|
||||
Object {
|
||||
"className": "rn-cursor-1loqt21",
|
||||
"role": "button",
|
||||
"tabIndex": "0",
|
||||
}
|
||||
`;
|
||||
exports[`modules/createDOMProps includes cursor style for "button" role 1`] = `"rn-cursor-1loqt21"`;
|
||||
|
||||
exports[`modules/createDOMProps includes reset styles for "a" elements 1`] = `
|
||||
Object {
|
||||
"className": "rn-backgroundColor-wib322 rn-color-homxoj rn-textDecoration-bauka4",
|
||||
}
|
||||
`;
|
||||
exports[`modules/createDOMProps includes reset styles for "a" elements 1`] = `"rn-backgroundColor-wib322 rn-color-homxoj rn-textDecoration-bauka4"`;
|
||||
|
||||
exports[`modules/createDOMProps includes reset styles for "button" elements 1`] = `
|
||||
Object {
|
||||
"className": "rn-appearance-30o5oe rn-backgroundColor-wib322 rn-color-homxoj rn-fontFamily-poiln3 rn-fontSize-7cikom rn-fontStyle-o11vmf rn-fontVariant-ebii48 rn-fontWeight-gul640 rn-lineHeight-t9a87b rn-textAlign-1ttztb7",
|
||||
}
|
||||
`;
|
||||
exports[`modules/createDOMProps includes reset styles for "button" elements 1`] = `"rn-appearance-30o5oe rn-backgroundColor-wib322 rn-color-homxoj rn-fontFamily-poiln3 rn-fontSize-7cikom rn-fontStyle-o11vmf rn-fontVariant-ebii48 rn-fontWeight-gul640 rn-lineHeight-t9a87b rn-textAlign-1ttztb7"`;
|
||||
|
||||
exports[`modules/createDOMProps includes reset styles for "ul" elements 1`] = `
|
||||
Object {
|
||||
"className": "rn-listStyle-1ebb2ja",
|
||||
}
|
||||
`;
|
||||
exports[`modules/createDOMProps includes reset styles for "ul" elements 1`] = `"rn-listStyle-1ebb2ja"`;
|
||||
|
||||
@@ -5,6 +5,147 @@ import createDOMProps from '..';
|
||||
const createProps = props => createDOMProps(null, props);
|
||||
|
||||
describe('modules/createDOMProps', () => {
|
||||
describe('focus-related accessibility attributes', () => {
|
||||
test('with no accessibility props', () => {
|
||||
expect(createProps({})).toEqual({});
|
||||
});
|
||||
|
||||
describe('"accessibilityRole" of "link"', () => {
|
||||
const accessibilityRole = 'link';
|
||||
|
||||
test('default case', () => {
|
||||
expect(createProps({ accessibilityRole })).toEqual(
|
||||
expect.objectContaining({ 'data-focusable': true })
|
||||
);
|
||||
});
|
||||
|
||||
test('when "accessible" is true', () => {
|
||||
expect(createProps({ accessibilityRole, accessible: true })).toEqual(
|
||||
expect.objectContaining({ 'data-focusable': true })
|
||||
);
|
||||
});
|
||||
|
||||
test('when "accessible" is false', () => {
|
||||
expect(createProps({ accessibilityRole, accessible: false })).toEqual(
|
||||
expect.objectContaining({ tabIndex: '-1' })
|
||||
);
|
||||
});
|
||||
|
||||
test('when "disabled" is true', () => {
|
||||
expect(createProps({ accessibilityRole, disabled: true })).toEqual(
|
||||
expect.objectContaining({ 'aria-disabled': true, disabled: true, tabIndex: '-1' })
|
||||
);
|
||||
expect(createProps({ accessibilityRole, 'aria-disabled': true })).toEqual(
|
||||
expect.objectContaining({ 'aria-disabled': true, disabled: true, tabIndex: '-1' })
|
||||
);
|
||||
});
|
||||
|
||||
test('when "disabled" is false', () => {
|
||||
expect(createProps({ accessibilityRole, disabled: false })).toEqual(
|
||||
expect.objectContaining({ 'data-focusable': true })
|
||||
);
|
||||
expect(createProps({ accessibilityRole, 'aria-disabled': false })).toEqual(
|
||||
expect.objectContaining({ 'data-focusable': true })
|
||||
);
|
||||
});
|
||||
|
||||
test('when "importantForAccessibility" is "no"', () => {
|
||||
expect(createProps({ accessibilityRole, importantForAccessibility: 'no' })).toEqual(
|
||||
expect.objectContaining({ tabIndex: '-1' })
|
||||
);
|
||||
});
|
||||
|
||||
test('when "importantForAccessibility" is "no-hide-descendants"', () => {
|
||||
expect(
|
||||
createProps({
|
||||
accessibilityRole,
|
||||
importantForAccessibility: 'no-hide-descendants'
|
||||
})
|
||||
).toEqual(expect.objectContaining({ tabIndex: '-1' }));
|
||||
});
|
||||
});
|
||||
|
||||
describe('"accessibilityRole" of "button"', () => {
|
||||
const accessibilityRole = 'button';
|
||||
|
||||
test('default case', () => {
|
||||
expect(createProps({ accessibilityRole })).toEqual(
|
||||
expect.objectContaining({ 'data-focusable': true, tabIndex: '0' })
|
||||
);
|
||||
});
|
||||
|
||||
test('when "accessible" is true', () => {
|
||||
expect(createProps({ accessibilityRole, accessible: true })).toEqual(
|
||||
expect.objectContaining({ 'data-focusable': true, tabIndex: '0' })
|
||||
);
|
||||
});
|
||||
|
||||
test('when "accessible" is false', () => {
|
||||
expect(createProps({ accessibilityRole, accessible: false })).not.toEqual(
|
||||
expect.objectContaining({ 'data-focusable': true, tabIndex: '0' })
|
||||
);
|
||||
});
|
||||
|
||||
test('when "disabled" is true', () => {
|
||||
expect(createProps({ accessibilityRole, disabled: true })).toEqual(
|
||||
expect.objectContaining({ 'aria-disabled': true, disabled: true })
|
||||
);
|
||||
expect(createProps({ accessibilityRole, 'aria-disabled': true })).toEqual(
|
||||
expect.objectContaining({ 'aria-disabled': true, disabled: true })
|
||||
);
|
||||
});
|
||||
|
||||
test('when "disabled" is false', () => {
|
||||
expect(createProps({ accessibilityRole, disabled: false })).toEqual(
|
||||
expect.objectContaining({ 'data-focusable': true, tabIndex: '0' })
|
||||
);
|
||||
expect(createProps({ accessibilityRole, 'aria-disabled': false })).toEqual(
|
||||
expect.objectContaining({ 'data-focusable': true, tabIndex: '0' })
|
||||
);
|
||||
});
|
||||
|
||||
test('when "importantForAccessibility" is "no"', () => {
|
||||
expect(createProps({ accessibilityRole, importantForAccessibility: 'no' })).not.toEqual(
|
||||
expect.objectContaining({ 'data-focusable': true, tabIndex: '0' })
|
||||
);
|
||||
});
|
||||
|
||||
test('when "importantForAccessibility" is "no-hide-descendants"', () => {
|
||||
expect(
|
||||
createProps({
|
||||
accessibilityRole,
|
||||
importantForAccessibility: 'no-hide-descendants'
|
||||
})
|
||||
).not.toEqual(expect.objectContaining({ 'data-focusable': true, tabIndex: '0' }));
|
||||
});
|
||||
});
|
||||
|
||||
describe('with unfocusable accessibilityRole', () => {
|
||||
test('when "accessible" is true', () => {
|
||||
expect(createProps({ accessible: true })).toEqual(
|
||||
expect.objectContaining({ 'data-focusable': true, tabIndex: '0' })
|
||||
);
|
||||
});
|
||||
|
||||
test('when "accessible" is false', () => {
|
||||
expect(createProps({ accessible: false })).toEqual({});
|
||||
});
|
||||
|
||||
test('when "importantForAccessibility" is "no"', () => {
|
||||
expect(createProps({ importantForAccessibility: 'no' })).toEqual({});
|
||||
expect(createProps({ accessible: true, importantForAccessibility: 'no' })).not.toEqual(
|
||||
expect.objectContaining({ 'data-focusable': true, tabIndex: '0' })
|
||||
);
|
||||
});
|
||||
|
||||
test('when "importantForAccessibility" is "no-hide-descendants"', () => {
|
||||
expect(
|
||||
createProps({ accessible: true, importantForAccessibility: 'no-hide-descendants' })
|
||||
).not.toEqual(expect.objectContaining({ 'data-focusable': true, tabIndex: '0' }));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('prop "accessibilityLabel" becomes "aria-label"', () => {
|
||||
const accessibilityLabel = 'accessibilityLabel';
|
||||
const props = createProps({ accessibilityLabel });
|
||||
@@ -42,33 +183,28 @@ describe('modules/createDOMProps', () => {
|
||||
expect(props['data-testid']).toEqual(testID);
|
||||
});
|
||||
|
||||
test('includes reset styles for "a" elements', () => {
|
||||
const props = createDOMProps('a');
|
||||
expect(props).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('includes "rel" values for "a" elements (to securely open external links)', () => {
|
||||
const props = createDOMProps('a', { target: '_blank' });
|
||||
expect(props.rel).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('includes reset styles for "a" elements', () => {
|
||||
const props = createDOMProps('a');
|
||||
expect(props.className).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('includes reset styles for "button" elements', () => {
|
||||
const props = createDOMProps('button');
|
||||
expect(props).toMatchSnapshot();
|
||||
expect(props.className).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('includes cursor style for "button" role', () => {
|
||||
const props = createDOMProps('span', { accessibilityRole: 'button' });
|
||||
expect(props).toMatchSnapshot();
|
||||
expect(props.className).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('includes reset styles for "ul" elements', () => {
|
||||
const props = createDOMProps('ul');
|
||||
expect(props).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('includes tabIndex when needed', () => {
|
||||
const props = createProps({ accessible: true });
|
||||
expect(props.tabIndex).toBeDefined();
|
||||
expect(props.className).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -90,24 +90,10 @@ const createDOMProps = (component, props, styleResolver) => {
|
||||
...domProps
|
||||
} = props;
|
||||
|
||||
const isDisabled = AccessibilityUtil.isDisabled(props);
|
||||
const disabled = AccessibilityUtil.isDisabled(props);
|
||||
const role = AccessibilityUtil.propsToAriaRole(props);
|
||||
const tabIndex = AccessibilityUtil.propsToTabIndex(props);
|
||||
const reactNativeStyle = [
|
||||
component === 'a' && resetStyles.link,
|
||||
component === 'button' && resetStyles.button,
|
||||
role === 'heading' && resetStyles.heading,
|
||||
component === 'ul' && resetStyles.list,
|
||||
role === 'button' && !isDisabled && resetStyles.ariaButton,
|
||||
pointerEvents && pointerEventsStyles[pointerEvents],
|
||||
providedStyle,
|
||||
placeholderTextColor && { placeholderTextColor }
|
||||
];
|
||||
const { className, style } = styleResolver(reactNativeStyle);
|
||||
|
||||
if (isDisabled) {
|
||||
domProps['aria-disabled'] = true;
|
||||
}
|
||||
// GENERAL ACCESSIBILITY
|
||||
if (importantForAccessibility === 'no-hide-descendants') {
|
||||
domProps['aria-hidden'] = true;
|
||||
}
|
||||
@@ -117,20 +103,70 @@ const createDOMProps = (component, props, styleResolver) => {
|
||||
if (accessibilityLiveRegion && accessibilityLiveRegion.constructor === String) {
|
||||
domProps['aria-live'] = accessibilityLiveRegion === 'none' ? 'off' : accessibilityLiveRegion;
|
||||
}
|
||||
if (className && className.constructor === String) {
|
||||
domProps.className = domProps.className ? `${domProps.className} ${className}` : className;
|
||||
}
|
||||
if (component === 'a' && domProps.target === '_blank') {
|
||||
domProps.rel = `${domProps.rel || ''} noopener noreferrer`;
|
||||
}
|
||||
if (role && role.constructor === String && role !== 'label') {
|
||||
domProps.role = role;
|
||||
}
|
||||
|
||||
// DISABLED
|
||||
if (disabled) {
|
||||
domProps['aria-disabled'] = disabled;
|
||||
domProps.disabled = disabled;
|
||||
}
|
||||
|
||||
// FOCUS
|
||||
// Assume that 'link' is focusable by default (uses <a>).
|
||||
// Assume that 'button' is not (uses <div role='button'>) but must be treated as such.
|
||||
const focusable =
|
||||
!disabled &&
|
||||
importantForAccessibility !== 'no' &&
|
||||
importantForAccessibility !== 'no-hide-descendants';
|
||||
if (
|
||||
role === 'link' ||
|
||||
component === 'input' ||
|
||||
component === 'select' ||
|
||||
component === 'textarea'
|
||||
) {
|
||||
if (accessible === false || !focusable) {
|
||||
domProps.tabIndex = '-1';
|
||||
} else {
|
||||
domProps['data-focusable'] = true;
|
||||
}
|
||||
} else if (role === 'button' || role === 'textbox') {
|
||||
if (accessible !== false && focusable) {
|
||||
domProps['data-focusable'] = true;
|
||||
domProps.tabIndex = '0';
|
||||
}
|
||||
} else {
|
||||
if (accessible === true && focusable) {
|
||||
domProps['data-focusable'] = true;
|
||||
domProps.tabIndex = '0';
|
||||
}
|
||||
}
|
||||
|
||||
// STYLE
|
||||
// Resolve React Native styles to optimized browser equivalent
|
||||
const reactNativeStyle = [
|
||||
component === 'a' && resetStyles.link,
|
||||
component === 'button' && resetStyles.button,
|
||||
role === 'heading' && resetStyles.heading,
|
||||
component === 'ul' && resetStyles.list,
|
||||
role === 'button' && !disabled && resetStyles.ariaButton,
|
||||
pointerEvents && pointerEventsStyles[pointerEvents],
|
||||
providedStyle,
|
||||
placeholderTextColor && { placeholderTextColor }
|
||||
];
|
||||
const { className, style } = styleResolver(reactNativeStyle);
|
||||
if (className && className.constructor === String) {
|
||||
domProps.className = props.className ? `${props.className} ${className}` : className;
|
||||
}
|
||||
if (style) {
|
||||
domProps.style = style;
|
||||
}
|
||||
if (tabIndex) {
|
||||
domProps.tabIndex = tabIndex;
|
||||
|
||||
// OTHER
|
||||
// Link security and automation test ids
|
||||
if (component === 'a' && domProps.target === '_blank') {
|
||||
domProps.rel = `${domProps.rel || ''} noopener noreferrer`;
|
||||
}
|
||||
if (testID && testID.constructor === String) {
|
||||
domProps['data-testid'] = testID;
|
||||
|
||||
@@ -114,6 +114,18 @@ value of `no` will remove a focusable element from the tab flow, and a value of
|
||||
`no-hide-descendants` will also hide the entire subtree from assistive
|
||||
technologies (this is implemented using `aria-hidden`).
|
||||
|
||||
### Spatial navigation
|
||||
|
||||
Focus-based web UIs, e.g., for TVs and Game Consoles can implement [TV remote
|
||||
control navigation](https://developer.mozilla.org/en-US/docs/Mozilla/Firefox_OS_for_TV/TV_remote_control_navigation)
|
||||
outside of React using existing directional-focus libraries. Every DOM element
|
||||
that React Native considers focusable can be matched by the attribute
|
||||
`data-focusable="true"`.
|
||||
|
||||
```js
|
||||
const focusableElements = document.querySelectorAll('[data-focusable="true"]');
|
||||
```
|
||||
|
||||
### Other
|
||||
|
||||
Other ARIA properties can be set via [direct
|
||||
|
||||
@@ -9,7 +9,7 @@ import { StyleSheet, TextInput, TouchableWithoutFeedback, View } from 'react-nat
|
||||
export default class TouchableWrapper extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<TouchableWithoutFeedback onPress={this._handlePress}>
|
||||
<TouchableWithoutFeedback importantForAccessibility="no" onPress={this._handlePress}>
|
||||
<View style={styles.container}>
|
||||
<TextInput multiline={false} ref={this._setRef} style={helperStyles.textinput} />
|
||||
</View>
|
||||
|
||||
Reference in New Issue
Block a user