@khanacademy/wonder-blocks-card 0.0.0-PR2860-20251112235619 → 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 +37 -6
- package/dist/components/card.d.ts +66 -23
- package/dist/components/dismiss-button.d.ts +1 -11
- package/dist/es/index.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +5 -5
- package/dist/__tests__/components/card.test.d.ts +0 -1
- package/dist/__tests__/components/card.typestest.d.ts +0 -1
- package/dist/__tests__/components/dismiss-button.test.d.ts +0 -1
- package/src/__tests__/components/card.test.tsx +0 -194
- package/src/__tests__/components/card.typestest.tsx +0 -174
- package/src/__tests__/components/dismiss-button.test.tsx +0 -145
- package/src/components/card.tsx +0 -264
- package/src/components/dismiss-button.tsx +0 -61
- package/src/index.ts +0 -3
- package/tsconfig-build.json +0 -16
- package/tsconfig-build.tsbuildinfo +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,16 +1,47 @@
|
|
|
1
1
|
# @khanacademy/wonder-blocks-card
|
|
2
2
|
|
|
3
|
-
## 0.0.0-
|
|
3
|
+
## 0.0.0-PR2876-20251209213809
|
|
4
4
|
|
|
5
5
|
### Patch Changes
|
|
6
6
|
|
|
7
|
-
-
|
|
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
|
|
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 [
|
|
40
|
+
- Updated dependencies [8a36c70]
|
|
10
41
|
- Updated dependencies [3e0d137]
|
|
11
|
-
- @khanacademy/wonder-blocks-core@
|
|
12
|
-
- @khanacademy/wonder-blocks-icon-button@
|
|
13
|
-
- @khanacademy/wonder-blocks-tokens@
|
|
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
|
-
*
|
|
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
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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<
|
|
179
|
-
|
|
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<
|
|
212
|
-
|
|
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-
|
|
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-
|
|
25
|
-
"@khanacademy/wonder-blocks-
|
|
26
|
-
"@khanacademy/wonder-blocks-tokens": "
|
|
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": "
|
|
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,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>;
|