@khanacademy/wonder-blocks-card 0.0.0-PR2860-20251112235619 → 0.0.0-PR2876-20251209223105

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,16 +1,47 @@
1
1
  # @khanacademy/wonder-blocks-card
2
2
 
3
- ## 0.0.0-PR2860-20251112235619
3
+ ## 0.0.0-PR2876-20251209223105
4
4
 
5
5
  ### Patch Changes
6
6
 
7
- - 4cca00d: Re-publish to publish with Trusted Publishing
7
+ - Updated dependencies [ad430d7]
8
+ - @khanacademy/wonder-blocks-core@0.0.0-PR2876-20251209223105
9
+ - @khanacademy/wonder-blocks-icon-button@0.0.0-PR2876-20251209223105
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
29
+
30
+ ### Minor Changes
31
+
32
+ - d36c492: Updates labeling requirements and adds testId to dismiss button
33
+
34
+ ## 1.2.5
35
+
36
+ ### Patch Changes
37
+
38
+ - 8a36c70: Re-publish to publish with Trusted Publishing
8
39
  - 3e0d137: Re-publishing via Trusted Publishing
9
- - Updated dependencies [4cca00d]
40
+ - Updated dependencies [8a36c70]
10
41
  - Updated dependencies [3e0d137]
11
- - @khanacademy/wonder-blocks-core@0.0.0-PR2860-20251112235619
12
- - @khanacademy/wonder-blocks-icon-button@0.0.0-PR2860-20251112235619
13
- - @khanacademy/wonder-blocks-tokens@0.0.0-PR2860-20251112235619
42
+ - @khanacademy/wonder-blocks-core@12.4.2
43
+ - @khanacademy/wonder-blocks-icon-button@10.5.7
44
+ - @khanacademy/wonder-blocks-tokens@14.1.2
14
45
 
15
46
  ## 1.2.4
16
47
 
@@ -1,15 +1,6 @@
1
1
  import * as React from "react";
2
2
  import { StyleType } from "@khanacademy/wonder-blocks-core";
