@khanacademy/wonder-blocks-card 0.0.0-PR2859-20251119004500 → 0.0.0-PR2876-20251209213809

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,10 +1,35 @@
1
1
  # @khanacademy/wonder-blocks-card
2
2
 
3
- ## 0.0.0-PR2859-20251119004500
3
+ ## 0.0.0-PR2876-20251209213809
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [ad430d7]
8
+ - @khanacademy/wonder-blocks-core@0.0.0-PR2876-20251209213809
9
+ - @khanacademy/wonder-blocks-icon-button@0.0.0-PR2876-20251209213809
10
+ - @khanacademy/wonder-blocks-tokens@14.1.3
11
+
12
+ ## 1.3.2
13
+
14
+ ### Patch Changes
15
+
16
+ - Updated dependencies [70d6c08]
17
+ - @khanacademy/wonder-blocks-tokens@14.1.3
18
+ - @khanacademy/wonder-blocks-icon-button@11.0.1
19
+
20
+ ## 1.3.1
21
+
22
+ ### Patch Changes
23
+
24
+ - Updated dependencies [0fd41cc]
25
+ - Updated dependencies [d36860e]
26
+ - @khanacademy/wonder-blocks-icon-button@11.0.0
27
+
28
+ ## 1.3.0
4
29
 
5
30
  ### Minor Changes
6
31
 
7
- - 6bab6f5: Updates labeling requirements and adds testId to dismiss button
32
+ - d36c492: Updates labeling requirements and adds testId to dismiss button
8
33
 
9
34
  ## 1.2.5
10
35
 
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Card component for Wonder Blocks.",
4
4
  "author": "Khan Academy",
5
5
  "license": "MIT",
6
- "version": "0.0.0-PR2859-20251119004500",
6
+ "version": "0.0.0-PR2876-20251209213809",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
@@ -21,9 +21,9 @@
21
21
  "types": "dist/index.d.ts",
22
22
  "source": "src/index.js",
23
23
  "dependencies": {
24
- "@khanacademy/wonder-blocks-core": "12.4.2",
25
- "@khanacademy/wonder-blocks-icon-button": "10.5.7",
26
- "@khanacademy/wonder-blocks-tokens": "14.1.2"
24
+ "@khanacademy/wonder-blocks-core": "0.0.0-PR2876-20251209213809",
25
+ "@khanacademy/wonder-blocks-icon-button": "0.0.0-PR2876-20251209213809",
26
+ "@khanacademy/wonder-blocks-tokens": "14.1.3"
27
27
  },
28
28
  "peerDependencies": {
29
29
  "aphrodite": "^1.2.5",
@@ -31,7 +31,7 @@
31
31
  "@phosphor-icons/core": "^2.0.2"
32
32
  },
33
33
  "devDependencies": {
34
- "@khanacademy/wb-dev-build-settings": "3.2.0"
34
+ "@khanacademy/wb-dev-build-settings": "0.0.0-PR2876-20251209213809"
35
35
  },
