@khanacademy/wonder-blocks-card 0.0.0-PR2816-20251007223425 → 0.0.0-PR2817-20251007222151

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,10 @@
1
1
  # @khanacademy/wonder-blocks-card
2
2
 
3
- ## 0.0.0-PR2816-20251007223425
3
+ ## 0.0.0-PR2817-20251007222151
4
4
 
5
- ### Minor Changes
5
+ ### Patch Changes
6
6
 
7
- - be020e9: Adds support for image backgrounds via the background prop
7
+ - @khanacademy/wonder-blocks-icon-button@0.0.0-PR2817-20251007222151
8
8
 
9
9
  ## 1.1.0
10
10
 
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 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 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 dynamicStyles={};const getStyleKey=({background,borderRadius,paddingSize,elevation})=>{return `${background||"default"}-${borderRadius||"small"}-${paddingSize||"small"}-${elevation||"none"}`};const getComponentStyles=props=>{const styleKey=getStyleKey(props);if(dynamicStyles[styleKey]){return dynamicStyles[styleKey]}const{background,borderRadius,paddingSize,elevation}=props;const isBackgroundColorStyle=background==="base-subtle"||background==="base-default";const newStyles=StyleSheet.create({root:{...isBackgroundColorStyle&&{backgroundColor:styleMap.backgroundColor[background]},...!isBackgroundColorStyle&&background&&{background:`url(${background})`,backgroundSize:"cover"},borderColor:semanticColor.core.border.neutral.subtle,borderStyle:"solid",borderWidth:border.width.thin,minInlineSize:sizing.size_280,position:"relative",borderRadius:borderRadius&&styleMap.borderRadius[borderRadius],boxShadow:elevation&&styleMap.elevation[elevation],padding:paddingSize&&styleMap.padding[paddingSize]}});dynamicStyles[styleKey]=newStyles;return newStyles};
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,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 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 dynamicStyles={};const getStyleKey=({background,borderRadius,paddingSize,elevation})=>{return `${background||"default"}-${borderRadius||"small"}-${paddingSize||"small"}-${elevation||"none"}`};const getComponentStyles=props=>{const styleKey=getStyleKey(props);if(dynamicStyles[styleKey]){return dynamicStyles[styleKey]}const{background,borderRadius,paddingSize,elevation}=props;const isBackgroundColorStyle=background==="base-subtle"||background==="base-default";const newStyles=aphrodite.StyleSheet.create({root:{...isBackgroundColorStyle&&{backgroundColor:styleMap.backgroundColor[background]},...!isBackgroundColorStyle&&background&&{background:`url(${background})`,backgroundSize:"cover"},borderColor:wonderBlocksTokens.semanticColor.core.border.neutral.subtle,borderStyle:"solid",borderWidth:wonderBlocksTokens.border.width.thin,minInlineSize:wonderBlocksTokens.sizing.size_280,position:"relative",borderRadius:borderRadius&&styleMap.borderRadius[borderRadius],boxShadow:elevation&&styleMap.elevation[elevation],padding:paddingSize&&styleMap.padding[paddingSize]}});dynamicStyles[styleKey]=newStyles;return newStyles};
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-PR2816-20251007223425",
3
+ "version": "0.0.0-PR2817-20251007222151",
4
4
  "design": "v1",
5
5
  "description": "Card component for Wonder Blocks.",
6
6
  "main": "dist/index.js",
@@ -14,8 +14,8 @@
14
14
  },
15
15
  "dependencies": {
16
16
  "@khanacademy/wonder-blocks-core": "12.4.0",
17
- "@khanacademy/wonder-blocks-tokens": "14.0.0",
18
- "@khanacademy/wonder-blocks-icon-button": "10.5.2"
17
+ "@khanacademy/wonder-blocks-icon-button": "0.0.0-PR2817-20251007222151",
18
+ "@khanacademy/wonder-blocks-tokens": "14.0.0"
19
19
  },
20
20
  "peerDependencies": {
21
21
  "aphrodite": "^1.2.5",
@@ -1,12 +1,6 @@
1
1
  import * as React from "react";
2
2
  import {render, screen} from "@testing-library/react";
3
3
  import {userEvent} from "@testing-library/user-event";
4
- import {
5
- boxShadow,
6
- border,
7
- semanticColor,
8
- sizing,
9
- } from "@khanacademy/wonder-blocks-tokens";
10
4
 
11
5
  import Card from "../../components/card";
12
6
 
@@ -197,180 +191,4 @@ describe("Card", () => {
197
191
  expect(secondChild).toBeInTheDocument();
198
192
  });
199
193
  });
