-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[CCUBE-1545][MAHI]Set up V3 Typography component with its test case a…
…nd storybook visualisation
- Loading branch information
1 parent
3e4177e
commit e10348b
Showing
11 changed files
with
678 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import { css } from "styled-components"; | ||
import { TypographyProps, TypographySizeType, TypographyWeight } from "./types"; | ||
import { TypographyStyle } from "./typography-style"; | ||
import { Colour, Font } from "../theme"; | ||
import { StyledComponentProps } from "../theme/helpers"; | ||
|
||
const getResolvedTypographyWeight = ( | ||
weight: TypographyWeight, | ||
props: StyledComponentProps | ||
): string => { | ||
const weightMap: Record<number, string> = { | ||
300: "light", | ||
400: "regular", | ||
600: "semibold", | ||
700: "bold", | ||
}; | ||
|
||
// If resolvedWeight is a string that is a number for eg "400", convert it to a number | ||
const numericWeight = | ||
typeof weight === "string" && !isNaN(Number(weight)) | ||
? Number(weight) | ||
: weight; | ||
|
||
// Map it to its string equivalent | ||
const mappedWeight = weightMap[numericWeight as number] || numericWeight; | ||
const finalWeight = Font[`weight-${mappedWeight}`]; | ||
|
||
// If final weight is a function, resolve it with props | ||
return typeof finalWeight === "function" ? finalWeight(props) : finalWeight; | ||
}; | ||
|
||
export const getTypographyStyle = ( | ||
type: TypographySizeType, | ||
weight: TypographyWeight, | ||
paragraph = false | ||
) => { | ||
return (props: any) => { | ||
const attrs = TypographyStyle[type]; | ||
|
||
const resolvedWeight = getResolvedTypographyWeight(weight, props); | ||
|
||
// Check if function if so resolve with props | ||
const fontSize = | ||
typeof attrs.fontSize === "function" | ||
? attrs.fontSize(props) | ||
: attrs.fontSize; | ||
|
||
// Make it a int for calc | ||
const fontSizeValue = parseFloat(fontSize); | ||
const fontSizeUnit = fontSize.replace(fontSizeValue.toString(), ""); | ||
|
||
// Add extra margin for paragraphs | ||
const getMarginBottomStyle = () => { | ||
const marginBottomScale = paragraph ? 1.05 : 0; | ||
return css` | ||
margin-bottom: ${fontSizeValue * marginBottomScale}${fontSizeUnit}; | ||
`; | ||
}; | ||
|
||
return css` | ||
font-size: ${fontSize}; | ||
line-height: ${attrs.lineHeight}; | ||
letter-spacing: ${attrs.letterSpacing || 0}; | ||
font-weight: ${resolvedWeight}; | ||
${getMarginBottomStyle()} | ||
`; | ||
}; | ||
}; | ||
|
||
export const getDisplayStyle = (inline = false, paragraph = false) => { | ||
if (paragraph) { | ||
return css` | ||
display: block; | ||
`; | ||
} else if (inline) { | ||
return css` | ||
display: inline; | ||
`; | ||
} else { | ||
return css` | ||
display: block; | ||
`; | ||
} | ||
}; | ||
|
||
// Helper func to refactor code | ||
export const createTypographyStyles = ( | ||
textStyle: TypographySizeType, | ||
props: TypographyProps | ||
) => css` | ||
${getTypographyStyle( | ||
textStyle, | ||
props.weight || "regular", | ||
props.paragraph | ||
)(props)} | ||
color: ${Colour.Primitive["neutral-20"]}; | ||
${getDisplayStyle(props.inline, props.paragraph)} | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
export type TypographySizeType = | ||
| "HeaderXXL" | ||
| "HeaderXL" | ||
| "HeaderLG" | ||
| "HeaderMD" | ||
| "HeaderSM" | ||
| "HeaderXS" | ||
| "BodyBL" | ||
| "BodyLG" | ||
| "BodyMD" | ||
| "BodySM" | ||
| "LinkBL" | ||
| "LinkMD" | ||
| "LinkLG" | ||
| "LinkSM"; | ||
|
||
export interface TypographyStyleSpec { | ||
fontSize?: number | undefined; | ||
fontWeight?: number | undefined; | ||
lineHeight?: number | undefined; | ||
letterSpacing?: number | undefined; | ||
} | ||
|
||
export type TypographyWeight = | ||
| "regular" | ||
| "semibold" | ||
| "bold" | ||
| "light" | ||
| 400 | ||
| 600 | ||
| 700 | ||
| 300; | ||
|
||
export type TextStyleSetType = { | ||
[key in TypographySizeType]: TypographyStyleSpec; | ||
}; | ||
|
||
export interface TypographyProps extends React.HTMLAttributes<HTMLElement> { | ||
// Can be any weight such as regular or 400 | ||
weight?: TypographyWeight; | ||
// For consumer to choose if they want the text to be inline for example | ||
inline?: boolean; | ||
// For consumer to choose for block level style | ||
paragraph?: boolean; | ||
} | ||
|
||
export interface LinkProps extends TypographyProps { | ||
// If the link is external | ||
external?: boolean; | ||
textStyle?: TypographySizeType; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import { Font } from "../theme"; | ||
|
||
export const TypographyStyle = { | ||
HeaderXXL: { | ||
fontSize: Font["header-size-xxl"], | ||
lineHeight: Font["header-lh-xxl"], | ||
letterSpacing: Font["header-ls-xxl"], | ||
}, | ||
HeaderXL: { | ||
fontSize: Font["header-size-xl"], | ||
lineHeight: Font["header-lh-xl"], | ||
letterSpacing: Font["header-ls-xl"], | ||
}, | ||
HeaderLG: { | ||
fontSize: Font["header-size-lg"], | ||
lineHeight: Font["header-lh-lg"], | ||
letterSpacing: Font["header-ls-lg"], | ||
}, | ||
HeaderMD: { | ||
fontSize: Font["header-size-md"], | ||
lineHeight: Font["header-lh-md"], | ||
letterSpacing: Font["header-ls-md"], | ||
}, | ||
HeaderSM: { | ||
fontSize: Font["header-size-sm"], | ||
lineHeight: Font["header-lh-sm"], | ||
letterSpacing: Font["header-ls-sm"], | ||
}, | ||
HeaderXS: { | ||
fontSize: Font["header-size-xs"], | ||
lineHeight: Font["header-lh-xs"], | ||
letterSpacing: Font["header-ls-xs"], | ||
}, | ||
|
||
BodyBL: { | ||
fontSize: Font["body-size-baseline"], | ||
lineHeight: Font["body-lh-baseline"], | ||
letterSpacing: Font["body-ls-baseline"], | ||
}, | ||
BodyLG: { | ||
fontSize: Font["body-size-lg"], | ||
lineHeight: Font["body-lh-lg"], | ||
letterSpacing: Font["body-ls-lg"], | ||
}, | ||
BodyMD: { | ||
fontSize: Font["body-size-md"], | ||
lineHeight: Font["body-lh-md"], | ||
letterSpacing: Font["body-ls-md"], | ||
}, | ||
BodySM: { | ||
fontSize: Font["body-size-sm"], | ||
lineHeight: Font["body-lh-sm"], | ||
letterSpacing: Font["body-ls-sm"], | ||
}, | ||
LinkBL: { | ||
fontSize: Font["body-size-baseline"], | ||
lineHeight: Font["body-lh-baseline"], | ||
letterSpacing: Font["body-ls-baseline"], | ||
}, | ||
LinkLG: { | ||
fontSize: Font["body-size-lg"], | ||
lineHeight: Font["body-lh-lg"], | ||
letterSpacing: Font["body-ls-lg"], | ||
}, | ||
LinkMD: { | ||
fontSize: Font["body-size-md"], | ||
lineHeight: Font["body-lh-md"], | ||
letterSpacing: Font["body-ls-md"], | ||
}, | ||
LinkSM: { | ||
fontSize: Font["body-size-sm"], | ||
lineHeight: Font["body-lh-sm"], | ||
letterSpacing: Font["body-ls-sm"], | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
import styled, { css } from "styled-components"; | ||
import { | ||
createTypographyStyles, | ||
getDisplayStyle, | ||
getTypographyStyle, | ||
} from "./helper"; | ||
import { Colour } from "../theme"; | ||
import { LinkProps, TypographyProps, TypographySizeType } from "./types"; | ||
import { ExternalIcon } from "@lifesg/react-icons/external"; | ||
|
||
export namespace Typography { | ||
const createHeader = ( | ||
tag: keyof JSX.IntrinsicElements, | ||
textStyle: TypographySizeType | ||
) => { | ||
const Header = styled(tag)` | ||
${(props: TypographyProps) => | ||
createTypographyStyles(textStyle, props)} | ||
`; | ||
Header.displayName = `Header-${textStyle}`; | ||
return Header; | ||
}; | ||
|
||
export const HeaderXXL = createHeader("h1", "HeaderXXL"); | ||
export const HeaderXL = createHeader("h2", "HeaderXL"); | ||
export const HeaderLG = createHeader("h3", "HeaderLG"); | ||
export const HeaderMD = createHeader("h4", "HeaderMD"); | ||
export const HeaderSM = createHeader("h5", "HeaderSM"); | ||
export const HeaderXS = createHeader("h6", "HeaderXS"); | ||
|
||
const createBody = (textStyle: TypographySizeType) => { | ||
const Body = styled.p` | ||
${(props: TypographyProps) => | ||
createTypographyStyles(textStyle, props)} | ||
`; | ||
Body.displayName = `Body-${textStyle}`; | ||
return Body; | ||
}; | ||
|
||
export const BodyBL = createBody("BodyBL"); | ||
export const BodyLG = createBody("BodyLG"); | ||
export const BodyMD = createBody("BodyMD"); | ||
export const BodySM = createBody("BodySM"); | ||
|
||
const createLinkComponent = (textStyle: TypographySizeType) => { | ||
const Component = (props: LinkProps) => ( | ||
<HyperlinkComponent {...props} textStyle={textStyle} /> | ||
); | ||
Component.displayName = `Link-${textStyle}`; | ||
return Component; | ||
}; | ||
|
||
export const LinkBL = createLinkComponent("LinkBL"); | ||
export const LinkMD = createLinkComponent("LinkMD"); | ||
export const LinkLG = createLinkComponent("LinkLG"); | ||
export const LinkSM = createLinkComponent("LinkSM"); | ||
} | ||
|
||
console.log("Hello"); | ||
|
||
// FOR LINK : | ||
export const StyledExternalIcon = styled(ExternalIcon)` | ||
height: 1em; | ||
width: 1em; | ||
margin-left: 0.4em; | ||
vertical-align: middle; | ||
`; | ||
|
||
const HyperlinkBase = styled.a<LinkProps>` | ||
${(props) => { | ||
return css` | ||
${getTypographyStyle( | ||
props.textStyle, | ||
props.weight || "regular" | ||
)(props)} | ||
color: ${Colour.hyperlink}; | ||
text-decoration: none; | ||
:hover, | ||
:active, | ||
:focus { | ||
color: ${Colour["text-hover"]}; | ||
svg { | ||
color: ${Colour["text-hover"]}; | ||
} | ||
} | ||
${getDisplayStyle(props.inline, props.paragraph)} | ||
`; | ||
}} | ||
`; | ||
|
||
const HyperlinkComponent = ({ | ||
external = false, | ||
children, | ||
...rest | ||
}: LinkProps) => { | ||
return ( | ||
<HyperlinkBase external={external} {...rest}> | ||
{children} | ||
{external && <StyledExternalIcon />} | ||
</HyperlinkBase> | ||
); | ||
}; | ||
HyperlinkComponent.displayName = "HyperlinkComponent"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { ThemeProvider } from "styled-components"; | ||
import { Typography } from "../../src/typography/typography"; | ||
import { mockOverrideTheme, mockTheme } from "./mock-theme"; | ||
|
||
export default { | ||
title: "Typography-Test/Body", | ||
component: Typography, | ||
}; | ||
|
||
export const Body_Inline = () => ( | ||
<ThemeProvider theme={mockTheme}> | ||
<Typography.BodyBL weight="bold" inline> | ||
Testing for BodyBL and Bold with inline | ||
</Typography.BodyBL> | ||
<br /> | ||
<Typography.BodyLG weight="regular" inline> | ||
Testing for BodyLG and SemiBold with inline | ||
</Typography.BodyLG> | ||
<br /> | ||
<Typography.BodyMD weight="light" inline> | ||
Testing for BodyMD and light with inline | ||
</Typography.BodyMD> | ||
</ThemeProvider> | ||
); | ||
|
||
export const Body_Paragraph = () => ( | ||
<ThemeProvider theme={mockOverrideTheme}> | ||
<Typography.BodyBL weight="bold" paragraph> | ||
Testing for BodyBL and Bold with paragraph | ||
</Typography.BodyBL> | ||
<Typography.BodyLG weight="regular" paragraph> | ||
Testing for BodyLG and regular with paragraph | ||
</Typography.BodyLG> | ||
<Typography.BodyMD weight="light" paragraph> | ||
Testing for BodyMD and Light with paragraph | ||
</Typography.BodyMD> | ||
</ThemeProvider> | ||
); |
Oops, something went wrong.