36
36
  "scripts": {
37
37
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1,274 +0,0 @@
1
- import * as React from "react";
2
- import {render, screen} from "@testing-library/react";
3
- import {userEvent} from "@testing-library/user-event";
4
-
5
- import Card from "../../components/card";
6
-
7
- describe("Card", () => {
8
- describe("Basic rendering", () => {
9
- it("should render children correctly", () => {
10
- // Arrange
11
- render(
12
- <Card>
13
- <div data-testid="child-content">Card Content</div>
14
- </Card>,
15
- );
16
-
17
- // Act
18
- const childContent = screen.getByTestId("child-content");
19
-
20
- // Assert
21
- expect(childContent).toBeInTheDocument();
22
- });
23
-
24
- it("should not render dismiss button by default", () => {
25
- // Arrange
26
- render(
27
- <Card>
28
- <div>Content</div>
29
- </Card>,
30
- );
31
-
32
- // Act
33
- const dismissButton = screen.queryByRole("button");
34
-
35
- // Assert
36
- expect(dismissButton).not.toBeInTheDocument();
37
- });
38
- });
39
-
40
- describe("Dismiss button functionality", () => {
41
- it("should render dismiss button when onDismiss is present", () => {
42
- // Arrange
43
- render(
44
- <Card
45
- onDismiss={() => {}}
46
- labels={{dismissButtonAriaLabel: "Close"}}
47
- >
48
- <div>Content</div>
49
- </Card>,
50
- );
51
-
52
- // Act
53
- const dismissButton = screen.getByRole("button");
54
-
55
- // Assert
56
- expect(dismissButton).toBeInTheDocument();
57
- });
58
-
59
- it("should not render dismiss button there is no onDismiss prop", () => {
60
- // Arrange
61
- render(
62
- <Card>
63
- <div>Content</div>
64
- </Card>,
65
- );
66
-
67
- // Act
68
- const dismissButton = screen.queryByRole("button");
69
-
70
- // Assert
71
- expect(dismissButton).not.toBeInTheDocument();
72
- });
73
-
74
- it("should call onDismiss when dismiss button is clicked", async () => {
75
- // Arrange
76
- const mockOnDismiss = jest.fn();
77
- render(
78
- <Card
79
- labels={{dismissButtonAriaLabel: "Close it!"}}
80
- onDismiss={mockOnDismiss}
81
- >
82
- <div>Content</div>
83
- </Card>,
84
- );
85
-
86
- // Act
87
- const dismissButton = screen.getByRole("button", {
88
- name: "Close it!",
89
- });
90
- await userEvent.click(dismissButton);
91
-
92
- // Assert
93
- expect(mockOnDismiss).toHaveBeenCalledTimes(1);
94
- });
95
- });
96
-
97
- describe("Test IDs", () => {
98
- it("should apply a custom testId for card", () => {
99
- // Arrange
100
- render(
101
- <Card testId="my-card">
102
- <div>Content</div>
103
- </Card>,
104
- );
105
-
106
- // Act
107
- const card = screen.getByTestId("my-card");
108
-
109
- // Assert
110
- expect(card).toBeInTheDocument();
111
- });
112
-
113
- it("should append -dismiss-button to testId for dismiss button", () => {
114
- // Arrange
115
- render(
116
- <Card
117
- testId="my-card"
118
- onDismiss={() => {}}
119
- labels={{dismissButtonAriaLabel: "Close"}}
120
- >
121
- <div>Content</div>
122
- </Card>,
123
- );
124
-
125
- // Act
126
- const dismissButton = screen.getByTestId("my-card-dismiss-button");
127
-
128
- // Assert
129
- expect(dismissButton).toBeInTheDocument();
130
- });
131
- });
132
-
133
- describe("Accessibility", () => {
134
- it("should pass custom aria-label to dismiss button", () => {
135
- // Arrange
136
- render(
137
- <Card
138
- onDismiss={() => {}}
139
- labels={{dismissButtonAriaLabel: "Custom Close"}}
140
- >
141
- <div>Content</div>
142
- </Card>,
143
- );
144
-
145
- // Act
146
- const dismissButton = screen.getByRole("button", {
147
- name: "Custom Close",
148
- });
149
-
150
- // Assert
151
- expect(dismissButton).toBeInTheDocument();
152
- });
153
-
154
- it("should render with a custom tag", () => {
155
- // Arrange
156
- render(
157
- <Card tag="section" labels={{cardAriaLabel: "Card Section"}}>
158
- <h2>Heading</h2>
159
- <p>Description</p>
160
- </Card>,
161
- );
162
-
163
- // Act
164
- const section = screen.getByRole("region");
165
-
166
- // Assert
167
- expect(section).toBeInTheDocument();
168
- });
169
-
170
- it("should apply labels.cardAriaLabel for a custom tag (preferred)", () => {
171
- // Arrange
172
- render(
173
- <Card
174
- tag="section"
175
- labels={{cardAriaLabel: "Custom section label"}}
176
- >
177
- <h2>Heading</h2>
178
- <p>Description</p>
179
- </Card>,
180
- );
181
-
182
- // Act
183
- const section = screen.getByRole("region", {
184
- name: "Custom section label",
185
- });
186
-
187
- // Assert
188
- expect(section).toBeInTheDocument();
189
- });
190
-
191
- it("should apply aria-labelledby for a custom tag", () => {
192
- // Arrange
193
- render(
194
- <div>
195
- <h2 id="card-heading">My Card Title</h2>
196
- <Card tag="section" aria-labelledby="card-heading">
197
- <p>Description</p>
198
- </Card>
199
- </div>,
200
- );
201
-
202
- // Act
203
- const section = screen.getByRole("region", {
204
- name: "My Card Title",
205
- });
206
-
207
- // Assert
208
- expect(section).toBeInTheDocument();
209
- });
210
-
211
- it("should only apply aria-labelledby", () => {
212
- // Arrange
213
- render(
214
- <div>
215
- <h2 id="card-heading">My Card Title</h2>
216
- <Card
217
- tag="section"
218
- aria-labelledby="card-heading"
219
- aria-label="Fallback label"
220
- >
221
- <p>Description</p>
222
- </Card>
223
- </div>,
224
- );
225
-
226
- // Act
227
- const section = screen.getByRole("region", {
228
- name: "My Card Title",
229
- });
230
-
231
- // Assert
232
- expect(section).toBeInTheDocument();
233
- });
234
-
235
- it("should apply the inert attribute", () => {
236
- // Arrange
237
- render(
238
- <Card inert testId="card">
239
- <h2>Heading</h2>
240
- <p>Description</p>
241
- <button>Button</button>
242
- </Card>,
243
- );
244
-
245
- // Act
246
- const section = screen.getByTestId("card");
247
-
248
- // Assert
249
- expect(section).toHaveAttribute("inert");
250
- });
251
- });
252
-
253
- describe("Complex content scenarios", () => {
254
- it("should work with fragment children", () => {
255
- // Arrange
256
- render(
257
- <Card>
258
- <>
259
- <span data-testid="fragment-child-1">First</span>
260
- <span data-testid="fragment-child-2">Second</span>
261
- </>
262
- </Card>,
263
- );
264
-
265
- // Act
266
- const firstChild = screen.getByTestId("fragment-child-1");
267
- const secondChild = screen.getByTestId("fragment-child-2");
268
-
269
- // Assert
270
- expect(firstChild).toBeInTheDocument();
271
- expect(secondChild).toBeInTheDocument();
272
- });
273
- });
274
- });
@@ -1,207 +0,0 @@
1
- import * as React from "react";
2
-
3
- // Import Button and Link for proper usage examples
4
- import Button from "@khanacademy/wonder-blocks-button";
5
- import Link from "@khanacademy/wonder-blocks-link";
6
- import Card from "../../components/card";
7
-
8
- /**
9
- * Basic Card usage
10
- */
11
-
12
- <Card>Hello, world!</Card>;
13
-
14
- <Card>
15
- <div>Some content</div>
16
- </Card>;
17
-
18
- /**
19
- * Card with Button and Link components (correct usage)
20
- */
21
-
22
- <Card>
23
- <Button onClick={() => {}}>Click me</Button>
24
- </Card>;
25
-
26
- <Card>
27
- <Link href="/foo">Click me</Link>
28
- </Card>;
29
-
30
- /**
31
- * Card with all style props
32
- */
33
-
34
- <Card
35
- background="base-default"
36
- borderRadius="small"
37
- paddingSize="small"
38
- elevation="none"
39
- >
40
- Content
41
- </Card>;
42
-
43
- <Card
44
- background="base-subtle"
45
- borderRadius="medium"
46
- paddingSize="medium"
47
- elevation="low"
48
- >
49
- Content
50
- </Card>;
51
-
52
- <Card paddingSize="none">Content</Card>;
53
-
54
- // @ts-expect-error - invalid background value
55
- <Card background="invalid-background">Content</Card>;
56
-
57
- // @ts-expect-error - invalid borderRadius value
58
- <Card borderRadius="invalid-radius">Content</Card>;
59
-
60
- // @ts-expect-error - invalid paddingSize value
61
- <Card paddingSize="invalid-padding">Content</Card>;
62
-
63
- // @ts-expect-error - invalid elevation value
64
- <Card elevation="invalid-elevation">Content</Card>;
65
-
66
- /**
67
- * Card with dismiss functionality
68
- */
69
-
70
- <Card onDismiss={() => {}} labels={{dismissButtonAriaLabel: "Close card"}}>
71
- Content
72
- </Card>;
73
-
74
- // @ts-expect-error - onDismiss requires dismissButtonAriaLabel
75
- <Card onDismiss={() => {}}>Content</Card>;
76
-
77
- // @ts-expect-error - onDismiss requires dismissButtonAriaLabel
78
- <Card onDismiss={() => {}} labels={{}}>
79
- Content
80
- </Card>;
81
-
82
- // @ts-expect-error - onClick is not allowed on Card wrapper
83
- <Card onClick={() => {}}>Content</Card>;
84
-
85
- /**
86
- * Card with different HTML tags
87
- */
88
-
89
- <Card tag="div">Content</Card>;
90
-
91
- // @ts-expect-error - button tag not allowed, use Wonder Blocks Button component instead
92
- <Card tag="button">Content</Card>;
93
-
94
- // @ts-expect-error - anchor tag not allowed, use Wonder Blocks Link component instead
95
- <Card tag="a">Content</Card>;
96
-
97
- <Card tag="section" labels={{cardAriaLabel: "Card section"}}>
98
- Content
99
- </Card>;
100
-
101
- <Card tag="figure" labels={{cardAriaLabel: "Card figure"}}>
102
- Content
103
- </Card>;
104
-
105
- <Card tag="section" aria-label="Card section">
106
- Content
107
- </Card>;
108
-
109
- <Card tag="figure" aria-label="Card figure">
110
- Content
111
- </Card>;
112
-
113
- <Card tag="section" aria-labelledby="someId">
114
- <h2 id="someId">Card title</h2>
115
- </Card>;
116
-
117
- <Card tag="figure" aria-labelledby="someId2">
118
- <h2 id="someId2">Card title</h2>
119
- </Card>;
120
-
121
- // @ts-expect-error - aria-labelledby cannot be used with labels.cardAriaLabel
122
- <Card
123
- tag="figure"
124
- aria-labelledby="someId2"
125
- labels={{cardAriaLabel: "preferred label"}}
126
- >
127
- <h2 id="someId2">Card title</h2>
128
- </Card>;
129
-
130
- // @ts-expect-error - aria-labelledby cannot be used with labels.cardAriaLabel
131
- <Card
132
- tag="section"
133
- aria-labelledby="someId2"
134
- labels={{cardAriaLabel: "preferred label"}}
135
- >
136
- <h2 id="someId2">Card title</h2>
137
- </Card>;
138
-
139
- <Card tag="figure" aria-labelledby="someId2" aria-label="fallback label">
140
- <h2 id="someId2">Card title</h2>
141
- </Card>;
142
-
143
- <Card tag="section">Content</Card>;
144
-
145
- <Card tag="figure">Content</Card>;
146
-
147
- <Card tag="section" labels={{}}>
148
- Content
149
- </Card>;
150
-
151
- <Card tag="figure" labels={{}}>
152
- Content
153
- </Card>;
154
-
155
- /**
156
- * Card with additional props
157
- */
158
-
159
- <Card testId="my-card">Content</Card>;
160
-
161
- <Card inert>Content</Card>;
162
-
163
- <Card ref={React.createRef<HTMLDivElement>()}>Content</Card>;
164
-
165
- <Card styles={{root: {width: 200}, dismissButton: {position: "absolute"}}}>
166
- Content
167
- </Card>;
168
-
169
- <Card
170
- tag="section"
171
- onDismiss={() => {}}
172
- labels={{
173
- cardAriaLabel: "Card section",
174
- dismissButtonAriaLabel: "Close card",
175
- }}
176
- >
177
- Content
178
- </Card>;
179
-
180
- /**
181
- * Card with all props
182
- */
183
-
184
- <Card
185
- aria-busy={true}
186
- aria-roledescription="A custom card"
187
- background="base-subtle"
188
- borderRadius="medium"
189
- paddingSize="medium"
190
- elevation="low"
191
- tag="figure"
192
- onDismiss={() => {}}
193
- labels={{
194
- cardAriaLabel: "Card figure",
195
- dismissButtonAriaLabel: "Close card",
196
- }}
197
- testId="complex-card"
198
- inert
199
- styles={{
200
- root: {width: 300},
201
- dismissButton: {top: 10, right: 10},
202
- }}
203
- ref={React.createRef<HTMLElement>()}
204
- role="status"
205
- >
206
- <div>Complex content</div>
207
- </Card>;
@@ -1,145 +0,0 @@
1
- import * as React from "react";
2
- import {render, screen} from "@testing-library/react";
3
- import {userEvent} from "@testing-library/user-event";
4
-
5
- import {DismissButton} from "../../components/dismiss-button";
6
-
7
- describe("DismissButton", () => {
8
- describe("Basic rendering", () => {
9
- it("should render as a button", () => {
10
- // Arrange & Act
11
- render(<DismissButton />);
12
-
13
- // Assert
14
- expect(screen.getByRole("button")).toBeInTheDocument();
15
- });
16
- });
17
-
18
- describe("Accessibility", () => {
19
- it("should use default aria-label when not provided", () => {
20
- // Arrange & Act
21
- render(<DismissButton />);
22
-
23
- // Assert
24
- expect(
25
- screen.getByRole("button", {name: "Close"}),
26
- ).toBeInTheDocument();
27
- });
28
-
29
- it("should use custom aria-label when provided", () => {
30
- // Arrange & Act
31
- render(<DismissButton aria-label="Dismiss notification" />);
32
-
33
- // Assert
34
- expect(
35
- screen.getByRole("button", {name: "Dismiss notification"}),
36
- ).toBeInTheDocument();
37
- });
38
-
39
- it("should be keyboard accessible", () => {
40
- // Arrange
41
- render(<DismissButton aria-label="Close" />);
42
-
43
- // Act
44
- const button = screen.getByRole("button");
45
- button.focus();
46
-
47
- // Assert
48
- expect(button).toHaveFocus();
49
- });
50
- });
51
-
52
- describe("Event handling", () => {
53
- it("should call onClick when button is clicked", async () => {
54
- // Arrange
55
- const mockOnClick = jest.fn();
56
- render(<DismissButton onClick={mockOnClick} aria-label="Close" />);
57
-
58
- // Act
59
- const button = screen.getByRole("button", {name: "Close"});
60
- await userEvent.click(button);
61
-
62
- // Assert
63
- expect(mockOnClick).toHaveBeenCalledTimes(1);
64
- });
65
-
66
- it("should call onClick with event parameter", async () => {
67
- // Arrange
68
- const mockOnClick = jest.fn();
69
- render(<DismissButton onClick={mockOnClick} aria-label="Close" />);
70
-
71
- // Act
72
- const button = screen.getByRole("button", {name: "Close"});
73
- await userEvent.click(button);
74
-
75
- // Assert
76
- expect(mockOnClick).toHaveBeenCalledWith(
77
- expect.objectContaining({
78
- type: "click",
79
- }),
80
- );
81
- });
82
-
83
- it("should handle missing onClick prop gracefully", async () => {
84
- // Arrange
85
- render(<DismissButton aria-label="Close" />);
86
-
87
- // Act & Assert
88
- const button = screen.getByRole("button", {name: "Close"});
89
- await expect(userEvent.click(button)).resolves.not.toThrow();
90
- });
91
-
92
- it("should support keyboard activation with Enter", async () => {
93
- // Arrange
94
- const mockOnClick = jest.fn();
95
- render(<DismissButton onClick={mockOnClick} aria-label="Close" />);
96
-
97
- // Act
98
- const button = screen.getByRole("button", {name: "Close"});
99
- button.focus();
100
- await userEvent.keyboard("{Enter}");
101
-
102
- // Assert
103
- expect(mockOnClick).toHaveBeenCalledTimes(1);
104
- });
105
-
106
- it("should support keyboard activation with Space", async () => {
107
- // Arrange
108
- const mockOnClick = jest.fn();
109
- render(<DismissButton onClick={mockOnClick} aria-label="Close" />);
110
-
111
- // Act
112
- const button = screen.getByRole("button", {name: "Close"});
113
- button.focus();
114
- await userEvent.keyboard(" ");
115
-
116
- // Assert
117
- expect(mockOnClick).toHaveBeenCalledTimes(1);
118
- });
119
- });
120
-
121
- describe("Test ID support", () => {
122
- it("should set data-testid attribute when testId is provided", () => {
123
- // Arrange
124
- const testId = "dismiss-button-test";
125
- render(<DismissButton testId={testId} aria-label="Close" />);
126
-
127
- // Act
128
- const button = screen.getByTestId(testId);
129
-
130
- // Assert
131
- expect(button).toHaveAttribute("data-testid", testId);
132
- });
133
-
134
- it("should not have data-testid attribute when testId is not provided", () => {
135
- // Arrange
136
- render(<DismissButton aria-label="Close" />);
137
-
138
- // Act
139
- const button = screen.getByRole("button");
140
-
141
- // Assert
142
- expect(button).not.toHaveAttribute("data-testid");
143
- });
144
- });
145
- });