Repository: marigold-ui/marigold
Pull request #1998 (2022-04-14 11:48:11)

feat(comp): headline component

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

Latest commit: a0935644403d6268ad7c1a329ad710e1da0ead14

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/create-headline-component?filename=.changeset/slow-rules-drop.md&value=---%0A%22%40marigold%2Fdocs%22%3A%20patch%0A%22%40marigold%2Fcomponents%22%3A%20patch%0A%22%40marigold%2Ftheme-b2b%22%3A%20patch%0A%22%40marigold%2Ftheme-core%22%3A%20patch%0A%22%40marigold%2Ftheme-unicorn%22%3A%20patch%0A---%0A%0Afeat(comp)%3A%20headline%20component%0A)



Patch:
diff --git a/.changeset/slow-rules-drop.md b/.changeset/slow-rules-drop.md
new file mode 100644
index 000000000..cdb2c601d
--- /dev/null
+++ b/.changeset/slow-rules-drop.md
@@ -0,0 +1,9 @@
+---
+"@marigold/docs": patch
+"@marigold/components": patch
+"@marigold/theme-b2b": patch
+"@marigold/theme-core": patch
+"@marigold/theme-unicorn": patch
+---
+
+feat(comp): headline component
diff --git a/docs/content/components/headline.mdx b/docs/content/components/headline.mdx
new file mode 100644
index 000000000..9db0d5724
--- /dev/null
+++ b/docs/content/components/headline.mdx
@@ -0,0 +1,37 @@
+---
+title: Headline
+caption: <short purpose of component> (optional)
+---
+
+# Headline
+
+## Description
+
+```tsx onlyCode
+import { Headline } from '@marigold/components';
+```
+
+## Props
+
+<!-- list all props with values and description -->
+
+| Name      | Type     | Default     | Description                                               |
+| :-------- | :------- | :---------- | :-------------------------------------------------------- |
+| `variant` | `string` |             | Use a different _variant_ from theme                      |
+| `size`    | `string` | `'level-1'` | Use a different _size_ from theme                         |
+| `level`   | `string` | `1`         | set a different _level_ from theme, values are from 1 - 6 |
+
+## Examples
+
+<!-- show some cases and scenarios how the component can be used -->
+
+```tsx
+<Content>
+  <Headline level="3">Lorem ipsum</Headline>
+  <Text>
+    Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
+    eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
+    voluptua. At vero eos et accusam et justo duo dolores et ea rebum.
+  </Text>
+</Content>
+```
diff --git a/packages/components/src/Content/Content.stories.tsx b/packages/components/src/Content/Content.stories.tsx
index 2d9841261..499bd6049 100644
--- a/packages/components/src/Content/Content.stories.tsx
+++ b/packages/components/src/Content/Content.stories.tsx
@@ -4,19 +4,19 @@ import { Content } from './Content';
 import { Container } from '../Container';
 
 export default {
-  title: 'Components/Card',
+  title: 'Components/COntent',
   argTypes: {
     variant: {
       control: {
         type: 'text',
       },
-      description: 'The variant of the card',
+      description: 'The variant of the content',
     },
     size: {
       control: {
         type: 'text',
       },
-      description: 'The size of the card',
+      description: 'The size of the content',
     },
   },
 } as Meta;