200
-
201
- describe("Style application", () => {
202
- it("should apply default styles", () => {
203
- // Arrange
204
- const testId = "test-card";
205
-
206
- // Act
207
- render(<Card testId={testId}>Content</Card>);
208
- const card = screen.getByTestId(testId);
209
-
210
- // Assert
211
- expect(card).toHaveStyle({
212
- position: "relative",
213
- borderStyle: "solid",
214
- });
215
- });
216
-
217
- it("should apply base-subtle background", () => {
218
- // Arrange
219
- const testId = "test-card";
220
-
221
- // Act
222
- render(
223
- <Card testId={testId} background="base-subtle">
224
- Content
225
- </Card>,
226
- );
227
- const card = screen.getByTestId(testId);
228
-
229
- // Assert
230
- expect(card).toHaveStyle({
231
- backgroundColor: semanticColor.core.background.base.subtle,
232
- });
233
- });
234
-
235
- it("should apply medium border radius", () => {
236
- // Arrange
237
- const testId = "test-card";
238
-
239
- // Act
240
- render(
241
- <Card testId={testId} borderRadius="medium">
242
- Content
243
- </Card>,
244
- );
245
- const card = screen.getByTestId(testId);
246
-
247
- // Assert
248
- expect(card).toHaveStyle({
249
- borderRadius: border.radius.radius_120,
250
- });
251
- });
252
-
253
- it("should apply medium padding", () => {
254
- // Arrange
255
- const testId = "test-card";
256
-
257
- // Act
258
- render(
259
- <Card testId={testId} paddingSize="medium">
260
- Content
261
- </Card>,
262
- );
263
- const card = screen.getByTestId(testId);
264
-
265
- // Assert
266
- expect(card).toHaveStyle({
267
- padding: sizing.size_240,
268
- });
269
- });
270
-
271
- it("should apply low elevation", () => {
272
- // Arrange
273
- const testId = "test-card";
274
-
275
- // Act
276
- render(
277
- <Card testId={testId} elevation="low">
278
- Content
279
- </Card>,
280
- );
281
- const card = screen.getByTestId(testId);
282
-
283
- // Assert
284
- expect(card).toHaveStyle({
285
- boxShadow: boxShadow.low,
286
- });
287
- });
288
-
289
- it("should apply image background", () => {
290
- // Arrange
291
- const testId = "test-card";
292
- const testImage = Image;
293
-
294
- // Act
295
- render(
296
- <Card testId={testId} background={testImage}>
297
- Content
298
- </Card>,
299
- );
300
- const card = screen.getByTestId(testId);
301
-
302
- // Assert
303
- expect(card).toHaveStyle({
304
- backgroundSize: "cover",
305
- });
306
- });
307
- });
308
-
309
- describe("Style application", () => {
310
- it("should apply custom margin style", () => {
311
- // Arrange
312
- const testId = "test-card";
313
- const customStyle = {marginTop: "10px"};
314
-
315
- // Act
316
- render(
317
- <Card testId={testId} styles={{root: customStyle}}>
318
- Content
319
- </Card>,
320
- );
321
-
322
- // Assert
323
- expect(screen.getByTestId(testId)).toHaveStyle(customStyle);
324
- });
325
-
326
- it("should apply custom padding style", () => {
327
- // Arrange
328
- const testId = "test-card";
329
- const customStyle = {padding: "20px"};
330
-
331
- // Act
332
- render(
333
- <Card testId={testId} styles={{root: customStyle}}>
334
- Content
335
- </Card>,
336
- );
337
-
338
- // Assert
339
- expect(screen.getByTestId(testId)).toHaveStyle(customStyle);
340
- });
341
-
342
- it("should apply custom background color", () => {
343
- // Arrange
344
- const testId = "test-card";
345
- const customStyle = {backgroundColor: "rgb(255, 0, 0)"};
346
-
347
- // Act
348
- render(
349
- <Card testId={testId} styles={{root: customStyle}}>
350
- Content
351
- </Card>,
352
- );
353
-
354
- // Assert
355
- expect(screen.getByTestId(testId)).toHaveStyle(customStyle);
356
- });
357
-
358
- it("should maintain default styles with custom styles", () => {
359
- // Arrange
360
- const testId = "test-card";
361
- const customStyle = {marginTop: "10px"};
362
-
363
- // Act
364
- render(
365
- <Card testId={testId} styles={{root: customStyle}}>
366
- Content
367
- </Card>,
368
- );
369
-
370
- // Assert
371
- const element = screen.getByTestId(testId);
372
- expect(element).toHaveStyle(customStyle);
373
- expect(element).toHaveStyle({position: "relative"}); // A default style
374
- });
375
- });
376
194
  });
@@ -64,9 +64,6 @@ import Card from "../../components/card";
64
64
  Content
65
65
  </Card>;
66
66
 
67
- // @ts-expect-error - onClick is not allowed on Card wrapper
68
- <Card onClick={() => {}}>Content</Card>;
69
-
70
67
  /**
71
68
  * Card with different HTML tags
72
69
  */
