@usefui/components 1.5.3 → 1.6.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 +6 -0
- package/dist/index.d.mts +237 -1
- package/dist/index.d.ts +237 -1
- package/dist/index.js +702 -231
- package/dist/index.mjs +668 -210
- package/package.json +12 -12
- package/src/__tests__/MessageBubble.test.tsx +179 -0
- package/src/__tests__/Shimmer.test.tsx +122 -0
- package/src/__tests__/Tree.test.tsx +275 -0
- package/src/accordion/hooks/index.tsx +3 -1
- package/src/badge/index.tsx +2 -3
- package/src/checkbox/hooks/index.tsx +5 -1
- package/src/collapsible/hooks/index.tsx +3 -1
- package/src/dialog/hooks/index.tsx +5 -1
- package/src/dropdown/hooks/index.tsx +3 -1
- package/src/dropdown/index.tsx +9 -9
- package/src/field/hooks/index.tsx +5 -1
- package/src/index.ts +6 -0
- package/src/message-bubble/MessageBubble.stories.tsx +91 -0
- package/src/message-bubble/hooks/index.tsx +41 -0
- package/src/message-bubble/index.tsx +153 -0
- package/src/message-bubble/styles/index.ts +61 -0
- package/src/otp-field/hooks/index.tsx +3 -1
- package/src/otp-field/index.tsx +5 -3
- package/src/sheet/hooks/index.tsx +5 -1
- package/src/shimmer/Shimmer.stories.tsx +95 -0
- package/src/shimmer/index.tsx +64 -0
- package/src/shimmer/styles/index.ts +33 -0
- package/src/switch/hooks/index.tsx +5 -1
- package/src/tabs/hooks/index.tsx +5 -1
- package/src/toolbar/hooks/index.tsx +5 -1
- package/src/tree/Tree.stories.tsx +139 -0
- package/src/tree/hooks/tree-node-provider.tsx +50 -0
- package/src/tree/hooks/tree-provider.tsx +75 -0
- package/src/tree/index.tsx +231 -0
- package/src/tree/styles/index.ts +23 -0
- package/tsconfig.build.json +20 -0
- package/tsconfig.json +1 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usefui/components",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "Open Source React components library",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -19,21 +19,21 @@
|
|
|
19
19
|
"@types/react": "*",
|
|
20
20
|
"@types/react-dom": "*",
|
|
21
21
|
"@types/styled-components": "*",
|
|
22
|
-
"react": "
|
|
23
|
-
"react-dom": "
|
|
24
|
-
"styled-components": "
|
|
22
|
+
"react": ">=16.8",
|
|
23
|
+
"react-dom": ">=16.8",
|
|
24
|
+
"styled-components": ">=5.1.34"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@types/react": "^
|
|
28
|
-
"@types/react-dom": "^
|
|
29
|
-
"@types/styled-components": "^5.1.
|
|
30
|
-
"react": "^
|
|
31
|
-
"react-dom": "^
|
|
27
|
+
"@types/react": "^19.2.14",
|
|
28
|
+
"@types/react-dom": "^19.2.3",
|
|
29
|
+
"@types/styled-components": "^5.1.36",
|
|
30
|
+
"react": "^19.2.4",
|
|
31
|
+
"react-dom": "^19.2.4",
|
|
32
32
|
"styled-components": "^5.3.11",
|
|
33
33
|
"typescript": "^5.9.3",
|
|
34
|
-
"@usefui/core": "^1.
|
|
35
|
-
"@usefui/hooks": "^1.
|
|
36
|
-
"@usefui/tokens": "^1.
|
|
34
|
+
"@usefui/core": "^1.4.0",
|
|
35
|
+
"@usefui/hooks": "^1.4.0",
|
|
36
|
+
"@usefui/tokens": "^1.6.0"
|
|
37
37
|
},
|
|
38
38
|
"scripts": {
|
|
39
39
|
"test": "vitest run --coverage --logHeapUsage",
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
import { test, vi, afterEach, describe, expect } from "vitest";
|
|
4
|
+
import { screen, render, cleanup, waitFor } from "@testing-library/react";
|
|
5
|
+
import { axe, toHaveNoViolations } from "jest-axe";
|
|
6
|
+
|
|
7
|
+
import { MessageBubble } from "../../src/message-bubble";
|
|
8
|
+
|
|
9
|
+
const MOCK_DATE = new Date("2026-03-17T13:00:00Z");
|
|
10
|
+
const MOCK_MESSAGE = "test-message-content";
|
|
11
|
+
|
|
12
|
+
afterEach(async () => {
|
|
13
|
+
vi.clearAllMocks();
|
|
14
|
+
vi.resetModules();
|
|
15
|
+
cleanup();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
expect.extend(toHaveNoViolations);
|
|
19
|
+
|
|
20
|
+
describe("MessageBubble", () => {
|
|
21
|
+
test("Renders without accessibility violation", async () => {
|
|
22
|
+
const { container } = render(
|
|
23
|
+
<MessageBubble.Root>
|
|
24
|
+
<MessageBubble side="left">
|
|
25
|
+
<MessageBubble.Content>{MOCK_MESSAGE}</MessageBubble.Content>
|
|
26
|
+
<MessageBubble.Meta createdAt={MOCK_DATE} />
|
|
27
|
+
</MessageBubble>
|
|
28
|
+
</MessageBubble.Root>,
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const ComponentContainer = await axe(container);
|
|
32
|
+
expect(ComponentContainer).toHaveNoViolations();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("Renders with accessibility definitions on the left side", async () => {
|
|
36
|
+
render(
|
|
37
|
+
<MessageBubble.Root>
|
|
38
|
+
<MessageBubble side="left">
|
|
39
|
+
<MessageBubble.Content>{MOCK_MESSAGE}</MessageBubble.Content>
|
|
40
|
+
<MessageBubble.Meta createdAt={MOCK_DATE} />
|
|
41
|
+
</MessageBubble>
|
|
42
|
+
</MessageBubble.Root>,
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
await waitFor(() => {
|
|
46
|
+
const bubble = screen.getByLabelText("message-bubble-left");
|
|
47
|
+
expect(bubble).toBeDefined();
|
|
48
|
+
expect(bubble.getAttribute("data-side")).toBe("left");
|
|
49
|
+
|
|
50
|
+
const meta = screen.getByLabelText("message-bubble-meta-left");
|
|
51
|
+
expect(meta).toBeDefined();
|
|
52
|
+
expect(meta.getAttribute("data-side")).toBe("left");
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("Renders with accessibility definitions on the right side", async () => {
|
|
57
|
+
render(
|
|
58
|
+
<MessageBubble.Root>
|
|
59
|
+
<MessageBubble side="right">
|
|
60
|
+
<MessageBubble.Content>{MOCK_MESSAGE}</MessageBubble.Content>
|
|
61
|
+
<MessageBubble.Meta createdAt={MOCK_DATE} />
|
|
62
|
+
</MessageBubble>
|
|
63
|
+
</MessageBubble.Root>,
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
await waitFor(() => {
|
|
67
|
+
const bubble = screen.getByLabelText("message-bubble-right");
|
|
68
|
+
expect(bubble).toBeDefined();
|
|
69
|
+
expect(bubble.getAttribute("data-side")).toBe("right");
|
|
70
|
+
|
|
71
|
+
const meta = screen.getByLabelText("message-bubble-meta-right");
|
|
72
|
+
expect(meta).toBeDefined();
|
|
73
|
+
expect(meta.getAttribute("data-side")).toBe("right");
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("Propagates side through context to all compound components", async () => {
|
|
78
|
+
render(
|
|
79
|
+
<MessageBubble.Root>
|
|
80
|
+
<MessageBubble side="right">
|
|
81
|
+
<MessageBubble.Content>{MOCK_MESSAGE}</MessageBubble.Content>
|
|
82
|
+
<MessageBubble.Meta createdAt={MOCK_DATE} />
|
|
83
|
+
</MessageBubble>
|
|
84
|
+
</MessageBubble.Root>,
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
await waitFor(() => {
|
|
88
|
+
const bubble = screen.getByLabelText("message-bubble-right");
|
|
89
|
+
const meta = screen.getByLabelText("message-bubble-meta-right");
|
|
90
|
+
|
|
91
|
+
expect(bubble.getAttribute("data-side")).toBe("right");
|
|
92
|
+
expect(meta.getAttribute("data-side")).toBe("right");
|
|
93
|
+
|
|
94
|
+
const content = meta.closest("[data-side]");
|
|
95
|
+
expect(content).toBeDefined();
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("Renders content with the correct message text", () => {
|
|
100
|
+
render(
|
|
101
|
+
<MessageBubble.Root>
|
|
102
|
+
<MessageBubble side="left">
|
|
103
|
+
<MessageBubble.Content>{MOCK_MESSAGE}</MessageBubble.Content>
|
|
104
|
+
<MessageBubble.Meta createdAt={MOCK_DATE} />
|
|
105
|
+
</MessageBubble>
|
|
106
|
+
</MessageBubble.Root>,
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
expect(screen.getByText(MOCK_MESSAGE)).toBeDefined();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("Renders Meta with a correctly formatted date", () => {
|
|
113
|
+
render(
|
|
114
|
+
<MessageBubble.Root>
|
|
115
|
+
<MessageBubble side="left">
|
|
116
|
+
<MessageBubble.Content>{MOCK_MESSAGE}</MessageBubble.Content>
|
|
117
|
+
<MessageBubble.Meta createdAt={MOCK_DATE} />
|
|
118
|
+
</MessageBubble>
|
|
119
|
+
</MessageBubble.Root>,
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
const expected = new Intl.DateTimeFormat("en-US", {
|
|
123
|
+
dateStyle: "medium",
|
|
124
|
+
timeStyle: "short",
|
|
125
|
+
}).format(MOCK_DATE);
|
|
126
|
+
|
|
127
|
+
expect(screen.getByText(expected)).toBeDefined();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("Renders raw prop on all compounds when set", async () => {
|
|
131
|
+
render(
|
|
132
|
+
<MessageBubble.Root>
|
|
133
|
+
<MessageBubble side="left" raw>
|
|
134
|
+
<MessageBubble.Content raw>{MOCK_MESSAGE}</MessageBubble.Content>
|
|
135
|
+
<MessageBubble.Meta createdAt={MOCK_DATE} raw />
|
|
136
|
+
</MessageBubble>
|
|
137
|
+
</MessageBubble.Root>,
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
await waitFor(() => {
|
|
141
|
+
const bubble = screen.getByLabelText("message-bubble-left");
|
|
142
|
+
const meta = screen.getByLabelText("message-bubble-meta-left");
|
|
143
|
+
|
|
144
|
+
expect(bubble.getAttribute("data-raw")).toBe("true");
|
|
145
|
+
expect(meta.getAttribute("data-raw")).toBe("true");
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test("Renders multiple bubbles in a conversation with correct sides", async () => {
|
|
150
|
+
render(
|
|
151
|
+
<React.Fragment>
|
|
152
|
+
<MessageBubble.Root>
|
|
153
|
+
<MessageBubble side="left">
|
|
154
|
+
<MessageBubble.Content>Hello</MessageBubble.Content>
|
|
155
|
+
<MessageBubble.Meta createdAt={MOCK_DATE} />
|
|
156
|
+
</MessageBubble>
|
|
157
|
+
</MessageBubble.Root>
|
|
158
|
+
<MessageBubble.Root>
|
|
159
|
+
<MessageBubble side="right">
|
|
160
|
+
<MessageBubble.Content>Hi back</MessageBubble.Content>
|
|
161
|
+
<MessageBubble.Meta createdAt={MOCK_DATE} />
|
|
162
|
+
</MessageBubble>
|
|
163
|
+
</MessageBubble.Root>
|
|
164
|
+
</React.Fragment>,
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
await waitFor(() => {
|
|
168
|
+
expect(screen.getByText("Hello")).toBeDefined();
|
|
169
|
+
expect(screen.getByText("Hi back")).toBeDefined();
|
|
170
|
+
|
|
171
|
+
expect(
|
|
172
|
+
screen.getByLabelText("message-bubble-left").getAttribute("data-side"),
|
|
173
|
+
).toBe("left");
|
|
174
|
+
expect(
|
|
175
|
+
screen.getByLabelText("message-bubble-right").getAttribute("data-side"),
|
|
176
|
+
).toBe("right");
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
});
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
import { test, vi, afterEach, describe, expect } from "vitest";
|
|
4
|
+
import { screen, render, cleanup } from "@testing-library/react";
|
|
5
|
+
import { axe, toHaveNoViolations } from "jest-axe";
|
|
6
|
+
|
|
7
|
+
import { Shimmer } from "../../src/shimmer";
|
|
8
|
+
|
|
9
|
+
afterEach(async () => {
|
|
10
|
+
vi.clearAllMocks();
|
|
11
|
+
vi.resetModules();
|
|
12
|
+
cleanup();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
expect.extend(toHaveNoViolations);
|
|
16
|
+
|
|
17
|
+
describe("Shimmer", () => {
|
|
18
|
+
test("Renders without accessibility violations", async () => {
|
|
19
|
+
const { container } = render(<Shimmer>Loading your content…</Shimmer>);
|
|
20
|
+
|
|
21
|
+
const ComponentContainer = await axe(container);
|
|
22
|
+
expect(ComponentContainer).toHaveNoViolations();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("Renders with accessibility definition", () => {
|
|
26
|
+
render(<Shimmer>Loading your content…</Shimmer>);
|
|
27
|
+
|
|
28
|
+
const shimmer = screen.getByLabelText("shimmer-text");
|
|
29
|
+
expect(shimmer).toBeDefined();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("Renders children correctly", () => {
|
|
33
|
+
render(<Shimmer>Loading your content…</Shimmer>);
|
|
34
|
+
|
|
35
|
+
expect(screen.getByText("Loading your content…")).toBeDefined();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("Applies correct data attributes for default props", () => {
|
|
39
|
+
render(<Shimmer>Default shimmer</Shimmer>);
|
|
40
|
+
|
|
41
|
+
const shimmer = screen.getByLabelText("shimmer-text");
|
|
42
|
+
expect(shimmer.getAttribute("data-raw")).toBe("false");
|
|
43
|
+
expect(shimmer.getAttribute("data-duration")).toBe("2");
|
|
44
|
+
expect(shimmer.getAttribute("data-spread")).toBe("200");
|
|
45
|
+
expect(shimmer.getAttribute("data-shimmer-color")).toBe(
|
|
46
|
+
"var(--font-color-alpha-60)",
|
|
47
|
+
);
|
|
48
|
+
expect(shimmer.getAttribute("data-base-color")).toBe(
|
|
49
|
+
"var(--font-color-alpha-30)",
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("Applies correct data attributes for custom props", () => {
|
|
54
|
+
render(
|
|
55
|
+
<Shimmer
|
|
56
|
+
duration={5}
|
|
57
|
+
spread={300}
|
|
58
|
+
shimmerColor="var(--color-brand-alpha-80)"
|
|
59
|
+
baseColor="var(--color-brand-alpha-30)"
|
|
60
|
+
>
|
|
61
|
+
Custom shimmer
|
|
62
|
+
</Shimmer>,
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const shimmer = screen.getByLabelText("shimmer-text");
|
|
66
|
+
expect(shimmer.getAttribute("data-duration")).toBe("5");
|
|
67
|
+
expect(shimmer.getAttribute("data-spread")).toBe("300");
|
|
68
|
+
expect(shimmer.getAttribute("data-shimmer-color")).toBe(
|
|
69
|
+
"var(--color-brand-alpha-80)",
|
|
70
|
+
);
|
|
71
|
+
expect(shimmer.getAttribute("data-base-color")).toBe(
|
|
72
|
+
"var(--color-brand-alpha-30)",
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("Applies data-raw attribute when raw prop is true", () => {
|
|
77
|
+
render(<Shimmer raw>Raw shimmer</Shimmer>);
|
|
78
|
+
|
|
79
|
+
const shimmer = screen.getByLabelText("shimmer-text");
|
|
80
|
+
expect(shimmer.getAttribute("data-raw")).toBe("true");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("Renders with a custom aria-label", () => {
|
|
84
|
+
render(
|
|
85
|
+
<Shimmer aria-label="custom-shimmer-label">Accessible shimmer</Shimmer>,
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
expect(screen.getByLabelText("custom-shimmer-label")).toBeDefined();
|
|
89
|
+
expect(() => screen.getByLabelText("shimmer-text")).toThrow();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test("Renders multiple instances independently", () => {
|
|
93
|
+
render(
|
|
94
|
+
<React.Fragment>
|
|
95
|
+
<Shimmer aria-label="shimmer-1">First</Shimmer>
|
|
96
|
+
<Shimmer aria-label="shimmer-2" duration={5}>
|
|
97
|
+
Second
|
|
98
|
+
</Shimmer>
|
|
99
|
+
</React.Fragment>,
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
const first = screen.getByLabelText("shimmer-1");
|
|
103
|
+
const second = screen.getByLabelText("shimmer-2");
|
|
104
|
+
|
|
105
|
+
expect(first).toBeDefined();
|
|
106
|
+
expect(second).toBeDefined();
|
|
107
|
+
expect(first.getAttribute("data-duration")).toBe("2");
|
|
108
|
+
expect(second.getAttribute("data-duration")).toBe("5");
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("Forwards extra HTML span attributes", () => {
|
|
112
|
+
render(
|
|
113
|
+
<Shimmer id="my-shimmer" className="custom-class">
|
|
114
|
+
Forwarded attrs
|
|
115
|
+
</Shimmer>,
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
const shimmer = screen.getByLabelText("shimmer-text");
|
|
119
|
+
expect(shimmer.getAttribute("id")).toBe("my-shimmer");
|
|
120
|
+
expect(shimmer.getAttribute("class")).toContain("custom-class");
|
|
121
|
+
});
|
|
122
|
+
});
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
import { test, vi, afterEach, describe, expect } from "vitest";
|
|
4
|
+
import {
|
|
5
|
+
screen,
|
|
6
|
+
render,
|
|
7
|
+
cleanup,
|
|
8
|
+
waitFor,
|
|
9
|
+
fireEvent,
|
|
10
|
+
} from "@testing-library/react";
|
|
11
|
+
import { axe, toHaveNoViolations } from "jest-axe";
|
|
12
|
+
|
|
13
|
+
import { Tree } from "../../src/tree";
|
|
14
|
+
|
|
15
|
+
afterEach(async () => {
|
|
16
|
+
vi.clearAllMocks();
|
|
17
|
+
vi.resetModules();
|
|
18
|
+
cleanup();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
expect.extend(toHaveNoViolations);
|
|
22
|
+
|
|
23
|
+
describe("Tree", () => {
|
|
24
|
+
test("Renders without accessibility violation", async () => {
|
|
25
|
+
const { container } = render(
|
|
26
|
+
<Tree.Root>
|
|
27
|
+
<Tree>
|
|
28
|
+
<Tree.Node nodeId="node-1">
|
|
29
|
+
<Tree.Trigger nodeId="node-1">Node 1</Tree.Trigger>
|
|
30
|
+
<Tree.Content nodeId="node-1" defaultOpen>
|
|
31
|
+
<Tree.Node level={1} nodeId="node-1-child">
|
|
32
|
+
<Tree.Trigger nodeId="node-1-child">Node 1 Child</Tree.Trigger>
|
|
33
|
+
</Tree.Node>
|
|
34
|
+
</Tree.Content>
|
|
35
|
+
</Tree.Node>
|
|
36
|
+
</Tree>
|
|
37
|
+
</Tree.Root>,
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const ComponentContainer = await axe(container);
|
|
41
|
+
expect(ComponentContainer).toHaveNoViolations();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// test("Renders with accessibility definition", async () => {
|
|
45
|
+
// render(
|
|
46
|
+
// <Tree.Root>
|
|
47
|
+
// <Tree>
|
|
48
|
+
// <Tree.Node nodeId="node-1">
|
|
49
|
+
// <Tree.Trigger nodeId="node-1">Node 1</Tree.Trigger>
|
|
50
|
+
// <Tree.Content nodeId="node-1">
|
|
51
|
+
// <Tree.Node level={1} nodeId="node-1-child">
|
|
52
|
+
// <Tree.Trigger nodeId="node-1-child">Node 1 Child</Tree.Trigger>
|
|
53
|
+
// </Tree.Node>
|
|
54
|
+
// </Tree.Content>
|
|
55
|
+
// </Tree.Node>
|
|
56
|
+
// </Tree>
|
|
57
|
+
// </Tree.Root>,
|
|
58
|
+
// );
|
|
59
|
+
|
|
60
|
+
// const treeView = screen.getByRole("tree");
|
|
61
|
+
// expect(treeView).toBeDefined();
|
|
62
|
+
|
|
63
|
+
// const treeItems = screen.getAllByRole("treeitem");
|
|
64
|
+
// expect(treeItems.length).toBe(1);
|
|
65
|
+
|
|
66
|
+
// expect(() => screen.getByRole("group")).toThrow();
|
|
67
|
+
|
|
68
|
+
// const trigger = screen.getByLabelText("node-1-action");
|
|
69
|
+
// expect(trigger).toBeDefined();
|
|
70
|
+
// expect(trigger.getAttribute("aria-expanded")).toBe("false");
|
|
71
|
+
// expect(trigger.getAttribute("aria-selected")).toBe("false");
|
|
72
|
+
// expect(trigger.getAttribute("data-state")).toBe("collapsed");
|
|
73
|
+
|
|
74
|
+
// fireEvent.click(trigger);
|
|
75
|
+
// await waitFor(() => {
|
|
76
|
+
// const group = screen.getByRole("group");
|
|
77
|
+
// expect(group).toBeDefined();
|
|
78
|
+
// expect(group.getAttribute("aria-labelledby")).toBeDefined();
|
|
79
|
+
// expect(group.getAttribute("id")).toBeDefined();
|
|
80
|
+
// expect(group.getAttribute("data-nodeid")).toBe("node-1");
|
|
81
|
+
|
|
82
|
+
// expect(trigger.getAttribute("aria-expanded")).toBe("true");
|
|
83
|
+
// expect(trigger.getAttribute("aria-selected")).toBe("true");
|
|
84
|
+
// expect(trigger.getAttribute("data-state")).toBe("expanded");
|
|
85
|
+
|
|
86
|
+
// const childItems = screen.getAllByRole("treeitem");
|
|
87
|
+
// expect(childItems.length).toBe(2);
|
|
88
|
+
// });
|
|
89
|
+
// });
|
|
90
|
+
|
|
91
|
+
test("Renders with correct aria-level on nested nodes", async () => {
|
|
92
|
+
render(
|
|
93
|
+
<Tree.Root>
|
|
94
|
+
<Tree>
|
|
95
|
+
<Tree.Node nodeId="root">
|
|
96
|
+
<Tree.Trigger nodeId="root">Root</Tree.Trigger>
|
|
97
|
+
<Tree.Content nodeId="root" defaultOpen>
|
|
98
|
+
<Tree.Node level={1} nodeId="child">
|
|
99
|
+
<Tree.Trigger nodeId="child">Child</Tree.Trigger>
|
|
100
|
+
<Tree.Content nodeId="child" defaultOpen>
|
|
101
|
+
<Tree.Node level={2} nodeId="grandchild">
|
|
102
|
+
<Tree.Trigger nodeId="grandchild">Grandchild</Tree.Trigger>
|
|
103
|
+
</Tree.Node>
|
|
104
|
+
</Tree.Content>
|
|
105
|
+
</Tree.Node>
|
|
106
|
+
</Tree.Content>
|
|
107
|
+
</Tree.Node>
|
|
108
|
+
</Tree>
|
|
109
|
+
</Tree.Root>,
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const treeItems = screen.getAllByRole("treeitem");
|
|
113
|
+
expect((treeItems[0] as HTMLElement).getAttribute("aria-level")).toBe("1");
|
|
114
|
+
expect((treeItems[1] as HTMLElement).getAttribute("aria-level")).toBe("2");
|
|
115
|
+
expect((treeItems[2] as HTMLElement).getAttribute("aria-level")).toBe("3");
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// test("Renders with defaultOpen and expands content on mount", async () => {
|
|
119
|
+
// render(
|
|
120
|
+
// <Tree.Root>
|
|
121
|
+
// <Tree>
|
|
122
|
+
// <Tree.Node nodeId="node-1">
|
|
123
|
+
// <Tree.Trigger nodeId="node-1">Node 1</Tree.Trigger>
|
|
124
|
+
// <Tree.Content nodeId="node-1" defaultOpen>
|
|
125
|
+
// <Tree.Node level={1} nodeId="node-1-child">
|
|
126
|
+
// <Tree.Trigger nodeId="node-1-child">Node 1 Child</Tree.Trigger>
|
|
127
|
+
// </Tree.Node>
|
|
128
|
+
// </Tree.Content>
|
|
129
|
+
// </Tree.Node>
|
|
130
|
+
// </Tree>
|
|
131
|
+
// </Tree.Root>,
|
|
132
|
+
// );
|
|
133
|
+
|
|
134
|
+
// await waitFor(() => {
|
|
135
|
+
// expect(screen.getByRole("group")).toBeDefined();
|
|
136
|
+
// expect(screen.getByText("Node 1 Child")).toBeDefined();
|
|
137
|
+
|
|
138
|
+
// const trigger = screen.getByLabelText("node-1-action");
|
|
139
|
+
// expect(trigger.getAttribute("aria-expanded")).toBe("true");
|
|
140
|
+
// expect(trigger.getAttribute("data-state")).toBe("expanded");
|
|
141
|
+
// });
|
|
142
|
+
// });
|
|
143
|
+
|
|
144
|
+
test("Fires the defined callback and toggles content when trigger is clicked", async () => {
|
|
145
|
+
const onClickCallback = vi.fn();
|
|
146
|
+
|
|
147
|
+
render(
|
|
148
|
+
<Tree.Root>
|
|
149
|
+
<Tree>
|
|
150
|
+
<Tree.Node nodeId="node-1">
|
|
151
|
+
<Tree.Trigger
|
|
152
|
+
nodeId="node-1"
|
|
153
|
+
name="trigger-1"
|
|
154
|
+
onClick={onClickCallback}
|
|
155
|
+
>
|
|
156
|
+
Node 1
|
|
157
|
+
</Tree.Trigger>
|
|
158
|
+
<Tree.Content nodeId="node-1" defaultOpen>
|
|
159
|
+
<Tree.Node level={1} nodeId="node-1-child">
|
|
160
|
+
<Tree.Trigger nodeId="node-1-child">Node 1 Child</Tree.Trigger>
|
|
161
|
+
</Tree.Node>
|
|
162
|
+
</Tree.Content>
|
|
163
|
+
</Tree.Node>
|
|
164
|
+
<Tree.Node nodeId="node-2">
|
|
165
|
+
<Tree.Trigger nodeId="node-2" name="trigger-2">
|
|
166
|
+
Node 2
|
|
167
|
+
</Tree.Trigger>
|
|
168
|
+
<Tree.Content nodeId="node-2">
|
|
169
|
+
<Tree.Node level={1} nodeId="node-2-child">
|
|
170
|
+
<Tree.Trigger nodeId="node-2-child">Node 2 Child</Tree.Trigger>
|
|
171
|
+
</Tree.Node>
|
|
172
|
+
</Tree.Content>
|
|
173
|
+
</Tree.Node>
|
|
174
|
+
</Tree>
|
|
175
|
+
</Tree.Root>,
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
expect(screen.getByText("Node 1 Child")).toBeDefined();
|
|
179
|
+
expect(() => screen.getByText("Node 2 Child")).toThrow();
|
|
180
|
+
|
|
181
|
+
fireEvent.click(screen.getByLabelText("trigger-2-action"));
|
|
182
|
+
await waitFor(() => {
|
|
183
|
+
expect(screen.getByText("Node 2 Child")).toBeDefined();
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
fireEvent.click(screen.getByLabelText("trigger-1-action"));
|
|
187
|
+
await waitFor(() => {
|
|
188
|
+
expect(() => screen.getByText("Node 1 Child")).toThrow();
|
|
189
|
+
expect(onClickCallback).toHaveBeenCalledTimes(1);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
fireEvent.click(screen.getByLabelText("trigger-1-action"));
|
|
193
|
+
await waitFor(() => {
|
|
194
|
+
expect(screen.getByText("Node 1 Child")).toBeDefined();
|
|
195
|
+
expect(onClickCallback).toHaveBeenCalledTimes(2);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
fireEvent.click(screen.getByLabelText("trigger-1-action"));
|
|
199
|
+
await waitFor(() => {
|
|
200
|
+
expect(() => screen.getByText("Node 1 Child")).toThrow();
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
test("Fires onSelectionChange callback with selected node ids", async () => {
|
|
205
|
+
const onSelectionChange = vi.fn();
|
|
206
|
+
|
|
207
|
+
render(
|
|
208
|
+
<Tree.Root onSelectionChange={onSelectionChange}>
|
|
209
|
+
<Tree>
|
|
210
|
+
<Tree.Node nodeId="node-1">
|
|
211
|
+
<Tree.Trigger nodeId="node-1" name="trigger-1">
|
|
212
|
+
Node 1
|
|
213
|
+
</Tree.Trigger>
|
|
214
|
+
</Tree.Node>
|
|
215
|
+
<Tree.Node nodeId="node-2">
|
|
216
|
+
<Tree.Trigger nodeId="node-2" name="trigger-2">
|
|
217
|
+
Node 2
|
|
218
|
+
</Tree.Trigger>
|
|
219
|
+
</Tree.Node>
|
|
220
|
+
</Tree>
|
|
221
|
+
</Tree.Root>,
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
fireEvent.click(screen.getByLabelText("trigger-1-action"));
|
|
225
|
+
await waitFor(() => {
|
|
226
|
+
expect(onSelectionChange).toHaveBeenCalledTimes(1);
|
|
227
|
+
expect(onSelectionChange).toHaveBeenCalledWith(["node-1"]);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
fireEvent.click(screen.getByLabelText("trigger-2-action"));
|
|
231
|
+
await waitFor(() => {
|
|
232
|
+
expect(onSelectionChange).toHaveBeenCalledTimes(2);
|
|
233
|
+
expect(onSelectionChange).toHaveBeenCalledWith(["node-1", "node-2"]);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
fireEvent.click(screen.getByLabelText("trigger-1-action"));
|
|
237
|
+
await waitFor(() => {
|
|
238
|
+
expect(onSelectionChange).toHaveBeenCalledTimes(3);
|
|
239
|
+
expect(onSelectionChange).toHaveBeenCalledWith(["node-2"]);
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test("Renders with defaultExpandedIds and expands matching nodes on mount", async () => {
|
|
244
|
+
render(
|
|
245
|
+
<Tree.Root defaultExpandedIds={["node-1", "node-2"]}>
|
|
246
|
+
<Tree>
|
|
247
|
+
<Tree.Node nodeId="node-1">
|
|
248
|
+
<Tree.Trigger nodeId="node-1">Node 1</Tree.Trigger>
|
|
249
|
+
<Tree.Content nodeId="node-1">
|
|
250
|
+
<Tree.Node level={1} nodeId="node-1-child">
|
|
251
|
+
<Tree.Trigger nodeId="node-1-child">Node 1 Child</Tree.Trigger>
|
|
252
|
+
</Tree.Node>
|
|
253
|
+
</Tree.Content>
|
|
254
|
+
</Tree.Node>
|
|
255
|
+
<Tree.Node nodeId="node-2">
|
|
256
|
+
<Tree.Trigger nodeId="node-2">Node 2</Tree.Trigger>
|
|
257
|
+
<Tree.Content nodeId="node-2">
|
|
258
|
+
<Tree.Node level={1} nodeId="node-2-child">
|
|
259
|
+
<Tree.Trigger nodeId="node-2-child">Node 2 Child</Tree.Trigger>
|
|
260
|
+
</Tree.Node>
|
|
261
|
+
</Tree.Content>
|
|
262
|
+
</Tree.Node>
|
|
263
|
+
</Tree>
|
|
264
|
+
</Tree.Root>,
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
await waitFor(() => {
|
|
268
|
+
expect(screen.getByText("Node 1 Child")).toBeDefined();
|
|
269
|
+
expect(screen.getByText("Node 2 Child")).toBeDefined();
|
|
270
|
+
|
|
271
|
+
const groups = screen.getAllByRole("group");
|
|
272
|
+
expect(groups.length).toBe(2);
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
});
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import React, { useState, createContext, useContext } from "react";
|
|
2
4
|
import { IReactChildren, IComponentAPI } from "../../../../../types";
|
|
3
5
|
|
|
@@ -11,7 +13,7 @@ export const useAccordion = () => useContext(AccordionContext);
|
|
|
11
13
|
|
|
12
14
|
export const AccordionProvider = ({
|
|
13
15
|
children,
|
|
14
|
-
}: IReactChildren): JSX.Element => {
|
|
16
|
+
}: IReactChildren): React.JSX.Element => {
|
|
15
17
|
const context = useAccordionProvider();
|
|
16
18
|
|
|
17
19
|
return (
|
package/src/badge/index.tsx
CHANGED
|
@@ -5,9 +5,8 @@ import { BadgeWrapper } from "./styles";
|
|
|
5
5
|
|
|
6
6
|
import type { IComponentStyling, TComponentShape } from "../../../../types";
|
|
7
7
|
|
|
8
|
-
interface IBadgeProperties
|
|
9
|
-
extends IComponentStyling,
|
|
10
|
-
React.ComponentProps<"div"> {
|
|
8
|
+
export interface IBadgeProperties
|
|
9
|
+
extends IComponentStyling, React.ComponentProps<"div"> {
|
|
11
10
|
variant?:
|
|
12
11
|
| "primary"
|
|
13
12
|
| "secondary"
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import React, { useState, createContext, useContext } from "react";
|
|
2
4
|
import {
|
|
3
5
|
IReactChildren,
|
|
@@ -13,7 +15,9 @@ const defaultComponentAPI = {
|
|
|
13
15
|
const CheckboxContext = createContext<IComponentAPI>(defaultComponentAPI);
|
|
14
16
|
export const useCheckbox = () => useContext(CheckboxContext);
|
|
15
17
|
|
|
16
|
-
export const CheckboxProvider = ({
|
|
18
|
+
export const CheckboxProvider = ({
|
|
19
|
+
children,
|
|
20
|
+
}: IReactChildren): React.JSX.Element => {
|
|
17
21
|
const context = useCheckboxProvider();
|
|
18
22
|
|
|
19
23
|
return (
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import React, { useState, createContext, useContext } from "react";
|
|
2
4
|
import { IReactChildren, IComponentAPI } from "../../../../../types";
|
|
3
5
|
|
|
@@ -11,7 +13,7 @@ export const useCollapsible = () => useContext(CollapsibleContext);
|
|
|
11
13
|
|
|
12
14
|
export const CollapsibleProvider = ({
|
|
13
15
|
children,
|
|
14
|
-
}: IReactChildren): JSX.Element => {
|
|
16
|
+
}: IReactChildren): React.JSX.Element => {
|
|
15
17
|
const context = useCollapsibleProvider();
|
|
16
18
|
|
|
17
19
|
return (
|