From 1caf38f5f8f19b939599613447fe3a4245a7646e Mon Sep 17 00:00:00 2001 From: theojungle Date: Wed, 13 Sep 2023 18:26:40 +0200 Subject: [PATCH] feat: upgrade hint on checkable inputs --- docs/pages/components/checkbox.mdx | 14 +++--- docs/pages/components/radio-group.mdx | 5 +- docs/pages/components/toggle.mdx | 10 ++-- docs/pages/theming/customize.mdx | 2 +- packages/Core/src/theme/radios.ts | 15 +----- packages/Core/src/theme/toggles.ts | 4 +- packages/Field/package.json | 3 +- packages/Field/src/index.tsx | 69 +++++++++++++++++---------- packages/Field/src/layout.ts | 7 --- packages/Field/src/styles.ts | 55 +++++++++++---------- packages/Field/tests/index.test.tsx | 8 ++-- packages/FieldGroup/src/styles.ts | 5 +- packages/Label/src/styles.ts | 11 ----- packages/Radio/src/index.tsx | 16 ++++--- packages/Radio/src/styles.ts | 27 +++++------ packages/RadioGroup/src/index.tsx | 3 -- packages/RadioGroup/src/styles.ts | 2 +- packages/Stack/src/index.tsx | 5 +- packages/Stack/tests/index.test.tsx | 15 +++--- 19 files changed, 128 insertions(+), 148 deletions(-) delete mode 100644 packages/Field/src/layout.ts diff --git a/docs/pages/components/checkbox.mdx b/docs/pages/components/checkbox.mdx index 3e75723c8a..40275638dc 100644 --- a/docs/pages/components/checkbox.mdx +++ b/docs/pages/components/checkbox.mdx @@ -24,7 +24,7 @@ function() { const [checkboxIndeterminate, setCheckboxIndeterminate] = React.useState(false) return ( - <> + + - + ) } ``` @@ -132,7 +132,7 @@ function() { const [checkbox, setCheckbox] = React.useState(false) return ( - <> + setCheckbox(!checkbox)} /> - + ) } ``` @@ -189,7 +189,7 @@ function() { } return ( - <> + setCheckboxB(!checkboxB)} /> - + ) } ``` diff --git a/docs/pages/components/radio-group.mdx b/docs/pages/components/radio-group.mdx index 90c48f842d..4a38d2b729 100644 --- a/docs/pages/components/radio-group.mdx +++ b/docs/pages/components/radio-group.mdx @@ -90,12 +90,14 @@ function () { ## With hint on radio +An example with a hint on radio. + ```jsx function () { const [value, setValue] = React.useState(null) return ( - + + setToggle(!toggle)} /> setToggleChecked(!toggleChecked)} /> @@ -45,7 +45,7 @@ function() { const [toggleChecked, setToggleChecked] = React.useState(true) return ( - <> + setToggle(!toggle)} /> @@ -58,7 +58,7 @@ function() { - + ) } ``` @@ -88,7 +88,7 @@ function() { const [toggle, setToggle] = React.useState(false) return ( - <> + setToggle(!toggle)} /> @@ -101,7 +101,7 @@ function() { setToggle(!toggle)} /> - + ) } ``` diff --git a/docs/pages/theming/customize.mdx b/docs/pages/theming/customize.mdx index 9cda62c129..bed9702f5c 100644 --- a/docs/pages/theming/customize.mdx +++ b/docs/pages/theming/customize.mdx @@ -142,7 +142,7 @@ export default function Root() { ## Change fonts -If you want to change the url path for the fonts, you can change the property `fontsUrl` on `createTheme()`. By default the fonts are served from the welcome-ui.com domain. In our case at Welcome to the Jungle, we want to have the fonts served from the same domain name as our main website. +If you want to change the url path for the fonts, you can change the property `fontsUrl` on `createTheme()`. By default the fonts are served from the welcome-ui.com domain. In our case at Welcome to the Jungle, we want to have the fonts served from the same domain name as our main website. ```jsx live=false const theme = createTheme({ fontsUrl: 'https://cdn.welcometothejungle.com/fonts', ...yourTheme }) diff --git a/packages/Core/src/theme/radios.ts b/packages/Core/src/theme/radios.ts index fb4cb21b14..d7f91f57be 100644 --- a/packages/Core/src/theme/radios.ts +++ b/packages/Core/src/theme/radios.ts @@ -9,14 +9,10 @@ export type ThemeRadios = { default: string disabled: string } - withHint: { - default: CSSObject - hint: CSSObject - } } export const getRadios = (theme: WuiTheme): ThemeRadios => { - const { colors, fontSizes, space, toRem } = theme + const { colors, toRem } = theme return { default: { @@ -31,14 +27,5 @@ export const getRadios = (theme: WuiTheme): ThemeRadios => { default: colors['primary-500'], disabled: colors['nude-600'], }, - withHint: { - default: { - fontSize: fontSizes.md, - color: colors['dark-900'], - }, - hint: { - marginTop: space.xs, - }, - }, } } diff --git a/packages/Core/src/theme/toggles.ts b/packages/Core/src/theme/toggles.ts index 5f037cd1f5..e201a6a097 100644 --- a/packages/Core/src/theme/toggles.ts +++ b/packages/Core/src/theme/toggles.ts @@ -18,9 +18,9 @@ export const getToggles = (theme: WuiTheme): ThemeToggles => { return { item: { default: { - width: toRem(26), + width: toRem(24), height: toRem(16), - borderRadius: toRem(9), + borderRadius: toRem(16), backgroundColor: colors['light-900'], borderColor: colors.border, borderWidth: borderWidths.sm, diff --git a/packages/Field/package.json b/packages/Field/package.json index eabaccf965..beae3eff21 100644 --- a/packages/Field/package.json +++ b/packages/Field/package.json @@ -50,7 +50,8 @@ "@welcome-ui/hint": "^5.0.0", "@welcome-ui/label": "^5.0.0", "@welcome-ui/system": "^5.0.0", - "@welcome-ui/utils": "^5.0.0" + "@welcome-ui/utils": "^5.0.0", + "@welcome-ui/variant-icon": "^5.0.0" }, "peerDependencies": { "@xstyled/styled-components": "^3.7.3", diff --git a/packages/Field/src/index.tsx b/packages/Field/src/index.tsx index 0f0f283ef3..668d6dfd3a 100644 --- a/packages/Field/src/index.tsx +++ b/packages/Field/src/index.tsx @@ -1,11 +1,10 @@ -import React, { Fragment } from 'react' +import React from 'react' import { Label } from '@welcome-ui/label' import { Hint } from '@welcome-ui/hint' import { CreateWuiProps, forwardRef } from '@welcome-ui/system' import { useIsomorphicLayoutEffect } from '@welcome-ui/utils' +import { VariantIcon } from '@welcome-ui/variant-icon' -// Fields -import { RowContainer } from './layout' import * as S from './styles' import { forwardedProps, generateRandomId, getBaseType, getVariant } from './utils' @@ -47,12 +46,13 @@ export const Field = forwardRef<'div', FieldProps>( ) => { const baseType = getBaseType(children.props.type || children.type.displayName) const isRadio = baseType === 'radio' - const isToggle = children.type.displayName === 'Toggle' + const isRadioGroup = baseType === 'RadioGroup' + const isFieldGroup = baseType === 'FieldGroup' const isCheckbox = baseType === 'checkbox' + const isToggle = children.type.displayName === 'Toggle' const isCheckable = isRadio || isCheckbox || isToggle const layout = flexDirection || (isCheckable ? 'row' : 'column') - const isGroup = ['FieldGroup', 'RadioGroup'].includes(baseType) - const Container = layout === 'row' ? RowContainer : Fragment + const isGroup = isFieldGroup || isRadioGroup const variant = getVariant({ error, warning, success, info }) const hintText = variant ? error || warning || success || info : hint const withHintText = !!hintText @@ -76,34 +76,51 @@ export const Field = forwardRef<'div', FieldProps>( } }) }, [children.props, children.type.displayName, htmlFor]) - return ( - - {label && ( - - )} - {!isCheckable && child} - - {hintText && ( - + + {isCheckable && child} + + {label && ( + + )} + {/* for a checkable field we add a hint below label name */} + {isCheckable && hintText && ( + + {hintText} + + )} + + + {!isCheckable && child} + {!isCheckable && hintText && ( + {hintText} )} diff --git a/packages/Field/src/layout.ts b/packages/Field/src/layout.ts deleted file mode 100644 index 601dbb0f8e..0000000000 --- a/packages/Field/src/layout.ts +++ /dev/null @@ -1,7 +0,0 @@ -import styled from '@xstyled/styled-components' - -export const RowContainer = styled.div` - display: flex; - align-items: center; - justify-content: flex-start; -` diff --git a/packages/Field/src/styles.ts b/packages/Field/src/styles.ts index 945ffa8adf..2467457f05 100644 --- a/packages/Field/src/styles.ts +++ b/packages/Field/src/styles.ts @@ -1,55 +1,58 @@ import styled, { css, system, th } from '@xstyled/styled-components' import { StyledLabel } from '@welcome-ui/label' -import { StyledHint } from '@welcome-ui/hint' -import { StyledFieldGroup } from '@welcome-ui/field-group' import { shouldForwardProp, WuiProps } from '@welcome-ui/system' import { FieldIconSize } from '@welcome-ui/utils' -const rowStyles = css` - margin-right: lg; -` - -const columnStyles = css` - margin-bottom: sm; -` - const checkableFieldStyles = css` ${th('defaultFields.checkablelabel.default')}; align-items: flex-start; overflow-wrap: break-word; - - input { - margin-top: xs; - } ` type StyledFieldProps = { - isCheckable?: boolean - flexDirection: WuiProps['flexDirection'] checked?: boolean + flexDirection: WuiProps['flexDirection'] + isCheckable?: boolean + isRadioGroup?: boolean withHintText?: boolean } export const Field = styled('div').withConfig({ shouldForwardProp })( - ({ checked, flexDirection, isCheckable, withHintText }) => css` - ${StyledFieldGroup} { - margin-bottom: ${isCheckable && 'xxs'}; - } + ({ checked, isCheckable, isRadioGroup, withHintText }) => css` + ${isCheckable && + css` + input { + margin-top: xs; + } + `} ${StyledLabel} { - ${flexDirection === 'row' && rowStyles}; - ${flexDirection === 'column' && !isCheckable && columnStyles}; ${isCheckable && checkableFieldStyles}; ${isCheckable && withHintText && th('defaultFields.checkablelabel.default')} ${checked && th('defaultFields.checkablelabel.checked')} - margin-bottom: ${!withHintText && 'xs'} - } - ${StyledHint} { - margin-bottom: ${isCheckable && 'xxs'}; + ${!isCheckable && + css` + margin-bottom: sm; + `} + ${isRadioGroup && + css` + margin-bottom: md; + `} } ${system}; ` ) +export const Label = styled.div` + display: flex; + align-items: flex-start; + gap: sm; +` + +export const LabelWithHint = styled.div` + display: flex; + flex-direction: column; +` + type IconWrapperProps = { iconPlacement: 'left' | 'right' size?: FieldIconSize diff --git a/packages/Field/tests/index.test.tsx b/packages/Field/tests/index.test.tsx index b32a413702..79dc778c5f 100644 --- a/packages/Field/tests/index.test.tsx +++ b/packages/Field/tests/index.test.tsx @@ -1,5 +1,6 @@ import React, { HTMLInputTypeAttribute } from 'react' import userEvent from '@testing-library/user-event' +import { screen } from '@testing-library/dom' import { render } from '../../../utils/tests' import { Field } from '../src' @@ -23,12 +24,13 @@ const errorText = 'Error' describe('', () => { it('should render', () => { - const { getByTestId } = render( + render( ) - const field = getByTestId('field') + + const field = screen.getByTestId('field') expect(field).toBeInTheDocument() const input = field.querySelector('input') @@ -37,7 +39,7 @@ describe('', () => { const label = field.querySelector('label') expect(label).not.toBeInTheDocument() - const hint = field.querySelector('div') + const hint = screen.getByTestId('field-hint') expect(hint).not.toBeInTheDocument() }) diff --git a/packages/FieldGroup/src/styles.ts b/packages/FieldGroup/src/styles.ts index 90fce2bbd9..8d0724df22 100644 --- a/packages/FieldGroup/src/styles.ts +++ b/packages/FieldGroup/src/styles.ts @@ -1,5 +1,4 @@ import styled, { system, th } from '@xstyled/styled-components' -import { StyledLabel } from '@welcome-ui/label' import { shouldForwardProp } from '@welcome-ui/system' export const FieldGroup = styled('fieldset').withConfig({ shouldForwardProp })` @@ -11,7 +10,7 @@ export const FieldGroup = styled('fieldset').withConfig({ shouldForwardProp })` ${th('defaultFields.fieldset')}; ${system}; - & > ${StyledLabel} { - margin-bottom: xs; + > * { + margin-bottom: md; } ` diff --git a/packages/Label/src/styles.ts b/packages/Label/src/styles.ts index 9d4d572a09..3f700e7f58 100644 --- a/packages/Label/src/styles.ts +++ b/packages/Label/src/styles.ts @@ -1,6 +1,5 @@ import styled, { css, system, th } from '@xstyled/styled-components' import { shouldForwardProp } from '@welcome-ui/system' -import { getVariantColor, Variant } from '@welcome-ui/utils' export const Label = styled('label').withConfig({ shouldForwardProp })<{ required: boolean @@ -45,13 +44,3 @@ export const Disabled = styled.div` display: inline-flex; margin-right: xs; ` - -export const Icon = styled.div<{ variant: Variant }>( - ({ variant }) => css` - display: inline-flex; - margin-right: xs; - color: ${getVariantColor(variant)}; - fill: ${getVariantColor(variant)}; - flex-shrink: 0; - ` -) diff --git a/packages/Radio/src/index.tsx b/packages/Radio/src/index.tsx index 0338550641..54ac10e360 100644 --- a/packages/Radio/src/index.tsx +++ b/packages/Radio/src/index.tsx @@ -2,6 +2,7 @@ import React from 'react' import { LabelOptions } from '@welcome-ui/label' import { CreateWuiProps, forwardRef } from '@welcome-ui/system' import { DefaultFieldStylesProps } from '@welcome-ui/utils' +import { Hint } from '@welcome-ui/hint' import * as S from './styles' @@ -10,7 +11,6 @@ export type RadioOptions = { label?: string onChange?: (event: React.MouseEvent) => void onClick?: (event: React.MouseEvent) => void - withHint?: boolean } & DefaultFieldStylesProps export type RadioProps = CreateWuiProps<'input', RadioOptions & LabelOptions> @@ -28,7 +28,6 @@ export const Radio = forwardRef<'input', RadioProps>( onChange, onClick, variant, - withHint, ...rest }, ref @@ -49,7 +48,6 @@ export const Radio = forwardRef<'input', RadioProps>( onClick={handleClick} variant={variant} withDisabledIcon={false} - withHint={withHint || !!hint} > @@ -62,11 +60,15 @@ export const Radio = forwardRef<'input', RadioProps>( {...rest} /> -
{label}
+ +
{label}
+ {hint && ( + + {hint} + + )} +
- {hint && ( - {hint} - )} ) } diff --git a/packages/Radio/src/styles.ts b/packages/Radio/src/styles.ts index a5d31ac029..eebc486592 100644 --- a/packages/Radio/src/styles.ts +++ b/packages/Radio/src/styles.ts @@ -2,7 +2,6 @@ import styled, { css, system, th } from '@xstyled/styled-components' import { Box } from '@welcome-ui/box' import { shouldForwardProp } from '@welcome-ui/system' import { defaultFieldStyles } from '@welcome-ui/utils' -import { Hint as HintWUI } from '@welcome-ui/hint' import { Label as WUILabel } from '@welcome-ui/label' import * as Ariakit from '@ariakit/react' @@ -53,21 +52,12 @@ export const Radio = styled(Ariakit.Radio).withConfig({ shouldForwardProp })( - ({ withHint }) => css` - ${withHint && th('radios.withHint.default')}; - max-width: 100%; - align-items: flex-start; - /** we need to reset margin-bottom from Label component */ - margin-bottom: 0 !important; - ${system} - ` -) - -export const Hint = styled(HintWUI)` - ${th('radios.withHint.hint')}; +export const Label = styled(WUILabel)` + max-width: 100%; + align-items: flex-start; + /** we need to reset margin-bottom from Label component */ + margin-bottom: 0 !important; + ${system} ` export const Input = styled.div` @@ -86,3 +76,8 @@ export const Wrapper = styled(Box)` margin-top: xs; } ` + +export const LabelWithHint = styled.div` + display: flex; + flex-direction: column; +` diff --git a/packages/RadioGroup/src/index.tsx b/packages/RadioGroup/src/index.tsx index bc4d1b7f7b..d9a9f977e0 100644 --- a/packages/RadioGroup/src/index.tsx +++ b/packages/RadioGroup/src/index.tsx @@ -53,8 +53,6 @@ export const RadioGroup = forwardRef<'fieldset', RadioGroupProps>( onChange?.(valueSelected) } - const withHint = options.some((obj: Option) => 'hint' in obj) - return ( ( name={name} onChange={() => handleChange(option.value)} value={option.value} - withHint={withHint} {...rest} /> ))} diff --git a/packages/RadioGroup/src/styles.ts b/packages/RadioGroup/src/styles.ts index ecf451a7af..692e5b43f8 100644 --- a/packages/RadioGroup/src/styles.ts +++ b/packages/RadioGroup/src/styles.ts @@ -8,7 +8,7 @@ export const Radios = styled.div<{ display: flex; flex-direction: ${flexDirection}; flex-wrap: wrap; - gap: xs; + gap: md; ${system}; ` diff --git a/packages/Stack/src/index.tsx b/packages/Stack/src/index.tsx index 6fcbe6a52c..dcfd5aa2a8 100644 --- a/packages/Stack/src/index.tsx +++ b/packages/Stack/src/index.tsx @@ -13,25 +13,22 @@ export const Stack = forwardRef<'div', StackProps>( ({ as = 'div', children, dataTestId, direction = 'column', spacing = 'md', ...rest }, ref) => { const validChildrenArray = Children.toArray(children).filter(isValidElement) - const marginType = direction === 'column' ? 'mb' : 'mr' - return ( {validChildrenArray.map((child, i) => { - const isLastChild = validChildrenArray.length === i + 1 const childAs: React.ElementType = as === 'ol' || as === 'ul' ? 'li' : 'div' const childProps = { as: childAs, key: `stack-item-${i}`, - [marginType]: isLastChild ? null : spacing, } // eslint-disable-next-line react/jsx-key diff --git a/packages/Stack/tests/index.test.tsx b/packages/Stack/tests/index.test.tsx index 3480701571..c59dc43107 100644 --- a/packages/Stack/tests/index.test.tsx +++ b/packages/Stack/tests/index.test.tsx @@ -1,23 +1,21 @@ import React from 'react' +import { screen } from '@testing-library/dom' import { render } from '../../../utils/tests' -import { createTheme } from '../../Core/src/theme/core' import { Stack } from '../src' const content = [
Foo
,
Bar
] describe('', () => { it('should render correctly with no props', () => { - const { getByText } = render({content}) - const foo = getByText('Foo') - const bar = getByText('Bar') + render({content}) + + const stack = screen.getByTestId('stack') - expect(getComputedStyle(foo.parentElement).marginBottom).toBe('0.75rem') - expect(getComputedStyle(bar.parentElement).marginBottom).toBe('') + expect(stack).toHaveTextContent('Foo') }) it('should render correctly with custom props', () => { - const theme = createTheme() const { getByText } = render( {content} @@ -25,7 +23,6 @@ describe('', () => { ) const foo = getByText('Foo') - expect(getComputedStyle(foo.parentElement).marginRight).toBe(theme.space.xl) - expect(foo.parentElement.tagName).toBe('LI') + expect(foo?.parentElement?.tagName).toBe('LI') }) })