@@ -79,13 +79,10 @@ type StyleProps = {
79
79
  * This can be one of:
80
80
  * - `"base-subtle"` (color), `semanticColor.core.background.base.subtle`: a light gray background.
81
81
  * - `"base-default"` (color), `semanticColor.core.background.base.default`: a white background.
82
- * - `Image` (image), a URL string for a background image. Can be an imported image file or a URL string.
83
- *
84
- * For additional background styling such as repeat or size, use the `styles.root` prop to pass in custom styles.
85
82
  *
86
83
  * Default: `"base-default"`
87
84
  */
88
- background?: "base-subtle" | "base-default" | typeof Image;
85
+ background?: "base-subtle" | "base-default";
89
86
  /**
90
87
  * The border radius of the card, as a string identifier that matches a border.radius token.
91
88
  * This can be one of:
@@ -144,7 +141,7 @@ type Props = BaseCardProps & TagProps & DismissProps;
144
141
  *
145
142
  * 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.
146
143
  *
147
- * See additional Accessibility docs.
144
+ * See additional [Accessibility docs](./?path=/docs/packages-card-card-accessibility--docs).
148
145
  */
149
146
 
150
147
  const Card = React.forwardRef(function Card(
@@ -191,87 +188,46 @@ const Card = React.forwardRef(function Card(
191
188
  );
192
189
  });
193
190
 
194
- // Map prop values to tokens
195
- const styleMap = {
196
- backgroundColor: {
197
- "base-subtle": semanticColor.core.background.base.subtle,
198
- "base-default": semanticColor.core.background.base.default,
199
- },
200
- borderRadius: {
201
- small: border.radius.radius_080,
202
- medium: border.radius.radius_120,
203
- },
204
- padding: {
205
- none: sizing.size_0,
206
- small: sizing.size_160,
207
- medium: sizing.size_240,
208
- },
209
- elevation: {
210
- none: "none",
211
- low: boxShadow.low,
212
- },
213
- } as const;
214
-
215
- // Cache for dynamically generated styles
216
- const dynamicStyles: Record<string, any> = {};
217
-
218
- /**
219
- * Generates a unique key for caching styles based on prop combinations
220
- */
221
- const getStyleKey = ({
191
+ const getComponentStyles = ({
222
192
  background,
223
193
  borderRadius,
224
194
  paddingSize,
225
195
  elevation,
226
- }: StyleProps): string => {
227
- return `${background || "default"}-${borderRadius || "small"}-${
228
- paddingSize || "small"
229
- }-${elevation || "none"}`;
230
- };
231
-
232
- /**
233
- * Generates the component styles with caching for better performance
234
- */
235
- const getComponentStyles = (props: StyleProps) => {
236
- const styleKey = getStyleKey(props);
237
- // Return cached styles if they exist
238
- if (dynamicStyles[styleKey]) {
239
- return dynamicStyles[styleKey];
240
- }
241
-
242
- const {background, borderRadius, paddingSize, elevation} = props;
243
- const isBackgroundColorStyle =
244
- background === "base-subtle" || background === "base-default";
196
+ }: StyleProps) => {
197
+ // Map prop values to tokens
198
+ const styleMap = {
199
+ backgroundColor: {
200
+ "base-subtle": semanticColor.core.background.base.subtle,
201
+ "base-default": semanticColor.core.background.base.default,
202
+ },
203
+ borderRadius: {
204
+ small: border.radius.radius_080,
205
+ medium: border.radius.radius_120,
206
+ },
207
+ padding: {
208
+ none: sizing.size_0,
209
+ small: sizing.size_160,
210
+ medium: sizing.size_240,
211
+ },
212
+ elevation: {
213
+ none: "none",
214
+ low: boxShadow.low,
215
+ },
216
+ } as const;
245
217
 
246
- // Generate new styles
247
- const newStyles = StyleSheet.create({
218
+ return StyleSheet.create({
248
219
  root: {
249
- // Background styles
250
- ...(isBackgroundColorStyle && {
251
- backgroundColor: styleMap.backgroundColor[background],
252
- }),
253
- // Background image styles
254
- ...(!isBackgroundColorStyle &&
255
- background && {
256
- background: `url(${background})`,
257
- backgroundSize: "cover",
258
- }),
259
- // Common styles
220
+ backgroundColor: background && styleMap.backgroundColor[background],
260
221
  borderColor: semanticColor.core.border.neutral.subtle,
261
222
  borderStyle: "solid",
262
- borderWidth: border.width.thin,
263
- minInlineSize: sizing.size_280,
264
- position: "relative",
265
- // Optional styles based on props
266
223
  borderRadius: borderRadius && styleMap.borderRadius[borderRadius],
224
+ borderWidth: border.width.thin,
267
225
  boxShadow: elevation && styleMap.elevation[elevation],
268
226
  padding: paddingSize && styleMap.padding[paddingSize],
227
+ minInlineSize: sizing.size_280,
228
+ position: "relative",
269
229
  },
270
230
  });
271
-
272
- // Cache the styles
273
- dynamicStyles[styleKey] = newStyles;
274
- return newStyles;
275
231
  };
276
232
 
277
233
  export default Card;