@khanacademy/wonder-blocks-card 0.0.0-PR2799-20250929232634 → 0.0.0-PR2799-20251001174203

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,7 +1,11 @@
1
1
  # @khanacademy/wonder-blocks-card
2
2
 
3
- ## 0.0.0-PR2799-20250929232634
3
+ ## 0.0.0-PR2799-20251001174203
4
4
 
5
5
  ### Minor Changes
6
6
 
7
7
  - 7506aef: Add new Card component
8
+
9
+ ### Patch Changes
10
+
11
+ - @khanacademy/wonder-blocks-core@12.4.0
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,backgroundColorStyle="base-default",borderRadiusStyle="small",paddingSize="small",children,onDismiss,inert}=props;const componentStyles=getComponentStyles({backgroundColorStyle,borderRadiusStyle,paddingSize});return jsxs(View,{"aria-label":labels?.cardAriaLabel,style:[componentStyles.root,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 getComponentStyles=({backgroundColorStyle,borderRadiusStyle,paddingSize})=>{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}};return StyleSheet.create({root:{backgroundColor:backgroundColorStyle&&styleMap.backgroundColor[backgroundColorStyle],borderColor:semanticColor.core.border.neutral.subtle,borderStyle:"solid",borderRadius:borderRadiusStyle&&styleMap.borderRadius[borderRadiusStyle],borderWidth:border.width.thin,boxShadow:boxShadow.low,padding:paddingSize&&styleMap.padding[paddingSize],minInlineSize:sizing.size_280,position:"relative"}})};
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 componentStyles=getComponentStyles({background,borderRadius,paddingSize,elevation});return jsxs(View,{"aria-label":labels?.cardAriaLabel,style:[componentStyles.root,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 getComponentStyles=({background,borderRadius,paddingSize,elevation})=>{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}};return StyleSheet.create({root:{backgroundColor:background&&styleMap.backgroundColor[background],borderColor:semanticColor.core.border.neutral.subtle,borderStyle:"solid",borderRadius:borderRadius&&styleMap.borderRadius[borderRadius],borderWidth:border.width.thin,boxShadow:elevation&&styleMap.elevation[elevation],padding:paddingSize&&styleMap.padding[paddingSize],minInlineSize:sizing.size_280,position:"relative"}})};
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,backgroundColorStyle="base-default",borderRadiusStyle="small",paddingSize="small",children,onDismiss,inert}=props;const componentStyles=getComponentStyles({backgroundColorStyle,borderRadiusStyle,paddingSize});return jsxRuntime.jsxs(wonderBlocksCore.View,{"aria-label":labels?.cardAriaLabel,style:[componentStyles.root,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 getComponentStyles=({backgroundColorStyle,borderRadiusStyle,paddingSize})=>{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}};return aphrodite.StyleSheet.create({root:{backgroundColor:backgroundColorStyle&&styleMap.backgroundColor[backgroundColorStyle],borderColor:wonderBlocksTokens.semanticColor.core.border.neutral.subtle,borderStyle:"solid",borderRadius:borderRadiusStyle&&styleMap.borderRadius[borderRadiusStyle],borderWidth:wonderBlocksTokens.border.width.thin,boxShadow:wonderBlocksTokens.boxShadow.low,padding:paddingSize&&styleMap.padding[paddingSize],minInlineSize:wonderBlocksTokens.sizing.size_280,position:"relative"}})};
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 componentStyles=getComponentStyles({background,borderRadius,paddingSize,elevation});return jsxRuntime.jsxs(wonderBlocksCore.View,{"aria-label":labels?.cardAriaLabel,style:[componentStyles.root,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 getComponentStyles=({background,borderRadius,paddingSize,elevation})=>{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}};return aphrodite.StyleSheet.create({root:{backgroundColor:background&&styleMap.backgroundColor[background],borderColor:wonderBlocksTokens.semanticColor.core.border.neutral.subtle,borderStyle:"solid",borderRadius:borderRadius&&styleMap.borderRadius[borderRadius],borderWidth:wonderBlocksTokens.border.width.thin,boxShadow:elevation&&styleMap.elevation[elevation],padding:paddingSize&&styleMap.padding[paddingSize],minInlineSize:wonderBlocksTokens.sizing.size_280,position:"relative"}})};
41
41
 
42
42
  exports.Card = Card;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khanacademy/wonder-blocks-card",
3
- "version": "0.0.0-PR2799-20250929232634",
3
+ "version": "0.0.0-PR2799-20251001174203",
4
4
  "design": "v1",
5
5
  "description": "Card component for Wonder Blocks.",
6
6
  "main": "dist/index.js",
@@ -0,0 +1,150 @@
1
+ import * as React from "react";
2
+
3
+ import Card from "../../components/card";
4
+
5
+ /**
6
+ * Basic Card usage
7
+ */
8
+
9
+ <Card>Hello, world!</Card>;
10
+
11
+ <Card>
12
+ <div>Some content</div>
13
+ </Card>;
14
+
15
+ /**
16
+ * Card with all style props
17
+ */
18
+
19
+ <Card
20
+ background="base-default"
21
+ borderRadius="small"
22
+ paddingSize="small"
23
+ elevation="none"
24
+ >
25
+ Content
26
+ </Card>;
27
+
28
+ <Card
29
+ background="base-subtle"
30
+ borderRadius="medium"
31
+ paddingSize="medium"
32
+ elevation="low"
33
+ >
34
+ Content
35
+ </Card>;
36
+
37
+ <Card paddingSize="none">Content</Card>;
38
+
39
+ // @ts-expect-error - invalid background value
40
+ <Card background="invalid-background">Content</Card>;
41
+
42
+ // @ts-expect-error - invalid borderRadius value
43
+ <Card borderRadius="invalid-radius">Content</Card>;
44
+
45
+ // @ts-expect-error - invalid paddingSize value
46
+ <Card paddingSize="invalid-padding">Content</Card>;
47
+
48
+ // @ts-expect-error - invalid elevation value
49
+ <Card elevation="invalid-elevation">Content</Card>;
50
+
51
+ /**
52
+ * Card with dismiss functionality
53
+ */
54
+
55
+ <Card onDismiss={() => {}} labels={{dismissButtonAriaLabel: "Close card"}}>
56
+ Content
57
+ </Card>;
58
+
59
+ // @ts-expect-error - onDismiss requires dismissButtonAriaLabel
60
+ <Card onDismiss={() => {}}>Content</Card>;
61
+
62
+ // @ts-expect-error - onDismiss requires dismissButtonAriaLabel
63
+ <Card onDismiss={() => {}} labels={{}}>
64
+ Content
65
+ </Card>;
66
+
67
+ /**
68
+ * Card with different HTML tags
69
+ */
70
+
71
+ <Card tag="div">Content</Card>;
72
+
73
+ <Card tag="section" labels={{cardAriaLabel: "Card section"}}>
74
+ Content
75
+ </Card>;
76
+
77
+ <Card tag="figure" labels={{cardAriaLabel: "Card figure"}}>
78
+ Content
79
+ </Card>;
80
+
81
+ // @ts-expect-error - section tag requires cardAriaLabel
82
+ <Card tag="section">Content</Card>;
83
+
84
+ // @ts-expect-error - figure tag requires cardAriaLabel
85
+ <Card tag="figure">Content</Card>;
86
+
87
+ // @ts-expect-error - section tag requires cardAriaLabel in labels
88
+ <Card tag="section" labels={{}}>
89
+ Content
90
+ </Card>;
91
+
92
+ // @ts-expect-error - figure tag requires cardAriaLabel in labels
93
+ <Card tag="figure" labels={{}}>
94
+ Content
95
+ </Card>;
96
+
97
+ /**
98
+ * Card with additional props
99
+ */
100
+
101
+ <Card testId="my-card">Content</Card>;
102
+
103
+ <Card inert>Content</Card>;
104
+
105
+ <Card ref={React.createRef<HTMLDivElement>()}>Content</Card>;
106
+
107
+ <Card styles={{root: {width: 200}, dismissButton: {position: "absolute"}}}>
108
+ Content
109
+ </Card>;
110
+
111
+ /**
112
+ * Card with dismiss and section tag (complex case)
113
+ */
114
+
115
+ <Card
116
+ tag="section"
117
+ onDismiss={() => {}}
118
+ labels={{
119
+ cardAriaLabel: "Card section",
120
+ dismissButtonAriaLabel: "Close card",
121
+ }}
122
+ >
123
+ Content
124
+ </Card>;
125
+
126
+ /**
127
+ * Card with all props
128
+ */
129
+
130
+ <Card
131
+ background="base-subtle"
132
+ borderRadius="medium"
133
+ paddingSize="medium"
134
+ elevation="low"
135
+ tag="figure"
136
+ onDismiss={() => {}}
137
+ labels={{
138
+ cardAriaLabel: "Card figure",
139
+ dismissButtonAriaLabel: "Close card",
140
+ }}
141
+ testId="complex-card"
142
+ inert
143
+ styles={{
144
+ root: {width: 300},
145
+ dismissButton: {top: 10, right: 10},
146
+ }}
147
+ ref={React.createRef<HTMLElement>()}
148
+ >
149
+ <div>Complex content</div>
150
+ </Card>;
@@ -40,7 +40,7 @@ type BaseCardProps = {
40
40
  * The test ID used to locate this component in automated tests.
41
41
  */
42
42
  testId?: string;
43
- } & StyleOnlyProps;
43
+ } & StyleProps;
44
44
 
45
45
  /**
46
46
  * A callback function to handle dismissing the card. When this prop is present,
@@ -73,21 +73,16 @@ type TagProps =
73
73
  labels?: Record<string, any>;
74
74
  };
75
75
 
76
- /**
77
- * Combined props - these two requirements work independently
78
- */
79
- type ConditionalProps = BaseCardProps & TagProps & DismissProps;
80
-
81
- type StyleOnlyProps = {
76
+ type StyleProps = {
82
77
  /**
83
- * The background color of the card, as a string identifier that matches a semanticColor token.
78
+ * The background style of the card, as a string identifier that matches a semanticColor token.
84
79
  * This can be one of:
85
- * - `"base-subtle"` `semanticColor.core.background.base.subtle`: a light gray background.
86
- * - `"base-default"`, matching `semanticColor.core.background.base.default`: a white background.
80
+ * - `"base-subtle"` (color), `semanticColor.core.background.base.subtle`: a light gray background.
81
+ * - `"base-default"` (color), `semanticColor.core.background.base.default`: a white background.
87
82
  *
88
83
  * Default: `"base-default"`
89
84
  */
90
- backgroundColorStyle?: "base-subtle" | "base-default";
85
+ background?: "base-subtle" | "base-default";
91
86
  /**
92
87
  * The border radius of the card, as a string identifier that matches a border.radius token.
93
88
  * This can be one of:
@@ -96,7 +91,7 @@ type StyleOnlyProps = {
96
91
  *
97
92
  * Default: `"radius_080"`
98
93
  */
99
- borderRadiusStyle?: "small" | "medium";
94
+ borderRadius?: "small" | "medium";
100
95
  /**
101
96
  * The padding inside the card, as a string identifier that matches a sizing token.
102
97
  * This can be one of:
@@ -107,9 +102,18 @@ type StyleOnlyProps = {
107
102
  * Default: `"size_160"`
108
103
  */
109
104
  paddingSize?: "none" | "small" | "medium";
105
+ /**
106
+ * The box-shadow for the card, as a string identifier that matches a sizing token.
107
+ * This can be one of:
108
+ * - `"none"`: no elevation.
109
+ * - `"low"`, matching `boxShadow.low`.
110
+ *
111
+ * Default: `"none"`
112
+ */
113
+ elevation?: "none" | "low";
110
114
  };
111
- type Props = ConditionalProps;
112
115
 
116
+ type Props = BaseCardProps & TagProps & DismissProps;
113
117
  /**
114
118
  * The Card component is a flexible, reusable UI building block designed to
115
119
  * encapsulate content within a structured, visually distinct container.
@@ -139,20 +143,27 @@ type Props = ConditionalProps;
139
143
  *
140
144
  * Cards can be customized via the following props:
141
145
  *
142
- * **`backgroundColorStyle` prop**
146
+ * **`background` prop**
143
147
  *
144
148
  * | value | resolves to |
145
149
  * |---|---|
146
150
  * | `base-subtle` | `semanticColor.core.background.base.subtle` (light gray) |
147
151
  * | `base-default` | `semanticColor.core.background.base.default` (white) |
148
152
  *
149
- * **`borderRadiusStyle` prop**
153
+ * **`borderRadius` prop**
150
154
  *
151
155
  * | value | resolves to |
152
156
  * |---|---|
153
157
  * | `small` | `border.radius.radius_080` |
154
158
  * | `medium` | `border.radius.radius_120` |
155
159
  *
160
+ * **`elevation` prop**
161
+ *
162
+ * | value | resolves to |
163
+ * |---|---|
164
+ * | `none` | `none` |
165
+ * | `low` | `boxShadow.low` |
166
+ *
156
167
  * **`paddingSize` prop**
157
168
  *
158
169
  * | value | resolves to |
@@ -178,18 +189,20 @@ const Card = React.forwardRef(function Card(
178
189
  labels,
179
190
  tag,
180
191
  testId,
181
- backgroundColorStyle = "base-default",
182
- borderRadiusStyle = "small",
192
+ background = "base-default",
193
+ borderRadius = "small",
183
194
  paddingSize = "small",
195
+ elevation = "none",
184
196
  children,
185
197
  onDismiss,
186
198
  inert,
187
199
  } = props;
188
200
 
189
201
  const componentStyles = getComponentStyles({
190
- backgroundColorStyle,
191
- borderRadiusStyle,
202
+ background,
203
+ borderRadius,
192
204
  paddingSize,
205
+ elevation,
193
206
  });
194
207
  return (
195
208
  <View
@@ -212,10 +225,11 @@ const Card = React.forwardRef(function Card(
212
225
  });
213
226
 
214
227
  const getComponentStyles = ({
215
- backgroundColorStyle,
216
- borderRadiusStyle,
228
+ background,
229
+ borderRadius,
217
230
  paddingSize,
218
- }: StyleOnlyProps) => {
231
+ elevation,
232
+ }: StyleProps) => {
219
233
  // Map prop values to tokens
220
234
  const styleMap = {
221
235
  backgroundColor: {
@@ -231,19 +245,20 @@ const getComponentStyles = ({
231
245
  small: sizing.size_160,
232
246
  medium: sizing.size_240,
233
247
  },
248
+ elevation: {
249
+ none: "none",
250
+ low: boxShadow.low,
251
+ },
234
252
  } as const;
235
253
 
236
254
  return StyleSheet.create({
237
255
  root: {
238
- backgroundColor:
239
- backgroundColorStyle &&
240
- styleMap.backgroundColor[backgroundColorStyle],
256
+ backgroundColor: background && styleMap.backgroundColor[background],
241
257
  borderColor: semanticColor.core.border.neutral.subtle,
242
258
  borderStyle: "solid",
243
- borderRadius:
244
- borderRadiusStyle && styleMap.borderRadius[borderRadiusStyle],
259
+ borderRadius: borderRadius && styleMap.borderRadius[borderRadius],
245
260
  borderWidth: border.width.thin,
246
- boxShadow: boxShadow.low,
261
+ boxShadow: elevation && styleMap.elevation[elevation],
247
262
  padding: paddingSize && styleMap.padding[paddingSize],
248
263
  minInlineSize: sizing.size_280,
249
264
  position: "relative",