@jobber/hooks 1.12.5 → 1.13.1-pre.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/useFocusTrap/index.d.ts +1 -0
- package/dist/useFocusTrap/index.js +4 -0
- package/dist/useFocusTrap/useFocusTrap.d.ts +10 -0
- package/dist/useFocusTrap/useFocusTrap.js +62 -0
- package/dist/useFocusTrap/useFocusTrap.test.d.ts +1 -0
- package/dist/useFocusTrap/useFocusTrap.test.js +53 -0
- package/dist/useFormState/useFormState.d.ts +0 -2
- package/dist/useResizeObserver/useResizeObserver.d.ts +0 -2
- package/package.json +8 -7
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,17 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [1.13.0](https://github.com/GetJobber/atlantis/compare/@jobber/hooks@1.12.5...@jobber/hooks@1.13.0) (2023-03-02)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **hooks:** Add useFocusTrap hook ([#1100](https://github.com/GetJobber/atlantis/issues/1100)) ([2fed042](https://github.com/GetJobber/atlantis/commit/2fed042db990704e1723e1ebe87bff7b04acc44f))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
6
17
|
## [1.12.5](https://github.com/GetJobber/atlantis/compare/@jobber/hooks@1.12.4...@jobber/hooks@1.12.5) (2023-02-28)
|
|
7
18
|
|
|
8
19
|
**Note:** Version bump only for package @jobber/hooks
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ function __export(m) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
__export(require("./useAssert"));
|
|
7
7
|
__export(require("./useCollectionQuery"));
|
|
8
|
+
__export(require("./useFocusTrap"));
|
|
8
9
|
__export(require("./useFormState"));
|
|
9
10
|
__export(require("./useIsMounted"));
|
|
10
11
|
__export(require("./useLiveAnnounce"));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useFocusTrap } from "./useFocusTrap";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
/**
|
|
3
|
+
* Traps the focus within the children of the ref element.
|
|
4
|
+
*
|
|
5
|
+
* @param active - Turns the focus trapping on or off. Also adds aria-hidden on the
|
|
6
|
+
* body but not the dialog.
|
|
7
|
+
*
|
|
8
|
+
* @returns ref
|
|
9
|
+
*/
|
|
10
|
+
export declare function useFocusTrap<T extends HTMLElement>(active: boolean): import("react").RefObject<T>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
var react_1 = require("react");
|
|
4
|
+
/**
|
|
5
|
+
* Traps the focus within the children of the ref element.
|
|
6
|
+
*
|
|
7
|
+
* @param active - Turns the focus trapping on or off. Also adds aria-hidden on the
|
|
8
|
+
* body but not the dialog.
|
|
9
|
+
*
|
|
10
|
+
* @returns ref
|
|
11
|
+
*/
|
|
12
|
+
function useFocusTrap(active) {
|
|
13
|
+
// There's an ongoing issue with useRef return type clashing with an element's
|
|
14
|
+
// ref prop type. TLDR: Use null because useRef doesn't expect undefined.
|
|
15
|
+
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/35572
|
|
16
|
+
var ref = react_1.useRef(null);
|
|
17
|
+
function handleKeyDown(event) {
|
|
18
|
+
if (!(active && ref.current) || event.key !== "Tab") {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
var _a = getElements(ref.current), firstElement = _a.firstElement, lastElement = _a.lastElement;
|
|
22
|
+
if (event.shiftKey) {
|
|
23
|
+
if (document.activeElement === firstElement) {
|
|
24
|
+
lastElement.focus();
|
|
25
|
+
event.preventDefault();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
if (document.activeElement === lastElement) {
|
|
30
|
+
firstElement.focus();
|
|
31
|
+
event.preventDefault();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
react_1.useEffect(function () {
|
|
36
|
+
var _a, _b;
|
|
37
|
+
if (active) {
|
|
38
|
+
(_a = ref.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
39
|
+
(_b = ref.current) === null || _b === void 0 ? void 0 : _b.addEventListener("keydown", handleKeyDown);
|
|
40
|
+
}
|
|
41
|
+
return function () {
|
|
42
|
+
var _a;
|
|
43
|
+
(_a = ref.current) === null || _a === void 0 ? void 0 : _a.removeEventListener("keydown", handleKeyDown);
|
|
44
|
+
};
|
|
45
|
+
}, [active]);
|
|
46
|
+
return ref;
|
|
47
|
+
}
|
|
48
|
+
exports.useFocusTrap = useFocusTrap;
|
|
49
|
+
function getElements(ref) {
|
|
50
|
+
var focusables = [
|
|
51
|
+
"button",
|
|
52
|
+
"[href]",
|
|
53
|
+
"input",
|
|
54
|
+
"select",
|
|
55
|
+
"textarea",
|
|
56
|
+
'[tabindex]:not([tabindex="-1"])',
|
|
57
|
+
];
|
|
58
|
+
var elements = ref.querySelectorAll(focusables.join(", "));
|
|
59
|
+
var firstElement = ref;
|
|
60
|
+
var lastElement = elements[elements.length - 1];
|
|
61
|
+
return { firstElement: firstElement, lastElement: lastElement };
|
|
62
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
var react_1 = __importDefault(require("react"));
|
|
7
|
+
var react_2 = require("@testing-library/react");
|
|
8
|
+
var user_event_1 = __importDefault(require("@testing-library/user-event"));
|
|
9
|
+
var useFocusTrap_1 = require("./useFocusTrap");
|
|
10
|
+
var targetId = "target";
|
|
11
|
+
var firstFocusableChild = "first-element";
|
|
12
|
+
var lastFocusableChild = "last-element";
|
|
13
|
+
it("should focus on the ref target on mount", function () {
|
|
14
|
+
var getByTestId = react_2.render(react_1.default.createElement(TestComponent, null)).getByTestId;
|
|
15
|
+
expect(getByTestId(targetId)).toHaveFocus();
|
|
16
|
+
});
|
|
17
|
+
it("should focus on the ref target when tabbing out of the last focusable element and ignore the tabindex'=-1'", function () {
|
|
18
|
+
var getByTestId = react_2.render(react_1.default.createElement(TestComponent, null)).getByTestId;
|
|
19
|
+
getByTestId(lastFocusableChild).focus();
|
|
20
|
+
user_event_1.default.tab();
|
|
21
|
+
expect(getByTestId(targetId)).toHaveFocus();
|
|
22
|
+
});
|
|
23
|
+
it("should focus on the first focusable element", function () {
|
|
24
|
+
var getByTestId = react_2.render(react_1.default.createElement(TestComponent, null)).getByTestId;
|
|
25
|
+
user_event_1.default.tab();
|
|
26
|
+
expect(getByTestId(firstFocusableChild)).toHaveFocus();
|
|
27
|
+
});
|
|
28
|
+
it("should focus on the last focusable element", function () {
|
|
29
|
+
var getByTestId = react_2.render(react_1.default.createElement(TestComponent, null)).getByTestId;
|
|
30
|
+
user_event_1.default.tab({ shift: true });
|
|
31
|
+
expect(getByTestId(lastFocusableChild)).toHaveFocus();
|
|
32
|
+
});
|
|
33
|
+
it("should not trap the tabbing and focus on the first child node", function () {
|
|
34
|
+
var getByTestId = react_2.render(react_1.default.createElement(TestComponent, { trap: false })).getByTestId;
|
|
35
|
+
user_event_1.default.tab();
|
|
36
|
+
expect(getByTestId(targetId).previousElementSibling).toHaveFocus();
|
|
37
|
+
});
|
|
38
|
+
function TestComponent(_a) {
|
|
39
|
+
var _b = _a.trap, trap = _b === void 0 ? true : _b;
|
|
40
|
+
var testRef = useFocusTrap_1.useFocusTrap(trap);
|
|
41
|
+
return (react_1.default.createElement(react_1.default.Fragment, null,
|
|
42
|
+
react_1.default.createElement("input", { type: "number" }),
|
|
43
|
+
react_1.default.createElement("div", { ref: testRef, "data-testid": targetId, tabIndex: 0 },
|
|
44
|
+
react_1.default.createElement("button", { "data-testid": firstFocusableChild }, "Click me"),
|
|
45
|
+
react_1.default.createElement("a", { href: "#" }),
|
|
46
|
+
react_1.default.createElement("input", { type: "text" }),
|
|
47
|
+
react_1.default.createElement("select", null,
|
|
48
|
+
react_1.default.createElement("option", { value: "A" })),
|
|
49
|
+
react_1.default.createElement("textarea", null),
|
|
50
|
+
react_1.default.createElement("span", { tabIndex: 0, "data-testId": lastFocusableChild }),
|
|
51
|
+
react_1.default.createElement("span", { tabIndex: -1 })),
|
|
52
|
+
react_1.default.createElement("input", { type: "calendar" })));
|
|
53
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jobber/hooks",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.1-pre.19+932bbb8b",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.js",
|
|
@@ -16,12 +16,13 @@
|
|
|
16
16
|
"devDependencies": {
|
|
17
17
|
"@apollo/client": "^3.3.16",
|
|
18
18
|
"@apollo/react-testing": "^4.0.0",
|
|
19
|
-
"@jobber/formatters": "^0.2.
|
|
20
|
-
"@testing-library/react": "^
|
|
19
|
+
"@jobber/formatters": "^0.2.2-pre.34+932bbb8b",
|
|
20
|
+
"@testing-library/react": "^14.0.0",
|
|
21
21
|
"@testing-library/react-hooks": "^7.0.0",
|
|
22
|
+
"@testing-library/user-event": "^12.0.2",
|
|
22
23
|
"@types/lodash": "4.14.136",
|
|
23
|
-
"@types/react": "
|
|
24
|
-
"@types/react-dom": "
|
|
24
|
+
"@types/react": "^18.0.28",
|
|
25
|
+
"@types/react-dom": "^18.0.11",
|
|
25
26
|
"@types/uuid": "^8.3.3",
|
|
26
27
|
"@types/zxcvbn": "^4.4.1",
|
|
27
28
|
"graphql": "^14.6.0",
|
|
@@ -38,7 +39,7 @@
|
|
|
38
39
|
},
|
|
39
40
|
"peerDependencies": {
|
|
40
41
|
"@apollo/client": "^3.3.16",
|
|
41
|
-
"react": "
|
|
42
|
+
"react": ">=16.8"
|
|
42
43
|
},
|
|
43
|
-
"gitHead": "
|
|
44
|
+
"gitHead": "932bbb8b9dd1cdacc858efb1f7feed546a710d11"
|
|
44
45
|
}
|