diff --git a/packages/components/src/Headline/Headline.stories.tsx b/packages/components/src/Headline/Headline.stories.tsx
new file mode 100644
index 000000000..1959453dc
--- /dev/null
+++ b/packages/components/src/Headline/Headline.stories.tsx
@@ -0,0 +1,32 @@
+import React from 'react';
+import type { Meta, ComponentStory } from '@storybook/react';
+import { Headline } from './Headline';
+
+export default {
+  title: 'Components/Headline',
+  argTypes: {
+    level: {
+      control: {
+        type: 'select',
+      },
+      options: ['1', '2', '3', '4', '5', '6'],
+      description: 'Level of the headline',
+    },
+    variant: {
+      control: {
+        type: 'text',
+      },
+      description: 'The variant of the headline',
+    },
+    size: {
+      control: {
+        type: 'text',
+      },
+      description: 'The size of the headline',
+    },
+  },
+} as Meta;
+
+export const Basic: ComponentStory<typeof Headline> = args => (
+  <Headline {...args}>This is a Headline!!</Headline>
+);
diff --git a/packages/components/src/Headline/Headline.tsx b/packages/components/src/Headline/Headline.tsx
new file mode 100644
index 000000000..42e388b94
--- /dev/null
+++ b/packages/components/src/Headline/Headline.tsx
@@ -0,0 +1,41 @@
+import React, { ReactNode } from 'react';
+import {
+  Box,
+  useComponentStyles,
+  ThemeComponentProps,
+  ThemeExtension,
+} from '@marigold/system';
+import { ComponentProps } from '@marigold/types';
+
+// Theme Extension
+// ---------------
+export interface HeadlineThemeExtension extends ThemeExtension<'Headline'> {}
+
+// Props
+// ---------------
+export interface HeadlineProps
+  extends ThemeComponentProps,
+    ComponentProps<'h1'> {
+  children?: ReactNode;
+  level?: '1' | '2' | '3' | '4' | '5' | '6';
+}
+
+// Component
+// ---------------
+export const Headline = ({
+  children,
+  variant,
+  size,
+  level = '1',
+  ...props
+}: HeadlineProps) => {
+  const styles = useComponentStyles('Headline', {
+    variant,
+    size: size ?? `level-${level}`,
+  });
+  return (
+    <Box as={`h${level}`} {...props} css={styles}>
+      {children}
+    </Box>
+  );
+};
diff --git a/packages/components/src/Headline/index.ts b/packages/components/src/Headline/index.ts
new file mode 100644
index 000000000..90e4df0e9
--- /dev/null
+++ b/packages/components/src/Headline/index.ts
@@ -0,0 +1,1 @@
+export * from './Headline';
diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts
index 224a88880..04a9e6f74 100644
--- a/packages/components/src/index.ts
+++ b/packages/components/src/index.ts
@@ -14,6 +14,7 @@ export * from './Content';
 export * from './Dialog';
 export * from './Divider';
 export * from './VisuallyHidden';
+export * from './Headline';
 export * from './Image';
 export * from './Inline';
 export * from './Label';
diff --git a/packages/components/src/theme.ts b/packages/components/src/theme.ts
index 5fb64f64b..77c498b04 100644
--- a/packages/components/src/theme.ts
+++ b/packages/components/src/theme.ts
@@ -8,6 +8,8 @@ import type { RootThemeExtension } from './Provider';
 // ---------------
 import type { BadgeThemeExtension } from './Badge';
 import type { CardThemeExtension } from './Card';
+import type { ContentThemeExtension } from './Content';
+import type { HeadlineThemeExtension } from './Headline';
 import type { HelpTextThemeExtension } from './Field/HelpText';
 import type { ImageThemeExtension } from './Image';
 import type { InputThemeExtension } from './Input';
@@ -34,6 +36,8 @@ import { type ValidationMessageThemeExtension } from './ValidationMessage';
 interface ComponentStyles
   extends BadgeThemeExtension,
     CardThemeExtension,
+    ContentThemeExtension,
+    HeadlineThemeExtension,
     HelpTextThemeExtension,
     ImageThemeExtension,
     LabelThemeExtension,
