@navikt/ds-react 7.32.4 → 7.33.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.
Files changed (45) hide show
  1. package/cjs/modal/Modal.js +12 -0
  2. package/cjs/modal/Modal.js.map +1 -1
  3. package/cjs/modal/ModalUtils.d.ts +3 -2
  4. package/cjs/modal/ModalUtils.js +60 -10
  5. package/cjs/modal/ModalUtils.js.map +1 -1
  6. package/cjs/process/Process.d.ts +5 -0
  7. package/cjs/process/Process.js +6 -6
  8. package/cjs/process/Process.js.map +1 -1
  9. package/cjs/timeline/Pin.js +5 -4
  10. package/cjs/timeline/Pin.js.map +1 -1
  11. package/cjs/timeline/period/ClickablePeriod.js +3 -2
  12. package/cjs/timeline/period/ClickablePeriod.js.map +1 -1
  13. package/cjs/util/detectBrowser.d.ts +3 -1
  14. package/cjs/util/detectBrowser.js +27 -1
  15. package/cjs/util/detectBrowser.js.map +1 -1
  16. package/cjs/util/hooks/useScrollLock.d.ts +11 -0
  17. package/cjs/util/hooks/useScrollLock.js +270 -0
  18. package/cjs/util/hooks/useScrollLock.js.map +1 -0
  19. package/esm/modal/Modal.js +13 -1
  20. package/esm/modal/Modal.js.map +1 -1
  21. package/esm/modal/ModalUtils.d.ts +3 -2
  22. package/esm/modal/ModalUtils.js +27 -7
  23. package/esm/modal/ModalUtils.js.map +1 -1
  24. package/esm/process/Process.d.ts +5 -0
  25. package/esm/process/Process.js +6 -6
  26. package/esm/process/Process.js.map +1 -1
  27. package/esm/timeline/Pin.js +5 -4
  28. package/esm/timeline/Pin.js.map +1 -1
  29. package/esm/timeline/period/ClickablePeriod.js +3 -2
  30. package/esm/timeline/period/ClickablePeriod.js.map +1 -1
  31. package/esm/util/detectBrowser.d.ts +3 -1
  32. package/esm/util/detectBrowser.js +25 -1
  33. package/esm/util/detectBrowser.js.map +1 -1
  34. package/esm/util/hooks/useScrollLock.d.ts +11 -0
  35. package/esm/util/hooks/useScrollLock.js +268 -0
  36. package/esm/util/hooks/useScrollLock.js.map +1 -0
  37. package/package.json +11 -6
  38. package/src/modal/Modal.test.tsx +13 -24
  39. package/src/modal/Modal.tsx +16 -0
  40. package/src/modal/ModalUtils.ts +35 -7
  41. package/src/process/Process.tsx +17 -3
  42. package/src/timeline/Pin.tsx +6 -3
  43. package/src/timeline/period/ClickablePeriod.tsx +4 -1
  44. package/src/util/detectBrowser.ts +41 -1
  45. package/src/util/hooks/useScrollLock.ts +317 -0
@@ -1,2 +1,4 @@
1
1
  declare const isSafari: boolean;
2
- export { isSafari };
2
+ declare const isWebKit: boolean;
3
+ declare const isIOS: boolean;
4
+ export { isSafari, isWebKit, isIOS };
@@ -1,4 +1,28 @@
1
1
  const hasNavigator = typeof navigator !== "undefined";
2
+ function getNavigatorData() {
3
+ var _a, _b;
4
+ if (!hasNavigator) {
5
+ return { platform: "", maxTouchPoints: -1 };
6
+ }
7
+ const uaData = navigator.userAgentData;
8
+ if (uaData === null || uaData === void 0 ? void 0 : uaData.platform) {
9
+ return {
10
+ platform: uaData.platform,
11
+ maxTouchPoints: navigator.maxTouchPoints,
12
+ };
13
+ }
14
+ return {
15
+ platform: (_a = navigator.platform) !== null && _a !== void 0 ? _a : "",
16
+ maxTouchPoints: (_b = navigator.maxTouchPoints) !== null && _b !== void 0 ? _b : -1,
17
+ };
18
+ }
19
+ const nav = getNavigatorData();
2
20
  const isSafari = hasNavigator && /apple/i.test(navigator.vendor);
