mirror of
https://github.com/zoriya/react-native-web.git
synced 2026-06-04 02:56:42 +00:00
[fix] ref.focus() should focus any element type
Ensure that programmatic focus can be moved to any element. Each instance of a primitive component type (e.g., `View`, `Text`, etc.) includes a `focus` method. However, on the web only certain elements can receive programmatic focus by default: those that can also receive keyboard focus, e.g., `a`, `button`, `input`, etc. All other element types must set `tabIndex="-1"` in order to be programmatically focusable without also being focusable via keyboard or mouse. Fix #1099
This commit is contained in:
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
import UIManager from '..';
|
import UIManager from '..';
|
||||||
|
|
||||||
const createStyledNode = (style = {}) => {
|
const createStyledNode = (name = 'div', style = {}) => {
|
||||||
const root = document.createElement('div');
|
const root = document.createElement(name);
|
||||||
Object.keys(style).forEach(prop => {
|
Object.keys(style).forEach(prop => {
|
||||||
root.style[prop] = style[prop];
|
root.style[prop] = style[prop];
|
||||||
});
|
});
|
||||||
@@ -18,6 +18,29 @@ const componentStub = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
describe('apis/UIManager', () => {
|
describe('apis/UIManager', () => {
|
||||||
|
describe('focus', () => {
|
||||||
|
test('sets tabIndex="-1" on elements not programmatically focusable by default', () => {
|
||||||
|
const node = createStyledNode();
|
||||||
|
UIManager.focus(node);
|
||||||
|
expect(node.getAttribute('tabIndex')).toEqual('-1');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('doesn\'t set tabIndex="-1" on elements with an existing tabIndex', () => {
|
||||||
|
const node = createStyledNode();
|
||||||
|
node.tabIndex = 0;
|
||||||
|
UIManager.focus(node);
|
||||||
|
expect(node.getAttribute('tabIndex')).toEqual('0');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('doesn\'t set tabIndex="-1" on elements focusable by default', () => {
|
||||||
|
['a', 'input', 'select', 'textarea'].forEach(name => {
|
||||||
|
const node = createStyledNode(name);
|
||||||
|
UIManager.focus(node);
|
||||||
|
expect(node.getAttribute('tabIndex')).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('updateView', () => {
|
describe('updateView', () => {
|
||||||
test('supports className alias for class', () => {
|
test('supports className alias for class', () => {
|
||||||
const node = createStyledNode();
|
const node = createStyledNode();
|
||||||
@@ -27,7 +50,7 @@ describe('apis/UIManager', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('adds correct DOM styles to existing style', () => {
|
test('adds correct DOM styles to existing style', () => {
|
||||||
const node = createStyledNode({ color: 'red' });
|
const node = createStyledNode('div', { color: 'red' });
|
||||||
const props = { style: { marginTop: 0, marginBottom: 0, opacity: 0 } };
|
const props = { style: { marginTop: 0, marginBottom: 0, opacity: 0 } };
|
||||||
UIManager.updateView(node, props, componentStub);
|
UIManager.updateView(node, props, componentStub);
|
||||||
expect(node.getAttribute('style')).toEqual(
|
expect(node.getAttribute('style')).toEqual(
|
||||||
@@ -36,7 +59,7 @@ describe('apis/UIManager', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('replaces input and textarea text', () => {
|
test('replaces input and textarea text', () => {
|
||||||
const node = createStyledNode();
|
const node = createStyledNode('textarea');
|
||||||
node.value = 'initial';
|
node.value = 'initial';
|
||||||
const textProp = { text: 'expected-text' };
|
const textProp = { text: 'expected-text' };
|
||||||
const valueProp = { value: 'expected-value' };
|
const valueProp = { value: 'expected-value' };
|
||||||
|
|||||||
@@ -37,6 +37,13 @@ const measureLayout = (node, relativeToNativeNode, callback) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const focusableElements = {
|
||||||
|
A: true,
|
||||||
|
INPUT: true,
|
||||||
|
SELECT: true,
|
||||||
|
TEXTAREA: true
|
||||||
|
};
|
||||||
|
|
||||||
const UIManager = {
|
const UIManager = {
|
||||||
blur(node) {
|
blur(node) {
|
||||||
try {
|
try {
|
||||||
@@ -46,6 +53,13 @@ const UIManager = {
|
|||||||
|
|
||||||
focus(node) {
|
focus(node) {
|
||||||
try {
|
try {
|
||||||
|
const name = node.nodeName;
|
||||||
|
// A tabIndex of -1 allows element to be programmatically focused but
|
||||||
|
// prevents keyboard focus, so we don't want to set the value on elements
|
||||||
|
// that support keyboard focus by default.
|
||||||
|
if (node.getAttribute('tabIndex') == null && focusableElements[name] == null) {
|
||||||
|
node.setAttribute('tabIndex', '-1');
|
||||||
|
}
|
||||||
node.focus();
|
node.focus();
|
||||||
} catch (err) {}
|
} catch (err) {}
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user