diff --git a/themes/theme-b2b/src/components/Headline.style.ts b/themes/theme-b2b/src/components/Headline.style.ts
new file mode 100644
index 000000000..5023db265
--- /dev/null
+++ b/themes/theme-b2b/src/components/Headline.style.ts
@@ -0,0 +1,29 @@
+import type { Theme } from '@marigold/components';
+
+export const Headline: Theme['components']['Headline'] = {
+  base: {
+    m: 'none',
+    fontWeight: 'heading',
+  },
+  size: {
+    'level-1': {
+      fontSize: 'xlarge',
+    },
+    'level-2': {
+      fontSize: 'large',
+    },
+    'level-3': {
+      fontSize: 'medium',
+    },
+    'level-4': {
+      fontSize: 'small',
+    },
+    'level-5': {
+      fontSize: 'xsmall',
+    },
+    'level-6': {
+      fontSize: 'xsmall',
+      textTransform: 'uppercase',
+    },
+  },
+};
diff --git a/themes/theme-b2b/src/components/index.ts b/themes/theme-b2b/src/components/index.ts
index 6871e8469..473dfb293 100644
--- a/themes/theme-b2b/src/components/index.ts
+++ b/themes/theme-b2b/src/components/index.ts
@@ -1,5 +1,6 @@
 export * from './Badge.style';
 export * from './Card.style';
+export * from './Headline.style';
 export * from './HelpText.style';
 export * from './Image.style';
 export * from './Input.style';
diff --git a/themes/theme-b2b/src/typography.ts b/themes/theme-b2b/src/typography.ts
index d11788ed2..8162af31a 100644
--- a/themes/theme-b2b/src/typography.ts
+++ b/themes/theme-b2b/src/typography.ts
@@ -50,41 +50,5 @@ export const typography = {
       ...text.root,
       ...text.span,
     },
-    headline1: {
-      ...text.root,
-      fontWeight: 'heading',
-      fontSize: 'xlarge',
-      m: 'none',
-    },
-    headline2: {
-      ...text.root,
-      fontWeight: 'heading',
-      fontSize: 'large',
-      m: 'none',
-    },
-    headline3: {
-      ...text.root,
-      fontWeight: 'heading',
-      fontSize: 'medium',
-      m: 'none',
-    },
-    headline4: {
-      ...text.root,
-      fontWeight: 'heading',
-      fontSize: 'small',
-      m: 'none',
-    },
-    headline5: {
-      ...text.root,
-      fontWeight: 'heading',
-      fontSize: 'xsmall',
-      m: 'none',
-    },
-    headline6: {
-      ...text.root,
-      fontSize: 'xsmall',
-      textTransform: 'uppercase',
-      m: 'none',
-    },
   },
 } as const;
diff --git a/themes/theme-core/src/components/Headline.style.ts b/themes/theme-core/src/components/Headline.style.ts
new file mode 100644
index 000000000..5023db265
--- /dev/null
+++ b/themes/theme-core/src/components/Headline.style.ts
@@ -0,0 +1,29 @@
+import type { Theme } from '@marigold/components';
+
+export const Headline: Theme['components']['Headline'] = {
+  base: {
+    m: 'none',
+    fontWeight: 'heading',
+  },
+  size: {
+    'level-1': {
+      fontSize: 'xlarge',
+    },
+    'level-2': {
+      fontSize: 'large',
+    },
+    'level-3': {
+      fontSize: 'medium',
+    },
+    'level-4': {
+      fontSize: 'small',
+    },
+    'level-5': {
+      fontSize: 'xsmall',
+    },
+    'level-6': {
+      fontSize: 'xsmall',
+      textTransform: 'uppercase',
+    },
+  },
+};
diff --git a/themes/theme-core/src/components/index.ts b/themes/theme-core/src/components/index.ts
index 6410b0690..74f1647b3 100644
--- a/themes/theme-core/src/components/index.ts
+++ b/themes/theme-core/src/components/index.ts
@@ -1,4 +1,5 @@
 export * from './Badge.style';
+export * from './Headline.style';
 export * from './HelpText.style';
 export * from './Image.style';
 export * from './Input.style';