3
- export { isSafari };
21
+ const isWebKit = typeof CSS === "undefined" || !CSS.supports
22
+ ? false
23
+ : CSS.supports("-webkit-backdrop-filter:none");
24
+ const isIOS = nav.platform === "MacIntel" && nav.maxTouchPoints > 1
25
+ ? true
26
+ : /iP(hone|ad|od)|iOS/.test(nav.platform);
27
+ export { isSafari, isWebKit, isIOS };
4
28
  //# sourceMappingURL=detectBrowser.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"detectBrowser.js","sourceRoot":"","sources":["../../src/util/detectBrowser.ts"],"names":[],"mappings":"AAAA,MAAM,YAAY,GAAG,OAAO,SAAS,KAAK,WAAW,CAAC;AAEtD,MAAM,QAAQ,GAAG,YAAY,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;AAEjE,OAAO,EAAE,QAAQ,EAAE,CAAC"}
1
+ {"version":3,"file":"detectBrowser.js","sourceRoot":"","sources":["../../src/util/detectBrowser.ts"],"names":[],"mappings":"AAAA,MAAM,YAAY,GAAG,OAAO,SAAS,KAAK,WAAW,CAAC;AAQtD,SAAS,gBAAgB;;IACvB,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,cAAc,EAAE,CAAC,CAAC,EAAE,CAAC;IAC9C,CAAC;IAED,MAAM,MAAM,GAAI,SAAiB,CAAC,aAErB,CAAC;IAEd,IAAI,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,QAAQ,EAAE,CAAC;QACrB,OAAO;YACL,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,cAAc,EAAE,SAAS,CAAC,cAAc;SACzC,CAAC;IACJ,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,MAAA,SAAS,CAAC,QAAQ,mCAAI,EAAE;QAClC,cAAc,EAAE,MAAA,SAAS,CAAC,cAAc,mCAAI,CAAC,CAAC;KAC/C,CAAC;AACJ,CAAC;AAED,MAAM,GAAG,GAAG,gBAAgB,EAAE,CAAC;AAE/B,MAAM,QAAQ,GAAG,YAAY,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;AAEjE,MAAM,QAAQ,GACZ,OAAO,GAAG,KAAK,WAAW,IAAI,CAAC,GAAG,CAAC,QAAQ;IACzC,CAAC,CAAC,KAAK;IACP,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,8BAA8B,CAAC,CAAC;AAEnD,MAAM,KAAK,GACT,GAAG,CAAC,QAAQ,KAAK,UAAU,IAAI,GAAG,CAAC,cAAc,GAAG,CAAC;IACnD,CAAC,CAAC,IAAI;IACN,CAAC,CAAC,oBAAoB,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAE9C,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Locks the scroll of the document when enabled.
3
+ * @param enabled - Whether to enable the scroll lock.
4
+ */
5
+ declare function useScrollLock(params: {
6
+ enabled: boolean;
7
+ mounted: boolean;
8
+ open: boolean;
9
+ referenceElement?: Element | null;
10
+ }): void;
11
+ export { useScrollLock };
@@ -0,0 +1,268 @@
1
+ import { isIOS, isWebKit } from "../detectBrowser.js";
2
+ import { ownerDocument, ownerWindow } from "../owner.js";
3
+ import { useClientLayoutEffect } from "./useClientLayoutEffect.js";
4
+ import { Timeout } from "./useTimeout.js";
5
+ let originalHtmlStyles = {};
6
+ let originalBodyStyles = {};
7
+ let originalHtmlScrollBehavior = "";
8
+ function hasInsetScrollbars(referenceElement) {
9
+ if (typeof document === "undefined") {
10
+ return false;
11
+ }
12
+ const doc = ownerDocument(referenceElement);
13
+ const win = ownerWindow(doc);
14
+ return win.innerWidth - doc.documentElement.clientWidth > 0;
15
+ }
16
+ function preventScrollBasic(referenceElement) {
17
+ const doc = ownerDocument(referenceElement);
18
+ const html = doc.documentElement;
19
+ const originalOverflow = html.style.overflow;
20
+ html.style.overflow = "hidden";
21
+ return () => {
22
+ html.style.overflow = originalOverflow;
23
+ };
24
+ }
25
+ function preventScrollStandard(referenceElement) {
26
+ var _a, _b;
27
+ const doc = ownerDocument(referenceElement);
28
+ const html = doc.documentElement;
29
+ const body = doc.body;
30
+ const win = ownerWindow(html);
31
+ let scrollTop = 0;
32
+ let scrollLeft = 0;
33
+ let resizeRaf = 0;
34
+ /* Pinch-zoom in Safari causes a shift. Just don't lock scroll if there's any pinch-zoom. */
35
+ if (isWebKit && ((_b = (_a = win.visualViewport) === null || _a === void 0 ? void 0 : _a.scale) !== null && _b !== void 0 ? _b : 1) !== 1) {
36
+ return () => { };
37
+ }
38
+ /**
39
+ * Locks the scroll by applying styles to Html and Body element.
40
+ * Reads the DOM first, then writes to avoid layout thrashing.
41
+ */
42
+ function lockScroll() {
43
+ /* DOM reads: */
44
+ var _a;
45
+ const htmlStyles = win.getComputedStyle(html);
46
+ const bodyStyles = win.getComputedStyle(body);
47
+ scrollTop = html.scrollTop;
48
+ scrollLeft = html.scrollLeft;
49
+ originalHtmlStyles = {
50
+ scrollbarGutter: html.style.scrollbarGutter,
51
+ overflowY: html.style.overflowY,
52
+ overflowX: html.style.overflowX,
53
+ };
54
+ originalHtmlScrollBehavior = html.style.scrollBehavior;
55
+ originalBodyStyles = {
56
+ position: body.style.position,
57
+ height: body.style.height,
58
+ width: body.style.width,
59
+ boxSizing: body.style.boxSizing,
60
+ overflowY: body.style.overflowY,
61
+ overflowX: body.style.overflowX,
62
+ scrollBehavior: body.style.scrollBehavior,
63
+ };
64
+ const isScrollableY = html.scrollHeight > html.clientHeight;
65
+ const isScrollableX = html.scrollWidth > html.clientWidth;
66
+ const hasConstantOverflowY = htmlStyles.overflowY === "scroll" || bodyStyles.overflowY === "scroll";
67
+ const hasConstantOverflowX = htmlStyles.overflowX === "scroll" || bodyStyles.overflowX === "scroll";
68
+ /* Values can be negative in Firefox */
69
+ const scrollbarWidth = Math.max(0, win.innerWidth - html.clientWidth);
70
+ const scrollbarHeight = Math.max(0, win.innerHeight - html.clientHeight);
71
+ /*
72
+ * Avoid shift due to <body> margin. NB: This does cause elements to be clipped
73
+ * with whitespace.
74
+ */
75
+ const marginY = parseFloat(bodyStyles.marginTop) + parseFloat(bodyStyles.marginBottom);
76
+ const marginX = parseFloat(bodyStyles.marginLeft) + parseFloat(bodyStyles.marginRight);
77
+ /**
78
+ * Check support for stable scrollbar gutter to avoid layout shift when scrollbars appear/disappear.
79
+ */
80
+ const supportsStableScrollbarGutter = typeof CSS !== "undefined" &&
81
+ ((_a = CSS.supports) === null || _a === void 0 ? void 0 : _a.call(CSS, "scrollbar-gutter", "stable"));
82
+ /*
83
+ * DOM writes:
84
+ * Do not read the DOM past this point!
85
+ */
86
+ Object.assign(html.style, {
87
+ scrollbarGutter: "stable",
88
+ overflowY: !supportsStableScrollbarGutter &&
89
+ (isScrollableY || hasConstantOverflowY)
90
+ ? "scroll"
91
+ : "hidden",
92
+ overflowX: !supportsStableScrollbarGutter &&
93
+ (isScrollableX || hasConstantOverflowX)
94
+ ? "scroll"
95
+ : "hidden",
96
+ });
97
+ Object.assign(body.style, {
98
+ /*
99
+ * Keeps existing positioned children in place (e.g. fixed headers).
100
+ */
101
+ position: "relative",
102
+ /**
103
+ * Limits height to the viewport minus margins/scrollbar compensation to stop vertical overflow from reappearing.
104
+ */
105
+ height: marginY || scrollbarHeight
106
+ ? `calc(100dvh - ${marginY + scrollbarHeight}px)`
107
+ : "100dvh",
108
+ /**
109
+ * Mirrors height-logic for width.
110
+ */
111
+ width: marginX || scrollbarWidth
112
+ ? `calc(100vw - ${marginX + scrollbarWidth}px)`
113
+ : "100vw",
114
+ /**
115
+ * Ensures the adjusted dimensions include padding/border, matching the measured values.
116
+ */
117
+ boxSizing: "border-box",
118
+ /**
119
+ * Blocks scrollable overflow.
120
+ */
121
+ overflow: "hidden",
122
+ /**
123
+ * Removes smooth-scrolling so immediate position restores occur without animation.
124
+ */
125
+ scrollBehavior: "unset",
126
+ });
127
+ body.scrollTop = scrollTop;
128
+ body.scrollLeft = scrollLeft;
129
+ html.setAttribute("data-aksel-scroll-locked", "");
130
+ html.style.scrollBehavior = "unset";
131
+ }
132
+ /**
133
+ * Restores the original scroll position and styles to Html and Body element.
134
+ */
135
+ function cleanup() {
136
+ Object.assign(html.style, originalHtmlStyles);
137
+ Object.assign(body.style, originalBodyStyles);
138
+ html.scrollTop = scrollTop;
139
+ html.scrollLeft = scrollLeft;
140
+ html.removeAttribute("data-aksel-scroll-locked");
141
+ html.style.scrollBehavior = originalHtmlScrollBehavior;
142
+ }
143
+ /**
144
+ * On resize, restore original styles, then re-apply scroll lock next frame.
145
+ */
146
+ function handleResize() {
147
+ cleanup();
148
+ if (resizeRaf) {
149
+ cancelAnimationFrame(resizeRaf);
150
+ }
151
+ /**
152
+ * Wait until next frame to re-apply scroll lock ensuring layout has settled after resize.
153
+ */
154
+ resizeRaf = requestAnimationFrame(lockScroll);
155
+ }
156
+ lockScroll();
157
+ win.addEventListener("resize", handleResize);
158
+ return () => {
159
+ if (resizeRaf) {
160
+ cancelAnimationFrame(resizeRaf);
161
+ }
162
+ cleanup();
163
+ win.removeEventListener("resize", handleResize);
164
+ };
165
+ }
166
+ class ScrollLocker {
167
+ constructor() {
168
+ this.lockCount = 0;
169
+ this.restore = null;
170
+ this.timeoutLock = Timeout.create();
171
+ this.timeoutUnlock = Timeout.create();
172
+ /**
173
+ * Releases a lock
174
+ * - If last lock, unlock document-scroll.
175
+ * - If not last lock, do nothing.
176
+ */
177
+ this.release = () => {
178
+ this.lockCount -= 1;
179
+ if (this.lockCount === 0 && this.restore) {
180
+ this.timeoutUnlock.start(0, this.unlock);
181
+ }
182
+ };
183
+ this.unlock = () => {
184
+ var _a;
185
+ if (this.lockCount === 0 && this.restore) {
186
+ (_a = this.restore) === null || _a === void 0 ? void 0 : _a.call(this);
187
+ this.restore = null;
188
+ }
189
+ };
190
+ }
191
+ /**
192
+ * Aquires a new lock
193
+ * - If first lock, lock document-scroll.
194
+ * - If not first lock, do nothing.
195
+ */
196
+ acquire(referenceElement) {
197
+ this.lockCount += 1;
198
+ if (this.lockCount === 1 && this.restore === null) {
199
+ /*
200
+ * Delay locking to avoid layout thrashing when multiple locks/unlocks are requested in quick succession.
201
+ */
202
+ this.timeoutLock.start(0, () => this.lock(referenceElement));
203
+ }
204
+ return this.release;
205
+ }
206
+ lock(referenceElement) {
207
+ if (this.lockCount === 0 || this.restore !== null) {
208
+ return;
209
+ }
210
+ const doc = ownerDocument(referenceElement);
211
+ const html = doc.documentElement;
212
+ const htmlOverflowY = ownerWindow(html).getComputedStyle(html).overflowY;
213
+ /* If the site author already hid overflow on <html>, respect it and bail out. */
214
+ if (htmlOverflowY === "hidden" || htmlOverflowY === "clip") {
215
+ this.restore = () => { };
216
+ return;
217
+ }
218
+ const shouldUseBasicLock = isIOS || !hasInsetScrollbars(referenceElement);
219
+ /**
220
+ * On iOS, the standard scroll locking method does not work properly if the navbar is collapsed.
221
+ * The following must be researched extensively before activating standard scroll locking on iOS:
222
+ * - Textboxes must scroll into view when focused, and not cause a glitchy scroll animation.
223
+ * - The navbar must not force itself into view and cause layout shift.
224
+ * - Scroll containers must not flicker upon closing a popup when it has an exit animation.
225
+ */
226
+ this.restore = shouldUseBasicLock
227
+ ? preventScrollBasic(referenceElement)
228
+ : preventScrollStandard(referenceElement);
229
+ }
230
+ }
231
+ const SCROLL_LOCKER = new ScrollLocker();
232
+ /**
233
+ * Locks the scroll of the document when enabled.
234
+ * @param enabled - Whether to enable the scroll lock.
235
+ */
236
+ function useScrollLock(params) {
237
+ const { enabled = true, mounted, open, referenceElement = null } = params;
238
+ /**
239
+ * When closing elements with "sloppy clicks" (clicks that start inside the element and ends outside),
240
+ * animating out on WebKit browsers (mounted + not open) can cause the whole page to be selected.
241
+ * To prevent this, we temporarily disable user-select on body while the element is animating out.
242
+ * This bug might be fixed in newer WebKit versions.
243
+ *
244
+ * @see https://github.com/mui/base-ui/issues/1135
245
+ */
246
+ useClientLayoutEffect(() => {
247
+ if (enabled && isWebKit && mounted && !open) {
248
+ const doc = ownerDocument(referenceElement);
249
+ const originalUserSelect = doc.body.style.userSelect;
250
+ const originalWebkitUserSelect = doc.body.style.webkitUserSelect;
251
+ doc.body.style.userSelect = "none";
252
+ doc.body.style.webkitUserSelect = "none";
253
+ return () => {
254
+ doc.body.style.userSelect = originalUserSelect;
255
+ doc.body.style.webkitUserSelect = originalWebkitUserSelect;
256
+ };
257
+ }
258
+ return undefined;
259
+ }, [enabled, mounted, open, referenceElement]);
260
+ useClientLayoutEffect(() => {
261
+ if (!enabled) {
262
+ return undefined;
263
+ }
264
+ return SCROLL_LOCKER.acquire(referenceElement);
265
+ }, [enabled, referenceElement]);
266
+ }
267
+ export { useScrollLock };
268
+ //# sourceMappingURL=useScrollLock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useScrollLock.js","sourceRoot":"","sources":["../../../src/util/hooks/useScrollLock.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@navikt/ds-react",
3
- "version": "7.32.4",
3
+ "version": "7.33.0",
4
4
  "description": "React components from the Norwegian Labour and Welfare Administration.",
5
5
  "author": "Aksel, a team part of the Norwegian Labour and Welfare Administration.",
6
6
  "license": "MIT",
@@ -15,6 +15,10 @@
15
15
  "url": "git+https://github.com/navikt/aksel.git",
16
16
  "directory": "@navikt/core/react"
17
17
  },
