Skip to content

Commit

Permalink
fix: correct autofocus behavior; accept empty data-autofocus prop
Browse files Browse the repository at this point in the history
  • Loading branch information
theKashey committed Sep 17, 2022
1 parent 33f8b4b commit e144d52
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 25 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2017 Anton
Copyright (c) 2017 Anton Korzunov

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,14 @@ to amend focus behavior in a desired way.

These are data-attributes one can add on the elements:

- `data-focus-lock` to create a focus group (scattered focus)
- `data-focus-lock-disabled` marks such group as disables and removes from the list
- `data-no-focus-lock` focus-lock will ignore focus inside marked area
- `data-autofocus-inside` focus-lock will try to autofocus elements within selected area
- `data-no-autofocus` focus-lock will not autofocus any node within marked area
- control
- `data-focus-lock` to create a focus group (scattered focus)
- `data-focus-lock-disabled` marks such group as disabled and removes from the list
- `data-no-focus-lock` focus-lock will ignore focus inside marked area
- autofocus
- `data-autofocus` will autofocus marked element on activation. Require _control delegation_ to focus-lock
- `data-autofocus-inside` focus-lock will try to autofocus elements within selected area
- `data-no-autofocus` focus-lock will not autofocus any node within marked area

These markers are available as `import * as markers from 'focus-lock/constants'`

Expand Down
27 changes: 27 additions & 0 deletions __tests__/focusMerge.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,31 @@ describe('FocusMerge', () => {

expect(focusMerge(querySelector('#d1'), null)!.node.innerHTML).toBe('1');
});

describe('data-autofocus', () => {
it('autofocus - should pick first available focusable if pointed directly', () => {
document.body.innerHTML = `
<div id="d1">
<button>1</button>
<button data-autofocus disabled>disabled</button>
<button data-autofocus>2</button>
<button>3</button>
</div>
`;

expect(focusMerge(querySelector('#d1'), null)!.node.innerHTML).toBe('2');
});

it('autofocus - false value', () => {
document.body.innerHTML = `
<div id="d1">
<button>1</button>
<button data-autofocus="false">2</button>
<button>3</button>
</div>
`;

expect(focusMerge(querySelector('#d1'), null)!.node.innerHTML).toBe('1');
});
});
});
23 changes: 4 additions & 19 deletions src/focusMerge.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
import { NEW_FOCUS, newFocus } from './solver';
import { filterAutoFocusable, getAllTabbableNodes, getTabbableNodes } from './utils/DOMutils';
import { getAllTabbableNodes, getTabbableNodes } from './utils/DOMutils';
import { getAllAffectedNodes } from './utils/all-affected';
import { pickFirstFocus } from './utils/firstFocus';
import { pickAutofocus } from './utils/auto-focus';
import { getActiveElement } from './utils/getActiveElement';
import { getDataset, isDefined, isNotAGuard } from './utils/is';
import { isDefined, isNotAGuard } from './utils/is';
import { allParentAutofocusables, getTopCommonParent } from './utils/parenting';
import { NodeIndex } from './utils/tabOrder';

const findAutoFocused =
(autoFocusables: Element[]) =>
(node: Element): boolean =>
// @ts-expect-error
node.autofocus || !!getDataset(node)?.autofocus || autoFocusables.indexOf(node) >= 0;

const reorderNodes = (srcNodes: Element[], dstNodes: NodeIndex[]): NodeIndex[] => {
const remap = new Map<Element, NodeIndex>();
// no Set(dstNodes) for IE11 :(
Expand Down Expand Up @@ -55,16 +49,7 @@ export const getFocusMerge = (
const newId = newFocus(innerNodes, outerNodes, activeElement, lastNode as HTMLElement);

if (newId === NEW_FOCUS) {
const autoFocusable = filterAutoFocusable(anyFocusable.map(({ node }) => node)).filter(
findAutoFocused(allParentAutofocusables(entries, visibilityCache))
);

return {
node:
autoFocusable && autoFocusable.length
? pickFirstFocus(autoFocusable)
: pickFirstFocus(filterAutoFocusable(innerNodes)),
};
return { node: pickAutofocus(anyFocusable, innerNodes, allParentAutofocusables(entries, visibilityCache)) };
}

if (newId === undefined) {
Expand Down
35 changes: 35 additions & 0 deletions src/utils/auto-focus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { filterAutoFocusable } from './DOMutils';
import { pickFirstFocus } from './firstFocus';
import { getDataset } from './is';
import { NodeIndex } from './tabOrder';

const findAutoFocused =
(autoFocusables: Element[]) =>
(node: Element): boolean => {
const autofocus = getDataset(node)?.autofocus;

return (
// @ts-expect-error
node.autofocus ||
//
(autofocus !== undefined && autofocus !== 'false') ||
//
autoFocusables.indexOf(node) >= 0
);
};

export const pickAutofocus = (
nodesIndexes: NodeIndex[],
orderedNodes: HTMLElement[],
groups: Element[]
): HTMLElement => {
const nodes = nodesIndexes.map(({ node }) => node);

const autoFocusable = filterAutoFocusable(nodes.filter(findAutoFocused(groups)));

if (autoFocusable && autoFocusable.length) {
return pickFirstFocus(autoFocusable);
}

return pickFirstFocus(filterAutoFocusable(orderedNodes));
};
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"removeComments": false,
"importHelpers": true,
"target": "es6",
"moduleResolution": "node",
Expand Down

0 comments on commit e144d52

Please sign in to comment.