diff --git a/themes/theme-core/src/typography.ts b/themes/theme-core/src/typography.ts
index 65fba11d7..2959e4247 100644
--- a/themes/theme-core/src/typography.ts
+++ b/themes/theme-core/src/typography.ts
@@ -50,44 +50,5 @@ export const typography = {
       ...text.root,
       ...text.span,
     },
-    headline1: {
-      ...text.root,
-      fontWeight: 'heading',
-      fontSize: 'xlarge',
-      lineHeight: '36px',
-      m: 'none',
-    },
-    headline2: {
-      ...text.root,
-      fontWeight: 'heading',
-      fontSize: 'large',
-      lineHeight: '27px',
-      m: 'none',
-    },
-    headline3: {
-      ...text.root,
-      fontWeight: 'heading',
-      fontSize: 'medium',
-      lineHeight: '21px',
-      m: 'none',
-    },
-    headline4: {
-      ...text.root,
-      fontWeight: 'heading',
-      fontSize: 'small',
-      m: 'none',
-    },
-    headline5: {
-      ...text.root,
-      fontWeight: 'heading',
-      fontSize: 'xsmall',
-      m: 'none',
-    },
-    headline6: {
-      ...text.root,
-      fontSize: 'xsmall',
-      textTransform: 'uppercase',
-      m: 'none',
-    },
   },
 } as const;
diff --git a/themes/theme-unicorn/src/components/Headline.style.ts b/themes/theme-unicorn/src/components/Headline.style.ts
new file mode 100644
index 000000000..4ebb40db6
--- /dev/null
+++ b/themes/theme-unicorn/src/components/Headline.style.ts
@@ -0,0 +1,43 @@
+import type { Theme } from '@marigold/components';
+
+export const Headline: Theme['components']['Headline'] = {
+  base: {
+    m: 'none',
+    fontWeight: 'heading',
+  },
+  size: {
+    'level-1': {
+      fontSize: 'xlarge',
+      marginBottom: 'medium',
+      '&:first-child': {
+        marginTop: 'none',
+      },
+    },
+    'level-2': {
+      fontSize: 'large',
+      marginTop: 'medium',
+      marginBottom: 'xsmall',
+      '&:first-child': {
+        marginTop: 'none',
+      },
+    },
+    'level-3': {
+      fontSize: 'medium',
+      marginTop: 'small',
+      marginBottom: 'small',
+      '&:first-child': {
+        marginTop: 'none',
+      },
+    },
+    'level-4': {
+      fontSize: 'small',
+    },
+    'level-5': {
+      fontSize: 'xsmall',
+    },
+    'level-6': {
+      fontSize: 'xsmall',
+      textTransform: 'uppercase',
+    },
+  },
+};
diff --git a/themes/theme-unicorn/src/components/index.ts b/themes/theme-unicorn/src/components/index.ts
index 6871e8469..473dfb293 100644
--- a/themes/theme-unicorn/src/components/index.ts
+++ b/themes/theme-unicorn/src/components/index.ts
@@ -1,5 +1,6 @@
 export * from './Badge.style';
 export * from './Card.style';
+export * from './Headline.style';
 export * from './HelpText.style';
 export * from './Image.style';
 export * from './Input.style';
diff --git a/themes/theme-unicorn/src/typography.ts b/themes/theme-unicorn/src/typography.ts
index a5f3e0f18..4e48fb0e2 100644
--- a/themes/theme-unicorn/src/typography.ts
+++ b/themes/theme-unicorn/src/typography.ts
@@ -50,49 +50,5 @@ export const typography = {
       ...text.root,
       ...text.span,
     },
