Skip to content

Commit

Permalink
Migrate typography components to KAIO (#4046)
Browse files Browse the repository at this point in the history
* Migrate Heading & Paragraph components to KAIO

* MDX lint fixes

* Rename Paragraph component to Text
  • Loading branch information
dougmacknz authored Sep 11, 2023
1 parent f23ccdd commit d1b1a1e
Show file tree
Hide file tree
Showing 17 changed files with 1,073 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .changeset/cunning-cass-curates.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
102 changes: 102 additions & 0 deletions packages/components/src/Heading/Heading.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
@import "~@kaizen/design-tokens/sass/color";
@import "~@kaizen/design-tokens/sass/typography";

.heading {
margin: 0;
}

.display-0 {
font-family: $typography-display-0-font-family;
font-weight: $typography-display-0-font-weight;
font-size: $typography-display-0-font-size;
line-height: $typography-display-0-line-height;
letter-spacing: $typography-display-0-letter-spacing;
}

.heading-1 {
font-family: $typography-heading-1-font-family;
font-weight: $typography-heading-1-font-weight;
font-size: $typography-heading-1-font-size;
line-height: $typography-heading-1-line-height;
letter-spacing: $typography-heading-1-letter-spacing;
}

.heading-2 {
font-family: $typography-heading-2-font-family;
font-weight: $typography-heading-2-font-weight;
font-size: $typography-heading-2-font-size;
line-height: $typography-heading-2-line-height;
letter-spacing: $typography-heading-2-letter-spacing;
}

.heading-3 {
font-family: $typography-heading-3-font-family;
font-weight: $typography-heading-3-font-weight;
font-size: $typography-heading-3-font-size;
line-height: $typography-heading-3-line-height;
letter-spacing: $typography-heading-3-letter-spacing;
}

.heading-4 {
font-family: $typography-heading-4-font-family;
font-weight: $typography-heading-4-font-weight;
font-size: $typography-heading-4-font-size;
line-height: $typography-heading-4-line-height;
letter-spacing: $typography-heading-4-letter-spacing;
}

.heading-5 {
font-family: $typography-heading-5-font-family;
font-weight: $typography-heading-5-font-weight;
font-size: $typography-heading-5-font-size;
line-height: $typography-heading-5-line-height;
letter-spacing: $typography-heading-5-letter-spacing;
}

.heading-6 {
font-family: $typography-heading-6-font-family;
font-weight: $typography-heading-6-font-weight;
font-size: $typography-heading-6-font-size;
line-height: $typography-heading-6-line-height;
letter-spacing: $typography-heading-6-letter-spacing;
}

.dark {
color: $color-purple-800;
opacity: 100%;
}

.dark-reduced-opacity {
color: $color-purple-800;
opacity: 70%;
}

.white {
color: $color-white;
opacity: 100%;
}

.white-reduced-opacity {
color: $color-white;
opacity: 80%;
}

.positive {
&.small {
color: $color-green-600;
}

&.large {
color: $color-green-500;
}
}

.negative {
&.small {
color: $color-red-600;
}

&.large {
color: $color-red-500;
}
}
84 changes: 84 additions & 0 deletions packages/components/src/Heading/Heading.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import * as React from "react"
import { render } from "@testing-library/react"
import { AllowedHeadingTags, Heading, HeadingVariants } from "./"

describe("<Heading />", () => {
it("renders the correct classes", () => {
const headingMock = render(<Heading variant="display-0">Example</Heading>)
const headingClasslist = headingMock.getByText("Example").classList
expect(headingClasslist).toContain("heading")
expect(headingClasslist).toContain("display-0")
expect(headingClasslist).toContain("large")
})

it("adds a .small class instead of .large if a Heading 3 is used", () => {
const headingMock = render(
<Heading variant="heading-3" tag="div">
Example
</Heading>
)
expect(headingMock.getByText("Example").classList).toContain("small")
expect(headingMock.getByText("Example").classList).not.toContain("large")
})

it("adds a .dark-reduced-opacity class if the color prop is set to that", () => {
const headingMock = render(
<Heading variant="heading-3" color="dark-reduced-opacity">
Example
</Heading>
)
expect(headingMock.getByText("Example").classList).toContain(
"dark-reduced-opacity"
)
})

it("changes rendered HTML element when passed tag", () => {
const headingMock = render(
<Heading variant="display-0" tag="div">
Example
</Heading>
)
expect(headingMock.getByText("Example").tagName).toBe("DIV")
})

it("passes through data attributes", () => {
const { container } = render(
<Heading variant="display-0" data-automation-id="test-id">
Example
</Heading>
)
expect(
container.querySelector('[data-automation-id="test-id"]')
).not.toBeNull()
})

describe("defaults to the correct HTML element", () => {
type TestObject = { variant: HeadingVariants; el: AllowedHeadingTags }
const testCases: TestObject[] = [
{ variant: "display-0", el: "h1" },
{ variant: "heading-1", el: "h1" },
{ variant: "heading-2", el: "h2" },
{ variant: "heading-3", el: "h3" },
{ variant: "heading-4", el: "h4" },
{ variant: "heading-5", el: "h5" },
{ variant: "heading-6", el: "h6" },
]

testCases.forEach(({ variant, el }) => {
it(`renders the correct element for <Heading variant={${variant}} />`, () => {
const headingMock = render(<Heading variant={variant}>Example</Heading>)
expect(headingMock.getByText("Example").tagName.toLowerCase()).toBe(el)
expect(headingMock.baseElement).toMatchSnapshot()
})
})
})

it("allows consumers to provide a className", () => {
const { getByText } = render(
<Heading variant="heading-4" classNameOverride="example-classname">
Example
</Heading>
)
expect(getByText("Example").classList).toContain("example-classname")
})
})
102 changes: 102 additions & 0 deletions packages/components/src/Heading/Heading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { createElement, HTMLAttributes } from "react"
import classnames from "classnames"
import { OverrideClassName } from "~types/OverrideClassName"
import styles from "./Heading.module.scss"

const VARIANTS_24PX_OR_GREATER = ["display-0", "heading-1", "heading-2"]

export type HeadingVariants =
| "display-0"
| "heading-1"
| "heading-2"
| "heading-3"
| "heading-4"
| "heading-5"
| "heading-6"

export type AllowedHeadingTags =
| "pre"
| "p"
| "div"
| "span"
| "h1"
| "h2"
| "h3"
| "h4"
| "h5"
| "h6"
| "label"

export type AllowedHeadingColors =
| "dark"
| "dark-reduced-opacity"
| "white"
| "white-reduced-opacity"
| "positive"
| "negative"

export interface HeadingProps
extends OverrideClassName<HTMLAttributes<HTMLElement>> {
children: React.ReactNode
/**
* HTML elements that are allowed on Headings. When not supplied, the tag is inferred from
* the variant. E.g. display-0 will infer h1
*/
tag?: AllowedHeadingTags
/**
* Allowed heading variants
*/
variant: HeadingVariants
color?: AllowedHeadingColors
}

/**
* {@link https://cultureamp.atlassian.net/wiki/spaces/DesignSystem/pages/3074885298/Typography#Headings Guidance}
* {@link https://cultureamp.design/?path=/docs/components-typography-heading--display-0 Storybook}
*/
export const Heading = ({
children,
tag,
variant,
color = "dark",
classNameOverride,
...restProps
}: HeadingProps): JSX.Element => {
const inferredTag =
tag === undefined ? translateHeadingLevelToTag(variant) : tag

const className = classnames(
styles.heading,
styles[variant],
classNameOverride,
styles[color],
VARIANTS_24PX_OR_GREATER.includes(variant) ? styles.large : styles.small
)

return createElement(inferredTag, { ...restProps, className }, children)
}

Heading.displayName = "Heading"

/**
* A helper to infer the tag when not explicitly passed as a prop
* @param headingLevel Level of the heading
*/
const translateHeadingLevelToTag = (headingLevel: HeadingVariants): string => {
switch (headingLevel) {
case "display-0":
case "heading-1":
return "h1"
case "heading-2":
return "h2"
case "heading-3":
return "h3"
case "heading-4":
return "h4"
case "heading-5":
return "h5"
case "heading-6":
default:
return "h6"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`<Heading /> defaults to the correct HTML element renders the correct element for <Heading variant={display-0} /> 1`] = `
<body>
<div>
<h1
class="heading display-0 dark large"
>
Example
</h1>
</div>
</body>
`;

exports[`<Heading /> defaults to the correct HTML element renders the correct element for <Heading variant={heading-1} /> 1`] = `
<body>
<div>
<h1
class="heading heading-1 dark large"
>
Example
</h1>
</div>
</body>
`;

exports[`<Heading /> defaults to the correct HTML element renders the correct element for <Heading variant={heading-2} /> 1`] = `
<body>
<div>
<h2
class="heading heading-2 dark large"
>
Example
</h2>
</div>
</body>
`;

exports[`<Heading /> defaults to the correct HTML element renders the correct element for <Heading variant={heading-3} /> 1`] = `
<body>
<div>
<h3
class="heading heading-3 dark small"
>
Example
</h3>
</div>
</body>
`;

exports[`<Heading /> defaults to the correct HTML element renders the correct element for <Heading variant={heading-4} /> 1`] = `
<body>
<div>
<h4
class="heading heading-4 dark small"
>
Example
</h4>
</div>
</body>
`;

exports[`<Heading /> defaults to the correct HTML element renders the correct element for <Heading variant={heading-5} /> 1`] = `
<body>
<div>
<h5
class="heading heading-5 dark small"
>
Example
</h5>
</div>
</body>
`;

exports[`<Heading /> defaults to the correct HTML element renders the correct element for <Heading variant={heading-6} /> 1`] = `
<body>
<div>
<h6
class="heading heading-6 dark small"
>
Example
</h6>
</div>
</body>
`;
Loading

0 comments on commit d1b1a1e

Please sign in to comment.