import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { createTestContainer } from '@fluentui/test-utilities'; import { isElementVisible, isElementTabbable, focusAsync, getElementIndexPath, getFirstTabbable, getFocusableByIndexPath, getLastTabbable, } from './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 = createTestContainer(); var _hiddenElement = renderIntoDocument(React.createElement("div", { "data-is-visible": false }, React.createElement("button", null)), testContainer); expect(isElementVisible(_hiddenElement)).toEqual(false); }); it('returns true if data-is-visible is true', function () { testContainer = createTestContainer(); var _visibleElement = renderIntoDocument(React.createElement("div", { "data-is-visible": true }, React.createElement("button", null)), testContainer); expect(isElementVisible(_visibleElement)).toEqual(true); }); it('returns true if data-is-visible is undefined but element is visible', function () { testContainer = 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(isElementVisible(_element)).toEqual(true); }); }); describe('isElementTabbable', function () { it('returns false on null', function () { expect(isElementVisible(null)).toEqual(false); }); it('returns false on normal divs', function () { var div = document.createElement('div'); expect(isElementTabbable(div)).toEqual(false); }); it('returns false on disabled buttons', function () { var button = document.createElement('button'); button.setAttribute('disabled', 'true'); expect(isElementTabbable(button)).toEqual(false); }); it('returns true on buttons', function () { var button = document.createElement('button'); expect(isElementTabbable(button)).toEqual(true); }); it('returns true on anchors', function () { var anchor = document.createElement('a'); expect(isElementTabbable(anchor)).toEqual(true); }); it('returns true on input elements', function () { var input = document.createElement('input'); expect(isElementTabbable(input)).toEqual(true); }); it('returns true on select elements', function () { var select = document.createElement('select'); expect(isElementTabbable(select)).toEqual(true); }); it('returns true on textarea elements', function () { var textarea = document.createElement('textarea'); expect(isElementTabbable(textarea)).toEqual(true); }); it('works with tabbable divs', function () { var div = document.createElement('div'); div.tabIndex = 0; expect(isElementTabbable(div)).toEqual(true); }); it('returns false with role=button divs', function () { var div = document.createElement('div'); div.setAttribute('role', 'button'); expect(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(isElementTabbable(button)).toEqual(false); }); it('returns false with -1 tabIndex', function () { var button = document.createElement('button'); button.tabIndex = -1; expect(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(isElementTabbable(div)).toEqual(true); }); it('returns true for elements with shadowRoot.delegatesFocus=false', function () { var div = createDivWithShadowRoot({ mode: 'open' }); expect(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(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 = 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. focusAsync(buttonA); window.requestAnimationFrame(function () { expect(container.ownerDocument.activeElement).toBe(buttonA); // Focus the second button, then the third before the next frame focusAsync(buttonB); 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); }, }; 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