diff --git a/skills/carbon-react/components/card-column.md b/skills/carbon-react/components/card-column.md index fff61699e5..78f58d9dc3 100644 --- a/skills/carbon-react/components/card-column.md +++ b/skills/carbon-react/components/card-column.md @@ -11,6 +11,8 @@ description: Carbon CardColumn component props and usage examples. ## Source - Export: `./components/card` - Props interface: `CardColumnProps` +- Deprecated: Yes +- Deprecation reason: `CardColumn` has been deprecated. ## Props | Name | Type | Required | Literals | Description | Default | diff --git a/skills/carbon-react/components/card-footer.md b/skills/carbon-react/components/card-footer.md index 9845108ef0..95e4f11df8 100644 --- a/skills/carbon-react/components/card-footer.md +++ b/skills/carbon-react/components/card-footer.md @@ -13,41 +13,41 @@ description: Carbon CardFooter component props and usage examples. - Props interface: `CardFooterProps` ## Props -| Name | Type | Required | Literals | Description | Default | -| --- | --- | --- | --- | --- | --- | -| children | React.ReactNode | Yes | | Child nodes | | -| roundness | "large" \| "default" | Yes | large \| default | Sets the level of roundness of the corners, "default" is 8px and "large" is 16px | | -| m | ResponsiveValue \| undefined | No | | Margin on top, left, bottom and right | | -| margin | ResponsiveValue \| undefined | No | | Margin on top, left, bottom and right | | -| marginBottom | ResponsiveValue \| undefined | No | | Margin on bottom | | -| marginLeft | ResponsiveValue \| undefined | No | | Margin on left | | -| marginRight | ResponsiveValue \| undefined | No | | Margin on right | | -| marginTop | ResponsiveValue \| undefined | No | | Margin on top | | -| marginX | ResponsiveValue \| undefined | No | | Margin on left and right | | -| marginY | ResponsiveValue \| undefined | No | | Margin on top and bottom | | -| mb | ResponsiveValue \| undefined | No | | Margin on bottom | | -| ml | ResponsiveValue \| undefined | No | | Margin on left | | -| mr | ResponsiveValue \| undefined | No | | Margin on right | | -| mt | ResponsiveValue \| undefined | No | | Margin on top | | -| mx | ResponsiveValue \| undefined | No | | Margin on left and right | | -| my | ResponsiveValue \| undefined | No | | Margin on top and bottom | | -| p | ResponsiveValue \| undefined | No | | Padding on top, left, bottom and right | | -| padding | ResponsiveValue \| undefined | No | | Padding on top, left, bottom and right | | -| paddingBottom | ResponsiveValue \| undefined | No | | Padding on bottom | | -| paddingLeft | ResponsiveValue \| undefined | No | | Padding on left | | -| paddingRight | ResponsiveValue \| undefined | No | | Padding on right | | -| paddingTop | ResponsiveValue \| undefined | No | | Padding on top | | -| paddingX | ResponsiveValue \| undefined | No | | Padding on left and right | | -| paddingY | ResponsiveValue \| undefined | No | | Padding on top and bottom | | -| pb | ResponsiveValue \| undefined | No | | Padding on bottom | | -| pl | ResponsiveValue \| undefined | No | | Padding on left | | -| pr | ResponsiveValue \| undefined | No | | Padding on right | | -| pt | ResponsiveValue \| undefined | No | | Padding on top | | -| px | ResponsiveValue \| undefined | No | | Padding on left and right | | -| py | ResponsiveValue \| undefined | No | | Padding on top and bottom | | -| variant | "default" \| "transparent" \| undefined | No | | Specify styling variant to render | "default" | -| data-element | string \| undefined | No | | Identifier used for testing purposes, applied to the root element of the component. | | -| data-role | string \| undefined | No | | Identifier used for testing purposes, applied to the root element of the component. | | +| Name | Type | Required | Literals | Deprecated | Deprecation reason | Description | Default | +| --- | --- | --- | --- | --- | --- | --- | --- | +| children | React.ReactNode | Yes | | | | Child nodes | | +| roundness | "large" \| "default" \| "moderate" \| "curved" | Yes | large \| default \| moderate \| curved | | | Sets the level of roundness of the corners. "moderate" is 16px and "curved" is 20px. "default" (alias for "moderate") and "large" (alias for "curved") are deprecated. Use "moderate" or "curved" instead. | | +| m | ResponsiveValue \| undefined | No | | | | Margin on top, left, bottom and right | | +| margin | ResponsiveValue \| undefined | No | | | | Margin on top, left, bottom and right | | +| marginBottom | ResponsiveValue \| undefined | No | | | | Margin on bottom | | +| marginLeft | ResponsiveValue \| undefined | No | | | | Margin on left | | +| marginRight | ResponsiveValue \| undefined | No | | | | Margin on right | | +| marginTop | ResponsiveValue \| undefined | No | | | | Margin on top | | +| marginX | ResponsiveValue \| undefined | No | | | | Margin on left and right | | +| marginY | ResponsiveValue \| undefined | No | | | | Margin on top and bottom | | +| mb | ResponsiveValue \| undefined | No | | | | Margin on bottom | | +| ml | ResponsiveValue \| undefined | No | | | | Margin on left | | +| mr | ResponsiveValue \| undefined | No | | | | Margin on right | | +| mt | ResponsiveValue \| undefined | No | | | | Margin on top | | +| mx | ResponsiveValue \| undefined | No | | | | Margin on left and right | | +| my | ResponsiveValue \| undefined | No | | | | Margin on top and bottom | | +| p | ResponsiveValue \| undefined | No | | | | Padding on top, left, bottom and right | | +| padding | ResponsiveValue \| undefined | No | | | | Padding on top, left, bottom and right | | +| paddingBottom | ResponsiveValue \| undefined | No | | | | Padding on bottom | | +| paddingLeft | ResponsiveValue \| undefined | No | | | | Padding on left | | +| paddingRight | ResponsiveValue \| undefined | No | | | | Padding on right | | +| paddingTop | ResponsiveValue \| undefined | No | | | | Padding on top | | +| paddingX | ResponsiveValue \| undefined | No | | | | Padding on left and right | | +| paddingY | ResponsiveValue \| undefined | No | | | | Padding on top and bottom | | +| pb | ResponsiveValue \| undefined | No | | | | Padding on bottom | | +| pl | ResponsiveValue \| undefined | No | | | | Padding on left | | +| pr | ResponsiveValue \| undefined | No | | | | Padding on right | | +| pt | ResponsiveValue \| undefined | No | | | | Padding on top | | +| px | ResponsiveValue \| undefined | No | | | | Padding on left and right | | +| py | ResponsiveValue \| undefined | No | | | | Padding on top and bottom | | +| data-element | string \| undefined | No | | | | Identifier used for testing purposes, applied to the root element of the component. | | +| data-role | string \| undefined | No | | | | Identifier used for testing purposes, applied to the root element of the component. | | +| variant | "default" \| "transparent" \| undefined | No | | Yes | Specify styling variant to render | | "default" | ## Examples ### Default diff --git a/skills/carbon-react/components/card-row.md b/skills/carbon-react/components/card-row.md index 83da801847..0a6b54974d 100644 --- a/skills/carbon-react/components/card-row.md +++ b/skills/carbon-react/components/card-row.md @@ -11,6 +11,8 @@ description: Carbon CardRow component props and usage examples. ## Source - Export: `./components/card` - Props interface: `CardRowProps` +- Deprecated: Yes +- Deprecation reason: `CardRow` has been deprecated. ## Props | Name | Type | Required | Literals | Description | Default | diff --git a/skills/carbon-react/components/card.md b/skills/carbon-react/components/card.md index 3bad034872..09e5db076b 100644 --- a/skills/carbon-react/components/card.md +++ b/skills/carbon-react/components/card.md @@ -13,41 +13,44 @@ description: Carbon Card component props and usage examples. - Props interface: `CardProps` ## Props -| Name | Type | Required | Literals | Description | Default | -| --- | --- | --- | --- | --- | --- | -| children | React.ReactNode | Yes | | Child nodes | | -| boxShadow | BoxShadowsType \| undefined | No | | Design token for custom Box Shadow. Note: please check that the box shadow design token you are using is compatible with the Card component. | | -| draggable | boolean \| undefined | No | | Flag to indicate if card is draggable | | -| footer | React.ReactNode | No | | The footer to render underneath the Card content | | -| height | string \| undefined | No | | Height of the component (any valid CSS value) | | -| hoverBoxShadow | BoxShadowsType \| undefined | No | | Design token for custom Box Shadow on hover. One of `onClick` or `href` props must be true. Note: please check that the box shadow design token you are using is compatible with the Card component. | | -| href | string \| undefined | No | | The path to navigate to. Renders an anchor element when passed and no draggable prop set | | -| m | ResponsiveValue \| undefined | No | | Margin on top, left, bottom and right | | -| margin | ResponsiveValue \| undefined | No | | Margin on top, left, bottom and right | | -| marginBottom | ResponsiveValue \| undefined | No | | Margin on bottom | | -| marginLeft | ResponsiveValue \| undefined | No | | Margin on left | | -| marginRight | ResponsiveValue \| undefined | No | | Margin on right | | -| marginTop | ResponsiveValue \| undefined | No | | Margin on top | | -| marginX | ResponsiveValue \| undefined | No | | Margin on left and right | | -| marginY | ResponsiveValue \| undefined | No | | Margin on top and bottom | | -| mb | ResponsiveValue \| undefined | No | | Margin on bottom | | -| ml | ResponsiveValue \| undefined | No | | Margin on left | | -| mr | ResponsiveValue \| undefined | No | | Margin on right | | -| mt | ResponsiveValue \| undefined | No | | Margin on top | | -| mx | ResponsiveValue \| undefined | No | | Margin on left and right | | -| my | ResponsiveValue \| undefined | No | | Margin on top and bottom | | -| onClick | ((event: React.MouseEvent \| React.MouseEvent \| React.KeyboardEvent \| React.KeyboardEvent) => void) \| undefined | No | | Action to be executed when card is clicked or enter pressed. Renders a button when passed and no draggable or href props set | | -| rel | string \| undefined | No | | String for rel property when card has an href prop set | | -| roundness | "large" \| "default" \| undefined | No | | Sets the level of roundness of the corners, "default" is 8px and "large" is 16px | "default" | -| spacing | "small" \| "medium" \| "large" \| undefined | No | | Size of card for applying padding | "medium" | -| target | string \| undefined | No | | Target property in which link should open ie: _blank, _self, _parent, _top | | -| width | string \| undefined | No | | Style value for width of card | "500px" | -| data-element | string \| undefined | No | | Identifier used for testing purposes, applied to the root element of the component. | | -| data-role | string \| undefined | No | | Identifier used for testing purposes, applied to the root element of the component. | | -| aria-label | string \| undefined | No | | Prop to specify an aria-label for the component | | +| Name | Type | Required | Literals | Deprecated | Deprecation reason | Description | Default | +| --- | --- | --- | --- | --- | --- | --- | --- | +| children | React.ReactNode | Yes | | | | Child nodes | | +| draggable | boolean \| undefined | No | | | | Flag to indicate if card is draggable | | +| footer | React.ReactNode | No | | | | The footer to render underneath the Card content | | +| header | React.ReactNode | No | | | | The header to render above the Card content | | +| height | string \| undefined | No | | | | Height of the component (any valid CSS value) | | +| href | string \| undefined | No | | | | The path to navigate to. Renders an anchor element when passed and no draggable prop set | | +| m | ResponsiveValue \| undefined | No | | | | Margin on top, left, bottom and right | | +| margin | ResponsiveValue \| undefined | No | | | | Margin on top, left, bottom and right | | +| marginBottom | ResponsiveValue \| undefined | No | | | | Margin on bottom | | +| marginLeft | ResponsiveValue \| undefined | No | | | | Margin on left | | +| marginRight | ResponsiveValue \| undefined | No | | | | Margin on right | | +| marginTop | ResponsiveValue \| undefined | No | | | | Margin on top | | +| marginX | ResponsiveValue \| undefined | No | | | | Margin on left and right | | +| marginY | ResponsiveValue \| undefined | No | | | | Margin on top and bottom | | +| mb | ResponsiveValue \| undefined | No | | | | Margin on bottom | | +| ml | ResponsiveValue \| undefined | No | | | | Margin on left | | +| mr | ResponsiveValue \| undefined | No | | | | Margin on right | | +| mt | ResponsiveValue \| undefined | No | | | | Margin on top | | +| mx | ResponsiveValue \| undefined | No | | | | Margin on left and right | | +| my | ResponsiveValue \| undefined | No | | | | Margin on top and bottom | | +| onClick | ((event: React.MouseEvent \| React.MouseEvent \| React.KeyboardEvent \| React.KeyboardEvent) => void) \| undefined | No | | | | Action to be executed when card is clicked or enter pressed. Renders a button when passed and no draggable or href props set | | +| rel | string \| undefined | No | | | | String for rel property when card has an href prop set | | +| rightChildren | React.ReactNode | No | | | | Slot rendered on the opposite side of the drag handle, only visible when `draggable` is true. Intended for accessibility controls (e.g. move-up / move-down buttons) for keyboard users. | | +| roundness | "large" \| "default" \| "moderate" \| "curved" \| undefined | No | | | | Sets the level of roundness of the corners. "moderate" is 16px and "curved" is 20px. **Note:** The values "default" and "large" are deprecated. Use "moderate" or "curved" instead. | "moderate" | +| spacing | "small" \| "medium" \| "large" \| "none" \| "extra-small" \| undefined | No | | | | Size padding applied to the card. | "medium" | +| target | string \| undefined | No | | | | Target property in which link should open ie: _blank, _self, _parent, _top | | +| variant | "standard" \| "outlined" \| undefined | No | | | | Visual style variant of the card | "standard" | +| width | string \| undefined | No | | | | Style value for width of card | "500px" | +| data-element | string \| undefined | No | | | | Identifier used for testing purposes, applied to the root element of the component. | | +| data-role | string \| undefined | No | | | | Identifier used for testing purposes, applied to the root element of the component. | | +| aria-label | string \| undefined | No | | | | Prop to specify an aria-label for the component | | +| boxShadow | BoxShadowsType \| undefined | No | | Yes | Design token for custom Box Shadow. Note: please check that the box shadow design token you are using is compatible with the Card component. | | | +| hoverBoxShadow | BoxShadowsType \| undefined | No | | Yes | Design token for custom Box Shadow on hover. One of `onClick` or `href` props must be true. Note: please check that the box shadow design token you are using is compatible with the Card component. | | | ## Examples -### default +### Default **Render** @@ -60,40 +63,97 @@ description: Carbon Card component props and usage examples. {...args} footer={ - - - Footer link - - + + + + Footer link + + + } > - - - - - Additional text - - - + + + Heading + Additional text + + - - - - - + + + + + Body text - - Even more text - - + + More text + + Even more text + + ); } ``` +### Draggable spacing sizes + +**Render** + +```tsx +() => { + return ( + <> + {(["none", "extra-small", "small", "medium", "large"] as const).map( + (spacing) => ( + + {}}> + Move up + + {}}> + Move down + + + } + spacing={spacing} + mb="16px" + footer={ + + + + + Footer link + + + + + } + > + + + Spacing: {spacing} + Additional text + + + + + + + ), + )} + + ); +} +``` + + ### CustomHeight **Render** @@ -107,68 +167,72 @@ description: Carbon Card component props and usage examples. onClick={() => {}} footer={ - - - Footer link - - + + + + Footer link + + + } > - - - - - Additional text - - - + + + Heading + Additional text + + - - - - - + + + + + Body text - - Even more text - - + + More text + + Even more text + + - - - Footer link - - + + + + Footer link + + + } > - - - - - Additional text - - - + + + Heading + Additional text + + - - - - - + + + + + Body text - - Even more text - - + + More text + + Even more text + + ); @@ -176,6 +240,295 @@ description: Carbon Card component props and usage examples. ``` +### InteractiveFocusedWithFooter + +**Render** + +```tsx +() => ( + {}} + footer={ + + + + + Footer link + + + + + } + > + + + Focused Card + Card with footer in focused state + + + + + + + ) +``` + + +### InteractiveFocusedWithoutFooter + +**Render** + +```tsx +() => ( + {}}> + + + Focused Card + Card without footer in focused state + + + + + + + ) +``` + + +### DeprecatedCardRowAndColumn + +**Render** + +```tsx +() => ( + + + + Footer link + + + + } + > + + + + + Additional text + + + + + + + + + + Body text + + + Even more text + + + + ) +``` + + +### DeprecatedCardRowWithPadding + +**Render** + +```tsx +() => ( + + + + View Stripe Dashboard + + + + } + > + + + Stripe - [account name] + + user.name@sage.com + + + + + + + + + + Stripe Balance + + £ 0.00 + LAST ENTRY: 5 DAYS AGO + + + + + + Stripe Balance + + £ 0.00 + LAST ENTRY: 15 DAYS AGO + + + + ) +``` + + +### DeprecatedCardRowSpacingSizes + +**Render** + +```tsx +() => ( + <> + {(["small", "medium", "large"] as const).map((spacing) => ( + + + + Footer link + + + + } + > + + + Spacing: {spacing} + Additional text + + + + + + + + + Body text + + More text + + + + ))} + + ) +``` + + +### DeprecatedCardRowWithInteractive + +**Render** + +```tsx +() => ( + {}} + footer={ + + + + Footer link + + + + } + > + + + + + Click me! + + + + + + + + ) +``` + + +### Playground + +**Args** + +```tsx +{ + spacing: "medium", + roundness: "moderate", + width: undefined, + height: undefined, + draggable: false, + } +``` + +**Render** + +```tsx +(args) => { + return ( + + {}}>Move up + {}}> + Move down + + + {}}>Delete + {}}>Edit + + ) : undefined + } + > + + + Heading + Additional text + + + + + + + + + Body text + + + More text + + Even more text + + + + ); + } +``` + + ### Default **Render** @@ -185,44 +538,295 @@ description: Carbon Card component props and usage examples. return ( {}} footer={ - - - Footer link - - + + + + Footer link + + + } > - - - - - Additional text - - - + + + Heading + Additional text + + - - - - - + + + + + Body text - - Even more text - - +
+ More text +
+ Even more text + +
); } ``` -### Small Spacing +### Without Footer + +**Render** + +```tsx +() => { + return ( + + + + Heading + Additional text + + + + + + + + + Body text + + + More text + + Even more text + + + + ); +} +``` + + +### Without Footer - Interactive + +**Render** + +```tsx +() => { + const [clickCounter, setClickCounter] = useState(0); + return ( + + + Card has been clicked {clickCounter} times + + setClickCounter((prevCounter) => prevCounter + 1)} + aria-label="Interactive card without footer" + > + + + + Heading + + + Additional text + + + + + + + + + + Body text + + + More text + + Even more text + + + + + ); +} +``` + + +### No Spacing + +**Render** + +```tsx +() => { + return ( + + + + + Footer link + + + + + } + > + + + Heading + Additional text + + + + + + + + + Body text + +
+ More text +
+ Even more text +
+
+
+ ); +} +``` + + +### Extra Small Spacing + +**Render** + +```tsx +() => { + return ( + + + + + Footer link + + + + + } + > + + + Heading + Additional text + + + + + + + + + Body text + +
+ More text +
+ Even more text +
+
+
+ ); +} +``` + + +### Small Spacing + +**Render** + +```tsx +() => { + return ( + + + + + Footer link + + + + + } + > + + + Heading + Additional text + + + + + + + + + Body text + +
+ More text +
+ Even more text +
+
+
+ ); +} +``` + + +### Medium Spacing **Render** @@ -230,37 +834,45 @@ description: Carbon Card component props and usage examples. () => { return ( - - - Footer link - - + + + + Footer link + + + } > - - - - - Additional text - - - + + + Heading + Additional text + + - - - - - + + + + + Body text - - Even more text - - +
+ More text +
+ Even more text + +
); } @@ -278,34 +890,42 @@ description: Carbon Card component props and usage examples. spacing="large" footer={ - - - Footer link - - + + + + Footer link + + + } > - - - - - Additional text - - - + + + Heading + Additional text + + - - - - - + + + + + Body text - - Even more text - - +
+ More text +
+ Even more text + + ); } @@ -323,34 +943,42 @@ description: Carbon Card component props and usage examples. width="500px" footer={ - - - Footer link - - + + + + Footer link + + + } > - - - - - Additional text - - - + + + Heading + Additional text + + - - - - - + + + + + Body text - - Even more text - - +
+ More text +
+ Even more text + + ); } @@ -368,41 +996,49 @@ description: Carbon Card component props and usage examples. height="500px" footer={ - - - Footer link - - + + + + Footer link + + + } > - - - - - Additional text - - - + + + Heading + Additional text + + - - - - - + + + + + Body text - - Even more text - - +
+ More text +
+ Even more text + + ); } ``` -### With Extra Roundness +### With Curved Roundness **Render** @@ -410,37 +1046,45 @@ description: Carbon Card component props and usage examples. () => { return ( - - - Footer link - - + + + + Footer link + + + } > - - - - - Additional text - - - + + + Heading + Additional text + + - - - - - + + + + + Body text - - Even more text - - +
+ More text +
+ Even more text + +
); } @@ -464,19 +1108,29 @@ description: Carbon Card component props and usage examples. aria-label="Card with button element" footer={ - - Footer link - + + + + Footer link + + + } > - - - + + + This Card is a button as it has an onClick prop - - + + - - Footer link - + + + + Footer link + + + } > - - - + + + This Card is a link as it has an href prop - - + + ); @@ -505,7 +1169,7 @@ description: Carbon Card component props and usage examples. ``` -### With Custom Box Shadow +### Draggable **Render** @@ -513,38 +1177,54 @@ description: Carbon Card component props and usage examples. () => { return ( + {}}>Move up + {}}>Move down + + {}}>Delete + {}}>Edit + + } footer={ - - - Footer link - - + + + + Footer link + + + } > - - - - - Additional text - - - + + + Heading + Additional text + + - - - - - + + + + + Body text - - Even more text - - + + More text + + Even more text + + ); } @@ -561,43 +1241,53 @@ description: Carbon Card component props and usage examples. - - - Footer link - - + + + + Footer link + + + } > - - - - - Additional text - - - + + + Heading + Additional text + + - - - - - + + + + + Body text - - Even more text - - - - - +
+ More text +
+ Even more text + + + + + Body text - - Even more text -
-
+
+ More text +
+ Even more text + +
); } @@ -617,144 +1307,144 @@ description: Carbon Card component props and usage examples. - - + } > - - - + + + Here is some text - - + + - - + } > - - - + + + Here is some text - - + + - - + } > - - - + + + Here is some text - - + + - - + } > - - - + + + Here is some text - - + + - - + } > - - - + + + Here is some text - - + + - - + } > - - - + + + Here is some text - - + + ); @@ -780,22 +1470,41 @@ description: Carbon Card component props and usage examples. justifyContent="space-around" > - {}}> - - - {}}> - - - {}}> - - - {}}> - - + - + @@ -803,13 +1512,13 @@ description: Carbon Card component props and usage examples. } > - - - + + + Here is some text - - + + - Edit Button - - - @@ -844,7 +1544,7 @@ description: Carbon Card component props and usage examples. p={0} iconPosition="after" iconType="edit" - buttonType="tertiary" + variantType="tertiary" ml={2} > Edit Button @@ -853,13 +1553,13 @@ description: Carbon Card component props and usage examples. } > - - - + + + Here is some text - - + + - Edit Button - - - @@ -894,7 +1585,7 @@ description: Carbon Card component props and usage examples. p={0} iconPosition="after" iconType="edit" - buttonType="tertiary" + variantType="tertiary" ml={2} > Edit Button @@ -903,13 +1594,13 @@ description: Carbon Card component props and usage examples. } > - - - + + + Here is some text - - + + } > - - - + + + Here is some text - - + + ); } ``` - -### With String as Child - -**Render** - -```tsx -() => { - return String passed as child; -} -``` - diff --git a/skills/carbon-react/index.md b/skills/carbon-react/index.md index 6c2b189a6d..89d61abc36 100644 --- a/skills/carbon-react/index.md +++ b/skills/carbon-react/index.md @@ -27,9 +27,9 @@ - [ButtonToggleGroup](components/button-toggle-group.md) - [CarbonProvider](components/carbon-provider.md) - [Card](components/card.md) -- [CardColumn](components/card-column.md) +- [CardColumn](components/card-column.md) (deprecated) - [CardFooter](components/card-footer.md) -- [CardRow](components/card-row.md) +- [CardRow](components/card-row.md) (deprecated) - [Checkbox](components/checkbox.md) - [CheckboxGroup](components/checkbox-group.md) - [Confirm](components/confirm.md) (deprecated) diff --git a/src/components/card/__internal__/card.context.ts b/src/components/card/__internal__/card.context.ts index 6a0c04e963..2a718d8283 100644 --- a/src/components/card/__internal__/card.context.ts +++ b/src/components/card/__internal__/card.context.ts @@ -2,8 +2,10 @@ import createStrictContext from "../../../__internal__/utils/createStrictContext import { CardSpacing } from "../card.config"; export interface CardContextProps { - /** Sets the level of roundness of the corners, "default" is 8px and "large" is 16px */ - roundness: "default" | "large"; + /** Sets the level of roundness of the corners. "moderate" is 16px and "curved" is 20px. + * "default" (alias for "moderate") and "large" (alias for "curved") are deprecated. Use "moderate" or "curved" instead. + */ + roundness: "moderate" | "curved" | "default" | "large"; /** Size of card for applying padding */ spacing: CardSpacing; } @@ -14,7 +16,7 @@ const [CardProvider, useCardContext] = createStrictContext({ "Carbon Card: Context not found. Have you wrapped your Carbon subcomponents properly? See stack trace for more details.", defaultValue: { spacing: "medium", - roundness: "default", + roundness: "moderate", }, }); diff --git a/src/components/card/card-column/card-column.component.tsx b/src/components/card/card-column/card-column.component.tsx index b80443cfe2..31308bc510 100644 --- a/src/components/card/card-column/card-column.component.tsx +++ b/src/components/card/card-column/card-column.component.tsx @@ -11,6 +11,9 @@ export interface CardColumnProps children: React.ReactNode; } +/** + * @deprecated `CardColumn` has been deprecated. + */ const CardColumn = ({ align = "center", children, diff --git a/src/components/card/card-footer/card-footer.component.tsx b/src/components/card/card-footer/card-footer.component.tsx index a8d1969e8f..c1254bad93 100644 --- a/src/components/card/card-footer/card-footer.component.tsx +++ b/src/components/card/card-footer/card-footer.component.tsx @@ -16,7 +16,7 @@ export interface CardFooterProps TagProps { /** Child nodes */ children: React.ReactNode; - /** Specify styling variant to render */ + /** @deprecated Specify styling variant to render */ variant?: "default" | "transparent"; } diff --git a/src/components/card/card-footer/card-footer.style.ts b/src/components/card/card-footer/card-footer.style.ts index 10205c4d85..d34d0cd89c 100644 --- a/src/components/card/card-footer/card-footer.style.ts +++ b/src/components/card/card-footer/card-footer.style.ts @@ -5,15 +5,20 @@ import { CardContextProps } from "../__internal__/card.context"; import { CardFooterProps } from "./card-footer.component"; const marginSizes = { - small: "0 -24px", - medium: "0 -32px", - large: "0 -48px", + none: "var(--global-size-none)", + "extra-small": + "var(--global-size-none) calc(-1 * var(--global-space-comp-s))", + small: "var(--global-size-none) calc(-1 * var(--global-space-comp-l))", + medium: "var(--global-size-none) calc(-1 * var(--global-space-comp-xl))", + large: "var(--global-size-none) calc(-1 * var(--global-space-comp-2-xl))", }; const paddingSizes = { - small: "16px 24px", - medium: "18px 32px", - large: "20px 48px", + none: "var(--global-size-none)", + "extra-small": "var(--global-size-none)", + small: "var(--global-space-comp-l) var(--global-space-comp-xl)", + medium: "18px var(--global-space-comp-2-xl)", + large: "var(--global-size-2-xs) var(--global-size-l)", }; export type StyledCardFooterProps = SpaceProps & @@ -26,25 +31,23 @@ const StyledCardFooter = styled.div` ${({ spacing, variant, roundness }) => css` background-color: ${variant === "transparent" ? "transparent" - : "var(--colorsUtilityMajor025)"}; - border-top: var(--colorsUtilityMajor100); - border-top-width: 1px; - border-top-style: solid; + : "var(--container-standard-bg-default)"}; + border-top: 1px solid var(--container-action-border-default); margin: ${marginSizes[spacing]}; display: flex; - ${roundness === "default" && + ${(roundness === "moderate" || roundness === "default") && css` - border-bottom-left-radius: var(--borderRadius100); - border-bottom-right-radius: var(--borderRadius100); + border-bottom-left-radius: var(--global-radius-container-l); + border-bottom-right-radius: var(--global-radius-container-l); `} - ${roundness === "large" && + ${(roundness === "curved" || roundness === "large") && css` - border-bottom-left-radius: var(--borderRadius200); - border-bottom-right-radius: var(--borderRadius200); + border-bottom-left-radius: var(--global-radius-container-xl); + border-bottom-right-radius: var(--global-radius-container-xl); `} ${StyledCardColumn} { - margin: 0; + margin: var(--global-size-none); padding: ${paddingSizes[spacing]}; } `} diff --git a/src/components/card/card-footer/card-footer.test.tsx b/src/components/card/card-footer/card-footer.test.tsx index 6ba0d17230..ed29a4ba90 100644 --- a/src/components/card/card-footer/card-footer.test.tsx +++ b/src/components/card/card-footer/card-footer.test.tsx @@ -37,9 +37,18 @@ test("when variant prop is `transparent`, render with transparent background", ( }); }); -test.each(["default", "large"] as const)( - "renders with the expected border radius styling when roundness is %s", - (roundness) => { +test.each([ + { + roundness: "moderate" as const, + expectedRadius: "var(--global-radius-container-l)", + }, + { + roundness: "curved" as const, + expectedRadius: "var(--global-radius-container-xl)", + }, +])( + "renders with the expected border radius styling when roundness is $roundness", + ({ roundness, expectedRadius }) => { render( foo @@ -49,11 +58,11 @@ test.each(["default", "large"] as const)( const cardFooterElement = screen.getByTestId("card-footer"); expect(cardFooterElement).toHaveStyleRule( "border-bottom-left-radius", - `var(--borderRadius${roundness === "default" ? "1" : "2"}00)`, + expectedRadius, ); expect(cardFooterElement).toHaveStyleRule( "border-bottom-right-radius", - `var(--borderRadius${roundness === "default" ? "1" : "2"}00)`, + expectedRadius, ); }, ); diff --git a/src/components/card/card-row/card-row.component.tsx b/src/components/card/card-row/card-row.component.tsx index 453c8e40d4..8bbfc65d93 100644 --- a/src/components/card/card-row/card-row.component.tsx +++ b/src/components/card/card-row/card-row.component.tsx @@ -13,6 +13,7 @@ export interface CardRowProps extends PaddingProps, TagProps { children: React.ReactNode; } +/** @deprecated `CardRow` has been deprecated. */ const CardRow = ({ children, ...rest }: CardRowProps) => { const { spacing } = useCardContext(); const id = useRef(guid()); diff --git a/src/components/card/card-row/card-row.style.ts b/src/components/card/card-row/card-row.style.ts index 0845559172..732728be5d 100644 --- a/src/components/card/card-row/card-row.style.ts +++ b/src/components/card/card-row/card-row.style.ts @@ -10,6 +10,8 @@ export interface StyledCardRowProps extends CardRowProps { } const paddingSizes = { + none: "var(--spacing000)", + "extra-small": "var(--spacing100)", small: "var(--spacing200)", medium: "var(--spacing300)", large: "var(--spacing400)", diff --git a/src/components/card/card-test.stories.tsx b/src/components/card/card-test.stories.tsx index adecffcc5b..a627aaf03b 100644 --- a/src/components/card/card-test.stories.tsx +++ b/src/components/card/card-test.stories.tsx @@ -1,11 +1,14 @@ import React from "react"; -import { Meta } from "@storybook/react-vite"; -import { Card, CardRow, CardFooter, CardColumn, CardProps } from "."; +import { Meta, StoryObj } from "@storybook/react-vite"; +import { within } from "storybook/test"; +import { Card, CardFooter, CardRow, CardColumn, CardProps } from "."; import Link from "../link"; -import Heading from "../heading"; import Typography from "../typography"; import Icon from "../icon"; +import Box from "../box"; +import { ActionPopover, ActionPopoverItem } from "../action-popover"; +import Heading from "../heading"; export default { title: "Card/Test", @@ -36,38 +39,89 @@ export const DefaultStory = ( {...args} footer={ - - - Footer link - - + + + + Footer link + + + } > - - - - - Additional text - - - + + + Heading + Additional text + + - - - - - + + + + + Body text - - Even more text - - + + More text + + Even more text + + ); }; -DefaultStory.storyName = "default"; +DefaultStory.storyName = "Default"; + +export const DraggableSpacingSizes = () => { + return ( + <> + {(["none", "extra-small", "small", "medium", "large"] as const).map( + (spacing) => ( + + {}}> + Move up + + {}}> + Move down + + + } + spacing={spacing} + mb="16px" + footer={ + + + + + Footer link + + + + + } + > + + + Spacing: {spacing} + Additional text + + + + + + + ), + )} + + ); +}; +DraggableSpacingSizes.storyName = "Draggable spacing sizes"; export const CustomHeight = () => { return ( @@ -77,69 +131,325 @@ export const CustomHeight = () => { onClick={() => {}} footer={ - - - Footer link - - + + + + Footer link + + + } > - - - - - Additional text - - - + + + Heading + Additional text + + - - - - - + + + + + Body text - - Even more text - - + + More text + + Even more text + + - - - Footer link - - + + + + Footer link + + + } > - - - - - Additional text - - - + + + Heading + Additional text + + - - - - - + + + + + Body text - - Even more text - - + + More text + + Even more text + + ); }; + +export const InteractiveFocusedWithFooter: StoryObj = { + parameters: { + chromatic: { + disableSnapshot: false, + }, + themeProvider: { chromatic: { theme: "sage" } }, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const card = canvas.getByRole("button"); + card.focus(); + }, + render: () => ( + {}} + footer={ + + + + + Footer link + + + + + } + > + + + Focused Card + Card with footer in focused state + + + + + + + ), +}; + +export const InteractiveFocusedWithoutFooter: StoryObj = { + parameters: { + chromatic: { + disableSnapshot: false, + }, + themeProvider: { chromatic: { theme: "sage" } }, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const card = canvas.getByRole("button"); + card.focus(); + }, + render: () => ( + {}}> + + + Focused Card + Card without footer in focused state + + + + + + + ), +}; + +export const DeprecatedCardRowAndColumn: StoryObj = { + parameters: { + docs: { + description: { + story: + "Test to ensure backward compatibility with deprecated `CardRow` and `CardColumn` components.", + }, + }, + }, + render: () => ( + + + + Footer link + + + + } + > + + + + + Additional text + + + + + + + + + + Body text + + + Even more text + + + + ), +}; + +export const DeprecatedCardRowWithPadding: StoryObj = { + parameters: { + docs: { + description: { + story: + "Test to ensure backward compatibility with deprecated `CardRow` component with custom padding props.", + }, + }, + }, + render: () => ( + + + + View Stripe Dashboard + + + + } + > + + + Stripe - [account name] + + user.name@sage.com + + + + + + + + + + Stripe Balance + + £ 0.00 + LAST ENTRY: 5 DAYS AGO + + + + + + Stripe Balance + + £ 0.00 + LAST ENTRY: 15 DAYS AGO + + + + ), +}; + +export const DeprecatedCardRowSpacingSizes: StoryObj = { + parameters: { + docs: { + description: { + story: + "Test to ensure `CardRow` receives spacing from parent Card context correctly.", + }, + }, + }, + render: () => ( + <> + {(["small", "medium", "large"] as const).map((spacing) => ( + + + + Footer link + + + + } + > + + + Spacing: {spacing} + Additional text + + + + + + + + + Body text + + More text + + + + ))} + + ), +}; + +export const DeprecatedCardRowWithInteractive: StoryObj = { + parameters: { + docs: { + description: { + story: + "Test to ensure deprecated `CardRow` and `CardColumn` work with interactive cards.", + }, + }, + }, + render: () => ( + {}} + footer={ + + + + Footer link + + + + } + > + + + + + Click me! + + + + + + + + ), +}; diff --git a/src/components/card/card.component.tsx b/src/components/card/card.component.tsx index e998170150..e2d8591509 100644 --- a/src/components/card/card.component.tsx +++ b/src/components/card/card.component.tsx @@ -3,7 +3,12 @@ import { MarginProps } from "styled-system"; import * as DesignTokens from "@sage/design-tokens/js/base/common"; import { filterStyledSystemMarginProps } from "../../style/utils"; import { CardProvider, CardContextProps } from "./__internal__/card.context"; -import { StyledCard, StyledCardContent } from "./card.style"; +import { + StyledCard, + StyledCardContent, + StyledDragRow, + StyledDragHandle, +} from "./card.style"; import Icon from "../icon"; import tagComponent, { TagProps, @@ -32,16 +37,23 @@ export interface CardProps extends MarginProps, TagProps { draggable?: boolean; /** Height of the component (any valid CSS value) */ height?: string; - /** Design token for custom Box Shadow. Note: please check that the box shadow design token you are using is compatible with the Card component. */ + /** @deprecated Design token for custom Box Shadow. Note: please check that the box shadow design token you are using is compatible with the Card component. */ boxShadow?: BoxShadowsType; - /** Design token for custom Box Shadow on hover. One of `onClick` or `href` props must be true. Note: please check that the box shadow design token you are using is compatible with the Card component. */ + /** @deprecated Design token for custom Box Shadow on hover. One of `onClick` or `href` props must be true. Note: please check that the box shadow design token you are using is compatible with the Card component. */ hoverBoxShadow?: BoxShadowsType; - /** Size of card for applying padding */ + /** Size padding applied to the card. */ spacing?: CardContextProps["spacing"]; - /** Sets the level of roundness of the corners, "default" is 8px and "large" is 16px */ + /** Sets the level of roundness of the corners. "moderate" is 16px and "curved" is 20px. + * + * **Note:** The values "default" and "large" are deprecated. Use "moderate" or "curved" instead. + */ roundness?: CardContextProps["roundness"]; /** The path to navigate to. Renders an anchor element when passed and no draggable prop set */ href?: string; + /** Visual style variant of the card */ + variant?: "standard" | "outlined"; + /** The header to render above the Card content */ + header?: React.ReactNode; /** The footer to render underneath the Card content */ footer?: React.ReactNode; /** Target property in which link should open ie: _blank, _self, _parent, _top */ @@ -50,6 +62,10 @@ export interface CardProps extends MarginProps, TagProps { rel?: string; /** Prop to specify an aria-label for the component */ "aria-label"?: string; + /** Slot rendered on the opposite side of the drag handle, only visible when `draggable` is true. + * Intended for accessibility controls (e.g. move-up / move-down buttons) for keyboard users. + */ + rightChildren?: React.ReactNode; } const Card = ({ @@ -64,11 +80,14 @@ const Card = ({ spacing = "medium", boxShadow, hoverBoxShadow, - roundness = "default", + roundness = "moderate", + header, footer, rel, target, "aria-label": ariaLabel, + variant = "standard", + rightChildren, ...rest }: CardProps) => { const [contentRef, setContentRef] = useState(null); @@ -89,32 +108,41 @@ const Card = ({ return ( - {draggable && } + {header} + {draggable && ( + + + + + {rightChildren} + + )} diff --git a/src/components/card/card.config.ts b/src/components/card/card.config.ts index cc36c9a2a4..f1b76198d3 100644 --- a/src/components/card/card.config.ts +++ b/src/components/card/card.config.ts @@ -1,4 +1,10 @@ -export const CARD_SPACING = ["small", "medium", "large"] as const; +export const CARD_SPACING = [ + "none", + "extra-small", + "small", + "medium", + "large", +] as const; export const CARD_ALIGNS = ["left", "center", "right"] as const; export type CardAlign = (typeof CARD_ALIGNS)[number]; export type CardSpacing = (typeof CARD_SPACING)[number]; diff --git a/src/components/card/card.mdx b/src/components/card/card.mdx index 767cff2f54..6227ef46cb 100644 --- a/src/components/card/card.mdx +++ b/src/components/card/card.mdx @@ -1,4 +1,4 @@ -import { Meta, ArgTypes, Canvas } from "@storybook/addon-docs/blocks"; +import { Meta, ArgTypes, Canvas, Controls } from "@storybook/addon-docs/blocks"; import * as CardStories from "./card.stories"; import * as CardColumnStories from "./card-column/card-column.stories"; @@ -23,26 +23,49 @@ A container for interactive content and controls related to a single subject. ## Contents - [Quick Start](#quick-start) +- [Playground](#playground) - [Examples](#examples) - [Props](#props) ## Quick start ```javascript -import { - Card, - CardRow, - CardFooter, - CardColumn, -} from "carbon-react/lib/components/card"; +import { Card, CardFooter } from "carbon-react/lib/components/card"; ``` +## Playground + +Use this interactive example to explore Card props with Storybook controls. + + + + + ## Examples ### Default - medium spacing +### Without footer + + + +### Without footer - interactive + + + +### No spacing + + + +### Extra small spacing + + + ### Small spacing @@ -51,9 +74,11 @@ import { -### With large roundness on corners +### With curved roundness on corners - +By default the `Card` component has a `moderate` roundness on its corners. You can increase this by passing the `roundness` prop with a value of `curved`. + + ### With width provided @@ -68,9 +93,11 @@ Setting the `draggable` prop will negate both `onClick` and `href` prop values a -### With custom box shadow +### Draggable + +Setting the `draggable` prop renders a drag handle icon and changes the cursor to `move`. It also negates any `onClick` or `href` props, so the card is no longer interactive. - + ### Different `` padding @@ -84,10 +111,6 @@ Setting the `draggable` prop will negate both `onClick` and `href` prop values a -## With string as child - - - ## With custom height @@ -98,11 +121,11 @@ Setting the `draggable` prop will negate both `onClick` and `href` prop values a -### CardRow +### CardRow (Deprecated) -### CardColumn +### CardColumn (Deprecated) diff --git a/src/components/card/card.pw.tsx b/src/components/card/card.pw.tsx index e583c75713..5985e4bd06 100644 --- a/src/components/card/card.pw.tsx +++ b/src/components/card/card.pw.tsx @@ -4,15 +4,7 @@ import "../../../playwright/components"; import { checkAccessibility } from "../../../playwright/support/helper"; import { CardComponent, - SmallSpacing, - LargeSpacing, - WithWidthProvided, - WithCustomBoxShadow, - WithCustomHeight, - DifferentCardFooterPadding, - DifferentCardRowPadding, Interactive, - MoreExamplesOfCardFooter, WithStringAsChild, } from "../../../src/components/card/components.test-pw"; @@ -26,42 +18,6 @@ test.describe("Accessibility tests for Card component", () => { await checkAccessibility(page); }); - test("should pass accessibility tests for small spacing example", async ({ - mount, - page, - }) => { - await mount(); - - await checkAccessibility(page); - }); - - test("should pass accessibility tests for large spacing example", async ({ - mount, - page, - }) => { - await mount(); - - await checkAccessibility(page); - }); - - test("should pass accessibility tests with width prop provided", async ({ - mount, - page, - }) => { - await mount(); - - await checkAccessibility(page); - }); - - test("should pass accessibility tests with custom height provided", async ({ - mount, - page, - }) => { - await mount(); - - await checkAccessibility(page); - }); - test("should pass accessibility tests for interactive example", async ({ mount, page, @@ -71,42 +27,6 @@ test.describe("Accessibility tests for Card component", () => { await checkAccessibility(page); }); - test("should pass accessibility tests with custom box shadow applied", async ({ - mount, - page, - }) => { - await mount(); - - await checkAccessibility(page); - }); - - test("should pass accessibility tests with different card row padding applied", async ({ - mount, - page, - }) => { - await mount(); - - await checkAccessibility(page); - }); - - test("should pass accessibility tests with different card footer padding applied", async ({ - mount, - page, - }) => { - await mount(); - - await checkAccessibility(page); - }); - - test("should pass accessibility tests for moreExamplesOfFooter example", async ({ - mount, - page, - }) => { - await mount(); - - await checkAccessibility(page); - }); - test("should pass accessibility tests with a string as a child", async ({ mount, page, diff --git a/src/components/card/card.stories.tsx b/src/components/card/card.stories.tsx index 9f0f8fbef4..6c5fb6c927 100644 --- a/src/components/card/card.stories.tsx +++ b/src/components/card/card.stories.tsx @@ -1,15 +1,18 @@ import React, { useState } from "react"; import { Meta, StoryObj } from "@storybook/react-vite"; -import { Card, CardRow, CardFooter, CardColumn, CardProps } from "."; +import { Card, CardFooter, CardProps } from "."; import Typography from "../typography"; -import Heading from "../heading"; import Icon from "../icon"; import Link from "../link"; import Box from "../box"; -import Button from "../button"; +import Button from "../button/__next__"; import Divider from "../divider"; -import IconButton from "../icon-button"; +import { + ActionPopover, + ActionPopoverDivider, + ActionPopoverItem, +} from "../action-popover"; import generateStyledSystemProps from "../../../.storybook/utils/styled-system-props"; @@ -29,121 +32,418 @@ const meta: Meta = { export default meta; type Story = StoryObj; +export const Playground: Story = { + render: (args) => { + return ( + + {}}>Move up + {}}> + Move down + + + {}}>Delete + {}}>Edit + + ) : undefined + } + > + + + Heading + Additional text + + + + + + + + + Body text + + + More text + + Even more text + + + + ); + }, + args: { + spacing: "medium", + roundness: "moderate", + width: undefined, + height: undefined, + draggable: false, + }, + argTypes: { + roundness: { + options: ["moderate", "curved"], + control: { type: "radio" }, + }, + }, + parameters: { controls: { disable: false } }, +}; +Playground.storyName = "Playground"; + export const DefaultStory: Story = { render: (args: CardProps) => { return ( {}} footer={ - - - Footer link - - + + + + Footer link + + + } > - - - - - Additional text - - - + + + Heading + Additional text + + - - - - - + + + + + Body text - - Even more text - - +
+ More text +
+ Even more text + +
); }, }; DefaultStory.storyName = "Default"; +export const WithoutFooter: Story = () => { + return ( + + + + Heading + Additional text + + + + + + + + + Body text + + + More text + + Even more text + + + + ); +}; +WithoutFooter.storyName = "Without Footer"; + +export const WithoutFooterInteractive: Story = () => { + const [clickCounter, setClickCounter] = useState(0); + return ( + + + Card has been clicked {clickCounter} times + + setClickCounter((prevCounter) => prevCounter + 1)} + aria-label="Interactive card without footer" + > + + + + Heading + + + Additional text + + + + + + + + + + Body text + + + More text + + Even more text + + + + + ); +}; +WithoutFooterInteractive.parameters = { chromatic: { disableSnapshot: true } }; +WithoutFooterInteractive.storyName = "Without Footer - Interactive"; + +export const NoneSpacing: Story = () => { + return ( + + + + + Footer link + + + + + } + > + + + Heading + Additional text + + + + + + + + + Body text + +
+ More text +
+ Even more text +
+
+
+ ); +}; +NoneSpacing.storyName = "No Spacing"; + +export const ExtraSmallSpacing: Story = () => { + return ( + + + + + Footer link + + + + + } + > + + + Heading + Additional text + + + + + + + + + Body text + +
+ More text +
+ Even more text +
+
+
+ ); +}; +ExtraSmallSpacing.storyName = "Extra Small Spacing"; + export const SmallSpacing: Story = () => { return ( - - - Footer link - - + + + + Footer link + + + } > - - - - - Additional text - - - + + + Heading + Additional text + + - - - - - + + + + + Body text - - Even more text - - +
+ More text +
+ Even more text + +
); }; SmallSpacing.storyName = "Small Spacing"; +export const MediumSpacing: Story = () => { + return ( + + + + + Footer link + + + + + } + > + + + Heading + Additional text + + + + + + + + + Body text + +
+ More text +
+ Even more text +
+
+
+ ); +}; +MediumSpacing.storyName = "Medium Spacing"; + export const LargeSpacing: Story = () => { return ( - - - Footer link - - + + + + Footer link + + + } > - - - - - Additional text - - - + + + Heading + Additional text + + - - - - - + + + + + Body text - - Even more text - - +
+ More text +
+ Even more text + +
); }; @@ -155,34 +455,42 @@ export const WithWidthProvided: Story = () => { width="500px" footer={ - - - Footer link - - + + + + Footer link + + + } > - - - - - Additional text - - - + + + Heading + Additional text + + - - - - - + + + + + Body text - - Even more text - - +
+ More text +
+ Even more text + + ); }; @@ -194,77 +502,93 @@ export const WithCustomHeight: Story = () => { height="500px" footer={ - - - Footer link - - + + + + Footer link + + + } > - - - - - Additional text - - - + + + Heading + Additional text + + - - - - - + + + + + Body text - - Even more text - - +
+ More text +
+ Even more text + + ); }; WithCustomHeight.storyName = "With Custom Height"; -export const WithExtraRoundness: Story = () => { +export const WithCurvedRoundness: Story = () => { return ( - - - Footer link - - + + + + Footer link + + + } > - - - - - Additional text - - - + + + Heading + Additional text + + - - - - - + + + + + Body text - - Even more text - - +
+ More text +
+ Even more text + +
); }; -WithExtraRoundness.storyName = "With Extra Roundness"; +WithCurvedRoundness.storyName = "With Curved Roundness"; export const Interactive: Story = () => { const [clickCounter, setClickCounter] = useState(0); @@ -278,19 +602,29 @@ export const Interactive: Story = () => { aria-label="Card with button element" footer={ - - Footer link - + + + + Footer link + + + } > - - - + + + This Card is a button as it has an onClick prop - - + + { aria-label="Card with anchor element" footer={ - - Footer link - + + + + Footer link + + + } > - - - + + + This Card is a link as it has an href prop - - + + ); @@ -319,88 +663,114 @@ export const Interactive: Story = () => { Interactive.parameters = { chromatic: { disableSnapshot: true } }; Interactive.storyName = "Interactive"; -export const WithCustomBoxShadow: Story = () => { +export const Draggable: Story = () => { return ( + {}}>Move up + {}}>Move down + + {}}>Delete + {}}>Edit + + } footer={ - - - Footer link - - + + + + Footer link + + + } > - - - - - Additional text - - - + + + Heading + Additional text + + - - - - - + + + + + Body text - - Even more text - - + + More text + + Even more text + + ); }; -WithCustomBoxShadow.storyName = "With Custom Box Shadow"; +Draggable.storyName = "Draggable"; export const DifferentCardRowPadding: Story = () => { return ( - - - Footer link - - + + + + Footer link + + + } > - - - - - Additional text - - - + + + Heading + Additional text + + - - - - - + + + + + Body text - - Even more text - - - - - +
+ More text +
+ Even more text + + + + + Body text - - Even more text -
-
+
+ More text +
+ Even more text + +
); }; @@ -415,144 +785,144 @@ export const DifferentCardFooterPadding: Story = () => { - - + } > - - - + + + Here is some text - - + + - - + } > - - - + + + Here is some text - - + + - - + } > - - - + + + Here is some text - - + + - - + } > - - - + + + Here is some text - - + + - - + } > - - - + + + Here is some text - - + + - - + } > - - - + + + Here is some text - - + + ); @@ -575,22 +945,41 @@ export const MoreExamplesOfCardFooter: Story = () => { justifyContent="space-around" > - {}}> - - - {}}> - - - {}}> - - - {}}> - - + - + @@ -598,13 +987,13 @@ export const MoreExamplesOfCardFooter: Story = () => { } > - - - + + + Here is some text - - + + { p={0} iconPosition="after" iconType="edit" - buttonType="tertiary" + variantType="tertiary" + mr={2} > Edit Button @@ -629,17 +1019,7 @@ export const MoreExamplesOfCardFooter: Story = () => { p={0} iconPosition="after" iconType="edit" - buttonType="tertiary" - ml={2} - > - Edit Button - - - @@ -679,17 +1060,7 @@ export const MoreExamplesOfCardFooter: Story = () => { p={0} iconPosition="after" iconType="edit" - buttonType="tertiary" - ml={2} - > - Edit Button - - - }> + Content + , + ); + + expect(screen.getByRole("button", { name: "Move up" })).toBeInTheDocument(); +}); + +test("drag icon still renders alongside rightChildren when both are provided", () => { + render( + Move up}> + Content + , + ); + + expect(screen.getByTestId("icon")).toBeInTheDocument(); + expect(screen.getByRole("button", { name: "Move up" })).toBeInTheDocument(); +}); + +test("does not render rightChildren when draggable is false", () => { + render( + Move up}>Content, + ); + + expect( + screen.queryByRole("button", { name: "Move up" }), + ).not.toBeInTheDocument(); +}); + +test("drag icon is not rendered when draggable is false", () => { + render( + Move up}>Content, + ); + + expect(screen.queryByTestId("icon")).not.toBeInTheDocument(); }); test("should call onClick callback when card is clicked", async () => { @@ -129,7 +172,7 @@ test("should call onClick callback when card is clicked", async () => { render( - Content + Content , ); @@ -139,3 +182,111 @@ test("should call onClick callback when card is clicked", async () => { expect(handleClick).toHaveBeenCalledTimes(1); }); + +test("renders with standard card type box shadow when variant is standard", () => { + render( + + Content + , + ); + + const card = screen.getByTestId("card"); + + expect(card).toHaveStyleRule("box-shadow", "var(--global-depth-lvl1)"); +}); + +test("renders with outlined card type box shadow", () => { + render( + + Content + , + ); + + const card = screen.getByTestId("card"); + + expect(card).toHaveStyleRule("box-shadow", "var(--global-depth-none)"); +}); + +test("renders with no hover box shadow when variant is outlined and card is interactive", () => { + render( + {}} data-role="card"> + Content + , + ); + + const card = screen.getByTestId("card"); + + expect(card).toHaveStyleRule("box-shadow", "none", { modifier: ":hover" }); +}); + +test("applies flex layout to the content container when spacing is extra-small", () => { + render( + {}} data-role="card"> + Content + , + ); + + const contentEl = screen.getByRole("button"); + + expect(contentEl).toHaveStyleRule("align-items", "stretch"); + expect(contentEl).toHaveStyleRule("align-self", "stretch"); +}); + +test("StyledCard applies standard box shadow by default when variant is not provided", () => { + render( + + Content + , + ); + + expect(screen.getByTestId("card")).toHaveStyleRule( + "box-shadow", + "var(--global-depth-lvl1)", + ); +}); + +test.each(["moderate", "curved"])( + "renders content container with the expected border radius when roundness is %s", + (roundness) => { + render( + {}} data-role="card"> + Content + , + ); + + const contentEl = screen.getByRole("button"); + const radius = + roundness === "moderate" + ? "var(--global-radius-container-l)" + : "var(--global-radius-container-xl)"; + + expect(contentEl).toHaveStyleRule("border-top-left-radius", radius); + expect(contentEl).toHaveStyleRule("border-top-right-radius", radius); + expect(contentEl).toHaveStyleRule("border-bottom-left-radius", radius); + expect(contentEl).toHaveStyleRule("border-bottom-right-radius", radius); + }, +); + +test("drag handle has grab cursor on hover", () => { + render(); + + const dragHandle = screen.getByTestId("drag-handle"); + + expect(dragHandle).toHaveStyleRule("cursor", "grab"); +}); + +test("drag handle has grabbing cursor when active", () => { + render(); + + const dragHandle = screen.getByTestId("drag-handle"); + + expect(dragHandle).toHaveStyleRule("cursor", "grabbing", { + modifier: ":active", + }); +}); diff --git a/src/components/card/components.test-pw.tsx b/src/components/card/components.test-pw.tsx index ab8efe4ba8..6ae3d457ef 100644 --- a/src/components/card/components.test-pw.tsx +++ b/src/components/card/components.test-pw.tsx @@ -1,12 +1,10 @@ import React, { useState } from "react"; -import { Card, CardRow, CardFooter, CardColumn, CardProps } from "."; +import { Card, CardFooter, CardProps } from "."; import Link from "../link"; -import Heading from "../heading"; import Typography from "../typography"; import Icon from "../icon"; import Box from "../box"; -import Button from "../button"; -import IconButton from "../icon-button"; +import Button from "../button/__next__"; import Divider from "../divider"; export const CardComponent = (props: Partial) => { @@ -15,99 +13,113 @@ export const CardComponent = (props: Partial) => { {...props} footer={ - - - View Stripe Dashboard - - + + + + View Stripe Dashboard + + + } > - - - - + + + Stripe - [account name] + user.name@sage.com - - + + - - - - - + + + + + Stripe Balance - + £ 0.00 LAST ENTRY: 15 DAYS AGO - - + + ); }; export const SmallSpacing = () => ( - - - - - + + + + + View Stripe Dashboard + + + + + } + > + + + Stripe - [account name] + user.name@sage.com - - + + - - - - - + + + + + Stripe Balance - + £ 0.00 LAST ENTRY: 15 DAYS AGO - - - - - - View Stripe Dashboard - - - + + ); export const LargeSpacing = () => ( - - - - - + + + + + View Stripe Dashboard + + + + + } + > + + + Stripe - [account name] + user.name@sage.com - - + + - - - - - + + + + + Stripe Balance - + £ 0.00 LAST ENTRY: 15 DAYS AGO - - - - - - View Stripe Dashboard - - - + + ); @@ -116,229 +128,222 @@ export const WithWidthProvided = () => ( width="500px" footer={ - - - Footer link - - + + + + Footer link + + + } > - - - - - Additional text - - - + + + Heading + Additional text + + - - - - - + + + + + Body text - - Even more text - - + More text + Even more text + + ); export const WithCustomHeight = (props: Partial) => ( - - - - - - user.name@sage.com - - - - - - - - - - Stripe Balance - - - LAST ENTRY: 15 DAYS AGO - - - - - - View Stripe Dashboard - - - - -); - -export const WithCustomBoxShadow = () => ( - - - - - + + + + + View Stripe Dashboard + + + + + } + > + + + Stripe - [account name] + user.name@sage.com - - + + - - - - - + + + + + Stripe Balance - + £ 0.00 LAST ENTRY: 15 DAYS AGO - - - - - - View Stripe Dashboard - - - + + ); export const DifferentCardFooterPadding = () => { return ( - - - - + + + + + + + + + + + } + > + + + Here is some text - - - - - - - - - - - + - - - - + + + + + + + + + + + } + > + + + Here is some text - - - - - - - - - - - + - - - - + + + + + + + + + + + } + > + + + Here is some text - - - - - - - - - - - + - - - - + + + + + + + + + + + } + > + + + Here is some text - - - - - - - - - - - + - - - - + + + + + + + + + + + } + > + + + Here is some text - - - - - - - - - - - + - - - - + + + + + + + + + + + } + > + + + Here is some text - - - - - - - - - - - + ); @@ -346,43 +351,48 @@ export const DifferentCardFooterPadding = () => { export const DifferentCardRowPadding = () => { return ( - - - - - + + + + + View Stripe Dashboard + + + + + } + > + + + Stripe - [account name] + user.name@sage.com - - + + - - - - - + + + + + Stripe Balance - + £ 0.00 LAST ENTRY: 5 DAYS AGO - - - - - + + + + + Stripe Balance - + £ 0.00 LAST ENTRY: 15 DAYS AGO - - - - - - View Stripe Dashboard - - - + + ); }; @@ -399,19 +409,21 @@ export const Interactive = () => { aria-label="Card with button element" footer={ - - Footer link - + + + Footer link + + } > - - - + + + This Card is a button as it has an onClick prop - - + + { aria-label="Card with anchor element" footer={ - - Footer link - + + + Footer link + + } > - - - + + + This Card is a link as it has an href prop - - + + ); @@ -441,151 +455,163 @@ export const Interactive = () => { export const MoreExamplesOfCardFooter = () => { return ( - - - - + + + + + + + + + } + > + + + Here is some text - - - - - - {}}> - - - {}}> - - - {}}> - - - {}}> - - - - - - - - + - - - - + + + + + + + + } + > + + + Here is some text - - - - - - - - - - + - - - - + + + + + + + + } + > + + + Here is some text - - - - - - - - - - + - - - - + + + + View Stripe Dashboard + + + + } + > + + + Here is some text - - - - - - View Stripe Dashboard - - + );