-    headline1: {
-      ...text.root,
-      fontWeight: 'heading',
-      fontSize: 'xlarge',
-      marginBottom: 'medium',
-      '&:first-child': {
-        marginTop: 'none',
-      },
-    },
-    headline2: {
-      ...text.root,
-      fontWeight: 'heading',
-      fontSize: 'large',
-      marginTop: 'medium',
-      marginBottom: 'xsmall',
-      '&:first-child': {
-        marginTop: 'none',
-      },
-    },
-    headline3: {
-      ...text.root,
-      fontWeight: 'heading',
-      fontSize: 'medium',
-      marginTop: 'small',
-      marginBottom: 'small',
-      '&:first-child': {
-        marginTop: 'none',
-      },
-    },
-    headline4: {
-      ...text.root,
-      fontWeight: 'heading',
-      fontSize: 'small',
-    },
-    headline5: {
-      ...text.root,
-      fontWeight: 'heading',
-      fontSize: 'xsmall',
-    },
-    headline6: {
-      ...text.root,
-      fontSize: 'xsmall',
-      textTransform: 'uppercase',
-    },
   },
 } as const;


Test patch:
diff --git a/packages/components/src/Headline/Headline.test.tsx b/packages/components/src/Headline/Headline.test.tsx
new file mode 100644
index 000000000..1cf50f86c
--- /dev/null
+++ b/packages/components/src/Headline/Headline.test.tsx
@@ -0,0 +1,107 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import { ThemeProvider } from '@marigold/system';
+
+import { Headline } from './Headline';
+
+const theme = {
+  space: {
+    none: 0,
+    'small-1': 4,
+    'medium-1': 16,
+  },
+  color: { white: '#FFF' },
+  fontSizes: {
+    xxsmall: '0.875rem',
+    xsmall: '1rem',
+    small: '1.125rem',
+    medium: '1.25rem',
+    large: '1.5rem',
+    xlarge: '2rem',
+  },
+  components: {
+    Headline: {
+      base: {
+        m: 'none',
+        fontWeight: 'heading',
+      },
+      size: {
+        'level-1': {
+          fontSize: 'xlarge',
+        },
+        'level-2': {
+          fontSize: 'large',
+        },
+        'level-3': {
+          fontSize: 'medium',
+        },
+        'level-4': {
+          fontSize: 'small',
+        },
+        'level-5': {
+          fontSize: 'xsmall',
+        },
+        'level-6': {
+          fontSize: 'xsmall',
+          textTransform: 'uppercase',
+        },
+      },
+      variant: {
+        one: {
+          color: 'white',
+        },
+      },
+    },
+  },
+};
+
+test('renders as a "section" element', () => {
+  render(
+    <ThemeProvider theme={theme}>
+      <Headline data-testid="headline" />
+    </ThemeProvider>
+  );
+
+  const headline = screen.getByTestId('headline');
+  expect(headline instanceof HTMLHeadingElement).toBeTruthy();
+});
+
+test.each(['1', '2', '3', '4', '5', '6'])(
+  'uses styles based on given level from theme sizes (%s)',
+  lvl => {
+    render(
+      <ThemeProvider theme={theme}>
+        <Headline data-testid="headline" level={lvl as any} />
+      </ThemeProvider>
+    );
+
+    const headline = screen.getByTestId('headline');
+    // @ts-expect-error
+    const token = theme.components.Headline.size[`level-${lvl}`].fontSize;
+    // @ts-expect-error
+    expect(headline).toHaveStyle(`font-size: ${theme.fontSizes[token]}`);
+  }
+);
+
+test('uses "level-1" by default', () => {
+  render(
+    <ThemeProvider theme={theme}>
+      <Headline data-testid="headline" />
+    </ThemeProvider>
+  );
+
+  const headline = screen.getByTestId('headline');
+  const token = theme.components.Headline.size[`level-1`].fontSize;
+  // @ts-expect-error
+  expect(headline).toHaveStyle(`font-size: ${theme.fontSizes[token]}`);
+});
+
+test('headline accepts a variant', () => {
+  render(
+    <ThemeProvider theme={theme}>
+      <Headline data-testid="headline" variant="one" />
+    </ThemeProvider>
+  );
+  const headline = screen.getByTestId('headline');
+  expect(headline).toHaveStyle(`color: white`);
+});