From 580004c578d44485229b421b96e312c48ad07af7 Mon Sep 17 00:00:00 2001 From: Michael Marszalek Date: Tue, 6 Oct 2020 10:01:17 +0200 Subject: [PATCH] Typography typescript (#645) * Moved base types to tokens library * Typography typescript * removed comment * Uniformed variants type --- .../src/TextField/HelperText/HelperText.tsx | 3 +- .../core-react/src/TextField/Input/Input.tsx | 7 +- .../core-react/src/Typography/Typography.jsx | 136 ----------------- ...ypography.test.jsx => Typography.test.tsx} | 9 +- .../src/Typography/Typography.tokens.js | 51 ------- .../src/Typography/Typography.tokens.ts | 61 ++++++++ .../core-react/src/Typography/Typography.tsx | 144 ++++++++++++++++++ libraries/core-react/src/_common/templates.ts | 30 +--- libraries/tokens/index.ts | 1 + libraries/tokens/src/types.ts | 69 +++++++++ 10 files changed, 286 insertions(+), 225 deletions(-) delete mode 100644 libraries/core-react/src/Typography/Typography.jsx rename libraries/core-react/src/Typography/{Typography.test.jsx => Typography.test.tsx} (93%) delete mode 100644 libraries/core-react/src/Typography/Typography.tokens.js create mode 100644 libraries/core-react/src/Typography/Typography.tokens.ts create mode 100644 libraries/core-react/src/Typography/Typography.tsx create mode 100644 libraries/tokens/src/types.ts diff --git a/libraries/core-react/src/TextField/HelperText/HelperText.tsx b/libraries/core-react/src/TextField/HelperText/HelperText.tsx index ec332698ac..ebbeb7a0fe 100644 --- a/libraries/core-react/src/TextField/HelperText/HelperText.tsx +++ b/libraries/core-react/src/TextField/HelperText/HelperText.tsx @@ -1,6 +1,6 @@ import React, { ReactNode } from 'react' import styled, { css } from 'styled-components' -import { Spacing, typographyTemplate } from '../../_common/templates' +import { typographyTemplate } from '../../_common/templates' import { HelperTextVariantProps, helperText as tokens, @@ -8,6 +8,7 @@ import { import { useTextField } from '../context' import { Icon } from '../Icon' import type { Variants } from '../types' +import type { Spacing } from '@equinor/eds-tokens' type VariantionProps = { variant: HelperTextVariantProps diff --git a/libraries/core-react/src/TextField/Input/Input.tsx b/libraries/core-react/src/TextField/Input/Input.tsx index 562d03ca86..6414975d94 100644 --- a/libraries/core-react/src/TextField/Input/Input.tsx +++ b/libraries/core-react/src/TextField/Input/Input.tsx @@ -1,14 +1,11 @@ import React, { ReactNode, ElementType } from 'react' import styled, { css } from 'styled-components' import { InputVariantProps, input as tokens } from './Input.tokens' -import { - Spacing, - typographyTemplate, - spacingsTemplate, -} from '../../_common/templates' +import { typographyTemplate, spacingsTemplate } from '../../_common/templates' import { useTextField } from '../context' import { Icon } from '../Icon' import type { Variants } from '../types' +import type { Spacing } from '@equinor/eds-tokens' const Variation = ({ variant }: { variant: InputVariantProps }) => { if (!variant) { diff --git a/libraries/core-react/src/Typography/Typography.jsx b/libraries/core-react/src/Typography/Typography.jsx deleted file mode 100644 index 68e7f2ce80..0000000000 --- a/libraries/core-react/src/Typography/Typography.jsx +++ /dev/null @@ -1,136 +0,0 @@ -// @ts-nocheck -import React, { forwardRef } from 'react' -import PropTypes from 'prop-types' -import styled, { css } from 'styled-components' -import { typographyTemplate } from '../_common/templates' -import { - groupNames, - variantNames, - colorNames, - quickVariants, - colors, - typography, -} from './Typography.tokens' - -const getElementType = (variant, link) => { - if (link) { - return 'a' - } - switch (variant) { - case 'h1': - case 'h2': - case 'h3': - case 'h4': - case 'h5': - case 'h6': - return variant - case 'caption': - case 'overline': - case 'ingress': - case 'meta': - case 'body_short': - case 'body_long': - default: - return 'p' - } -} - -const findTypography = (variantName, group) => { - // For quick use when using paragraphs and headings we can skip group - if (typeof group === 'undefined') { - return quickVariants[variantName] - } - - return typography[group][variantName] -} - -const findColor = (inputColor) => - typeof colors[inputColor] === 'undefined' ? inputColor : colors[inputColor] - -const toVariantName = (variant, bold = false, italic = false, link = false) => - `${variant}${bold ? '_bold' : ''}${italic ? '_italic' : ''}${ - link ? '_link' : '' - }` - -const StyledTypography = styled.p` - ${({ typography, link }) => typographyTemplate(typography, link)} - ${({ color }) => css({ color: findColor(color) })} - ${({ lines }) => - //https://caniuse.com/#feat=css-line-clamp - lines > 0 && - css` - & { - display: -webkit-box; - -webkit-line-clamp: ${lines}; - -webkit-box-orient: vertical; - overflow: hidden; - text-overflow: ellipsis; - } - `} -` - -export const Typography = forwardRef(function EdsTypography( - { variant, children, bold, italic, link, group, token, ...other }, - ref, -) { - const as = getElementType(variant, link) - const variantName = toVariantName(variant, bold, italic, link) - const typography = findTypography(variantName, group) - - if (typeof typography === 'undefined') { - throw new Error( - `Typography variant not found for variant "${variantName}" ("${variant}") & group "${ - group || '' - }"`, - ) - } - return ( - - {children} - - ) -}) - -Typography.propTypes = { - /** @ignore */ - className: PropTypes.string, - /** @ignore */ - children: PropTypes.node, - /** Specifies which variant to use */ - variant: PropTypes.oneOf(variantNames), - /** Specifices which typography group to use */ - group: PropTypes.oneOf(groupNames), - /** Specifies if text should be bold */ - bold: PropTypes.bool, - /** Specifies if text should be italic */ - italic: PropTypes.bool, - /** Specifies if text should be a link */ - link: PropTypes.bool, - /** Specifies which color to use */ - color: PropTypes.oneOfType([PropTypes.oneOf(colorNames), PropTypes.string]), - /** Specifies which typography token to use */ - token: PropTypes.object, - /** Specifies how many lines of text are shown */ - lines: PropTypes.number, -} - -Typography.defaultProps = { - variant: 'body_short', - children: undefined, - group: undefined, - bold: false, - italic: false, - link: false, - className: '', - color: undefined, - token: undefined, - lines: undefined, -} - -Typography.displayName = 'eds-typography' diff --git a/libraries/core-react/src/Typography/Typography.test.jsx b/libraries/core-react/src/Typography/Typography.test.tsx similarity index 93% rename from libraries/core-react/src/Typography/Typography.test.jsx rename to libraries/core-react/src/Typography/Typography.test.tsx index c5ed7ff5f2..6ff03c1169 100644 --- a/libraries/core-react/src/Typography/Typography.test.jsx +++ b/libraries/core-react/src/Typography/Typography.test.tsx @@ -6,15 +6,16 @@ import 'jest-styled-components' import styled from 'styled-components' import { Typography } from '.' import { tokens } from '@equinor/eds-tokens' -import { colors } from './Typography.tokens.js' +import { colors } from './Typography.tokens' +import type { Typography as TypographyType } from '@equinor/eds-tokens' const StyledTypography = styled(Typography)` margin-top: 16px; margin-bottom: 32px; ` -const stripSpaces = (t) => t.replace(/\s/g, '') +const stripSpaces = (t: string): string => t.replace(/\s/g, '') -const expectToMatchTypography = (element, token) => { +const expectToMatchTypography = (element, token: TypographyType) => { const { color, fontFamily, @@ -35,7 +36,7 @@ afterEach(cleanup) describe('Typography', () => { it('throws error when variant is wrong', () => { - jest.spyOn(console, 'error').mockImplementation(() => {}) + jest.spyOn(console, 'error').mockImplementation() expect(() => { render(Test) diff --git a/libraries/core-react/src/Typography/Typography.tokens.js b/libraries/core-react/src/Typography/Typography.tokens.js deleted file mode 100644 index 304cc9c3a9..0000000000 --- a/libraries/core-react/src/Typography/Typography.tokens.js +++ /dev/null @@ -1,51 +0,0 @@ -// @ts-nocheck -import { tokens } from '@equinor/eds-tokens' - -const { typography, colors: colorsToken } = tokens -const { heading, paragraph } = typography - -const { - interactive: { - primary__resting: { rgba: primary }, - secondary__resting: { rgba: secondary }, - danger__resting: { rgba: danger }, - warning__resting: { rgba: warning }, - success__resting: { rgba: success }, - disabled__text: { rgba: disabled }, - }, -} = colorsToken - -const colors = { - primary, - secondary, - danger, - warning, - success, - disabled, -} - -const groupNames = Object.keys(tokens.typography) - -// Only used for propTypes as groups have duplicate variants -const variantNames = Object.keys( - Object.entries({ ...tokens.typography }).reduce( - (acc, [, val]) => ({ ...acc, ...val }), - {}, - ), -) - -const colorNames = Object.keys(colors) - -const quickVariants = { - ...heading, - ...paragraph, -} - -export { - typography, - colors, - quickVariants, - colorNames, - groupNames, - variantNames, -} diff --git a/libraries/core-react/src/Typography/Typography.tokens.ts b/libraries/core-react/src/Typography/Typography.tokens.ts new file mode 100644 index 0000000000..732ffe260b --- /dev/null +++ b/libraries/core-react/src/Typography/Typography.tokens.ts @@ -0,0 +1,61 @@ +import { tokens } from '@equinor/eds-tokens' +import type { TypographyTokens } from '@equinor/eds-tokens' + +const { typography, colors: colorsToken } = tokens +const { heading, paragraph } = typography + +const { + interactive: { + primary__resting: { rgba: primary }, + secondary__resting: { rgba: secondary }, + danger__resting: { rgba: danger }, + warning__resting: { rgba: warning }, + success__resting: { rgba: success }, + disabled__text: { rgba: disabled }, + }, +} = colorsToken + +const colors = { + primary, + secondary, + danger, + warning, + success, + disabled, +} + +export type QuickTypographyVariants = + | TypographyTokens['heading'] + | TypographyTokens['paragraph'] + +const quickVariants: QuickTypographyVariants = { + ...heading, + ...paragraph, +} + +type TypographyVariants = + | keyof TypographyTokens['heading'] + | keyof TypographyTokens['paragraph'] + | keyof TypographyTokens['navigation'] + | keyof TypographyTokens['input'] + | keyof TypographyTokens['ui'] + | keyof TypographyTokens['table'] + +type ColorVariants = + | 'primary' + | 'secondary' + | 'danger' + | 'warning' + | 'success' + | 'disabled' + +type TypographyGroups = keyof typeof typography + +export { + typography, + colors, + quickVariants, + TypographyVariants, + ColorVariants, + TypographyGroups, +} diff --git a/libraries/core-react/src/Typography/Typography.tsx b/libraries/core-react/src/Typography/Typography.tsx new file mode 100644 index 0000000000..45ffc39911 --- /dev/null +++ b/libraries/core-react/src/Typography/Typography.tsx @@ -0,0 +1,144 @@ +import React, { forwardRef, ElementType } from 'react' +import styled, { css } from 'styled-components' +import { typographyTemplate } from '../_common/templates' +import { + quickVariants, + colors, + typography, + TypographyVariants, + ColorVariants, + TypographyGroups, +} from './Typography.tokens' +import type { Typography as TypographyType } from '@equinor/eds-tokens' + +const getElementType = (variant: string, link: boolean): ElementType => { + if (link) { + return 'a' + } + switch (variant) { + case 'h1': + case 'h2': + case 'h3': + case 'h4': + case 'h5': + case 'h6': + return variant + case 'caption': + case 'overline': + case 'ingress': + case 'meta': + case 'body_short': + case 'body_long': + default: + return 'p' + } +} + +const findTypography = ( + variantName: TypographyVariants, + group?: TypographyGroups, +): TypographyType => { + // For quick use when using paragraphs and headings we can skip group + if (!group && quickVariants[variantName]) { + return quickVariants[variantName] as TypographyType + } + return (typography[group] as unknown)[variantName] as TypographyType +} + +const findColor = (inputColor: ColorVariants): string => + typeof colors[inputColor] === 'undefined' ? inputColor : colors[inputColor] + +const toVariantName = ( + variant: TypographyVariants, + bold = false, + italic = false, + link = false, +) => + `${variant}${bold ? '_bold' : ''}${italic ? '_italic' : ''}${ + link ? '_link' : '' + }` + +type StyledProps = { + typography: Partial + link: boolean + color: ColorVariants + lines: number +} + +const StyledTypography = styled.p` + ${({ typography, link }) => typographyTemplate(typography, link)} + ${({ color }) => css({ color: findColor(color) })} + ${({ lines }) => + //https://caniuse.com/#feat=css-line-clamp + lines > 0 && + css` + & { + display: -webkit-box; + -webkit-line-clamp: ${lines}; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; + } + `} +` +type Props = { + variant?: TypographyVariants + group?: TypographyGroups + bold?: boolean + italic?: boolean + link?: boolean + color?: ColorVariants + token?: Partial + lines?: number + as?: ElementType +} & React.HTMLAttributes + +export const Typography = forwardRef(function EdsTypography( + { + variant = 'body_short', + children, + bold, + italic, + link, + group, + token, + as: providedAs, + ...other + }, + ref, +) { + const as: ElementType = providedAs + ? providedAs + : getElementType(variant, link) + + const variantName = toVariantName( + variant, + bold, + italic, + link, + ) as TypographyVariants + + const typography = findTypography(variantName, group) + + if (typeof typography === 'undefined') { + throw new Error( + `Typography variant not found for variant "${variantName}" ("${variant}") & group "${ + group || '' + }"`, + ) + } + + return ( + + {children} + + ) +}) + +Typography.displayName = 'eds-typography' diff --git a/libraries/core-react/src/_common/templates.ts b/libraries/core-react/src/_common/templates.ts index 2ff91c8c38..73e96c469d 100644 --- a/libraries/core-react/src/_common/templates.ts +++ b/libraries/core-react/src/_common/templates.ts @@ -1,37 +1,11 @@ import { css } from 'styled-components' import type { FlattenSimpleInterpolation } from 'styled-components' +import type { Typography, Border, Spacing } from '@equinor/eds-tokens' type StyledCSS = FlattenSimpleInterpolation -export type Typography = { - color: string - fontFamily: string - fontSize: string - fontWeight: number - letterSpacing?: string - lineHeight: string - textAlign?: string - fontStyle?: string - textTransform?: string - textDecoration?: string - fontFeature?: string -} - -export type Border = { - radius: string - color: string - width: string -} - -export type Spacing = { - left: string - right: string - top: string - bottom: string -} - export const typographyTemplate = ( - typography: Typography, + typography: Partial, link?: boolean, ): string => { if (!typography) { diff --git a/libraries/tokens/index.ts b/libraries/tokens/index.ts index dd183ade02..68378cd7f7 100644 --- a/libraries/tokens/index.ts +++ b/libraries/tokens/index.ts @@ -1 +1,2 @@ export { tokens } from './base' +export * from './src/types' diff --git a/libraries/tokens/src/types.ts b/libraries/tokens/src/types.ts new file mode 100644 index 0000000000..e266393dd0 --- /dev/null +++ b/libraries/tokens/src/types.ts @@ -0,0 +1,69 @@ +import { typography } from '../base/typography' + +export type Typography = { + color: string + fontFamily: string + fontSize: string + fontWeight: number | string + lineHeight: string + textAlign: string + letterSpacing?: string + fontStyle?: string + textTransform?: string + textDecoration?: string + fontFeature?: string +} + +export type TypographyTokens = { + [P1 in keyof typeof typography]: { + [P2 in keyof typeof typography[P1]]: Typography + } +} + +export type Border = { + radius: string + color: string + width: string +} + +export type Spacing = { + left: string + right: string + top: string + bottom: string +} + +export type Clickbounds = { + jumbo__base: string + default__base: string + default__input: string +} + +export type Elevations = { + above_scrim: string + none: string + overlay: string + raised: string + sticky: string + temporary_nav: string +} + +export type Color = { + hex: string + hsla: string + rgba: string +} + +export type Spacings = { + comfortable: { + xxx_large: string + xx_large: string + x_large: string + large: string + medium: string + medium_small: string + small: string + x_small: string + xx_small: string + } +}