diff --git a/.changeset/true-stars-listen.md b/.changeset/true-stars-listen.md new file mode 100644 index 00000000..0533af66 --- /dev/null +++ b/.changeset/true-stars-listen.md @@ -0,0 +1,5 @@ +--- +"@devup-ui/components": patch +--- + +Feat toggle diff --git a/packages/components/src/components/Radio/Radio.stories.tsx b/packages/components/src/components/Radio/Radio.stories.tsx index 794101aa..d97642ba 100644 --- a/packages/components/src/components/Radio/Radio.stories.tsx +++ b/packages/components/src/components/Radio/Radio.stories.tsx @@ -8,20 +8,6 @@ export default { export const Default = { args: { checked: undefined, - colors: { - primary: 'var(--primary)', - border: 'var(--border)', - text: 'var(--text)', - bg: 'var(--bg)', - hoverBg: 'var(--hoverBg)', - hoverBorder: 'var(--hoverBorder)', - hoverColor: 'var(--hoverColor)', - checkedBg: 'var(--checkedBg)', - checkedBorder: 'var(--checkedBorder)', - checkedColor: 'var(--checkedColor)', - disabledBg: 'var(--disabledBg)', - disabledColor: 'var(--disabledColor)', - }, name: 'radio', children: '옵션1', variant: 'default', diff --git a/packages/components/src/components/Radio/__tests__/__snapshots__/index.browser.test.tsx.snap b/packages/components/src/components/Radio/__tests__/__snapshots__/index.browser.test.tsx.snap index 9e29e522..b12130b5 100644 --- a/packages/components/src/components/Radio/__tests__/__snapshots__/index.browser.test.tsx.snap +++ b/packages/components/src/components/Radio/__tests__/__snapshots__/index.browser.test.tsx.snap @@ -28,7 +28,7 @@ exports[`Radio > should Radio snapshot 2`] = ` type="radio" />
@@ -69,7 +69,7 @@ exports[`Radio > should Radio snapshot 4`] = ` />
@@ -111,7 +111,7 @@ exports[`Radio > should Radio snapshot 6`] = ` />
@@ -154,7 +154,7 @@ exports[`Radio > should Radio snapshot 8`] = ` />
@@ -192,7 +192,7 @@ exports[`Radio > should Radio snapshot 10`] = ` type="radio" />
@@ -210,7 +210,7 @@ exports[`Radio > should Radio snapshot 11`] = ` type="radio" />
diff --git a/packages/components/src/components/Radio/index.tsx b/packages/components/src/components/Radio/index.tsx index 677c860d..b6b5e921 100644 --- a/packages/components/src/components/Radio/index.tsx +++ b/packages/components/src/components/Radio/index.tsx @@ -144,7 +144,7 @@ export function Radio({ border="1px solid" borderColor="$border" borderRadius={ - firstButton ? '6px 0 0 6px' : lastButton ? '0 6px 6px 0' : undefined + firstButton ? '8px 0 0 8px' : lastButton ? '0 8px 8px 0' : undefined } className={className} color="var(--text, light-dark(#000, #fff))" @@ -152,7 +152,7 @@ export function Radio({ data-radio-button display="flex" px={8} - py={4} + py={3} selectors={{ // checked '[data-radio-input]:checked + &:not([aria-disabled=true])': { diff --git a/packages/components/src/components/RadioGroup/RadioGroup.stories.tsx b/packages/components/src/components/RadioGroup/RadioGroup.stories.tsx index f6ad479e..43faab38 100644 --- a/packages/components/src/components/RadioGroup/RadioGroup.stories.tsx +++ b/packages/components/src/components/RadioGroup/RadioGroup.stories.tsx @@ -9,20 +9,6 @@ export const Default = { args: { disabled: false, name: 'radio', - colors: { - primary: 'var(--primary)', - border: 'var(--border)', - text: 'var(--text)', - bg: 'var(--bg)', - hoverBg: 'var(--hoverBg)', - hoverBorder: 'var(--hoverBorder)', - hoverColor: 'var(--hoverColor)', - checkedBg: 'var(--checkedBg)', - checkedBorder: 'var(--checkedBorder)', - checkedColor: 'var(--checkedColor)', - disabledBg: 'var(--disabledBg)', - disabledColor: 'var(--disabledColor)', - }, options: [ { value: '1', diff --git a/packages/components/src/components/RadioGroup/__tests__/__snapshots__/index.browser.test.tsx.snap b/packages/components/src/components/RadioGroup/__tests__/__snapshots__/index.browser.test.tsx.snap index 76e41e1d..c15f6b19 100644 --- a/packages/components/src/components/RadioGroup/__tests__/__snapshots__/index.browser.test.tsx.snap +++ b/packages/components/src/components/RadioGroup/__tests__/__snapshots__/index.browser.test.tsx.snap @@ -179,7 +179,7 @@ exports[`RadioGroup > should RadioGroup snapshot 5`] = ` type="radio" />
옵션 1 @@ -194,7 +194,7 @@ exports[`RadioGroup > should RadioGroup snapshot 5`] = ` type="radio" />
옵션 2 @@ -222,7 +222,7 @@ exports[`RadioGroup > should RadioGroup snapshot 6`] = ` />
옵션 1 @@ -240,7 +240,7 @@ exports[`RadioGroup > should RadioGroup snapshot 6`] = ` />
옵션 2 @@ -383,7 +383,7 @@ exports[`RadioGroup > should RadioGroup snapshot 10`] = ` type="radio" />
@@ -399,7 +399,7 @@ exports[`RadioGroup > should RadioGroup snapshot 10`] = ` type="radio" />
diff --git a/packages/components/src/components/Toggle/Toggle.stories.tsx b/packages/components/src/components/Toggle/Toggle.stories.tsx new file mode 100644 index 00000000..47f51fcc --- /dev/null +++ b/packages/components/src/components/Toggle/Toggle.stories.tsx @@ -0,0 +1,27 @@ +import { Meta, StoryObj } from '@storybook/react-vite' + +import { Toggle } from './index' + +type Story = StoryObj + +const meta: Meta = { + title: 'Devfive/Toggle', + component: Toggle, + decorators: [ + (Story) => ( +
+ +
+ ), + ], +} + +export const Default: Story = { + args: { + defaultValue: false, + variant: 'default', + disabled: false, + }, +} + +export default meta diff --git a/packages/components/src/components/Toggle/__tests__/__snapshots__/index.browser.test.tsx.snap b/packages/components/src/components/Toggle/__tests__/__snapshots__/index.browser.test.tsx.snap new file mode 100644 index 00000000..2013a32a --- /dev/null +++ b/packages/components/src/components/Toggle/__tests__/__snapshots__/index.browser.test.tsx.snap @@ -0,0 +1,213 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Toggle > should Toggle snapshot 1`] = ` +
+
+
+
+ +
+`; + +exports[`Toggle > should Toggle snapshot 2`] = ` +
+
+
+
+ +
+`; + +exports[`Toggle > should Toggle snapshot 3`] = ` +
+
+
+
+ +
+`; + +exports[`Toggle > should Toggle snapshot 4`] = ` +
+
+
+
+ +
+`; + +exports[`Toggle > should Toggle snapshot 5`] = ` +
+
+
+
+ +
+`; + +exports[`Toggle > should Toggle snapshot 6`] = ` +
+
+
+
+ +
+`; + +exports[`Toggle > should Toggle snapshot 7`] = ` +
+
+
+
+ +
+`; + +exports[`Toggle > should Toggle snapshot 8`] = ` +
+
+
+
+ +
+`; + +exports[`Toggle > should Toggle snapshot 9`] = ` +
+
+
+
+ +
+`; + +exports[`Toggle > should Toggle snapshot 10`] = ` +
+
+
+
+ +
+`; + +exports[`Toggle > should Toggle snapshot 11`] = ` +
+
+
+
+ +
+`; + +exports[`Toggle > should Toggle snapshot 12`] = ` +
+
+
+
+ +
+`; diff --git a/packages/components/src/components/Toggle/__tests__/index.browser.test.tsx b/packages/components/src/components/Toggle/__tests__/index.browser.test.tsx new file mode 100644 index 00000000..b35828f8 --- /dev/null +++ b/packages/components/src/components/Toggle/__tests__/index.browser.test.tsx @@ -0,0 +1,101 @@ +import { act, render } from '@testing-library/react' +import userEvent from '@testing-library/user-event' + +import { Toggle } from '../index' + +vi.mock('react', async (originImport: any) => { + const origin = await originImport() + return { + ...origin, + cache: vi.fn((arg) => arg), + } +}) +describe('Toggle', () => { + it('should Toggle snapshot', () => { + expect(render().container).toMatchSnapshot() + expect(render().container).toMatchSnapshot() + expect(render().container).toMatchSnapshot() + expect(render().container).toMatchSnapshot() + expect(render().container).toMatchSnapshot() + expect( + render().container, + ).toMatchSnapshot() + expect( + render().container, + ).toMatchSnapshot() + expect( + render().container, + ).toMatchSnapshot() + expect( + render( + , + ).container, + ).toMatchSnapshot() + expect( + render( + , + ).container, + ).toMatchSnapshot() + expect( + render( + , + ).container, + ).toMatchSnapshot() + expect( + render( + , + ).container, + ).toMatchSnapshot() + }) + + it('should change value when use onChange prop', async () => { + const onChange = vi.fn() + const { container } = render( + , + ) + const toggleButton = container.querySelector('.test') + const input = container.querySelector('input') + toggleButton && + (await act(async () => { + await userEvent.click(toggleButton) + })) + expect(input).toHaveAttribute('value', 'true') + }) +}) diff --git a/packages/components/src/components/Toggle/index.tsx b/packages/components/src/components/Toggle/index.tsx new file mode 100644 index 00000000..873c3fa6 --- /dev/null +++ b/packages/components/src/components/Toggle/index.tsx @@ -0,0 +1,129 @@ +'use client' +import { Box, Input } from '@devup-ui/react' +import { useState } from 'react' + +interface ToggleProps { + defaultValue?: boolean | null + value?: boolean | null + onChange?: (value: boolean) => void + disabled?: boolean + variant?: 'default' | 'switch' + className?: string + style?: React.CSSProperties + classNames?: { + toggle?: string + } + styles?: { + toggle?: React.CSSProperties + } + colors?: { + primary?: string + bg?: string + hoverBg?: string + primaryHoverBg?: string + disabledBg?: string + switchHoverOutline?: string + switchShadow?: string + } +} + +export function Toggle({ + defaultValue = null, + value = null, + onChange, + disabled, + className, + style, + variant = 'default', + colors, + classNames, + styles, +}: ToggleProps) { + const [innerValue, setInnerValue] = useState( + value ?? defaultValue ?? false, + ) + + const resultValue = value ?? innerValue + + function handleToggle(value: boolean) { + onChange?.(!value) + setInnerValue((prev) => !prev) + } + + const isDefault = variant === 'default' + + return ( + <> + !disabled && handleToggle(resultValue)} + p={!isDefault && 1} + position="relative" + role="group" + selectors={{ + '&[aria-disabled=true]': { + cursor: 'not-allowed', + bg: 'var(--disabledBg, light-dark(#D6D7DE, #373737))', + }, + '&:hover:not([aria-disabled=true]):not(:disabled)': { + bg: resultValue + ? `var(--primaryHoverBg, light-dark(color-mix(in srgb, var(--primary) 100%, #000 15%), color-mix(in srgb, var(--primary) 100%, #FFF 15%)))` + : 'var(--hoverBg, light-dark(#C3C2C8, #696A6F))', + }, + }} + style={style} + styleVars={{ + primary: colors?.primary, + bg: colors?.bg, + primaryHoverBg: colors?.primaryHoverBg, + hoverBg: colors?.hoverBg, + disabledBg: colors?.disabledBg, + }} + test-id="toggle-wrapper" + transition=".25s" + w={isDefault ? '50px' : '40px'} + > + + + + + ) +}