@jobber/hooks 1.12.4 → 1.13.0

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 CHANGED
@@ -1,13 +1,27 @@
1
- ---
2
- name: Hooks
3
- menu: Changelog
4
- ---
5
-
6
- # @jobber/hooks: Change Log
1
+ # Change Log
7
2
 
8
3
  All notable changes to this project will be documented in this file.
9
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
10
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
+
17
+ ## [1.12.5](https://github.com/GetJobber/atlantis/compare/@jobber/hooks@1.12.4...@jobber/hooks@1.12.5) (2023-02-28)
18
+
19
+ **Note:** Version bump only for package @jobber/hooks
20
+
21
+
22
+
23
+
24
+
11
25
  ## [1.12.4](https://github.com/GetJobber/atlantis/compare/@jobber/hooks@1.12.3...@jobber/hooks@1.12.4) (2023-02-27)
12
26
 
13
27
  **Note:** Version bump only for package @jobber/hooks
@@ -361,16 +375,3 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
361
375
  ### Features
362
376
 
363
377
  * add hook package with useResizeOberserver hook ([#242](https://github.com/GetJobber/atlantis/issues/242)) ([59411d7](https://github.com/GetJobber/atlantis/commit/59411d7dd34508e56dcdd4b9de9806414196959c))
364
-
365
-
366
-
367
-
368
-
369
- ---
370
- name: Atlantis Hooks
371
- menu: Changelog
372
- ---
373
-
374
- # @jobber/hooks: Change Log
375
-
376
- All notable changes to this project will be documented in this file.
package/README.mdx CHANGED
@@ -1,9 +1,3 @@
1
- ---
2
- name: Hooks
3
- menu: Packages
4
- route: /packages/hooks
5
- ---
6
-
7
1
  # Atlantis Hooks
8
2
 
9
3
  Shared hooks for components in Atlantis.
package/dist/index.d.ts CHANGED
@@ -7,3 +7,4 @@ export * from "./useOnKeyDown";
7
7
  export * from "./usePasswordStrength";
8
8
  export * from "./useRefocusOnActivator";
9
9
  export * from "./useResizeObserver";
10
+ export * from "./useFocusTrap";
package/dist/index.js CHANGED
@@ -12,3 +12,4 @@ __export(require("./useOnKeyDown"));
12
12
  __export(require("./usePasswordStrength"));
13
13
  __export(require("./useRefocusOnActivator"));
14
14
  __export(require("./useResizeObserver"));
15
+ __export(require("./useFocusTrap"));
@@ -0,0 +1 @@
1
+ export { useFocusTrap } from "./useFocusTrap";
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ var useFocusTrap_1 = require("./useFocusTrap");
4
+ exports.useFocusTrap = useFocusTrap_1.useFocusTrap;
@@ -0,0 +1,12 @@
1
+ /// <reference types="react" />
2
+ /// <reference types="@emotion/core" />
3
+ /// <reference types="theme-ui" />
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
+ 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.12.4",
3
+ "version": "1.13.0",
4
4
  "license": "MIT",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.js",
@@ -16,9 +16,10 @@
16
16
  "devDependencies": {
17
17
  "@apollo/client": "^3.3.16",
18
18
  "@apollo/react-testing": "^4.0.0",
19
- "@jobber/formatters": "^0.2.0",
19
+ "@jobber/formatters": "^0.2.1",
20
20
  "@testing-library/react": "^10.2.1",
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
24
  "@types/react": "16.14.2",
24
25
  "@types/react-dom": "16.9.10",
@@ -40,5 +41,5 @@
40
41
  "@apollo/client": "^3.3.16",
41
42
  "react": "^16.8.6"
42
43
  },
43
- "gitHead": "cccd4892ef869a786ddf838acfb26ecaec0407a8"
44
+ "gitHead": "347e591cc612d851632e3728e0a7d0c36f684aad"
44
45
  }
@@ -3,14 +3,4 @@ import Readme from "./README.mdx";
3
3
 
4
4
  <Meta title="Packages/Hooks" />
5
5
 
6
- # Atlantis Hooks
7
-
8
- Shared hooks for components in Atlantis.
9
-
10
- ## Hooks
11
-
12
- - [useResizeObserver](../?path=/docs/hooks-useresizeobserver--use-resize-observer)
13
-
14
- ## Installing
15
-
16
- `npm install @jobber/hooks`
6
+ <Readme />