Skip to content

Commit

Permalink
[New] Add isFocusable utils method
Browse files Browse the repository at this point in the history
  • Loading branch information
khiga8 authored and ljharb committed Aug 30, 2022
1 parent bbae2c4 commit e199d17
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 0 deletions.
86 changes: 86 additions & 0 deletions __tests__/src/util/isFocusable-test.js
@@ -0,0 +1,86 @@
import expect from 'expect';
import { elementType } from 'jsx-ast-utils';
import isFocusable from '../../../src/util/isFocusable';
import {
genElementSymbol,
genInteractiveElements,
genNonInteractiveElements,
} from '../../../__mocks__/genInteractives';
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';

function mergeTabIndex(index, attributes) {
return [...attributes, JSXAttributeMock('tabIndex', index)];
}

describe('isFocusable', () => {
describe('interactive elements', () => {
genInteractiveElements().forEach(({ openingElement }) => {
it(`should identify \`${genElementSymbol(openingElement)}\` as a focusable element`, () => {
expect(isFocusable(
elementType(openingElement),
openingElement.attributes,
)).toBe(true);
});

it(`should not identify \`${genElementSymbol(openingElement)}\` with tabIndex of -1 as a focusable element`, () => {
expect(isFocusable(
elementType(openingElement),
mergeTabIndex(-1, openingElement.attributes),
)).toBe(false);
});

it(`should identify \`${genElementSymbol(openingElement)}\` with tabIndex of 0 as a focusable element`, () => {
expect(isFocusable(
elementType(openingElement),
mergeTabIndex(0, openingElement.attributes),
)).toBe(true);
});

it(`should identify \`${genElementSymbol(openingElement)}\` with tabIndex of 1 as a focusable element`, () => {
expect(isFocusable(
elementType(openingElement),
mergeTabIndex(1, openingElement.attributes),
)).toBe(true);
});
});
});

describe('non-interactive elements', () => {
genNonInteractiveElements().forEach(({ openingElement }) => {
it(`should not identify \`${genElementSymbol(openingElement)}\` as a focusable element`, () => {
expect(isFocusable(
elementType(openingElement),
openingElement.attributes,
)).toBe(false);
});

it(`should not identify \`${genElementSymbol(openingElement)}\` with tabIndex of -1 as a focusable element`, () => {
expect(isFocusable(
elementType(openingElement),
mergeTabIndex(-1, openingElement.attributes),
)).toBe(false);
});

it(`should identify \`${genElementSymbol(openingElement)}\` with tabIndex of 0 as a focusable element`, () => {
expect(isFocusable(
elementType(openingElement),
mergeTabIndex(0, openingElement.attributes),
)).toBe(true);
});

it(`should identify \`${genElementSymbol(openingElement)}\` with tabIndex of 1 as a focusable element`, () => {
expect(isFocusable(
elementType(openingElement),
mergeTabIndex(1, openingElement.attributes),
)).toBe(true);
});

it(`should not identify \`${genElementSymbol(openingElement)}\` with tabIndex of 'bogus' as a focusable element`, () => {
expect(isFocusable(
elementType(openingElement),
mergeTabIndex('bogus', openingElement.attributes),
)).toBe(false);
});
});
});
});
17 changes: 17 additions & 0 deletions src/util/isFocusable.js
@@ -0,0 +1,17 @@
import { getProp } from 'jsx-ast-utils';
import getTabIndex from './getTabIndex';
import isInteractiveElement from './isInteractiveElement';

/**
* Returns boolean indicating whether an element appears in tab focus.
* Identifies an element as focusable if it is an interactive element, or an element with a tabIndex greater than or equal to 0.
*/
function isFocusable(type, attributes) {
const tabIndex = getTabIndex(getProp(attributes, 'tabIndex'));
if (isInteractiveElement(type, attributes)) {
return (tabIndex === undefined || tabIndex >= 0);
}
return tabIndex >= 0;
}

export default isFocusable;

0 comments on commit e199d17

Please sign in to comment.