From c4d31183281d074a891aa66d7029a2c12a963913 Mon Sep 17 00:00:00 2001 From: Henri Bernard <132286421+hebernardEquisoft@users.noreply.github.com> Date: Tue, 30 Jan 2024 16:30:49 -0500 Subject: [PATCH] feat(Theme): add component tokens (#710) --- packages/react/.eslintrc.js | 2 +- .../components/accordion/accordion-item.tsx | 8 +- .../react/src/components/avatar/avatar.tsx | 4 +- packages/react/src/components/bar/bar.tsx | 4 +- .../bento-menu-button.test.tsx.snap | 15 +- .../components/buttons/abstract-button.tsx | 169 ++------- .../src/components/buttons/button.test.tsx | 2 +- .../components/buttons/button.test.tsx.snap | 115 ++++--- .../react/src/components/buttons/button.tsx | 4 +- .../buttons/icon-button.test.tsx.snap | 18 +- .../src/components/buttons/icon-button.tsx | 4 +- .../src/components/buttons/search-button.tsx | 16 +- .../chooser-button/styles/choose.ts | 4 +- .../date-picker/date-picker.test.tsx.snap | 2 + .../components/date-picker/date-picker.tsx | 8 +- .../device-context-provider.test.tsx | 2 +- .../device-context-provider.tsx | 2 +- .../dropdown-list/dropdown-list.tsx | 6 +- .../dropdown-menu-button.test.tsx.snap | 8 +- .../dropdown-navigation.test.tsx.snap | 12 +- .../dropdown-navigation.tsx | 2 +- .../field-container/field-container.tsx | 4 +- .../global-banner/global-banner.test.tsx.snap | 112 +++--- .../global-navigation.test.tsx.snap | 2 + .../components/heading/heading.test.tsx.snap | 5 + .../react/src/components/heading/heading.tsx | 6 +- packages/react/src/components/label/label.tsx | 2 +- .../react/src/components/lozenge/lozenge.tsx | 4 +- .../components/menu-button/menu-button.tsx | 2 +- .../modal/modal-dialog.test.tsx.snap | 31 ++ .../src/components/modal/modal.test.tsx.snap | 6 + .../numeric-input/numeric-input.tsx | 4 +- .../password-input.test.tsx.snap | 6 + .../progress-circle/progress-circle.tsx | 4 +- .../progress-indicator/progress-indicator.tsx | 4 +- .../radio-button-group/radio-button-group.tsx | 4 +- .../radio-card-group/styled-components.ts | 8 +- .../src/components/search/search-input.tsx | 6 +- .../sectional-banner/sectional-banner.tsx | 8 +- .../react/src/components/status/status.tsx | 4 +- .../stepper-input/stepper-input.tsx | 4 +- .../table/sort-button-icon.test.tsx | 3 +- .../react/src/components/table/table-row.tsx | 8 +- packages/react/src/components/table/table.tsx | 4 +- packages/react/src/components/tag/tag.tsx | 4 +- .../src/components/text-area/text-area.tsx | 4 +- .../components/text-input/styles/inputs.tsx | 8 +- .../theme-wrapper/theme-wrapper.test.tsx | 36 +- .../theme-wrapper/theme-wrapper.test.tsx.snap | 40 ++- .../theme-wrapper/theme-wrapper.tsx | 15 +- .../toast/toast-container.test.tsx.snap | 8 + .../src/components/toast/toast-container.tsx | 14 +- .../src/components/toast/toasts-container.tsx | 4 +- .../toggle-switch/toggle-switch.tsx | 8 +- .../toggletip/toggletip.test.tsx.snap | 8 + .../react/src/components/tooltip/tooltip.tsx | 2 +- .../user-profile/user-profile.test.tsx.snap | 16 +- packages/react/src/hooks/use-theme.ts | 4 +- packages/react/src/index.ts | 3 +- .../breakpoints.ts | 0 .../menuDimensions.ts | 0 .../react/src/styled-components-theme.d.ts | 4 +- packages/react/src/theme.ts | 2 +- packages/react/src/themes/default-theme.ts | 8 + packages/react/src/themes/equisoft.ts | 16 +- packages/react/src/themes/greys.ts | 12 - packages/react/src/themes/index.ts | 2 +- packages/react/src/themes/main.ts | 13 - packages/react/src/themes/merge-theme.test.ts | 186 ++++++++++ packages/react/src/themes/merge-theme.ts | 80 +++++ packages/react/src/themes/notifications.ts | 16 - packages/react/src/themes/test-theme.ts | 21 -- packages/react/src/themes/theme.ts | 24 +- packages/react/src/themes/tokens.ts | 12 - .../react/src/themes/tokens/alias-tokens.ts | 15 + .../src/themes/tokens/component-tokens.ts | 31 ++ .../themes/tokens/component/button-tokens.ts | 322 ++++++++++++++++++ .../themes/tokens/component/focus-tokens.ts | 27 ++ .../themes/tokens/component/heading-tokens.ts | 17 + .../themes/tokens/component/label-tokens.ts | 15 + packages/react/src/themes/tokens/index.ts | 3 + .../react/src/themes/tokens/legacy-tokens.ts | 49 +++ .../react/src/themes/tokens/ref-tokens.ts | 23 ++ .../themes/tokens/ref/color-utility-tokens.ts | 30 ++ .../src/themes/tokens/ref/palette-tokens.ts | 96 ++++++ .../tokens/ref/text-attributes-tokens.ts | 12 + packages/react/src/utility-types.ts | 1 + packages/react/src/utils/css-state.ts | 36 +- .../storybook/stories/0-intro.stories.tsx | 183 +++++++++- 89 files changed, 1570 insertions(+), 508 deletions(-) rename packages/react/src/{tokens => legacy-constants}/breakpoints.ts (100%) rename packages/react/src/{tokens => legacy-constants}/menuDimensions.ts (100%) create mode 100644 packages/react/src/themes/default-theme.ts delete mode 100644 packages/react/src/themes/greys.ts delete mode 100644 packages/react/src/themes/main.ts create mode 100644 packages/react/src/themes/merge-theme.test.ts create mode 100644 packages/react/src/themes/merge-theme.ts delete mode 100644 packages/react/src/themes/notifications.ts delete mode 100644 packages/react/src/themes/test-theme.ts delete mode 100644 packages/react/src/themes/tokens.ts create mode 100644 packages/react/src/themes/tokens/alias-tokens.ts create mode 100644 packages/react/src/themes/tokens/component-tokens.ts create mode 100644 packages/react/src/themes/tokens/component/button-tokens.ts create mode 100644 packages/react/src/themes/tokens/component/focus-tokens.ts create mode 100644 packages/react/src/themes/tokens/component/heading-tokens.ts create mode 100644 packages/react/src/themes/tokens/component/label-tokens.ts create mode 100644 packages/react/src/themes/tokens/index.ts create mode 100644 packages/react/src/themes/tokens/legacy-tokens.ts create mode 100644 packages/react/src/themes/tokens/ref-tokens.ts create mode 100644 packages/react/src/themes/tokens/ref/color-utility-tokens.ts create mode 100644 packages/react/src/themes/tokens/ref/palette-tokens.ts create mode 100644 packages/react/src/themes/tokens/ref/text-attributes-tokens.ts create mode 100644 packages/react/src/utility-types.ts diff --git a/packages/react/.eslintrc.js b/packages/react/.eslintrc.js index 525af404c6..30a4f9cea3 100644 --- a/packages/react/.eslintrc.js +++ b/packages/react/.eslintrc.js @@ -11,5 +11,5 @@ module.exports = { 'react/jsx-no-bind': 'off', 'react/jsx-uses-react': 'off', 'react/react-in-jsx-scope': 'off', - } + }, }; diff --git a/packages/react/src/components/accordion/accordion-item.tsx b/packages/react/src/components/accordion/accordion-item.tsx index 46e94b97c4..3631b5e0b1 100644 --- a/packages/react/src/components/accordion/accordion-item.tsx +++ b/packages/react/src/components/accordion/accordion-item.tsx @@ -1,6 +1,6 @@ import React from 'react'; import styled from 'styled-components'; -import { Theme } from '../../themes'; +import { ResolvedTheme } from '../../themes/theme'; import { Button } from '../buttons/button'; import { Icon } from '../icon/icon'; import { Heading, Type, Tag } from '../heading/heading'; @@ -18,7 +18,7 @@ export interface AccordionItemProps { buttonRef?: React.RefObject | undefined; } -const AccordionSection = styled.section<{ theme: Theme }>` +const AccordionSection = styled.section<{ theme: ResolvedTheme }>` background: ${({ theme }) => theme.greys['colored-white']}; border-color: ${({ theme }) => theme.greys.grey}; border-radius: 0 0 var(--border-radius-2x) var(--border-radius-2x); @@ -38,7 +38,7 @@ const AccordionSection = styled.section<{ theme: Theme }>` } `; -const AccordionBody = styled.div<{ theme: Theme }>` +const AccordionBody = styled.div<{ theme: ResolvedTheme }>` background: ${({ theme }) => theme.greys['colored-white']}; color: ${({ theme }) => theme.greys['neutral-90']}; font-size: 0.75rem; @@ -60,7 +60,7 @@ const HeadingStyled = styled(Heading)` position: relative; `; -const ButtonStyled = styled(Button)<{ theme: Theme }>` +const ButtonStyled = styled(Button)<{ theme: ResolvedTheme }>` align-items: flex-start; border: 1px solid ${({ theme }) => theme.greys.grey}; border-radius: var(--border-radius-2x); diff --git a/packages/react/src/components/avatar/avatar.tsx b/packages/react/src/components/avatar/avatar.tsx index b8b8024127..09e642cc88 100644 --- a/packages/react/src/components/avatar/avatar.tsx +++ b/packages/react/src/components/avatar/avatar.tsx @@ -1,7 +1,7 @@ import { useMemo, VoidFunctionComponent } from 'react'; import styled, { css, FlattenInterpolation, ThemeProps } from 'styled-components'; import { useTranslation } from '../../i18n/use-translation'; -import { Theme } from '../../themes'; +import { ResolvedTheme } from '../../themes/theme'; import { getInitialsFromUsername } from '../../utils/user'; import { useDeviceContext } from '../device-context-provider/device-context-provider'; import { Icon } from '../icon/icon'; @@ -21,7 +21,7 @@ interface SizeStyleProps { isMobile: boolean; } -function getSpecificSizeStyle({ size, isMobile }: SizeStyleProps): FlattenInterpolation> { +function getSpecificSizeStyle({ size, isMobile }: SizeStyleProps): FlattenInterpolation> { switch (size) { case 'xsmall': return css` diff --git a/packages/react/src/components/bar/bar.tsx b/packages/react/src/components/bar/bar.tsx index 296c711b5b..9e9268320f 100644 --- a/packages/react/src/components/bar/bar.tsx +++ b/packages/react/src/components/bar/bar.tsx @@ -1,10 +1,10 @@ import { ReactText, VoidFunctionComponent } from 'react'; import styled from 'styled-components'; -import { Theme } from '../../themes'; +import { ResolvedTheme } from '../../themes/theme'; const Container = styled.div` p { - color: ${(props: { theme: Theme }) => props.theme.greys.black}; + color: ${(props: { theme: ResolvedTheme }) => props.theme.greys.black}; letter-spacing: 0.02875rem; line-height: 1.5rem; margin: 0; diff --git a/packages/react/src/components/bento-menu-button/bento-menu-button.test.tsx.snap b/packages/react/src/components/bento-menu-button/bento-menu-button.test.tsx.snap index d541e44686..6e5921b0dc 100644 --- a/packages/react/src/components/bento-menu-button/bento-menu-button.test.tsx.snap +++ b/packages/react/src/components/bento-menu-button/bento-menu-button.test.tsx.snap @@ -78,12 +78,14 @@ exports[`BentoMenuButton Matches Snapshot (productGroups and externalLinks) 1`] .c3:hover, .c3[aria-expanded='true'] { background-color: #003A5A; + border-color: transparent; color: #FFFFFF; } .c3:disabled { background-color: transparent; - color: #003A5A; + border-color: transparent; + color: #006296; } .c3 > svg { @@ -287,6 +289,7 @@ exports[`BentoMenuButton Matches Snapshot (productGroups and externalLinks) 1`] } .c7 { + color: #000000; font-size: 1rem; font-weight: var(--font-semi-bold); line-height: 1.5rem; @@ -906,12 +909,14 @@ exports[`BentoMenuButton Matches Snapshot (productLinks and externalLinks) 1`] = .c3:hover, .c3[aria-expanded='true'] { background-color: #003A5A; + border-color: transparent; color: #FFFFFF; } .c3:disabled { background-color: transparent; - color: #003A5A; + border-color: transparent; + color: #006296; } .c3 > svg { @@ -1115,6 +1120,7 @@ exports[`BentoMenuButton Matches Snapshot (productLinks and externalLinks) 1`] = } .c7 { + color: #000000; font-size: 1rem; font-weight: var(--font-semi-bold); line-height: 1.5rem; @@ -1646,12 +1652,14 @@ exports[`BentoMenuButton Matches Snapshot (tag="nav") 1`] = ` .c3:hover, .c3[aria-expanded='true'] { background-color: #003A5A; + border-color: transparent; color: #FFFFFF; } .c3:disabled { background-color: transparent; - color: #003A5A; + border-color: transparent; + color: #006296; } .c3 > svg { @@ -1855,6 +1863,7 @@ exports[`BentoMenuButton Matches Snapshot (tag="nav") 1`] = ` } .c7 { + color: #000000; font-size: 1rem; font-weight: var(--font-semi-bold); line-height: 1.5rem; diff --git a/packages/react/src/components/buttons/abstract-button.tsx b/packages/react/src/components/buttons/abstract-button.tsx index d759abd2a7..ca2e798724 100755 --- a/packages/react/src/components/buttons/abstract-button.tsx +++ b/packages/react/src/components/buttons/abstract-button.tsx @@ -1,6 +1,6 @@ import { ButtonHTMLAttributes, forwardRef, PropsWithChildren, Ref } from 'react'; import styled, { css, FlattenInterpolation, ThemeProps } from 'styled-components'; -import { Theme } from '../../themes'; +import { ResolvedTheme } from '../../themes/theme'; import { focus } from '../../utils/css-state'; type Size = 'small' | 'medium'; @@ -80,149 +80,46 @@ export type ButtonType = 'primary' | 'secondary' | 'tertiary' | 'destructive' | export interface ButtonTypeStyles { buttonType: ButtonType; inverted?: boolean; - theme: Theme; + theme: ResolvedTheme; } -const getPrimaryButtonStyles: (props: ButtonTypeStyles) => FlattenInterpolation> = ({ +const getButtonStyles: (props: ButtonTypeStyles) => FlattenInterpolation> = ({ inverted, + buttonType, theme, -}) => css` - background-color: ${inverted ? theme.greys.white : theme.main['primary-1.1']}; - border-color: ${inverted ? theme.greys.white : theme.main['primary-1.1']}; - color: ${inverted ? theme.main['primary-1.1'] : theme.greys.white}; - - &:hover, - &[aria-expanded='true'] { - background-color: ${inverted ? theme.greys.white : theme.main['primary-1.3']}; - border-color: ${inverted ? theme.greys.white : theme.main['primary-1.3']}; - ${inverted && `color: ${theme.main['primary-1.3']}`} - } - - &:disabled { - background-color: ${inverted ? theme.greys.white : theme.main['primary-1.2']}; - border-color: ${inverted ? theme.greys.white : theme.main['primary-1.2']}; - ${inverted && `color: ${theme.main['primary-1.2']}`} - } -`; - -const getSecondaryButtonStyles: (props: ButtonTypeStyles) => FlattenInterpolation> = ({ - inverted, - theme, -}) => css` - background-color: ${inverted ? 'transparent' : theme.greys.white}; - border-color: ${inverted ? theme.greys.white : theme.main['primary-1.1']}; - color: ${inverted ? theme.greys.white : theme.main['primary-1.1']}; - - &:hover, - &[aria-expanded='true'] { - border-color: ${inverted ? theme.main['primary-1.2'] : theme.main['primary-1.3']}; - color: ${inverted ? theme.main['primary-1.2'] : theme.main['primary-1.3']}; - } - - &:disabled { - border-color: ${inverted ? theme.main['primary-1.3'] : theme.main['primary-1.2']}; - color: ${inverted ? theme.main['primary-1.3'] : theme.main['primary-1.2']}; - } - - ${inverted && `&:focus { - background-color: ${theme.main['primary-2']}; - border-color: ${theme.main['primary-1.1']} - color: ${theme.greys.white}; - }`} -`; - -const getTertiaryButtonStyles: (props: ButtonTypeStyles) => FlattenInterpolation> = ({ - inverted, - theme, -}) => css` - background-color: transparent; - border-color: transparent; - color: ${inverted ? theme.greys.white : theme.greys['dark-grey']}; - - &:hover, - &[aria-expanded='true'] { - background-color: ${inverted ? theme.main['primary-1.3'] : theme.greys.grey}; - color: ${inverted ? theme.greys.white : theme.greys['neutral-90']}; - } - - &:disabled { - background-color: transparent; - color: ${inverted ? theme.main['primary-1.3'] : theme.greys['mid-grey']}; - } -`; - -const getDestructiveButtonStyles: (props: ButtonTypeStyles) => FlattenInterpolation> = ({ - inverted, - theme, -}) => css` - background-color: ${inverted ? theme.greys.white : theme.notifications['alert-2.1']}; - border-color: ${inverted ? theme.greys.white : theme.notifications['alert-2.1']}; - color: ${inverted ? theme.notifications['alert-2.1'] : theme.greys.white}; - - &:hover, - &[aria-expanded='true'] { - /* TODO change colors when updating thematization */ - background-color: ${inverted ? theme.greys.white : '#7B1A15'}; - border-color: ${inverted ? theme.greys.white : '#7B1A15'}; - color: ${inverted ? '#7B1A15' : theme.greys.white}; - } - - &:disabled { - &, - &:focus, - &:hover { - /* TODO change colors when updating thematization */ - background-color: ${inverted ? theme.greys.white : '#F99D99'}; - border-color: ${inverted ? theme.greys.white : '#F99D99'}; - color: ${inverted ? '#F99D99' : theme.greys.white}; +}) => { + const inversionSuffix = inverted ? '-inverted' : ''; + + return css` + background-color: ${theme.component[`button-${buttonType}${inversionSuffix}-background-color`]}; + border-color: ${theme.component[`button-${buttonType}${inversionSuffix}-border-color`]}; + color: ${theme.component[`button-${buttonType}${inversionSuffix}-text-color`]}; + + &:hover, + &[aria-expanded='true'] { + background-color: ${theme.component[`button-${buttonType}${inversionSuffix}-hover-background-color`]}; + border-color: ${theme.component[`button-${buttonType}${inversionSuffix}-hover-border-color`]}; + color: ${theme.component[`button-${buttonType}${inversionSuffix}-hover-text-color`]}; } - } -`; -const getDestructiveSecondaryButtonStyles: (props: ButtonTypeStyles) => FlattenInterpolation> = ({ - inverted, - theme, -}) => css` - background-color: ${inverted ? 'transparent' : theme.greys.white}; - border-color: ${inverted ? theme.greys.white : theme.notifications['alert-2.1']}; - color: ${inverted ? theme.greys.white : theme.notifications['alert-2.1']}; - - &:hover, - &[aria-expanded='true'] { - /* TODO change colors when updating thematization */ - background-color: ${inverted ? '#7B1A15' : theme.greys.white}; - border-color: ${inverted ? theme.greys.white : '#7B1A15'}; - color: ${inverted ? theme.greys.white : '#7B1A15'}; - } - - &:disabled { - color: ${inverted ? theme.greys.white : '#F99D99'}; - - &, - &:focus, - &:hover { - /* TODO change colors when updating thematization */ - background-color: ${inverted ? '#F99D99' : theme.greys.white}; - border-color: ${inverted ? theme.greys.white : '#F99D99'}; - color: ${inverted ? theme.greys.white : '#F99D99'}; + &:disabled { + background-color: ${theme.component[`button-${buttonType}${inversionSuffix}-disabled-background-color`]}; + border-color: ${theme.component[`button-${buttonType}${inversionSuffix}-disabled-border-color`]}; + color: ${theme.component[`button-${buttonType}${inversionSuffix}-disabled-text-color`]}; + ${buttonType === 'destructive' ? css` + &, + &:focus, + &:hover { + background-color: ${theme.component[`button-${buttonType}${inversionSuffix}-disabled-background-color`]}; + border-color: ${theme.component[`button-${buttonType}${inversionSuffix}-disabled-border-color`]}; + color: ${theme.component[`button-${buttonType}${inversionSuffix}-disabled-text-color`]}; + } +` : ''} } - } `; +}; -export const getButtonTypeStyles: (props: ButtonTypeStyles) => FlattenInterpolation> = (props) => css` +export const getButtonTypeStyles: (props: ButtonTypeStyles) => FlattenInterpolation> = (props) => css` ${focus(props, true)}; - ${() => { - switch (props.buttonType) { - case 'primary': - return getPrimaryButtonStyles(props); - case 'secondary': - return getSecondaryButtonStyles(props); - case 'tertiary': - return getTertiaryButtonStyles(props); - case 'destructive': - return getDestructiveButtonStyles(props); - case 'destructive-secondary': - return getDestructiveSecondaryButtonStyles(props); - } - }} + ${getButtonStyles(props)}; `; diff --git a/packages/react/src/components/buttons/button.test.tsx b/packages/react/src/components/buttons/button.test.tsx index d055d88a2d..1a733dff85 100644 --- a/packages/react/src/components/buttons/button.test.tsx +++ b/packages/react/src/components/buttons/button.test.tsx @@ -28,7 +28,7 @@ describe('Button', () => { expect(callback).toHaveBeenCalledTimes(0); }); - test('has disabled styles', () => { + test('has primary disabled styles', () => { const tree = renderWithProviders( `; -exports[`Button has mobile styles 1`] = ` +exports[`Button has primary disabled styles 1`] = ` .c0 { -webkit-align-items: center; -webkit-box-align: center; @@ -500,21 +513,21 @@ exports[`Button has mobile styles 1`] = ` display: -ms-inline-flexbox; display: inline-flex; font-family: inherit; - font-size: 0.875rem; + font-size: 0.75rem; font-weight: var(--font-bold); -webkit-box-pack: center; -webkit-justify-content: center; -ms-flex-pack: center; justify-content: center; - -webkit-letter-spacing: 0.033125rem; - -moz-letter-spacing: 0.033125rem; - -ms-letter-spacing: 0.033125rem; - letter-spacing: 0.033125rem; - line-height: 1.5rem; - min-height: var(--size-3x); + -webkit-letter-spacing: 0.025rem; + -moz-letter-spacing: 0.025rem; + -ms-letter-spacing: 0.025rem; + letter-spacing: 0.025rem; + line-height: 1rem; + min-height: var(--size-2x); min-width: 2rem; outline: none; - padding: 0 var(--spacing-3x); + padding: 0 var(--spacing-2x); text-transform: uppercase; -webkit-user-select: none; -moz-user-select: none; @@ -534,8 +547,8 @@ exports[`Button has mobile styles 1`] = ` .c0 > svg { color: inherit; - height: var(--size-1halfx); - width: var(--size-1halfx); + height: var(--size-1x); + width: var(--size-1x); } .c1 { @@ -558,15 +571,18 @@ exports[`Button has mobile styles 1`] = ` .c1[aria-expanded='true'] { background-color: #003A5A; border-color: #003A5A; + color: #FFFFFF; } .c1:disabled { background-color: #84C6EA; border-color: #84C6EA; + color: #FFFFFF; }