3
- /**
4
- * Provide a specific HTML tag that overrides the default (`div`).
5
- *
6
- * Notes:
7
- * - When `tag="section"` or `"figure"`, `cardAriaLabel` is required for accessibility.
8
- * - `button` and `a` tags are not allowed - use Wonder Blocks Button and Link components as children instead.
9
- * Valid HTML tags for the Card component.
10
- * Excludes button and anchor tags which should use Wonder Blocks Button and Link components instead.
11
- */
12
- type ValidCardTags = Exclude<keyof JSX.IntrinsicElements, "button" | "a">;
3
+ import type { AriaProps } from "@khanacademy/wonder-blocks-core";
13
4
  type StyleProps = {
14
5
  /**
15
6
  * The background style of the card, as a string identifier that matches a semanticColor token.
@@ -77,7 +68,11 @@ type StyleProps = {
77
68
  * </Card>
78
69
  * ```
79
70
  *
80
- * When the `onDismiss` prop is provided, a dismiss button will be rendered. In this case, the `labels.dismissButtonAriaLabel` prop is required to provide an accessible label for the dismiss button.
71
+ * ### Accessibility
72
+ *
73
+ * When the `onDismiss` prop is provided, a dismiss button will be rendered.
74
+ * In this case, the `labels.dismissButtonAriaLabel` prop is required to provide
75
+ * a translatable screen reader label for the dismiss button.
81
76
  *
82
77
  * See additional Accessibility docs.
83
78
  */
@@ -104,13 +99,24 @@ declare const Card: React.ForwardRefExoticComponent<(Omit<{
104
99
  inert?: boolean;
105
100
  /**
106
101
  * The test ID used to locate this component in automated tests.
102
+ *
103
+ * The test ID will also be passed to the dismiss button as
104
+ * `{testId}-dismiss-button` if the `onDismiss` prop is provided.
107
105
  */
108
106
  testId?: string;
109
107
  } & StyleProps & {
110
- tag: "section" | "figure";
111
- labels: {
112
- cardAriaLabel: string;
113
- } & Record<string, any>;
108
+ tag?: Exclude<keyof JSX.IntrinsicElements, "button" | "a">;
109
+ } & {
110
+ /** Translatable label string for aria-label */
111
+ labels?: {
112
+ cardAriaLabel?: string;
113
+ dismissButtonAriaLabel?: string;
114
+ };
115
+ "aria-labelledby"?: never;
116
+ } & {
117
+ "aria-busy"?: AriaProps["aria-busy"];
118
+ "aria-roledescription"?: AriaProps["aria-roledescription"];
119
+ role?: AriaProps["role"];
114
120
  } & {
115
121
  onDismiss: (e?: React.SyntheticEvent) => void;
116
122
  labels: {
@@ -139,13 +145,24 @@ declare const Card: React.ForwardRefExoticComponent<(Omit<{
139
145
  inert?: boolean;
140
146
  /**
141
147
  * The test ID used to locate this component in automated tests.
148
+ *
149
+ * The test ID will also be passed to the dismiss button as
150
+ * `{testId}-dismiss-button` if the `onDismiss` prop is provided.
142
151
  */
143
152
  testId?: string;
144
153
  } & StyleProps & {
145
- tag: "section" | "figure";
146
- labels: {
147
- cardAriaLabel: string;
148
- } & Record<string, any>;
154
+ tag?: Exclude<keyof JSX.IntrinsicElements, "button" | "a">;
155
+ } & {
156
+ /** Translatable label string for aria-label */
157
+ labels?: {
158
+ cardAriaLabel?: string;
159
+ dismissButtonAriaLabel?: string;
160
+ };
161
+ "aria-labelledby"?: never;
162
+ } & {
163
+ "aria-busy"?: AriaProps["aria-busy"];
164
+ "aria-roledescription"?: AriaProps["aria-roledescription"];
165
+ role?: AriaProps["role"];
149
166
  } & {
150
167
  onDismiss?: never;
151
168
  labels?: Record<string, any>;
@@ -172,11 +189,24 @@ declare const Card: React.ForwardRefExoticComponent<(Omit<{
172
189
  inert?: boolean;
173
190
  /**
174
191
  * The test ID used to locate this component in automated tests.
192
+ *
193
+ * The test ID will also be passed to the dismiss button as
194
+ * `{testId}-dismiss-button` if the `onDismiss` prop is provided.
175
195
  */
176
196
  testId?: string;
177
197
  } & StyleProps & {
178
- tag?: Exclude<ValidCardTags, "section" | "figure">;
179
- labels?: Record<string, any>;
198
+ tag?: Exclude<keyof JSX.IntrinsicElements, "button" | "a">;
199
+ } & {
200
+ /** ID reference for aria-labelledby */
201
+ "aria-labelledby"?: string;
202
+ labels?: {
203
+ cardAriaLabel?: never;
204
+ dismissButtonAriaLabel?: string;
205
+ };
206
+ } & {
207
+ "aria-busy"?: AriaProps["aria-busy"];
208
+ "aria-roledescription"?: AriaProps["aria-roledescription"];
209
+ role?: AriaProps["role"];
180
210
  } & {
181
211
  onDismiss: (e?: React.SyntheticEvent) => void;
182
212
  labels: {
@@ -205,11 +235,24 @@ declare const Card: React.ForwardRefExoticComponent<(Omit<{
205
235
  inert?: boolean;
206
236
  /**
207
237
  * The test ID used to locate this component in automated tests.
238
+ *
239
+ * The test ID will also be passed to the dismiss button as
240
+ * `{testId}-dismiss-button` if the `onDismiss` prop is provided.
208
241
  */
209
242
  testId?: string;
210
243
  } & StyleProps & {
211
- tag?: Exclude<ValidCardTags, "section" | "figure">;
212
- labels?: Record<string, any>;
244
+ tag?: Exclude<keyof JSX.IntrinsicElements, "button" | "a">;
245
+ } & {
246
+ /** ID reference for aria-labelledby */
247
+ "aria-labelledby"?: string;
248
+ labels?: {
249
+ cardAriaLabel?: never;
250
+ dismissButtonAriaLabel?: string;
251
+ };
252
+ } & {
253
+ "aria-busy"?: AriaProps["aria-busy"];
254
+ "aria-roledescription"?: AriaProps["aria-roledescription"];
255
+ role?: AriaProps["role"];
213
256
  } & {
214
257
  onDismiss?: never;
215
258
  labels?: Record<string, any>;
@@ -7,17 +7,7 @@ type Props = {
7
7
  "aria-label"?: string;
8
8
  /** Optional custom styles. */
9
9
  style?: StyleType;
10
- /**
11
- * Test ID used for e2e testing.
12
- *
13
- * In this case, this component is internal, so `testId` is composed with
14
- * the `testId` passed down from the Dialog variant + a suffix to scope it
15
- * to this component.
16
- *
17
- * @example
18
- * For testId="some-random-id"
19
- * The result will be: `some-random-id-modal-panel`
20
- */
10
+ /** Test ID used for e2e testing, passed down from its parent card.*/
21
11
  testId?: string;
22
12
  };
23
13
  export declare const DismissButton: (props: Props) => React.JSX.Element;
package/dist/es/index.js CHANGED
@@ -9,6 +9,6 @@ import { focusStyles } from '@khanacademy/wonder-blocks-styles';
9
9
 
10
10
  const DismissButton=props=>{const{onClick,style,testId}=props;return jsx(IconButton,{icon:xIcon,"aria-label":props["aria-label"]||"Close",onClick:onClick,kind:"tertiary",actionType:"neutral",style:[componentStyles.root,style],testId:testId})};const componentStyles=StyleSheet.create({root:{position:"absolute",insetInlineEnd:sizing.size_080,top:sizing.size_080,zIndex:1,":focus":focusStyles.focus[":focus-visible"]}});
11
11
 
12
- const Card=React.forwardRef(function Card(props,ref){const{styles,labels,tag,testId,background="base-default",borderRadius="small",paddingSize="small",elevation="none",children,onDismiss,inert}=props;const isBackgroundToken=background==="base-default"||background==="base-subtle";const componentStyles=getComponentStyles({background:isBackgroundToken?background:null,borderRadius,paddingSize,elevation});return jsxs(View,{"aria-label":labels?.cardAriaLabel,style:[componentStyles.root,!isBackgroundToken&&{background:`url(${background})`,backgroundSize:"cover"},styles?.root],ref:ref,tag:tag,testId:testId,inert:inert?"":undefined,children:[onDismiss?jsx(DismissButton,{"aria-label":labels?.dismissButtonAriaLabel||"Close",onClick:e=>onDismiss?.(e)}):null,children]})});const styleMap={backgroundColor:{"base-subtle":semanticColor.core.background.base.subtle,"base-default":semanticColor.core.background.base.default},borderRadius:{small:border.radius.radius_080,medium:border.radius.radius_120},padding:{none:sizing.size_0,small:sizing.size_160,medium:sizing.size_240},elevation:{none:"none",low:boxShadow.low}};const getComponentStyles=({background="base-default",borderRadius="small",paddingSize="small",elevation="none"})=>{const bgColor=background;return StyleSheet.create({root:{backgroundColor:bgColor&&styleMap.backgroundColor[bgColor],borderColor:semanticColor.core.border.neutral.subtle,borderStyle:"solid",borderWidth:border.width.thin,fontFamily:font.family.sans,minInlineSize:sizing.size_280,position:"relative",borderRadius:styleMap.borderRadius[borderRadius],boxShadow:styleMap.elevation[elevation],padding:styleMap.padding[paddingSize]}})};
12
+ const Card=React.forwardRef(function Card(props,ref){const{styles,labels,tag,testId,background="base-default",borderRadius="small",paddingSize="small",elevation="none",children,onDismiss,inert,"aria-labelledby":ariaLabelledBy,"aria-busy":ariaBusy,role}=props;const isBackgroundToken=background==="base-default"||background==="base-subtle";const componentStyles=getComponentStyles({background:isBackgroundToken?background:null,borderRadius,paddingSize,elevation});return jsxs(View,{"aria-busy":ariaBusy,"aria-label":labels?.cardAriaLabel,"aria-labelledby":ariaLabelledBy,style:[componentStyles.root,!isBackgroundToken&&{background:`url(${background})`,backgroundSize:"cover"},styles?.root],ref:ref,role:role,tag:tag,testId:testId,inert:inert?"":undefined,children:[onDismiss?jsx(DismissButton,{"aria-label":labels?.dismissButtonAriaLabel||"Close",onClick:e=>onDismiss?.(e),testId:testId&&`${testId}-dismiss-button`}):null,children]})});const styleMap={backgroundColor:{"base-subtle":semanticColor.core.background.base.subtle,"base-default":semanticColor.core.background.base.default},borderRadius:{small:border.radius.radius_080,medium:border.radius.radius_120},padding:{none:sizing.size_0,small:sizing.size_160,medium:sizing.size_240},elevation:{none:"none",low:boxShadow.low}};const getComponentStyles=({background="base-default",borderRadius="small",paddingSize="small",elevation="none"})=>{const bgColor=background;return StyleSheet.create({root:{backgroundColor:bgColor&&styleMap.backgroundColor[bgColor],borderColor:semanticColor.core.border.neutral.subtle,borderStyle:"solid",borderWidth:border.width.thin,fontFamily:font.family.sans,minInlineSize:sizing.size_280,position:"relative",borderRadius:styleMap.borderRadius[borderRadius],boxShadow:styleMap.elevation[elevation],padding:styleMap.padding[paddingSize]}})};
13
13
 
14
14
  export { Card };
package/dist/index.js CHANGED
@@ -37,6 +37,6 @@ var IconButton__default = /*#__PURE__*/_interopDefaultLegacy(IconButton);
37
37
 
38
38
  const DismissButton=props=>{const{onClick,style,testId}=props;return jsxRuntime.jsx(IconButton__default["default"],{icon:xIcon__default["default"],"aria-label":props["aria-label"]||"Close",onClick:onClick,kind:"tertiary",actionType:"neutral",style:[componentStyles.root,style],testId:testId})};const componentStyles=aphrodite.StyleSheet.create({root:{position:"absolute",insetInlineEnd:wonderBlocksTokens.sizing.size_080,top:wonderBlocksTokens.sizing.size_080,zIndex:1,":focus":wonderBlocksStyles.focusStyles.focus[":focus-visible"]}});
39
39
 
40
- const Card=React__namespace.forwardRef(function Card(props,ref){const{styles,labels,tag,testId,background="base-default",borderRadius="small",paddingSize="small",elevation="none",children,onDismiss,inert}=props;const isBackgroundToken=background==="base-default"||background==="base-subtle";const componentStyles=getComponentStyles({background:isBackgroundToken?background:null,borderRadius,paddingSize,elevation});return jsxRuntime.jsxs(wonderBlocksCore.View,{"aria-label":labels?.cardAriaLabel,style:[componentStyles.root,!isBackgroundToken&&{background:`url(${background})`,backgroundSize:"cover"},styles?.root],ref:ref,tag:tag,testId:testId,inert:inert?"":undefined,children:[onDismiss?jsxRuntime.jsx(DismissButton,{"aria-label":labels?.dismissButtonAriaLabel||"Close",onClick:e=>onDismiss?.(e)}):null,children]})});const styleMap={backgroundColor:{"base-subtle":wonderBlocksTokens.semanticColor.core.background.base.subtle,"base-default":wonderBlocksTokens.semanticColor.core.background.base.default},borderRadius:{small:wonderBlocksTokens.border.radius.radius_080,medium:wonderBlocksTokens.border.radius.radius_120},padding:{none:wonderBlocksTokens.sizing.size_0,small:wonderBlocksTokens.sizing.size_160,medium:wonderBlocksTokens.sizing.size_240},elevation:{none:"none",low:wonderBlocksTokens.boxShadow.low}};const getComponentStyles=({background="base-default",borderRadius="small",paddingSize="small",elevation="none"})=>{const bgColor=background;return aphrodite.StyleSheet.create({root:{backgroundColor:bgColor&&styleMap.backgroundColor[bgColor],borderColor:wonderBlocksTokens.semanticColor.core.border.neutral.subtle,borderStyle:"solid",borderWidth:wonderBlocksTokens.border.width.thin,fontFamily:wonderBlocksTokens.font.family.sans,minInlineSize:wonderBlocksTokens.sizing.size_280,position:"relative",borderRadius:styleMap.borderRadius[borderRadius],boxShadow:styleMap.elevation[elevation],padding:styleMap.padding[paddingSize]}})};
40
+ const Card=React__namespace.forwardRef(function Card(props,ref){const{styles,labels,tag,testId,background="base-default",borderRadius="small",paddingSize="small",elevation="none",children,onDismiss,inert,"aria-labelledby":ariaLabelledBy,"aria-busy":ariaBusy,role}=props;const isBackgroundToken=background==="base-default"||background==="base-subtle";const componentStyles=getComponentStyles({background:isBackgroundToken?background:null,borderRadius,paddingSize,elevation});return jsxRuntime.jsxs(wonderBlocksCore.View,{"aria-busy":ariaBusy,"aria-label":labels?.cardAriaLabel,"aria-labelledby":ariaLabelledBy,style:[componentStyles.root,!isBackgroundToken&&{background:`url(${background})`,backgroundSize:"cover"},styles?.root],ref:ref,role:role,tag:tag,testId:testId,inert:inert?"":undefined,children:[onDismiss?jsxRuntime.jsx(DismissButton,{"aria-label":labels?.dismissButtonAriaLabel||"Close",onClick:e=>onDismiss?.(e),testId:testId&&`${testId}-dismiss-button`}):null,children]})});const styleMap={backgroundColor:{"base-subtle":wonderBlocksTokens.semanticColor.core.background.base.subtle,"base-default":wonderBlocksTokens.semanticColor.core.background.base.default},borderRadius:{small:wonderBlocksTokens.border.radius.radius_080,medium:wonderBlocksTokens.border.radius.radius_120},padding:{none:wonderBlocksTokens.sizing.size_0,small:wonderBlocksTokens.sizing.size_160,medium:wonderBlocksTokens.sizing.size_240},elevation:{none:"none",low:wonderBlocksTokens.boxShadow.low}};const getComponentStyles=({background="base-default",borderRadius="small",paddingSize="small",elevation="none"})=>{const bgColor=background;return aphrodite.StyleSheet.create({root:{backgroundColor:bgColor&&styleMap.backgroundColor[bgColor],borderColor:wonderBlocksTokens.semanticColor.core.border.neutral.subtle,borderStyle:"solid",borderWidth:wonderBlocksTokens.border.width.thin,fontFamily:wonderBlocksTokens.font.family.sans,minInlineSize:wonderBlocksTokens.sizing.size_280,position:"relative",borderRadius:styleMap.borderRadius[borderRadius],boxShadow:styleMap.elevation[elevation],padding:styleMap.padding[paddingSize]}})};
41
41
 
42
42
  exports.Card = Card;
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-PR2860-20251112235619",
6
+ "version": "0.0.0-PR2876-20251209223105",
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-icon-button": "0.0.0-PR2860-20251112235619",
25
- "@khanacademy/wonder-blocks-core": "0.0.0-PR2860-20251112235619",
26
- "@khanacademy/wonder-blocks-tokens": "0.0.0-PR2860-20251112235619"
24
+ "@khanacademy/wonder-blocks-core": "0.0.0-PR2876-20251209223105",
25
+ "@khanacademy/wonder-blocks-icon-button": "0.0.0-PR2876-20251209223105",
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-20251209223105"
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,194 +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("Accessibility", () => {
98
- it("should pass custom aria-label to dismiss button", () => {
99
- // Arrange
100
- render(
101
- <Card
102
- onDismiss={() => {}}
103
- labels={{dismissButtonAriaLabel: "Custom Close"}}
104
- >
105
- <div>Content</div>
106
- </Card>,
107
- );
108
-
109
- // Act
110
- const dismissButton = screen.getByRole("button", {
111
- name: "Custom Close",
112
- });
113
-
114
- // Assert
115
- expect(dismissButton).toBeInTheDocument();
116
- });
117
-
118
- it("should render with a custom tag", () => {
119
- // Arrange
120
- render(
121
- <Card tag="section" labels={{cardAriaLabel: "Card Section"}}>
122
- <h2>Heading</h2>
123
- <p>Description</p>
124
- </Card>,
125
- );
126
-
127
- // Act
128
- const section = screen.getByRole("region");
129
-
130
- // Assert
131
- expect(section).toBeInTheDocument();
132
- });
133
-
134
- it("should apply labels.cardAriaLabel for a custom tag", () => {
135
- // Arrange
136
- render(
137
- <Card
138
- tag="section"
139
- labels={{cardAriaLabel: "Custom section label"}}
140
- >
141
- <h2>Heading</h2>
142
- <p>Description</p>
143
- </Card>,
144
- );
145
-
146
- // Act
147
- const section = screen.getByRole("region", {
148
- name: "Custom section label",
149
- });
150
-
151
- // Assert
152
- expect(section).toBeInTheDocument();
153
- });
154
-
155
- it("should apply the inert attribute", () => {
156
- // Arrange
157
- render(
158
- <Card inert testId="card">
159
- <h2>Heading</h2>
160
- <p>Description</p>
161
- <button>Button</button>
162
- </Card>,
163
- );
164
-
165
- // Act
166
- const section = screen.getByTestId("card");
167
-
168
- // Assert
169
- expect(section).toHaveAttribute("inert");
170
- });
171
- });
172
-
173
- describe("Complex content scenarios", () => {
174
- it("should work with fragment children", () => {
175
- // Arrange
176
- render(
177
- <Card>
178
- <>
179
- <span data-testid="fragment-child-1">First</span>
180
- <span data-testid="fragment-child-2">Second</span>
181
- </>
182
- </Card>,
183
- );
184
-
185
- // Act
186
- const firstChild = screen.getByTestId("fragment-child-1");
187
- const secondChild = screen.getByTestId("fragment-child-2");
188
-
189
- // Assert
190
- expect(firstChild).toBeInTheDocument();
191
- expect(secondChild).toBeInTheDocument();
192
- });
193
- });
194
- });
@@ -1,174 +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
- // @ts-expect-error - section tag requires cardAriaLabel
106
- <Card tag="section">Content</Card>;
107
-
108
- // @ts-expect-error - figure tag requires cardAriaLabel
109
- <Card tag="figure">Content</Card>;
110
-
111
- // @ts-expect-error - section tag requires cardAriaLabel in labels
112
- <Card tag="section" labels={{}}>
113
- Content
114
- </Card>;
115
-
116
- // @ts-expect-error - figure tag requires cardAriaLabel in labels
117
- <Card tag="figure" labels={{}}>
118
- Content
119
- </Card>;
120
-
121
- /**
122
- * Card with additional props
123
- */
124
-
125
- <Card testId="my-card">Content</Card>;
126
-
127
- <Card inert>Content</Card>;
128
-
129
- <Card ref={React.createRef<HTMLDivElement>()}>Content</Card>;
130
-
131
- <Card styles={{root: {width: 200}, dismissButton: {position: "absolute"}}}>
132
- Content
133
- </Card>;
134
-
135
- /**
136
- * Card with dismiss and section tag (complex case)
137
- */
138
-
139
- <Card
140
- tag="section"
141
- onDismiss={() => {}}
142
- labels={{
143
- cardAriaLabel: "Card section",
144
- dismissButtonAriaLabel: "Close card",
145
- }}
146
- >
147
- Content
148
- </Card>;
149
-
150
- /**
151
- * Card with all props
152
- */
153
-
154
- <Card
155
- background="base-subtle"
156
- borderRadius="medium"
157
- paddingSize="medium"
158
- elevation="low"
159
- tag="figure"
160
- onDismiss={() => {}}
161
- labels={{
162
- cardAriaLabel: "Card figure",
163
- dismissButtonAriaLabel: "Close card",
164
- }}
165
- testId="complex-card"
166
- inert
167
- styles={{
168
- root: {width: 300},
169
- dismissButton: {top: 10, right: 10},
170
- }}
171
- ref={React.createRef<HTMLElement>()}
172
- >
173
- <div>Complex content</div>
174
- </Card>;