From 8932c60111b24b90d9d1014266095dc9d157f41e Mon Sep 17 00:00:00 2001 From: Josh <44883293+josh-bagwell@users.noreply.github.com> Date: Wed, 15 May 2024 13:39:50 -0600 Subject: [PATCH 1/7] chore: Examples Updated to Remove Style Props (#2726) Fixes: #2725 Removes style props from examples of components that have been updated in v11 to use new style utils. [category:Documentation] --- .../form-field/stories/examples/AllFields.tsx | 18 +++++---- .../stories/examples/RefForwarding.tsx | 10 ++++- .../stories/examples/ThemedErrors.tsx | 10 ++++- .../radio/stories/examples/Basic.tsx | 7 +++- .../radio/stories/examples/Custom.tsx | 25 ++++++++++-- .../radio/stories/examples/Inverse.tsx | 26 ++++++++----- .../stories/examples/Emphasis.tsx | 8 +++- .../stories/examples/Icon.tsx | 8 +++- .../stories/examples/Overflow.tsx | 8 +++- .../stories/examples/Variants.tsx | 18 +++++++-- .../button/stories/button/examples/Delete.tsx | 9 ++++- .../examples/ExternalHyperlinkInverse.tsx | 9 ++++- .../button/examples/HyperlinkInverse.tsx | 9 ++++- .../stories/button/examples/Primary.tsx | 9 ++++- .../button/examples/PrimaryInverse.tsx | 10 ++++- .../stories/button/examples/Secondary.tsx | 9 ++++- .../button/examples/SecondaryInverse.tsx | 10 ++++- .../stories/button/examples/Tertiary.tsx | 9 ++++- .../button/examples/TertiaryInverse.tsx | 11 +++++- .../stories/examples/Indeterminate.tsx | 9 ++++- .../checkbox/stories/examples/Inverse.tsx | 10 ++++- .../stories/examples/Autocomplete.tsx | 24 ++++++++++-- .../icon/stories/examples/AccentIconList.tsx | 33 +++++++++++----- .../icon/stories/examples/AppletIconList.tsx | 33 +++++++++++----- .../react/icon/stories/examples/IconList.tsx | 34 ++++++++++++----- .../react/icon/stories/examples/Overview.tsx | 17 ++++++++- .../stories/examples/Accessible.tsx | 19 ++++++---- .../react/select/stories/examples/Alert.tsx | 7 +++- .../react/select/stories/examples/Basic.tsx | 7 +++- .../react/select/stories/examples/Complex.tsx | 7 +++- .../select/stories/examples/Disabled.tsx | 7 +++- .../stories/examples/DisabledOption.tsx | 7 +++- .../react/select/stories/examples/Error.tsx | 7 +++- .../stories/examples/FetchingDynamicItems.tsx | 8 +++- .../stories/examples/InitialSelectedItem.tsx | 7 +++- .../select/stories/examples/MenuHeight.tsx | 7 +++- .../select/stories/examples/Placeholder.tsx | 7 +++- .../select/stories/examples/Required.tsx | 7 +++- .../select/stories/examples/WithIcons.tsx | 14 ++++++- .../react/table/stories/examples/Basic.tsx | 10 ++++- .../stories/examples/BasicWithHeading.tsx | 17 +++++++-- .../table/stories/examples/FixedColumn.tsx | 38 ++++++++++--------- .../table/stories/examples/RightToLeft.tsx | 10 ++++- 43 files changed, 446 insertions(+), 123 deletions(-) diff --git a/modules/preview-react/form-field/stories/examples/AllFields.tsx b/modules/preview-react/form-field/stories/examples/AllFields.tsx index fb8376c5f2..6251291a48 100644 --- a/modules/preview-react/form-field/stories/examples/AllFields.tsx +++ b/modules/preview-react/form-field/stories/examples/AllFields.tsx @@ -7,17 +7,19 @@ import {Checkbox} from '@workday/canvas-kit-react/checkbox'; import {Select} from '@workday/canvas-kit-react/select'; import {TextArea} from '@workday/canvas-kit-react/text-area'; import {Switch} from '@workday/canvas-kit-react/switch'; +import {calc, createStyles} from '@workday/canvas-kit-styling'; +import {system} from '@workday/canvas-tokens-web'; + +const parentContainerStyles = createStyles({ + flexDirection: 'column', + gap: calc.subtract(system.space.x6, system.space.x1), + padding: calc.subtract(system.space.x10, system.space.x1), + borderRadius: system.space.x1, +}); export const AllFields = () => { return ( - + First Name diff --git a/modules/preview-react/form-field/stories/examples/RefForwarding.tsx b/modules/preview-react/form-field/stories/examples/RefForwarding.tsx index f86bd9fe38..e8be6a685f 100644 --- a/modules/preview-react/form-field/stories/examples/RefForwarding.tsx +++ b/modules/preview-react/form-field/stories/examples/RefForwarding.tsx @@ -4,6 +4,14 @@ import {Flex} from '@workday/canvas-kit-react/layout'; import {SecondaryButton} from '@workday/canvas-kit-react/button'; import {TextInput} from '@workday/canvas-kit-react/text-input'; import {FormField} from '@workday/canvas-kit-preview-react/form-field'; +import {createStyles} from '@workday/canvas-kit-styling'; +import {system} from '@workday/canvas-tokens-web'; + +const parentContainerStyles = createStyles({ + gap: system.space.x1, + alignItems: 'flex-start', + flexDirection: 'column', +}); export const RefForwarding = () => { const [value, setValue] = React.useState(''); @@ -18,7 +26,7 @@ export const RefForwarding = () => { }; return ( - + Email diff --git a/modules/preview-react/form-field/stories/examples/ThemedErrors.tsx b/modules/preview-react/form-field/stories/examples/ThemedErrors.tsx index 1e15ab38a4..5a4ecc0ef4 100644 --- a/modules/preview-react/form-field/stories/examples/ThemedErrors.tsx +++ b/modules/preview-react/form-field/stories/examples/ThemedErrors.tsx @@ -3,6 +3,12 @@ import {TextInput} from '@workday/canvas-kit-react/text-input'; import {FormField} from '@workday/canvas-kit-preview-react/form-field'; import {CanvasProvider, PartialEmotionCanvasTheme} from '@workday/canvas-kit-react/common'; import {colors, space} from '@workday/canvas-kit-react/tokens'; +import {createStyles} from '@workday/canvas-kit-styling'; +import {system} from '@workday/canvas-tokens-web'; + +const formFieldHintStyles = createStyles({ + paddingTop: system.space.x2, +}); export const ThemedError = () => { const [value, setValue] = React.useState(''); @@ -29,7 +35,9 @@ export const ThemedError = () => { Email - {!value && 'Please enter an email.'} + + {!value && 'Please enter an email.'} + ); diff --git a/modules/preview-react/radio/stories/examples/Basic.tsx b/modules/preview-react/radio/stories/examples/Basic.tsx index e09b4be6f8..7082faa43a 100644 --- a/modules/preview-react/radio/stories/examples/Basic.tsx +++ b/modules/preview-react/radio/stories/examples/Basic.tsx @@ -2,6 +2,11 @@ import React from 'react'; import {FormField} from '@workday/canvas-kit-preview-react/form-field'; import {RadioGroup} from '@workday/canvas-kit-preview-react/radio'; import {Flex} from '@workday/canvas-kit-react/layout'; +import {createStyles, px2rem} from '@workday/canvas-kit-styling'; + +const formfieldInputStyles = createStyles({ + width: px2rem(200), +}); export const Basic = () => { const [value, setValue] = React.useState('deep-dish'); @@ -18,10 +23,10 @@ export const Basic = () => { Choose Your Pizza Crust Deep dish diff --git a/modules/preview-react/radio/stories/examples/Custom.tsx b/modules/preview-react/radio/stories/examples/Custom.tsx index 74df7932ca..7910f9e901 100644 --- a/modules/preview-react/radio/stories/examples/Custom.tsx +++ b/modules/preview-react/radio/stories/examples/Custom.tsx @@ -2,6 +2,17 @@ import React from 'react'; import {FormField} from '@workday/canvas-kit-preview-react/form-field'; import {RadioGroup} from '@workday/canvas-kit-preview-react/radio'; import {Flex} from '@workday/canvas-kit-react/layout'; +import {createStyles, px2rem} from '@workday/canvas-kit-styling'; +import {base} from '@workday/canvas-tokens-web'; + +const styleOverrides = { + formfieldInputStyles: createStyles({ + width: px2rem(200), + }), + radioGroupLabelTextStyles: createStyles({ + color: base.berrySmoothie400, + }), +}; export const Custom = () => { const [value, setValue] = React.useState('deep-dish'); @@ -21,20 +32,26 @@ export const Custom = () => { as={RadioGroup} name="pizza-crust-custom" onChange={handleChange} - width="200px" + cs={styleOverrides.formfieldInputStyles} value={value} > - Deep dish + + Deep dish + - Gluten free + + Gluten free + - Cauliflower + + Cauliflower + diff --git a/modules/preview-react/radio/stories/examples/Inverse.tsx b/modules/preview-react/radio/stories/examples/Inverse.tsx index 2405e731a3..2587803a99 100644 --- a/modules/preview-react/radio/stories/examples/Inverse.tsx +++ b/modules/preview-react/radio/stories/examples/Inverse.tsx @@ -2,14 +2,20 @@ import React from 'react'; import {Box} from '@workday/canvas-kit-react/layout'; import {RadioGroup} from '@workday/canvas-kit-preview-react/radio'; import {FormField} from '@workday/canvas-kit-preview-react/form-field'; -import {styled, StyledType} from '@workday/canvas-kit-react/common'; -import {colors} from '@workday/canvas-kit-react/tokens'; +import {createStyles} from '@workday/canvas-kit-styling'; +import {system} from '@workday/canvas-tokens-web'; -const StyledFormField = styled(FormField)({ - legend: { - color: colors.frenchVanilla100, - }, -}); +const styleOverrides = { + containerStyles: createStyles({ + backgroundColor: system.color.bg.primary.default, + padding: system.space.x4, + }), + formFieldStyles: createStyles({ + legend: { + color: system.color.text.inverse, + }, + }), +}; export const Inverse = () => { const [value, setValue] = React.useState('deep-dish'); @@ -22,8 +28,8 @@ export const Inverse = () => { }; return ( - - + + Choose Your Pizza Crust @@ -39,7 +45,7 @@ export const Inverse = () => { Cauliflower - + ); }; diff --git a/modules/preview-react/status-indicator/stories/examples/Emphasis.tsx b/modules/preview-react/status-indicator/stories/examples/Emphasis.tsx index 7a1774afb0..4baedca4a9 100644 --- a/modules/preview-react/status-indicator/stories/examples/Emphasis.tsx +++ b/modules/preview-react/status-indicator/stories/examples/Emphasis.tsx @@ -3,10 +3,16 @@ import React from 'react'; import {StatusIndicator} from '@workday/canvas-kit-preview-react/status-indicator'; import {uploadCloudIcon} from '@workday/canvas-system-icons-web'; import {Flex} from '@workday/canvas-kit-react/layout'; +import {createStyles} from '@workday/canvas-kit-styling'; +import {system} from '@workday/canvas-tokens-web'; + +const parentContainerStyles = createStyles({ + gap: system.space.x4, +}); export const Emphasis = () => { return ( - + High Emphasis diff --git a/modules/preview-react/status-indicator/stories/examples/Icon.tsx b/modules/preview-react/status-indicator/stories/examples/Icon.tsx index 8c79898e11..84f599f862 100644 --- a/modules/preview-react/status-indicator/stories/examples/Icon.tsx +++ b/modules/preview-react/status-indicator/stories/examples/Icon.tsx @@ -3,10 +3,16 @@ import React from 'react'; import {StatusIndicator} from '@workday/canvas-kit-preview-react/status-indicator'; import {uploadCloudIcon} from '@workday/canvas-system-icons-web'; import {Flex} from '@workday/canvas-kit-react/layout'; +import {createStyles} from '@workday/canvas-kit-styling'; +import {system} from '@workday/canvas-tokens-web'; + +const parentContainerStyles = createStyles({ + gap: system.space.x4, +}); export const Icon = () => { return ( - + Unpublished diff --git a/modules/preview-react/status-indicator/stories/examples/Overflow.tsx b/modules/preview-react/status-indicator/stories/examples/Overflow.tsx index d54e3b9cfa..9704e43beb 100644 --- a/modules/preview-react/status-indicator/stories/examples/Overflow.tsx +++ b/modules/preview-react/status-indicator/stories/examples/Overflow.tsx @@ -3,11 +3,17 @@ import React from 'react'; import {StatusIndicator} from '@workday/canvas-kit-preview-react/status-indicator'; import {uploadCloudIcon} from '@workday/canvas-system-icons-web'; import {OverflowTooltip} from '@workday/canvas-kit-react/tooltip'; +import {calc, createStyles} from '@workday/canvas-kit-styling'; +import {system} from '@workday/canvas-tokens-web'; + +const statusIndicatorStyles = createStyles({ + maxWidth: calc.add(system.space.x20, system.space.x4), +}); export const Overflow = () => { return ( - + Your workbook is currently in process of saving diff --git a/modules/preview-react/status-indicator/stories/examples/Variants.tsx b/modules/preview-react/status-indicator/stories/examples/Variants.tsx index 962a6b5c0c..1c52e9eeaa 100644 --- a/modules/preview-react/status-indicator/stories/examples/Variants.tsx +++ b/modules/preview-react/status-indicator/stories/examples/Variants.tsx @@ -3,11 +3,23 @@ import React from 'react'; import {StatusIndicator} from '@workday/canvas-kit-preview-react/status-indicator'; import {uploadCloudIcon} from '@workday/canvas-system-icons-web'; import {Flex} from '@workday/canvas-kit-react/layout'; +import {createStyles} from '@workday/canvas-kit-styling'; +import {system} from '@workday/canvas-tokens-web'; + +const styleOverrides = { + parentContainerStyles: createStyles({ + gap: system.space.x4, + flexDirection: 'column', + }), + innerContainerStyles: createStyles({ + gap: system.space.x4, + }), +}; export const Variants = () => { return ( - - + + Lorem ipsum dolor @@ -33,7 +45,7 @@ export const Variants = () => { - + Lorem ipsum dolor diff --git a/modules/react/button/stories/button/examples/Delete.tsx b/modules/react/button/stories/button/examples/Delete.tsx index 1d11e34ed8..0d71a82204 100644 --- a/modules/react/button/stories/button/examples/Delete.tsx +++ b/modules/react/button/stories/button/examples/Delete.tsx @@ -3,9 +3,16 @@ import React from 'react'; import {DeleteButton} from '@workday/canvas-kit-react/button'; import {Flex} from '@workday/canvas-kit-react/layout'; import {trashIcon} from '@workday/canvas-system-icons-web'; +import {createStyles} from '@workday/canvas-kit-styling'; +import {system} from '@workday/canvas-tokens-web'; + +const parentContainerStyles = createStyles({ + gap: system.space.x4, + padding: system.space.x4, +}); export const Delete = () => ( - + Delete Delete diff --git a/modules/react/button/stories/button/examples/ExternalHyperlinkInverse.tsx b/modules/react/button/stories/button/examples/ExternalHyperlinkInverse.tsx index 7ee9aef6f0..77717e73b8 100644 --- a/modules/react/button/stories/button/examples/ExternalHyperlinkInverse.tsx +++ b/modules/react/button/stories/button/examples/ExternalHyperlinkInverse.tsx @@ -2,9 +2,16 @@ import React from 'react'; import {ExternalHyperlink} from '@workday/canvas-kit-react/button'; import {Box} from '@workday/canvas-kit-react/layout'; +import {createStyles} from '@workday/canvas-kit-styling'; +import {system} from '@workday/canvas-tokens-web'; + +const parentContainerStyles = createStyles({ + backgroundColor: system.color.bg.primary.default, + padding: system.space.x4, +}); export const ExternalLinkInverse = () => ( - + Hyperlink diff --git a/modules/react/button/stories/button/examples/HyperlinkInverse.tsx b/modules/react/button/stories/button/examples/HyperlinkInverse.tsx index af0790264a..2e23875b11 100644 --- a/modules/react/button/stories/button/examples/HyperlinkInverse.tsx +++ b/modules/react/button/stories/button/examples/HyperlinkInverse.tsx @@ -2,9 +2,16 @@ import React from 'react'; import {Hyperlink} from '@workday/canvas-kit-react/button'; import {Box} from '@workday/canvas-kit-react/layout'; +import {createStyles} from '@workday/canvas-kit-styling'; +import {system} from '@workday/canvas-tokens-web'; + +const parentContainerStyles = createStyles({ + backgroundColor: system.color.bg.primary.default, + padding: system.space.x4, +}); export const LinkInverse = () => ( - + Hyperlink diff --git a/modules/react/button/stories/button/examples/Primary.tsx b/modules/react/button/stories/button/examples/Primary.tsx index ede4ef219f..f7597c68d0 100644 --- a/modules/react/button/stories/button/examples/Primary.tsx +++ b/modules/react/button/stories/button/examples/Primary.tsx @@ -7,9 +7,16 @@ import { relatedActionsVerticalIcon, caretDownIcon, } from '@workday/canvas-system-icons-web'; +import {createStyles} from '@workday/canvas-kit-styling'; +import {system} from '@workday/canvas-tokens-web'; + +const parentContainerStyles = createStyles({ + gap: system.space.x4, + padding: system.space.x4, +}); export const Primary = () => ( - + Primary Primary diff --git a/modules/react/button/stories/button/examples/PrimaryInverse.tsx b/modules/react/button/stories/button/examples/PrimaryInverse.tsx index c0589600b7..e8c25a4f76 100644 --- a/modules/react/button/stories/button/examples/PrimaryInverse.tsx +++ b/modules/react/button/stories/button/examples/PrimaryInverse.tsx @@ -7,9 +7,17 @@ import { relatedActionsVerticalIcon, caretDownIcon, } from '@workday/canvas-system-icons-web'; +import {createStyles} from '@workday/canvas-kit-styling'; +import {system} from '@workday/canvas-tokens-web'; + +const parentContainerStyles = createStyles({ + gap: system.space.x4, + backgroundColor: system.color.bg.primary.default, + padding: system.space.x4, +}); export const PrimaryInverse = () => ( - + Primary Primary diff --git a/modules/react/button/stories/button/examples/Secondary.tsx b/modules/react/button/stories/button/examples/Secondary.tsx index cdeafc8c8c..466454826a 100644 --- a/modules/react/button/stories/button/examples/Secondary.tsx +++ b/modules/react/button/stories/button/examples/Secondary.tsx @@ -7,9 +7,16 @@ import { relatedActionsVerticalIcon, caretDownIcon, } from '@workday/canvas-system-icons-web'; +import {createStyles} from '@workday/canvas-kit-styling'; +import {system} from '@workday/canvas-tokens-web'; + +const parentContainerStyles = createStyles({ + gap: system.space.x4, + padding: system.space.x4, +}); export const Secondary = () => ( - + Secondary Secondary diff --git a/modules/react/button/stories/button/examples/SecondaryInverse.tsx b/modules/react/button/stories/button/examples/SecondaryInverse.tsx index 4fa8bbc4fd..882471fa74 100644 --- a/modules/react/button/stories/button/examples/SecondaryInverse.tsx +++ b/modules/react/button/stories/button/examples/SecondaryInverse.tsx @@ -7,9 +7,17 @@ import { relatedActionsVerticalIcon, caretDownIcon, } from '@workday/canvas-system-icons-web'; +import {createStyles} from '@workday/canvas-kit-styling'; +import {system} from '@workday/canvas-tokens-web'; + +const parentContainerStyles = createStyles({ + gap: system.space.x4, + padding: system.space.x4, + backgroundColor: system.color.bg.primary.default, +}); export const SecondaryInverse = () => ( - + Secondary Secondary diff --git a/modules/react/button/stories/button/examples/Tertiary.tsx b/modules/react/button/stories/button/examples/Tertiary.tsx index 40504b4dfd..f2f8c3e7c2 100644 --- a/modules/react/button/stories/button/examples/Tertiary.tsx +++ b/modules/react/button/stories/button/examples/Tertiary.tsx @@ -7,9 +7,16 @@ import { relatedActionsVerticalIcon, caretDownIcon, } from '@workday/canvas-system-icons-web'; +import {createStyles} from '@workday/canvas-kit-styling'; +import {system} from '@workday/canvas-tokens-web'; + +const parentContainerStyles = createStyles({ + gap: system.space.x4, + padding: system.space.x4, +}); export const Tertiary = () => ( - + Tertiary Tertiary diff --git a/modules/react/button/stories/button/examples/TertiaryInverse.tsx b/modules/react/button/stories/button/examples/TertiaryInverse.tsx index d82dfc0e80..1cbbce4610 100644 --- a/modules/react/button/stories/button/examples/TertiaryInverse.tsx +++ b/modules/react/button/stories/button/examples/TertiaryInverse.tsx @@ -5,12 +5,19 @@ import {Flex} from '@workday/canvas-kit-react/layout'; import { plusIcon, relatedActionsVerticalIcon, - arrowRightIcon, caretDownIcon, } from '@workday/canvas-system-icons-web'; +import {createStyles} from '@workday/canvas-kit-styling'; +import {system} from '@workday/canvas-tokens-web'; + +const parentContainerStyles = createStyles({ + gap: system.space.x4, + padding: system.space.x4, + backgroundColor: system.color.bg.primary.default, +}); export const TertiaryInverse = () => ( - + Tertiary Tertiary diff --git a/modules/react/checkbox/stories/examples/Indeterminate.tsx b/modules/react/checkbox/stories/examples/Indeterminate.tsx index 27e776b163..042d0c90fe 100644 --- a/modules/react/checkbox/stories/examples/Indeterminate.tsx +++ b/modules/react/checkbox/stories/examples/Indeterminate.tsx @@ -1,6 +1,13 @@ import React from 'react'; import {Checkbox} from '@workday/canvas-kit-react/checkbox'; import {Box} from '@workday/canvas-kit-react/layout'; +import {createStyles} from '@workday/canvas-kit-styling'; +import {system} from '@workday/canvas-tokens-web'; + +const styleOverrides = createStyles({ + marginInlineLeft: system.space.x8, + marginTop: system.space.x2, +}); export const Indeterminate = () => { const [pizzaChecked, setPizzaChecked] = React.useState(false); @@ -58,7 +65,7 @@ export const Indeterminate = () => { label="Supreme Pizza Toppings" onChange={handlePizzaChange} /> - + {toppings.map((topping, index) => ( { const [checked, setChecked] = React.useState(false); @@ -10,7 +18,7 @@ export const Inverse = () => { }; return ( - + ; }); +const styleOverrides = { + inputGroupInner: createStyles({ + width: px2rem(20), + transition: 'opacity 100ms ease', + }), + loadingDots: createStyles({ + display: 'flex', + transform: 'scale(0.3)', + }), + comboboxMenuList: createStyles({ + maxHeight: px2rem(200), + }), +}; + export const Autocomplete = () => { const {model, loader} = useComboboxLoader( { @@ -95,12 +111,12 @@ export const Autocomplete = () => { - + @@ -112,7 +128,7 @@ export const Autocomplete = () => { No Results Found )} {model.state.items.length > 0 && ( - + {item => {item}} )} diff --git a/modules/react/icon/stories/examples/AccentIconList.tsx b/modules/react/icon/stories/examples/AccentIconList.tsx index 20cf7c5fe2..06dfa1b238 100644 --- a/modules/react/icon/stories/examples/AccentIconList.tsx +++ b/modules/react/icon/stories/examples/AccentIconList.tsx @@ -3,11 +3,31 @@ import * as CanvasAccenttIcons from '@workday/canvas-accent-icons-web'; import {Box, Flex} from '@workday/canvas-kit-react/layout'; import {AccentIcon} from '@workday/canvas-kit-react/icon'; import {TextInput} from '@workday/canvas-kit-react/text-input'; +import {createStyles, px2rem} from '@workday/canvas-kit-styling'; +import {system} from '@workday/canvas-tokens-web'; const ImportedIcons = Object.keys(CanvasAccenttIcons); const allIcons = ImportedIcons.filter(icon => icon !== 'CanvasAccenttIcons'); +const styleOverrides = { + parentContainer: createStyles({ + flexDirection: 'column', + alignItems: 'center', + gap: system.space.x6, + }), + iconGroupContainer: createStyles({ + flexWrap: 'wrap', + }), + individualIconContainer: createStyles({ + alignItems: 'center', + width: `max(${px2rem(320)},20%)`, + flexDirection: 'row', + gap: system.space.x3, + padding: system.space.x3, + }), +}; + export const AccentIconList = () => { const [value, setValue] = React.useState(''); @@ -16,9 +36,9 @@ export const AccentIconList = () => { }; return ( - + handleSearch(e)} placeholder="Search for an icon" /> - + {allIcons .filter(icon => { if (value === '') { @@ -29,14 +49,7 @@ export const AccentIconList = () => { }) .map((singleIcon, index) => { return ( - + diff --git a/modules/react/icon/stories/examples/AppletIconList.tsx b/modules/react/icon/stories/examples/AppletIconList.tsx index f8cac5d872..16b3f3624a 100644 --- a/modules/react/icon/stories/examples/AppletIconList.tsx +++ b/modules/react/icon/stories/examples/AppletIconList.tsx @@ -3,11 +3,31 @@ import * as CanvasAppletIcons from '@workday/canvas-applet-icons-web'; import {Box, Flex} from '@workday/canvas-kit-react/layout'; import {AppletIcon} from '@workday/canvas-kit-react/icon'; import {TextInput} from '@workday/canvas-kit-react/text-input'; +import {createStyles, px2rem} from '@workday/canvas-kit-styling'; +import {system} from '@workday/canvas-tokens-web'; const ImportedIcons = Object.keys(CanvasAppletIcons); const allIcons = ImportedIcons.filter(icon => icon !== 'CanvasAppletIcons'); +const styleOverrides = { + parentContainer: createStyles({ + flexDirection: 'column', + alignItems: 'center', + gap: system.space.x6, + }), + iconGroupContainer: createStyles({ + flexWrap: 'wrap', + }), + individualIconContainer: createStyles({ + alignItems: 'center', + width: `max(${px2rem(320)},20%)`, + flexDirection: 'row', + gap: system.space.x3, + padding: system.space.x3, + }), +}; + export const AppletIconList = () => { const [value, setValue] = React.useState(''); @@ -16,9 +36,9 @@ export const AppletIconList = () => { }; return ( - + handleSearch(e)} placeholder="Search for an icon" /> - + {allIcons .filter(icon => { if (value === '') { @@ -29,14 +49,7 @@ export const AppletIconList = () => { }) .map((singleIcon, index) => { return ( - + diff --git a/modules/react/icon/stories/examples/IconList.tsx b/modules/react/icon/stories/examples/IconList.tsx index 041383a6cd..f7cc6265a2 100644 --- a/modules/react/icon/stories/examples/IconList.tsx +++ b/modules/react/icon/stories/examples/IconList.tsx @@ -3,10 +3,31 @@ import * as CanvasIcons from '@workday/canvas-system-icons-web'; import {Box, Flex} from '@workday/canvas-kit-react/layout'; import {SystemIcon} from '@workday/canvas-kit-react/icon'; import {TextInput} from '@workday/canvas-kit-react/text-input'; +import {createStyles} from '@workday/canvas-kit-styling'; +import {system} from '@workday/canvas-tokens-web'; const ImportedIcons = Object.keys(CanvasIcons); const allIcons = ImportedIcons.filter(icon => icon !== 'CanvasSystemIcons'); + +const styleOverrides = { + parentContainer: createStyles({ + flexDirection: 'column', + alignItems: 'center', + gap: system.space.x6, + }), + firstChildContainer: createStyles({ + flexWrap: 'wrap', + }), + secondChildContainer: createStyles({ + alignItems: 'center', + width: `max(320px,20%)`, + flexDirection: 'row', + gap: system.space.x3, + padding: system.space.x3, + }), +}; + export const SystemIconList = () => { const [value, setValue] = React.useState(''); @@ -15,9 +36,9 @@ export const SystemIconList = () => { }; return ( - + handleSearch(e)} placeholder="Search for an icon" /> - + {allIcons .filter(icon => { if (value === '') { @@ -28,14 +49,7 @@ export const SystemIconList = () => { }) .map((singleIcon, index) => { return ( - + diff --git a/modules/react/icon/stories/examples/Overview.tsx b/modules/react/icon/stories/examples/Overview.tsx index 08efeb65ed..3a4c389fce 100644 --- a/modules/react/icon/stories/examples/Overview.tsx +++ b/modules/react/icon/stories/examples/Overview.tsx @@ -8,6 +8,8 @@ import {CanvasGraphic, CanvasIconTypes} from '@workday/design-assets-types'; import {AccentIcon, AppletIcon, SystemIcon, SystemIconCircle, Graphic} from '../../index'; import {activityStreamIcon} from '@workday/canvas-system-icons-web'; +import {createStyles} from '@workday/canvas-kit-styling'; +import {base, system} from '@workday/canvas-tokens-web'; const graphicExample: CanvasGraphic = { name: 'badgeAchievement', @@ -18,11 +20,22 @@ const graphicExample: CanvasGraphic = { tags: [], }; +const styleOverrides = { + container: createStyles({ + flexDirection: 'row', + alignItems: 'center', + gap: system.space.x4, + }), + systemIconStyles: createStyles({ + background: base.berrySmoothie100, + }), +}; + export const Overview = () => ( - + - + diff --git a/modules/react/loading-dots/stories/examples/Accessible.tsx b/modules/react/loading-dots/stories/examples/Accessible.tsx index 5583647cd6..375138aa21 100644 --- a/modules/react/loading-dots/stories/examples/Accessible.tsx +++ b/modules/react/loading-dots/stories/examples/Accessible.tsx @@ -3,13 +3,18 @@ import {LoadingDots} from '@workday/canvas-kit-react/loading-dots'; import {base, system} from '@workday/canvas-tokens-web'; import {Flex} from '@workday/canvas-kit-react/layout'; import {SecondaryButton} from '@workday/canvas-kit-react/button'; -import {createStyles, cssVar} from '@workday/canvas-kit-styling'; +import {createStyles} from '@workday/canvas-kit-styling'; import {AccessibleHide, AriaLiveRegion} from '@workday/canvas-kit-react/common'; -const loadingStyles = createStyles({ - backgroundColor: base.licorice300, - padding: system.space.x3, -}); +const styleOverrides = { + parentContainer: createStyles({ + gap: system.space.x4, + }), + loadingStyles: createStyles({ + backgroundColor: system.color.bg.muted.default, + padding: system.space.x3, + }), +}; export const Accessible = () => { const [loadingState, setLoadingState] = React.useState('idle'); @@ -31,11 +36,11 @@ export const Accessible = () => { }; return ( - + Start {loadingState === 'loading' && ( - + )} {loadingState === 'success' && Complete.} diff --git a/modules/react/select/stories/examples/Alert.tsx b/modules/react/select/stories/examples/Alert.tsx index f1b5595254..b1ecd30947 100644 --- a/modules/react/select/stories/examples/Alert.tsx +++ b/modules/react/select/stories/examples/Alert.tsx @@ -2,6 +2,11 @@ import React from 'react'; import {FormField} from '@workday/canvas-kit-preview-react/form-field'; import {Select} from '@workday/canvas-kit-react/select'; import {Flex} from '@workday/canvas-kit-react/layout'; +import {createStyles} from '@workday/canvas-kit-styling'; + +const parentContainerStyles = createStyles({ + flexDirection: 'column', +}); const options = [ 'E-mail', @@ -19,7 +24,7 @@ export const Alert = () => { setValue(event.target.value); }; return ( - + Contact diff --git a/modules/react/select/stories/examples/Complex.tsx b/modules/react/select/stories/examples/Complex.tsx index 5fcdfda574..84789ebe5f 100644 --- a/modules/react/select/stories/examples/Complex.tsx +++ b/modules/react/select/stories/examples/Complex.tsx @@ -2,6 +2,11 @@ import React from 'react'; import {FormField} from '@workday/canvas-kit-preview-react/form-field'; import {Select} from '@workday/canvas-kit-react/select'; import {Flex} from '@workday/canvas-kit-react/layout'; +import {createStyles} from '@workday/canvas-kit-styling'; + +const parentContainerStyles = createStyles({ + flexDirection: 'column', +}); const options = [ {serverId: 'email', label: 'E-mail'}, @@ -25,7 +30,7 @@ export const Complex = () => { }; return ( - + Contact diff --git a/modules/react/select/stories/examples/DisabledOption.tsx b/modules/react/select/stories/examples/DisabledOption.tsx index 3d00786355..2c4582a720 100644 --- a/modules/react/select/stories/examples/DisabledOption.tsx +++ b/modules/react/select/stories/examples/DisabledOption.tsx @@ -2,6 +2,11 @@ import React from 'react'; import {FormField} from '@workday/canvas-kit-preview-react/form-field'; import {Select} from '@workday/canvas-kit-react/select'; import {Flex} from '@workday/canvas-kit-react/layout'; +import {createStyles} from '@workday/canvas-kit-styling'; + +const parentContainerStyles = createStyles({ + flexDirection: 'column', +}); const options = [ 'E-mail', @@ -20,7 +25,7 @@ export const DisabledOptions = () => { }; return ( - + Contact diff --git a/modules/react/select/stories/examples/FetchingDynamicItems.tsx b/modules/react/select/stories/examples/FetchingDynamicItems.tsx index ea10fa3815..aa4ecf4940 100644 --- a/modules/react/select/stories/examples/FetchingDynamicItems.tsx +++ b/modules/react/select/stories/examples/FetchingDynamicItems.tsx @@ -4,6 +4,12 @@ import {Select, useSelectModel} from '@workday/canvas-kit-react/select'; import {Flex} from '@workday/canvas-kit-react/layout'; import {PrimaryButton} from '@workday/canvas-kit-react/button'; import {useMount} from '@workday/canvas-kit-react/common'; +import {createStyles, px2rem} from '@workday/canvas-kit-styling'; + +const parentContainerStyles = createStyles({ + flexDirection: 'column', + maxWidth: px2rem(300), +}); const movieListItems = [ { @@ -68,7 +74,7 @@ export const FetchingDynamicItems = () => { }); return ( - + { handleChange(e)} /> diff --git a/modules/react/select/stories/examples/Required.tsx b/modules/react/select/stories/examples/Required.tsx index 74ea21d250..ddc12909ed 100644 --- a/modules/react/select/stories/examples/Required.tsx +++ b/modules/react/select/stories/examples/Required.tsx @@ -2,6 +2,11 @@ import React from 'react'; import {FormField} from '@workday/canvas-kit-preview-react/form-field'; import {Select} from '@workday/canvas-kit-react/select'; import {Flex} from '@workday/canvas-kit-react/layout'; +import {createStyles} from '@workday/canvas-kit-styling'; + +const parentContainerStyles = createStyles({ + flexDirection: 'column', +}); const options = [ 'E-mail', @@ -20,7 +25,7 @@ export const Required = () => { }; return ( - + ().toMatchTypeOf<'--foo-label-emotion-safe'>(); - }); - it('should type optimized style functions correctly so they can be consumed by other repositories', () => { const vars = createVars({id: 'foo', args: ['one']}); - expectTypeOf(vars.one).toMatchTypeOf<'--foo-one'>(); + expectTypeOf(vars.one).toMatchTypeOf<'--one-foo'>(); }); it('should handle an object configuration', () => { @@ -205,9 +198,9 @@ describe('cs', () => { 'foo' ); - expect(myVars.color).toEqual('--foo-color'); + expect(myVars.color).toEqual('--color-foo'); - expectTypeOf(myVars.color).toEqualTypeOf<'--foo-color'>(); + expectTypeOf(myVars.color).toEqualTypeOf<'--color-foo'>(); }); it('should pass the optional ID to each default', () => { @@ -218,10 +211,10 @@ describe('cs', () => { 'foo' ); - expect(myVars.$$defaults).toHaveProperty('--foo-color', 'red'); + expect(myVars.$$defaults).toHaveProperty('--color-foo', 'red'); expectTypeOf(myVars.$$defaults).toEqualTypeOf<{ - '--foo-color': 'red'; + '--color-foo': 'red'; }>(); }); @@ -238,12 +231,12 @@ describe('cs', () => { expect(myVars).toHaveProperty('default'); expect(myVars.default).toHaveProperty( 'color', - expect.stringMatching(/--[a-z0-9]+-default-color/) + expect.stringMatching(/--default-color-[a-z0-9]+/) ); expect(myVars).toHaveProperty('hover'); expect(myVars.hover).toHaveProperty( 'color', - expect.stringMatching(/--[a-z0-9]+-hover-color/) + expect.stringMatching(/--hover-color-[a-z0-9]+/) ); expectTypeOf(myVars).toHaveProperty('default'); @@ -284,16 +277,16 @@ describe('cs', () => { }, 'foo' ); - + // BigO2023 expect(myVars).toHaveProperty('default'); - expect(myVars.default).toHaveProperty('color', expect.stringMatching('--foo-default-color')); + expect(myVars.default).toHaveProperty('color', expect.stringMatching('--default-color-foo')); expect(myVars).toHaveProperty('hover'); - expect(myVars.hover).toHaveProperty('color', expect.stringMatching('--foo-hover-color')); + expect(myVars.hover).toHaveProperty('color', expect.stringMatching('--hover-color-foo')); expectTypeOf(myVars).toHaveProperty('default'); - expectTypeOf(myVars.default).toEqualTypeOf<{color: '--foo-default-color'}>(); + expectTypeOf(myVars.default).toEqualTypeOf<{color: '--default-color-foo'}>(); expectTypeOf(myVars).toHaveProperty('hover'); - expectTypeOf(myVars.hover).toEqualTypeOf<{color: '--foo-hover-color'}>(); + expectTypeOf(myVars.hover).toEqualTypeOf<{color: '--hover-color-foo'}>(); }); it('should use the optional ID with nested variables in $$defaults', () => { @@ -311,15 +304,15 @@ describe('cs', () => { ); expect(myVars).toHaveProperty('$$defaults'); - expect(myVars.$$defaults).toHaveProperty('--foo-default-color', 'blue'); - expect(myVars.$$defaults).toHaveProperty('--foo-default-background', 'white'); - expect(myVars.$$defaults).toHaveProperty('--foo-hover-color', 'red'); + expect(myVars.$$defaults).toHaveProperty('--default-color-foo', 'blue'); + expect(myVars.$$defaults).toHaveProperty('--default-background-foo', 'white'); + expect(myVars.$$defaults).toHaveProperty('--hover-color-foo', 'red'); expectTypeOf(myVars).toHaveProperty('$$defaults'); expectTypeOf(myVars.$$defaults).toEqualTypeOf<{ - '--foo-default-color': 'blue'; - '--foo-default-background': 'white'; - '--foo-hover-color': 'red'; + '--default-color-foo': 'blue'; + '--default-background-foo': 'white'; + '--hover-color-foo': 'red'; }>(); }); }); @@ -675,7 +668,7 @@ describe('cs', () => { expectTypeOf(myStencil.vars).toHaveProperty('color'); expectTypeOf(myStencil.vars.color).toEqualTypeOf(); - expect(myStencil).toHaveProperty('vars.color', expect.stringMatching(/--[a-z0-9]+-color/)); + expect(myStencil).toHaveProperty('vars.color', expect.stringMatching(/--color-[a-z0-9]+/)); }); it('should coerce a variable input to a type of string', () => { @@ -709,9 +702,9 @@ describe('cs', () => { expectTypeOf(myStencil).toHaveProperty('vars'); expectTypeOf(myStencil.vars).toHaveProperty('color'); - expectTypeOf(myStencil.vars.color).toEqualTypeOf<'--foo-color'>(); + expectTypeOf(myStencil.vars.color).toEqualTypeOf<'--color-foo'>(); - expect(myStencil).toHaveProperty('vars.color', expect.stringMatching(/--foo-color/)); + expect(myStencil).toHaveProperty('vars.color', expect.stringMatching(/--color-foo/)); }); it('should return a function that takes in an object with the values and wrap passed css variables', () => { @@ -1031,13 +1024,13 @@ describe('cs', () => { expectTypeOf(extendedStencil.vars.color).toEqualTypeOf(); expect(extendedStencil).toHaveProperty( 'vars.color', - expect.stringMatching(/--[0-9a-z]+-color/i) + expect.stringMatching(/--color-[0-9a-z]+/i) ); expectTypeOf(extendedStencil.vars.background).toEqualTypeOf(); expect(extendedStencil).toHaveProperty( 'vars.background', - expect.stringMatching(/--[0-9a-z]+-background/i) + expect.stringMatching(/--background-[0-9a-z]+/i) ); }); @@ -1067,18 +1060,18 @@ describe('cs', () => { 'extended' ); - expectTypeOf(extendedStencil.vars.color).toEqualTypeOf<'--base-color'>(); - expectTypeOf(extendedStencil.vars.background).toEqualTypeOf<'--extended-background'>(); - expectTypeOf(extendedStencil.vars.$$defaults).toHaveProperty('--base-color'); - expectTypeOf(extendedStencil.vars.$$defaults).toHaveProperty('--extended-background'); - expectTypeOf(extendedStencil.vars.$$defaults['--base-color']).toMatchTypeOf(); + expectTypeOf(extendedStencil.vars.color).toEqualTypeOf<'--color-base'>(); + expectTypeOf(extendedStencil.vars.background).toEqualTypeOf<'--background-extended'>(); + expectTypeOf(extendedStencil.vars.$$defaults).toHaveProperty('--color-base'); + expectTypeOf(extendedStencil.vars.$$defaults).toHaveProperty('--background-extended'); + expectTypeOf(extendedStencil.vars.$$defaults['--color-base']).toMatchTypeOf(); expectTypeOf( - extendedStencil.vars.$$defaults['--extended-background'] + extendedStencil.vars.$$defaults['--background-extended'] ).toMatchTypeOf(); - expect(extendedStencil).toHaveProperty('vars.color', '--base-color'); + expect(extendedStencil).toHaveProperty('vars.color', '--color-base'); - expect(extendedStencil).toHaveProperty('vars.background', '--extended-background'); + expect(extendedStencil).toHaveProperty('vars.background', '--background-extended'); }); it('should extend, adding class names in the correct order', () => { diff --git a/modules/styling/stories/Basics.stories.mdx b/modules/styling/stories/Basics.stories.mdx index 80ca7435e9..3ee720bf92 100644 --- a/modules/styling/stories/Basics.stories.mdx +++ b/modules/styling/stories/Basics.stories.mdx @@ -39,8 +39,8 @@ versatility. In this example, the HTML will look like: ```html -
-
+
+
``` And the CSS will look like: @@ -49,7 +49,7 @@ And the CSS will look like: .css-4iptxs { width: 100px; height: 100px; - background-color: var(--c6kna-background); + background-color: var(--background-c6kna); } ``` @@ -94,11 +94,11 @@ const myStencil = createStencil({ nonDefaultedColor: '', // will allow for uninitialization }, base: ({defaultColor}) => { - color: defaultColor // `defaultColor` is '--abc123-defaultColor', not 'red' + color: defaultColor // `defaultColor` is '--defaultColor-abc123', not 'red' } }) -const elemProps = myStencil({color: 'blue'}) // {style: {'--abc123-defaultColor': 'blue'}} +const elemProps = myStencil({color: 'blue'}) // {style: {'--defaultColor-abc123': 'blue'}}
``` @@ -108,11 +108,11 @@ This will produce the following HTML: ```html -
+
``` The element will have a `color` property of `'blue'` because the element style is the highest @@ -121,12 +121,12 @@ like the following: ``` element.style { - --abc123-defaultColor: blue; + --defaultColor-abc123: blue; } .css-abc123 { - --abc123-defaultColor: red; - color: var(--abc123-defaultColor); // blue + --defaultColor-abc123: red; + color: var(--defaultColor-abc123); // blue } ``` @@ -350,7 +350,7 @@ the modifier option or a string. The value will be sent as a variable regardless will only match if it is a valid modifer key. ```tsx -const myStencil = createStencil({ +const buttonStencil = createStencil({ vars: { width: '10px', }, @@ -370,11 +370,11 @@ const myStencil = createStencil({ // `'zero'` is part of autocomplete myStencil({width: 'zero'}); -// returns {className: 'css-base css--width-zero', styles: { '--width': 'zero'}} +// returns {className: 'css-button css-button--width-zero', styles: { '--button-width': 'zero'}} // width also accepts a string myStencil({width: '10px'}); -// returns {className: 'css-base', styles: { '--width': '10px'}} +// returns {className: 'css-button', styles: { '--button-width': '10px'}} ``` ### `keyframes` diff --git a/modules/styling/stories/FromEmotion.stories.mdx b/modules/styling/stories/FromEmotion.stories.mdx index 1e07a5004f..653d08564a 100644 --- a/modules/styling/stories/FromEmotion.stories.mdx +++ b/modules/styling/stories/FromEmotion.stories.mdx @@ -59,14 +59,14 @@ We can create modifers using `createStyles` and organize them in an object: const modifierStyles = { variant: { primary: createStyles({ - background: `var(--button-background-color, blue)`, + background: `var(--background-color-button, blue)`, color: 'white', }), secondary: createStyles({ - background: `var(--button-background-color, gray)`, + background: `var(--background-color-button, gray)`, }), danger: createStyles({ - background: `var(--button-background-color, red)`, + background: `var(--background-color-button, red)`, }), }, size: { @@ -112,7 +112,7 @@ If you want to change the background color, you'll have to pass it using `style` ```jsx @@ -121,7 +121,7 @@ If you want to change the background color, you'll have to pass it using `style` The output HTML will look like: ```html - ``` @@ -166,13 +166,13 @@ myModifiers() // '' ```tsx const myVars = createVars('background', 'color') -myVars.color // something like `--{hash}-color` +myVars.color // something like `--color-{hash}` // the function knows what keys are allowed -myVars({color: 'red'}) // {'--{hash}-color': 'red'} +myVars({color: 'red'}) // {'--color-{hash}': 'red'} // in a component -
//
+
//
``` diff --git a/utils/style-transform/spec/handleFocusRing.spec.ts b/utils/style-transform/spec/handleFocusRing.spec.ts index 0ba46e303e..c8842d37b4 100644 --- a/utils/style-transform/spec/handleFocusRing.spec.ts +++ b/utils/style-transform/spec/handleFocusRing.spec.ts @@ -38,7 +38,7 @@ describe('handleFocusRing', () => { const result = handleFocusRing( node, withDefaultContext(program.getTypeChecker(), { - variables: {'my-boxShadowInner': '--foo-bar'}, + names: {'myVars.boxShadowInner': '--foo-bar'}, }) ); @@ -70,7 +70,7 @@ describe('handleFocusRing', () => { program, 'test.ts', withDefaultContext(program.getTypeChecker(), { - variables: { + names: { '--foo-bar': 'red', }, objectTransforms: [handleFocusRing], From 78514b9b8a486bc3b361b57a0f5d340a2a86e39d Mon Sep 17 00:00:00 2001 From: Nicholas Boll Date: Thu, 16 May 2024 14:21:21 -0600 Subject: [PATCH 6/7] fix(styling): Fix parent selectors in compat mode (#2743) PR #2741 added the ability to target parent modifier class names to build selectors, but doesn't work in compat mode because compat mode merges all the CSS class names into a single, new class name. I added a `parentModifiers` function that handles this for both React Kit and CSS Kit and updated stencils to add inert class names for the purpose of always matching a selector. I also fixed a bug where passing `undefined` to a Stencil for a modifier key resulted in overriding `defaultModifiers`. [category:Styling] --- .../styling-transform/lib/styleTransform.ts | 3 +- .../lib/utils/handleCreateStencil.ts | 6 +- .../lib/utils/handleParentModifier.ts | 29 +++++++ .../spec/utils/handleCreateStencil.spec.ts | 37 ++++++++- .../spec/utils/handleParentModifier.spec.ts | 82 +++++++++++++++++++ modules/styling/lib/cs.ts | 45 +++++++++- modules/styling/spec/cs.spec.tsx | 51 ++++++++++-- 7 files changed, 235 insertions(+), 18 deletions(-) create mode 100644 modules/styling-transform/lib/utils/handleParentModifier.ts create mode 100644 modules/styling-transform/spec/utils/handleParentModifier.spec.ts diff --git a/modules/styling-transform/lib/styleTransform.ts b/modules/styling-transform/lib/styleTransform.ts index b8536dbc50..f9a164cfb0 100644 --- a/modules/styling-transform/lib/styleTransform.ts +++ b/modules/styling-transform/lib/styleTransform.ts @@ -12,6 +12,7 @@ import {handleCssVar} from './utils/handleCssVar'; import {Config, NodeTransformer, ObjectTransform, TransformerContext} from './utils/types'; import {handleKeyframes} from './utils/handleKeyframes'; import {handleInjectGlobal} from './utils/handleInjectGlobal'; +import {handleParentModifier} from './utils/handleParentModifier'; export type NestedStyleObject = {[key: string]: string | NestedStyleObject}; @@ -140,7 +141,7 @@ export function withDefaultContext( objectTransforms: [] as ObjectTransform[], transform: handleTransformers(transformers || defaultTransformers), ...input, - propertyTransforms: [handleCalc, handlePx2Rem, handleCssVar].concat( + propertyTransforms: [handleCalc, handlePx2Rem, handleCssVar, handleParentModifier].concat( input.propertyTransforms || [] ), } as TransformerContext; diff --git a/modules/styling-transform/lib/utils/handleCreateStencil.ts b/modules/styling-transform/lib/utils/handleCreateStencil.ts index fd6d82aaa3..4849eb6b52 100644 --- a/modules/styling-transform/lib/utils/handleCreateStencil.ts +++ b/modules/styling-transform/lib/utils/handleCreateStencil.ts @@ -14,7 +14,7 @@ import {getHash} from './getHash'; * Handle all arguments of the CallExpression `createStencil()` */ export const handleCreateStencil: NodeTransformer = (node, context) => { - const {checker, prefix, names, extractedNames} = context; + const {checker, names, extractedNames} = context; /** * This will match whenever a `createStencil()` call expression is encountered. It will loop * over all the config to extract variables and styles. @@ -423,11 +423,11 @@ function createStyleReplacementNode( className: string, context: TransformerContext ) { - const {prefix, names, extractedNames} = context; + const {names, extractedNames} = context; const serialized = serializeStyles(node, styleObj, `.${className}{%s}`, context); const varName = getVarName(node); - const value = `${prefix}-${serialized.name}`; + const value = `css-${serialized.name}`; names[varName] = value; extractedNames[value] = getClassName(varName, context); diff --git a/modules/styling-transform/lib/utils/handleParentModifier.ts b/modules/styling-transform/lib/utils/handleParentModifier.ts new file mode 100644 index 0000000000..5ac8080b8d --- /dev/null +++ b/modules/styling-transform/lib/utils/handleParentModifier.ts @@ -0,0 +1,29 @@ +import ts from 'typescript'; + +import {parentModifier} from '@workday/canvas-kit-styling'; +import {createPropertyTransform} from '../createPropertyTransform'; +import {parseNodeToStaticValue} from './parseNodeToStaticValue'; + +export const handleParentModifier = createPropertyTransform((node, context) => { + const {names, extractedNames} = context; + + if ( + ts.isComputedPropertyName(node) && + ts.isCallExpression(node.expression) && + ts.isIdentifier(node.expression.expression) && + node.expression.expression.text === 'parentModifier' + ) { + const args = node.expression.arguments.map(arg => parseNodeToStaticValue(arg, context)); + const hash = args[0].toString().replace('css-', ''); + + // add a mapping from `css-{hash}` to `{hash}` for extraction string replacement + names[args[0]] = hash; + + // map the `{hash}` to the extracted CSS class name + extractedNames[hash] = extractedNames[args[0]]; + + return parentModifier(args[0].toString()); + } + + return; +}); diff --git a/modules/styling-transform/spec/utils/handleCreateStencil.spec.ts b/modules/styling-transform/spec/utils/handleCreateStencil.spec.ts index 9e2cab112b..06cb71f663 100644 --- a/modules/styling-transform/spec/utils/handleCreateStencil.spec.ts +++ b/modules/styling-transform/spec/utils/handleCreateStencil.spec.ts @@ -355,7 +355,7 @@ describe('handleCreateStencil', () => { it('should handle parsing modifiers with ObjectLiteralExpressions', () => { const program = createProgramFromSource(` - import {createStencil} from '@workday/canvas-kit-styling'; + import {createStencil, parentModifier} from '@workday/canvas-kit-styling'; const buttonStencil = createStencil({ vars: { @@ -369,12 +369,45 @@ describe('handleCreateStencil', () => { } } }) + + const childStencil = createStencil({ + base: { + [parentModifier(buttonStencil.modifiers.size.large)]: { + color: 'blue', + } + } + }) `); - const result = transform(program, 'test.ts', withDefaultContext(program.getTypeChecker())); + const styles = {}; + const names = {}; + const extractedNames = {}; + + const result = transform( + program, + 'test.ts', + withDefaultContext(program.getTypeChecker(), {styles, names, extractedNames}) + ); expect(result).toMatch(/large: { name: "[0-9a-z]+", styles: "padding:20px;" }/); expect(result).toMatch(/small: { name: "[0-9a-z]+", styles: "padding:10px;" }/); + + // runtime selector + expect(result).toContain( + `.${names['buttonStencil.modifiers.size.large'].replace('css-', '')} :where(&){color:blue;}` + ); + + // extracted selector + expect(getFile(styles, 'test.css')).toContainEqual( + compileCSS(` + .css-child { + box-sizing: border-box; + } + .css-button--size-large :where(.css-child) { + color: blue; + } + `) + ); }); it('should handle parsing modifiers with ObjectLiteralExpressions', () => { diff --git a/modules/styling-transform/spec/utils/handleParentModifier.spec.ts b/modules/styling-transform/spec/utils/handleParentModifier.spec.ts new file mode 100644 index 0000000000..f129c1f622 --- /dev/null +++ b/modules/styling-transform/spec/utils/handleParentModifier.spec.ts @@ -0,0 +1,82 @@ +import ts from 'typescript'; + +import {createProgramFromSource} from '../createProgramFromSource'; + +import {transform, withDefaultContext, _reset} from '../../lib/styleTransform'; +import {compileCSS} from '../../lib/utils/createStyleObjectNode'; + +describe('handleParentModifier', () => { + let program: ts.Program; + let result: string; + + const styles = {}; + const names = {}; + const extractedNames = {}; + + beforeAll(() => { + _reset(); + program = createProgramFromSource(` + import {createStencil, parentModifier} from '@workday/canvas-kit-styling'; + + const buttonStencil = createStencil({ + vars: { + color: 'red' + }, + base: {}, + modifiers: { + size: { + large: {padding: 20}, + small: {padding: 10} + } + } + }) + + const childStencil = createStencil({ + base: { + [parentModifier(buttonStencil.modifiers.size.large)]: { + color: 'blue', + } + } + }) + `); + + result = transform( + program, + 'test.ts', + withDefaultContext(program.getTypeChecker(), {styles, names, extractedNames}) + ); + }); + + it('should add a mapping from the CSS class name to the hash to the names cache', () => { + expect(names).toHaveProperty( + names['buttonStencil.modifiers.size.large'], + names['buttonStencil.modifiers.size.large'].replace('css-', '') + ); + }); + + it('should add a mapping from the hash to the extracted CSS class name to the extractedNames cache', () => { + expect(extractedNames).toHaveProperty( + names['buttonStencil.modifiers.size.large'].replace('css-', ''), + 'css-button--size-large' + ); + }); + + it('should transform the runtime to include a selector with only the hash', () => { + expect(result).toContain( + `.${names['buttonStencil.modifiers.size.large'].replace('css-', '')} :where(&){color:blue;}` + ); + }); + + it('should extract CSS to include the fully qualified modifier name', () => { + expect(styles['test.css']).toContainEqual( + compileCSS(` + .css-child { + box-sizing: border-box; + } + .css-button--size-large :where(.css-child) { + color: blue; + } + `) + ); + }); +}); diff --git a/modules/styling/lib/cs.ts b/modules/styling/lib/cs.ts index 5c55ca13b3..12bd7deb87 100644 --- a/modules/styling/lib/cs.ts +++ b/modules/styling/lib/cs.ts @@ -1049,6 +1049,26 @@ function combineClassNames(input: (string | undefined)[]): string { .join(' '); } +/** + * This function is used in conjunction with Stencils to get a selector to match the current element + * and a parent modifier. The selector will match a parent element with a modifier applied and the current + * element. It should be used on the `base` config of a Stencil. + * + * ```ts + * const childStencil = createStencil({ + * base: { + * // base styles + * [parentModifier(parentStencil.modifiers.size.large)]: { + * // maybe adjust padding of this element + * } + * } + * }) + * ``` + */ +export function parentModifier(value: string) { + return `.${value.replace('css-', '')} :where(&)`; +} + /** * Creates a reuseable Stencil for styling elements. It takes vars, base styles, modifiers, and * compound modifiers. @@ -1087,6 +1107,7 @@ export function createStencil< result[modifierKey] = createStyles( typeof modifier === 'function' ? modifier(_vars) : modifier ); + return result; }, {}); @@ -1127,14 +1148,32 @@ export function createStencil< ) : () => ''; - const stencil: Stencil = ((input: {}) => { - const inputModifiers = {...composes?.defaultModifiers, ...defaultModifiers, ...input}; + const stencil: Stencil = ((input: Record) => { + const inputModifiers = {...composes?.defaultModifiers, ...defaultModifiers}; + // Only override defaults if a value is defined + for (const key in input) { + if (input[key]) { + // @ts-ignore + inputModifiers[key] = input[key]; + } + } const composesReturn = composes?.(inputModifiers as any); + const modifierClasses = _modifiers(inputModifiers); return { className: combineClassNames([ composesReturn?.className, _base, - _modifiers(inputModifiers), + modifierClasses, + // For compat mode, we need to add inert class names of modifiers where the `css-` prefix is + // removed. For example, `css-base css-mod-1` will become `css-base css-mod-1 mod-1`. The + // modifier class without the prefix will be ignored by the Emotion CSS `cx` function and + // will remain for the `parentModifier` function to still select on. We decided to add these + // inert class names instead of adding `data-m-*` attributes because the output DOM looks + // much cleaner and it saves bytes on the bundled output. + modifierClasses + .split(' ') + .map(c => c.replace('css-', '')) + .join(' '), compound ? _compound(inputModifiers) : '', ]), style: {...composesReturn?.style, ..._vars(input || {})}, diff --git a/modules/styling/spec/cs.spec.tsx b/modules/styling/spec/cs.spec.tsx index add8606547..56b7d29519 100644 --- a/modules/styling/spec/cs.spec.tsx +++ b/modules/styling/spec/cs.spec.tsx @@ -529,7 +529,9 @@ describe('cs', () => { expect(screen.getByTestId('test1')).toHaveStyle({padding: '10px'}); expect(screen.getByTestId('test2')).toHaveStyle({padding: '20px'}); - expect(myStencil({size: 'large'}).className.split(' ')).toHaveLength(2); + + expect(myStencil({size: 'large'}).className).toContain(myStencil.base); + expect(myStencil({size: 'large'}).className).toContain(myStencil.modifiers.size.large); // type signature of stencil call expression type Arg = Parameters[0]; @@ -577,11 +579,30 @@ describe('cs', () => { }, }); - expect(myStencil({position: 'start', size: 'large'}).className).toEqual( + expect(myStencil({position: 'start', size: 'large'}).className).toContain( `${myStencil.base} ${myStencil.modifiers.size.large} ${myStencil.modifiers.position.start}` ); }); + it('should add the hash of the modifier to the className to handle parentModifier selectors in compat mode', () => { + const myStencil = createStencil({ + base: {}, + modifiers: { + size: { + large: {}, + }, + }, + }); + + const {className} = myStencil({size: 'large'}); + + expect(className).toEqual( + `${myStencil.base} ${ + myStencil.modifiers.size.large + } ${myStencil.modifiers.size.large.replace('css-', '')}` + ); + }); + it('should default modifiers if no modifier override is passed', () => { const myStencil = createStencil({ base: { @@ -653,7 +674,13 @@ describe('cs', () => { }); // 4 class names - base, size, position, size-position - expect(myStencil({size: 'large', position: 'start'}).className.split(' ')).toHaveLength(4); + expect(myStencil({size: 'large', position: 'start'}).className.split(' ')).toHaveLength(6); // 4 + 2 modifier hashes + const {className} = myStencil({size: 'large', position: 'start'}); + + expect(className).toContain(myStencil.base); + expect(className).toContain(myStencil.modifiers.size.large); + expect(className).toContain(myStencil.modifiers.position.start); + // we don't have access to the compound modifier class name }); it('should return access to variables for use in other components', () => { @@ -796,9 +823,6 @@ describe('cs', () => { width: '0', }, }, - foo: { - true: {}, - }, }, }); @@ -818,7 +842,7 @@ describe('cs', () => { expect(result2.style).toHaveProperty(myStencil.vars.width, 'zero'); // match base and width modifier - expect(result2.className).toEqual(`${myStencil.base} ${myStencil.modifiers.width.zero}`); + expect(result2.className).toContain(`${myStencil.base} ${myStencil.modifiers.width.zero}`); }); it('should convert "true" modifiers into boolean', () => { @@ -959,7 +983,13 @@ describe('cs', () => { const {className} = extendedStencil({size: 'large'}); - expect(className.split(' ')).toHaveProperty('length', 5); + expect(className.split(' ')).toHaveProperty('length', 6); // 1 base + 1 extended + 1 size modifier + 2 compound modifiers + 1 modifier hash + + expect(className).toContain(baseStencil.base); + extendedStencil.base.split(' ').forEach(c => { + expect(className).toContain(c); + }); + expect(className).toContain(baseStencil.modifiers.size.large); // calling the stencil type Args = Exclude[0], undefined>; @@ -1115,6 +1145,9 @@ describe('cs', () => { large: {}, small: {}, }, + foo: { + bar: {}, + }, }, compound: [ { @@ -1142,7 +1175,7 @@ describe('cs', () => { expect(className).toContain(baseStencil.modifiers.position.only); // baseStencil.base, baseStencil large, baseStencil only position, baseStencil compound, extendedStencil.base, extendedStencil large, extendedStencil compound - expect(className.split(' ')).toHaveLength(7); + expect(className.split(' ')).toHaveLength(10); // 7 + 3 modifier hashes }); }); }); From d3ed48908566d8ef153d8992c5c4de11b18f9970 Mon Sep 17 00:00:00 2001 From: Nicholas Boll Date: Fri, 17 May 2024 10:51:05 -0600 Subject: [PATCH 7/7] chore(form-field): Update the preview FormField Stencil (#2746) Refactor preview `FormField` Stencil to use `parentModifier` and remove the need for style information in the model and extra modifiers needed in the CSS Kit output [category:Components] --- modules/css/package.json | 2 +- modules/docs/mdx/11.0-UPGRADE-GUIDE.mdx | 23 +++- modules/labs-css/package.json | 2 +- modules/preview-css/package.json | 2 +- modules/preview-react/form-field/index.ts | 4 + .../form-field/lib/FormField.tsx | 57 +++++----- .../form-field/lib/FormFieldContainer.tsx | 2 +- .../form-field/lib/FormFieldHint.tsx | 52 ++++----- .../form-field/lib/FormFieldLabel.tsx | 104 ++++++------------ .../form-field/lib/formFieldStencil.ts | 42 +++++++ .../lib/hooks/useFormFieldModel.tsx | 5 - .../lib/hooks/useFormFieldOrientation.tsx | 2 + .../form-field/stories/examples/Custom.tsx | 6 +- .../preview-react/text-area/lib/TextArea.tsx | 23 ++-- .../text-input/lib/TextInput.tsx | 23 ++-- .../text-input/spec/TextInput.spec.tsx | 12 -- .../lib/utils/handleParentModifier.ts | 2 +- .../spec/utils/handleCreateStencil.spec.ts | 2 +- .../spec/utils/handleParentModifier.spec.ts | 6 +- modules/styling/lib/cs.ts | 7 +- modules/styling/spec/cs.spec.tsx | 2 +- 21 files changed, 203 insertions(+), 177 deletions(-) create mode 100644 modules/preview-react/form-field/lib/formFieldStencil.ts diff --git a/modules/css/package.json b/modules/css/package.json index 109ffaa2f8..ffedf9d567 100644 --- a/modules/css/package.json +++ b/modules/css/package.json @@ -1,6 +1,6 @@ { "name": "@workday/canvas-kit-css", - "version": "10.3.36", + "version": "10.3.39", "description": "The parent module that contains all Workday Canvas Kit CSS components", "author": "Workday, Inc. (https://www.workday.com)", "license": "Apache-2.0", diff --git a/modules/docs/mdx/11.0-UPGRADE-GUIDE.mdx b/modules/docs/mdx/11.0-UPGRADE-GUIDE.mdx index 251a70cb1f..a43fbc4d76 100644 --- a/modules/docs/mdx/11.0-UPGRADE-GUIDE.mdx +++ b/modules/docs/mdx/11.0-UPGRADE-GUIDE.mdx @@ -524,7 +524,8 @@ variant prop. ### Form Field (Preview) -**PR:** [#2472](https://github.com/Workday/canvas-kit/pull/2472) +**PR:** [#2472](https://github.com/Workday/canvas-kit/pull/2472), +[#2746](https://github.com/Workday/canvas-kit/pull/2746) `FormField` in [Preview](#preview) is a compound component and we intend to promote it in a future version to replace the `FormField` in [Main](#main). Because of this, we've refactored how errors @@ -538,6 +539,26 @@ are applied to `FormField` in [Preview](#preview) in order to match the API from - `FormField` does **not** support the `useFieldSet` prop that the `FormField` in [Main](#main) does. In order to achieve the same behavior, set the `as` prop on the `FormField` element to `fieldset` and the `as` prop of `FormField.Label` to `legend`. +- The required asterisk is now a pseudo element. While the asterisk was never read out loud by + screen readers, Testing Library required it in the `*ByLabelText` query. `*ByRole` uses the w3c + [accessible name calculation specification](https://www.w3.org/TR/accname-1.2/), but + `*ByLabelText` uses + [textContent](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent). Additional + information: https://github.com/testing-library/dom-testing-library/issues/929 + + v10: + + ```ts + screen.getByLabelText('Email*'); // Email is a required field and asterisk is included in name + screen.getByRole('textbox', {name: 'Email'}); // correctly ignores the `*` + ``` + + v11: + + ```ts + screen.getByLabelText('Email'); + screen.getByRole('textbox', {name: 'Email'}); + ``` ```tsx // v10 FormField in Preview diff --git a/modules/labs-css/package.json b/modules/labs-css/package.json index bd7c8c944a..b6d6aa1f8a 100644 --- a/modules/labs-css/package.json +++ b/modules/labs-css/package.json @@ -1,6 +1,6 @@ { "name": "@workday/canvas-kit-labs-css", - "version": "10.3.36", + "version": "10.3.39", "description": "The parent module that contains all Workday Canvas Kit Labs CSS components", "author": "Workday, Inc. (https://www.workday.com)", "license": "Apache-2.0", diff --git a/modules/preview-css/package.json b/modules/preview-css/package.json index a9ebb79df0..e0fa9596d9 100644 --- a/modules/preview-css/package.json +++ b/modules/preview-css/package.json @@ -1,6 +1,6 @@ { "name": "@workday/canvas-kit-preview-css", - "version": "10.3.36", + "version": "10.3.39", "description": "The parent module that contains all Workday Canvas Kit Preview CSS components", "author": "Workday, Inc. (https://www.workday.com)", "license": "Apache-2.0", diff --git a/modules/preview-react/form-field/index.ts b/modules/preview-react/form-field/index.ts index 47fb19e0c2..bf873ef0ea 100644 --- a/modules/preview-react/form-field/index.ts +++ b/modules/preview-react/form-field/index.ts @@ -1,2 +1,6 @@ export * from './lib/FormField'; export * from './lib/hooks/'; +export {formFieldStencil} from './lib/formFieldStencil'; +export {formFieldContainerStencil} from './lib/FormFieldContainer'; +export {formFieldHintStencil} from './lib/FormFieldHint'; +export {formFieldLabelStencil} from './lib/FormFieldLabel'; diff --git a/modules/preview-react/form-field/lib/FormField.tsx b/modules/preview-react/form-field/lib/FormField.tsx index c53bfb18d7..8d3415c40c 100644 --- a/modules/preview-react/form-field/lib/FormField.tsx +++ b/modules/preview-react/form-field/lib/FormField.tsx @@ -1,37 +1,24 @@ import React from 'react'; import {createContainer, GrowthBehavior} from '@workday/canvas-kit-react/common'; -import {Flex, FlexProps, mergeStyles} from '@workday/canvas-kit-react/layout'; -import {createStencil} from '@workday/canvas-kit-styling'; -import {system} from '@workday/canvas-tokens-web'; +import {FlexProps, mergeStyles} from '@workday/canvas-kit-react/layout'; -import {useFormFieldModel, useFormFieldOrientation} from './hooks'; +import {useFormFieldModel} from './hooks'; import {FormFieldInput} from './FormFieldInput'; import {FormFieldLabel} from './FormFieldLabel'; import {FormFieldHint} from './FormFieldHint'; import {FormFieldContainer} from './FormFieldContainer'; +import {formFieldStencil} from './formFieldStencil'; export interface FormFieldProps extends FlexProps, GrowthBehavior { + /** + * The direction the child elements should stack + * @default vertical + */ + orientation?: 'vertical' | 'horizontal'; children: React.ReactNode; } -const formFieldStencil = createStencil({ - base: { - border: 'none', - padding: system.space.zero, - margin: `${system.space.zero} ${system.space.zero} ${system.space.x6}`, - }, - modifiers: { - grow: { - true: { - width: '100%', - '[data-width="ck-formfield-width"]': { - width: '100%', - }, - }, - }, - }, -}); /** * Use `FormField` to wrap input components to make them accessible. You can customize the field * by passing in `TextInput`, `Select`, `RadioGroup` and other form elements to `FormField.Input` through the `as` prop. @@ -42,6 +29,8 @@ const formFieldStencil = createStencil({ * console.log(e)} /> * * ``` + * + * @stencil formFieldStencil */ export const FormField = createContainer('div')({ displayName: 'FormField', @@ -70,6 +59,8 @@ export const FormField = createContainer('div')({ * console.log(e)} /> * * ``` + * + * @stencil formFieldLabelStencil */ Label: FormFieldLabel, /** @@ -83,6 +74,8 @@ export const FormField = createContainer('div')({ * This is your hint text * * ``` + * + * @stencil formFieldHintStencil */ Hint: FormFieldHint, /** @@ -97,19 +90,25 @@ export const FormField = createContainer('div')({ * * * ``` + * + * @stencil formFieldContainerStencil */ Container: FormFieldContainer, }, -})(({children, ...elemProps}, Element, model) => { - const layoutProps = useFormFieldOrientation(model.state.orientation); - +})(({children, grow, orientation, ...elemProps}, Element, model) => { return ( - {children} - + ); }); diff --git a/modules/preview-react/form-field/lib/FormFieldContainer.tsx b/modules/preview-react/form-field/lib/FormFieldContainer.tsx index bd9d961983..ad49192ae0 100644 --- a/modules/preview-react/form-field/lib/FormFieldContainer.tsx +++ b/modules/preview-react/form-field/lib/FormFieldContainer.tsx @@ -5,7 +5,7 @@ import {handleCsProp, CSProps, createStencil} from '@workday/canvas-kit-styling' import {useFormFieldModel} from './hooks'; -const formFieldContainerStencil = createStencil({ +export const formFieldContainerStencil = createStencil({ base: { display: 'flex', flexDirection: 'column', diff --git a/modules/preview-react/form-field/lib/FormFieldHint.tsx b/modules/preview-react/form-field/lib/FormFieldHint.tsx index 0df6c5c363..e753891365 100644 --- a/modules/preview-react/form-field/lib/FormFieldHint.tsx +++ b/modules/preview-react/form-field/lib/FormFieldHint.tsx @@ -1,45 +1,41 @@ import React from 'react'; + import {createSubcomponent, ExtractProps} from '@workday/canvas-kit-react/common'; import {system, brand} from '@workday/canvas-tokens-web'; -import {createStencil} from '@workday/canvas-kit-styling'; +import {createStencil, parentModifier} from '@workday/canvas-kit-styling'; +import {Text, textStencil} from '@workday/canvas-kit-react/text'; +import {mergeStyles} from '@workday/canvas-kit-react/layout'; import {useFormFieldHint, useFormFieldModel} from './hooks'; -import {Subtext} from '@workday/canvas-kit-react/text'; -import {mergeStyles} from '@workday/canvas-kit-react/layout'; +import {formFieldStencil} from './formFieldStencil'; -const formFieldHintStencil = createStencil({ +export const formFieldHintStencil = createStencil({ + extends: textStencil, base: { margin: `${system.space.x2} ${system.space.zero} ${system.space.zero}`, - }, - modifiers: { - error: { - error: { - color: brand.error.base, - }, - alert: {}, + + [parentModifier(formFieldStencil.modifiers.error.error)]: { + color: brand.error.base, }, }, + defaultModifiers: { + typeLevel: 'subtext.medium', + }, }); export const FormFieldHint = createSubcomponent('p')({ displayName: 'FormField.Hint', modelHook: useFormFieldModel, elemPropsHook: useFormFieldHint, -}), 'size'>>( - ({children, ...elemProps}, Element, model) => { - if (!children) { - // If there is no hint text just skip rendering - return null; - } - - return ( - - {children} - - ); +})>(({children, typeLevel, variant, ...elemProps}, Element) => { + if (!children) { + // If there is no hint text just skip rendering + return null; } -); + + return ( + + {children} + + ); +}); diff --git a/modules/preview-react/form-field/lib/FormFieldLabel.tsx b/modules/preview-react/form-field/lib/FormFieldLabel.tsx index 7009296e00..42c01551da 100644 --- a/modules/preview-react/form-field/lib/FormFieldLabel.tsx +++ b/modules/preview-react/form-field/lib/FormFieldLabel.tsx @@ -1,39 +1,27 @@ import React from 'react'; -import {createSubcomponent} from '@workday/canvas-kit-react/common'; -import {useFormFieldLabel, useFormFieldModel} from './hooks'; -import {createStencil, px2rem} from '@workday/canvas-kit-styling'; -import {mergeStyles} from '@workday/canvas-kit-react/layout'; +import {createSubcomponent, ExtractProps} from '@workday/canvas-kit-react/common'; +import {createStencil, parentModifier, px2rem} from '@workday/canvas-kit-styling'; +import {Text, textStencil} from '@workday/canvas-kit-react/text'; +import {FlexProps, mergeStyles} from '@workday/canvas-kit-react/layout'; import {brand, system} from '@workday/canvas-tokens-web'; -export interface FormFieldLabelProps { +import {useFormFieldLabel, useFormFieldModel} from './hooks'; +import {formFieldStencil} from './formFieldStencil'; + +export interface FormFieldLabelProps + extends FlexProps, + Omit, 'display'> { /** * The text of the label. */ children: React.ReactNode; - /** - * Will style the text as disabled - */ - disabled?: boolean; - /** - * Changes the color of the text - */ - variant?: 'error' | 'hint' | 'inverse'; } -const formFieldLabelAsteriskStencil = createStencil({ - base: { - fontSize: system.fontSize.body.large, - fontWeight: system.fontWeight.normal, - color: brand.error.base, - textDecoration: 'unset', - marginInlineStart: system.space.x1, - }, -}); - -const formFieldLabelStencil = createStencil({ +export const formFieldLabelStencil = createStencil({ + extends: textStencil, + // @ts-ignore Still weird about CSS variables base: { - ...system.type.subtext.large, fontWeight: system.fontWeight.medium, color: system.color.text.default, paddingInlineStart: system.space.zero, @@ -41,63 +29,41 @@ const formFieldLabelStencil = createStencil({ display: 'flex', alignItems: 'center', minWidth: px2rem(180), - }, - modifiers: { - orientation: { - horizontal: { - float: 'left', - maxHeight: system.space.x10, - }, - vertical: { - width: '100%', + + // asterisk + [parentModifier(formFieldStencil.modifiers.required.true)]: { + '&::after': { + content: '"*"', + fontSize: system.fontSize.body.large, + fontWeight: system.fontWeight.normal, + color: brand.error.base, + textDecoration: 'unset', + marginInlineStart: system.space.x1, }, }, - variant: { - error: { - color: system.color.text.critical.default, - }, - hint: { - color: system.color.text.hint, - }, - inverse: { - color: system.color.text.inverse, - }, + + // orientation modifier from parent FormField + [parentModifier(formFieldStencil.modifiers.orientation.horizontal)]: { + float: 'left', + maxHeight: system.space.x10, }, - disabled: { - true: { - cursor: 'default', - color: system.color.text.disabled, - }, + [parentModifier(formFieldStencil.modifiers.orientation.vertical)]: { + width: '100%', }, }, - compound: [ - { - modifiers: {variant: 'inverse', disabled: true}, - styles: { - opacity: system.opacity.disabled, - color: system.color.text.inverse, - }, - }, - ], + defaultModifiers: { + typeLevel: 'subtext.large', + }, }); export const FormFieldLabel = createSubcomponent('label')({ displayName: 'FormField.Label', modelHook: useFormFieldModel, elemPropsHook: useFormFieldLabel, -})(({children, disabled, variant, ...elemProps}, Element, model) => { +})(({children, typeLevel, variant, ...elemProps}, Element) => { return ( - + {children} - {model.state.isRequired && ( - - )} ); }); diff --git a/modules/preview-react/form-field/lib/formFieldStencil.ts b/modules/preview-react/form-field/lib/formFieldStencil.ts new file mode 100644 index 0000000000..bdd9c2ce44 --- /dev/null +++ b/modules/preview-react/form-field/lib/formFieldStencil.ts @@ -0,0 +1,42 @@ +import {createStencil} from '@workday/canvas-kit-styling'; +import {system} from '@workday/canvas-tokens-web'; + +export const formFieldStencil = createStencil({ + base: { + display: 'flex', + border: 'none', + padding: system.space.zero, + margin: `${system.space.zero} ${system.space.zero} ${system.space.x6}`, + }, + modifiers: { + grow: { + true: { + width: '100%', + '[data-width="ck-formfield-width"]': { + width: '100%', + }, + }, + }, + orientation: { + horizontal: { + flexDirection: 'row', + gap: system.space.x8, + }, + vertical: { + flexDirection: 'column', + gap: system.space.x1, + alignItems: 'flex-start', + }, + }, + required: { + true: {}, + }, + error: { + error: {}, + alert: {}, + }, + }, + defaultModifiers: { + orientation: 'vertical', + }, +}); diff --git a/modules/preview-react/form-field/lib/hooks/useFormFieldModel.tsx b/modules/preview-react/form-field/lib/hooks/useFormFieldModel.tsx index 160583fbf7..814fdd4dbc 100644 --- a/modules/preview-react/form-field/lib/hooks/useFormFieldModel.tsx +++ b/modules/preview-react/form-field/lib/hooks/useFormFieldModel.tsx @@ -8,11 +8,6 @@ export const useFormFieldModel = createModelHook({ * If value is `alert`: A visual orange warning ring is added to the `FormField.Input`. */ error: undefined as undefined | 'error' | 'alert', - /** - * The direction the child elements should stack - * @default vertical - */ - orientation: 'vertical' as 'vertical' | 'horizontal', /** * Optional `id` provided to `FormField`'s subcomponents as HTML attributes: * - `FormField.Input` will set `aria-describedby` to `hint-${id}` diff --git a/modules/preview-react/form-field/lib/hooks/useFormFieldOrientation.tsx b/modules/preview-react/form-field/lib/hooks/useFormFieldOrientation.tsx index c1bf53e60b..69105d3217 100644 --- a/modules/preview-react/form-field/lib/hooks/useFormFieldOrientation.tsx +++ b/modules/preview-react/form-field/lib/hooks/useFormFieldOrientation.tsx @@ -3,6 +3,8 @@ import {space} from '@workday/canvas-kit-react/tokens'; /** * Adds the necessary layout props to a `FormField` component. + * + * @deprecated */ export const useFormFieldOrientation = (orientation: 'horizontal' | 'vertical') => { let layoutProps: { diff --git a/modules/preview-react/form-field/stories/examples/Custom.tsx b/modules/preview-react/form-field/stories/examples/Custom.tsx index 92dbcd9218..be540db3a8 100644 --- a/modules/preview-react/form-field/stories/examples/Custom.tsx +++ b/modules/preview-react/form-field/stories/examples/Custom.tsx @@ -4,7 +4,7 @@ import { useFormFieldInput, useFormFieldLabel, useFormFieldModel, - useFormFieldOrientation, + formFieldStencil, } from '@workday/canvas-kit-preview-react/form-field'; import {useModelContext} from '@workday/canvas-kit-react/common'; import {Flex} from '@workday/canvas-kit-react/layout'; @@ -44,10 +44,8 @@ export const Custom = () => { const model = useFormFieldModel({isRequired: true}); - const layoutProps = useFormFieldOrientation('vertical'); - return ( - + You can be anything diff --git a/modules/preview-react/text-area/lib/TextArea.tsx b/modules/preview-react/text-area/lib/TextArea.tsx index 132b17216a..b45766cd3a 100644 --- a/modules/preview-react/text-area/lib/TextArea.tsx +++ b/modules/preview-react/text-area/lib/TextArea.tsx @@ -1,8 +1,8 @@ import React from 'react'; import {createContainer, ExtractProps} from '@workday/canvas-kit-react/common'; -import {FormField, useFormFieldOrientation} from '@workday/canvas-kit-preview-react/form-field'; -import {Flex} from '@workday/canvas-kit-react/layout'; +import {FormField, formFieldStencil} from '@workday/canvas-kit-preview-react/form-field'; +import {mergeStyles} from '@workday/canvas-kit-react/layout'; import {TextAreaField} from './TextAreaField'; import {useTextInputModel} from '@workday/canvas-kit-preview-react/text-input'; @@ -17,6 +17,7 @@ export interface TextAreaProps extends ExtractProps { } /** + * @stencil formFieldStencil * @deprecated ⚠️ `TextArea` in Preview has been deprecated and will be removed in a future major version. Please use [`FormField` in Preview](https://workday.github.io/canvas-kit/?path=/story/preview-inputs-form-field--basic) instead. */ export const TextArea = createContainer('div')({ @@ -27,12 +28,20 @@ export const TextArea = createContainer('div')({ Label: FormField.Label, Hint: FormField.Hint, }, -})(({children, orientation = 'vertical', ...elemProps}, Element) => { - const layoutProps = useFormFieldOrientation(orientation); - +})(({children, grow, orientation, ...elemProps}, Element, model) => { return ( - + {children} - + ); }); diff --git a/modules/preview-react/text-input/lib/TextInput.tsx b/modules/preview-react/text-input/lib/TextInput.tsx index ac3229e4f7..106f4ef5d3 100644 --- a/modules/preview-react/text-input/lib/TextInput.tsx +++ b/modules/preview-react/text-input/lib/TextInput.tsx @@ -4,9 +4,9 @@ import {createContainer, ExtractProps} from '@workday/canvas-kit-react/common'; import { FormField, useFormFieldModel, - useFormFieldOrientation, + formFieldStencil, } from '@workday/canvas-kit-preview-react/form-field'; -import {Flex} from '@workday/canvas-kit-react/layout'; +import {mergeStyles} from '@workday/canvas-kit-react/layout'; import {TextInputField} from './TextInputField'; @@ -20,6 +20,7 @@ export interface TextInputProps extends ExtractProps { children: React.ReactNode; } /** + * @stencil formFieldStencil * @deprecated ⚠️ `TextInput` in Preview has been deprecated and will be removed in a future major version. Please use [`FormField` in Preview](https://workday.github.io/canvas-kit/?path=/story/preview-inputs-form-field--basic) instead. */ export const TextInput = createContainer('div')({ @@ -31,13 +32,21 @@ export const TextInput = createContainer('div')({ Hint: FormField.Hint, }, })>( - ({children, orientation = 'vertical', ...elemProps}, Element) => { - const layoutProps = useFormFieldOrientation(orientation); - + ({children, orientation, grow, ...elemProps}, Element, model) => { return ( - + {children} - + ); } ); diff --git a/modules/preview-react/text-input/spec/TextInput.spec.tsx b/modules/preview-react/text-input/spec/TextInput.spec.tsx index 588a2f5d55..0f0881bd3a 100644 --- a/modules/preview-react/text-input/spec/TextInput.spec.tsx +++ b/modules/preview-react/text-input/spec/TextInput.spec.tsx @@ -79,18 +79,6 @@ describe('Text Input', () => { }); }); - describe('when rendered as required', () => { - it('should add an asterisk to the label to indicate that it is required', () => { - const {getByText} = render( - - Test - - ); - - expect(getByText('*')).toHaveAttribute('aria-hidden', 'true'); - }); - }); - describe('when rendered a hint id', () => { it('the input and hint text should have matching ids for accessibility', () => { const hintId = 'hintId'; diff --git a/modules/styling-transform/lib/utils/handleParentModifier.ts b/modules/styling-transform/lib/utils/handleParentModifier.ts index 5ac8080b8d..4de8cea075 100644 --- a/modules/styling-transform/lib/utils/handleParentModifier.ts +++ b/modules/styling-transform/lib/utils/handleParentModifier.ts @@ -14,7 +14,7 @@ export const handleParentModifier = createPropertyTransform((node, context) => { node.expression.expression.text === 'parentModifier' ) { const args = node.expression.arguments.map(arg => parseNodeToStaticValue(arg, context)); - const hash = args[0].toString().replace('css-', ''); + const hash = args[0].toString().replace('css-', 'm'); // add a mapping from `css-{hash}` to `{hash}` for extraction string replacement names[args[0]] = hash; diff --git a/modules/styling-transform/spec/utils/handleCreateStencil.spec.ts b/modules/styling-transform/spec/utils/handleCreateStencil.spec.ts index 06cb71f663..273237510a 100644 --- a/modules/styling-transform/spec/utils/handleCreateStencil.spec.ts +++ b/modules/styling-transform/spec/utils/handleCreateStencil.spec.ts @@ -394,7 +394,7 @@ describe('handleCreateStencil', () => { // runtime selector expect(result).toContain( - `.${names['buttonStencil.modifiers.size.large'].replace('css-', '')} :where(&){color:blue;}` + `.${names['buttonStencil.modifiers.size.large'].replace('css-', 'm')} :where(&){color:blue;}` ); // extracted selector diff --git a/modules/styling-transform/spec/utils/handleParentModifier.spec.ts b/modules/styling-transform/spec/utils/handleParentModifier.spec.ts index f129c1f622..a6cdc837ed 100644 --- a/modules/styling-transform/spec/utils/handleParentModifier.spec.ts +++ b/modules/styling-transform/spec/utils/handleParentModifier.spec.ts @@ -50,20 +50,20 @@ describe('handleParentModifier', () => { it('should add a mapping from the CSS class name to the hash to the names cache', () => { expect(names).toHaveProperty( names['buttonStencil.modifiers.size.large'], - names['buttonStencil.modifiers.size.large'].replace('css-', '') + names['buttonStencil.modifiers.size.large'].replace('css-', 'm') ); }); it('should add a mapping from the hash to the extracted CSS class name to the extractedNames cache', () => { expect(extractedNames).toHaveProperty( - names['buttonStencil.modifiers.size.large'].replace('css-', ''), + names['buttonStencil.modifiers.size.large'].replace('css-', 'm'), 'css-button--size-large' ); }); it('should transform the runtime to include a selector with only the hash', () => { expect(result).toContain( - `.${names['buttonStencil.modifiers.size.large'].replace('css-', '')} :where(&){color:blue;}` + `.${names['buttonStencil.modifiers.size.large'].replace('css-', 'm')} :where(&){color:blue;}` ); }); diff --git a/modules/styling/lib/cs.ts b/modules/styling/lib/cs.ts index 12bd7deb87..376deba31a 100644 --- a/modules/styling/lib/cs.ts +++ b/modules/styling/lib/cs.ts @@ -1066,7 +1066,7 @@ function combineClassNames(input: (string | undefined)[]): string { * ``` */ export function parentModifier(value: string) { - return `.${value.replace('css-', '')} :where(&)`; + return `.${value.replace('css-', 'm')} :where(&)`; } /** @@ -1170,10 +1170,7 @@ export function createStencil< // will remain for the `parentModifier` function to still select on. We decided to add these // inert class names instead of adding `data-m-*` attributes because the output DOM looks // much cleaner and it saves bytes on the bundled output. - modifierClasses - .split(' ') - .map(c => c.replace('css-', '')) - .join(' '), + modifierClasses.replace(/css-/g, 'm'), compound ? _compound(inputModifiers) : '', ]), style: {...composesReturn?.style, ..._vars(input || {})}, diff --git a/modules/styling/spec/cs.spec.tsx b/modules/styling/spec/cs.spec.tsx index 56b7d29519..c4b139863d 100644 --- a/modules/styling/spec/cs.spec.tsx +++ b/modules/styling/spec/cs.spec.tsx @@ -599,7 +599,7 @@ describe('cs', () => { expect(className).toEqual( `${myStencil.base} ${ myStencil.modifiers.size.large - } ${myStencil.modifiers.size.large.replace('css-', '')}` + } ${myStencil.modifiers.size.large.replace('css-', 'm')}` ); });