18
+ "bugs": {
19
+ "url": "https://github.com/navikt/aksel/issues"
20
+ },
21
+ "homepage": "https://aksel.nav.no/designsystemet/",
18
22
  "sideEffects": false,
19
23
  "typings": "./esm/index.d.ts",
20
24
  "publishConfig": {
@@ -650,8 +654,8 @@
650
654
  "dependencies": {
651
655
  "@floating-ui/react": "0.27.8",
652
656
  "@floating-ui/react-dom": "^2.1.6",
653
- "@navikt/aksel-icons": "^7.32.4",
654
- "@navikt/ds-tokens": "^7.32.4",
657
+ "@navikt/aksel-icons": "^7.33.0",
658
+ "@navikt/ds-tokens": "^7.33.0",
655
659
  "clsx": "^2.1.0",
656
660
  "date-fns": "^4.0.0",
657
661
  "react-day-picker": "9.7.0"
@@ -667,7 +671,7 @@
667
671
  "copyfiles": "^2.4.1",
668
672
  "fast-glob": "3.2.11",
669
673
  "jscodeshift": "^0.15.1",
670
- "jsdom": "26.1.0",
674
+ "jsdom": "27.0.1",
671
675
  "react-dom": "^18.0.0",
672
676
  "react-router": "^7.6.2",
673
677
  "rimraf": "6.0.1",
@@ -678,8 +682,9 @@
678
682
  "vitest": "^3.2.4"
679
683
  },
680
684
  "peerDependencies": {
681
- "@types/react": ">=17.0.30",
682
- "react": ">=17.0.0 || >19.0.0-rc"
685
+ "@types/react": "^17.0.30 || ^18 || ^19",
686
+ "react": "^17 || ^18 || ^19",
687
+ "react-dom": "^17 || ^18 || ^19"
683
688
  },
684
689
  "peerDependenciesMeta": {
685
690
  "@types/react": {
@@ -2,7 +2,6 @@ import { fireEvent, render, screen, waitFor } from "@testing-library/react";
2
2
  import React, { useState } from "react";
3
3
  import { describe, expect, test } from "vitest";
4
4
  import { Button, Modal } from "..";
5
- import { BODY_CLASS, BODY_CLASS_LEGACY } from "./ModalUtils";
6
5
 
7
6
  const Test = () => {
8
7
  const [open, setOpen] = useState(true);
@@ -29,35 +28,25 @@ describe("Modal", () => {
29
28
  expect(screen.getByText("Foobar")).not.toBeVisible();
30
29
  });
31
30
 
32
- test("should toggle body class", async () => {
31
+ test("should toggle scroll lock", async () => {
33
32
  render(<Test />);
34
- expect(document.body.classList).toContain(BODY_CLASS);
35
- expect(document.body.classList).toContain(BODY_CLASS_LEGACY);
36
33
 
37
- fireEvent.click(screen.getByText("Close"));
38
34
  await waitFor(() => {
39
- expect(document.body.classList).not.toContain(BODY_CLASS);
35
+ expect(document.documentElement.style.overflowX).toBe("hidden");
40
36
  });
41
37
  await waitFor(() => {
42
- expect(document.body.classList).not.toContain(BODY_CLASS_LEGACY);
38
+ expect(document.documentElement.style.overflowY).toBe("hidden");
39
+ });
40
+ await waitFor(() => {
41
+ expect(document.documentElement.style.scrollBehavior).toBe("unset");
42
+ });
43
+ await waitFor(() => {
44
+ expect(document.documentElement.style.scrollbarGutter).toBe("stable");
43
45
  });
44
- });
45
-
46
- test("should toggle body class when using portal", async () => {
47
- render(
48
- <Modal portal open onClose={() => null} aria-label="Test">
49
- <Modal.Header />
50
- </Modal>,
51
- );
52
- expect(document.body.classList).toContain(BODY_CLASS);
53
- expect(document.body.classList).toContain(BODY_CLASS_LEGACY);
54
46
 
55
- fireEvent.click(screen.getByRole("button"));
56
- await waitFor(() =>
57
- expect(document.body.classList).not.toContain(BODY_CLASS),
58
- );
59
- await waitFor(() =>
60
- expect(document.body.classList).not.toContain(BODY_CLASS_LEGACY),
61
- );
47
+ fireEvent.click(screen.getByText("Close"));
48
+ await waitFor(() => {
49
+ expect(document.documentElement.style.cssText).toBe("");
50
+ });
62
51
  });
63
52
  });
@@ -8,6 +8,7 @@ import { Detail, Heading } from "../typography";
8
8
  import { composeEventHandlers } from "../util/composeEventHandlers";
9
9
  import { useId } from "../util/hooks";
10
10
  import { useMergeRefs } from "../util/hooks/useMergeRefs";
11
+ import { useScrollLock } from "../util/hooks/useScrollLock";
11
12
  import { ModalContextProvider, useModalContext } from "./Modal.context";
12
13
  import ModalBody from "./ModalBody";
13
14
  import ModalFooter from "./ModalFooter";
@@ -17,6 +18,7 @@ import {
17
18
  coordsAreInside,
18
19
  getCloseHandler,
19
20
  useBodyScrollLock,
21
+ useIsModalOpen,
20
22
  } from "./ModalUtils";
21
23
  import dialogPolyfill, { needPolyfill } from "./dialog-polyfill";
22
24
  import { ModalProps } from "./types";
@@ -110,6 +112,9 @@ export const Modal = forwardRef<HTMLDialogElement, ModalProps>(
110
112
 
111
113
  const dateContext = useDateInputContext(false);
112
114
  const isNested = useModalContext(false) !== undefined;
115
+
116
+ const isModalOpen = useIsModalOpen(modalRef.current);
117
+
113
118
  if (isNested && !dateContext) {
114
119
  console.error("Modals should not be nested");
115
120
  }
@@ -144,6 +149,17 @@ export const Modal = forwardRef<HTMLDialogElement, ModalProps>(
144
149
  }
145
150
  }, [portalNode, open]);
146
151
 
152
+ useScrollLock({
153
+ enabled: isModalOpen,
154
+ mounted: isModalOpen,
155
+ open: isModalOpen,
156
+ referenceElement: modalRef.current,
157
+ });
158
+
159
+ /**
160
+ * TODO: Kept for legacy support.
161
+ * - Remove utility in v8 and deprecate body-classes in ModalUtils.ts
162
+ */
147
163
  useBodyScrollLock(modalRef, portalNode, isNested);
148
164
 
149
165
  const isWidthPreset =
@@ -1,4 +1,4 @@
1
- import React from "react";
1
+ import React, { useEffect } from "react";
2
2
  import { ownerDocument } from "../util/owner";
3
3
  import type { ModalProps } from "./types";
4
4
 
@@ -28,10 +28,36 @@ export function getCloseHandler(
28
28
  return () => modalRef.current?.close();
29
29
  }
30
30
 
31
+ function useIsModalOpen(modalRef: HTMLDialogElement | null) {
32
+ const [isOpen, setIsOpen] = React.useState<boolean>(false);
33
+
34
+ useEffect(() => {
35
+ if (!modalRef) {
36
+ return;
37
+ }
38
+
39
+ setIsOpen(modalRef.open);
40
+
41
+ const observer = new MutationObserver(() => {
42
+ setIsOpen(modalRef.open);
43
+ });
44
+
45
+ observer.observe(modalRef, {
46
+ attributes: true,
47
+ attributeFilter: ["open"],
48
+ });
49
+
50
+ return () => {
51
+ observer.disconnect();
52
+ };
53
+ }, [modalRef]);
54
+
55
+ return isOpen;
56
+ }
57
+
31
58
  export const BODY_CLASS_LEGACY = "navds-modal__document-body";
32
- export const BODY_CLASS = "aksel-modal__document-body";
33
59
 
34
- export function useBodyScrollLock(
60
+ function useBodyScrollLock(
35
61
  modalRef: React.RefObject<HTMLDialogElement | null>,
36
62
  portalNode: HTMLElement | null,
37
63
  isNested: boolean,
@@ -50,14 +76,14 @@ export function useBodyScrollLock(
50
76
 
51
77
  // In case `open` is true initially
52
78
  if (modalRef.current.open) {
53
- ownerDoc.body.classList.add(BODY_CLASS, BODY_CLASS_LEGACY);
79
+ ownerDoc.body.classList.add(BODY_CLASS_LEGACY);
54
80
  }
55
81
 
56
82
  const observer = new MutationObserver(() => {
57
83
  if (modalRef.current?.open) {
58
- ownerDoc.body.classList.add(BODY_CLASS, BODY_CLASS_LEGACY);
84
+ ownerDoc.body.classList.add(BODY_CLASS_LEGACY);
59
85
  } else {
60
- ownerDoc.body.classList.remove(BODY_CLASS, BODY_CLASS_LEGACY);
86
+ ownerDoc.body.classList.remove(BODY_CLASS_LEGACY);
61
87
  }
62
88
  });
63
89
 
@@ -69,7 +95,9 @@ export function useBodyScrollLock(
69
95
  return () => {
70
96
  observer.disconnect();
71
97
  // In case modal is unmounted before it's closed
72
- ownerDoc.body.classList.remove(BODY_CLASS, BODY_CLASS_LEGACY);
98
+ ownerDoc.body.classList.remove(BODY_CLASS_LEGACY);
73
99
  };
74
100
  }, [modalRef, portalNode, isNested]);
75
101
  }
102
+
103
+ export { useIsModalOpen, useBodyScrollLock };
@@ -22,6 +22,11 @@ interface ProcessProps extends React.HTMLAttributes<HTMLOListElement> {
22
22
  * @default false
23
23
  */
24
24
  hideStatusText?: boolean;
25
+ /**
26
+ * Indicates that the process is truncated and that there are more Events
27
+ * not shown either before, after or on both sides of the current list.
28
+ */
29
+ isTruncated?: "start" | "end" | "both";
25
30
  }
26
31
 
27
32
  type ProcessContextProps = Pick<ProcessProps, "hideStatusText"> & {
@@ -100,6 +105,7 @@ export const Process: ProcessComponent = forwardRef<
100
105
  className,
101
106
  hideStatusText = false,
102
107
  id,
108
+ isTruncated,
103
109
  ...restProps
104
110
  }: ProcessProps,
105
111
  forwardedRef,
@@ -157,6 +163,7 @@ export const Process: ProcessComponent = forwardRef<
157
163
  className={cn("navds-process", className)}
158
164
  id={rootId}
159
165
  aria-controls={activeChildId}
166
+ data-truncated={isTruncated}
160
167
  >
161
168
  <ProcessContextProvider
162
169
  hideStatusText={hideStatusText}
@@ -237,6 +244,7 @@ export const ProcessEvent = forwardRef<HTMLLIElement, ProcessEventProps>(
237
244
  data-process-event=""
238
245
  data-status={status}
239
246
  >
247
+ <ProcessLine position="start" />
240
248
  <div className={cn("navds-process__item")}>
241
249
  <ProcessBullet>{bullet}</ProcessBullet>
242
250
 
@@ -256,7 +264,7 @@ export const ProcessEvent = forwardRef<HTMLLIElement, ProcessEventProps>(
256
264
  )}
257
265
  </div>
258
266
  </div>
259
- <ProcessLine />
267
+ <ProcessLine position="end" />
260
268
  </li>
261
269
  );
262
270
  },
@@ -346,10 +354,16 @@ const ProcessBullet = ({ children }: ProcessBulletProps) => {
346
354
  };
347
355
 
348
356
  /* ------------------------------ Process Line ------------------------------ */
349
- const ProcessLine = () => {
357
+
358
+ type ProcessLineProps = {
359
+ position?: "start" | "end";
360
+ };
361
+ const ProcessLine = ({ position }: ProcessLineProps) => {
350
362
  const { cn } = useRenameCSS();
351
363
 
352
- return <span className={cn("navds-process__line")} />;
364
+ return (
365
+ <span className={cn("navds-process__line")} data-position={position} />
366
+ );
353
367
  };
354
368
 
355
369
  /* -------------------------- Process exports ------------------------- */
@@ -94,6 +94,10 @@ export const Pin = forwardRef<HTMLButtonElement, TimelinePinProps>(
94
94
  left: "right",
95
95
  }[placement.split("-")[0]];
96
96
 
97
+ const label = translate("Pin.pin", {
98
+ date: format(date, translate("dateFormat")),
99
+ });
100
+
97
101
  return (
98
102
  <>
99
103
  <div
@@ -104,9 +108,7 @@ export const Pin = forwardRef<HTMLButtonElement, TimelinePinProps>(
104
108
  {...rest}
105
109
  ref={mergedRef}
106
110
  className={cn("navds-timeline__pin-button")}
107
- aria-label={translate("Pin.pin", {
108
- date: format(date, translate("dateFormat")),
109
- })}
111
+ aria-label={label}
110
112
  type="button"
111
113
  aria-expanded={children ? open : undefined}
112
114
  {...getReferenceProps({
@@ -133,6 +135,7 @@ export const Pin = forwardRef<HTMLButtonElement, TimelinePinProps>(
133
135
  data-placement={placement}
134
136
  ref={refs.setFloating}
135
137
  role="dialog"
138
+ aria-label={label}
136
139
  {...getFloatingProps()}
137
140
  style={floatingStyles}
138
141
  >