Repository: marigold-ui/marigold
Pull request #1994 (2022-04-14 09:39:57)

refa: card styles structur

changeset-bot[bot]: ###  ⚠️  No Changeset found

Latest commit: 08dbeed6dde5cdbadecf2ee2a2265ab077161f27

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. **If these changes should result in a version bump, you need to add a changeset.**

<details><summary>This PR includes no changesets</summary>

  When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

</details>

[Click here to learn what changesets are, and how to add one](https://github.com/changesets/changesets/blob/master/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add a changeset to this PR](https://github.com/marigold-ui/marigold/new/refa-card-styles-structur?filename=.changeset/lucky-bats-type.md&value=---%0A%22%40marigold%2Fdocs%22%3A%20patch%0A%22%40marigold%2Fcomponents%22%3A%20patch%0A%22%40marigold%2Fsystem%22%3A%20patch%0A%22%40marigold%2Ftheme-b2b%22%3A%20patch%0A%22%40marigold%2Ftheme-unicorn%22%3A%20patch%0A---%0A%0ARefa%3A%20card-styles-structur%0A)



Patch:
diff --git a/.changeset/lucky-bats-type.md b/.changeset/lucky-bats-type.md
new file mode 100644
index 000000000..04a2775c1
--- /dev/null
+++ b/.changeset/lucky-bats-type.md
@@ -0,0 +1,9 @@
+---
+"@marigold/docs": patch
+"@marigold/components": patch
+"@marigold/system": patch
+"@marigold/theme-b2b": patch
+"@marigold/theme-unicorn": patch
+---
+
+Refa: card-styles-structur
diff --git a/docs/content/components/card.mdx b/docs/content/components/card.mdx
index f0a31c3f6..00ca0c746 100644
--- a/docs/content/components/card.mdx
+++ b/docs/content/components/card.mdx
@@ -9,7 +9,7 @@ import { DoAndDont } from '../../src/components/DoAndDont';
 The card component can be used for organizing information inside the UI, as well it provides a better readability and helps the user to find information in an easier way.
 
 <MarigoldTheme>
-  <Card title="Title">
+  <Card>
     <Text>
       Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse
       dignissim dapibus elit, vel.
@@ -26,18 +26,17 @@ import { Card } from '@marigold/components';
 
 ## Props
 
-| Property  | Type                           | Default     |
-| :-------- | :----------------------------- | :---------- |
-| `variant` | `string`                       | `__default` |
-| `width`   | `ResponsiveStyleValue<string>` |             |
-| `title`   | `string`                       |             |
+| Property  | Type     | Default |
+| :-------- | :------- | :------ |
+| `variant` | `string` |         |
+| `size`    | `string` |         |
 
 ## Examples
 
-### Standard title and text
+### Standard
 
 ```tsx expandCode
-<Card title="Title">
+<Card>
   <Text>
     Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse
     dignissim dapibus elit, vel.
@@ -48,18 +47,10 @@ import { Card } from '@marigold/components';
 </Card>
 ```
 
-### Custom width
-
-```tsx
-<Card title="Title" width="320px">
-  <Text>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</Text>
-</Card>
-```
-
 ### Card with form fields
 
 ```tsx
-<Card title="Name">
+<Card>
   <TextField label="Firstname" />
   <TextField label="Lastname" />
 </Card>
@@ -68,7 +59,7 @@ import { Card } from '@marigold/components';
 ### Card with select
 
 ```tsx
-<Card title="Choose Color">
+<Card>
   <Select label="Favorite Color" placeholder="Choose a color" width={450}>
     <Item>Red</Item>
     <Item>Orange</Item>
@@ -92,7 +83,7 @@ import { Card } from '@marigold/components';
   >
     <DoAndDont
       preview={
-        <Card title="Title">
+        <Card>
           <Text>
             Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse
             dignissim dapibus elit, vel.
@@ -109,7 +100,7 @@ import { Card } from '@marigold/components';
     <DoAndDont
       dont
       preview={
-        <Card title="Title">
+        <Card>
           <Text>
             Lorem Ipsum is simply dummy text of the printing and typesetting
             industry. Lorem Ipsum has been the industry's standard dummy text
diff --git a/packages/components/src/Card/Card.stories.tsx b/packages/components/src/Card/Card.stories.tsx
index 919227d94..87494a13b 100644
--- a/packages/components/src/Card/Card.stories.tsx
+++ b/packages/components/src/Card/Card.stories.tsx
@@ -1,7 +1,8 @@
 import React from 'react';
 import type { Meta, ComponentStory } from '@storybook/react';
+import { Link } from '../Link';
 import { Card } from './Card';
-import { Text } from '../Text';
+import { Container } from '../Container';
 
 export default {
   title: 'Components/Card',
@@ -10,32 +11,36 @@ export default {
       control: {
         type: 'text',
       },
-      table: {
-        defaultValue: {
-          summary: '__default',
-        },
-      },
-    },
-    title: {
-      control: {
-        type: 'text',
-      },
-      description: 'card title',
+      description: 'The variant of the card',
     },
-    width: {
+    size: {
       control: {
         type: 'text',
       },
-      description: 'max width of the card',
+      description: 'The size of the card',
     },
   },
 } as Meta;
 
 export const Basic: ComponentStory<typeof Card> = args => (
-  <Card title="Card" {...args}>
-    <Text>
-      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse
-      dignissim dapibus elit.
-    </Text>
-  </Card>
+  <Container contentType="content" size="medium">
+    <Card {...args}>
+      <h2>Professor Severus Snape</h2>
+      <section>
+        <p>
+          <strong>Professor Severus Snape</strong> (9 January, 1960[1] - 2 May,
+          1998)[2] was an English half-blood[3] wizard serving as Potions Master
+          (1981-1996), Head of Slytherin House (1981-1997), Defence Against the
+          Dark Arts professor (1996-1997), and Headmaster (1997-1998) of the
+          Hogwarts School of Witchcraft and Wizardry as well as a member of the
+          Order of the Phoenix and a Death Eater. His double life played an
+          extremely important role in both of the Wizarding Wars against
+          Voldemort.
+        </p>
+        <Link href="https://harrypotter.fandom.com/wiki/Severus_Snape">
+          Source
+        </Link>
+      </section>
+    </Card>
+  </Container>
 );
diff --git a/packages/components/src/Card/Card.tsx b/packages/components/src/Card/Card.tsx
old mode 100755
new mode 100644
index ee0e34801..b25fdabcc
--- a/packages/components/src/Card/Card.tsx
+++ b/packages/components/src/Card/Card.tsx
@@ -1,47 +1,28 @@
-import React from 'react';
-import { ResponsiveStyleValue } from '@marigold/system';
+import React, { ReactNode } from 'react';
+import {
+  Box,
+  useComponentStyles,
+  ThemeComponentProps,
+  ThemeExtension,
+} from '@marigold/system';
 import { ComponentProps } from '@marigold/types';
 
-import { Box } from '../Box';
-
 // Theme Extension
 // ---------------
-export interface CardThemeExtension<Value> {
-  card?: {
-    [key: string]: Value;
-  };
-}
+export interface CardThemeExtension extends ThemeExtension<'Card'> {}
 
 // Props
 // ---------------
-export interface CardProps extends ComponentProps<'div'> {
-  title?: string;
-  width?: ResponsiveStyleValue<string>;
-  variant?: string;
+export interface CardProps extends ThemeComponentProps, ComponentProps<'div'> {
+  children?: ReactNode;
 }
 
 // Component
 // ---------------
-export const Card: React.FC<CardProps> = ({
-  variant = '',
-  title,
-  width,
-  className,
-  children,
-  ...props
-}) => {
+export const Card = ({ children, variant, size, ...props }: CardProps) => {
+  const styles = useComponentStyles('Card', { variant, size });
   return (
-    <Box
-      {...props}
-      variant={`card.${variant}`}
-      maxWidth={width}
-      className={className}
-    >
-      {title && (
-        <Box as="h2" variant="text.h2" pb="small">
-          {title}
-        </Box>
-      )}
+    <Box {...props} css={styles}>
       {children}
     </Box>
   );
diff --git a/packages/components/src/Field/FieldBase.tsx b/packages/components/src/Field/FieldBase.tsx
index 3342b0f07..93ee6b176 100644
--- a/packages/components/src/Field/FieldBase.tsx
+++ b/packages/components/src/Field/FieldBase.tsx
@@ -8,7 +8,7 @@ import { HelpText } from './HelpText';
 // Props
 // ---------------
 export interface FieldBaseProps {
-  children?: React.ReactNode;
+  children?: ReactNode;
   variant?: string;
   size?: string;
   disabled?: boolean;
diff --git a/packages/components/src/theme.ts b/packages/components/src/theme.ts
index 37afa481a..5fb64f64b 100644
--- a/packages/components/src/theme.ts
+++ b/packages/components/src/theme.ts
@@ -7,6 +7,7 @@ import type { RootThemeExtension } from './Provider';
 // Components
 // ---------------
 import type { BadgeThemeExtension } from './Badge';
+import type { CardThemeExtension } from './Card';
 import type { HelpTextThemeExtension } from './Field/HelpText';
 import type { ImageThemeExtension } from './Image';
 import type { InputThemeExtension } from './Input';
@@ -16,7 +17,6 @@ import type { TextAreaThemeExtension } from './TextArea';
 
 // Old Styling Solution
 import { type ButtonThemeExtension } from './Button';
-import { type CardThemeExtension } from './Card';
 import { type CheckboxThemeExtension } from './Checkbox';
 import { type DividerThemeExtension } from './Divider';
 import { type LabelThemeExtension as LegacyLabelThemeExtension } from './Label';
@@ -33,6 +33,7 @@ import { type ValidationMessageThemeExtension } from './ValidationMessage';
 
 interface ComponentStyles
   extends BadgeThemeExtension,
+    CardThemeExtension,
     HelpTextThemeExtension,
     ImageThemeExtension,
     LabelThemeExtension,
@@ -45,7 +46,6 @@ export interface Theme
   extends Scales,
     RootThemeExtension<CSSObject>,
     ButtonThemeExtension<CSSObject>,
-    CardThemeExtension<CSSObject>,
     CheckboxThemeExtension<CSSObject>,
     DividerThemeExtension<CSSObject>,
     LegacyLabelThemeExtension<CSSObject>,
diff --git a/packages/system/src/hooks/useComponentStyles.ts b/packages/system/src/hooks/useComponentStyles.ts
index 90252bc19..ba0d8bf96 100644
--- a/packages/system/src/hooks/useComponentStyles.ts
+++ b/packages/system/src/hooks/useComponentStyles.ts
@@ -37,6 +37,10 @@ export interface ComponentStylesProps {
   size?: string;
 }
 
+export type ComponentStyleParts<Parts extends string[]> = {
+  [P in Parts[number]]: CSSObject;
+};
+
 export function useComponentStyles(
   componentName: string,
   props?: ComponentStylesProps,
diff --git a/packages/system/src/types/theme.ts b/packages/system/src/types/theme.ts
index af361aec0..bc2c06ac2 100644
--- a/packages/system/src/types/theme.ts
+++ b/packages/system/src/types/theme.ts
@@ -2,6 +2,14 @@ import * as CSS from 'csstype';
 import { NestedScaleDict } from '@theme-ui/css';
 import { CSSObject } from './system';
 
+/**
+ * Props that every component should accepts to change the styling
+ */
+export type ThemeComponentProps = {
+  variant?: string;
+  size?: string;
+};
+
 /**
  * Structure for component styles in a theme.
  */
diff --git a/themes/theme-b2b/src/component.ts b/themes/theme-b2b/src/component.ts
index 9cb1f7fc1..dd8b31659 100644
--- a/themes/theme-b2b/src/component.ts
+++ b/themes/theme-b2b/src/component.ts
@@ -360,15 +360,6 @@ export const component = {
       ...button.select,
     },
   },
-  card: {
-    __default: {
-      maxWidth: 500,
-      bg: 'gray00',
-      p: 'small',
-      boxShadow: '0px 4px 4px rgba(165, 165, 165, 0.25)',
-      borderRadius: 'large',
-    },
-  },
   checkbox: {
     __default: {
       fill: 'gray00',
diff --git a/themes/theme-b2b/src/components/Card.style.ts b/themes/theme-b2b/src/components/Card.style.ts
new file mode 100644
index 000000000..f6dc879a7
--- /dev/null
+++ b/themes/theme-b2b/src/components/Card.style.ts
@@ -0,0 +1,10 @@
+import type { Theme } from '@marigold/components';
+
+export const Card: Theme['components']['Card'] = {
+  base: {
+    bg: 'gray00',
+    p: 'small',
+    boxShadow: '0px 4px 4px rgba(165, 165, 165, 0.25)',
+    borderRadius: 'large',
+  },
+};
diff --git a/themes/theme-b2b/src/components/index.ts b/themes/theme-b2b/src/components/index.ts
index 6410b0690..6871e8469 100644
--- a/themes/theme-b2b/src/components/index.ts
+++ b/themes/theme-b2b/src/components/index.ts
@@ -1,4 +1,5 @@
 export * from './Badge.style';
+export * from './Card.style';
 export * from './HelpText.style';
 export * from './Image.style';
 export * from './Input.style';
diff --git a/themes/theme-unicorn/src/component.ts b/themes/theme-unicorn/src/component.ts
index 0ba5466e4..ab33dfeea 100644
--- a/themes/theme-unicorn/src/component.ts
+++ b/themes/theme-unicorn/src/component.ts
@@ -356,15 +356,6 @@ export const component = {
       ...button.select,
     },
   },
-  card: {
-    __default: {
-      maxWidth: '500px',
-      bg: 'gray00',
-      p: 'small',
-      boxShadow: '8px 8px 4px rgba(201, 177, 255, 0.5)',
-      borderRadius: '8px',
-    },
-  },
   checkbox: {
     __default: {
       fill: 'gray00',
diff --git a/themes/theme-unicorn/src/components/Card.style.ts b/themes/theme-unicorn/src/components/Card.style.ts
new file mode 100644
index 000000000..f6dc879a7
--- /dev/null
+++ b/themes/theme-unicorn/src/components/Card.style.ts
@@ -0,0 +1,10 @@
+import type { Theme } from '@marigold/components';
+
+export const Card: Theme['components']['Card'] = {
+  base: {
+    bg: 'gray00',
+    p: 'small',
+    boxShadow: '0px 4px 4px rgba(165, 165, 165, 0.25)',
+    borderRadius: 'large',
+  },
+};
diff --git a/themes/theme-unicorn/src/components/index.ts b/themes/theme-unicorn/src/components/index.ts
index 6410b0690..6871e8469 100644
--- a/themes/theme-unicorn/src/components/index.ts
+++ b/themes/theme-unicorn/src/components/index.ts
@@ -1,4 +1,5 @@
 export * from './Badge.style';
+export * from './Card.style';
 export * from './HelpText.style';
 export * from './Image.style';
 export * from './Input.style';


Test patch:
diff --git a/packages/components/src/Card/Card.test.tsx b/packages/components/src/Card/Card.test.tsx
index 41ab450e1..486c73038 100644
--- a/packages/components/src/Card/Card.test.tsx
+++ b/packages/components/src/Card/Card.test.tsx
@@ -7,65 +7,62 @@ import { Card } from './Card';
 const theme = {
   space: {
     none: 0,
-    small: 4,
-    medium: 8,
+    'small-1': 4,
+    'medium-1': 16,
   },
-  card: {
-    __default: {
-      p: 'medium',
-    },
-    custom: {
-      p: 'small',
+  colors: {
+    grey: '#dee2e6',
+    yellow: '#fff9db',
+  },
+  components: {
+    Card: {
+      base: {
+        p: 'small-1',
+        border: '1px solid',
+        borderColor: 'grey',
+      },
+      variant: {
+        yellow: {
+          bg: 'yellow',
+        },
+      },
+      size: {
+        medium: {
+          p: 'medium-1',
+        },
+      },
     },
   },
 };
 
-test('supports default variant', () => {
+test('renders as a "div" element', () => {
   render(
     <ThemeProvider theme={theme}>
-      <Card>card</Card>
+      <Card data-testid="card" />
     </ThemeProvider>
   );
-  const card = screen.getByText(/card/);
-  expect(card).toHaveStyle(`padding: 8px`);
-});
 
-test('supports other variant than default', () => {
-  render(
-    <ThemeProvider theme={theme}>
-      <Card variant="custom">card</Card>
-    </ThemeProvider>
-  );
-  const card = screen.getByText(/card/);
-  expect(card).toHaveStyle(`padding: 4px`);
-});
-
-test('accepts title prop', () => {
-  render(
-    <ThemeProvider theme={theme}>
-      <Card title="title">content</Card>
-    </ThemeProvider>
-  );
-  const title = screen.getByText(/title/);
-  expect(title).toBeDefined();
+  const card = screen.getByTestId('card');
+  expect(card instanceof HTMLDivElement).toBeTruthy();
 });
 
-test('accepts width prop', () => {
+test('uses base styling form "Card" in theme', () => {
   render(
     <ThemeProvider theme={theme}>
-      <Card width="320px">content</Card>
+      <Card data-testid="card" />
     </ThemeProvider>
   );
-  const card = screen.getByText(/content/);
-  expect(card).toHaveStyle(`maxWidth: 320px`);
+  const card = screen.getByTestId('card');
+  expect(card).toHaveStyle(`padding: ${theme.space['small-1']}px`);
 });
 
-test('renders correct HTMl element', () => {
+test('Card accepts a variant and size', () => {
   render(
     <ThemeProvider theme={theme}>
-      <Card>card</Card>
+      <Card data-testid="card" variant="yellow" size="medium" />
     </ThemeProvider>
   );
-  const card = screen.getByText(/card/);
-  expect(card instanceof HTMLDivElement).toBeTruthy();
+  const card = screen.getByTestId('card');
+  expect(card).toHaveStyle(`background: ${theme.colors.yellow}`);
+  expect(card).toHaveStyle(`padding: ${theme.space['medium-1']}px`);
 });