diff --git a/shell/composables/focusTrap.test.ts b/shell/composables/focusTrap.test.ts new file mode 100644 index 00000000000..b1820117edf --- /dev/null +++ b/shell/composables/focusTrap.test.ts @@ -0,0 +1,110 @@ +import { getFirstFocusableElement } from './focusTrap'; + +describe('getFirstFocusableElement', () => { + it('returns document.body when the container has no focusable elements', () => { + const container = document.createElement('div'); + + container.innerHTML = 'plain text

paragraph

'; + + expect(getFirstFocusableElement(container)).toStrictEqual(document.body); + }); + + it('returns document.body when no element argument is provided and document has no focusable elements', () => { + expect(getFirstFocusableElement()).toStrictEqual(document.body); + }); + + it.each([ + { + desc: 'button', + html: '', + }, + { + desc: 'anchor', + html: 'link', + }, + { + desc: 'input', + html: '', + }, + { + desc: 'textarea', + html: '', + }, + { + desc: 'select', + html: '', + }, + { + desc: 'details', + html: '
summary
', + }, + { + desc: 'element with tabindex="0"', + html: '
div
', + }, + { + desc: 'element with tabindex="1"', + html: '
div
', + }, + ])('returns the $desc element when it is the only focusable element', ({ html }) => { + const container = document.createElement('div'); + + container.innerHTML = html; + + const expected = container.firstElementChild; + + expect(getFirstFocusableElement(container)).toStrictEqual(expected); + }); + + it('skips a disabled button and returns the next non-disabled focusable element', () => { + const container = document.createElement('div'); + + container.innerHTML = ''; + const buttons = container.querySelectorAll('button'); + + expect(getFirstFocusableElement(container)).toStrictEqual(buttons[1]); + }); + + it('skips a disabled input and returns the next non-disabled focusable element', () => { + const container = document.createElement('div'); + + container.innerHTML = ''; + const button = container.querySelector('button'); + + expect(getFirstFocusableElement(container)).toStrictEqual(button); + }); + + it('returns document.body when every focusable element is disabled', () => { + const container = document.createElement('div'); + + container.innerHTML = ''; + + expect(getFirstFocusableElement(container)).toStrictEqual(document.body); + }); + + it('does not return an element with tabindex="-1"', () => { + const container = document.createElement('div'); + + container.innerHTML = '
not focusable
'; + + expect(getFirstFocusableElement(container)).toStrictEqual(document.body); + }); + + it('returns the first of multiple focusable elements', () => { + const container = document.createElement('div'); + + container.innerHTML = ''; + const [first] = container.querySelectorAll('button'); + + expect(getFirstFocusableElement(container)).toStrictEqual(first); + }); + + it('returns the first non-disabled element when leading elements are all disabled', () => { + const container = document.createElement('div'); + + container.innerHTML = ''; + const buttons = container.querySelectorAll('button'); + + expect(getFirstFocusableElement(container)).toStrictEqual(buttons[2]); + }); +});