305 lines
16 KiB
JavaScript
305 lines
16 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
var React = require("react");
|
|
var ReactDOM = require("react-dom");
|
|
var test_utilities_1 = require("@fluentui/test-utilities");
|
|
var focus_1 = require("./focus");
|
|
function renderIntoDocument(element, container) {
|
|
var component = ReactDOM.render(element, container);
|
|
var renderedDOM = ReactDOM.findDOMNode(component);
|
|
return renderedDOM;
|
|
}
|
|
// JSDOM does not currently set `delegatesFocus`
|
|
// https://github.com/jsdom/jsdom/blob/b7683ed68ebe259cd2c68e5faf12d484a785f45f/lib/jsdom/living/nodes/Element-impl.js#L420-L424
|
|
function createDivWithShadowRoot(initOptions) {
|
|
var div = {
|
|
getAttribute: function (qualifiedName) { return null; },
|
|
shadowRoot: {
|
|
mode: initOptions.mode,
|
|
delegatesFocus: initOptions.delegatesFocus,
|
|
},
|
|
};
|
|
return div;
|
|
}
|
|
function makeShadowDiv(innerHTML) {
|
|
var ShadowDiv = function () {
|
|
var setRef = function (node) {
|
|
if (node) {
|
|
node.attachShadow({ mode: 'open' });
|
|
node.shadowRoot.innerHTML = innerHTML;
|
|
}
|
|
};
|
|
return React.createElement("div", { className: "parent", ref: setRef });
|
|
};
|
|
return ShadowDiv;
|
|
}
|
|
describe('isElementVisible', function () {
|
|
var testContainer;
|
|
afterEach(function () {
|
|
if (testContainer) {
|
|
ReactDOM.unmountComponentAtNode(testContainer);
|
|
testContainer.remove();
|
|
testContainer = undefined;
|
|
}
|
|
});
|
|
it('returns false if data-is-visible is false', function () {
|
|
testContainer = (0, test_utilities_1.createTestContainer)();
|
|
var _hiddenElement = renderIntoDocument(React.createElement("div", { "data-is-visible": false },
|
|
React.createElement("button", null)), testContainer);
|
|
expect((0, focus_1.isElementVisible)(_hiddenElement)).toEqual(false);
|
|
});
|
|
it('returns true if data-is-visible is true', function () {
|
|
testContainer = (0, test_utilities_1.createTestContainer)();
|
|
var _visibleElement = renderIntoDocument(React.createElement("div", { "data-is-visible": true },
|
|
React.createElement("button", null)), testContainer);
|
|
expect((0, focus_1.isElementVisible)(_visibleElement)).toEqual(true);
|
|
});
|
|
it('returns true if data-is-visible is undefined but element is visible', function () {
|
|
testContainer = (0, test_utilities_1.createTestContainer)();
|
|
var _element = renderIntoDocument(React.createElement("div", null,
|
|
React.createElement("button", null)), testContainer);
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
_element.isVisible = true;
|
|
expect((0, focus_1.isElementVisible)(_element)).toEqual(true);
|
|
});
|
|
});
|
|
describe('isElementTabbable', function () {
|
|
it('returns false on null', function () {
|
|
expect((0, focus_1.isElementVisible)(null)).toEqual(false);
|
|
});
|
|
it('returns false on normal divs', function () {
|
|
var div = document.createElement('div');
|
|
expect((0, focus_1.isElementTabbable)(div)).toEqual(false);
|
|
});
|
|
it('returns false on disabled buttons', function () {
|
|
var button = document.createElement('button');
|
|
button.setAttribute('disabled', 'true');
|
|
expect((0, focus_1.isElementTabbable)(button)).toEqual(false);
|
|
});
|
|
it('returns true on buttons', function () {
|
|
var button = document.createElement('button');
|
|
expect((0, focus_1.isElementTabbable)(button)).toEqual(true);
|
|
});
|
|
it('returns true on anchors', function () {
|
|
var anchor = document.createElement('a');
|
|
expect((0, focus_1.isElementTabbable)(anchor)).toEqual(true);
|
|
});
|
|
it('returns true on input elements', function () {
|
|
var input = document.createElement('input');
|
|
expect((0, focus_1.isElementTabbable)(input)).toEqual(true);
|
|
});
|
|
it('returns true on select elements', function () {
|
|
var select = document.createElement('select');
|
|
expect((0, focus_1.isElementTabbable)(select)).toEqual(true);
|
|
});
|
|
it('returns true on textarea elements', function () {
|
|
var textarea = document.createElement('textarea');
|
|
expect((0, focus_1.isElementTabbable)(textarea)).toEqual(true);
|
|
});
|
|
it('works with tabbable divs', function () {
|
|
var div = document.createElement('div');
|
|
div.tabIndex = 0;
|
|
expect((0, focus_1.isElementTabbable)(div)).toEqual(true);
|
|
});
|
|
it('returns false with role=button divs', function () {
|
|
var div = document.createElement('div');
|
|
div.setAttribute('role', 'button');
|
|
expect((0, focus_1.isElementTabbable)(div)).toEqual(false);
|
|
});
|
|
it('returns false with role=button disabled buttons', function () {
|
|
var button = document.createElement('button');
|
|
button.setAttribute('role', 'button');
|
|
button.setAttribute('disabled', 'true');
|
|
expect((0, focus_1.isElementTabbable)(button)).toEqual(false);
|
|
});
|
|
it('returns false with -1 tabIndex', function () {
|
|
var button = document.createElement('button');
|
|
button.tabIndex = -1;
|
|
expect((0, focus_1.isElementTabbable)(button, true)).toEqual(false);
|
|
});
|
|
it('returns true for elements with shadowRoot.delegatesFocus=true', function () {
|
|
var _a;
|
|
var div = createDivWithShadowRoot({ mode: 'open', delegatesFocus: true });
|
|
expect((_a = div.shadowRoot) === null || _a === void 0 ? void 0 : _a.delegatesFocus).toEqual(true);
|
|
expect((0, focus_1.isElementTabbable)(div)).toEqual(true);
|
|
});
|
|
it('returns true for elements with shadowRoot.delegatesFocus=false', function () {
|
|
var div = createDivWithShadowRoot({ mode: 'open' });
|
|
expect((0, focus_1.isElementTabbable)(div)).toEqual(false);
|
|
});
|
|
it('returns true for elements with shadowRoot.delegatesFocus=true when set to ignore shadow roots', function () {
|
|
var div = createDivWithShadowRoot({ mode: 'open', delegatesFocus: true });
|
|
expect((0, focus_1.isElementTabbable)(div, undefined, false)).toEqual(false);
|
|
});
|
|
});
|
|
describe('focusAsync', function () {
|
|
var testContainer;
|
|
afterEach(function () {
|
|
if (testContainer) {
|
|
ReactDOM.unmountComponentAtNode(testContainer);
|
|
testContainer.remove();
|
|
testContainer = undefined;
|
|
}
|
|
});
|
|
beforeAll(function () {
|
|
jest.useFakeTimers();
|
|
});
|
|
afterAll(function () {
|
|
jest.useRealTimers();
|
|
});
|
|
it('focuses on an item on the next frame', function () {
|
|
testContainer = (0, test_utilities_1.createTestContainer)();
|
|
var container = renderIntoDocument(React.createElement("div", null,
|
|
React.createElement("button", { className: "a" }, "a"),
|
|
React.createElement("button", { className: "b" }, "b"),
|
|
React.createElement("button", { className: "c" }, "c")), testContainer);
|
|
var buttonA = container.querySelector('.a');
|
|
var buttonB = container.querySelector('.b');
|
|
var buttonC = container.querySelector('.c');
|
|
// Focus the first button.
|
|
(0, focus_1.focusAsync)(buttonA);
|
|
window.requestAnimationFrame(function () {
|
|
expect(container.ownerDocument.activeElement).toBe(buttonA);
|
|
// Focus the second button, then the third before the next frame
|
|
(0, focus_1.focusAsync)(buttonB);
|
|
(0, focus_1.focusAsync)(buttonC);
|
|
window.requestAnimationFrame(function () {
|
|
expect(container.ownerDocument.activeElement).toBe(buttonC);
|
|
});
|
|
});
|
|
jest.runAllTimers();
|
|
});
|
|
it('can focus a component which implements focus()', function () {
|
|
var calledFocus = false;
|
|
var fakeComponent = {
|
|
ownerDocument: {},
|
|
focus: function () { return (calledFocus = true); },
|
|
};
|
|
(0, focus_1.focusAsync)(fakeComponent);
|
|
jest.runAllTimers();
|
|
expect(calledFocus).toEqual(true);
|
|
});
|
|
});
|
|
describe('getFocusableByIndexPath', function () {
|
|
it('can recover a path', function () {
|
|
var parent = document.createElement('div');
|
|
parent.innerHTML = "\n <div>\n <div></div>\n <div></div>\n <div>\n <div></div>\n <button id='child' data-is-visible='true' />\n </div>\n </div>\n ";
|
|
var child = parent.querySelector('#child');
|
|
expect((0, focus_1.getFocusableByIndexPath)(parent, [0, 2, 1])).toEqual(child);
|
|
});
|
|
it('ignores hidden elements', function () {
|
|
var parent = document.createElement('div');
|
|
parent.innerHTML = "\n <div>\n <div></div>\n <div></div>\n <div>\n <div></div>\n <button id='child' data-is-visible='false' />\n </div>\n </div>\n ";
|
|
parent.querySelector('#child');
|
|
expect((0, focus_1.getFocusableByIndexPath)(parent, [0, 2, 1])).toEqual(null);
|
|
});
|
|
it('can fallback to a previous element', function () {
|
|
var parent = document.createElement('div');
|
|
parent.innerHTML = "\n <div>\n <button id='child' data-is-visible='true'>\n <div>\n <div/>\n </div>\n </button>\n </div>\n ";
|
|
var child = parent.querySelector('#child');
|
|
expect((0, focus_1.getFocusableByIndexPath)(parent, [0, 0, 0, 0, 0, 0])).toEqual(child);
|
|
});
|
|
});
|
|
describe('getElementIndexPath', function () {
|
|
it('can get a path', function () {
|
|
var parent = document.createElement('div');
|
|
parent.innerHTML = "\n <div>\n <div></div>\n <div></div>\n <div>\n <div></div>\n <div id='child'></div>\n </div>\n </div>\n ";
|
|
var child = parent.querySelector('#child');
|
|
expect((0, focus_1.getElementIndexPath)(parent, child)).toEqual([0, 2, 1]);
|
|
});
|
|
it('can handle the same element', function () {
|
|
var parent = document.createElement('div');
|
|
expect((0, focus_1.getElementIndexPath)(parent, parent)).toEqual([]);
|
|
});
|
|
});
|
|
describe('getFirstTabbable', function () {
|
|
var testContainer;
|
|
afterEach(function () {
|
|
if (testContainer) {
|
|
ReactDOM.unmountComponentAtNode(testContainer);
|
|
testContainer.remove();
|
|
testContainer = undefined;
|
|
}
|
|
});
|
|
it('focuses on the next tabbable item', function () {
|
|
testContainer = (0, test_utilities_1.createTestContainer)();
|
|
var container = renderIntoDocument(React.createElement("div", null,
|
|
React.createElement("div", { className: "parent" },
|
|
React.createElement("button", { className: "a", "data-is-visible": true }, "a"),
|
|
React.createElement("button", { className: "b", "data-is-visible": true }, "b"),
|
|
React.createElement("button", { className: "c", "data-is-visible": true }, "c"))), testContainer);
|
|
var parent = container.querySelector('.parent');
|
|
var buttonA = container.querySelector('.a');
|
|
var buttonB = container.querySelector('.b');
|
|
expect((0, focus_1.getFirstTabbable)(parent, buttonA, true, false)).toEqual(buttonB);
|
|
});
|
|
it('focuses on the next tabbable item in shadow DOM', function () {
|
|
var _a, _b;
|
|
testContainer = (0, test_utilities_1.createTestContainer)();
|
|
var innerHTML = "\n <button class=\"a\" data-is-visible=\"true\">\n a\n </button>\n <button class=\"b\" data-is-visible=\"true\">\n b\n </button>\n <button class=\"c\" data-is-visible=\"true\">\n c\n </button>\n ";
|
|
var ShadowDiv = makeShadowDiv(innerHTML);
|
|
var container = renderIntoDocument(React.createElement("div", null,
|
|
React.createElement(ShadowDiv, null)), testContainer);
|
|
var parent = container.querySelector('.parent');
|
|
var buttonA = (_a = parent === null || parent === void 0 ? void 0 : parent.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('.a');
|
|
var buttonB = (_b = parent === null || parent === void 0 ? void 0 : parent.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('.b');
|
|
expect((0, focus_1.getFirstTabbable)(parent, buttonA, true, false, true)).toEqual(buttonB);
|
|
});
|
|
it('does not focus on an item with tabIndex of -1', function () {
|
|
testContainer = (0, test_utilities_1.createTestContainer)();
|
|
var container = renderIntoDocument(React.createElement("div", null,
|
|
React.createElement("div", { className: "parent" },
|
|
React.createElement("button", { className: "a", "data-is-visible": true, tabIndex: -1 }, "a"),
|
|
React.createElement("button", { className: "b", "data-is-visible": true, tabIndex: -1 }, "b"),
|
|
React.createElement("button", { className: "c", "data-is-visible": true, tabIndex: -1 }, "c"))), testContainer);
|
|
var parent = container.querySelector('.parent');
|
|
var buttonA = container.querySelector('.a');
|
|
expect((0, focus_1.getFirstTabbable)(parent, buttonA, true, false)).toEqual(null);
|
|
});
|
|
});
|
|
describe('getLastTabbable', function () {
|
|
var testContainer;
|
|
afterEach(function () {
|
|
if (testContainer) {
|
|
ReactDOM.unmountComponentAtNode(testContainer);
|
|
testContainer.remove();
|
|
testContainer = undefined;
|
|
}
|
|
});
|
|
it('focuses on the last tabbable item', function () {
|
|
testContainer = (0, test_utilities_1.createTestContainer)();
|
|
var container = renderIntoDocument(React.createElement("div", null,
|
|
React.createElement("div", { className: "parent" },
|
|
React.createElement("button", { className: "a", "data-is-visible": true }, "a"),
|
|
React.createElement("button", { className: "b", "data-is-visible": true }, "b"),
|
|
React.createElement("button", { className: "c", "data-is-visible": true }, "c"))), testContainer);
|
|
var parent = container.querySelector('.parent');
|
|
var buttonB = container.querySelector('.b');
|
|
var buttonC = container.querySelector('.c');
|
|
expect((0, focus_1.getLastTabbable)(parent, buttonC, true, false)).toEqual(buttonB);
|
|
});
|
|
it('focuses on the last tabbable item in shadow DOM', function () {
|
|
var _a, _b;
|
|
testContainer = (0, test_utilities_1.createTestContainer)();
|
|
var innerHTML = "\n <button class=\"a\" data-is-visible=\"true\">\n a\n </button>\n <button class=\"b\" data-is-visible=\"true\">\n b\n </button>\n <button class=\"c\" data-is-visible=\"true\">\n c\n </button>\n ";
|
|
var ShadowDiv = makeShadowDiv(innerHTML);
|
|
var container = renderIntoDocument(React.createElement("div", null,
|
|
React.createElement(ShadowDiv, null)), testContainer);
|
|
var parent = container.querySelector('.parent');
|
|
var buttonB = (_a = parent === null || parent === void 0 ? void 0 : parent.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('.b');
|
|
var buttonC = (_b = parent === null || parent === void 0 ? void 0 : parent.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('.c');
|
|
expect((0, focus_1.getLastTabbable)(parent, buttonC, true, false, true)).toEqual(buttonB);
|
|
});
|
|
it('does not focus on an item with tabIndex of -1', function () {
|
|
testContainer = (0, test_utilities_1.createTestContainer)();
|
|
var container = renderIntoDocument(React.createElement("div", null,
|
|
React.createElement("div", { className: "parent" },
|
|
React.createElement("button", { className: "a", "data-is-visible": true, tabIndex: -1 }, "a"),
|
|
React.createElement("button", { className: "b", "data-is-visible": true, tabIndex: -1 }, "b"),
|
|
React.createElement("button", { className: "c", "data-is-visible": true, tabIndex: -1 }, "c"))), testContainer);
|
|
var parent = container.querySelector('.parent');
|
|
var buttonC = container.querySelector('.c');
|
|
expect((0, focus_1.getLastTabbable)(parent, buttonC, true, false)).toEqual(null);
|
|
});
|
|
});
|
|
//# sourceMappingURL=focus.test.js.map
|