@olenbetong/appframe-ds 1.3.3 → 1.3.4
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 +6 -0
- package/package.json +3 -3
- package/src/AfLookup/Lookup.tsx +6 -4
- package/src/grid/slots/index.tsx +30 -3
- package/src/grid/slots/slots.test.tsx +39 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @olenbetong/appframe-ds
|
|
2
2
|
|
|
3
|
+
## 1.3.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 9c2ba29: The data grid no longer freezes the page for seconds when it loads. Its loading overlay mounts one skeleton per visible cell, and Designsystemet's `Skeleton` calls `document.getAnimations()` on mount to synchronize its animation, so hundreds of skeletons scanned hundreds of animations each. The grid's skeleton slot now renders the same markup without that hook.
|
|
8
|
+
|
|
3
9
|
## 1.3.3
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@olenbetong/appframe-ds",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.4",
|
|
4
4
|
"description": "Components that bind Designsystemet components to Appframe data objects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./es/index.d.ts",
|
|
@@ -76,13 +76,13 @@
|
|
|
76
76
|
"temporal-polyfill": "1.0.5"
|
|
77
77
|
},
|
|
78
78
|
"devDependencies": {
|
|
79
|
-
"@digdir/designsystemet-react": "1.
|
|
79
|
+
"@digdir/designsystemet-react": "1.23.0",
|
|
80
80
|
"@emotion/react": "^11.14.0",
|
|
81
81
|
"@emotion/styled": "^11.14.1",
|
|
82
82
|
"@mui/material": "9.4.0",
|
|
83
83
|
"@mui/x-data-grid-premium": "8.29.3",
|
|
84
84
|
"@mui/x-license": "8.29.3",
|
|
85
|
-
"@navikt/aksel-icons": "8.
|
|
85
|
+
"@navikt/aksel-icons": "8.17.2",
|
|
86
86
|
"@testing-library/dom": "^10.4.2",
|
|
87
87
|
"@testing-library/jest-dom": "^7.0.1",
|
|
88
88
|
"@testing-library/react": "16.3.3",
|
package/src/AfLookup/Lookup.tsx
CHANGED
|
@@ -5,7 +5,7 @@ import { MagnifyingGlassIcon, XMarkIcon } from "@navikt/aksel-icons";
|
|
|
5
5
|
import { type DistributiveOmit, useLookupState } from "@olenbetong/appframe-react";
|
|
6
6
|
|
|
7
7
|
import clsx from "clsx";
|
|
8
|
-
import {
|
|
8
|
+
import { useState } from "react";
|
|
9
9
|
import { createPortal } from "react-dom";
|
|
10
10
|
|
|
11
11
|
import { LookupCombobox, type LookupComboboxProps } from "./LookupCombobox.js";
|
|
@@ -63,7 +63,7 @@ export function useLookup<T extends Record<string, unknown>>({
|
|
|
63
63
|
getAnchorElement,
|
|
64
64
|
scrollParentSelector: "[data-virtualized-scroller]",
|
|
65
65
|
});
|
|
66
|
-
let
|
|
66
|
+
let [initialSearchValue, setInitialSearchValue] = useState<string | undefined>(undefined);
|
|
67
67
|
|
|
68
68
|
function handleChange(item: T | null) {
|
|
69
69
|
props.onChange?.(item);
|
|
@@ -71,7 +71,9 @@ export function useLookup<T extends Record<string, unknown>>({
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
function handleOpenDialog(searchValue?: string) {
|
|
74
|
-
|
|
74
|
+
// openDialog renders synchronously (flushSync), which includes this update, so the combobox
|
|
75
|
+
// mounts with the search value already set.
|
|
76
|
+
setInitialSearchValue(searchValue);
|
|
75
77
|
openDialog();
|
|
76
78
|
}
|
|
77
79
|
|
|
@@ -93,7 +95,7 @@ export function useLookup<T extends Record<string, unknown>>({
|
|
|
93
95
|
{open && (
|
|
94
96
|
<LookupCombobox
|
|
95
97
|
{...props}
|
|
96
|
-
initialSearchValue={
|
|
98
|
+
initialSearchValue={initialSearchValue}
|
|
97
99
|
onClose={() => dialogRef.current?.close()}
|
|
98
100
|
onChange={handleChange}
|
|
99
101
|
/>
|
package/src/grid/slots/index.tsx
CHANGED
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
Checkbox,
|
|
7
7
|
Divider,
|
|
8
8
|
Select,
|
|
9
|
-
Skeleton,
|
|
10
9
|
Spinner,
|
|
11
10
|
Switch,
|
|
12
11
|
Tag,
|
|
@@ -272,11 +271,39 @@ function BaseLinearProgress(props: GridSlotProps["baseLinearProgress"]) {
|
|
|
272
271
|
return <div {...(other as any)} role="progressbar" className={clsx("AfGrid-linearProgress", className)} />;
|
|
273
272
|
}
|
|
274
273
|
|
|
274
|
+
/**
|
|
275
|
+
* Renders Designsystemet's skeleton markup rather than its `Skeleton`
|
|
276
|
+
* component. The component synchronizes its animation with every other
|
|
277
|
+
* skeleton by calling `document.getAnimations()` when it mounts, and the grid's
|
|
278
|
+
* loading overlay mounts one per visible cell in the same commit - hundreds of
|
|
279
|
+
* scans over hundreds of animations, which froze the page for seconds on every
|
|
280
|
+
* load. Skeletons mounted together start their animation together anyway.
|
|
281
|
+
*/
|
|
275
282
|
function BaseSkeleton(props: GridSlotProps["baseSkeleton"]) {
|
|
276
|
-
let { variant, width, height, ...other } = props;
|
|
283
|
+
let { variant, width, height, className, style, ...other } = props;
|
|
284
|
+
let dsVariant = variant === "circular" ? "circle" : variant;
|
|
285
|
+
|
|
286
|
+
if (dsVariant === "text") {
|
|
287
|
+
return (
|
|
288
|
+
<span
|
|
289
|
+
{...(other as any)}
|
|
290
|
+
aria-hidden="true"
|
|
291
|
+
className={clsx("ds-skeleton", className)}
|
|
292
|
+
data-text={"-".repeat(Number(width) || 1)}
|
|
293
|
+
data-variant="text"
|
|
294
|
+
style={style}
|
|
295
|
+
/>
|
|
296
|
+
);
|
|
297
|
+
}
|
|
277
298
|
|
|
278
299
|
return (
|
|
279
|
-
<
|
|
300
|
+
<span
|
|
301
|
+
{...(other as any)}
|
|
302
|
+
aria-hidden="true"
|
|
303
|
+
className={clsx("ds-skeleton", className)}
|
|
304
|
+
data-variant={dsVariant}
|
|
305
|
+
style={{ width, height, ...style }}
|
|
306
|
+
/>
|
|
280
307
|
);
|
|
281
308
|
}
|
|
282
309
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { render } from "@testing-library/react";
|
|
2
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
|
|
4
|
+
import { dsBaseSlots } from "./index.js";
|
|
5
|
+
|
|
6
|
+
const BaseSkeleton = dsBaseSlots.baseSkeleton as React.ComponentType<Record<string, unknown>>;
|
|
7
|
+
|
|
8
|
+
describe("baseSkeleton", () => {
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
vi.restoreAllMocks();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("renders Designsystemet's skeleton markup", () => {
|
|
14
|
+
let { container } = render(<BaseSkeleton variant="rectangular" width={80} height={12} />);
|
|
15
|
+
let skeleton = container.firstElementChild as HTMLElement;
|
|
16
|
+
|
|
17
|
+
expect(skeleton).toHaveClass("ds-skeleton");
|
|
18
|
+
expect(skeleton).toHaveAttribute("data-variant", "rectangular");
|
|
19
|
+
expect(skeleton).toHaveAttribute("aria-hidden", "true");
|
|
20
|
+
expect(skeleton.style.width).toBe("80px");
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("does not scan the page's animations when many mount at once", () => {
|
|
24
|
+
// The loading overlay mounts one skeleton per visible cell. Designsystemet's
|
|
25
|
+
// Skeleton calls document.getAnimations() for each, which froze the page.
|
|
26
|
+
let getAnimations = vi.fn(() => []);
|
|
27
|
+
Object.defineProperty(document, "getAnimations", { value: getAnimations, configurable: true });
|
|
28
|
+
|
|
29
|
+
render(
|
|
30
|
+
<>
|
|
31
|
+
{Array.from({ length: 200 }, (_, index) => (
|
|
32
|
+
<BaseSkeleton key={index} variant="text" width={10} />
|
|
33
|
+
))}
|
|
34
|
+
</>,
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
expect(getAnimations).not.toHaveBeenCalled();
|
|
38
|
+
});
|
|
39
|
+
});
|