diff --git a/.changeset/five-tigers-behave.md b/.changeset/five-tigers-behave.md new file mode 100644 index 000000000..d7dd83bb6 --- /dev/null +++ b/.changeset/five-tigers-behave.md @@ -0,0 +1,7 @@ +--- +'@drivenets/design-system': major +'@drivenets/eslint-plugin-design-system': major +--- + +Remove deprecated components `DsChip` and `DsChipGroup` +Remove deprecation eslint rules for `DsChip` and `DsChipGroup` diff --git a/.changeset/fluffy-clubs-greet.md b/.changeset/fluffy-clubs-greet.md new file mode 100644 index 000000000..9fe45666d --- /dev/null +++ b/.changeset/fluffy-clubs-greet.md @@ -0,0 +1,7 @@ +--- +'@drivenets/design-system': major +'@drivenets/eslint-plugin-design-system': major +--- + +Remove deprecated components `DsDropdownMenuLegacy` and `DsRadioGroupLegacy` +Remove deprecation eslint rules for `DsDropdownMenuLegacy` and `DsRadioGroupLegacy` diff --git a/.changeset/loose-stamps-study.md b/.changeset/loose-stamps-study.md new file mode 100644 index 000000000..28b797bb5 --- /dev/null +++ b/.changeset/loose-stamps-study.md @@ -0,0 +1,7 @@ +--- +'@drivenets/design-system': major +'@drivenets/eslint-plugin-design-system': major +--- + +Remove deprecated component `DsSystemStatus` +Remove deprecation eslint rules for `DsSystemStatus` diff --git a/.changeset/ninety-signs-move.md b/.changeset/ninety-signs-move.md new file mode 100644 index 000000000..a0201eef8 --- /dev/null +++ b/.changeset/ninety-signs-move.md @@ -0,0 +1,8 @@ +--- +'@drivenets/design-system': major +'@drivenets/eslint-plugin-design-system': major +--- + +Remove deprecated `DsButton` and `DsButtonLegacy`. +Rename `DsButtonV3` to `DsButton` (it becomes the default `DsButton` component). +Remove deprecation rules for `DsButtonLegacy`. diff --git a/.changeset/remove-ds-date-input.md b/.changeset/remove-ds-date-input.md new file mode 100644 index 000000000..404ce1fee --- /dev/null +++ b/.changeset/remove-ds-date-input.md @@ -0,0 +1,8 @@ +--- +'@drivenets/design-system': major +'@drivenets/eslint-plugin-design-system': major +--- + +Remove deprecated `DsDateInput`, `DsFormControl.DateInput` components. +Remove deprecation rules for `DsDateInput`. +Replace `DsDateInput` with `DsDatePicker` or `DsDateRangePicker` instead. diff --git a/.changeset/ripe-ravens-sleep.md b/.changeset/ripe-ravens-sleep.md new file mode 100644 index 000000000..522c78ef0 --- /dev/null +++ b/.changeset/ripe-ravens-sleep.md @@ -0,0 +1,7 @@ +--- +'@drivenets/design-system': major +'@drivenets/eslint-plugin-design-system': major +--- + +Removed deprecated components `DsDialog` and `DsConfirm` +Removed deprecation eslint rules for `DsDialog` and `DsConfirm` diff --git a/packages/design-system/package.json b/packages/design-system/package.json index 4f47226a3..76e8ba196 100644 --- a/packages/design-system/package.json +++ b/packages/design-system/package.json @@ -60,11 +60,8 @@ "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", "@radix-ui/react-collapsible": "^1.1.12", - "@radix-ui/react-dialog": "^1.1.15", - "@radix-ui/react-dropdown-menu": "^2.1.16", "@radix-ui/react-popover": "^1.1.15", "@radix-ui/react-tooltip": "^1.2.8", - "@radix-ui/react-visually-hidden": "^1.2.4", "@tanstack/react-table": "^8.21.3", "@tanstack/react-virtual": "^3.13.24", "@zag-js/react": "^1.40.0", @@ -92,13 +89,13 @@ "@storybook/react-vite": "^10.2.19", "@tanstack/react-query": "^5.90.21", "@tanstack/react-router": "^1.166.2", - "@tsdown/css": "^0.21.6", + "@tsdown/css": "^0.21.10", "@types/eslint-plugin-jsx-a11y": "^6.10.1", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@typescript/native-preview": "catalog:", "@vitejs/plugin-react": "^6.0.1", - "@vitest/browser-playwright": "^4.1.0", + "@vitest/browser-playwright": "^4.1.5", "@vitest/coverage-v8": "catalog:", "babel-plugin-react-compiler": "^1.0.0", "eslint": "catalog:", diff --git a/packages/design-system/src/components/ds-alert-banner/stories/ds-alert-banner-global.stories.tsx b/packages/design-system/src/components/ds-alert-banner/stories/ds-alert-banner-global.stories.tsx index 144923938..353abf0c0 100644 --- a/packages/design-system/src/components/ds-alert-banner/stories/ds-alert-banner-global.stories.tsx +++ b/packages/design-system/src/components/ds-alert-banner/stories/ds-alert-banner-global.stories.tsx @@ -61,10 +61,10 @@ export const InfoBlue: Story = { bit longer so that you can see how spacing within an alert works with this kind of content. - + Proceed - + Skip @@ -135,10 +135,10 @@ export const WithActions: Story = { a bit longer so that you can see how spacing within an alert works with this kind of content. - + Proceed - + Skip @@ -217,13 +217,13 @@ export const CustomBody: Story = { - + Fix Now - + View Details - + Ignore diff --git a/packages/design-system/src/components/ds-alert-banner/stories/ds-alert-banner-inline.stories.tsx b/packages/design-system/src/components/ds-alert-banner/stories/ds-alert-banner-inline.stories.tsx index 52db4b1b0..c7999350d 100644 --- a/packages/design-system/src/components/ds-alert-banner/stories/ds-alert-banner-inline.stories.tsx +++ b/packages/design-system/src/components/ds-alert-banner/stories/ds-alert-banner-inline.stories.tsx @@ -183,10 +183,10 @@ export const WithActions: Story = { bit longer so that you can see how spacing within an alert works with this kind of content. - + Proceed - + Skip @@ -255,13 +255,13 @@ export const CustomBody: Story = { - + Update Now - + Schedule Later - + Learn More diff --git a/packages/design-system/src/components/ds-button-v3/ds-button-v3.tsx b/packages/design-system/src/components/ds-button-v3/ds-button-v3.tsx deleted file mode 100644 index de8951c40..000000000 --- a/packages/design-system/src/components/ds-button-v3/ds-button-v3.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import classNames from 'classnames'; -import { DsIcon, type IconSize } from '../ds-icon'; -import { DsSpinner } from '../ds-spinner'; -import styles from './ds-button-v3.module.scss'; -import type { ButtonV3Size, DsButtonV3BaseProps } from './ds-button-v3.types.ts'; - -const iconSizeMap: Record = Object.freeze({ - large: 'small', - medium: 'tiny', - small: 'tiny', - tiny: 'tiny', -}); - -const DsButtonV3 = ({ - ref, - className, - style, - children, - icon, - disabled, - loading = false, - color = 'default', - variant = 'primary', - size = 'medium', - selected = false, - type = 'button', - ...rest -}: DsButtonV3BaseProps) => { - const isIconOnly = icon !== undefined && !children; - - return ( - - ); -}; - -export default DsButtonV3; diff --git a/packages/design-system/src/components/ds-button-v3/ds-button-v3.types.ts b/packages/design-system/src/components/ds-button-v3/ds-button-v3.types.ts deleted file mode 100644 index 9dc97d224..000000000 --- a/packages/design-system/src/components/ds-button-v3/ds-button-v3.types.ts +++ /dev/null @@ -1,65 +0,0 @@ -import type { ButtonHTMLAttributes, Ref } from 'react'; -import type { IconType } from '../ds-icon'; -import type { ResponsiveValue } from '../../utils/responsive'; - -export const buttonV3Variants = ['primary', 'secondary', 'tertiary'] as const; -export type ButtonV3Variant = (typeof buttonV3Variants)[number]; - -export const buttonV3Colors = ['default', 'error', 'light'] as const; -export type ButtonV3Color = (typeof buttonV3Colors)[number]; - -export const buttonV3Sizes = ['large', 'medium', 'small', 'tiny'] as const; -export type ButtonV3Size = (typeof buttonV3Sizes)[number]; - -export interface DsButtonV3BaseProps extends ButtonHTMLAttributes { - ref?: Ref; - - /** - * - `default` — standard light-UI palette - * - `error` — destructive / danger palette (red tones) - * - `light` — palette for dark-background surfaces (Figma **Type** onDark) - * @default 'default' - */ - color?: ButtonV3Color; - - /** - * Visual variant of the button: - * - `primary` — filled, highest-emphasis action - * - `secondary` — outlined, medium-emphasis action - * - `tertiary` — borderless, low-emphasis action (text-like) - * @default 'primary' - */ - variant?: ButtonV3Variant; - - /** - * Size of the button. Controls height, padding, font size, and icon size. - * @default 'medium' - */ - size?: ButtonV3Size; - - /** - * Whether the button is in a selected/pressed state. Used for toggle buttons - * and segmented controls. - * @default false - */ - selected?: boolean; - - /** - * Leading icon. When set without children, renders as icon-only (square) layout. - */ - icon?: IconType; - - /** - * Shows a spinner as the leading element and disables interaction. - * @default false - */ - loading?: boolean; -} - -export interface DsButtonV3Props extends Omit { - /** - * Size of the button. Accepts a static value or a responsive object. - * @default 'medium' - */ - size?: ResponsiveValue; -} diff --git a/packages/design-system/src/components/ds-button-v3/index.ts b/packages/design-system/src/components/ds-button-v3/index.ts deleted file mode 100644 index 780edd3f8..000000000 --- a/packages/design-system/src/components/ds-button-v3/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { withResponsiveProps } from '../../utils/responsive'; -import DsButtonV3Base from './ds-button-v3'; - -export const DsButtonV3 = withResponsiveProps(DsButtonV3Base, ['size']); - -export * from './ds-button-v3.types'; diff --git a/packages/design-system/src/components/ds-button-v3/__tests__/ds-button-v3.browser.test.tsx b/packages/design-system/src/components/ds-button/__tests__/ds-button.browser.test.tsx similarity index 79% rename from packages/design-system/src/components/ds-button-v3/__tests__/ds-button-v3.browser.test.tsx rename to packages/design-system/src/components/ds-button/__tests__/ds-button.browser.test.tsx index f4bd8756f..297ed4180 100644 --- a/packages/design-system/src/components/ds-button-v3/__tests__/ds-button-v3.browser.test.tsx +++ b/packages/design-system/src/components/ds-button/__tests__/ds-button.browser.test.tsx @@ -1,13 +1,13 @@ import { createRef } from 'react'; import { describe, expect, it, vi } from 'vitest'; import { page } from 'vitest/browser'; -import { DsButtonV3 } from '../index'; +import { DsButton } from '../index'; -describe('DsButtonV3', () => { +describe('DsButton', () => { it('calls onClick when clicked', async () => { const onClick = vi.fn(); - await page.render(Click me); + await page.render(Click me); await page.getByRole('button', { name: 'Click me' }).click(); @@ -18,9 +18,9 @@ describe('DsButtonV3', () => { const onClick = vi.fn(); await page.render( - + Click me - , + , ); const button = page.getByRole('button', { name: 'Click me', disabled: true }); @@ -32,7 +32,7 @@ describe('DsButtonV3', () => { }); it('applies selected state', async () => { - await page.render(Label); + await page.render(Label); const button = page.getByRole('button', { name: 'Label' }); @@ -40,7 +40,7 @@ describe('DsButtonV3', () => { }); it('sets data-color for error palette', async () => { - await page.render(Delete); + await page.render(Delete); const button = page.getByRole('button', { name: 'Delete' }); @@ -48,7 +48,7 @@ describe('DsButtonV3', () => { }); it('applies iconOnly layout when icon is set without children', async () => { - await page.render(); + await page.render(); const button = page.getByRole('button', { name: 'Confirm' }); @@ -56,7 +56,7 @@ describe('DsButtonV3', () => { }); it('does not apply iconOnly layout when icon is set with children', async () => { - await page.render(Save); + await page.render(Save); const button = page.getByRole('button', { name: 'Save' }); @@ -64,7 +64,7 @@ describe('DsButtonV3', () => { }); it('renders native submit button type', async () => { - await page.render(Send); + await page.render(Send); const button = page.getByRole('button', { name: 'Send' }); @@ -72,13 +72,13 @@ describe('DsButtonV3', () => { }); it('merges className', async () => { - await page.render(X); + await page.render(X); await expect.element(page.getByRole('button', { name: 'X' })).toHaveClass('extra'); }); it('sets aria-busy and data-loading when loading', async () => { - await page.render(Save); + await page.render(Save); const button = page.getByRole('button', { name: 'Save' }); @@ -87,7 +87,7 @@ describe('DsButtonV3', () => { }); it('renders spinner instead of icon when loading', async () => { - await page.render(); + await page.render(); const button = page.getByRole('button', { name: 'Saving' }); @@ -102,9 +102,9 @@ describe('DsButtonV3', () => { const onClick = vi.fn(); await page.render( - + Save - , + , ); await page.getByRole('button', { name: 'Save' }).click({ force: true }); @@ -115,9 +115,9 @@ describe('DsButtonV3', () => { it('sets data-variant for each variant', async () => { for (const variant of ['primary', 'secondary', 'tertiary'] as const) { await page.render( - + Label - , + , ); await expect @@ -127,7 +127,7 @@ describe('DsButtonV3', () => { }); it('applies default props when none are specified', async () => { - await page.render(Default); + await page.render(Default); const button = page.getByRole('button', { name: 'Default' }); @@ -139,7 +139,7 @@ describe('DsButtonV3', () => { it('forwards ref to the button element', async () => { const ref = createRef(); - await page.render(Ref); + await page.render(Ref); expect(ref.current).toBeInstanceOf(HTMLButtonElement); expect(ref.current?.textContent).toBe('Ref'); @@ -149,9 +149,9 @@ describe('DsButtonV3', () => { const onClick = vi.fn(); await page.render( - + Saving - , + , ); const button = page.getByRole('button', { name: 'Saving', disabled: true }); @@ -164,9 +164,9 @@ describe('DsButtonV3', () => { it('loading + disabled shows spinner with disabled styling', async () => { await page.render( - + Saving - , + , ); const button = page.getByRole('button', { name: 'Saving', disabled: true }); @@ -178,9 +178,9 @@ describe('DsButtonV3', () => { it('selected + disabled does not remove selected styling', async () => { await page.render( - + Toggle - , + , ); const button = page.getByRole('button', { name: 'Toggle', disabled: true }); @@ -192,9 +192,9 @@ describe('DsButtonV3', () => { it('spreads rest props onto the button element', async () => { await page.render( - + Rest - , + , ); const button = page.getByRole('button', { name: 'Spread' }); diff --git a/packages/design-system/src/components/ds-button-v3/ds-button-v3.module.scss b/packages/design-system/src/components/ds-button/ds-button.module.scss similarity index 100% rename from packages/design-system/src/components/ds-button-v3/ds-button-v3.module.scss rename to packages/design-system/src/components/ds-button/ds-button.module.scss diff --git a/packages/design-system/src/components/ds-button-v3/ds-button-v3.stories.module.scss b/packages/design-system/src/components/ds-button/ds-button.stories.module.scss similarity index 100% rename from packages/design-system/src/components/ds-button-v3/ds-button-v3.stories.module.scss rename to packages/design-system/src/components/ds-button/ds-button.stories.module.scss diff --git a/packages/design-system/src/components/ds-button-v3/ds-button-v3.stories.tsx b/packages/design-system/src/components/ds-button/ds-button.stories.tsx similarity index 79% rename from packages/design-system/src/components/ds-button-v3/ds-button-v3.stories.tsx rename to packages/design-system/src/components/ds-button/ds-button.stories.tsx index 247291e71..6a0aeb4ca 100644 --- a/packages/design-system/src/components/ds-button-v3/ds-button-v3.stories.tsx +++ b/packages/design-system/src/components/ds-button/ds-button.stories.tsx @@ -1,27 +1,26 @@ import type { Meta, StoryObj } from '@storybook/react-vite'; import classNames from 'classnames'; import { fn } from 'storybook/test'; -import DsButtonV3 from './ds-button-v3'; -import { DsButtonV3 as DsButtonV3Wrapped } from './index'; +import { DsButton } from './index'; import { - type ButtonV3Color, - buttonV3Colors, - buttonV3Sizes, - type ButtonV3Variant, - buttonV3Variants, -} from './ds-button-v3.types'; -import storyStyles from './ds-button-v3.stories.module.scss'; - -const meta: Meta = { - title: 'Components/ButtonV3', - component: DsButtonV3, + type ButtonColor, + buttonColors, + buttonSizes, + type ButtonVariant, + buttonVariants, +} from './ds-button.types'; +import storyStyles from './ds-button.stories.module.scss'; + +const meta: Meta = { + title: 'Components/Button', + component: DsButton, parameters: { layout: 'centered', }, argTypes: { - color: { control: 'select', options: buttonV3Colors }, - variant: { control: 'select', options: buttonV3Variants }, - size: { control: 'select', options: buttonV3Sizes }, + color: { control: 'select', options: buttonColors }, + variant: { control: 'select', options: buttonVariants }, + size: { control: 'select', options: buttonSizes }, loading: { control: 'boolean' }, disabled: { control: 'boolean' }, className: { table: { disable: true } }, @@ -32,7 +31,7 @@ const meta: Meta = { }; export default meta; -type Story = StoryObj; +type Story = StoryObj; export const Default: Story = { args: { @@ -45,7 +44,7 @@ export const Default: Story = { }; const matrixRows = [ - ...buttonV3Variants.map((v) => ({ label: v, loading: false })), + ...buttonVariants.map((v) => ({ label: v, loading: false })), { label: 'loading', loading: true }, ]; @@ -69,13 +68,13 @@ const onDarkIconMatrixRows = [ { label: 'loading', icon: 'info', variant: 'primary', color: 'light', loading: true }, ] as const; -const MatrixGrid = ({ color }: { color?: ButtonV3Color }) => { +const MatrixGrid = ({ color }: { color?: ButtonColor }) => { const isOnDark = color === 'light'; return (
- {buttonV3Sizes.map((size) => ( + {buttonSizes.map((size) => ( { {label} - {buttonV3Sizes.map((size) => ( + {buttonSizes.map((size) => (
- {size !== 'tiny' ? 'Button' : undefined} - +
))}
@@ -124,15 +123,15 @@ const IconMatrixGrid = ({ rows: ReadonlyArray<{ label: string; icon: 'check_circle' | 'info' | 'delete' | 'keyboard_arrow_down' | 'home'; - variant: ButtonV3Variant; - color: ButtonV3Color; + variant: ButtonVariant; + color: ButtonColor; loading: boolean; }>; isOnDark?: boolean; }) => (
- {buttonV3Sizes.map((size) => ( + {buttonSizes.map((size) => ( - {buttonV3Sizes.map((size) => { + {buttonSizes.map((size) => { const ariaLabel = `${label} ${size}`; return (
- (
- + lg: large / md: small - + - + lg: medium / md: tiny - + - + static: medium - +
), }; diff --git a/packages/design-system/src/components/ds-button/ds-button.tsx b/packages/design-system/src/components/ds-button/ds-button.tsx index 5482465c3..949bae04d 100644 --- a/packages/design-system/src/components/ds-button/ds-button.tsx +++ b/packages/design-system/src/components/ds-button/ds-button.tsx @@ -1,12 +1,60 @@ -import { DsButtonLegacy } from './versions/ds-button-legacy'; -import { DsButtonNew } from './versions/ds-button-new'; -import type { DsButtonUnifiedProps } from './ds-button.types'; - -const DsButton = (props: DsButtonUnifiedProps) => { - if (props.design === 'v1.2') { - return ; - } - return ; +import classNames from 'classnames'; +import { DsIcon, type IconSize } from '../ds-icon'; +import { DsSpinner } from '../ds-spinner'; +import styles from './ds-button.module.scss'; +import type { ButtonSize, DsButtonProps } from './ds-button.types'; + +const iconSizeMap: Record = Object.freeze({ + large: 'small', + medium: 'tiny', + small: 'tiny', + tiny: 'tiny', +}); + +const DsButton = ({ + ref, + className, + style, + children, + icon, + disabled, + loading = false, + color = 'default', + variant = 'primary', + size = 'medium', + selected = false, + type = 'button', + slotProps, + ...rest +}: DsButtonProps) => { + const isIconOnly = icon !== undefined && !children; + + return ( + + ); }; export default DsButton; diff --git a/packages/design-system/src/components/ds-button/ds-button.types.ts b/packages/design-system/src/components/ds-button/ds-button.types.ts index 76ef2b5ac..78976db08 100644 --- a/packages/design-system/src/components/ds-button/ds-button.types.ts +++ b/packages/design-system/src/components/ds-button/ds-button.types.ts @@ -1,17 +1,63 @@ -import type { DsButtonProps as DsButtonLegacyProps } from './versions/ds-button-legacy/ds-button-legacy.types'; -import type { DsButtonProps as DsButtonNewProps } from './versions/ds-button-new/ds-button-new.types'; - -export type DsButtonUnifiedProps = - | (DsButtonLegacyProps & { - /** - * Selects the legacy button implementation. Accepts `DsButtonLegacyProps`. - * Omit or pass `'legacy'` to opt into this branch. - */ - design?: undefined | 'legacy'; - }) - | (DsButtonNewProps & { - /** - * Selects the new (v1.2) button implementation. Accepts `DsButtonNewProps`. - */ - design: 'v1.2'; - }); +import type { ButtonHTMLAttributes, Ref } from 'react'; +import type { DsIconProps, IconType } from '../ds-icon'; + +export const buttonVariants = ['primary', 'secondary', 'tertiary'] as const; +export type ButtonVariant = (typeof buttonVariants)[number]; + +export const buttonColors = ['default', 'error', 'light'] as const; +export type ButtonColor = (typeof buttonColors)[number]; + +export const buttonSizes = ['large', 'medium', 'small', 'tiny'] as const; +export type ButtonSize = (typeof buttonSizes)[number]; + +export interface DsButtonProps extends ButtonHTMLAttributes { + ref?: Ref; + + /** + * - `default` — standard light-UI palette + * - `error` — destructive / danger palette (red tones) + * - `light` — palette for dark-background surfaces (Figma **Type** onDark) + * @default 'default' + */ + color?: ButtonColor; + + /** + * Visual variant of the button: + * - `primary` — filled, highest-emphasis action + * - `secondary` — outlined, medium-emphasis action + * - `tertiary` — borderless, low-emphasis action (text-like) + * @default 'primary' + */ + variant?: ButtonVariant; + + /** + * Size of the button. Controls height, padding, font size, and icon size. + * @default 'medium' + */ + size?: ButtonSize; + + /** + * Whether the button is in a selected/pressed state. Used for toggle buttons + * and segmented controls. + * @default false + */ + selected?: boolean; + + /** + * Leading icon. When set without children, renders as icon-only (square) layout. + */ + icon?: IconType; + + /** + * Shows a spinner as the leading element and disables interaction. + * @default false + */ + loading?: boolean; + + /** + * Props forwarded to internal slots. Use to override defaults on the leading icon. + */ + slotProps?: { + icon?: Omit, 'icon'>; + }; +} diff --git a/packages/design-system/src/components/ds-button/index.ts b/packages/design-system/src/components/ds-button/index.ts index 26a914360..5a72a4fde 100644 --- a/packages/design-system/src/components/ds-button/index.ts +++ b/packages/design-system/src/components/ds-button/index.ts @@ -1,2 +1,18 @@ -export { default as DsButton } from './ds-button'; -export * from './ds-button.types'; +import type { ComponentProps } from 'react'; +import { withResponsiveProps } from '../../utils/responsive'; +import DsButtonBase from './ds-button'; + +export { + buttonVariants, + buttonColors, + buttonSizes, + type ButtonVariant, + type ButtonColor, + type ButtonSize, +} from './ds-button.types'; + +export const DsButton = withResponsiveProps(DsButtonBase, ['size']); + +DsButton.displayName = 'DsButton'; + +export type DsButtonProps = ComponentProps; diff --git a/packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.module.scss b/packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.module.scss deleted file mode 100644 index 91656561d..000000000 --- a/packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.module.scss +++ /dev/null @@ -1,257 +0,0 @@ -@use '../../../../styles/typography'; - -$color-primary-default: var(--color-dap-blue-600); -$color-primary-hover: var(--background-active-status); -$color-primary-active: var(--color-dap-purple-200); -$color-primary-disabled: var(--color-dap-gray-400); - -$color-secondary-default: var(--color-dap-brand-300); -$color-secondary-hover: var(--color-dap-gray-500); -$color-secondary-disabled: var(--color-dap-gray-400); - -$color-error-default: var(--background-error); -$color-error-hover: var(--color-dap-red-100); -$color-error-active: var(--color-dap-red-100); -$color-error-disabled: var(--color-dap-gray-400); - -$color-white: var(--color-dap-gray-050); -$color-neutral-6: var(--color-dap-gray-200); -$color-neutral-4: var(--color-dap-gray-500); - -@mixin button-filled($background, $hover, $active, $disabled) { - background: $background; - color: $color-white; - - &:disabled { - color: $color-neutral-6; - background: $disabled; - } -} - -@mixin button-ghost($color, $hover, $active, $disabled) { - border: 2px solid $color; - color: $color; - - &:disabled { - color: $disabled; - border-color: $disabled; - } -} - -@mixin button-borderless($color, $hover, $active, $disabled) { - color: $color; - border: none; - - &.content { - gap: 4px; - } - - &:hover { - color: $hover; - } - - &:active { - color: $active; - } - - &:disabled { - color: $disabled; - } -} - -@mixin button-round($background, $hover, $active, $disabled) { - @include button-filled($background, $hover, $active, $disabled); - border-radius: 32px; -} - -.button { - @include typography.regular; - display: flex; - align-items: center; - justify-content: center; - border-radius: 4px; - white-space: nowrap; - cursor: pointer; - border: none; - padding: 0; - font: inherit; - background: none; - - &:disabled { - cursor: not-allowed; - } - - &:not(:disabled) { - cursor: pointer; - } - - .content { - display: flex; - align-items: center; - justify-content: center; - gap: 8px; - } - - &.small { - @include typography.p-base; - padding: 6px 12px; - - .content { - gap: 6px; - } - } - - &.medium { - @include typography.p-base; - @include typography.medium; - padding: 12px; - - .content { - gap: 9px; - } - } - - &.large { - @include typography.p-base; - @include typography.medium; - padding: 14px 12px; - } - - &.primary-filled { - @include button-filled( - $color-primary-default, - $color-primary-hover, - $color-primary-active, - $color-primary-disabled - ); - } - - &.primary-ghost { - @include button-ghost( - $color-primary-default, - $color-primary-hover, - $color-primary-active, - $color-neutral-4 - ); - } - - &.primary-borderless { - @include button-borderless( - $color-primary-default, - $color-primary-hover, - $color-primary-active, - $color-primary-disabled - ); - } - - &.primary-round { - @include button-round( - $color-primary-default, - $color-primary-hover, - $color-primary-active, - $color-primary-disabled - ); - } - - &.primary-dashed { - @include button-ghost( - $color-primary-default, - $color-primary-hover, - $color-primary-active, - $color-neutral-4 - ); - border-style: dashed; - } - - &.secondary-filled { - @include button-filled( - $color-secondary-default, - $color-secondary-hover, - $color-secondary-hover, - $color-secondary-disabled - ); - } - - &.secondary-ghost { - @include button-ghost( - $color-secondary-default, - $color-secondary-hover, - $color-secondary-hover, - $color-secondary-disabled - ); - } - - &.secondary-borderless { - @include button-borderless( - $color-secondary-default, - $color-secondary-hover, - $color-secondary-hover, - $color-secondary-disabled - ); - } - - &.secondary-round { - @include button-round( - $color-secondary-default, - $color-secondary-hover, - $color-secondary-hover, - $color-secondary-disabled - ); - } - - &.secondary-dashed { - @include button-ghost( - $color-secondary-default, - $color-secondary-hover, - $color-secondary-hover, - $color-secondary-disabled - ); - border-style: dashed; - } - - &.error-filled { - @include button-filled( - $color-error-default, - $color-error-hover, - $color-error-active, - $color-error-disabled - ); - } - - &.error-ghost { - @include button-ghost( - $color-error-default, - $color-error-hover, - $color-error-active, - $color-error-disabled - ); - } - - &.error-borderless { - @include button-borderless( - $color-error-default, - $color-error-hover, - $color-error-active, - $color-error-disabled - ); - } - - &.error-round { - @include button-round( - $color-error-default, - $color-error-hover, - $color-error-active, - $color-error-disabled - ); - } - - &.error-dashed { - @include button-ghost( - $color-error-default, - $color-error-hover, - $color-error-active, - $color-error-disabled - ); - border-style: dashed; - } -} diff --git a/packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.stories.module.scss b/packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.stories.module.scss deleted file mode 100644 index a3ea9e1dc..000000000 --- a/packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.stories.module.scss +++ /dev/null @@ -1,62 +0,0 @@ -.row { - display: flex; - gap: 20px; - padding-bottom: 20px; - align-items: center; -} - -.combinationsContainer { - display: flex; - flex-direction: column; - gap: 20px; - margin: 20px; -} - -.schemaContainer { - display: flex; - gap: 20px; - border-bottom: 1px solid #eaeaea; -} - -.iconButton-small { - width: 26px; - height: 26px; -} - -.iconButton-medium { - width: 42px; - height: 42px; -} - -.iconButton-large { - width: 56px; - height: 56px; -} - -.schemaTitle { - font-size: 20px; - font-weight: 600; - margin: 0; - padding-bottom: 8px; - min-width: 150px; - text-transform: capitalize; - align-self: center; -} - -.variantContainer { - display: flex; - flex-direction: column; - gap: 16px; -} - -.variantTitle { - font-size: 16px; - font-weight: 500; - margin: 0; - color: #666; - text-transform: capitalize; -} - -.buttonShowcase { - text-transform: capitalize; -} diff --git a/packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.stories.tsx b/packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.stories.tsx deleted file mode 100644 index 5f8c0bc27..000000000 --- a/packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.stories.tsx +++ /dev/null @@ -1,147 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react-vite'; -import { expect, fn, userEvent, within } from 'storybook/test'; -import classNames from 'classnames'; -import { DsIcon } from '../../../ds-icon'; -import styles from './ds-button-legacy.stories.module.scss'; -import DsButtonLegacy from './ds-button-legacy'; -import { - type ButtonSchema, - type ButtonVariant, - buttonSchemas, - buttonSizes, - buttonVariants, -} from './ds-button-legacy.types'; - -const meta: Meta = { - title: 'Components/ButtonLegacy (Deprecated)', - component: DsButtonLegacy, - parameters: { - layout: 'centered', - }, - tags: ['deprecated'], - argTypes: { - schema: { - control: { type: 'select' }, - options: buttonSchemas, - description: 'Button color schema', - table: { - defaultValue: { - summary: 'primary', - }, - }, - }, - variant: { - control: { type: 'select' }, - options: buttonVariants, - table: { - defaultValue: { - summary: 'filled', - }, - }, - }, - size: { - control: { type: 'select' }, - options: buttonSizes, - table: { - defaultValue: { - summary: 'medium', - }, - }, - }, - }, - // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args - args: { onClick: fn() }, -}; -export default meta; -type Story = StoryObj; - -const defaultButtonText = 'Button Text'; - -export const DefaultButton: Story = { - args: { - schema: 'primary', - variant: 'filled', - disabled: false, - children: defaultButtonText, - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const button = canvas.getByRole('button', { name: defaultButtonText }); - await userEvent.click(button); - await expect(button).toBeInTheDocument(); - }, -}; - -export const Showcase: Story = { - parameters: { - layout: 'fullscreen', - }, - render: function Render() { - const renderButtonRow = ( - schema: ButtonSchema, - variant: ButtonVariant, - disabled?: boolean, - showIcon?: boolean, - showTitle?: boolean, - ) => { - return ( -
- {buttonSizes.map((size) => ( - - {showIcon && } - {showTitle && {size} Button} - - ))} -
- ); - }; - - const renderButtonContainer = ( - schema: ButtonSchema, - variant: ButtonVariant, - disabled?: boolean, - title?: boolean, - ) => { - return ( -
- {title && ( -
-

{variant}

-
- )} - {renderButtonRow(schema, variant, disabled, false, true)} - {renderButtonRow(schema, variant, disabled, true, false)} - {renderButtonRow(schema, variant, disabled, true, true)} -
- ); - }; - - return ( -
- {buttonSchemas.map((schema) => ( -
-

{schema}

- - {buttonVariants.map((variant) => - renderButtonContainer(schema, variant, false, schema === 'primary'), - )} -
- ))} -
-

Disabled

- - {buttonVariants.map((variant) => renderButtonContainer('primary', variant, true))} -
-
- ); - }, -}; diff --git a/packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.tsx b/packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.tsx deleted file mode 100644 index 4b98c4b17..000000000 --- a/packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import classNames from 'classnames'; -import type React from 'react'; -import styles from './ds-button-legacy.module.scss'; -import type { DsButtonProps } from './ds-button-legacy.types'; - -/** - * Design system Button component - * @deprecated Use [DsButton](../ds-button-new/ds-button-new.tsx) instead. - */ -const DsButton: React.FC = ({ - schema = 'primary', - variant = 'filled', - size = 'medium', - disabled = false, - className, - contentClassName, - children, - ...props -}) => { - const buttonClass = classNames(styles.button, styles[`${schema}-${variant}`], styles[size], className); - - return ( - - ); -}; - -export default DsButton; diff --git a/packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.types.ts b/packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.types.ts deleted file mode 100644 index e87f759c4..000000000 --- a/packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.types.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type React from 'react'; - -export const buttonSchemas = ['primary', 'secondary', 'error'] as const; -export type ButtonSchema = (typeof buttonSchemas)[number]; - -export const buttonVariants = ['filled', 'ghost', 'borderless', 'round', 'dashed'] as const; -export type ButtonVariant = (typeof buttonVariants)[number]; - -export const buttonSizes = ['large', 'medium', 'small'] as const; -export type ButtonSize = (typeof buttonSizes)[number]; - -export interface DsButtonProps extends React.ButtonHTMLAttributes { - /** - * Color schema of the button - * @default 'primary' - */ - schema?: ButtonSchema; - - /** - * Visual variant of the button - * @default 'filled' - */ - variant?: ButtonVariant; - - /** - * Size of the button - * @default 'medium' - */ - size?: ButtonSize; - - /** - * Whether the button is disabled - * @default false - */ - disabled?: boolean; - - /** - * Class name for the button content - */ - contentClassName?: string; -} diff --git a/packages/design-system/src/components/ds-button/versions/ds-button-legacy/index.ts b/packages/design-system/src/components/ds-button/versions/ds-button-legacy/index.ts deleted file mode 100644 index 46f7b822b..000000000 --- a/packages/design-system/src/components/ds-button/versions/ds-button-legacy/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default as DsButtonLegacy } from './ds-button-legacy'; -export * from './ds-button-legacy.types'; diff --git a/packages/design-system/src/components/ds-button/versions/ds-button-new/__tests__/ds-button.browser.test.tsx b/packages/design-system/src/components/ds-button/versions/ds-button-new/__tests__/ds-button.browser.test.tsx deleted file mode 100644 index 0843774f7..000000000 --- a/packages/design-system/src/components/ds-button/versions/ds-button-new/__tests__/ds-button.browser.test.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { describe, expect, it, vi } from 'vitest'; -import DsButton from '../ds-button-new'; -import { page } from 'vitest/browser'; - -describe('DsButton', () => { - it('should trigger onClick handler when clicked', async () => { - // Arrange. - const onClick = vi.fn(); - - await page.render(Click me); - - // Act. - await page.getByRole('button', { name: 'Click me' }).click(); - - // Assert. - expect(onClick).toHaveBeenCalled(); - }); - - it('should support disabled state', async () => { - // Arrange. - const onClick = vi.fn(); - - await page.render( - - Click me - , - ); - - const button = page.getByRole('button', { name: 'Click me', disabled: true }); - - // Act. - await button.click({ force: true }); - - // Assert. - expect(onClick).not.toHaveBeenCalled(); - await expect.element(button).toBeDisabled(); - }); - - it('should support custom class names', async () => { - // Arrange. - const className = 'custom-class'; - - // Act. - await page.render(Click me); - - // Assert. - await expect.element(page.getByRole('button', { name: 'Click me' })).toHaveClass(className); - }); -}); diff --git a/packages/design-system/src/components/ds-button/versions/ds-button-new/ds-button-new.module.scss b/packages/design-system/src/components/ds-button/versions/ds-button-new/ds-button-new.module.scss deleted file mode 100644 index dc83821ca..000000000 --- a/packages/design-system/src/components/ds-button/versions/ds-button-new/ds-button-new.module.scss +++ /dev/null @@ -1,604 +0,0 @@ -@use '../../../../styles/typography'; - -$button-size-tiny: 16px; -$button-size-small: 28px; -$button-size-default: 32px; -$button-size-large: 40px; - -@mixin content-size-tiny { - @include typography.body-xs-md; - height: $button-size-tiny; - border-radius: var(--4xs); - --icon-width-tiny: 13px; -} - -@mixin content-size-small { - @include typography.body-xs-md; - height: $button-size-small; -} - -@mixin content-size-medium { - height: $button-size-default; -} - -@mixin content-size-large { - @include typography.body-md-md; - height: $button-size-large; -} - -.button { - @include typography.body-sm-md; - display: flex; - align-items: center; - justify-content: center; - white-space: nowrap; - cursor: pointer; - background: none; - outline: none; - padding-block: 0; - padding-inline: 0; - border: 2px solid transparent; - border-radius: var(--3xs); - - .content { - display: flex; - align-items: center; - justify-content: center; - gap: var(--3xs); - padding: 0 var(--sm); - border: 1px solid transparent; - border-radius: var(--3xs); - flex-grow: 1; - } - - &.tiny .content { - @include content-size-tiny; - } - - &.small .content { - @include content-size-small; - } - - &.medium .content { - @include content-size-medium; - } - - &.large .content { - @include content-size-large; - } - - &.iconButton { - aspect-ratio: 1 / 1; - align-items: stretch; - - .content { - flex: 1; - padding: 0; - } - - &.large .content { - height: $button-size-large; - width: $button-size-large; - } - - &.medium .content { - height: $button-size-default; - width: $button-size-default; - } - - &.small .content { - height: $button-size-small; - width: $button-size-small; - } - - &.tiny .content { - height: $button-size-tiny; - width: $button-size-tiny; - } - } - - &.primary-filled { - border: 2px solid transparent; - color: var(--font-on-action); - - .content { - background: var(--background-primary); - } - - &:hover { - .content { - background: var(--background-primary-hover); - } - } - - &:focus-visible { - border-color: var(--background-primary); - - .content { - border-color: var(--background); - border-radius: calc(var(--3xs) / 2); - } - } - - &:disabled { - cursor: not-allowed; - - .content { - background: var(--background-disable); - border-color: var(--background-disable); - } - } - } - - &.secondary-filled { - background: transparent; - border: 2px solid transparent; - color: var(--background-action); - - .content { - background: var(--background); - border-color: var(--border); - } - - &:hover { - &:not(:disabled) { - .content { - border-color: var(--background-action-hover); - background: var(--background-action-hover-weak); - } - } - - &:focus-visible { - background: var(--background); - - .content { - border-color: var(--background-action-secondary); - } - } - } - - &:focus-visible { - background: var(--background); - border-color: var(--background-action-hover); - - .content { - border-color: var(--background); - background: var(--background-action-hover-weak); - } - } - - &:disabled { - cursor: not-allowed; - color: var(--background-disable); - - .content { - border-color: var(--background-disable); - } - } - } - - &.tertiary-filled { - background: transparent; - border: 2px solid transparent; - color: var(--background-action); - - .content { - background: transparent; - border-color: transparent; - } - - &:hover:not(:disabled) { - .content { - border-color: var(--border); - background: var(--background-action-hover-weak); - } - } - - &:active { - .content { - border-color: var(--border); - } - } - - &:focus-visible { - background: var(--background); - border-color: var(--background-action-hover); - - .content { - border-color: var(--background); - background: var(--background-action-hover-weak); - border-radius: calc(var(--3xs) / 2); - } - } - - &:disabled { - cursor: not-allowed; - color: var(--background-disable); - } - } - - &.secondary-ghost { - background: transparent; - border: 2px solid transparent; - color: var(--background-primary); - - .content { - background: var(--background-action-secondary); - border-color: var(--background-primary); - } - - &:hover { - &:not(:disabled) { - .content { - background: var(--background-action-secondary-hover); - } - } - - &:focus-visible { - .content { - border-color: var(--background-action-secondary); - } - } - } - - &:focus-visible { - background: var(--background); - border-color: var(--background-primary); - - .content { - border-color: var(--background); - background: var(--background-action-secondary-hover); - } - } - - &:disabled { - cursor: not-allowed; - color: var(--background-disable); - - .content { - border-color: var(--background-disable); - } - } - } - - &.tertiary-ghost { - background: transparent; - border: 2px solid transparent; - color: var(--background-primary); - - .content { - background: transparent; - border-color: transparent; - } - - &:hover:not(:disabled) { - .content { - background: var(--background-action-secondary-hover); - } - } - - &:active { - .content { - border-color: var(--border); - } - } - - &:focus-visible { - background: var(--background); - border-color: var(--background-primary); - - .content { - border-color: var(--background); - background: var(--background-action-secondary-hover); - } - } - - &:disabled { - cursor: not-allowed; - color: var(--background-disable); - } - } - - &.primary-danger { - border: 2px solid transparent; - color: var(--font-on-action); - - .content { - background: var(--background-error); - } - - &:hover { - .content { - background: var(--background-error-hover); - } - } - - &:focus-visible { - border-color: var(--background-error); - - .content { - border-color: var(--background); - border-radius: calc(var(--3xs) / 2); - } - } - - &:disabled { - cursor: not-allowed; - - .content { - background: var(--background-disable); - border-color: var(--background-disable); - } - } - } - - &.secondary-danger { - background: transparent; - border: 2px solid transparent; - color: var(--background-error); - - .content { - background: var(--background); - border-color: var(--background-error); - } - - &:hover { - &:not(:disabled) { - color: var(--background-error-hover); - - .content { - border-color: var(--background-error-hover); - background: var(--background-error-secondary-hover); - } - } - - &:focus-visible { - .content { - border-color: var(--background-action-secondary); - } - } - } - - &:focus-visible { - background: var(--background); - border-color: var(--background-error); - - .content { - color: var(--background-error-hover); - border-color: var(--background); - background: var(--background-error-secondary-hover); - } - } - - &:disabled { - cursor: not-allowed; - color: var(--background-disable); - - .content { - border-color: var(--background-disable); - } - } - } - - &.tertiary-danger { - background: transparent; - border: 2px solid transparent; - color: var(--background-error); - - .content { - background: transparent; - border-color: transparent; - } - - &:hover:not(:disabled) { - .content { - border-color: var(--background-error-hover); - background: var(--background-error-secondary-hover); - } - - &:focus-visible { - background: var(--background); - - .content { - border-color: var(--background); - } - } - } - - &:focus-visible { - background: var(--background); - border-color: var(--background-error); - - .content { - border-color: var(--background); - background: var(--background-error-secondary-hover); - } - } - - &:disabled { - cursor: not-allowed; - background: var(--background); - color: var(--background-disable); - } - } - - &.primary-dark { - border: 2px solid transparent; - color: var(--font-on-action); - - .content { - background: var(--light-buttons-primary); - } - - &:hover:not(:disabled) { - .content { - background: var(--light-buttons-primary-hover); - } - } - - &:active:not(:disabled) { - .content { - background: var(--light-buttons-primary-hover); - } - } - - &:focus-visible { - background: var(--background-brand); - border-color: var(--light-buttons-primary); - - .content { - border-color: var(--font-on-action); - border-radius: calc(var(--3xs) / 2); - } - } - - &:disabled { - cursor: not-allowed; - - .content { - background: var(--background-action-inverse); - color: var(--background-deselected); - } - } - } - - &.secondary-dark { - background: transparent; - border: 2px solid transparent; - color: var(--light-buttons-primary-hover); - - .content { - background: transparent; - border-color: var(--light-buttons-primary-hover); - } - - &:hover:not(:disabled) { - .content { - background: var(--light-buttons-secondary-background); - border-color: var(--light-buttons-secondary-hover); - color: var(--light-buttons-secondary-hover); - } - } - - &:active:not(:disabled) { - .content { - background: transparent; - border-color: var(--light-buttons-secondary-hover); - color: var(--light-buttons-secondary-hover); - } - } - - &:focus-visible { - background: transparent; - border-color: var(--light-buttons-secondary-hover); - - .content { - border-color: transparent; - background: var(--light-buttons-secondary-background); - color: var(--light-buttons-secondary-hover); - border-radius: calc(var(--3xs) / 2); - } - } - - &:disabled { - cursor: not-allowed; - color: var(--background-deselected); - - .content { - border-color: var(--background-deselected); - } - } - } - - &.secondary-light-dark { - background: transparent; - border: 2px solid transparent; - color: var(--light-buttons-secondary-light); - - .content { - background: transparent; - border-color: var(--light-buttons-secondary-light); - } - - &:hover:not(:disabled) { - .content { - background: var(--light-buttons-secondary-light-hover-background); - border-color: var(--border); - color: var(--light-buttons-tertiary); - } - } - - &:active:not(:disabled) { - .content { - background: transparent; - border-color: var(--background-secondary); - color: var(--font-on-action); - } - } - - &:focus-visible { - background: transparent; - border-color: var(--light-buttons-tertiary); - border-radius: var(--2xs); - - .content { - background: var(--Dark-Buttons-secondary-light-hover-background); - color: var(--light-buttons-tertiary); - border-radius: var(--3xs); - } - } - - &:disabled { - cursor: not-allowed; - color: var(--background-deselected); - - .content { - border-color: var(--background-deselected); - } - } - } - - &.tertiary-dark { - background: transparent; - border: 2px solid transparent; - color: var(--light-buttons-tertiary); - - .content { - background: transparent; - border-color: transparent; - } - - &:hover:not(:disabled) { - .content { - background: var(--light-buttons-secondary-light-hover-background); - border-color: var(--border); - color: var(--light-buttons-tertiary); - } - } - - &:active:not(:disabled) { - .content { - background: transparent; - border-color: var(--background-secondary); - color: var(--font-on-action); - } - } - - &:focus-visible { - background: transparent; - border-color: var(--light-buttons-tertiary); - border-radius: var(--2xs); - - .content { - background: var(--Dark-Buttons-secondary-light-hover-background); - color: var(--light-buttons-tertiary); - border-radius: var(--3xs); - } - } - - &:disabled { - cursor: not-allowed; - color: var(--background-deselected); - } - } -} diff --git a/packages/design-system/src/components/ds-button/versions/ds-button-new/ds-button-new.stories.module.scss b/packages/design-system/src/components/ds-button/versions/ds-button-new/ds-button-new.stories.module.scss deleted file mode 100644 index 408c5afd9..000000000 --- a/packages/design-system/src/components/ds-button/versions/ds-button-new/ds-button-new.stories.module.scss +++ /dev/null @@ -1,36 +0,0 @@ -.showcaseContainer { - background: var(--color-dap-gray-100); - padding: 24px; - overflow: auto; -} - -.showcaseTable { - border-collapse: collapse; - width: 100%; -} - -.showcaseHeader { - text-align: center; - vertical-align: middle; - padding: 16px; -} - -.showcaseCell { - padding: 16px; - text-align: center; - vertical-align: middle; -} - -.showcaseCellBold { - font-weight: bold; - padding-right: 8px; -} - -.showcaseCellInline { - display: inline-block; -} - -.showcaseCellDark { - background: var(--brand-500-default); - color: var(--secondary-050); -} diff --git a/packages/design-system/src/components/ds-button/versions/ds-button-new/ds-button-new.stories.tsx b/packages/design-system/src/components/ds-button/versions/ds-button-new/ds-button-new.stories.tsx deleted file mode 100644 index 7f452df82..000000000 --- a/packages/design-system/src/components/ds-button/versions/ds-button-new/ds-button-new.stories.tsx +++ /dev/null @@ -1,213 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react-vite'; -import { fn } from 'storybook/test'; -import classNames from 'classnames'; -import { DsIcon } from '../../../ds-icon'; -import { DsSpinner } from '../../../ds-spinner'; -import DsButtonNew from './ds-button-new'; -import { buttonSizes, buttonTypes, buttonVariants } from './ds-button-new.types'; -import styles from './ds-button-new.stories.module.scss'; - -const meta: Meta = { - // eslint-disable-next-line @drivenets/ds-internal/consistent-story-titles -- We want to show user something different than code. - title: 'Components/Button', - component: DsButtonNew, - parameters: { - // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout - layout: 'centered', - }, - // More on argTypes: https://storybook.js.org/docs/api/argtypes - argTypes: { - buttonType: { - control: { type: 'select' }, - options: buttonTypes, - description: 'Button type', - table: { - defaultValue: { - summary: 'primary', - }, - }, - }, - variant: { - control: { type: 'select' }, - options: buttonVariants, - table: { - defaultValue: { - summary: 'filled', - }, - }, - }, - size: { - control: { type: 'select' }, - options: buttonSizes, - table: { - defaultValue: { - summary: 'medium', - }, - }, - }, - }, - // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args - args: { onClick: fn() }, -}; -export default meta; -type Story = StoryObj; - -const defaultButtonText = 'Button Text'; - -export const DefaultButton: Story = { - args: { - buttonType: 'primary', - variant: 'filled', - size: 'large', - disabled: false, - children: ( - <> -
@@ -209,15 +203,13 @@ export const DsCommentBubble = ({ {(isInitialMode || hasThread) && ( - - + /> )}
@@ -233,15 +225,13 @@ export const DsCommentBubble = ({ )} - - + />
)} diff --git a/packages/design-system/src/components/ds-comment-bubble/ds-comment-bubble.types.ts b/packages/design-system/src/components/ds-comment-bubble/ds-comment-bubble.types.ts index b2aaf4465..8caa822ff 100644 --- a/packages/design-system/src/components/ds-comment-bubble/ds-comment-bubble.types.ts +++ b/packages/design-system/src/components/ds-comment-bubble/ds-comment-bubble.types.ts @@ -91,7 +91,7 @@ export interface DsCommentBubbleProps { */ onMessageResolved?: (messageId: string) => void; /** - * Optional reference chip shown in the bubble header. A string is rendered as a + * Optional reference tag shown in the bubble header. A string is rendered as a * `DsTag` with a sell icon; any other `ReactNode` is rendered verbatim. */ referenceTag?: ReactNode; diff --git a/packages/design-system/src/components/ds-comment-card/ds-comment-card.tsx b/packages/design-system/src/components/ds-comment-card/ds-comment-card.tsx index 8e7020c37..ebf8dac7f 100644 --- a/packages/design-system/src/components/ds-comment-card/ds-comment-card.tsx +++ b/packages/design-system/src/components/ds-comment-card/ds-comment-card.tsx @@ -101,9 +101,7 @@ export const DsCommentCard = ({ {showMoreMenu && ( - - - + {onToggleActionRequired && ( @@ -137,14 +135,12 @@ export const DsCommentCard = ({ {onResolve && ( - - + /> )} diff --git a/packages/design-system/src/components/ds-comment-card/ds-comment-card.types.ts b/packages/design-system/src/components/ds-comment-card/ds-comment-card.types.ts index 0217a7233..2a8c0e8f9 100644 --- a/packages/design-system/src/components/ds-comment-card/ds-comment-card.types.ts +++ b/packages/design-system/src/components/ds-comment-card/ds-comment-card.types.ts @@ -73,16 +73,16 @@ export interface CommentData { */ isResolved: boolean; /** - * Ordered list of messages in the thread, from oldest to newest. The first + * Ordered the list of messages in the thread, from oldest to newest. The first * message is typically the top-level comment body. */ messages: CommentMessage[]; /** - * Optional tag labels used for filtering and grouping in the comments drawer. + * Optional tag labels used for filtering and grouping in the comment's drawer. */ labels?: string[]; /** - * Optional reference chip shown in the card header. A string renders as a tag; + * Optional reference tag shown in the card header. A string renders as a tag; * any other `ReactNode` renders verbatim. */ referenceTag?: ReactNode; diff --git a/packages/design-system/src/components/ds-comments-drawer/comments-filters.ts b/packages/design-system/src/components/ds-comments-drawer/comments-filters.ts index f605955c0..0f3dd1157 100644 --- a/packages/design-system/src/components/ds-comments-drawer/comments-filters.ts +++ b/packages/design-system/src/components/ds-comments-drawer/comments-filters.ts @@ -3,6 +3,15 @@ import type { FilterTag, CommentsFilterState } from './comments-filters.types'; export type { FilterTag, CommentsFilterState } from './comments-filters.types'; +/** Formats using local calendar date (not UTC) to stay consistent with applyFilters, + * which treats dateFrom/dateTo as local dates via setHours(0,0,0,0). */ +const formatFilterDateForTag = (d: Date): string => { + const year = d.getFullYear(); + const month = String(d.getMonth() + 1).padStart(2, '0'); + const day = String(d.getDate()).padStart(2, '0'); + return `${String(year)}-${month}-${day}`; +}; + export const initialFilterState: CommentsFilterState = { authors: [], labels: [], @@ -42,6 +51,7 @@ export const applyFilters = ( if (filters.dateFrom) { const fromDate = new Date(filters.dateFrom); + fromDate.setHours(0, 0, 0, 0); if (comment.createdAt < fromDate) { return false; } @@ -89,8 +99,8 @@ export const filtersToTags = (filters: CommentsFilterState, authorMap: Map = {}): CommentData => ({ id: '1', numericId: 1, author: makeAuthor('user-1', 'Alice'), - createdAt: new Date('2025-06-15'), + createdAt: new Date(2025, 5, 15), isResolved: false, messages: [], ...overrides, @@ -125,11 +125,11 @@ describe('comments-filters', () => { it('should filter by dateFrom', () => { const comments = [ - makeComment({ id: '1', createdAt: new Date('2025-01-01') }), - makeComment({ id: '2', createdAt: new Date('2025-06-15') }), - makeComment({ id: '3', createdAt: new Date('2025-12-31') }), + makeComment({ id: '1', createdAt: new Date(2025, 0, 1) }), + makeComment({ id: '2', createdAt: new Date(2025, 5, 15) }), + makeComment({ id: '3', createdAt: new Date(2025, 11, 31) }), ]; - const filters: CommentsFilterState = { ...emptyFilters, dateFrom: '2025-06-01' }; + const filters: CommentsFilterState = { ...emptyFilters, dateFrom: new Date(2025, 5, 1) }; const result = applyFilters(comments, filters, neverActionRequired); @@ -139,11 +139,11 @@ describe('comments-filters', () => { it('should filter by dateTo (inclusive end of day)', () => { const comments = [ - makeComment({ id: '1', createdAt: new Date('2025-01-01') }), - makeComment({ id: '2', createdAt: new Date('2025-06-15T23:59:59.999') }), - makeComment({ id: '3', createdAt: new Date('2025-12-31') }), + makeComment({ id: '1', createdAt: new Date(2025, 0, 1) }), + makeComment({ id: '2', createdAt: new Date(2025, 5, 15, 23, 59, 59, 999) }), + makeComment({ id: '3', createdAt: new Date(2025, 11, 31) }), ]; - const filters: CommentsFilterState = { ...emptyFilters, dateTo: '2025-06-15' }; + const filters: CommentsFilterState = { ...emptyFilters, dateTo: new Date(2025, 5, 15) }; const result = applyFilters(comments, filters, neverActionRequired); @@ -153,14 +153,14 @@ describe('comments-filters', () => { it('should filter by date range', () => { const comments = [ - makeComment({ id: '1', createdAt: new Date('2025-01-01') }), - makeComment({ id: '2', createdAt: new Date('2025-06-15') }), - makeComment({ id: '3', createdAt: new Date('2025-12-31') }), + makeComment({ id: '1', createdAt: new Date(2025, 0, 1) }), + makeComment({ id: '2', createdAt: new Date(2025, 5, 15) }), + makeComment({ id: '3', createdAt: new Date(2025, 11, 31) }), ]; const filters: CommentsFilterState = { ...emptyFilters, - dateFrom: '2025-03-01', - dateTo: '2025-09-01', + dateFrom: new Date(2025, 2, 1), + dateTo: new Date(2025, 8, 1), }; const result = applyFilters(comments, filters, neverActionRequired); @@ -319,8 +319,8 @@ describe('comments-filters', () => { it('should create date range tag with both dates', () => { const filters: CommentsFilterState = { ...emptyFilters, - dateFrom: '2025-01-01', - dateTo: '2025-12-31', + dateFrom: new Date(2025, 0, 1), + dateTo: new Date(2025, 11, 31), }; const result = filtersToTags(filters, authorMap); @@ -335,7 +335,7 @@ describe('comments-filters', () => { }); it('should create date range tag with only dateFrom', () => { - const filters: CommentsFilterState = { ...emptyFilters, dateFrom: '2025-01-01' }; + const filters: CommentsFilterState = { ...emptyFilters, dateFrom: new Date(2025, 0, 1) }; const result = filtersToTags(filters, authorMap); @@ -344,7 +344,7 @@ describe('comments-filters', () => { }); it('should create date range tag with only dateTo', () => { - const filters: CommentsFilterState = { ...emptyFilters, dateTo: '2025-12-31' }; + const filters: CommentsFilterState = { ...emptyFilters, dateTo: new Date(2025, 11, 31) }; const result = filtersToTags(filters, authorMap); @@ -382,8 +382,8 @@ describe('comments-filters', () => { const filters: CommentsFilterState = { statuses: ['resolved'], authors: ['user-1'], - dateFrom: '2025-01-01', - dateTo: '2025-12-31', + dateFrom: new Date(2025, 0, 1), + dateTo: new Date(2025, 11, 31), labels: ['bug'], }; @@ -444,8 +444,8 @@ describe('comments-filters', () => { it('should clear both dateFrom and dateTo when removing a date tag', () => { const filters: CommentsFilterState = { ...emptyFilters, - dateFrom: '2025-01-01', - dateTo: '2025-12-31', + dateFrom: new Date(2025, 0, 1), + dateTo: new Date(2025, 11, 31), }; const tag: FilterTag = { id: 'date-range', diff --git a/packages/design-system/src/components/ds-comments-drawer/components/comments-filter-modal/comments-filter-modal.tsx b/packages/design-system/src/components/ds-comments-drawer/components/comments-filter-modal/comments-filter-modal.tsx index 8359eacdd..277a6d1d6 100644 --- a/packages/design-system/src/components/ds-comments-drawer/components/comments-filter-modal/comments-filter-modal.tsx +++ b/packages/design-system/src/components/ds-comments-drawer/components/comments-filter-modal/comments-filter-modal.tsx @@ -7,7 +7,7 @@ import { DsTypography } from '../../../ds-typography'; import { DsCheckbox } from '../../../ds-checkbox'; import { DsButton } from '../../../ds-button'; import { DsIcon } from '../../../ds-icon'; -import { DsDateInput } from '../../../ds-date-input'; +import { DsDatePicker } from '../../../ds-date-picker'; export const CommentsFilterModal = ({ open, @@ -42,12 +42,12 @@ export const CommentsFilterModal = ({ }); }; - const handleDateFromChange = (value: string | undefined) => { - onFiltersChange({ ...filters, dateFrom: value }); + const handleDateFromChange = (value: Date | null) => { + onFiltersChange({ ...filters, dateFrom: value ?? undefined }); }; - const handleDateToChange = (value: string | undefined) => { - onFiltersChange({ ...filters, dateTo: value }); + const handleDateToChange = (value: Date | null) => { + onFiltersChange({ ...filters, dateTo: value ?? undefined }); }; const filterCounts = { @@ -160,10 +160,10 @@ export const CommentsFilterModal = ({ From - @@ -172,10 +172,10 @@ export const CommentsFilterModal = ({ To - @@ -204,12 +204,12 @@ export const CommentsFilterModal = ({ - + Clear all - + Apply diff --git a/packages/design-system/src/components/ds-comments-drawer/ds-comments-drawer.stories.tsx b/packages/design-system/src/components/ds-comments-drawer/ds-comments-drawer.stories.tsx index 010c5d038..abdb129e5 100644 --- a/packages/design-system/src/components/ds-comments-drawer/ds-comments-drawer.stories.tsx +++ b/packages/design-system/src/components/ds-comments-drawer/ds-comments-drawer.stories.tsx @@ -465,8 +465,8 @@ Interactive story demonstrating the filter functionality. - **Date range**: Filter by creation date - **Labels**: Filter by tags (Bug, High Priority, Feature Request, Documentation, Enhancement, Question) 3. Click **Apply** to see the filtered results -4. Selected filters appear as chips below the toolbar -5. Click on a chip to remove that filter, or use **Clear all** to remove all filters +4. Selected filters appear as tags below the toolbar +5. Click on a tag to remove that filter, or use **Clear all** to remove all filters **Current mock data:** - 4 unresolved comments with various authors and labels diff --git a/packages/design-system/src/components/ds-comments-drawer/ds-comments-drawer.tsx b/packages/design-system/src/components/ds-comments-drawer/ds-comments-drawer.tsx index 819129864..9a5c835e7 100644 --- a/packages/design-system/src/components/ds-comments-drawer/ds-comments-drawer.tsx +++ b/packages/design-system/src/components/ds-comments-drawer/ds-comments-drawer.tsx @@ -114,9 +114,7 @@ export const DsCommentsDrawer = ({ {commentCount} Comments
- - - +
@@ -136,19 +134,17 @@ export const DsCommentsDrawer = ({ /> - - + /> {hasResolved && ( - + {showResolved ? 'Hide' : 'Show'} resolved ({resolvedCount}) diff --git a/packages/design-system/src/components/ds-confirmation/ds-confirmation.stories.module.scss b/packages/design-system/src/components/ds-confirmation/ds-confirmation.stories.module.scss deleted file mode 100644 index ca51df58e..000000000 --- a/packages/design-system/src/components/ds-confirmation/ds-confirmation.stories.module.scss +++ /dev/null @@ -1,31 +0,0 @@ -.customBodyContainer { - display: flex; - flex-direction: column; - gap: 16px; -} - -.customBodyText { - margin: 0; - color: var(--font-secondary); -} - -.radioGroup { - display: flex; - flex-direction: column; - gap: 8px; -} - -.radioLabel { - display: flex; - align-items: center; - gap: 8px; - cursor: pointer; -} - -.infoNote { - background: var(--background-secondary); - padding: 12px; - border-radius: 6px; - font-size: 14px; - color: var(--font-secondary); -} diff --git a/packages/design-system/src/components/ds-confirmation/ds-confirmation.stories.tsx b/packages/design-system/src/components/ds-confirmation/ds-confirmation.stories.tsx deleted file mode 100644 index 8d9ac6256..000000000 --- a/packages/design-system/src/components/ds-confirmation/ds-confirmation.stories.tsx +++ /dev/null @@ -1,257 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react-vite'; -import { useState } from 'react'; -import { DsConfirmation } from './ds-confirmation'; -import { DsButton } from '../ds-button'; -import styles from './ds-confirmation.stories.module.scss'; - -/** - * @deprecated This component is deprecated. Use DsModal instead. - * @see {@link ../ds-modal/ds-modal.stories} for examples of the replacement component. - */ -const meta: Meta = { - title: 'Components/Confirmation (Deprecated)', - component: DsConfirmation, - tags: ['deprecated'], - parameters: { - layout: 'centered', - }, - argTypes: { - open: { - control: { type: 'boolean' }, - }, - }, -}; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - render: function Render() { - const [open, setOpen] = useState(false); - return ( - <> - setOpen(true)}>Open Confirmation - - - Confirm Action - - - Are you sure you want to proceed with this action? - - - { - console.log('Reject clicked'); - setOpen(false); - }} - > - No - - { - console.log('Accept clicked'); - setOpen(false); - }} - > - Yes - - - - - - ); - }, -}; - -export const WithCancel: Story = { - render: function Render() { - const [open, setOpen] = useState(false); - return ( - <> - setOpen(true)}>Open Confirmation - - - Save Changes - - - Do you want to save your changes before closing? - - { - console.log('Cancel clicked'); - setOpen(false); - }} - > - Cancel - - - { - console.log('Discard clicked'); - setOpen(false); - }} - > - Discard - - { - console.log('Save clicked'); - setOpen(false); - }} - > - Save - - - - - - ); - }, -}; - -export const Danger: Story = { - render: function Render() { - const [open, setOpen] = useState(false); - return ( - <> - setOpen(true)}>Open Confirmation - - - Delete Item - - - - Are you sure you want to delete this item? This action cannot be undone. - - - - { - console.log('Cancel clicked'); - setOpen(false); - }} - > - Cancel - - { - console.log('Delete clicked'); - setOpen(false); - }} - > - Delete - - - - - - ); - }, -}; - -export const CustomBody: Story = { - render: function Render() { - const [open, setOpen] = useState(false); - const [selectedOption, setSelectedOption] = useState('option1'); - - return ( - <> - setOpen(true)}>Open Custom Confirmation - - - Advanced Configuration - - - -
-

Please select your preferred configuration option:

-
- - - -
-
- Note: This action will apply the selected configuration to your current - project. You can change this setting later in the project settings. -
-
-
- - - { - console.log('Cancel clicked'); - setOpen(false); - }} - > - Cancel - - { - console.log('Apply configuration:', selectedOption); - setOpen(false); - }} - > - Apply Configuration - - - -
- - ); - }, -}; diff --git a/packages/design-system/src/components/ds-confirmation/ds-confirmation.tsx b/packages/design-system/src/components/ds-confirmation/ds-confirmation.tsx deleted file mode 100644 index ddf72a68b..000000000 --- a/packages/design-system/src/components/ds-confirmation/ds-confirmation.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { DsModal } from '../ds-modal'; -import type { DsConfirmationProps } from './ds-confirmation.types'; - -/** - * @deprecated DsConfirmation is deprecated. Use DsModal instead. - * @see {@link ../ds-modal} for the replacement component. - */ -export const DsConfirmation = (props: DsConfirmationProps) => ( - -); - -DsConfirmation.Header = DsModal.Header; -DsConfirmation.Title = DsModal.Title; -DsConfirmation.CloseTrigger = DsModal.CloseTrigger; -DsConfirmation.Body = DsModal.Body; -DsConfirmation.Footer = DsModal.Footer; -DsConfirmation.Actions = DsModal.Actions; diff --git a/packages/design-system/src/components/ds-confirmation/ds-confirmation.types.ts b/packages/design-system/src/components/ds-confirmation/ds-confirmation.types.ts deleted file mode 100644 index a5e97cd91..000000000 --- a/packages/design-system/src/components/ds-confirmation/ds-confirmation.types.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { CSSProperties, ReactNode } from 'react'; - -/** - * @deprecated DsConfirmationProps is deprecated. Use DsModal with variant="info" instead. - * @see {@link ../ds-modal/ds-modal} for the replacement. - */ -export interface DsConfirmationProps { - /** - * Whether the modal is open - */ - open: boolean; - /** - * Callback when modal open state changes - * @param open - whether the modal is open - */ - onOpenChange: (open: boolean) => void; - /** - * Additional CSS styles - */ - style?: CSSProperties; - /** - * Additional CSS class name - */ - className?: string; - /** - * Modal body content - */ - children: ReactNode; -} diff --git a/packages/design-system/src/components/ds-confirmation/index.ts b/packages/design-system/src/components/ds-confirmation/index.ts deleted file mode 100644 index 1c2e2a0ac..000000000 --- a/packages/design-system/src/components/ds-confirmation/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { DsConfirmation } from './ds-confirmation'; -export type { DsConfirmationProps } from './ds-confirmation.types'; diff --git a/packages/design-system/src/components/ds-date-input/ds-date-input.module.scss b/packages/design-system/src/components/ds-date-input/ds-date-input.module.scss deleted file mode 100644 index 6d6d01cc1..000000000 --- a/packages/design-system/src/components/ds-date-input/ds-date-input.module.scss +++ /dev/null @@ -1,247 +0,0 @@ -@use '../../styles/shared/input'; -@use '../../styles/shared/dropdown'; - -.dateInputControl { - @include input.base-input-container; - @include input.input-container-states; - max-height: input.$input-text-height-default; - justify-content: space-between; - padding: var(--xs) var(--sm); - gap: var(--3xs); - - border-color: var(--form-control-border-color, var(--border)); - - &:hover:not(.disabled) { - border-color: var(--form-control-border-color, var(--color-border-hover)); - } - - &:focus-within { - border-color: var(--form-control-border-color, var(--color-border-action)); - } - - &.disabled { - color: var(--background-deselected); - background: var(--background-secondary); - border-color: var(--border-disabled); - cursor: not-allowed; - - .input { - color: var(--font-disabled); - } - - .dateInputButton { - color: var(--background-disable); - cursor: not-allowed; - } - } - - &.readOnly { - background: var(--background-secondary); - cursor: default; - } -} - -.input { - @include input.base-input; - min-width: 0; - flex: 1; - padding: 0; -} - -.dateInputButton { - @include input.icon-button; - display: flex; - align-items: center; - cursor: pointer; - padding: 0; - transition: color 0.2s ease; - - &:hover:not(:disabled) { - color: var(--background-action-hover); - } - - &:disabled { - color: var(--background-disable); - cursor: not-allowed; - } -} - -.positioner { - @include dropdown.dropdown-viewport; -} - -.content { - @include dropdown.dropdown-content; - padding: var(--standard); - min-width: fit-content; -} - -.viewControl { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: var(--xs); -} - -.viewTrigger { - flex: 1; - display: flex; - align-items: center; - justify-content: center; - background: transparent; - border: none; - cursor: pointer; - padding: var(--2xs) var(--xs); - border-radius: var(--2xs); - color: var(--font-main); - transition: background-color 0.2s; - - &:hover { - background: var(--color-background-hover); - } -} - -.prevTrigger, -.nextTrigger { - display: flex; - align-items: center; - justify-content: center; - background: transparent; - border: none; - cursor: pointer; - padding: var(--2xs); - border-radius: var(--2xs); - color: var(--font-secondary); - transition: all 0.2s; - - &:hover { - background: var(--color-background-hover); - color: var(--font-main); - } - - &:disabled { - cursor: not-allowed; - opacity: 0.5; - } -} - -.calendarView { - display: flex; - flex-direction: column; - gap: var(--xs); -} - -.table { - width: 100%; - border-collapse: collapse; - - .tableBody .tableRow .tableCellTrigger:focus-visible { - .tableCellTriggerValue { - outline: 2px solid var(--background-action-hover); - border-radius: var(--2xs); - border-width: 0; - } - } -} - -.tableHead { - border-bottom: 1px solid var(--border); -} - -.tableHeader { - text-align: center; - padding: var(--2xs); - color: var(--font-secondary); - font-weight: var(--font-weight-medium); -} - -.tableRow { - &:first-child { - .tableCellTrigger { - margin-top: var(--xs); - } - } -} - -.tableCellTrigger { - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; - user-select: none; - -webkit-tap-highlight-color: transparent; - outline: none; - - .tableCellTriggerValue { - display: flex; - align-items: center; - justify-content: center; - width: var(--xl); - height: var(--xl); - margin: var(--3xs); - color: var(--font-main); - border-radius: var(--xs); - } - - &:hover:not([data-disabled], [data-selected], [data-in-range], [data-in-hover-range]) - .tableCellTriggerValue { - outline: 1px solid var(--border); - background: var(--background-action-hover-weak); - } - - &[data-outside-range] .tableCellTriggerValue { - color: var(--font-disabled); - } - - &[data-selected] .tableCellTriggerValue { - background: var(--background-primary); - color: var(--font-on-action); - } - - &[data-in-range]:not([data-selected]) { - position: relative; - - .tableCellTriggerValue { - background: var(--background-action-secondary-hover); - } - } - - &[data-today]:not([data-selected], [data-in-range]):hover .tableCellTriggerValue { - border-radius: var(--xs); - } - - &[data-today]:not([data-selected]) .tableCellTriggerValue { - position: relative; - - &::after { - content: ''; - position: absolute; - width: 75%; - bottom: 4px; - border-bottom: 1px solid var(--background-brand); - } - } - - &[data-today][data-selected] .tableCellTriggerValue { - position: relative; - - &::after { - content: ''; - position: absolute; - width: 75%; - bottom: 4px; - border-bottom: 1px solid var(--background); - } - } - - &[data-disabled] .tableCellTriggerValue { - color: var(--font-disabled); - cursor: not-allowed; - opacity: 0.5; - } - - &[data-unavailable] .tableCellTriggerValue { - text-decoration: line-through; - opacity: 0.4; - } -} diff --git a/packages/design-system/src/components/ds-date-input/ds-date-input.stories.module.scss b/packages/design-system/src/components/ds-date-input/ds-date-input.stories.module.scss deleted file mode 100644 index bbb23cef7..000000000 --- a/packages/design-system/src/components/ds-date-input/ds-date-input.stories.module.scss +++ /dev/null @@ -1,29 +0,0 @@ -.containerSingle { - width: 320px; -} - -.containerRange { - width: 420px; -} - -.helperText { - font-size: 12px; - margin-top: 8px; - color: var(--font-secondary); -} - -.errorText { - font-size: 12px; - margin-top: 8px; - color: var(--color-error); -} - -.infoContainer { - font-size: 12px; - margin-top: 8px; - color: var(--font-secondary); -} - -.buttonContainer { - margin-bottom: 16px; -} diff --git a/packages/design-system/src/components/ds-date-input/ds-date-input.stories.tsx b/packages/design-system/src/components/ds-date-input/ds-date-input.stories.tsx deleted file mode 100644 index 21a72e707..000000000 --- a/packages/design-system/src/components/ds-date-input/ds-date-input.stories.tsx +++ /dev/null @@ -1,339 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react-vite'; -import { useState } from 'react'; -import { expect, screen, userEvent, waitFor, within } from 'storybook/test'; -import MockDate from 'mockdate'; -import DsDateInput from './ds-date-input'; -import type { DsDateInputProps } from './ds-date-input.types'; -import styles from './ds-date-input.stories.module.scss'; - -// Mock system time to January 15, 2026 for consistent test dates -const MOCK_DATE = new Date('2026-01-15T12:00:00'); - -/** - * @deprecated This component is deprecated. Use `DsDatePicker` or `DsDateRangePicker` instead. - * @see {@link ../ds-date-picker/ds-date-picker.stories} for single date selection examples. - * @see {@link ../ds-date-range-picker/ds-date-range-picker.stories} for date range selection examples. - */ -const meta: Meta = { - title: 'Components/DateInput (Deprecated)', - component: DsDateInput, - parameters: { - layout: 'centered', - docs: { - description: { - component: - '**Deprecated**: This component is deprecated. Please use `DsDatePicker` for single date selection or `DsDateRangePicker` for date range selection instead.', - }, - }, - }, - tags: ['deprecated'], - beforeEach: () => { - // We use mockdate here and not vi.useFakeTimers() because the latter is not compatible with Storybook. - // See https://github.com/storybookjs/storybook/issues/31400#issuecomment-2943382690 for more details. - MockDate.set(MOCK_DATE); - - return () => { - MockDate.reset(); - }; - }, -}; - -export default meta; - -type Story = StoryObj; - -/** - * Basic single date input with default configuration - */ -export const Default: Story = { - render: function Render() { - const [value, setValue] = useState(); - - return ; - }, - play: async ({ canvasElement }: { canvasElement: HTMLElement }) => { - const canvas = within(canvasElement); - const input = canvas.getByPlaceholderText('MM/DD/YYYY'); - const calendarButton = canvas.getByRole('button', { name: /open calendar/i }); - - // 1. Open calendar and select a date (01/15/2026) - await userEvent.click(calendarButton); - const jan15Button = screen.getByRole('button', { name: /Choose.*January 15, 2026/i }); - await userEvent.click(jan15Button); - - // Verify input shows selected date - await expect(input).toHaveValue('01/15/2026'); - - // 2. Open calendar again and verify it shows the new date as selected - await userEvent.click(calendarButton); - const initialSelectedCell = screen.queryByRole('gridcell', { selected: true }); - await expect(initialSelectedCell).toBeInTheDocument(); - await expect(initialSelectedCell).toHaveAttribute('data-value', '2026-01-15'); - - // 3. Type a different date into the input (01/16/2026) - await userEvent.clear(input); - await userEvent.type(input, '01/16/2026'); - - // Verify input updated - await expect(input).toHaveValue('01/16/2026'); - - // 4. Verify calendar shows the new date as selected - const selectedCell = screen.queryByRole('gridcell', { selected: true }); - await expect(selectedCell).toBeInTheDocument(); - await expect(selectedCell).toHaveAttribute('data-value', '2026-01-16'); - - // 5. Select another date (01/17/2026) - const jan17Button = screen.getByRole('button', { name: /Choose.*January 17, 2026/i }); - await userEvent.click(jan17Button); - - // 6. Verify input text updated - await expect(input).toHaveValue('01/17/2026'); - - // 7. Click clear button and verify input is cleared - const clearButton = canvas.getByRole('button', { name: /clear date/i }); - await userEvent.click(clearButton); - - await expect(input).toHaveValue(''); - - // 8. Verify clear button is gone after clearing - const clearButtonAfter = canvas.queryByRole('button', { name: /clear date/i }); - await expect(clearButtonAfter).not.toBeInTheDocument(); - - // Exit the calendar display - await userEvent.keyboard('{Escape}'); - }, -}; - -/** - * Date range input - unified input field for start and end dates - */ -export const RangeMode: Story = { - render: function Render() { - const [value, setValue] = useState<[string, string]>(); - - return ; - }, - play: async ({ canvasElement }: { canvasElement: HTMLElement }) => { - const canvas = within(canvasElement); - const input = canvas.getByPlaceholderText('MM/DD/YYYY - MM/DD/YYYY'); - const calendarButton = canvas.getByRole('button', { name: /open calendar/i }); - - // 1. Open calendar and select range [01/15/2026 - 01/17/2026] - await userEvent.click(calendarButton); - - await waitFor(() => { - return expect(screen.getByRole('application', { name: 'calendar' })).toBeVisible(); - }); - - // Click start date (01/15/2026) - const jan15Button = screen.getByRole('button', { name: /January 15, 2026/i }); - await userEvent.click(jan15Button); - - // Click end date (01/17/2026) - const jan17Button = screen.getByRole('button', { name: /January 17, 2026/i }); - await userEvent.click(jan17Button); - - // 2. Check calendar shows the selection correctly (both dates marked as selected) - const allCells = screen.queryAllByRole('gridcell'); - const jan15Cell = allCells.find((cell) => cell.getAttribute('data-value') === '2026-01-15'); - const jan17Cell = allCells.find((cell) => cell.getAttribute('data-value') === '2026-01-17'); - - await expect(jan15Cell).toBeInTheDocument(); - await expect(jan17Cell).toBeInTheDocument(); - await expect(jan15Cell).toHaveAttribute('aria-selected', 'true'); - await expect(jan17Cell).toHaveAttribute('aria-selected', 'true'); - - // 3. Check input text is updated correctly - await expect(input).toHaveValue('01/15/2026 - 01/17/2026'); - - // 4. Type a new range [01/14/2026 - 01/18/2026] - await userEvent.clear(input); - await userEvent.type(input, '01/14/2026 - 01/18/2026'); - await userEvent.click(input); - await userEvent.type(input, '{Escape}'); // Trigger parsing. - - // Verify input updated - await expect(input).toHaveValue('01/14/2026 - 01/18/2026'); - - // 5. Check calendar is updated correctly - await userEvent.click(calendarButton); - - // Check the new selected dates - const jan14Cell = allCells.find((cell) => cell.getAttribute('data-value') === '2026-01-14'); - const jan18Cell = allCells.find((cell) => cell.getAttribute('data-value') === '2026-01-18'); - - await expect(jan14Cell).toBeInTheDocument(); - await expect(jan18Cell).toBeInTheDocument(); - await expect(jan14Cell).toHaveAttribute('aria-selected', 'true'); - await expect(jan18Cell).toHaveAttribute('aria-selected', 'true'); - - // 6. Click clear button and verify input is cleared - const clearButton = canvas.getByRole('button', { name: /clear date/i }); - await userEvent.click(clearButton); - - await expect(input).toHaveValue(''); - - // Verify clear button is gone after clearing - const clearButtonAfter = canvas.queryByRole('button', { name: /clear date/i }); - await expect(clearButtonAfter).not.toBeInTheDocument(); - - // Exit the calendar display - await userEvent.keyboard('{Escape}'); - }, -}; - -/** - * Single date input with pre-filled value - */ -export const WithDefaultValue: Story = { - args: { - className: styles.containerSingle, - defaultValue: '2024-12-25', - }, -}; - -/** - * Range input with pre-filled values - */ -export const RangeWithDefaultValue: Story = { - args: { - className: styles.containerRange, - defaultValue: ['2024-12-01', '2024-12-31'], - range: true, - }, -}; - -/** - * Date input with min and max constraints - */ -export const WithMinMax: Story = { - render: function Render() { - const [value, setValue] = useState(); - const today = new Date(); - const minDate = new Date(today.getFullYear(), today.getMonth(), 1).toISOString().split('T')[0]; - const maxDate = new Date(today.getFullYear(), today.getMonth() + 3, 0).toISOString().split('T')[0]; - - return ( -
- -

Allowed: Current month to next 3 months only

-
- ); - }, -}; - -/** - * Disabled date input - */ -export const Disabled: Story = { - args: { - className: styles.containerSingle, - value: '2024-12-25', - disabled: true, - }, -}; - -/** - * Read-only date input - */ -export const ReadOnly: Story = { - args: { - className: styles.containerSingle, - value: '2024-12-25', - readOnly: true, - }, -}; - -/** - * Date input with hidden clear button - */ -export const HideClearButton: Story = { - render: function Render() { - const [value, setValue] = useState(); - - return ( - - ); - }, - play: async ({ canvasElement }: { canvasElement: HTMLElement }) => { - const canvas = within(canvasElement); - const calendarButton = canvas.getByRole('button', { name: /open calendar/i }); - - // Select a date - await userEvent.click(calendarButton); - const jan15Button = screen.getByRole('button', { name: /Choose.*January 15, 2026/i }); - await userEvent.click(jan15Button); - - // Verify the date is selected - const input = canvas.getByPlaceholderText('MM/DD/YYYY'); - await expect(input).toHaveValue('01/15/2026'); - - // Verify the clear button is not rendered - const clearButton = canvas.queryByRole('button', { name: /clear date/i }); - await expect(clearButton).not.toBeInTheDocument(); - }, -}; - -/** - * Controlled open state - */ -export const ControlledOpen: Story = { - render: function Render() { - const [value, setValue] = useState(); - const [open, setOpen] = useState(false); - - return ( -
-
- -
- -
- ); - }, -}; - -/** - * Uncontrolled with defaultValue - */ -export const Uncontrolled: Story = { - render: function Render() { - return ( -
- -
- ); - }, -}; - -/** - * Manual typing demonstration - */ -export const ManualTyping: Story = { - render: function Render() { - const [value, setValue] = useState<[string, string]>(); - - return ( -
- -
-

Try typing: "12/01/2024 - 12/31/2024"

-

Validation happens on blur

-

Invalid format resets to empty

-

Current value: {JSON.stringify(value)}

-
-
- ); - }, -}; diff --git a/packages/design-system/src/components/ds-date-input/ds-date-input.tsx b/packages/design-system/src/components/ds-date-input/ds-date-input.tsx deleted file mode 100644 index 39d510812..000000000 --- a/packages/design-system/src/components/ds-date-input/ds-date-input.tsx +++ /dev/null @@ -1,310 +0,0 @@ -import { type ChangeEvent, Fragment, useRef, useState } from 'react'; -import { DatePicker } from '@ark-ui/react/date-picker'; -import { Portal } from '@ark-ui/react/portal'; -import classNames from 'classnames'; -import type { DsDateInputProps } from './ds-date-input.types'; -import { - dateValuesToStrings, - formatDateInputValue, - isoStringToDateValue, - parseInputText, - stringToDateValue, - validateDateString, -} from './ds-date-input.utils'; -import { DsIcon } from '../ds-icon'; -import { DsTypography } from '../ds-typography'; -import styles from './ds-date-input.module.scss'; - -/** - * @deprecated DsDateInput is deprecated. Use DsDatePicker or DsDateRangePicker instead. - * @see {@link ../ds-date-picker} for single date selection. - * @see {@link ../ds-date-range-picker} for date range selection. - */ -const DsDateInput = ({ - range = false, - placeholder, - style, - className, - disablePortal = false, - hideClearButton = false, - onValueChange, - onOpenChange, - value: _value, - defaultValue: _defaultValue, - min: _min, - max: _max, - disabled, - readOnly, - id, - ...datePickerProps -}: DsDateInputProps) => { - const min = _min ? isoStringToDateValue(_min) : undefined; - const max = _max ? isoStringToDateValue(_max) : undefined; - - // Convert public API (string | [string, string]) to internal API (DateValue[]) - const value = _value ? stringToDateValue(_value) : []; - const defaultValue = _defaultValue ? stringToDateValue(_defaultValue) : []; - - // Local state for manual text editing - const [isFocused, setIsFocused] = useState(false); - const [isOpen, setIsOpen] = useState(false); - - const prevValue = useRef(null); - const inputRef = useRef(null); - - // Reformat the input value on every value change - // We don't format when the input is focused since the date value might get updated while the user is typing - // but we don't want to reformat the input text for him while he's typing. - if (!isFocused && prevValue.current !== JSON.stringify(value)) { - prevValue.current = JSON.stringify(value); - - if (inputRef.current) { - inputRef.current.value = formatDateInputValue(value, range); - } - } - - const clear = () => { - if (inputRef.current) { - inputRef.current.value = ''; - } - - onValueChange?.(undefined); - }; - - const handleInputChange = (e: ChangeEvent) => { - const text = e.target.value; - - // Try to parse and validate as the user types - const validation = validateDateString({ text, range, min, max }); - - if (validation.isValid) { - const parsedValue = parseInputText(text, range); - onValueChange?.(dateValuesToStrings(parsedValue, range) as never); - } - }; - - const handleBlur = () => { - setIsFocused(false); - - const text = inputRef.current?.value || ''; - const validation = validateDateString({ text, range, min, max }); - - // Clear invalid input on blur - if (!validation.isValid) { - clear(); - return; - } - - const parsedValue = parseInputText(text, range); - - onValueChange?.(dateValuesToStrings(parsedValue, range) as never); - }; - - const showClearButton = - !hideClearButton && !disabled && !readOnly && !!(value.length > 0 ? value : defaultValue).length; - const Wrapper = disablePortal ? Fragment : Portal; - - return ( - { - onValueChange?.(dateValuesToStrings(details.value, range) as never); - }} - selectionMode={range ? 'range' : 'single'} - positioning={{ placement: 'bottom-start', gutter: 4 }} - closeOnSelect={!range} - min={min} - max={max} - disabled={disabled} - readOnly={readOnly} - open={isOpen} - onOpenChange={(details) => { - // Don't close the calendar when the user is typing - if (!isFocused) { - setIsOpen(details.open); - onOpenChange?.(details.open); - } - }} - {...datePickerProps} - > - - - - - - 0 ? value : defaultValue, range)} - onChange={handleInputChange} - onBlur={handleBlur} - onFocus={() => setIsFocused(true)} - disabled={disabled} - readOnly={readOnly} - /> - - {showClearButton && ( - - )} - - - - - - -
- - - -
-
-
-
-
- ); -}; - -/** - * Calendar view control with navigation buttons - */ -const CalendarViewControl = () => ( - - - - - - - - - - - -); - -/** - * Day view - displays calendar grid with days of the month - */ -const DayView = () => ( - - - {(datePicker) => ( - - - - {datePicker.weekDays.map((weekDay, id) => ( - - {weekDay.short} - - ))} - - - - {datePicker.weeks.map((week, weekId) => ( - - {week.map((day, dayId) => ( - - - - {day.day} - - - - ))} - - ))} - - - )} - - -); - -/** - * Month view - displays grid with months of the year - */ -const MonthView = () => ( - - - {(datePicker) => ( - - - {datePicker.getMonthsGrid({ columns: 4, format: 'short' }).map((months, rowId) => ( - - {months.map((month, monthId) => ( - - - - {month.label} - - - - ))} - - ))} - - - )} - - -); - -/** - * Year view - displays grid with years in a decade - */ -const YearView = () => ( - - - {(datePicker) => ( - - - {datePicker.getYearsGrid({ columns: 4 }).map((years, rowId) => ( - - {years.map((year, yearId) => ( - - - - {year.label} - - - - ))} - - ))} - - - )} - - -); - -export default DsDateInput; diff --git a/packages/design-system/src/components/ds-date-input/ds-date-input.types.ts b/packages/design-system/src/components/ds-date-input/ds-date-input.types.ts deleted file mode 100644 index 4abda87f8..000000000 --- a/packages/design-system/src/components/ds-date-input/ds-date-input.types.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { type DatePickerRootProps } from '@ark-ui/react/date-picker'; - -/** - * Range-specific props with discriminated union for type safety - */ -type DsDateInputRangeProps = - | { - /** - * Whether to enable range selection - * @default false - */ - range?: false; - - /** - * Controlled value as ISO date string (YYYY-MM-DD) - * @example '2024-12-25' - */ - value?: string; - - /** - * Default value for uncontrolled mode - * @example '2024-12-25' - */ - defaultValue?: string; - - /** - * Callback when the value changes - * @param value - ISO date string (YYYY-MM-DD) or undefined when empty - * @example '2024-12-25' - */ - onValueChange?: (value: string | undefined) => void; - } - | { - /** - * Whether to enable range selection - */ - range: true; - - /** - * Controlled value as ISO date string tuple (YYYY-MM-DD) - * @example ['2024-12-01', '2024-12-31'] - */ - value?: [string, string]; - - /** - * Default value for uncontrolled mode - * @example ['2024-12-01', '2024-12-31'] - */ - defaultValue?: [string, string]; - - /** - * Callback when the value changes - * @param value - ISO date string tuple (YYYY-MM-DD) or undefined when empty - * @example ['2024-12-01', '2024-12-31'] - */ - onValueChange?: (value: [string, string] | undefined) => void; - }; - -/** - * Base props shared across all modes - */ -type DsDateInputBaseProps = Pick< - DatePickerRootProps, - 'disabled' | 'readOnly' | 'id' | 'open' | 'name' | 'style' -> & { - /** - * Unique identifier for the input field - */ - id?: string; - /** - * HTML name attribute for the input field, used in form submissions - */ - name?: string; - /** - * Whether the input is disabled and cannot be interacted with - * @default false - */ - disabled?: boolean; - /** - * Whether the input is read-only. The field is focusable but the value cannot - * be changed. - * @default false - */ - readOnly?: boolean; - /** - * Whether the calendar popover is open (controlled). Pair with `onOpenChange`. - */ - open?: boolean; - /** - * Callback when the open state changes - * @param open - Whether the calendar is open - */ - onOpenChange?: (open: boolean) => void; - /** - * Minimum allowed date as ISO string (YYYY-MM-DD) - * @example '2024-01-01' - */ - min?: string; - - /** - * Maximum allowed date as ISO string (YYYY-MM-DD) - * @example '2024-12-31' - */ - max?: string; - - /** - * Placeholder text for the input - * @default 'MM/DD/YYYY' for single date, 'MM/DD/YYYY - MM/DD/YYYY' for range - */ - placeholder?: string; - - /** - * Custom class name for the root container - */ - className?: string; - - /** - * Whether to disable the portal for the calendar content - * @default false - */ - disablePortal?: boolean; - - /** - * Whether to hide the clear button - * @default false - */ - hideClearButton?: boolean; -}; - -/** - * @deprecated DsDateInput is deprecated. Use DsDatePicker or DsDateRangePicker instead. - * @see {@link ../ds-date-picker} for single date selection. - * @see {@link ../ds-date-range-picker} for date range selection. - */ -export type DsDateInputProps = DsDateInputBaseProps & DsDateInputRangeProps; diff --git a/packages/design-system/src/components/ds-date-input/ds-date-input.utils.ts b/packages/design-system/src/components/ds-date-input/ds-date-input.utils.ts deleted file mode 100644 index ca44e60bc..000000000 --- a/packages/design-system/src/components/ds-date-input/ds-date-input.utils.ts +++ /dev/null @@ -1,256 +0,0 @@ -import { type DateValue } from '@ark-ui/react/date-picker'; -import { CalendarDate, parseDate } from '@internationalized/date'; - -/** - * Validation result for date input - */ -interface DateInputValidation { - isValid: boolean; - error?: string; -} - -/** - * Parsed date range result - */ -interface ParsedDateRange { - start: string | null; - end: string | null; - isValid: boolean; -} - -/** - * Convert ISO date string to DateValue - */ -export const isoStringToDateValue = (isoDate: string): DateValue | undefined => { - try { - return parseDate(isoDate) as unknown as DateValue; - } catch { - return undefined; - } -}; - -/** - * Convert string or tuple to DateValue array (for internal use with Ark UI) - */ -export const stringToDateValue = (dates: string | [string, string]): DateValue[] => { - const dateArray = Array.isArray(dates) ? dates : [dates]; - - return dateArray.map(isoStringToDateValue).filter((d) => !!d); -}; - -/** - * Convert DateValue array to string or tuple (for public API) - */ -export const dateValuesToStrings = (dates: DateValue[], range: boolean): string | [string, string] => { - const values = dates.map((date) => date.toString()); - - if (range) { - return [values[0] ?? '', values[1] ?? '']; - } - - return values[0] ?? ''; -}; - -/** - * Format a DateValue to string (MM/DD/YYYY) - */ -const formatDateValue = (date: DateValue): string => { - const paddedMonth = String(date.month).padStart(2, '0'); - const paddedDay = String(date.day).padStart(2, '0'); - - return `${paddedMonth}/${paddedDay}/${String(date.year)}`; -}; - -/** - * Format an array of DateValues to a unified string - * Single: "12/25/2024" - * Range: "12/01/2024 - 12/31/2024" - */ -export const formatDateInputValue = (value: DateValue[], range: boolean): string => { - if (value.length === 0) { - return ''; - } - const newValue = value.map(formatDateValue); - - return range ? newValue.join(' - ') : (newValue[0] ?? ''); -}; - -/** - * Parse a date string (MM/DD/YYYY) to DateValue - */ -const parseDateString = (dateStr: string): DateValue | null => { - if (!dateStr || !dateStr.trim()) { - return null; - } - - // Try MM/DD/YYYY format - const match = dateStr.trim().match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/); - if (!match) { - return null; - } - - const [, month, day, year] = match; - - if (!month || !day || !year) { - return null; - } - - const monthNum = parseInt(month, 10); - const dayNum = parseInt(day, 10); - const yearNum = parseInt(year, 10); - - // Use native Date for proper validation (handles leap years, month lengths, etc.) - // Note: Date months are 0-indexed, so subtract 1 - const date = new Date(yearNum, monthNum - 1, dayNum); - - // Check if the date is valid and matches what we parsed - // (e.g., 02/31/2024 would roll over to 03/02/2024, so we detect that) - if (date.getFullYear() !== yearNum || date.getMonth() !== monthNum - 1 || date.getDate() !== dayNum) { - return null; - } - - return new CalendarDate(yearNum, monthNum, dayNum) as unknown as DateValue; -}; - -/** - * Parse a range input string (MM/DD/YYYY - MM/DD/YYYY) - */ -const parseRangeInput = (text: string): ParsedDateRange => { - if (!text.trim()) { - return { start: null, end: null, isValid: true }; // Empty is valid - } - - // Check if it contains a dash separator - if (!text.includes('-')) { - // Single date format, treat as incomplete range - const date = parseDateString(text.trim()); - return { - start: date ? formatDateValue(date) : null, - end: null, - isValid: false, // Incomplete range - }; - } - - // Split by dash - const parts = text.split('-').map((p) => p.trim()); - if (parts.length !== 2) { - return { start: null, end: null, isValid: false }; - } - - const [startStr, endStr] = parts; - - if (!startStr || !endStr) { - return { start: null, end: null, isValid: false }; - } - - const startDate = parseDateString(startStr); - const endDate = parseDateString(endStr); - - // Both must be valid dates - if (!startDate || !endDate) { - return { - start: startDate ? formatDateValue(startDate) : null, - end: endDate ? formatDateValue(endDate) : null, - isValid: false, - }; - } - - return { - start: formatDateValue(startDate), - end: formatDateValue(endDate), - isValid: true, - }; -}; - -/** - * Parse input text based on range flag - */ -export const parseInputText = (text: string, range: boolean): DateValue[] => { - if (!range) { - const date = parseDateString(text); - return date ? [date] : []; - } - - // Range mode - const parsed = parseRangeInput(text); - if (!parsed.isValid || !parsed.start || !parsed.end) { - return []; - } - - const startDate = parseDateString(parsed.start); - const endDate = parseDateString(parsed.end); - - if (!startDate || !endDate) { - return []; - } - - return [startDate, endDate]; -}; - -/** - * Validate date input - */ -export const validateDateString = ({ - text, - range, - min, - max, -}: { - text: string; - range: boolean; - min?: DateValue; - max?: DateValue; -}): DateInputValidation => { - if (!text.trim()) { - return { isValid: true }; // Empty is valid (let required validation handle it) - } - - if (!range) { - const date = parseDateString(text); - if (!date) { - return { isValid: false, error: 'Invalid date format. Use MM/DD/YYYY' }; - } - - // Check min/max - if (min && date.compare(min) < 0) { - return { isValid: false, error: 'Date is before minimum allowed date' }; - } - if (max && date.compare(max) > 0) { - return { isValid: false, error: 'Date is after maximum allowed date' }; - } - - return { isValid: true }; - } - - // Range mode validation - const parsed = parseRangeInput(text); - if (!parsed.isValid) { - return { isValid: false, error: 'Invalid date range format. Use MM/DD/YYYY - MM/DD/YYYY' }; - } - - if (!parsed.start || !parsed.end) { - return { isValid: false, error: 'Both start and end dates are required' }; - } - - const startDate = parseDateString(parsed.start); - const endDate = parseDateString(parsed.end); - - if (!startDate || !endDate) { - return { isValid: false, error: 'Invalid date format. Use MM/DD/YYYY' }; - } - - // Validate start <= end - if (startDate.compare(endDate) > 0) { - return { isValid: false, error: 'Start date must be before or equal to end date' }; - } - - // Check min/max - if (min && startDate.compare(min) < 0) { - return { isValid: false, error: 'Start date is before minimum allowed date' }; - } - if (max && endDate.compare(max) > 0) { - return { isValid: false, error: 'End date is after maximum allowed date' }; - } - - return { isValid: true }; -}; diff --git a/packages/design-system/src/components/ds-date-input/index.ts b/packages/design-system/src/components/ds-date-input/index.ts deleted file mode 100644 index 12b09c0c5..000000000 --- a/packages/design-system/src/components/ds-date-input/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * @deprecated DsDateInput is deprecated. Use DsDatePicker or DsDateRangePicker instead. - * @see {@link ../ds-date-picker} for single date selection. - * @see {@link ../ds-date-range-picker} for date range selection. - */ -export { default as DsDateInput } from './ds-date-input'; -/** - * @deprecated These types are deprecated. Use DsDatePicker or DsDateRangePicker types instead. - * @see {@link ../ds-date-picker} for single date selection. - * @see {@link ../ds-date-range-picker} for date range selection. - */ -export type { DsDateInputProps } from './ds-date-input.types'; diff --git a/packages/design-system/src/components/ds-date-picker/ds-date-picker.tsx b/packages/design-system/src/components/ds-date-picker/ds-date-picker.tsx index 712806c60..0ee16bab4 100644 --- a/packages/design-system/src/components/ds-date-picker/ds-date-picker.tsx +++ b/packages/design-system/src/components/ds-date-picker/ds-date-picker.tsx @@ -11,7 +11,6 @@ import { validateDateString, } from './ds-date-picker.utils'; import { DsTimePicker } from '../ds-time-picker'; -import { DsIcon } from '../ds-icon'; import styles from './ds-date-picker.module.scss'; import { useControlled } from '../../utils/use-controlled'; import { DsTextInput } from '../ds-text-input'; @@ -148,27 +147,23 @@ const DsDatePicker = ({ {showClearButton && ( setValue(null)}> - - + /> )} - - + /> ), diff --git a/packages/design-system/src/components/ds-date-range-picker/ds-date-range-picker.tsx b/packages/design-system/src/components/ds-date-range-picker/ds-date-range-picker.tsx index c5c52e27c..cf1022bea 100644 --- a/packages/design-system/src/components/ds-date-range-picker/ds-date-range-picker.tsx +++ b/packages/design-system/src/components/ds-date-range-picker/ds-date-range-picker.tsx @@ -89,7 +89,7 @@ const DsDateRangePicker = ({ {showClearAll && ( - + {locale?.clearAllLabel ?? 'Clear all'} diff --git a/packages/design-system/src/components/ds-dialog/ds-dialog.module.scss b/packages/design-system/src/components/ds-dialog/ds-dialog.module.scss deleted file mode 100644 index 811e3ae3f..000000000 --- a/packages/design-system/src/components/ds-dialog/ds-dialog.module.scss +++ /dev/null @@ -1,50 +0,0 @@ -@use '../../styles/typography'; -@use '../../styles/mixins/bounding-box'; - -.overlay { - position: fixed; - inset: 0; - background: rgba(0, 0, 0, 0.32); - z-index: 1199; -} - -.dialog { - @include bounding-box.bounding-box-vars; - - --_shadow-size: 18px; - --_shadow-y-offset: 6px; - - background: var(--color-dap-gray-050, #fff); - border-radius: 8px; - box-shadow: 0 var(--_shadow-y-offset) var(--_shadow-size) 0 rgba(4, 69, 204, 0.3); - padding: 12px; - min-width: 320px; - min-height: 120px; - z-index: 1200; - position: fixed; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - max-width: 90vw; - max-height: 90vh; - display: flex; - flex-direction: column; -} - -.title { - @include typography.p-base; - @include typography.medium; - color: var(--color-dap-gray-700); -} - -.description { - @include typography.p-base; - color: var(--color-dap-gray-500); - margin-bottom: 16px; -} - -.customPlacement { - transform: none !important; - max-width: none; - max-height: none; -} diff --git a/packages/design-system/src/components/ds-dialog/ds-dialog.stories.module.scss b/packages/design-system/src/components/ds-dialog/ds-dialog.stories.module.scss deleted file mode 100644 index c823ab33c..000000000 --- a/packages/design-system/src/components/ds-dialog/ds-dialog.stories.module.scss +++ /dev/null @@ -1,15 +0,0 @@ -@use '../../styles/typography'; - -.dialogContent { - padding: 20px; - min-width: 300px; -} - -.menuIcon { - cursor: pointer; - position: fixed; - top: 20px; - left: 20px; - z-index: 1; - color: var(--color-dap-blue-600); -} diff --git a/packages/design-system/src/components/ds-dialog/ds-dialog.stories.tsx b/packages/design-system/src/components/ds-dialog/ds-dialog.stories.tsx deleted file mode 100644 index 70c4917cf..000000000 --- a/packages/design-system/src/components/ds-dialog/ds-dialog.stories.tsx +++ /dev/null @@ -1,125 +0,0 @@ -import React from 'react'; -import type { Meta, StoryObj } from '@storybook/react-vite'; -import { expect, screen, userEvent, waitFor, within } from 'storybook/test'; -import { DsIcon } from '../ds-icon'; -import { DsButton } from '../ds-button'; -import { DsDialog } from '../ds-dialog'; -import styles from './ds-dialog.stories.module.scss'; - -const meta: Meta = { - title: 'Components/Dialog', - component: DsDialog, - parameters: { - layout: 'centered', - }, - argTypes: { - open: { - control: 'boolean', - description: 'Controls whether the dialog is open', - }, - onOpenChange: { - action: 'onOpenChange', - description: 'Function called when dialog open state changes', - }, - title: { - control: 'text', - description: 'Title of the dialog', - }, - description: { - control: 'text', - description: 'Description text for the dialog', - }, - hideTitle: { - control: 'boolean', - description: 'Whether to hide the title visually', - }, - hideDescription: { - control: 'boolean', - description: 'Whether to hide the description visually', - }, - modal: { - control: 'boolean', - description: 'Whether the dialog should be modal', - }, - customPosition: { - control: 'object', - description: - 'Custom position for the dialog in pixels relative to the viewport. Expects {top: number, left: number}', - }, - }, -}; - -export default meta; -type Story = StoryObj; - -export const Centered: Story = { - render: function Render(args) { - const [open, setOpen] = React.useState(false); - return ( - <> - setOpen(true)}>Open Dialog - -
This is a centered dialog example
-
- - ); - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const trigger = canvas.getByText('Open Dialog'); - await userEvent.click(trigger); - // Verify dialog is opened - await waitFor(() => { - return expect(screen.getByText(/Centered Dialog/)).toBeTruthy(); - }); - // Close dialog with Escape key - await userEvent.keyboard('{Escape}'); - // Verify dialog is closed - await waitFor(() => { - return expect(screen.queryByText(/Centered Dialog/)).toBeNull(); - }); - }, -}; - -export const CustomPosition: Story = { - render: function Render(args) { - const [open, setOpen] = React.useState(false); - - return ( - <> - setOpen(true)} /> - -
This is a custom positioned dialog example
-
- - ); - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const trigger = canvas.getByText(/menu/i); - await userEvent.click(trigger); - // Verify dialog is opened - await waitFor(() => { - return expect(screen.getByText(/Custom Position Dialog/)).toBeTruthy(); - }); - // Close dialog with Escape key - await userEvent.keyboard('{Escape}'); - // Verify dialog is closed - await waitFor(() => { - return expect(screen.queryByText(/Custom Position Dialog/)).toBeNull(); - }); - }, -}; diff --git a/packages/design-system/src/components/ds-dialog/ds-dialog.tsx b/packages/design-system/src/components/ds-dialog/ds-dialog.tsx deleted file mode 100644 index 05fd477fc..000000000 --- a/packages/design-system/src/components/ds-dialog/ds-dialog.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import type React from 'react'; -import * as RadixDialog from '@radix-ui/react-dialog'; -import { Root as VisuallyHidden } from '@radix-ui/react-visually-hidden'; -import classNames from 'classnames'; -import styles from './ds-dialog.module.scss'; -import type { DsDialogProps } from './ds-dialog.types'; - -const DsDialog: React.FC = ({ - open, - onOpenChange, - title, - hideTitle, - description, - hideDescription, - children, - className, - anchorRef, - customPosition, - modal = true, -}) => { - let style: React.CSSProperties = {}; - if (customPosition) { - style = { position: 'fixed', ...customPosition }; - } else if (anchorRef?.current) { - const rect = anchorRef.current.getBoundingClientRect(); - style = { position: 'fixed', top: rect.bottom, left: rect.left }; - } - - return ( - - - {modal && } - - - {hideTitle ? ( - - {title} - - ) : ( - {title} - )} - - {description && ( - - {hideDescription ? ( - - - {description} - - - ) : ( - - {description} - - )} - - )} - {children} - - - - ); -}; - -export default DsDialog; diff --git a/packages/design-system/src/components/ds-dialog/ds-dialog.types.ts b/packages/design-system/src/components/ds-dialog/ds-dialog.types.ts deleted file mode 100644 index 9a42a6313..000000000 --- a/packages/design-system/src/components/ds-dialog/ds-dialog.types.ts +++ /dev/null @@ -1,57 +0,0 @@ -import type React from 'react'; - -export interface DsDialogProps { - /** - * Controls whether the dialog is open. - */ - open: boolean; - /** - * Callback fired when the open state should change. - */ - onOpenChange?: (open: boolean) => void; - /** - * The accessible title for the dialog. Required for accessibility. - */ - title: string; - /** - * If true, the dialog title will be visually hidden but accessible to screen readers. - */ - hideTitle?: boolean; - /** - * The accessible description for the dialog. Optional, but recommended for accessibility. - */ - description?: string; - /** - * If true, the dialog description will be visually hidden but accessible to screen readers. - */ - hideDescription?: boolean; - /** - * The content to render inside the dialog. - */ - children: React.ReactNode; - /** - * Additional CSS class names for the dialog container. - */ - className?: string; - /** - * Ref to the element the dialog should be anchored to (for relative placement) - */ - anchorRef?: React.RefObject; - /** - * Custom fixed position (e.g., { top: number, left: number }) - */ - customPosition?: { - /** - * Distance in pixels from the top of the viewport. - */ - top: number; - /** - * Distance in pixels from the left of the viewport. - */ - left: number; - }; - /** - * If true, show modal overlay and center dialog (default: true) - */ - modal?: boolean; -} diff --git a/packages/design-system/src/components/ds-dialog/index.ts b/packages/design-system/src/components/ds-dialog/index.ts deleted file mode 100644 index cfebd91ab..000000000 --- a/packages/design-system/src/components/ds-dialog/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default as DsDialog } from './ds-dialog'; -export * from './ds-dialog.types'; diff --git a/packages/design-system/src/components/ds-drawer/ds-drawer.stories.tsx b/packages/design-system/src/components/ds-drawer/ds-drawer.stories.tsx index 71b75b574..166c93139 100644 --- a/packages/design-system/src/components/ds-drawer/ds-drawer.stories.tsx +++ b/packages/design-system/src/components/ds-drawer/ds-drawer.stories.tsx @@ -5,8 +5,7 @@ import DsDrawer from './ds-drawer'; import { DsButton } from '../ds-button'; import { DsTextInput } from '../ds-text-input'; import { DsIcon } from '../ds-icon'; -// TODO: Use DsStatusBadge instead. -import { DsSystemStatus } from '../ds-system-status'; +import { DsStatusBadge } from '../ds-status-badge'; import styles from './ds-drawer.stories.module.scss'; import { DsTypography } from '../ds-typography'; import type { DsDrawerColumns, DsDrawerProps } from './ds-drawer.types'; @@ -82,7 +81,7 @@ export const Default: Story = { <> - Default Drawer + Default Drawer
- ), - }, - }, - render: (args) => ( - - - - - - - ), - play: async ({ canvasElement }) => { - await sanityCheckSingle(canvasElement); - }, -}; - -export const Success: Story = { - render: function Render() { - const [value] = useState('2024-12-25'); - - return ( -
- - - - - - -
- ); - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const input = canvas.getByPlaceholderText('MM/DD/YYYY'); - await waitFor(async () => { - await expect(input).toHaveValue('12/25/2024'); - }); - }, -}; - -export const Error: Story = { - args: { - status: 'error', - label: 'Event Date', - required: true, - message: 'Date is required.', - messageIcon: 'error', - style: { width: '400px' }, - }, - render: (args) => ( - - - - - - - ), -}; - -export const Warning: Story = { - render: function Render() { - const [value] = useState('2024-12-25'); - - return ( -
- - - - - - -
- ); - }, -}; - -export const Disabled: Story = { - args: { - label: 'Event Date', - style: { width: '400px' }, - }, - render: (args) => ( - - - - - - - ), - play: async ({ canvasElement }) => { - await checkDisabled(canvasElement); - }, -}; - -export const RangeMode: Story = { - args: { - label: 'Date Range', - required: true, - message: 'Select start and end dates', - style: { width: '400px' }, - }, - render: (args) => ( - - - - ), - play: async ({ canvasElement }) => { - await sanityCheckRange(canvasElement); - }, -}; - -export const RangeWithValidation: Story = { - render: function Render() { - const [value, setValue] = useState<[string, string]>(); - const [touched, setTouched] = useState(false); - const error = touched && !value ? 'Start and end dates are required' : undefined; - - return ( -
- - { - setValue(value); - setTouched(true); - }} - range - /> - -
- ); - }, -}; - -export const WithValidation: Story = { - render: function Render() { - const [value, setValue] = useState(); - const [touched, setTouched] = useState(false); - const error = touched && !value ? 'Date is required' : undefined; - - return ( -
- - { - setValue(value); - setTouched(true); - }} - /> - -
- ); - }, -}; diff --git a/packages/design-system/src/components/ds-grid/ds-grid.stories.tsx b/packages/design-system/src/components/ds-grid/ds-grid.stories.tsx index 874a592fc..5405c7363 100644 --- a/packages/design-system/src/components/ds-grid/ds-grid.stories.tsx +++ b/packages/design-system/src/components/ds-grid/ds-grid.stories.tsx @@ -113,7 +113,7 @@ export const NavigationLayout: Story = {
- setIsCollapsed(!isCollapsed)}> + setIsCollapsed(!isCollapsed)}> Toggle
diff --git a/packages/design-system/src/components/ds-modal/ds-modal.stories.tsx b/packages/design-system/src/components/ds-modal/ds-modal.stories.tsx index c0d9df68c..5ef57538e 100644 --- a/packages/design-system/src/components/ds-modal/ds-modal.stories.tsx +++ b/packages/design-system/src/components/ds-modal/ds-modal.stories.tsx @@ -83,7 +83,7 @@ export const Default: Story = {

Default Modal

The default variant has an inset header underline and no footer border.

- setIsOpen(true)}> + setIsOpen(true)}> Open Modal
@@ -98,10 +98,10 @@ export const Default: Story = { - setIsOpen(false)}> + setIsOpen(false)}> Cancel - setIsOpen(false)}> + setIsOpen(false)}> Confirm @@ -186,7 +186,7 @@ export const Divided: Story = { Click the button below to open a form modal. Fill out the form and click "Save Changes" to see the results displayed here.

- setIsOpen(true)}> + setIsOpen(true)}> Open Form Modal
@@ -218,8 +218,7 @@ export const Divided: Story = {
setSubmittedData(null)} className={styles.clearResultsButton} @@ -401,11 +400,11 @@ export const Divided: Story = { - + Reset

Custom Modal Demo

Click the button below to open a custom modal with custom header and footer content.

- setIsOpen(true)}> + setIsOpen(true)}> Open Custom Modal
@@ -782,7 +781,7 @@ export const WithIcon: Story = {

Modal with Icon

Example showing how to add an icon to the modal header.

- setIsOpen(true)}> + setIsOpen(true)}> Open Modal
@@ -838,7 +837,7 @@ export const WithoutHeader: Story = {

Modal Without Header

Modal with only body and footer content, no header section.

- setIsOpen(true)}> + setIsOpen(true)}> Open Modal
@@ -852,10 +851,10 @@ export const WithoutHeader: Story = { - setIsOpen(false)}> + setIsOpen(false)}> Close - setIsOpen(false)}> + setIsOpen(false)}> Continue diff --git a/packages/design-system/src/components/ds-panel/__tests__/ds-panel.browser.test.tsx b/packages/design-system/src/components/ds-panel/__tests__/ds-panel.browser.test.tsx index b5e35da19..a760da8de 100644 --- a/packages/design-system/src/components/ds-panel/__tests__/ds-panel.browser.test.tsx +++ b/packages/design-system/src/components/ds-panel/__tests__/ds-panel.browser.test.tsx @@ -3,14 +3,14 @@ import { describe, expect, it } from 'vitest'; import { page } from 'vitest/browser'; import { DsPanel } from '../'; -import { DsButtonV3 } from '../../ds-button-v3'; +import { DsButton } from '../../ds-button'; function CollapsiblePanel({ variant }: { variant?: 'docked' | 'floating' }) { const [open, setOpen] = useState(true); return ( <> - {!open && setOpen(true)}>Open Panel} + {!open && setOpen(true)}>Open Panel}

This is a panel

diff --git a/packages/design-system/src/components/ds-panel/ds-panel.stories.tsx b/packages/design-system/src/components/ds-panel/ds-panel.stories.tsx index b27f3f8c7..2a4c71b66 100644 --- a/packages/design-system/src/components/ds-panel/ds-panel.stories.tsx +++ b/packages/design-system/src/components/ds-panel/ds-panel.stories.tsx @@ -1,6 +1,6 @@ import type { Meta, StoryObj } from '@storybook/react-vite'; import { DsPanel } from './'; -import { DsButton } from '../ds-button/'; +import { DsButton } from '../ds-button'; import { DsStepper, DsStep, DsStepContent, DsNextStepButton } from '../ds-stepper'; import { useState } from 'react'; import type { DsPanelVariant } from './ds-panel.types'; diff --git a/packages/design-system/src/components/ds-radio-group/ds-radio-group.tsx b/packages/design-system/src/components/ds-radio-group/ds-radio-group.tsx index 85e199122..0658bfeee 100644 --- a/packages/design-system/src/components/ds-radio-group/ds-radio-group.tsx +++ b/packages/design-system/src/components/ds-radio-group/ds-radio-group.tsx @@ -3,11 +3,7 @@ import { RadioGroup } from '@ark-ui/react/radio-group'; import classNames from 'classnames'; import { DsTypography } from '../ds-typography'; import styles from './ds-radio-group.module.scss'; -import type { - DsRadioGroupItemProps, - DsRadioGroupLegacyProps, - DsRadioGroupRootProps, -} from './ds-radio-group.types'; +import type { DsRadioGroupItemProps, DsRadioGroupRootProps } from './ds-radio-group.types'; /** * Root component - provides radio group context @@ -57,58 +53,6 @@ const Item: React.FC = ({ ); }; -/** - * DEPRECATED: Legacy DsRadioGroup component with options array - * Use compound component pattern instead: DsRadioGroup.Root + DsRadioGroup.Item - * @deprecated - */ -/* c8 ignore start */ -export const DsRadioGroupLegacy: React.FC = ({ - options, - value, - defaultValue, - onValueChange, - disabled, - className, - style, -}) => ( - { - if (details.value !== null) { - onValueChange?.(details.value); - } - }} - > - {options.map((option) => ( - - -
-
-
- - - {option.label} - {!!option.labelInfo && ( - - {option.labelInfo} - - )} - - ))} - -); -/* c8 ignore stop */ - /** * Design system RadioGroup component * diff --git a/packages/design-system/src/components/ds-radio-group/ds-radio-group.types.ts b/packages/design-system/src/components/ds-radio-group/ds-radio-group.types.ts index 26473524a..55c1b1cdd 100644 --- a/packages/design-system/src/components/ds-radio-group/ds-radio-group.types.ts +++ b/packages/design-system/src/components/ds-radio-group/ds-radio-group.types.ts @@ -1,4 +1,3 @@ -import type React from 'react'; import { type RadioGroupItemProps, type RadioGroupRootProps } from '@ark-ui/react/radio-group'; /** @@ -62,63 +61,3 @@ export interface DsRadioGroupItemProps extends Pick< */ labelInfo?: string; } - -/** - * DEPRECATED: Legacy radio option configuration - * Use compound component pattern instead: - * @deprecated - */ -export interface DsRadioOptionLegacy { - /** - * Display label for the radio option - */ - label: string; - /** - * Value associated with this radio option - */ - value: ValueType; - /** - * Additional label info for the radio option - */ - labelInfo?: string; - /** - * Whether this radio option is disabled - */ - disabled?: boolean; -} - -/** - * DEPRECATED: Legacy props for DsRadioGroupLegacy component - * Use compound component pattern instead - * @deprecated - */ -export interface DsRadioGroupLegacyProps { - /** - * The radio group options - */ - options: TOption[]; - /** - * The selected value - */ - value?: TOption['value']; - /** - * The default selected value - */ - defaultValue?: TOption['value']; - /** - * Event handler called when the selected value changes - */ - onValueChange?: (value: TOption['value']) => void; - /** - * Whether the entire radio group is disabled. - */ - disabled?: boolean; - /** - * Additional CSS class names - */ - className?: string; - /** - * Additional inline styles - */ - style?: React.CSSProperties; -} diff --git a/packages/design-system/src/components/ds-radio-group/index.ts b/packages/design-system/src/components/ds-radio-group/index.ts index e531d2179..44a3f31c4 100644 --- a/packages/design-system/src/components/ds-radio-group/index.ts +++ b/packages/design-system/src/components/ds-radio-group/index.ts @@ -1,2 +1,2 @@ -export { DsRadioGroup, DsRadioGroupLegacy } from './ds-radio-group'; +export { DsRadioGroup } from './ds-radio-group'; export * from './ds-radio-group.types'; diff --git a/packages/design-system/src/components/ds-select/__tests__/ds-select.browser.test.tsx b/packages/design-system/src/components/ds-select/__tests__/ds-select.browser.test.tsx index e4321effe..bb6025063 100644 --- a/packages/design-system/src/components/ds-select/__tests__/ds-select.browser.test.tsx +++ b/packages/design-system/src/components/ds-select/__tests__/ds-select.browser.test.tsx @@ -191,7 +191,7 @@ describe('DsSelect', () => { await expect.element(trigger).toHaveTextContent(PLACEHOLDER); }); - it('should select all, expand +N chip, delete chip, and clear all (MultiSelect story)', async () => { + it('should select all, expand +N tag, delete tag, and clear all (MultiSelect story)', async () => { await page.render(); const trigger = page.getByRole('combobox'); @@ -200,12 +200,12 @@ describe('DsSelect', () => { await page.getByRole('option', { name: 'All' }).click(); - const expandChip = page.getByRole('button', { name: /^\+\d+$/ }); - await expandChip.click(); + const expandTag = page.getByRole('button', { name: /^\+\d+$/ }); + await expandTag.click(); for (const option of mockOptions) { - const chip = page.getByRole('button', { name: exactAriaName(option.label) }); - await expect.element(chip).toBeInTheDocument(); + const tag = page.getByRole('button', { name: exactAriaName(option.label) }); + await expect.element(tag).toBeInTheDocument(); } await trigger.click(); @@ -217,11 +217,11 @@ describe('DsSelect', () => { await trigger.click(); const firstOption = mockOptions[0] as DsSelectOption; - const firstOptionChip = page.getByRole('button', { name: exactAriaName(firstOption.label) }); - const deleteButton = firstOptionChip.getByRole('button', { name: 'Delete chip' }); + const firstOptionTag = page.getByRole('button', { name: exactAriaName(firstOption.label) }); + const deleteButton = firstOptionTag.getByRole('button', { name: 'Delete tag' }); await deleteButton.click(); - await expect.element(firstOptionChip).not.toBeInTheDocument(); + await expect.element(firstOptionTag).not.toBeInTheDocument(); await page.getByRole('button', { name: 'Clear All' }).click(); @@ -299,7 +299,7 @@ describe('DsSelect', () => { await expect.element(trigger).toHaveTextContent('France'); }); - it('should render selected items as chips with custom option markup (CustomRenderOptionMultiSelect story)', async () => { + it('should render selected items as tags with custom option markup (CustomRenderOptionMultiSelect story)', async () => { await page.render( setShowAllItems(true)} diff --git a/packages/design-system/src/components/ds-select/ds-select.types.ts b/packages/design-system/src/components/ds-select/ds-select.types.ts index 8e40c24a5..5a8ecd5c3 100644 --- a/packages/design-system/src/components/ds-select/ds-select.types.ts +++ b/packages/design-system/src/components/ds-select/ds-select.types.ts @@ -60,7 +60,7 @@ export type DsSelectProps = { /** * Custom render function for dropdown options. * When provided, replaces the default label text inside each dropdown item. - * The string `label` is still used for search, trigger text, chips, and accessibility. + * The string `label` is still used for search, trigger text, tags, and accessibility. */ renderOption?: (option: DsSelectOption) => ReactNode; } & ( diff --git a/packages/design-system/src/components/ds-select/select-items-chips.tsx b/packages/design-system/src/components/ds-select/select-items-tags.tsx similarity index 66% rename from packages/design-system/src/components/ds-select/select-items-chips.tsx rename to packages/design-system/src/components/ds-select/select-items-tags.tsx index d0147d971..64aab1d53 100644 --- a/packages/design-system/src/components/ds-select/select-items-chips.tsx +++ b/packages/design-system/src/components/ds-select/select-items-tags.tsx @@ -1,18 +1,17 @@ import { useSelectContext, type UseSelectContext } from '@ark-ui/react/select'; import styles from './ds-select.module.scss'; import { DsButton } from '../ds-button'; -// TODO: Use DsTag instead. -import { DsChip } from '../ds-chip'; +import { DsTag } from '../ds-tag'; import type { DsSelectOption, SelectOptionValue } from './ds-select.types'; -type SelectItemsChipsProps = { +type SelectItemsTagsProps = { showAll: boolean; onValueChange?: (value: SelectOptionValue[]) => void; onShowAll: () => void; count: number; }; -export function SelectItemsChips({ showAll, onShowAll, onValueChange, count }: SelectItemsChipsProps) { +export function SelectItemsTags({ showAll, onShowAll, onValueChange, count }: SelectItemsTagsProps) { const { collection, value: selectedItems } = useSelectContext() as UseSelectContext; if (!selectedItems.length) { @@ -36,20 +35,14 @@ export function SelectItemsChips({ showAll, onShowAll, onValueChange, count }: S onValueChange?.(filteredValue); }; - return ; + return ; })} {!showAll && selectedItems.length > count && ( - + )} - onValueChange?.([])} - > + onValueChange?.([])}> Clear All
diff --git a/packages/design-system/src/components/ds-split-button/ds-split-button.tsx b/packages/design-system/src/components/ds-split-button/ds-split-button.tsx index c4b4dff53..ced088be1 100644 --- a/packages/design-system/src/components/ds-split-button/ds-split-button.tsx +++ b/packages/design-system/src/components/ds-split-button/ds-split-button.tsx @@ -2,7 +2,7 @@ import classNames from 'classnames'; import { DsSelect, type SelectSize } from '../ds-select'; import styles from './ds-split-button.module.scss'; import type { DsSplitButtonProps, SplitButtonSize } from './ds-split-button.types'; -import { DsButtonV3 } from '../ds-button-v3'; +import { DsButton } from '../ds-button'; const DsSplitButton = ({ ref, @@ -18,7 +18,7 @@ const DsSplitButton = ({ return (
- ; +type ButtonSlotProps = Omit; type SelectSlotProps = DistributiveOmit; diff --git a/packages/design-system/src/components/ds-stepper/components/ds-step-next-button.tsx b/packages/design-system/src/components/ds-stepper/components/ds-step-next-button.tsx index 62b0c9809..3e030370f 100644 --- a/packages/design-system/src/components/ds-stepper/components/ds-step-next-button.tsx +++ b/packages/design-system/src/components/ds-stepper/components/ds-step-next-button.tsx @@ -1,21 +1,19 @@ import { useStepper } from '../hooks/use-stepper'; -import { DsButton, type DsButtonUnifiedProps } from '../../ds-button'; - -// TODO: Use the new button props once we deprecate the legacy button. -type DsLegacyButtonProps = Extract & { - design?: undefined | 'legacy'; -}; +import { DsButton, type DsButtonProps } from '../../ds-button'; export type DsNextStepButtonProps = Pick< - DsLegacyButtonProps, + DsButtonProps, 'variant' | 'size' | 'className' | 'style' | 'children' >; export function DsNextStepButton({ children, size = 'small', ...rest }: DsNextStepButtonProps) { const context = useStepper(); + // strip color — ark returns `string` which conflicts with DsButton union + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { color, ...triggerProps } = context.stepsApi.getNextTriggerProps(); return ( - + {children} ); diff --git a/packages/design-system/src/components/ds-stepper/ds-stepper.stories.tsx b/packages/design-system/src/components/ds-stepper/ds-stepper.stories.tsx index 15838ce14..454334198 100644 --- a/packages/design-system/src/components/ds-stepper/ds-stepper.stories.tsx +++ b/packages/design-system/src/components/ds-stepper/ds-stepper.stories.tsx @@ -360,7 +360,7 @@ export const CustomizedHorizontal: Story = { activeStep={activeStep} onStepChange={({ step }) => setActiveStep(step)} actions={ - + {activeStep === customSteps.length - 1 ? 'Finish' : 'Continue'} } diff --git a/packages/design-system/src/components/ds-system-status/ds-system-status.module.scss b/packages/design-system/src/components/ds-system-status/ds-system-status.module.scss deleted file mode 100644 index 22f488ffa..000000000 --- a/packages/design-system/src/components/ds-system-status/ds-system-status.module.scss +++ /dev/null @@ -1,87 +0,0 @@ -@use '../../styles/typography'; - -.container { - @include typography.p-special; - @include typography.medium; - display: inline-flex; - align-items: center; - gap: 4px; - padding: 4px 8px; - border-radius: 4px; - width: max-content; - text-transform: uppercase; -} - -.dot { - width: 10px; - height: 10px; - border-radius: 50%; - border: 1px solid var(--color-dap-gray-050); -} - -.label { - @include typography.p-special; -} - -.healthy { - background: var(--color-dap-data-light-green); - color: var(--color-dap-data-green); - - .dot { - background: var(--color-dap-data-green); - } -} - -.neutral { - background: var(--color-dap-blue-200); - color: var(--color-dap-blue-600); - - .dot { - background: var(--color-dap-blue-600); - } -} - -.error { - background: var(--color-dap-red-100); - color: var(--background-error); - - .dot { - background: var(--background-error); - } -} - -.in-progress { - background: var(--schema-light-blue); - color: var(--color-dap-blue-800); - - .dot { - background: var(--color-dap-blue-800); - } -} - -.pending { - background: var(--background-pending); - color: var(--color-dap-data-deep-fuchsia); - - .dot { - background: var(--color-dap-data-deep-fuchsia); - } -} - -.alert { - background: var(--schema-light-amber); - color: var(--color-dap-orange-600); - - .dot { - background: var(--color-dap-orange-600); - } -} - -.disabled { - background: var(--color-dap-gray-200); - color: var(--color-dap-gray-600); - - .dot { - background: var(--color-dap-gray-600); - } -} diff --git a/packages/design-system/src/components/ds-system-status/ds-system-status.stories.ts b/packages/design-system/src/components/ds-system-status/ds-system-status.stories.ts deleted file mode 100644 index ae75fae09..000000000 --- a/packages/design-system/src/components/ds-system-status/ds-system-status.stories.ts +++ /dev/null @@ -1,48 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react-vite'; -import DsSystemStatus from './ds-system-status'; -import { systemStatuses } from './ds-system-status.types'; - -/** - * @deprecated This component is deprecated. Use `DsStatusBadge` instead. - * @see {@link ../ds-status-badge/ds-status-badge.stories} for examples of the replacement component. - */ -const meta: Meta = { - title: 'Components/SystemStatus (Deprecated)', - component: DsSystemStatus, - parameters: { - layout: 'centered', - docs: { - description: { - component: - '**Deprecated**: This component is deprecated. Please use `DsStatusBadge` instead. See the StatusBadge stories for the replacement component.', - }, - }, - }, - tags: ['deprecated'], - argTypes: { - status: { - control: { type: 'select' }, - options: systemStatuses, - }, - label: { - control: 'text', - description: 'Custom label text (optional)', - }, - }, -}; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - status: 'healthy', - }, -}; - -export const CustomLabel: Story = { - args: { - status: 'error', - label: 'Critical Error', - }, -}; diff --git a/packages/design-system/src/components/ds-system-status/ds-system-status.tsx b/packages/design-system/src/components/ds-system-status/ds-system-status.tsx deleted file mode 100644 index ec3e1ef40..000000000 --- a/packages/design-system/src/components/ds-system-status/ds-system-status.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import type React from 'react'; -import classNames from 'classnames'; -import styles from './ds-system-status.module.scss'; -import type { DsSystemStatusProps } from './ds-system-status.types'; - -/** - * @deprecated This component is deprecated. Use `DsStatusBadge` instead. - * @see {@link ../ds-status-badge/ds-status-badge} for the replacement component. - */ -const defaultLabels: Record = { - healthy: 'HEALTHY', - neutral: 'NEUTRAL', - error: 'ERROR', - 'in-progress': 'IN PROGRESS', - pending: 'PENDING', - alert: 'ALERT', - disabled: 'DISABLED', -}; - -/** - * @deprecated This component is deprecated. Use `DsStatusBadge` instead. - * @see {@link ../ds-status-badge/ds-status-badge} for the replacement component. - */ -const DsSystemStatus: React.FC = ({ status, label, className }) => { - return ( -
- - {label || defaultLabels[status]} -
- ); -}; - -export default DsSystemStatus; diff --git a/packages/design-system/src/components/ds-system-status/ds-system-status.types.ts b/packages/design-system/src/components/ds-system-status/ds-system-status.types.ts deleted file mode 100644 index dd6db780b..000000000 --- a/packages/design-system/src/components/ds-system-status/ds-system-status.types.ts +++ /dev/null @@ -1,37 +0,0 @@ -/** - * @deprecated This type is deprecated. Use `DsStatusBadge` and its related types instead. - * @see {@link ../ds-status-badge/ds-status-badge} for the replacement component. - */ -export const systemStatuses = [ - 'healthy', - 'neutral', - 'error', - 'in-progress', - 'pending', - 'alert', - 'disabled', -] as const; - -/** - * @deprecated This type is deprecated. Use `DsStatus` from `ds-status-badge` instead. - */ -export type SystemStatus = (typeof systemStatuses)[number]; - -/** - * @deprecated This interface is deprecated. Use `DsStatusBadgeProps` from `ds-status-badge` instead. - * @see {@link ../ds-status-badge/ds-status-badge.types} for the replacement interface. - */ -export interface DsSystemStatusProps { - /** - * The status of the system - */ - status: SystemStatus; - /** - * The label to be displayed - */ - label?: string; - /** - * Additional CSS class names - */ - className?: string; -} diff --git a/packages/design-system/src/components/ds-system-status/index.ts b/packages/design-system/src/components/ds-system-status/index.ts deleted file mode 100644 index ebe6c6671..000000000 --- a/packages/design-system/src/components/ds-system-status/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * @deprecated DsSystemStatus is deprecated. Use DsStatusBadge instead. - * @see {@link ../ds-status-badge} for the replacement component. - */ -export { default as DsSystemStatus } from './ds-system-status'; -/** - * @deprecated These types are deprecated. Use DsStatusBadge types instead. - * @see {@link ../ds-status-badge} for the replacement types. - */ -export * from './ds-system-status.types'; diff --git a/packages/design-system/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.module.scss b/packages/design-system/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.module.scss index 3566de565..2c77d66e7 100644 --- a/packages/design-system/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.module.scss +++ b/packages/design-system/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.module.scss @@ -1,17 +1,18 @@ @use '../../../../styles/typography'; +$border-radius: 8px; + .bulkActionsContainer { position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); background-color: var(--color-dap-gray-050); - border-radius: 8px; + border-radius: $border-radius; box-shadow: 0 4px 12px #0445cc66; z-index: 1000; display: flex; align-items: center; - border-radius: 8px; overflow: hidden; animation-duration: 0.3s; animation-timing-function: ease-out; @@ -29,8 +30,6 @@ .bulkActionsContent { display: flex; align-items: center; - padding-right: 6px; - padding-left: 6px; } .selectedCountContainer { @@ -52,40 +51,60 @@ padding-right: 56px; } +.actionButton { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: var(--3xs); + width: 68px; + height: 48px; + border-radius: 0; +} + .bulkActionsActions { display: flex; align-items: center; - gap: 8px; > .actionButton { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: var(--3xs); - border-right: 1px solid var(--color-dap-gray-400); - width: 68px; - height: 48px; - border-radius: 0; - } + position: relative; + + &::after { + content: ''; + position: absolute; + top: 0; + right: 0; + width: 1px; + height: 100%; + background-color: var(--color-dap-gray-400); + } - > .actionButton:first-child { - border-left: 1px solid var(--color-dap-gray-400); + &:first-child::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 1px; + height: 100%; + background-color: var(--color-dap-gray-400); + } } } -.escapeButtonContent { +.targetButton.escapeButtonContent { display: flex; flex-direction: column; justify-content: center; align-items: center; color: var(--color-dap-gray-600); - gap: 2px !important; - width: 36px; padding-left: 4px; + margin-right: 2px; + background: none; + border-radius: 0 $border-radius $border-radius 0; &:hover { color: var(--color-dap-gray-500); + background: inherit; } } diff --git a/packages/design-system/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.tsx b/packages/design-system/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.tsx index 297000935..86a5b9560 100644 --- a/packages/design-system/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.tsx +++ b/packages/design-system/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.tsx @@ -43,22 +43,25 @@ const DsTableBulkActions: React.FC = ({ numSelectedRows, actio
{actions.map((action, index) => ( - + ))}
- - + slotProps={{ + icon: { + size: 'medium', + }, + }} + />
); diff --git a/packages/design-system/src/components/ds-table/components/ds-table-row-expandable-cell/ds-table-row-expandable-cell.tsx b/packages/design-system/src/components/ds-table/components/ds-table-row-expandable-cell/ds-table-row-expandable-cell.tsx index a49431662..2ccbda3d7 100644 --- a/packages/design-system/src/components/ds-table/components/ds-table-row-expandable-cell/ds-table-row-expandable-cell.tsx +++ b/packages/design-system/src/components/ds-table/components/ds-table-row-expandable-cell/ds-table-row-expandable-cell.tsx @@ -21,8 +21,7 @@ export const DsTableRowExpandableCell = ({ {isExpandable && ( { diff --git a/packages/design-system/src/components/ds-table/filters/adapters/create-checkbox-filter-adapter.tsx b/packages/design-system/src/components/ds-table/filters/adapters/create-checkbox-filter-adapter.tsx index 4b9f69357..4bad354cb 100644 --- a/packages/design-system/src/components/ds-table/filters/adapters/create-checkbox-filter-adapter.tsx +++ b/packages/design-system/src/components/ds-table/filters/adapters/create-checkbox-filter-adapter.tsx @@ -26,10 +26,10 @@ export interface CheckboxFilterAdapterConfig { renderer?: (item: CheckboxFilterItem) => ReactNode; /** - * Optional custom chip label generator + * Optional custom tag label generator * @default (item) => `${label}: ${item.label}` */ - chipLabelTemplate?: (item: CheckboxFilterItem) => string; + tagLabelTemplate?: (item: CheckboxFilterItem) => string; /** * Optional custom cell renderer for table column @@ -58,7 +58,7 @@ export function createCheckboxFilterAdapter( label, items, renderer, - chipLabelTemplate = (item) => `${label}: ${item.label}`, + tagLabelTemplate = (item) => `${label}: ${item.label}`, cellRenderer, getRowValue = (row) => row.getValue(id), } = config; @@ -81,11 +81,10 @@ export function createCheckboxFilterAdapter( cellRenderer, - toChips: (selectedItems) => { - // Generate chips for all selected items + toTags: (selectedItems) => { return selectedItems.map((item) => ({ id: `${id}_${String(item.value)}`, - label: chipLabelTemplate(item), + label: tagLabelTemplate(item), metadata: { key: id, value: item.value, @@ -93,8 +92,8 @@ export function createCheckboxFilterAdapter( })); }, - fromChip: (chip, currentValue) => { - return currentValue.filter((item) => item.value !== chip.metadata?.value); + fromTag: (tag, currentValue) => { + return currentValue.filter((item) => item.value !== tag.metadata?.value); }, getActiveFiltersCount: (selectedItems) => { diff --git a/packages/design-system/src/components/ds-table/filters/adapters/create-dual-range-filter-adapter.tsx b/packages/design-system/src/components/ds-table/filters/adapters/create-dual-range-filter-adapter.tsx index 61296c5c8..811c74224 100644 --- a/packages/design-system/src/components/ds-table/filters/adapters/create-dual-range-filter-adapter.tsx +++ b/packages/design-system/src/components/ds-table/filters/adapters/create-dual-range-filter-adapter.tsx @@ -1,5 +1,5 @@ import type { Row } from '@tanstack/react-table'; -import type { ChipItem } from '../../../ds-chip-group'; +import type { TagFilterItem } from '../../../ds-tag-filter'; import type { FilterAdapter } from '../types/filter-adapter.types'; import { RangeFilter } from '../components/range-filter'; import { createFilterAdapter } from './create-filter-adapter'; @@ -31,7 +31,7 @@ export interface DualRangeFilterAdapterConfig { fields: Record; /** - * Optional formatter for chip display + * Optional formatter for tag display * @default (num) => num.toLocaleString('en-US') */ formatNumber?: (num: number) => string; @@ -91,8 +91,8 @@ export function createDualRangeFilterAdapter( cellRenderer: undefined, // Let the column definition handle rendering - toChips: (value) => { - const chips: ChipItem[] = []; + toTags: (value) => { + const tags: TagFilterItem[] = []; Object.entries(value).forEach(([fieldKey, range]) => { const hasFilter = range.from !== undefined || range.to !== undefined; @@ -102,7 +102,7 @@ export function createDualRangeFilterAdapter( const fromText = range.from !== undefined ? formatNumber(range.from) : ''; const toText = range.to !== undefined ? formatNumber(range.to) : ''; - chips.push({ + tags.push({ id: `${id}_${fieldKey}`, label: `${fieldLabel}: From ${fromText} to ${toText}`, metadata: { @@ -115,11 +115,11 @@ export function createDualRangeFilterAdapter( } }); - return chips; + return tags; }, - fromChip: (chip, currentValue) => { - const fieldKey = chip.metadata?.field as string; + fromTag: (tag, currentValue) => { + const fieldKey = tag.metadata?.field as string; if (!fieldKey) { return currentValue; } diff --git a/packages/design-system/src/components/ds-table/filters/adapters/create-filter-adapter.ts b/packages/design-system/src/components/ds-table/filters/adapters/create-filter-adapter.ts index acfb449e9..126599649 100644 --- a/packages/design-system/src/components/ds-table/filters/adapters/create-filter-adapter.ts +++ b/packages/design-system/src/components/ds-table/filters/adapters/create-filter-adapter.ts @@ -23,14 +23,14 @@ export interface FilterAdapterConfig filterFn: FilterAdapter['columnFilterFn']; /** - * Convert filter value to display chips + * Convert filter value to display tags */ - toChips: FilterAdapter['toChips']; + toTags: FilterAdapter['toTags']; /** - * Remove chip effect from filter value + * Remove tag effect from filter value */ - fromChip: FilterAdapter['fromChip']; + fromTag: FilterAdapter['fromTag']; /** * Calculate active filter count @@ -59,7 +59,7 @@ export interface FilterAdapterConfig * - Complex filtering logic (e.g., editor + date range) * - Unique UI that doesn't fit generic patterns * - Combining multiple sub-filters - * - Special chip generation requirements + * - Special tag generation requirements * * ## When to use specialized helpers instead: * - **Checkbox filters**: Use `createCheckboxFilterAdapter` for multi-select @@ -82,13 +82,13 @@ export interface FilterAdapterConfig * return matchesUsers && matchesDateRange; * }, * - * toChips: (value) => { - * // Generate chips from your filter value - * return chips; + * toTags: (value) => { + * // Generate tags from your filter value + * return tags; * }, * - * fromChip: (chip, currentValue) => { - * // Remove chip's effect from value + * fromTag: (tag, currentValue) => { + * // Remove tag's effect from value * return updatedValue; * }, * @@ -122,8 +122,8 @@ export function createFilterAdapter( initialValue: config.initialValue, columnFilterFn: config.filterFn, cellRenderer: config.cellRenderer, - toChips: config.toChips, - fromChip: config.fromChip, + toTags: config.toTags, + fromTag: config.fromTag, getActiveFiltersCount: config.getActiveFiltersCount, reset: () => config.initialValue, renderFilter: config.renderFilter, diff --git a/packages/design-system/src/components/ds-table/filters/components/range-filter/range-filter.tsx b/packages/design-system/src/components/ds-table/filters/components/range-filter/range-filter.tsx index 9d1aaa6fb..52483cec9 100644 --- a/packages/design-system/src/components/ds-table/filters/components/range-filter/range-filter.tsx +++ b/packages/design-system/src/components/ds-table/filters/components/range-filter/range-filter.tsx @@ -47,7 +47,7 @@ export const RangeFilter = ({ label, value, onChange, onClear }: RangeFilterProp
{label} {hasValue && onClear && ( - + Clear )} diff --git a/packages/design-system/src/components/ds-table/filters/hooks/use-table-filters.ts b/packages/design-system/src/components/ds-table/filters/hooks/use-table-filters.ts index bc31248b7..9307405c7 100644 --- a/packages/design-system/src/components/ds-table/filters/hooks/use-table-filters.ts +++ b/packages/design-system/src/components/ds-table/filters/hooks/use-table-filters.ts @@ -1,6 +1,6 @@ import { type ReactNode, useState } from 'react'; import type { CellContext, ColumnDef } from '@tanstack/react-table'; -import type { ChipItem } from '../../../ds-chip-group'; +import type { TagFilterItem } from '../../../ds-tag-filter'; import type { AnyAdapter, ColumnFilterState, @@ -29,7 +29,7 @@ export interface UseTableFiltersOptions { /** * Controlled mode: Callback when filters change. - * Called by applyFilters(), deleteChip(), clearAll(). + * Called by applyFilters(), deleteTag(), clearAll(). */ onFiltersChange?: (filters: FilterState) => void; } @@ -46,9 +46,9 @@ export interface UseTableFiltersResult { columnFilters: ColumnFilterState[]; /** - * Filter chips for display (derived from applied state) + * Filter tags for display (derived from applied state) */ - filterChips: ChipItem[]; + filterTags: TagFilterItem[]; /** * Filter navigation items with active counts @@ -70,8 +70,8 @@ export interface UseTableFiltersResult { applyFilters: () => void; /** Clear all filters */ clearAll: () => void; - /** Delete a specific filter chip */ - deleteChip: (chip: ChipItem) => void; + /** Delete a specific filter tag */ + deleteTag: (tag: TagFilterItem) => void; }; /** @@ -82,21 +82,21 @@ export interface UseTableFiltersResult { /** * Hook to orchestrate table filtering with adapters. - * Manages filter state, generates chips, and handles column definitions. + * Manages filter state, generates tags, and handles column definitions. * * **Uncontrolled mode** (default): Filters are managed internally. * **Controlled mode**: Filters are managed externally via appliedFilters/onFiltersChange. * * @example * // Uncontrolled mode - * const { filterChips, handlers } = useTableFilters({ + * const { filterTags, handlers } = useTableFilters({ * filterAdapters: adapters, * baseColumns: columns, * }); * * @example * // Controlled mode (e.g., URL-driven filtering) - * const { filterChips, handlers } = useTableFilters({ + * const { filterTags, handlers } = useTableFilters({ * filterAdapters: adapters, * baseColumns: columns, * appliedFilters: filtersFromUrl, @@ -134,9 +134,9 @@ export function useTableFilters({ }) .map(([id, value]) => ({ id, value })); - const filterChips = _filterAdapters.flatMap((adapter) => { + const filterTags = _filterAdapters.flatMap((adapter) => { const value = appliedFilters[adapter.id]; - return value !== undefined ? adapter.toChips(value) : []; + return value !== undefined ? adapter.toTags(value) : []; }); const filterNavItems: FilterNavItem[] = _filterAdapters.map((adapter) => ({ @@ -185,8 +185,8 @@ export function useTableFilters({ setAppliedFilters({}); }; - const deleteChip = (chip: ChipItem) => { - const filterKey = typeof chip.metadata?.key === 'string' ? chip.metadata.key : undefined; + const deleteTag = (tag: TagFilterItem) => { + const filterKey = typeof tag.metadata?.key === 'string' ? tag.metadata.key : undefined; if (!filterKey) { return; } @@ -201,7 +201,7 @@ export function useTableFilters({ return; } - const newValue = adapter.fromChip(chip, currentValue); + const newValue = adapter.fromTag(tag, currentValue); const newFilters = adapter.getActiveFiltersCount(newValue) === 0 @@ -226,14 +226,14 @@ export function useTableFilters({ return { filterState: draftFilters, columnFilters, - filterChips, + filterTags, filterNavItems, enhancedColumns, handlers: { updateFilter, applyFilters, clearAll, - deleteChip, + deleteTag, }, renderFilterContent, }; diff --git a/packages/design-system/src/components/ds-table/filters/index.ts b/packages/design-system/src/components/ds-table/filters/index.ts index f6699565c..03db798e4 100644 --- a/packages/design-system/src/components/ds-table/filters/index.ts +++ b/packages/design-system/src/components/ds-table/filters/index.ts @@ -20,7 +20,7 @@ * // 2. Use in component with useTableFilters hook * import { useTableFilters } from '../filters'; * - * const { columnFilters, filterChips, enhancedColumns, handlers } = + * const { columnFilters, filterTags, enhancedColumns, handlers } = * useTableFilters(myFilters, columns); * ``` * diff --git a/packages/design-system/src/components/ds-table/filters/types/filter-adapter.types.ts b/packages/design-system/src/components/ds-table/filters/types/filter-adapter.types.ts index fd98a6653..57b1e93bb 100644 --- a/packages/design-system/src/components/ds-table/filters/types/filter-adapter.types.ts +++ b/packages/design-system/src/components/ds-table/filters/types/filter-adapter.types.ts @@ -1,6 +1,6 @@ import type { ReactNode } from 'react'; import type { Row } from '@tanstack/react-table'; -import type { ChipItem } from '../../../ds-chip-group'; +import type { TagFilterItem } from '../../../ds-tag-filter'; // eslint-disable-next-line @typescript-eslint/no-explicit-any export type AnyAdapter = FilterAdapter; @@ -43,16 +43,16 @@ export interface FilterAdapter { cellRenderer?: (value: TCellValue) => ReactNode; /** - * Convert filter value to filter chips for display - * Returns empty array if no chips should be shown + * Convert filter value to filter tags for display + * Returns empty array if no tags should be shown */ - toChips: (value: TFilterValue) => ChipItem[]; + toTags: (value: TFilterValue) => TagFilterItem[]; /** - * Remove a chip from the filter value - * Returns updated filter value with the chip's effect removed + * Remove a tag from the filter value + * Returns updated filter value with the tag's effect removed */ - fromChip: (chip: ChipItem, currentValue: TFilterValue) => TFilterValue; + fromTag: (tag: TagFilterItem, currentValue: TFilterValue) => TFilterValue; /** * Calculate how many active filters are applied diff --git a/packages/design-system/src/components/ds-table/stories/components/progress-infographic/progress-infographic.module.scss b/packages/design-system/src/components/ds-table/stories/components/progress-infographic/progress-infographic.module.scss index 73dabf49e..18b92d647 100644 --- a/packages/design-system/src/components/ds-table/stories/components/progress-infographic/progress-infographic.module.scss +++ b/packages/design-system/src/components/ds-table/stories/components/progress-infographic/progress-infographic.module.scss @@ -17,15 +17,15 @@ transition: width 0.3s ease-in-out; &--low { - background-color: var(--background-error); + background-color: var(--background-background-negative); } &--medium { - background-color: var(--color-dap-blue-600); + background-color: var(--background-background-info-strong); } &--high { - background-color: var(--color-dap-data-green); + background-color: var(--background-background-positive-strong); } } } diff --git a/packages/design-system/src/components/ds-table/stories/ds-table.stories.module.scss b/packages/design-system/src/components/ds-table/stories/ds-table.stories.module.scss index 066576180..f0f1ba894 100644 --- a/packages/design-system/src/components/ds-table/stories/ds-table.stories.module.scss +++ b/packages/design-system/src/components/ds-table/stories/ds-table.stories.module.scss @@ -20,15 +20,15 @@ color: var(--color-dap-gray-050); &--single { - background-color: var(--color-dap-data-green); + background-color: var(--background-background-positive-strong); } &--relationship { - background-color: var(--color-dap-blue-600); + background-color: var(--background-background-info-strong); } &--complicated { - background-color: var(--background-error); + background-color: var(--background-background-negative); } } @@ -328,5 +328,5 @@ } .destructiveAction { - color: var(--background-error); + color: var(--background-background-negative); } diff --git a/packages/design-system/src/components/ds-table/stories/filters-panel.stories.tsx b/packages/design-system/src/components/ds-table/stories/filters-panel.stories.tsx index 2f33e2419..fbf9adc0e 100644 --- a/packages/design-system/src/components/ds-table/stories/filters-panel.stories.tsx +++ b/packages/design-system/src/components/ds-table/stories/filters-panel.stories.tsx @@ -8,7 +8,7 @@ import { DsButton } from '../../ds-button'; import { DsModal } from '../../ds-modal'; import { DsVerticalTabs } from '../../ds-vertical-tabs'; import { DsTypography } from '../../ds-typography'; -import { DsChipGroup } from '../../ds-chip-group'; +import { DsTagFilter } from '../../ds-tag-filter'; import { useTableFilters } from '../filters/hooks/use-table-filters'; import type { FilterNavItem } from '../filters/types/filter-adapter.types'; import { type Workflow, workflowFilters } from './filters-panel/workflow-filters.config'; @@ -239,7 +239,7 @@ A plug-and-play filter system using the **Filter Adapter Pattern** that eliminat - **Plug-and-play**: Add filters by adding to config array - **Type-safe**: Full TypeScript support -- **Automatic**: Chip generation, nav items, column enhancement +- **Automatic**: Tag generation, nav items, column enhancement - **Reusable**: Generic adapters work across tables - **Extensible**: Custom adapters for complex scenarios @@ -281,10 +281,10 @@ function MyTable() { const { columnFilters, // For TanStack Table - filterChips, // For DsChipGroup + filterTags, // For DsTagFilter filterNavItems, // For filter navigation (FilterNavItem[]) enhancedColumns, // Columns with filters - handlers, // { applyFilters, clearAll, deleteChip } + handlers, // { applyFilters, clearAll, deleteTag } renderFilterContent, // Render function } = useTableFilters({ filterAdapters: myFilters, @@ -297,11 +297,11 @@ function MyTable() { - {filterChips.length > 0 && ( - 0 && ( + )} @@ -329,7 +329,7 @@ createCheckboxFilterAdapter({ label: 'Display Label', items: [{ value: 'val1', label: 'Label 1' }], renderer?: (item) => , // Optional - chipLabelTemplate?: (item) => \`\${item.label}\`, // Optional + tagLabelTemplate?: (item) => \`\${item.label}\`, // Optional cellRenderer?: (value) => , // Optional }); \`\`\` @@ -354,8 +354,8 @@ createCustomFilterAdapter({ label: 'Display Label', initialValue: { /* your state */ }, filterFn: (row, columnId, filterValue) => boolean, - toChips: (value) => FilterChipItem[], - fromChip: (chip, currentValue) => newValue, + toTags: (value) => TagFilterItem[], + fromTag: (tag, currentValue) => newValue, getActiveFiltersCount: (value) => number, // 0 means none active renderFilter: (value, onChange) => ReactNode, cellRenderer?: (value) => ReactNode, // Optional @@ -364,7 +364,7 @@ createCustomFilterAdapter({ ## What You Get Automatically -- Chip generation from filter state +- Tag generation from filter state - Filter nav items with active counts - Column enhancement with filter functions - State management across all filters @@ -413,7 +413,7 @@ This story demonstrates the complete filter system with: - **Status Filter**: Checkbox multi-select with custom rendering (status badges) - **Running/Completed Filter**: Dual-range numeric filter - **Category Filter**: Simple checkbox multi-select -- **Version Filter**: Checkbox with custom chip labels +- **Version Filter**: Checkbox with custom tag labels #### Key Implementation Details: @@ -426,10 +426,10 @@ This story demonstrates the complete filter system with: \`\`\`typescript const { columnFilters, // Pass to DsTable - filterChips, // Pass to DsChipGroup + filterTags, // Pass to DsTagFilter filterNavItems, // Pass to DsVerticalTabs in modal enhancedColumns, // Pass to DsTable (includes filter functions) - handlers, // { applyFilters, clearAll, deleteChip } + handlers, // { applyFilters, clearAll, deleteTag } renderFilterContent, // Render function for modal content } = useTableFilters({ filterAdapters: workflowFilters, @@ -439,7 +439,7 @@ This story demonstrates the complete filter system with: 3. **What's Handled Automatically**: - Filter state management - - Chip generation and deletion + - Tag generation and deletion - Nav item counts (updates in real-time) - Column enhancement with filter functions - Type-safe filter values @@ -490,9 +490,9 @@ const handleValueChange = (value: string | null) => { - Clear all + Clear all - Apply + Apply @@ -506,8 +506,8 @@ See the story code for complete implementation with styles. 1. Click the filter icon to open the modal 2. Select filters in different categories 3. Notice the nav item counts update as you make changes -4. Click "Apply" to see filtered data and chips -5. Delete individual chips or clear all filters +4. Click "Apply" to see filtered data and tags +5. Delete individual tags or clear all filters #### Adding More Filters: To add a new filter, just add one adapter to \`workflowFilters\` array. No other changes needed! @@ -517,7 +517,7 @@ To add a new filter, just add one adapter to \`workflowFilters\` array. No other }, render: function Render(args) { // useTableFilters hook orchestrates all filter logic - const { columnFilters, filterChips, filterNavItems, enhancedColumns, handlers, renderFilterContent } = + const { columnFilters, filterTags, filterNavItems, enhancedColumns, handlers, renderFilterContent } = useTableFilters({ filterAdapters: workflowFilters, baseColumns: args.columns, @@ -571,14 +571,14 @@ To add a new filter, just add one adapter to \`workflowFilters\` array. No other
{/* Toolbar with filter button */}
- setIsOpen(true)}> + setIsOpen(true)}>
- {/* Filter chips (automatically generated from filter state) */} - {filterChips.length > 0 && ( - + {/* Filter tags (automatically generated from filter state) */} + {filterTags.length > 0 && ( + )} {/* Table with enhanced columns (includes filter functions) */} @@ -616,12 +616,12 @@ To add a new filter, just add one adapter to \`workflowFilters\` array. No other - + Clear all - + Apply @@ -680,7 +680,7 @@ To add a new filter, just add one adapter to \`workflowFilters\` array. No other // 6. Apply filters await userEvent.click(screen.getByRole('button', { name: /apply/i })); - // Verify chips appear + // Verify tags appear await expect(canvas.getByText(/status: active/i)).toBeInTheDocument(); await expect(canvas.getByText(/status: running/i)).toBeInTheDocument(); await expect(canvas.getByText(/running.*0.*50/i)).toBeInTheDocument(); @@ -700,9 +700,9 @@ To add a new filter, just add one adapter to \`workflowFilters\` array. No other await userEvent.click(screen.getByRole('button', { name: /apply/i })); - // 9. Delete individual chip - const activeChip = canvas.getByRole('button', { name: /status: active/i }); - const deleteButton = within(activeChip).getByRole('button', { name: /delete/i }); + // 9. Delete individual tag + const activeTag = canvas.getByRole('button', { name: /status: active/i }); + const deleteButton = activeTag.querySelector('button[aria-label="Delete tag"]') as HTMLElement; await userEvent.click(deleteButton); await expect(canvas.queryByRole('button', { name: /status: active/i })).not.toBeInTheDocument(); @@ -735,7 +735,7 @@ This is useful for: \`\`\`typescript const [appliedFilters, setAppliedFilters] = useState({}); -const { filterChips, handlers } = useTableFilters({ +const { filterTags, handlers } = useTableFilters({ filterAdapters: workflowFilters, baseColumns: columns, appliedFilters, // External state @@ -752,7 +752,7 @@ The debug panel below shows the current filter state as JSON. // External filter state (controlled mode) const [appliedFilters, setAppliedFilters] = useState>({}); - const { columnFilters, filterChips, filterNavItems, enhancedColumns, handlers, renderFilterContent } = + const { columnFilters, filterTags, filterNavItems, enhancedColumns, handlers, renderFilterContent } = useTableFilters({ filterAdapters: workflowFilters, baseColumns: args.columns, @@ -811,13 +811,13 @@ The debug panel below shows the current filter state as JSON.
- setIsOpen(true)}> + setIsOpen(true)}>
- {filterChips.length > 0 && ( - + {filterTags.length > 0 && ( + )} @@ -853,12 +853,12 @@ The debug panel below shows the current filter state as JSON. - + Clear all - + Apply @@ -892,7 +892,7 @@ The debug panel below shows the current filter state as JSON. // 2. Verify external state is updated (debug panel shows filter) await expect(canvas.getByText(/"status"/)).toBeInTheDocument(); - // 3. Verify chip appears + // 3. Verify tag appears await expect(canvas.getByText(/status: active/i)).toBeInTheDocument(); // 4. Clear all and verify state resets diff --git a/packages/design-system/src/components/ds-table/stories/filters-panel/components/last-edited-filter/last-edited-filter.tsx b/packages/design-system/src/components/ds-table/stories/filters-panel/components/last-edited-filter/last-edited-filter.tsx index d79a2feda..4361c4e54 100644 --- a/packages/design-system/src/components/ds-table/stories/filters-panel/components/last-edited-filter/last-edited-filter.tsx +++ b/packages/design-system/src/components/ds-table/stories/filters-panel/components/last-edited-filter/last-edited-filter.tsx @@ -85,7 +85,7 @@ export const LastEditedFilter = ({ value, onChange, availableEditors }: LastEdit
Editor {value.editors.length > 0 && ( - + Clear )} @@ -126,7 +126,7 @@ export const LastEditedFilter = ({ value, onChange, availableEditors }: LastEdit
Last edited {value.timeRange && ( - + Clear )} diff --git a/packages/design-system/src/components/ds-table/stories/filters-panel/workflow-filters.config.tsx b/packages/design-system/src/components/ds-table/stories/filters-panel/workflow-filters.config.tsx index 3ac723a68..9463acc39 100644 --- a/packages/design-system/src/components/ds-table/stories/filters-panel/workflow-filters.config.tsx +++ b/packages/design-system/src/components/ds-table/stories/filters-panel/workflow-filters.config.tsx @@ -6,18 +6,18 @@ * * 1. **Checkbox Filter** (statusFilterAdapter): * - Multi-select with custom rendering (status badges) - * - Custom chip labels + * - Custom tag labels * - Custom cell renderer * * 2. **Dual-Range Filter** (runningCompletedFilterAdapter): * - Multiple numeric range fields in one filter * - Number formatting - * - Automatic chip generation + * - Automatic tag generation * * 3. **Custom Filter** (lastEditedFilterAdapter): * - Complex filter with editor selection + date range * - Custom render component - * - Multiple chip types from one filter + * - Multiple tag types from one filter * - Custom cell renderer * * ## Usage Pattern: @@ -30,7 +30,7 @@ * export const workflowFilters = [myFilter, ...]; * * // 3. Use in component with useTableFilters hook - * const { columnFilters, filterChips, enhancedColumns, handlers } = + * const { columnFilters, filterTags, enhancedColumns, handlers } = * useTableFilters(workflowFilters, columns); * ``` * @@ -122,20 +122,20 @@ const renderStatusBadge = (status: DsStatus): ReactNode => { * * Demonstrates: * - Custom item rendering with DsStatusBadge - * - Custom chip label template + * - Custom tag label template * - Custom cell renderer for table column * * Features: * - Empty selection = show all (no filter applied) * - Selected items = show only those items - * - Automatic chip generation for each selected status + * - Automatic tag generation for each selected status */ const statusFilterAdapter = createCheckboxFilterAdapter({ id: 'status', label: 'Status', items: statusItems, renderer: (item) => renderStatusBadge(item.value), - chipLabelTemplate: (item) => `Status: ${item.label}`, + tagLabelTemplate: (item) => `Status: ${item.label}`, cellRenderer: (value) => renderStatusBadge(value), }); @@ -150,8 +150,8 @@ const statusFilterAdapter = createCheckboxFilterAdapter({ * Features: * - Each field can have independent from/to ranges * - All ranges must match (AND logic) - * - Automatic chip generation for each active range - * - Formatted numbers in chips + * - Automatic tag generation for each active range + * - Formatted numbers in tags */ const runningCompletedFilterAdapter = createDualRangeFilterAdapter({ id: 'runningCompleted', @@ -233,14 +233,14 @@ const parseTimestamp = (timestamp: string): Date => { * Demonstrates: * - Complex filter state (editor multi-select + time range) * - Custom filter function with multiple conditions - * - Multiple chip types from one filter + * - Multiple tag types from one filter * - Custom filter UI component * - Custom cell renderer * * Features: * - Filter by multiple editors (multi-select) * - Filter by time range (preset options + custom date range) - * - Separate chips for editors and time range + * - Separate tags for editors and time range * - Both conditions must match (AND logic) * * This is a reference implementation for building custom filters @@ -277,19 +277,17 @@ const lastEditedFilterAdapter = createFilterAdapter { - const chips = []; + toTags: (value) => { + const tags = []; - // Editor chips value.editors.forEach((editor) => { - chips.push({ + tags.push({ id: `editor-${editor}`, label: `Editor: ${editor}`, metadata: { key: 'lastEdited', type: 'editor', value: editor }, }); }); - // Time range chip if (value.timeRange) { let label = ''; switch (value.timeRange) { @@ -312,22 +310,22 @@ const lastEditedFilterAdapter = createFilterAdapter { - const { type, value: chipValue } = chip.metadata || {}; + fromTag: (tag, currentValue) => { + const { type, value: tagValue } = tag.metadata || {}; if (type === 'editor') { return { ...currentValue, - editors: currentValue.editors.filter((e) => e !== chipValue), + editors: currentValue.editors.filter((e) => e !== tagValue), }; } diff --git a/packages/design-system/src/components/ds-tag-filter/ds-tag-filter.tsx b/packages/design-system/src/components/ds-tag-filter/ds-tag-filter.tsx index 60b67bc52..9b046ca1c 100644 --- a/packages/design-system/src/components/ds-tag-filter/ds-tag-filter.tsx +++ b/packages/design-system/src/components/ds-tag-filter/ds-tag-filter.tsx @@ -104,11 +104,8 @@ const DsTagFilter = ({
{hasOverflow && ( @@ -118,15 +115,7 @@ const DsTagFilter = ({ )} {onClearAll && ( - + {clearButton} diff --git a/packages/design-system/src/components/ds-time-picker/ds-time-picker.tsx b/packages/design-system/src/components/ds-time-picker/ds-time-picker.tsx index 70c4d0ffa..32ee34ebc 100644 --- a/packages/design-system/src/components/ds-time-picker/ds-time-picker.tsx +++ b/packages/design-system/src/components/ds-time-picker/ds-time-picker.tsx @@ -139,9 +139,8 @@ const DsTimePicker = (props: DsTimePickerProps) => { {showClearButton && ( setValue(null)} @@ -151,14 +150,12 @@ const DsTimePicker = (props: DsTimePickerProps) => { )} - - + /> ), diff --git a/packages/design-system/src/components/ds-toast/ds-toast.stories.tsx b/packages/design-system/src/components/ds-toast/ds-toast.stories.tsx index 7d89da608..832e52626 100644 --- a/packages/design-system/src/components/ds-toast/ds-toast.stories.tsx +++ b/packages/design-system/src/components/ds-toast/ds-toast.stories.tsx @@ -78,7 +78,7 @@ const ToastDemo = ({ return (

{variant.charAt(0).toUpperCase() + variant.slice(1)} Toast

- + Show {variant.charAt(0).toUpperCase() + variant.slice(1)} Toast
@@ -124,7 +124,7 @@ export const WarningNoTitleAction: Story = { variant="warning" description="Something went wrong. Please try again." actions={ - + Restart } @@ -149,23 +149,22 @@ export const WarningWithActions: Story = {
{ setAction('abort'); dismissToast(toastId); }} - variant="ghost" + variant="tertiary" > Abort { setAction('retry'); dismissToast(toastId); }} - variant="danger" + variant="primary" + color="error" > Re-try @@ -178,12 +177,7 @@ export const WarningWithActions: Story = { return (

Toast with Action

- + Show Toast with Actions {action &&

{action}

} @@ -262,7 +256,7 @@ export const LongContent: Story = { return (

Long Content Toast

- + Show Long Content Toast
@@ -325,20 +319,10 @@ const MultipleToastsDemo = () => {

Multiple Toasts Demo

- + Create Multiple Toasts - + Dismiss All
diff --git a/packages/design-system/src/components/ds-workspace/__tests__/ds-workspace.browser.test.tsx b/packages/design-system/src/components/ds-workspace/__tests__/ds-workspace.browser.test.tsx index 4de1ba26f..ad5ed0af4 100644 --- a/packages/design-system/src/components/ds-workspace/__tests__/ds-workspace.browser.test.tsx +++ b/packages/design-system/src/components/ds-workspace/__tests__/ds-workspace.browser.test.tsx @@ -89,7 +89,7 @@ describe('DsWorkspace', () => { const root = page.getByRole('banner').element().parentElement as HTMLElement; const style = getComputedStyle(root); - expect(style.height).toBe(`${String(window.innerHeight)}px`); + expect(parseFloat(style.height)).toBeCloseTo(window.innerHeight, 0); }); it('fills parent height when fillParent is true', async () => { diff --git a/packages/design-system/src/components/ds-workspace/ds-workspace.stories.tsx b/packages/design-system/src/components/ds-workspace/ds-workspace.stories.tsx index e6c0cff62..4078177d3 100644 --- a/packages/design-system/src/components/ds-workspace/ds-workspace.stories.tsx +++ b/packages/design-system/src/components/ds-workspace/ds-workspace.stories.tsx @@ -1,8 +1,8 @@ import { useState } from 'react'; import type { Meta, StoryObj } from '@storybook/react-vite'; import DsWorkspace from './ds-workspace'; -import { DsButtonV3 } from '../ds-button-v3'; import { DsTypography } from '../ds-typography'; +import { DsButton } from '../ds-button'; import { DsIcon } from '../ds-icon'; import { DsStatusBadge } from '../ds-status-badge'; import { DsDrawer } from '../ds-drawer'; @@ -57,9 +57,9 @@ type Story = StoryObj; const WorkspaceHeader = ({ onAction }: { onAction?: () => void }) => (
- + Close - +
@@ -75,13 +75,13 @@ const WorkspaceHeader = ({ onAction }: { onAction?: () => void }) => ( Last update: 2d ago
- + Discard - - + + Save project - - + +
); @@ -117,12 +117,12 @@ export const Default: Story = {
v1.2.0
- + Help - - + + Feedback - +
@@ -171,12 +171,12 @@ export const WithDrawer: Story = { - setDrawerOpen(false)}> + setDrawerOpen(false)}> Cancel - - +
+ Save - + diff --git a/packages/design-system/src/index.ts b/packages/design-system/src/index.ts index be0efec60..d8d690af1 100644 --- a/packages/design-system/src/index.ts +++ b/packages/design-system/src/index.ts @@ -4,21 +4,15 @@ export * from './components/ds-avatar'; export * from './components/ds-avatar-group'; export * from './components/ds-breadcrumb'; export * from './components/ds-button'; -export * from './components/ds-button-v3'; export * from './components/ds-card'; export * from './components/ds-checkbox'; export * from './components/ds-checkbox-group'; -export * from './components/ds-chip'; -export * from './components/ds-chip-group'; export * from './components/ds-comment-bubble'; export * from './components/ds-comment-card'; export * from './components/ds-comment-indicator'; export * from './components/ds-comments-drawer'; -export * from './components/ds-confirmation'; -export * from './components/ds-date-input'; export * from './components/ds-date-picker'; export * from './components/ds-date-range-picker'; -export * from './components/ds-dialog'; export * from './components/ds-divider'; export * from './components/ds-drawer'; export * from './components/ds-dropdown-menu'; @@ -48,7 +42,6 @@ export * from './components/ds-split-button'; export * from './components/ds-stack'; export * from './components/ds-status-badge'; export * from './components/ds-stepper'; -export * from './components/ds-system-status'; export * from './components/ds-table'; export * from './components/ds-tabs'; export * from './components/ds-tag'; diff --git a/packages/design-system/src/stories/sample-form/sample-form.tsx b/packages/design-system/src/stories/sample-form/sample-form.tsx index 0b7968c91..bb6a891a6 100644 --- a/packages/design-system/src/stories/sample-form/sample-form.tsx +++ b/packages/design-system/src/stories/sample-form/sample-form.tsx @@ -7,7 +7,7 @@ import { DsCheckbox } from '../../components/ds-checkbox'; import { DsTypography } from '../../components/ds-typography'; import { sampleFormSchema, type SampleFormValues } from './sample-form-schema'; import { useState } from 'react'; -import { DsButtonV3 } from '../../components/ds-button-v3'; +import { DsButton } from '../../components/ds-button'; const defaultValues = { name: '', @@ -235,9 +235,9 @@ const SampleForm = () => { )} /> - + Submit - + ); diff --git a/packages/design-system/src/styles/_colors.scss b/packages/design-system/src/styles/_colors.scss index ac2f8d8e7..cc7f9f5d7 100644 --- a/packages/design-system/src/styles/_colors.scss +++ b/packages/design-system/src/styles/_colors.scss @@ -27,13 +27,6 @@ $colors: ( action-active-light: #c7d8ff, action-hover-light: #e0eaff, action-disabled: #b0b4bf, - // System Status - system-status-ok: #067a00, - system-status-info: #005dcf, - system-status-error: #d70a00, - system-status-alert: #d04d07, - system-status-in-progress: #1513da, - system-status-pending: #8836d6, // Utility utility-ok: #d6f5cf, utility-info: #c4dcff, diff --git a/packages/design-system/vitest.config.ts b/packages/design-system/vitest.config.ts index 0e8cd96f0..6d7f23734 100644 --- a/packages/design-system/vitest.config.ts +++ b/packages/design-system/vitest.config.ts @@ -13,20 +13,7 @@ export default defineConfig({ test: { coverage: { include: ['src/**/*.{ts,tsx}'], - exclude: [ - '**/stories/**', - '**/*.stories.*{ts,tsx}', - '**/.storybook/**', - '**/*.scss', - 'dist/**', - - // deprecated components - '**/ds-chip/**', - '**/ds-chip-group/**', - '**/ds-confirmation/**', - '**/ds-date-input/**', - '**/ds-system-status/**', - ], + exclude: ['**/stories/**', '**/*.stories.*{ts,tsx}', '**/.storybook/**', '**/*.scss', 'dist/**'], thresholds: { lines: 90, }, diff --git a/packages/eslint-plugin/src/__tests__/no-deprecated.test.ts b/packages/eslint-plugin/src/__tests__/no-deprecated.test.ts index 92922f8b2..4f0983976 100644 --- a/packages/eslint-plugin/src/__tests__/no-deprecated.test.ts +++ b/packages/eslint-plugin/src/__tests__/no-deprecated.test.ts @@ -1,5 +1,5 @@ import { RuleTester } from 'eslint'; -import plugin from '../index'; +import { expect, it } from 'vitest'; const ruleTester = new RuleTester({ languageOptions: { @@ -11,200 +11,27 @@ const ruleTester = new RuleTester({ }, }); -ruleTester.run( - 'no-deprecated-ds-button-legacy-design', - plugin.rules['no-deprecated-ds-button-legacy-design'], - { - valid: ['Click me'], - - invalid: [ - { - code: 'Click me', - errors: [ - { - message: `Using the 'legacy' design for DsButton is deprecated. Use 'v1.2' instead.`, - line: 1, - endLine: 1, - column: 11, - endColumn: 26, - }, - ], - }, - ], - }, -); - -ruleTester.run('no-deprecated-ds-button-empty-design', plugin.rules['no-deprecated-ds-button-empty-design'], { - valid: ['Click me'], - - invalid: [ - { - code: 'Click me', - errors: [ - { - message: `Omitting the design attribute for DsButton is not allowed. Pass 'design="v1.2"' so the new design is used.`, - line: 1, - endLine: 1, - column: 2, - endColumn: 10, - }, - ], - }, - ], -}); - -ruleTester.run('no-deprecated-ds-dialog', plugin.rules['no-deprecated-ds-dialog'], { - valid: ['', '', ''], - - invalid: [ - { - code: 'Click me', - errors: [ - { - message: `DsDialog is deprecated. Use DsModal or DsConfirmation instead.`, - line: 1, - endLine: 1, - column: 2, - endColumn: 10, - }, - ], - }, - ], +it('placeholder: RuleTester instantiates', () => { + expect(ruleTester).toBeInstanceOf(RuleTester); }); -ruleTester.run('no-deprecated-ds-confirmation', plugin.rules['no-deprecated-ds-confirmation'], { - valid: [''], +// Example test for the future reference - invalid: [ - { - code: '', - errors: [ - { - message: `DsConfirmation is deprecated. Use DsModal instead.`, - line: 1, - endLine: 1, - column: 2, - endColumn: 16, - }, - ], - }, - ], -}); +// ruleTester.run('no-deprecated-ds-date-input', plugin.rules['no-deprecated-ds-date-input'], { +// valid: ['', ''], -ruleTester.run('no-deprecated-ds-system-status', plugin.rules['no-deprecated-ds-system-status'], { - valid: [''], - - invalid: [ - { - code: '', - errors: [ - { - message: `DsSystemStatus is deprecated. Use DsStatusBadge instead.`, - line: 1, - endLine: 1, - column: 2, - endColumn: 16, - }, - ], - }, - ], -}); - -ruleTester.run( - 'no-deprecated-ds-dropdown-menu-legacy', - plugin.rules['no-deprecated-ds-dropdown-menu-legacy'], - { - valid: [''], - - invalid: [ - { - code: '', - errors: [ - { - message: `DsDropdownMenuLegacy is deprecated. Use DsDropdownMenu instead.`, - line: 1, - endLine: 1, - column: 2, - endColumn: 22, - }, - ], - }, - ], - }, -); - -ruleTester.run('no-deprecated-ds-radio-group-legacy', plugin.rules['no-deprecated-ds-radio-group-legacy'], { - valid: [''], - - invalid: [ - { - code: '', - errors: [ - { - message: `DsRadioGroupLegacy is deprecated. Use DsRadioGroup instead.`, - line: 1, - endLine: 1, - column: 2, - endColumn: 20, - }, - ], - }, - ], -}); - -ruleTester.run('no-deprecated-ds-chip', plugin.rules['no-deprecated-ds-chip'], { - valid: ['', ''], - - invalid: [ - { - code: '', - errors: [ - { - message: `DsChip is deprecated. Use DsTag instead.`, - line: 1, - endLine: 1, - column: 2, - endColumn: 8, - }, - ], - }, - ], -}); - -ruleTester.run('no-deprecated-ds-chip-group', plugin.rules['no-deprecated-ds-chip-group'], { - valid: ['', ''], - - invalid: [ - { - code: '', - errors: [ - { - message: `DsChipGroup is deprecated. Use DsTagFilter instead.`, - line: 1, - endLine: 1, - column: 2, - endColumn: 13, - }, - ], - }, - ], -}); - -ruleTester.run('no-deprecated-ds-date-input', plugin.rules['no-deprecated-ds-date-input'], { - valid: ['', ''], - - invalid: [ - { - code: '', - errors: [ - { - message: `DsDateInput is deprecated. Use DsDatePicker or DsDateRangePicker instead.`, - line: 1, - endLine: 1, - column: 2, - endColumn: 13, - }, - ], - }, - ], -}); +// invalid: [ +// { +// code: '', +// errors: [ +// { +// message: `DsDateInput is deprecated. Use DsDatePicker or DsDateRangePicker instead.`, +// line: 1, +// endLine: 1, +// column: 2, +// endColumn: 13, +// }, +// ], +// }, +// ], +// }); diff --git a/packages/eslint-plugin/src/index.ts b/packages/eslint-plugin/src/index.ts index afa3524f7..1be0fe8bf 100644 --- a/packages/eslint-plugin/src/index.ts +++ b/packages/eslint-plugin/src/index.ts @@ -3,65 +3,12 @@ import { createPlugin } from './create-plugin'; const eslintPlugin = createPlugin( '@drivenets/design-system', - { - name: 'no-deprecated-ds-button-legacy-design', - selector: JSXElementAttribute('DsButton', 'design', 'legacy'), - message: `Using the 'legacy' design for DsButton is deprecated. Use 'v1.2' instead.`, - }, - - { - name: 'no-deprecated-ds-button-empty-design', - selector: `${JSXElement('DsButton')}:not(:has( > ${JSXAttribute('design')} )) > .name`, - message: `Omitting the design attribute for DsButton is not allowed. Pass 'design="v1.2"' so the new design is used.`, - }, - - { - name: 'no-deprecated-ds-dialog', - selector: JSXElementName('DsDialog'), - message: `DsDialog is deprecated. Use DsModal or DsConfirmation instead.`, - }, - - { - name: 'no-deprecated-ds-confirmation', - selector: JSXElementName('DsConfirmation'), - message: `DsConfirmation is deprecated. Use DsModal instead.`, - }, - - { - name: 'no-deprecated-ds-system-status', - selector: JSXElementName('DsSystemStatus'), - message: `DsSystemStatus is deprecated. Use DsStatusBadge instead.`, - }, - - { - name: 'no-deprecated-ds-dropdown-menu-legacy', - selector: JSXElementName('DsDropdownMenuLegacy'), - message: `DsDropdownMenuLegacy is deprecated. Use DsDropdownMenu instead.`, - }, - - { - name: 'no-deprecated-ds-radio-group-legacy', - selector: JSXElementName('DsRadioGroupLegacy'), - message: `DsRadioGroupLegacy is deprecated. Use DsRadioGroup instead.`, - }, - - { - name: 'no-deprecated-ds-chip', - selector: JSXElementName('DsChip'), - message: `DsChip is deprecated. Use DsTag instead.`, - }, - - { - name: 'no-deprecated-ds-chip-group', - selector: JSXElementName('DsChipGroup'), - message: `DsChipGroup is deprecated. Use DsTagFilter instead.`, - }, - - { - name: 'no-deprecated-ds-date-input', - selector: JSXElementName('DsDateInput'), - message: `DsDateInput is deprecated. Use DsDatePicker or DsDateRangePicker instead.`, - }, + // Example test for deprecated components, for future reference + // { + // name: 'no-deprecated-ds-date-input', + // selector: JSXElementName('DsDateInput'), + // message: `DsDateInput is deprecated. Use DsDatePicker or DsDateRangePicker instead.`, + // }, { name: 'no-native-button', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ae3ea67b7..9cf012254 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -199,21 +199,12 @@ importers: '@radix-ui/react-collapsible': specifier: ^1.1.12 version: 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-dialog': - specifier: ^1.1.15 - version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-dropdown-menu': - specifier: ^2.1.16 - version: 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@radix-ui/react-popover': specifier: ^1.1.15 version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@radix-ui/react-tooltip': specifier: ^1.2.8 version: 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-visually-hidden': - specifier: ^1.2.4 - version: 1.2.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@tanstack/react-table': specifier: ^8.21.3 version: 8.21.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -262,7 +253,7 @@ importers: version: 10.3.5(@types/react@19.2.14)(esbuild@0.17.19)(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) '@storybook/addon-vitest': specifier: ^10.2.19 - version: 10.3.5(@vitest/browser-playwright@4.1.3)(@vitest/runner@4.1.5)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vitest@4.1.5) + version: 10.3.5(@vitest/browser-playwright@4.1.5)(@vitest/browser@4.1.5)(@vitest/runner@4.1.5)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vitest@4.1.5) '@storybook/react-vite': specifier: ^10.2.19 version: 10.3.5(esbuild@0.17.19)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@6.0.3)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) @@ -273,8 +264,8 @@ importers: specifier: ^1.166.2 version: 1.168.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@tsdown/css': - specifier: ^0.21.6 - version: 0.21.7(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(jiti@2.6.1)(postcss-modules@6.0.1(postcss@8.5.10))(postcss@8.5.10)(sass-embedded@1.99.0)(sass@1.99.0)(tsdown@0.21.10)(yaml@2.8.3) + specifier: ^0.21.10 + version: 0.21.10(jiti@2.6.1)(postcss-modules@6.0.1(postcss@8.5.10))(postcss@8.5.10)(sass-embedded@1.99.0)(sass@1.99.0)(tsdown@0.21.10)(yaml@2.8.3) '@types/eslint-plugin-jsx-a11y': specifier: ^6.10.1 version: 6.10.1(jiti@2.6.1) @@ -291,11 +282,11 @@ importers: specifier: ^6.0.1 version: 6.0.1(@rolldown/plugin-babel@0.2.2(@babel/core@7.29.0)(@babel/runtime@7.29.2)(rolldown@1.0.0-rc.17)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)))(babel-plugin-react-compiler@1.0.0)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) '@vitest/browser-playwright': - specifier: ^4.1.0 - version: 4.1.3(playwright@1.59.1)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3))(vitest@4.1.5) + specifier: ^4.1.5 + version: 4.1.5(playwright@1.59.1)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3))(vitest@4.1.5) '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.1.5(vitest@4.1.5) + version: 4.1.5(@vitest/browser@4.1.5)(vitest@4.1.5) babel-plugin-react-compiler: specifier: ^1.0.0 version: 1.0.0 @@ -343,7 +334,7 @@ importers: version: 10.3.5(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) tsdown: specifier: 'catalog:' - version: 0.21.10(@arethetypeswrong/core@0.18.2)(@tsdown/css@0.21.7)(@typescript/native-preview@7.0.0-dev.20260426.1)(oxc-resolver@11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(publint@0.3.18)(typescript@6.0.3) + version: 0.21.10(@arethetypeswrong/core@0.18.2)(@tsdown/css@0.21.10)(@typescript/native-preview@7.0.0-dev.20260426.1)(oxc-resolver@11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(publint@0.3.18)(typescript@6.0.3) typed-scss-modules: specifier: ^8.1.1 version: 8.1.1(sass@1.99.0) @@ -358,7 +349,7 @@ importers: version: 8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3) vitest: specifier: 'catalog:' - version: 4.1.5(@types/node@25.5.2)(@vitest/browser-playwright@4.1.3)(@vitest/coverage-v8@4.1.5)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) + version: 4.1.5(@types/node@25.5.2)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.1.5) @@ -386,7 +377,7 @@ importers: version: 7.0.0-dev.20260426.1 '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.1.5(vitest@4.1.5) + version: 4.1.5(@vitest/browser@4.1.5)(vitest@4.1.5) eslint: specifier: ^10.0.1 version: 10.2.0(jiti@2.6.1) @@ -395,13 +386,13 @@ importers: version: 0.3.18 tsdown: specifier: 'catalog:' - version: 0.21.10(@arethetypeswrong/core@0.18.2)(@tsdown/css@0.21.7)(@typescript/native-preview@7.0.0-dev.20260426.1)(oxc-resolver@11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(publint@0.3.18)(typescript@6.0.3) + version: 0.21.10(@arethetypeswrong/core@0.18.2)(@tsdown/css@0.21.10)(@typescript/native-preview@7.0.0-dev.20260426.1)(oxc-resolver@11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(publint@0.3.18)(typescript@6.0.3) typescript: specifier: ^6.0.3 version: 6.0.3 vitest: specifier: 'catalog:' - version: 4.1.5(@types/node@25.5.2)(@vitest/coverage-v8@4.1.5)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) + version: 4.1.5(@types/node@25.5.2)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) packages/eslint-plugin-internal: dependencies: @@ -420,7 +411,7 @@ importers: version: 8.58.2(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.3) '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.1.5(vitest@4.1.5) + version: 4.1.5(@vitest/browser@4.1.5)(vitest@4.1.5) eslint: specifier: ^10.0.1 version: 10.2.0(jiti@2.6.1) @@ -432,7 +423,7 @@ importers: version: 6.0.3 vitest: specifier: 'catalog:' - version: 4.1.5(@types/node@25.5.2)(@vitest/coverage-v8@4.1.5)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) + version: 4.1.5(@types/node@25.5.2)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) packages/vite-plugin: dependencies: @@ -448,7 +439,7 @@ importers: version: 7.0.0-dev.20260426.1 '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.1.5(vitest@4.1.5) + version: 4.1.5(@vitest/browser@4.1.5)(vitest@4.1.5) eslint: specifier: ^10.0.1 version: 10.2.0(jiti@2.6.1) @@ -457,13 +448,13 @@ importers: version: 0.3.18 tsdown: specifier: 'catalog:' - version: 0.21.10(@arethetypeswrong/core@0.18.2)(@tsdown/css@0.21.7)(@typescript/native-preview@7.0.0-dev.20260426.1)(oxc-resolver@11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(publint@0.3.18)(typescript@6.0.3) + version: 0.21.10(@arethetypeswrong/core@0.18.2)(@tsdown/css@0.21.10)(@typescript/native-preview@7.0.0-dev.20260426.1)(oxc-resolver@11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(publint@0.3.18)(typescript@6.0.3) typescript: specifier: ^6.0.3 version: 6.0.3 vitest: specifier: 'catalog:' - version: 4.1.5(@types/node@25.5.2)(@vitest/coverage-v8@4.1.5)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) + version: 4.1.5(@types/node@25.5.2)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) packages: @@ -1909,19 +1900,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-collection@1.1.7': - resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-compose-refs@1.1.2': resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} peerDependencies: @@ -1940,28 +1918,6 @@ packages: '@types/react': optional: true - '@radix-ui/react-dialog@1.1.15': - resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-direction@1.1.1': - resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@radix-ui/react-dismissable-layer@1.1.11': resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==} peerDependencies: @@ -1975,19 +1931,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-dropdown-menu@2.1.16': - resolution: {integrity: sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-focus-guards@1.1.3': resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==} peerDependencies: @@ -2019,19 +1962,6 @@ packages: '@types/react': optional: true - '@radix-ui/react-menu@2.1.16': - resolution: {integrity: sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-popover@1.1.15': resolution: {integrity: sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==} peerDependencies: @@ -2097,32 +2027,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-primitive@2.1.4': - resolution: {integrity: sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-roving-focus@1.1.11': - resolution: {integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-slot@1.2.3': resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} peerDependencies: @@ -2132,15 +2036,6 @@ packages: '@types/react': optional: true - '@radix-ui/react-slot@1.2.4': - resolution: {integrity: sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@radix-ui/react-tooltip@1.2.8': resolution: {integrity: sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==} peerDependencies: @@ -2230,19 +2125,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-visually-hidden@1.2.4': - resolution: {integrity: sha512-kaeiyGCe844dkb9AVF+rb4yTyb1LiLN/e3es3nLiRyN4dC8AduBYPMnnNlDjX2VDOcvDEiPnRNMJeWCfsX0txg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - '@radix-ui/rect@1.1.1': resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} @@ -2646,8 +2528,8 @@ packages: peerDependencies: '@testing-library/dom': '>=7.21.4' - '@tsdown/css@0.21.7': - resolution: {integrity: sha512-kydfZ109LIXwoBDrdIeEVi+PtM8375X9d/6UtYtjhj6TS94J25gJVUXw9AyJE6THEqB6OdGKM5MLqJPutO4kkA==} + '@tsdown/css@0.21.10': + resolution: {integrity: sha512-JCq5KKx2WymgJiKYB7QIJLh8JjVyD3ncIr1xdX4sxh3XsSN+jVAKpp3X53bhaGNsPnaZmu3j/TLIl9F9sBJPiQ==} engines: {node: '>=20.19.0'} peerDependencies: postcss: ^8.4.0 @@ -2655,7 +2537,7 @@ packages: postcss-modules: ^6.0.0 sass: '*' sass-embedded: '*' - tsdown: 0.21.7 + tsdown: 0.21.10 peerDependenciesMeta: postcss: optional: true @@ -2698,8 +2580,8 @@ packages: cpu: [arm64] os: [win32] - '@tybys/wasm-util@0.10.1': - resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + '@tybys/wasm-util@0.10.2': + resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} '@types/aria-query@5.0.4': resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} @@ -3006,16 +2888,16 @@ packages: babel-plugin-react-compiler: optional: true - '@vitest/browser-playwright@4.1.3': - resolution: {integrity: sha512-D3Q+YozvSpiFaLPgd6/OMbyqEIZeucSe6AHJJ7VnNJKQhIyBE60TlBZlxzwM8bvjzQE9ZnYWQCPeCw5pnhbiNg==} + '@vitest/browser-playwright@4.1.5': + resolution: {integrity: sha512-CWy0lBQJq97nionyJJdnaU4961IXTl43a7UCu5nHy51IoKxAt6PVIJLo+76rVl7KOOgcWHNkG4kbJu/pW7knvA==} peerDependencies: playwright: '*' - vitest: 4.1.3 + vitest: 4.1.5 - '@vitest/browser@4.1.3': - resolution: {integrity: sha512-CS9KjO2vijuBlbwz0JIgC0YuoI1BuqWI5ziD3Nll6jkpNYtWdjPMVgGynQ9vZovjsECeUqEeNjWrypP414d0CQ==} + '@vitest/browser@4.1.5': + resolution: {integrity: sha512-iCDGI8c4yg+xmjUg2VsygdAUSIIB4x5Rht/P68OXy1hPELKXHDkzh87lkuTcdYmemRChDkEpB426MmDjzC0ziA==} peerDependencies: - vitest: 4.1.3 + vitest: 4.1.5 '@vitest/coverage-v8@4.1.5': resolution: {integrity: sha512-38C0/Ddb7HcRG0Z4/DUem8x57d2p9jYgp18mkaYswEOQBGsI1CG4f/hjm0ZCeaJfWhSZ4k7jgs29V1Zom7Ki9A==} @@ -3048,17 +2930,6 @@ packages: '@vitest/expect@4.1.5': resolution: {integrity: sha512-PWBaRY5JoKuRnHlUHfpV/KohFylaDZTupcXN1H9vYryNLOnitSw60Mw9IAE2r67NbwwzBw/Cc/8q9BK3kIX8Kw==} - '@vitest/mocker@4.1.3': - resolution: {integrity: sha512-XN3TrycitDQSzGRnec/YWgoofkYRhouyVQj4YNsJ5r/STCUFqMrP4+oxEv3e7ZbLi4og5kIHrZwekDJgw6hcjw==} - peerDependencies: - msw: ^2.4.9 - vite: ^6.0.0 || ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - msw: - optional: true - vite: - optional: true - '@vitest/mocker@4.1.5': resolution: {integrity: sha512-/x2EmFC4mT4NNzqvC3fmesuV97w5FC903KPmey4gsnJiMQ3Be1IlDKVaDaG8iqaLFHqJ2FVEkxZk5VmeLjIItw==} peerDependencies: @@ -3073,9 +2944,6 @@ packages: '@vitest/pretty-format@3.2.4': resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} - '@vitest/pretty-format@4.1.3': - resolution: {integrity: sha512-hYqqwuMbpkkBodpRh4k4cQSOELxXky1NfMmQvOfKvV8zQHz8x8Dla+2wzElkMkBvSAJX5TRGHJAQvK0TcOafwg==} - '@vitest/pretty-format@4.1.5': resolution: {integrity: sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g==} @@ -3088,18 +2956,12 @@ packages: '@vitest/spy@3.2.4': resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} - '@vitest/spy@4.1.3': - resolution: {integrity: sha512-ujj5Uwxagg4XUIfAUyRQxAg631BP6e9joRiN99mr48Bg9fRs+5mdUElhOoZ6rP5mBr8Bs3lmrREnkrQWkrsTCw==} - '@vitest/spy@4.1.5': resolution: {integrity: sha512-2lNOsh6+R2Idnf1TCZqSwYlKN2E/iDlD8sgU59kYVl+OMDmvldO1VDk39smRfpUNwYpNRVn3w4YfuC7KfbBnkQ==} '@vitest/utils@3.2.4': resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} - '@vitest/utils@4.1.3': - resolution: {integrity: sha512-Pc/Oexse/khOWsGB+w3q4yzA4te7W4gpZZAvk+fr8qXfTURZUMj5i7kuxsNK5mP/dEB6ao3jfr0rs17fHhbHdw==} - '@vitest/utils@4.1.5': resolution: {integrity: sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==} @@ -7260,14 +7122,14 @@ snapshots: dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 - '@tybys/wasm-util': 0.10.1 + '@tybys/wasm-util': 0.10.2 optional: true '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 - '@tybys/wasm-util': 0.10.1 + '@tybys/wasm-util': 0.10.2 optional: true '@nodelib/fs.scandir@2.1.5': @@ -7574,18 +7436,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.4) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.4) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.14)(react@19.2.4)': dependencies: react: 19.2.4 @@ -7598,34 +7448,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.4) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.4) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.14)(react@19.2.4) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.4) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.4) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.4) - aria-hidden: 1.2.6 - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) - react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@19.2.4) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - - '@radix-ui/react-direction@1.1.1(@types/react@19.2.14)(react@19.2.4)': - dependencies: - react: 19.2.4 - optionalDependencies: - '@types/react': 19.2.14 - '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7639,21 +7461,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.4) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.4) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.4) - '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.14)(react@19.2.4)': dependencies: react: 19.2.4 @@ -7678,32 +7485,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.4) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.4) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.4) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.14)(react@19.2.4) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.4) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.4) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.4) - aria-hidden: 1.2.6 - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) - react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@19.2.4) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7774,32 +7555,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-primitive@2.1.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': - dependencies: - '@radix-ui/react-slot': 1.2.4(@types/react@19.2.14)(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - - '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.4) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.4) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.4) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.4) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.4) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-slot@1.2.3(@types/react@19.2.14)(react@19.2.4)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.4) @@ -7807,13 +7562,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-slot@1.2.4(@types/react@19.2.14)(react@19.2.4)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.4) - react: 19.2.4 - optionalDependencies: - '@types/react': 19.2.14 - '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7891,15 +7639,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-visually-hidden@1.2.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': - dependencies: - '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/rect@1.1.1': {} '@rolldown/binding-android-arm64@1.0.0-rc.12': @@ -8057,15 +7796,16 @@ snapshots: - vite - webpack - '@storybook/addon-vitest@10.3.5(@vitest/browser-playwright@4.1.3)(@vitest/runner@4.1.5)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vitest@4.1.5)': + '@storybook/addon-vitest@10.3.5(@vitest/browser-playwright@4.1.5)(@vitest/browser@4.1.5)(@vitest/runner@4.1.5)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vitest@4.1.5)': dependencies: '@storybook/global': 5.0.0 '@storybook/icons': 2.0.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) storybook: 10.3.5(@testing-library/dom@10.4.1)(prettier@2.8.8)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) optionalDependencies: - '@vitest/browser-playwright': 4.1.3(playwright@1.59.1)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3))(vitest@4.1.5) + '@vitest/browser': 4.1.5(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3))(vitest@4.1.5) + '@vitest/browser-playwright': 4.1.5(playwright@1.59.1)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3))(vitest@4.1.5) '@vitest/runner': 4.1.5 - vitest: 4.1.5(@types/node@25.5.2)(@vitest/browser-playwright@4.1.3)(@vitest/coverage-v8@4.1.5)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) + vitest: 4.1.5(@types/node@25.5.2)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) transitivePeerDependencies: - react - react-dom @@ -8216,20 +7956,18 @@ snapshots: dependencies: '@testing-library/dom': 10.4.1 - '@tsdown/css@0.21.7(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(jiti@2.6.1)(postcss-modules@6.0.1(postcss@8.5.10))(postcss@8.5.10)(sass-embedded@1.99.0)(sass@1.99.0)(tsdown@0.21.10)(yaml@2.8.3)': + '@tsdown/css@0.21.10(jiti@2.6.1)(postcss-modules@6.0.1(postcss@8.5.10))(postcss@8.5.10)(sass-embedded@1.99.0)(sass@1.99.0)(tsdown@0.21.10)(yaml@2.8.3)': dependencies: lightningcss: 1.32.0 postcss-load-config: 6.0.1(jiti@2.6.1)(postcss@8.5.10)(yaml@2.8.3) - rolldown: 1.0.0-rc.12(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) - tsdown: 0.21.10(@arethetypeswrong/core@0.18.2)(@tsdown/css@0.21.7)(@typescript/native-preview@7.0.0-dev.20260426.1)(oxc-resolver@11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(publint@0.3.18)(typescript@6.0.3) + rolldown: 1.0.0-rc.17 + tsdown: 0.21.10(@arethetypeswrong/core@0.18.2)(@tsdown/css@0.21.10)(@typescript/native-preview@7.0.0-dev.20260426.1)(oxc-resolver@11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(publint@0.3.18)(typescript@6.0.3) optionalDependencies: postcss: 8.5.10 postcss-modules: 6.0.1(postcss@8.5.10) sass: 1.99.0 sass-embedded: 1.99.0 transitivePeerDependencies: - - '@emnapi/core' - - '@emnapi/runtime' - jiti - tsx - yaml @@ -8252,7 +7990,7 @@ snapshots: '@turbo/windows-arm64@2.9.5': optional: true - '@tybys/wasm-util@0.10.1': + '@tybys/wasm-util@0.10.2': dependencies: tslib: 2.8.1 optional: true @@ -8545,37 +8283,69 @@ snapshots: '@rolldown/plugin-babel': 0.2.2(@babel/core@7.29.0)(@babel/runtime@7.29.2)(rolldown@1.0.0-rc.17)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) babel-plugin-react-compiler: 1.0.0 - '@vitest/browser-playwright@4.1.3(playwright@1.59.1)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3))(vitest@4.1.5)': + '@vitest/browser-playwright@4.1.5(playwright@1.59.1)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3))(vitest@4.1.5)': + dependencies: + '@vitest/browser': 4.1.5(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3))(vitest@4.1.5) + '@vitest/mocker': 4.1.5(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) + playwright: 1.59.1 + tinyrainbow: 3.1.0 + vitest: 4.1.5(@types/node@25.5.2)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + + '@vitest/browser-playwright@4.1.5(playwright@1.59.1)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3))(vitest@4.1.5)': dependencies: - '@vitest/browser': 4.1.3(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3))(vitest@4.1.5) - '@vitest/mocker': 4.1.3(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) + '@vitest/browser': 4.1.5(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3))(vitest@4.1.5) + '@vitest/mocker': 4.1.5(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) playwright: 1.59.1 tinyrainbow: 3.1.0 - vitest: 4.1.5(@types/node@25.5.2)(@vitest/browser-playwright@4.1.3)(@vitest/coverage-v8@4.1.5)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) + vitest: 4.1.5(@types/node@25.5.2)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite + optional: true - '@vitest/browser@4.1.3(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3))(vitest@4.1.5)': + '@vitest/browser@4.1.5(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3))(vitest@4.1.5)': dependencies: '@blazediff/core': 1.9.1 - '@vitest/mocker': 4.1.3(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) - '@vitest/utils': 4.1.3 + '@vitest/mocker': 4.1.5(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) + '@vitest/utils': 4.1.5 + magic-string: 0.30.21 + pngjs: 7.0.0 + sirv: 3.0.2 + tinyrainbow: 3.1.0 + vitest: 4.1.5(@types/node@25.5.2)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) + ws: 8.20.0 + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + + '@vitest/browser@4.1.5(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3))(vitest@4.1.5)': + dependencies: + '@blazediff/core': 1.9.1 + '@vitest/mocker': 4.1.5(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) + '@vitest/utils': 4.1.5 magic-string: 0.30.21 pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.1.0 - vitest: 4.1.5(@types/node@25.5.2)(@vitest/browser-playwright@4.1.3)(@vitest/coverage-v8@4.1.5)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) + vitest: 4.1.5(@types/node@25.5.2)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) ws: 8.20.0 transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite + optional: true - '@vitest/coverage-v8@4.1.5(vitest@4.1.5)': + '@vitest/coverage-v8@4.1.5(@vitest/browser@4.1.5)(vitest@4.1.5)': dependencies: '@bcoe/v8-coverage': 1.0.2 '@vitest/utils': 4.1.5 @@ -8587,7 +8357,9 @@ snapshots: obug: 2.1.1 std-env: 4.1.0 tinyrainbow: 3.1.0 - vitest: 4.1.5(@types/node@25.5.2)(@vitest/coverage-v8@4.1.5)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) + vitest: 4.1.5(@types/node@25.5.2)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) + optionalDependencies: + '@vitest/browser': 4.1.5(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3))(vitest@4.1.5) '@vitest/eslint-plugin@1.6.14(@typescript-eslint/eslint-plugin@8.58.2(@typescript-eslint/parser@8.58.2(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.3))(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.3))(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.3)(vitest@4.1.5)': dependencies: @@ -8597,7 +8369,7 @@ snapshots: optionalDependencies: '@typescript-eslint/eslint-plugin': 8.58.2(@typescript-eslint/parser@8.58.2(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.3))(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.3) typescript: 6.0.3 - vitest: 4.1.5(@types/node@25.5.2)(@vitest/coverage-v8@4.1.5)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) + vitest: 4.1.5(@types/node@25.5.2)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) transitivePeerDependencies: - supports-color @@ -8618,14 +8390,6 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.3(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3))': - dependencies: - '@vitest/spy': 4.1.3 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - vite: 8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3) - '@vitest/mocker@4.1.5(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3))': dependencies: '@vitest/spy': 4.1.5 @@ -8646,10 +8410,6 @@ snapshots: dependencies: tinyrainbow: 2.0.0 - '@vitest/pretty-format@4.1.3': - dependencies: - tinyrainbow: 3.1.0 - '@vitest/pretty-format@4.1.5': dependencies: tinyrainbow: 3.1.0 @@ -8670,8 +8430,6 @@ snapshots: dependencies: tinyspy: 4.0.4 - '@vitest/spy@4.1.3': {} - '@vitest/spy@4.1.5': {} '@vitest/utils@3.2.4': @@ -8680,12 +8438,6 @@ snapshots: loupe: 3.2.1 tinyrainbow: 2.0.0 - '@vitest/utils@4.1.3': - dependencies: - '@vitest/pretty-format': 4.1.3 - convert-source-map: 2.0.0 - tinyrainbow: 3.1.0 - '@vitest/utils@4.1.5': dependencies: '@vitest/pretty-format': 4.1.5 @@ -11880,7 +11632,7 @@ snapshots: minimist: 1.2.8 strip-bom: 3.0.0 - tsdown@0.21.10(@arethetypeswrong/core@0.18.2)(@tsdown/css@0.21.7)(@typescript/native-preview@7.0.0-dev.20260426.1)(oxc-resolver@11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(publint@0.3.18)(typescript@6.0.3): + tsdown@0.21.10(@arethetypeswrong/core@0.18.2)(@tsdown/css@0.21.10)(@typescript/native-preview@7.0.0-dev.20260426.1)(oxc-resolver@11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(publint@0.3.18)(typescript@6.0.3): dependencies: ansis: 4.2.0 cac: 7.0.0 @@ -11900,7 +11652,7 @@ snapshots: unrun: 0.2.37 optionalDependencies: '@arethetypeswrong/core': 0.18.2 - '@tsdown/css': 0.21.7(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(jiti@2.6.1)(postcss-modules@6.0.1(postcss@8.5.10))(postcss@8.5.10)(sass-embedded@1.99.0)(sass@1.99.0)(tsdown@0.21.10)(yaml@2.8.3) + '@tsdown/css': 0.21.10(jiti@2.6.1)(postcss-modules@6.0.1(postcss@8.5.10))(postcss@8.5.10)(sass-embedded@1.99.0)(sass@1.99.0)(tsdown@0.21.10)(yaml@2.8.3) publint: 0.3.18 typescript: 6.0.3 transitivePeerDependencies: @@ -12157,12 +11909,12 @@ snapshots: dependencies: react: 19.2.4 react-dom: 19.2.4(react@19.2.4) - vitest: 4.1.5(@types/node@25.5.2)(@vitest/browser-playwright@4.1.3)(@vitest/coverage-v8@4.1.5)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) + vitest: 4.1.5(@types/node@25.5.2)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) optionalDependencies: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - vitest@4.1.5(@types/node@25.5.2)(@vitest/browser-playwright@4.1.3)(@vitest/coverage-v8@4.1.5)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)): + vitest@4.1.5(@types/node@25.5.2)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)): dependencies: '@vitest/expect': 4.1.5 '@vitest/mocker': 4.1.5(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) @@ -12186,12 +11938,12 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 25.5.2 - '@vitest/browser-playwright': 4.1.3(playwright@1.59.1)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3))(vitest@4.1.5) - '@vitest/coverage-v8': 4.1.5(vitest@4.1.5) + '@vitest/browser-playwright': 4.1.5(playwright@1.59.1)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.17.19)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3))(vitest@4.1.5) + '@vitest/coverage-v8': 4.1.5(@vitest/browser@4.1.5)(vitest@4.1.5) transitivePeerDependencies: - msw - vitest@4.1.5(@types/node@25.5.2)(@vitest/coverage-v8@4.1.5)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)): + vitest@4.1.5(@types/node@25.5.2)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)): dependencies: '@vitest/expect': 4.1.5 '@vitest/mocker': 4.1.5(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3)) @@ -12215,7 +11967,8 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 25.5.2 - '@vitest/coverage-v8': 4.1.5(vitest@4.1.5) + '@vitest/browser-playwright': 4.1.5(playwright@1.59.1)(vite@8.0.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.5.2)(esbuild@0.27.7)(jiti@2.6.1)(less@4.6.4)(sass-embedded@1.99.0)(sass@1.99.0)(stylus@0.62.0)(yaml@2.8.3))(vitest@4.1.5) + '@vitest/coverage-v8': 4.1.5(@vitest/browser@4.1.5)(vitest@4.1.5) transitivePeerDependencies: - msw