From 1a632f1f09b23b6c0b5cea1164fff05aff3e74ad Mon Sep 17 00:00:00 2001 From: Micky Date: Tue, 14 Apr 2026 14:22:57 +0200 Subject: [PATCH 1/7] refactor(design-system): remove DsDialog and DsConfirmation [AR-53409] (#344) --- .changeset/ripe-ravens-sleep.md | 7 + .../ds-confirmation.stories.module.scss | 31 --- .../ds-confirmation.stories.tsx | 257 ------------------ .../ds-confirmation/ds-confirmation.tsx | 17 -- .../ds-confirmation/ds-confirmation.types.ts | 29 -- .../src/components/ds-confirmation/index.ts | 2 - .../ds-dialog/ds-dialog.module.scss | 50 ---- .../ds-dialog/ds-dialog.stories.module.scss | 15 - .../ds-dialog/ds-dialog.stories.tsx | 125 --------- .../src/components/ds-dialog/ds-dialog.tsx | 70 ----- .../components/ds-dialog/ds-dialog.types.ts | 57 ---- .../src/components/ds-dialog/index.ts | 2 - packages/design-system/src/index.ts | 2 - packages/design-system/vitest.config.ts | 1 - .../src/__tests__/no-deprecated.test.ts | 99 ------- packages/eslint-plugin/src/index.ts | 30 -- 16 files changed, 7 insertions(+), 787 deletions(-) create mode 100644 .changeset/ripe-ravens-sleep.md delete mode 100644 packages/design-system/src/components/ds-confirmation/ds-confirmation.stories.module.scss delete mode 100644 packages/design-system/src/components/ds-confirmation/ds-confirmation.stories.tsx delete mode 100644 packages/design-system/src/components/ds-confirmation/ds-confirmation.tsx delete mode 100644 packages/design-system/src/components/ds-confirmation/ds-confirmation.types.ts delete mode 100644 packages/design-system/src/components/ds-confirmation/index.ts delete mode 100644 packages/design-system/src/components/ds-dialog/ds-dialog.module.scss delete mode 100644 packages/design-system/src/components/ds-dialog/ds-dialog.stories.module.scss delete mode 100644 packages/design-system/src/components/ds-dialog/ds-dialog.stories.tsx delete mode 100644 packages/design-system/src/components/ds-dialog/ds-dialog.tsx delete mode 100644 packages/design-system/src/components/ds-dialog/ds-dialog.types.ts delete mode 100644 packages/design-system/src/components/ds-dialog/index.ts 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/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-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/index.ts b/packages/design-system/src/index.ts index be0efec60..b769a7060 100644 --- a/packages/design-system/src/index.ts +++ b/packages/design-system/src/index.ts @@ -14,11 +14,9 @@ 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'; diff --git a/packages/design-system/vitest.config.ts b/packages/design-system/vitest.config.ts index 0e8cd96f0..d97ca94cb 100644 --- a/packages/design-system/vitest.config.ts +++ b/packages/design-system/vitest.config.ts @@ -23,7 +23,6 @@ export default defineConfig({ // deprecated components '**/ds-chip/**', '**/ds-chip-group/**', - '**/ds-confirmation/**', '**/ds-date-input/**', '**/ds-system-status/**', ], diff --git a/packages/eslint-plugin/src/__tests__/no-deprecated.test.ts b/packages/eslint-plugin/src/__tests__/no-deprecated.test.ts index 92922f8b2..0bcc6b2de 100644 --- a/packages/eslint-plugin/src/__tests__/no-deprecated.test.ts +++ b/packages/eslint-plugin/src/__tests__/no-deprecated.test.ts @@ -53,105 +53,6 @@ ruleTester.run('no-deprecated-ds-button-empty-design', plugin.rules['no-deprecat ], }); -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, - }, - ], - }, - ], -}); - -ruleTester.run('no-deprecated-ds-confirmation', plugin.rules['no-deprecated-ds-confirmation'], { - valid: [''], - - invalid: [ - { - code: '', - errors: [ - { - message: `DsConfirmation is deprecated. Use DsModal instead.`, - line: 1, - endLine: 1, - column: 2, - endColumn: 16, - }, - ], - }, - ], -}); - -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: ['', ''], diff --git a/packages/eslint-plugin/src/index.ts b/packages/eslint-plugin/src/index.ts index afa3524f7..5251dcd18 100644 --- a/packages/eslint-plugin/src/index.ts +++ b/packages/eslint-plugin/src/index.ts @@ -15,36 +15,6 @@ const eslintPlugin = createPlugin( 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'), From dc859aee7c99804c8868e324b382934ad0a4718b Mon Sep 17 00:00:00 2001 From: Micky Date: Wed, 15 Apr 2026 09:52:55 +0200 Subject: [PATCH 2/7] refactor(design-system)!: remove deprecated DsDropdownMenuLegacy and DsRadioGroupLegacy [AR-53409] (#340) --- .changeset/fluffy-clubs-greet.md | 7 + packages/design-system/package.json | 1 - .../ds-dropdown-menu-legacy.stories.tsx | 99 ------------- .../ds-dropdown-menu.module.scss | 18 --- .../ds-dropdown-menu/ds-dropdown-menu.tsx | 92 ------------ .../ds-dropdown-menu.types.ts | 89 +---------- .../src/components/ds-dropdown-menu/index.ts | 2 +- .../ds-radio-group/ds-radio-group.tsx | 58 +------- .../ds-radio-group/ds-radio-group.types.ts | 61 -------- .../src/components/ds-radio-group/index.ts | 2 +- pnpm-lock.yaml | 140 ------------------ 11 files changed, 11 insertions(+), 558 deletions(-) create mode 100644 .changeset/fluffy-clubs-greet.md delete mode 100644 packages/design-system/src/components/ds-dropdown-menu/ds-dropdown-menu-legacy.stories.tsx 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/packages/design-system/package.json b/packages/design-system/package.json index 4f47226a3..291c1024d 100644 --- a/packages/design-system/package.json +++ b/packages/design-system/package.json @@ -61,7 +61,6 @@ "@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", diff --git a/packages/design-system/src/components/ds-dropdown-menu/ds-dropdown-menu-legacy.stories.tsx b/packages/design-system/src/components/ds-dropdown-menu/ds-dropdown-menu-legacy.stories.tsx deleted file mode 100644 index 22e690805..000000000 --- a/packages/design-system/src/components/ds-dropdown-menu/ds-dropdown-menu-legacy.stories.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react-vite'; -import { expect, screen, userEvent, within } from 'storybook/test'; -import { DsDropdownMenuLegacy } from './ds-dropdown-menu'; -import './ds-dropdown-menu.stories.scss'; -import { DsIcon } from '../ds-icon'; - -const meta: Meta = { - title: 'Components/DropdownMenuLegacy (Deprecated)', - component: DsDropdownMenuLegacy, - parameters: { - layout: 'centered', - }, - tags: ['deprecated'], - argTypes: { - children: { - control: 'text', - description: 'Content to display inside the component', - }, - contentGap: { - control: 'number', - description: 'The gap between the trigger and dropdown content in pixels', - }, - }, -}; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - parameters: { - docs: { - description: { - story: - 'Dropdown menu with a custom trigger styled to match the design. The menu items can include icons and can be disabled.', - }, - }, - }, - args: { - options: [ - { label: 'Edit', icon: 'edit', onClick: () => console.log('Edit clicked') }, - { label: 'Delete', icon: 'delete', onClick: () => console.log('Delete clicked') }, - { label: 'Share', icon: 'share', onClick: () => console.log('Share clicked') }, - { - label: 'Disabled Option', - icon: 'block', - disabled: true, - onClick: () => console.log('Disabled clicked'), - }, - ], - contentGap: 4, - }, - render: function Render(args) { - return ( - -
- Actions - -
-
- ); - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - - // Check initial state - await expect(canvas.getByText('Actions')).toBeInTheDocument(); - - // Open dropdown menu - await userEvent.click(canvas.getByText('Actions')); - - // Check all menu items are present - await expect(screen.getByRole('menuitem', { name: /Edit/ })).toBeInTheDocument(); - await expect(screen.getByRole('menuitem', { name: /Delete/ })).toBeInTheDocument(); - await expect(screen.getByRole('menuitem', { name: /Share/ })).toBeInTheDocument(); - await expect(screen.getByRole('menuitem', { name: /Disabled Option/ })).toBeInTheDocument(); - - // Check disabled state - const disabledOption = screen.getByRole('menuitem', { name: /Disabled Option/ }); - await expect(disabledOption).toHaveAttribute('aria-disabled', 'true'); - - // Click an option - await userEvent.click(screen.getByRole('menuitem', { name: /Edit/ })); - - // Close dropdown with Escape key - await userEvent.keyboard('{Escape}'); - - // Open dropdown again - await userEvent.click(canvas.getByText('Actions')); - - // Check all options are shown again - await expect(screen.getByRole('menuitem', { name: /Edit/ })).toBeInTheDocument(); - await expect(screen.getByRole('menuitem', { name: /Delete/ })).toBeInTheDocument(); - await expect(screen.getByRole('menuitem', { name: /Share/ })).toBeInTheDocument(); - await expect(screen.getByRole('menuitem', { name: /Disabled Option/ })).toBeInTheDocument(); - - // Close dropdown with Escape key - await userEvent.keyboard('{Escape}'); - }, -}; diff --git a/packages/design-system/src/components/ds-dropdown-menu/ds-dropdown-menu.module.scss b/packages/design-system/src/components/ds-dropdown-menu/ds-dropdown-menu.module.scss index 059b8d05e..3ad027d63 100644 --- a/packages/design-system/src/components/ds-dropdown-menu/ds-dropdown-menu.module.scss +++ b/packages/design-system/src/components/ds-dropdown-menu/ds-dropdown-menu.module.scss @@ -1,4 +1,3 @@ -@use '../../styles/typography'; @use '../../styles/shared/dropdown'; .content { @@ -111,20 +110,3 @@ .triggerItemIcon { margin-left: auto; } - -/** Legacy classes */ -.contentLegacy { - @include dropdown.dropdown-content; - min-width: 200px; - width: var(--radix-select-trigger-width); -} - -.viewportLegacy { - @include dropdown.dropdown-viewport; -} - -.itemLegacy { - @include dropdown.dropdown-item; - @include typography.p-special; - @include typography.ellipsis; -} diff --git a/packages/design-system/src/components/ds-dropdown-menu/ds-dropdown-menu.tsx b/packages/design-system/src/components/ds-dropdown-menu/ds-dropdown-menu.tsx index de231c810..ebd76db8e 100644 --- a/packages/design-system/src/components/ds-dropdown-menu/ds-dropdown-menu.tsx +++ b/packages/design-system/src/components/ds-dropdown-menu/ds-dropdown-menu.tsx @@ -1,13 +1,11 @@ import type React from 'react'; import { createContext, Fragment, useContext, useState } from 'react'; -import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; import { Menu } from '@ark-ui/react/menu'; import { Portal } from '@ark-ui/react/portal'; import classNames from 'classnames'; import styles from './ds-dropdown-menu.module.scss'; import { DsIcon } from '../ds-icon'; import { DsTypography } from '../ds-typography'; -import { DsTextInput } from '../ds-text-input'; import type { DsDropdownMenuActionsProps, DsDropdownMenuContentProps, @@ -17,7 +15,6 @@ import type { DsDropdownMenuItemGroupProps, DsDropdownMenuItemIndicatorProps, DsDropdownMenuItemProps, - DsDropdownMenuLegacyProps, DsDropdownMenuPositioning, DsDropdownMenuRootProps, DsDropdownMenuSeparatorProps, @@ -279,95 +276,6 @@ const TriggerItem: React.FC = ({ className, styl ); }; -/** - * DEPRECATED: Legacy DsDropdownMenu component with options array - * Use compound component pattern instead - * @deprecated - */ -/* c8 ignore start */ -export const DsDropdownMenuLegacy: React.FC = ({ - options, - children, - contentGap = 0, - className, - style, - align = 'center', - side = 'bottom', - disablePortal = false, - disableSearch = true, - selected, - onSelect, -}) => { - const [open, setOpen] = useState(false); - const [searchTerm, setSearchTerm] = useState(''); - - const Wrapper = disablePortal ? Fragment : DropdownMenu.Portal; - - const filteredOptions = disableSearch - ? options - : options.filter((option) => option.label.toLowerCase().includes(searchTerm.toLowerCase())); - - return ( - - {children} - - - {!disableSearch && ( - , - }} - onKeyDown={(e) => e.stopPropagation()} - /> - )} - {filteredOptions.map((option, i) => ( - { - e.stopPropagation(); - - if (!option.disabled) { - if (option.value) { - onSelect?.(option.value); - } - option.onClick?.(e); - setOpen(false); - } - }} - > - {option.icon && } - {option.label} - {option.value && selected === option.value && ( - - - - )} - - ))} - - - - ); -}; -/* c8 ignore stop */ - /** * Design system DsDropdownMenu component * diff --git a/packages/design-system/src/components/ds-dropdown-menu/ds-dropdown-menu.types.ts b/packages/design-system/src/components/ds-dropdown-menu/ds-dropdown-menu.types.ts index aa0c1abcd..ceb8670b2 100644 --- a/packages/design-system/src/components/ds-dropdown-menu/ds-dropdown-menu.types.ts +++ b/packages/design-system/src/components/ds-dropdown-menu/ds-dropdown-menu.types.ts @@ -1,6 +1,5 @@ -import type { CSSProperties, MouseEvent, ReactNode } from 'react'; +import type { CSSProperties, ReactNode } from 'react'; import type { Menu } from '@ark-ui/react/menu'; -import type { IconType } from '../ds-icon'; /** * Base positioning type @@ -15,92 +14,6 @@ export type DsDropdownMenuPositioning = Pick< 'placement' | 'gutter' | 'sameWidth' | 'getAnchorRect' >; -/** - * DEPRECATED: Legacy dropdown menu option configuration - * Use compound component pattern instead - * @deprecated - */ -export interface DsDropdownMenuOptionLegacy { - /** - * Display label for the menu option - */ - label: string; - /** - * Optional icon to display - */ - icon?: IconType; - /** - * Whether this option is disabled - */ - disabled?: boolean; - /** - * Click handler for the option - */ - onClick?: (e: MouseEvent) => void; - /** - * Optional value for selection tracking - */ - value?: string; -} - -/** - * DEPRECATED: Legacy props for DsDropdownMenuLegacy component - * Use compound component pattern instead - * @deprecated - */ -export interface DsDropdownMenuLegacyProps { - /** - * The options to be displayed in the dropdown menu - */ - options: DsDropdownMenuOptionLegacy[]; - /** - * Optional children to be rendered inside the component - * Typically used for the trigger element - */ - children?: ReactNode | undefined; - /** - * The gap between the trigger and dropdown content in pixels - * @default 0 - */ - contentGap?: number; - /** - * Additional CSS class names - */ - className?: string; - /** - * Optional inline styles to apply to the component - */ - style?: CSSProperties; - /** - * The alignment of the dropdown content - * @default 'center' - */ - align?: 'start' | 'center' | 'end'; - /** - * The side of the dropdown content - * @default 'bottom' - */ - side?: 'top' | 'right' | 'bottom' | 'left'; - /** - * Whether to render in place instead of using portals - * @default false - */ - disablePortal?: boolean; - /** - * Whether to disable the search functionality - * @default false - */ - disableSearch?: boolean; - /** - * Currently selected value (for selection tracking) - */ - selected?: string; - /** - * Callback when an option with a value is selected - */ - onSelect?: (value: string) => void; -} - /** * Props for the DsDropdownMenu Root component */ diff --git a/packages/design-system/src/components/ds-dropdown-menu/index.ts b/packages/design-system/src/components/ds-dropdown-menu/index.ts index 3a6c15bb8..c12c6088a 100644 --- a/packages/design-system/src/components/ds-dropdown-menu/index.ts +++ b/packages/design-system/src/components/ds-dropdown-menu/index.ts @@ -1,2 +1,2 @@ -export { DsDropdownMenu, DsDropdownMenuLegacy } from './ds-dropdown-menu'; +export { DsDropdownMenu } from './ds-dropdown-menu'; export * from './ds-dropdown-menu.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/pnpm-lock.yaml b/pnpm-lock.yaml index ae3ea67b7..371fdfcfc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -202,9 +202,6 @@ importers: '@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) @@ -1909,19 +1906,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: @@ -1953,15 +1937,6 @@ packages: '@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 +1950,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 +1981,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: @@ -2110,19 +2059,6 @@ packages: '@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: @@ -7574,18 +7510,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 @@ -7620,12 +7544,6 @@ snapshots: '@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 +7557,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 +7581,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 @@ -7783,23 +7660,6 @@ snapshots: '@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) From 1d65eae94322d173816632018dc41b7dc477785f Mon Sep 17 00:00:00 2001 From: Micky Date: Wed, 15 Apr 2026 14:19:53 +0200 Subject: [PATCH 3/7] refactor(design-system)!: remove deprecated DsSystemStatus component [AR-53409] (#339) --- .changeset/loose-stamps-study.md | 7 ++ .../ds-button-legacy.module.scss | 22 ++--- .../ds-drawer/ds-drawer.stories.tsx | 7 +- .../ds-system-status.module.scss | 87 ------------------- .../ds-system-status.stories.ts | 48 ---------- .../ds-system-status/ds-system-status.tsx | 33 ------- .../ds-system-status.types.ts | 37 -------- .../src/components/ds-system-status/index.ts | 10 --- .../progress-infographic.module.scss | 6 +- .../stories/ds-table.stories.module.scss | 8 +- packages/design-system/src/index.ts | 1 - .../design-system/src/styles/_colors.scss | 7 -- packages/design-system/vitest.config.ts | 1 - 13 files changed, 28 insertions(+), 246 deletions(-) create mode 100644 .changeset/loose-stamps-study.md delete mode 100644 packages/design-system/src/components/ds-system-status/ds-system-status.module.scss delete mode 100644 packages/design-system/src/components/ds-system-status/ds-system-status.stories.ts delete mode 100644 packages/design-system/src/components/ds-system-status/ds-system-status.tsx delete mode 100644 packages/design-system/src/components/ds-system-status/ds-system-status.types.ts delete mode 100644 packages/design-system/src/components/ds-system-status/index.ts 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/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 index 91656561d..65e84c617 100644 --- 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 @@ -1,21 +1,21 @@ @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-primary-default: var(--action-cta1); +$color-primary-hover: var(--action-hover-light); +$color-primary-active: var(--action-active-light); +$color-primary-disabled: var(--action-disabled); -$color-secondary-default: var(--color-dap-brand-300); +$color-secondary-default: var(--action-cta3); $color-secondary-hover: var(--color-dap-gray-500); -$color-secondary-disabled: var(--color-dap-gray-400); +$color-secondary-disabled: var(--action-disabled); -$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-error-default: var(--background-background-negative); +$color-error-hover: var(--utility-error); +$color-error-active: var(--utility-error); +$color-error-disabled: var(--action-disabled); $color-white: var(--color-dap-gray-050); -$color-neutral-6: var(--color-dap-gray-200); +$color-neutral-6: var(--utility-disabled); $color-neutral-4: var(--color-dap-gray-500); @mixin button-filled($background, $hover, $active, $disabled) { 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..e93884203 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
-

Total filters: {filters.length}

-

- Selected filters: [ - {filters - .filter((filter) => filter.selected) - .map((filter) => `"${filter.label}"`) - .join(', ')} - ] -

-
-
- ); - }, -}; diff --git a/packages/design-system/src/components/ds-chip-group/ds-chip-group.tsx b/packages/design-system/src/components/ds-chip-group/ds-chip-group.tsx deleted file mode 100644 index f11738afb..000000000 --- a/packages/design-system/src/components/ds-chip-group/ds-chip-group.tsx +++ /dev/null @@ -1,122 +0,0 @@ -import type React from 'react'; -import { useRef, useState } 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-chip-group.module.scss'; -import type { DsChipGroupProps } from './ds-chip-group.types'; -import { useChipRowCalculation } from './hooks/use-chip-row-calculation'; -import { DsTypography } from '../ds-typography'; -import { DsChip } from '../ds-chip'; -import { DsCheckbox } from '../ds-checkbox'; -import { DsButton } from '../ds-button'; -import { DsIcon } from '../ds-icon'; - -/** - * @deprecated This component is deprecated. Use `DsTagFilter` instead. - * @see {@link ../ds-tag-filter} for the replacement component. - */ -const DsChipGroup: React.FC = ({ - items, - label = 'Filtered by:', - onClearAll, - onItemDelete, - onItemSelect, - className, - style, -}) => { - const [dialogOpen, setDialogOpen] = useState(false); - const showAllChipsRef = useRef(null); - const chipsWrapperRef = useRef(null); - const visibleCount = useChipRowCalculation({ - chipsWrapperRef, - totalFilters: items.length, - }); - - if (items.length === 0) { - return null; - } - - const hiddenCount = items.length - visibleCount; - const showExpandButton = hiddenCount > 0; - - const handleOpenDialog = () => { - setDialogOpen(true); - }; - - let dialogStyle: React.CSSProperties = {}; - if (showExpandButton && showAllChipsRef.current) { - const rect = showAllChipsRef.current.getBoundingClientRect(); - dialogStyle = { - position: 'fixed', - top: rect.bottom + 4, - left: rect.left, - }; - } - - return ( -
- {label && ( - - {label} - - )} - -
- {items.map((item, index) => ( - onItemSelect?.(item)} - onDelete={() => onItemDelete?.(item)} - className={classNames({ - [styles.hidden]: index >= visibleCount, - })} - /> - ))} - {showExpandButton && ( - - )} -
- - {onClearAll && ( - - - Clear all - - )} - - - - - - - -
- {items - .filter((item, index) => index >= visibleCount) - .map((item) => ( -
- onItemSelect?.(item)} - label={item.label} - /> -
- ))} -
-
-
-
-
- ); -}; - -export default DsChipGroup; diff --git a/packages/design-system/src/components/ds-chip-group/ds-chip-group.types.ts b/packages/design-system/src/components/ds-chip-group/ds-chip-group.types.ts deleted file mode 100644 index 6deec135d..000000000 --- a/packages/design-system/src/components/ds-chip-group/ds-chip-group.types.ts +++ /dev/null @@ -1,60 +0,0 @@ -import type { CSSProperties } from 'react'; - -/** - * @deprecated This interface is deprecated. Use `DsTagFilter` types instead. - * @see {@link ../ds-tag-filter} for the replacement types. - */ -export interface ChipItem { - /** - * Unique identifier for the chip - */ - id: string; - /** - * The label text to display in the chip - */ - label: string; - /** - * Additional metadata to store with the chip - */ - metadata?: Record; - /** - * Whether the chip is selected/checked - */ - selected?: boolean; -} - -/** - * @deprecated This interface is deprecated. Use `DsTagFilterProps` from `ds-tag-filter` instead. - * @see {@link ../ds-tag-filter/ds-tag-filter.types} for the replacement interface. - */ -export interface DsChipGroupProps { - /** - * Array of chip items to display - */ - items: ChipItem[]; - /** - * Label text to display before the chips (e.g., "Filtered by:") - * @default "Filtered by:" - */ - label?: string; - /** - * Callback when "Clear all" is clicked - */ - onClearAll?: () => void; - /** - * Callback when a chip is deleted/unchecked - */ - onItemDelete?: (item: ChipItem) => void; - /** - * Callback when a chip is selected - */ - onItemSelect?: (item: ChipItem) => void; - /** - * Additional CSS class names - */ - className?: string; - /** - * Additional styles to apply to the component - */ - style?: CSSProperties; -} diff --git a/packages/design-system/src/components/ds-chip-group/hooks/use-chip-row-calculation.ts b/packages/design-system/src/components/ds-chip-group/hooks/use-chip-row-calculation.ts deleted file mode 100644 index d53c99c76..000000000 --- a/packages/design-system/src/components/ds-chip-group/hooks/use-chip-row-calculation.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { type RefObject, useLayoutEffect, useState } from 'react'; - -interface UseChipRowCalculationProps { - chipsWrapperRef: RefObject; - totalFilters: number; -} - -/** - * Custom hook to calculate how many chips can fit in 2 rows - * Uses a simple approach: render all, measure positions, hide overflow - */ -export const useChipRowCalculation = ({ chipsWrapperRef, totalFilters }: UseChipRowCalculationProps) => { - const [visibleCount, setVisibleCount] = useState(totalFilters); - - useLayoutEffect(() => { - const calculateVisibleChips = () => { - if (!chipsWrapperRef.current) { - return; - } - - const wrapper = chipsWrapperRef.current; - const children = Array.from(wrapper.children) as HTMLElement[]; - - if (children.length === 0) { - return; - } - - const cs = getComputedStyle(wrapper); - const gap = parseFloat(cs.columnGap || cs.gap || '0') || 8; - let index; - let line = 0; - let total = 0; - - for (index = 0; index < children.length; index++) { - const current = children[index]; - - if (!current) { - continue; - } - - const offset = current.offsetWidth === wrapper.clientWidth ? 0 : gap; - const next = offset + current.offsetWidth; - - if (total + next >= wrapper.clientWidth) { - line++; - if (line >= 2) { - break; - } - total = next; - } else { - total += next; - } - } - - const result = Math.max(1, line < 2 ? index : index - 1); - setVisibleCount(result); - }; - - // Use requestAnimationFrame to ensure DOM is fully laid out - const rafId = requestAnimationFrame(() => { - calculateVisibleChips(); - }); - - const resizeObserver = new ResizeObserver(() => { - requestAnimationFrame(() => { - calculateVisibleChips(); - }); - }); - - if (chipsWrapperRef.current) { - resizeObserver.observe(chipsWrapperRef.current); - } - - return () => { - cancelAnimationFrame(rafId); - resizeObserver.disconnect(); - }; - }, [chipsWrapperRef, totalFilters]); - - return visibleCount; -}; diff --git a/packages/design-system/src/components/ds-chip-group/index.ts b/packages/design-system/src/components/ds-chip-group/index.ts deleted file mode 100644 index 133bd29d9..000000000 --- a/packages/design-system/src/components/ds-chip-group/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { default as DsChipGroup } from './ds-chip-group'; - -export type { DsChipGroupProps, ChipItem } from './ds-chip-group.types'; diff --git a/packages/design-system/src/components/ds-chip/ds-chip.module.scss b/packages/design-system/src/components/ds-chip/ds-chip.module.scss deleted file mode 100644 index dbf2cbde4..000000000 --- a/packages/design-system/src/components/ds-chip/ds-chip.module.scss +++ /dev/null @@ -1,71 +0,0 @@ -@use '../../styles/typography'; - -.chip { - display: inline-flex; - align-items: center; - gap: var(--3xs); - padding: 1px var(--3xs) 1px var(--xs); - background: var(--background); - border: 1px solid var(--border-contrast); - border-radius: 100vmax; - color: var(--font-main); - cursor: default; - overflow: hidden; - transition: - background-color 0.2s, - border-color 0.2s, - color 0.2s; - - &.clickable { - cursor: pointer; - - &:hover { - background: var(--background-action-hover-weak); - border-color: var(--border-action-secondary-hover); - } - - &:focus-visible { - outline: 2px solid var(--background-primary); - outline-offset: 2px; - } - } - - &.small { - padding: 0 var(--4xs) 0 var(--3xs); - gap: var(--4xs); - } - - &[aria-pressed='true'] { - background: var(--background-secondary-hover); - border-color: var(--border-action-primary); - color: var(--font-action); - } -} - -.label { - @include typography.ellipsis; - padding: var(--4xs) var(--3xs); - flex: 1; -} - -.deleteButton { - display: flex; - align-items: center; - justify-content: center; - border: none; - background: transparent; - color: var(--icon-information-main); - cursor: pointer; - transition: - background-color 0.2s, - color 0.2s; - flex-shrink: 0; - - &:hover { - color: var(--icon-action-secondary); - } - - .chip.small & { - padding: 1px var(--4xs); - } -} diff --git a/packages/design-system/src/components/ds-chip/ds-chip.stories.ts b/packages/design-system/src/components/ds-chip/ds-chip.stories.ts deleted file mode 100644 index 250f6e463..000000000 --- a/packages/design-system/src/components/ds-chip/ds-chip.stories.ts +++ /dev/null @@ -1,62 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react-vite'; -import DsChip from './ds-chip'; -import { chipSizes } from './ds-chip.types'; - -/** - * @deprecated This component is deprecated. Use `DsTag` instead. - * @see {@link ../ds-tag/ds-tag.stories} for examples of the replacement component. - */ -const meta: Meta = { - title: 'Components/Chip (Deprecated)', - component: DsChip, - parameters: { - layout: 'centered', - docs: { - description: { - component: - '**Deprecated**: This component is deprecated. Please use `DsTag` instead. See the Tag stories for the replacement component.', - }, - }, - }, - tags: ['deprecated'], - argTypes: { - label: { - control: 'text', - description: 'The label text to display in the chip', - }, - className: { - control: 'text', - description: 'Additional CSS class names', - }, - style: { - control: 'object', - description: 'Inline styles to apply to the component', - }, - onClick: { - action: 'clicked', - description: 'Function called when component is clicked', - }, - onDelete: { - action: 'deleted', - description: 'Function called when delete icon is clicked', - }, - size: { - control: 'select', - options: chipSizes, - description: 'Size of the chip', - }, - deleteIcon: { - control: false, - description: 'Custom delete icon element', - }, - }, -}; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - label: 'Default Label', - }, -}; diff --git a/packages/design-system/src/components/ds-chip/ds-chip.tsx b/packages/design-system/src/components/ds-chip/ds-chip.tsx deleted file mode 100644 index b2b4b422b..000000000 --- a/packages/design-system/src/components/ds-chip/ds-chip.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import type React from 'react'; -import classNames from 'classnames'; -import styles from './ds-chip.module.scss'; -import type { DsChipProps } from './ds-chip.types'; -import { DsIcon } from '../ds-icon'; -import { DsTypography } from '../ds-typography'; - -/** - * @deprecated This component is deprecated. Use `DsTag` instead. - * @see {@link ../ds-tag} for the replacement component. - */ -const DsChip: React.FC = ({ - ref, - label, - className, - style = {}, - onClick, - onDelete, - size = 'medium', - deleteIcon, - selected = false, -}) => { - const chipClass = classNames( - styles.chip, - { - [styles.clickable]: onClick !== undefined, - [styles.small]: size === 'small', - }, - className, - ); - - const handleDeleteClick = (event: React.MouseEvent) => { - event.stopPropagation(); - onDelete?.(event); - }; - - const handleKeyDown = (event: React.KeyboardEvent) => { - if (onDelete && (event.key === 'Backspace' || event.key === 'Delete')) { - event.preventDefault(); - onDelete(event); - } - }; - - return ( -
} - className={chipClass} - style={style} - onClick={onClick} - onKeyDown={handleKeyDown} - role={onClick || onDelete ? 'button' : undefined} - tabIndex={onClick || onDelete ? 0 : undefined} - aria-label={label} - aria-pressed={onClick && selected ? 'true' : undefined} - > - - {label} - - {onDelete && ( - - )} -
- ); -}; - -export default DsChip; diff --git a/packages/design-system/src/components/ds-chip/ds-chip.types.ts b/packages/design-system/src/components/ds-chip/ds-chip.types.ts deleted file mode 100644 index a466c63ff..000000000 --- a/packages/design-system/src/components/ds-chip/ds-chip.types.ts +++ /dev/null @@ -1,59 +0,0 @@ -import type { CSSProperties, KeyboardEvent, MouseEvent, Ref } from 'react'; -import type { IconType } from '../ds-icon'; - -/** - * @deprecated This type is deprecated. Use `DsTag` instead. - * @see {@link ../ds-tag} for the replacement component. - */ -export const chipSizes = ['medium', 'small'] as const; - -/** - * @deprecated This type is deprecated. Use `DsTag` instead. - * @see {@link ../ds-tag} for the replacement component. - */ -export type ChipSize = (typeof chipSizes)[number]; - -/** - * @deprecated This interface is deprecated. Use `DsTagProps` from `ds-tag` instead. - * @see {@link ../ds-tag/ds-tag.types} for the replacement interface. - */ -export interface DsChipProps { - /** - * Ref to the chip element - */ - ref?: Ref; - /** - * The label text to display in the chip - */ - label: string; - /** - * Additional CSS class names - */ - className?: string; - /** - * Additional styles to apply to the component - */ - style?: CSSProperties; - /** - * Optional click handler - */ - onClick?: (event: MouseEvent) => void; - /** - * Callback function when delete icon is clicked - */ - onDelete?: (event: MouseEvent | KeyboardEvent) => void; - /** - * Size of the chip - * @default 'medium' - */ - size?: ChipSize; - /** - * Custom delete icon element - */ - deleteIcon?: IconType; - /** - * Whether the chip is in a selected/pressed state - * @default false - */ - selected?: boolean; -} diff --git a/packages/design-system/src/components/ds-chip/index.ts b/packages/design-system/src/components/ds-chip/index.ts deleted file mode 100644 index 6d7de51e9..000000000 --- a/packages/design-system/src/components/ds-chip/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * @deprecated DsChip is deprecated. Use DsTag instead. - * @see {@link ../ds-tag} for the replacement component. - */ -export { default as DsChip } from './ds-chip'; -/** - * @deprecated These types are deprecated. Use DsTag types instead. - * @see {@link ../ds-tag} for the replacement types. - */ -export type { ChipSize, DsChipProps } from './ds-chip.types'; 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-select/ds-select.tsx b/packages/design-system/src/components/ds-select/ds-select.tsx index 55e489a70..d8bf70e88 100644 --- a/packages/design-system/src/components/ds-select/ds-select.tsx +++ b/packages/design-system/src/components/ds-select/ds-select.tsx @@ -6,7 +6,7 @@ import styles from './ds-select.module.scss'; import type { DsSelectOption, DsSelectProps } from './ds-select.types'; import { DsIcon } from '../ds-icon'; import { type DsCheckboxProps, DsCheckbox } from '../ds-checkbox'; -import { SelectItemsChips } from './select-items-chips'; +import { SelectItemsTags } from './select-items-tags.tsx'; import { DsTypography } from '../ds-typography'; import { DsTextInput } from '../ds-text-input'; import { SELECT_ALL_VALUE, getUserSelectedItems } from './utils'; @@ -202,7 +202,7 @@ const DsSelect = ({ )} {multiselectProps.multiple && ( - 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 75% 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..16657260c 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,11 +35,11 @@ export function SelectItemsChips({ showAll, onShowAll, onValueChange, count }: S onValueChange?.(filteredValue); }; - return ; + return ; })} {!showAll && selectedItems.length > count && ( - + )} { 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/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/filters-panel.stories.tsx b/packages/design-system/src/components/ds-table/stories/filters-panel.stories.tsx index 2f33e2419..da03a096b 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 @@ -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, @@ -576,9 +576,9 @@ To add a new filter, just add one adapter to \`workflowFilters\` array. No other - {/* 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) */} @@ -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, @@ -816,8 +816,8 @@ The debug panel below shows the current filter state as JSON. - {filterChips.length > 0 && ( - + {filterTags.length > 0 && ( + )} @@ -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/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/index.ts b/packages/design-system/src/index.ts index 6b239bde8..e79770ecc 100644 --- a/packages/design-system/src/index.ts +++ b/packages/design-system/src/index.ts @@ -8,8 +8,6 @@ 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'; diff --git a/packages/design-system/vitest.config.ts b/packages/design-system/vitest.config.ts index ccd04daf6..157ea3628 100644 --- a/packages/design-system/vitest.config.ts +++ b/packages/design-system/vitest.config.ts @@ -21,8 +21,6 @@ export default defineConfig({ 'dist/**', // deprecated components - '**/ds-chip/**', - '**/ds-chip-group/**', '**/ds-date-input/**', ], thresholds: { diff --git a/packages/eslint-plugin/src/__tests__/no-deprecated.test.ts b/packages/eslint-plugin/src/__tests__/no-deprecated.test.ts index 0bcc6b2de..8511ebadb 100644 --- a/packages/eslint-plugin/src/__tests__/no-deprecated.test.ts +++ b/packages/eslint-plugin/src/__tests__/no-deprecated.test.ts @@ -53,44 +53,6 @@ ruleTester.run('no-deprecated-ds-button-empty-design', plugin.rules['no-deprecat ], }); -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: ['', ''], diff --git a/packages/eslint-plugin/src/index.ts b/packages/eslint-plugin/src/index.ts index 5251dcd18..b028c2e7f 100644 --- a/packages/eslint-plugin/src/index.ts +++ b/packages/eslint-plugin/src/index.ts @@ -15,18 +15,6 @@ const eslintPlugin = createPlugin( message: `Omitting the design attribute for DsButton is not allowed. Pass 'design="v1.2"' so the new design is used.`, }, - { - 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'), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 371fdfcfc..0afa0499c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -199,18 +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-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) @@ -1924,19 +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-dismissable-layer@1.1.11': resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==} peerDependencies: @@ -2046,19 +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-slot@1.2.3': resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} peerDependencies: @@ -2166,19 +2134,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==} @@ -7522,28 +7477,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-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 @@ -7651,15 +7584,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-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) @@ -7751,15 +7675,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': From c00f3e79481620f4258207a60a02f02a509bed37 Mon Sep 17 00:00:00 2001 From: Micky Date: Thu, 23 Apr 2026 10:06:37 +0200 Subject: [PATCH 5/7] refactor(design-system)!: remove DsButton, DsButtonLegacy; DsButtonV3 becomes default [AR-53409] (#381) --- .changeset/ninety-signs-move.md | 8 ++ .../ds-alert-banner-global.stories.tsx | 14 ++-- .../ds-alert-banner-inline.stories.tsx | 10 +-- .../components/ds-button-v3/ds-button-v3.tsx | 55 ------------- .../ds-button-v3/ds-button-v3.types.ts | 65 --------------- .../src/components/ds-button-v3/index.ts | 6 -- .../__tests__/ds-button.browser.test.tsx} | 54 ++++++------- .../ds-button.module.scss} | 0 .../ds-button.stories.module.scss} | 0 .../ds-button.stories.tsx} | 69 ++++++++-------- .../src/components/ds-button/ds-button.tsx | 66 ++++++++++++--- .../components/ds-button/ds-button.types.ts | 80 +++++++++++++++---- .../src/components/ds-button/index.ts | 20 ++++- .../ds-button-legacy.stories.module.scss | 62 -------------- .../ds-button-legacy/ds-button-legacy.tsx | 29 ------- .../ds-button-legacy.types.ts | 41 ---------- .../versions/ds-button-legacy/index.ts | 2 - .../__tests__/ds-button.browser.test.tsx | 49 ------------ .../versions/ds-button-new/ds-button-new.tsx | 51 ------------ .../ds-button-new/ds-button-new.types.ts | 51 ------------ .../ds-button/versions/ds-button-new/index.ts | 5 -- .../ds-thread-item/ds-thread-item.stories.tsx | 6 +- .../ds-thread-item/ds-thread-item.tsx | 11 +-- .../ds-comment-bubble/ds-comment-bubble.tsx | 36 +++------ .../ds-comment-card/ds-comment-card.tsx | 12 +-- .../comments-filter-modal.tsx | 4 +- .../ds-comments-drawer/ds-comments-drawer.tsx | 14 ++-- .../ds-date-picker/ds-date-picker.tsx | 17 ++-- .../ds-date-range-picker.tsx | 2 +- .../ds-drawer/ds-drawer.stories.tsx | 8 +- .../ds-dropdown-menu.stories.tsx | 14 ++-- .../ds-expandable-text-input.tsx | 4 +- .../file-upload-item/file-upload-item.tsx | 12 +-- .../components/file-upload/file-upload.tsx | 2 +- .../ds-file-upload/ds-file-upload.stories.tsx | 10 +-- .../components/ds-grid/ds-grid.stories.tsx | 2 +- .../components/ds-modal/ds-modal.stories.tsx | 25 +++--- .../__tests__/ds-panel.browser.test.tsx | 4 +- .../components/ds-panel/ds-panel.stories.tsx | 2 +- .../src/components/ds-select/ds-select.tsx | 2 +- .../ds-select/select-items-tags.tsx | 8 +- .../ds-split-button/ds-split-button.tsx | 4 +- .../ds-split-button/ds-split-button.types.ts | 4 +- .../components/ds-step-next-button.tsx | 14 ++-- .../ds-stepper/ds-stepper.stories.tsx | 2 +- .../ds-table-bulk-actions.module.scss | 69 ++++++++++------ .../ds-table-bulk-actions.tsx | 21 ++--- .../ds-table-row-expandable-cell.tsx | 3 +- .../components/range-filter/range-filter.tsx | 2 +- .../stories/filters-panel.stories.tsx | 16 ++-- .../last-edited-filter/last-edited-filter.tsx | 4 +- .../ds-tag-filter/ds-tag-filter.tsx | 15 +--- .../ds-time-picker/ds-time-picker.tsx | 11 +-- .../components/ds-toast/ds-toast.stories.tsx | 34 +++----- packages/design-system/src/index.ts | 1 - .../src/__tests__/no-deprecated.test.ts | 42 ---------- packages/eslint-plugin/src/index.ts | 12 --- 57 files changed, 387 insertions(+), 799 deletions(-) create mode 100644 .changeset/ninety-signs-move.md delete mode 100644 packages/design-system/src/components/ds-button-v3/ds-button-v3.tsx delete mode 100644 packages/design-system/src/components/ds-button-v3/ds-button-v3.types.ts delete mode 100644 packages/design-system/src/components/ds-button-v3/index.ts rename packages/design-system/src/components/{ds-button-v3/__tests__/ds-button-v3.browser.test.tsx => ds-button/__tests__/ds-button.browser.test.tsx} (79%) rename packages/design-system/src/components/{ds-button-v3/ds-button-v3.module.scss => ds-button/ds-button.module.scss} (100%) rename packages/design-system/src/components/{ds-button-v3/ds-button-v3.stories.module.scss => ds-button/ds-button.stories.module.scss} (100%) rename packages/design-system/src/components/{ds-button-v3/ds-button-v3.stories.tsx => ds-button/ds-button.stories.tsx} (79%) delete mode 100644 packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.stories.module.scss delete mode 100644 packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.tsx delete mode 100644 packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.types.ts delete mode 100644 packages/design-system/src/components/ds-button/versions/ds-button-legacy/index.ts delete mode 100644 packages/design-system/src/components/ds-button/versions/ds-button-new/__tests__/ds-button.browser.test.tsx delete mode 100644 packages/design-system/src/components/ds-button/versions/ds-button-new/ds-button-new.tsx delete mode 100644 packages/design-system/src/components/ds-button/versions/ds-button-new/ds-button-new.types.ts delete mode 100644 packages/design-system/src/components/ds-button/versions/ds-button-new/index.ts 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/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.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.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.tsx b/packages/design-system/src/components/ds-button/versions/ds-button-new/ds-button-new.tsx deleted file mode 100644 index c9908bc03..000000000 --- a/packages/design-system/src/components/ds-button/versions/ds-button-new/ds-button-new.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import classNames from 'classnames'; -import type React from 'react'; -import { Children, isValidElement } from 'react'; - -import styles from './ds-button-new.module.scss'; -import type { DsButtonBaseProps } from './ds-button-new.types'; -import { DsIcon } from '../../../ds-icon'; - -const isIconOnly = (children: React.ReactNode) => { - if (Children.count(children) !== 1) { - return false; - } - - const childArray = Children.toArray(children); - const onlyChild = childArray[0]; - - return isValidElement(onlyChild) && onlyChild.type === DsIcon; -}; - -/** - * Design system Button component - */ -const DsButton: React.FC = ({ - buttonType, - variant = 'filled', - size = 'medium', - disabled = false, - className, - contentClassName, - children, - ...props -}) => { - const type = buttonType ?? (variant === 'ghost' ? 'secondary' : 'primary'); - const buttonClass = classNames( - styles.button, - { [styles.iconButton]: isIconOnly(children) }, - styles[size], - className, - - // Casting because we don't have all variations of classnames defined - styles[`${type}-${variant}` as keyof typeof styles], - ); - - return ( - - ); -}; - -export default DsButton; diff --git a/packages/design-system/src/components/ds-button/versions/ds-button-new/ds-button-new.types.ts b/packages/design-system/src/components/ds-button/versions/ds-button-new/ds-button-new.types.ts deleted file mode 100644 index 31347b58d..000000000 --- a/packages/design-system/src/components/ds-button/versions/ds-button-new/ds-button-new.types.ts +++ /dev/null @@ -1,51 +0,0 @@ -import type React from 'react'; - -import type { ResponsiveValue } from '../../../../utils/responsive'; - -export const buttonTypes = ['primary', 'secondary', 'secondary-light', 'tertiary'] as const; -export type ButtonType = (typeof buttonTypes)[number]; - -export const buttonVariants = ['filled', 'ghost', 'danger', 'dark'] as const; -export type ButtonVariant = (typeof buttonVariants)[number]; - -export const buttonSizes = ['large', 'medium', 'small', 'tiny'] as const; -export type ButtonSize = (typeof buttonSizes)[number]; - -export interface DsButtonBaseProps extends React.ButtonHTMLAttributes { - /** - * Type of the button - * @default 'primary' - */ - buttonType?: ButtonType; - - /** - * 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; -} - -export interface DsButtonProps 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/versions/ds-button-new/index.ts b/packages/design-system/src/components/ds-button/versions/ds-button-new/index.ts deleted file mode 100644 index fd2a5f46e..000000000 --- a/packages/design-system/src/components/ds-button/versions/ds-button-new/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { withResponsiveProps } from '../../../../utils/responsive'; -import DsButtonBase from './ds-button-new'; - -export const DsButtonNew = withResponsiveProps(DsButtonBase, ['size']); -export * from './ds-button-new.types'; diff --git a/packages/design-system/src/components/ds-comment-bubble/components/ds-thread-item/ds-thread-item.stories.tsx b/packages/design-system/src/components/ds-comment-bubble/components/ds-thread-item/ds-thread-item.stories.tsx index 910420a68..656e37747 100644 --- a/packages/design-system/src/components/ds-comment-bubble/components/ds-thread-item/ds-thread-item.stories.tsx +++ b/packages/design-system/src/components/ds-comment-bubble/components/ds-thread-item/ds-thread-item.stories.tsx @@ -312,7 +312,7 @@ export const ContentChangeWhileNotEditing: Story = { onDelete={fn()} /> - setContent(UPDATED_CONTENT)}> + setContent(UPDATED_CONTENT)}> Simulate external update @@ -353,7 +353,7 @@ export const ContentChangeWhileEditing: Story = { onDelete={fn()} /> - setContent(UPDATED_CONTENT)}> + setContent(UPDATED_CONTENT)}> Simulate external update @@ -398,7 +398,7 @@ export const ContentChangeWhileEditingThenCancel: Story = { onDelete={fn()} /> - setContent(UPDATED_CONTENT)}> + setContent(UPDATED_CONTENT)}> Simulate external update diff --git a/packages/design-system/src/components/ds-comment-bubble/components/ds-thread-item/ds-thread-item.tsx b/packages/design-system/src/components/ds-comment-bubble/components/ds-thread-item/ds-thread-item.tsx index 158fd4b41..c117be637 100644 --- a/packages/design-system/src/components/ds-comment-bubble/components/ds-thread-item/ds-thread-item.tsx +++ b/packages/design-system/src/components/ds-comment-bubble/components/ds-thread-item/ds-thread-item.tsx @@ -79,10 +79,10 @@ export const DsThreadItem = ({ rows={3} />
- + Cancel - + Save
@@ -97,9 +97,7 @@ export const DsThreadItem = ({ {hasEditActions && ( - - - + {onEdit && ( @@ -122,8 +120,7 @@ export const DsThreadItem = ({ )} {onResolved && ( onResolved(id)} aria-label="Mark message as resolved" diff --git a/packages/design-system/src/components/ds-comment-bubble/ds-comment-bubble.tsx b/packages/design-system/src/components/ds-comment-bubble/ds-comment-bubble.tsx index d11a36bdf..7f173d915 100644 --- a/packages/design-system/src/components/ds-comment-bubble/ds-comment-bubble.tsx +++ b/packages/design-system/src/components/ds-comment-bubble/ds-comment-bubble.tsx @@ -128,9 +128,7 @@ export const DsCommentBubble = ({
- - - + {!hideActionRequired && ( @@ -155,27 +153,23 @@ export const DsCommentBubble = ({ {onResolve && ( onResolve()} aria-label="Resolve" - > - - + /> )}
{onClose && ( onClose()} aria-label="Close" - > - - + /> )}
@@ -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-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-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..67ab5d9f4 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 @@ -204,12 +204,12 @@ export const CommentsFilterModal = ({ - + Clear all - + Apply 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-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-drawer/ds-drawer.stories.tsx b/packages/design-system/src/components/ds-drawer/ds-drawer.stories.tsx index e93884203..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 @@ -125,10 +125,10 @@ export const Default: Story = { - + Cancel - + Save @@ -192,10 +192,10 @@ export const WithTabs: Story = { - + Cancel - + Save diff --git a/packages/design-system/src/components/ds-dropdown-menu/ds-dropdown-menu.stories.tsx b/packages/design-system/src/components/ds-dropdown-menu/ds-dropdown-menu.stories.tsx index 1ff3ea74e..1e5ce0e75 100644 --- a/packages/design-system/src/components/ds-dropdown-menu/ds-dropdown-menu.stories.tsx +++ b/packages/design-system/src/components/ds-dropdown-menu/ds-dropdown-menu.stories.tsx @@ -244,10 +244,10 @@ export const CheckboxList: Story = { )} - + Cancel - + Apply @@ -398,13 +398,13 @@ export const RadioList: Story = { ))} - + Reset - + Cancel - + Apply @@ -434,9 +434,7 @@ export const ActionMenu: Story = { return ( - - - + diff --git a/packages/design-system/src/components/ds-expandable-text-input/ds-expandable-text-input.tsx b/packages/design-system/src/components/ds-expandable-text-input/ds-expandable-text-input.tsx index 2d5db863e..08bfe5a03 100644 --- a/packages/design-system/src/components/ds-expandable-text-input/ds-expandable-text-input.tsx +++ b/packages/design-system/src/components/ds-expandable-text-input/ds-expandable-text-input.tsx @@ -83,10 +83,8 @@ export function DsExpandableTextInput({ endAdornment: ( { setExpanded(false); diff --git a/packages/design-system/src/components/ds-file-upload/components/file-upload-item/file-upload-item.tsx b/packages/design-system/src/components/ds-file-upload/components/file-upload-item/file-upload-item.tsx index e05df44dc..643b9c6d4 100644 --- a/packages/design-system/src/components/ds-file-upload/components/file-upload-item/file-upload-item.tsx +++ b/packages/design-system/src/components/ds-file-upload/components/file-upload-item/file-upload-item.tsx @@ -52,8 +52,7 @@ export const FileUploadItem: React.FC = ({ onRetry(id)} > @@ -66,8 +65,7 @@ export const FileUploadItem: React.FC = ({ onRemove?.(id)} > @@ -80,8 +78,7 @@ export const FileUploadItem: React.FC = ({ onCancel(id)} > @@ -94,8 +91,7 @@ export const FileUploadItem: React.FC = ({ onDelete?.(id)} > diff --git a/packages/design-system/src/components/ds-file-upload/components/file-upload/file-upload.tsx b/packages/design-system/src/components/ds-file-upload/components/file-upload/file-upload.tsx index b43a7b379..ce0e28cad 100644 --- a/packages/design-system/src/components/ds-file-upload/components/file-upload/file-upload.tsx +++ b/packages/design-system/src/components/ds-file-upload/components/file-upload/file-upload.tsx @@ -63,7 +63,7 @@ export const FileUpload: React.FC = ({ {dropzoneText} - + {triggerText} diff --git a/packages/design-system/src/components/ds-file-upload/ds-file-upload.stories.tsx b/packages/design-system/src/components/ds-file-upload/ds-file-upload.stories.tsx index ffaddb152..3e42c6457 100644 --- a/packages/design-system/src/components/ds-file-upload/ds-file-upload.stories.tsx +++ b/packages/design-system/src/components/ds-file-upload/ds-file-upload.stories.tsx @@ -121,16 +121,10 @@ export const Manual: Story = { {hasFiles && (
- uploadAll()} disabled={isUploading}> + uploadAll()} disabled={isUploading}> {isUploading ? 'Uploading...' : 'Upload All'} - clearFiles()} - disabled={isUploading} - > + clearFiles()} disabled={isUploading}> Clear All
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-select/ds-select.tsx b/packages/design-system/src/components/ds-select/ds-select.tsx index d8bf70e88..5242a5793 100644 --- a/packages/design-system/src/components/ds-select/ds-select.tsx +++ b/packages/design-system/src/components/ds-select/ds-select.tsx @@ -6,7 +6,7 @@ import styles from './ds-select.module.scss'; import type { DsSelectOption, DsSelectProps } from './ds-select.types'; import { DsIcon } from '../ds-icon'; import { type DsCheckboxProps, DsCheckbox } from '../ds-checkbox'; -import { SelectItemsTags } from './select-items-tags.tsx'; +import { SelectItemsTags } from './select-items-tags'; import { DsTypography } from '../ds-typography'; import { DsTextInput } from '../ds-text-input'; import { SELECT_ALL_VALUE, getUserSelectedItems } from './utils'; diff --git a/packages/design-system/src/components/ds-select/select-items-tags.tsx b/packages/design-system/src/components/ds-select/select-items-tags.tsx index 16657260c..64aab1d53 100644 --- a/packages/design-system/src/components/ds-select/select-items-tags.tsx +++ b/packages/design-system/src/components/ds-select/select-items-tags.tsx @@ -42,13 +42,7 @@ export function SelectItemsTags({ showAll, onShowAll, onValueChange, count }: Se )} - 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-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..445460a49 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; + background-color: var(--white); + 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 { @@ -41,51 +40,71 @@ } .selectedCountBadge { - color: var(--color-dap-gray-050); + color: var(--white); margin: 0; } .bulkActionsInfo { - color: var(--color-dap-gray-600); + color: var(--neutral-2); text-wrap: nowrap; padding-left: 8px; padding-right: 56px; } +.actionButton { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: var(--spacing-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(--neutral-6); + } - > .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(--neutral-6); + } } } -.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; + color: var(--neutral-2); padding-left: 4px; + margin-right: 2px; + background: none; + border-radius: 0 $border-radius $border-radius 0; &:hover { - color: var(--color-dap-gray-500); + color: var(--neutral-4); + 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/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/stories/filters-panel.stories.tsx b/packages/design-system/src/components/ds-table/stories/filters-panel.stories.tsx index da03a096b..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 @@ -490,9 +490,9 @@ const handleValueChange = (value: string | null) => { - Clear all + Clear all - Apply + Apply @@ -571,7 +571,7 @@ To add a new filter, just add one adapter to \`workflowFilters\` array. No other
{/* Toolbar with filter button */}
- setIsOpen(true)}> + setIsOpen(true)}>
@@ -616,12 +616,12 @@ To add a new filter, just add one adapter to \`workflowFilters\` array. No other - + Clear all - + Apply @@ -811,7 +811,7 @@ The debug panel below shows the current filter state as JSON.
- setIsOpen(true)}> + setIsOpen(true)}>
@@ -853,12 +853,12 @@ The debug panel below shows the current filter state as JSON. - + Clear all - + Apply 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-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/index.ts b/packages/design-system/src/index.ts index e79770ecc..2b4d6f0e3 100644 --- a/packages/design-system/src/index.ts +++ b/packages/design-system/src/index.ts @@ -4,7 +4,6 @@ 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'; diff --git a/packages/eslint-plugin/src/__tests__/no-deprecated.test.ts b/packages/eslint-plugin/src/__tests__/no-deprecated.test.ts index 8511ebadb..0581f6298 100644 --- a/packages/eslint-plugin/src/__tests__/no-deprecated.test.ts +++ b/packages/eslint-plugin/src/__tests__/no-deprecated.test.ts @@ -11,48 +11,6 @@ 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-date-input', plugin.rules['no-deprecated-ds-date-input'], { valid: ['', ''], diff --git a/packages/eslint-plugin/src/index.ts b/packages/eslint-plugin/src/index.ts index b028c2e7f..f2f4668fe 100644 --- a/packages/eslint-plugin/src/index.ts +++ b/packages/eslint-plugin/src/index.ts @@ -3,18 +3,6 @@ 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-date-input', selector: JSXElementName('DsDateInput'), From f77a95f91028970525405b2b8c09786ee4156b28 Mon Sep 17 00:00:00 2001 From: Micky Date: Thu, 23 Apr 2026 12:13:12 +0200 Subject: [PATCH 6/7] refactor(design-system)!: remove DsDateInput component [AR-53409] (#400) --- .changeset/remove-ds-date-input.md | 8 + .../ds-comments-drawer/comments-filters.ts | 9 +- .../comments-filters.types.ts | 4 +- .../comments-filters.unit.test.ts | 44 +-- .../comments-filter-modal.tsx | 26 +- .../ds-date-input/ds-date-input.module.scss | 247 ------------ .../ds-date-input.stories.module.scss | 29 -- .../ds-date-input/ds-date-input.stories.tsx | 339 ---------------- .../ds-date-input/ds-date-input.tsx | 310 --------------- .../ds-date-input/ds-date-input.types.ts | 135 ------- .../ds-date-input/ds-date-input.utils.ts | 256 ------------ .../src/components/ds-date-input/index.ts | 12 - .../ds-form-control/ds-form-control.tsx | 3 - .../ds-form-control-date-input.stories.tsx | 373 ------------------ packages/design-system/src/index.ts | 1 - packages/design-system/vitest.config.ts | 11 +- .../src/__tests__/no-deprecated.test.ts | 42 +- packages/eslint-plugin/src/index.ts | 11 +- 18 files changed, 83 insertions(+), 1777 deletions(-) create mode 100644 .changeset/remove-ds-date-input.md delete mode 100644 packages/design-system/src/components/ds-date-input/ds-date-input.module.scss delete mode 100644 packages/design-system/src/components/ds-date-input/ds-date-input.stories.module.scss delete mode 100644 packages/design-system/src/components/ds-date-input/ds-date-input.stories.tsx delete mode 100644 packages/design-system/src/components/ds-date-input/ds-date-input.tsx delete mode 100644 packages/design-system/src/components/ds-date-input/ds-date-input.types.ts delete mode 100644 packages/design-system/src/components/ds-date-input/ds-date-input.utils.ts delete mode 100644 packages/design-system/src/components/ds-date-input/index.ts delete mode 100644 packages/design-system/src/components/ds-form-control/stories/ds-form-control-date-input.stories.tsx 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/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..ac0654ad6 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,10 @@ import type { FilterTag, CommentsFilterState } from './comments-filters.types'; export type { FilterTag, CommentsFilterState } from './comments-filters.types'; +const formatFilterDateForTag = (d: Date): string => { + return d.toISOString().split('T')[0] ?? ''; +}; + export const initialFilterState: CommentsFilterState = { authors: [], labels: [], @@ -42,6 +46,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 +94,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 67ab5d9f4..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 - 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-form-control/ds-form-control.tsx b/packages/design-system/src/components/ds-form-control/ds-form-control.tsx index ce96a8de5..878b77be5 100644 --- a/packages/design-system/src/components/ds-form-control/ds-form-control.tsx +++ b/packages/design-system/src/components/ds-form-control/ds-form-control.tsx @@ -9,7 +9,6 @@ import { DsNumberInput } from '../ds-number-input'; import { DsPasswordInput } from '../ds-password-input'; import type { DsFormControlDescriptionProps, DsFormControlProps } from './ds-form-control.types'; import styles from './ds-form-control.module.scss'; -import { DsDateInput } from '../ds-date-input'; import { DsDatePicker } from '../ds-date-picker'; import { DsTimePicker } from '../ds-time-picker'; @@ -96,8 +95,6 @@ const DsFormControl = ({ DsFormControl.TextInput = controlify(DsTextInput); DsFormControl.NumberInput = controlify(DsNumberInput); DsFormControl.PasswordInput = controlify(DsPasswordInput); -/** @deprecated DsDateInput is deprecated. Use DsDatePicker or DsDateRangePicker instead. */ -DsFormControl.DateInput = controlify(DsDateInput); DsFormControl.DatePicker = controlify(DsDatePicker); DsFormControl.TimePicker = controlify(DsTimePicker); DsFormControl.Textarea = controlify(DsTextarea); diff --git a/packages/design-system/src/components/ds-form-control/stories/ds-form-control-date-input.stories.tsx b/packages/design-system/src/components/ds-form-control/stories/ds-form-control-date-input.stories.tsx deleted file mode 100644 index 2d75b8cc4..000000000 --- a/packages/design-system/src/components/ds-form-control/stories/ds-form-control-date-input.stories.tsx +++ /dev/null @@ -1,373 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react-vite'; -import { useState } from 'react'; -import { expect, userEvent, waitFor, within } from 'storybook/test'; -import { controlStatuses } from '../ds-form-control.types'; -import DsFormControl from '../ds-form-control'; -import { DefaultDescription } from './ds-form-control-stories-shared'; -import styles from './ds-form-control.stories.module.scss'; -import { DsIcon } from '../../ds-icon'; - -const meta: Meta = { - title: 'Components/FormControl/DateInput (Deprecated)', - component: DsFormControl.DateInput, - parameters: { - layout: 'centered', - docs: { - description: { - component: - '**Deprecated**: DsFormControl.DateInput is deprecated. Please use `DsFormControl.DsDatePicker` for single date selection or `DsFormControl.DsDateRangePicker` for date range selection instead.', - }, - }, - }, - tags: ['deprecated'], - argTypes: { - status: { - control: { type: 'select' }, - options: controlStatuses, - description: 'Form control color status', - table: { - defaultValue: { - summary: controlStatuses[0], - }, - }, - }, - label: { - control: 'text', - description: 'Label for the form control', - }, - required: { - control: 'boolean', - description: 'Indicates if the field is required', - }, - message: { - control: 'text', - description: 'Message to display below the form control', - }, - messageIcon: { - control: 'text', - description: 'Icon to display in the message', - }, - className: { - control: 'text', - description: 'Additional CSS class names', - }, - style: { - control: 'object', - description: 'Additional styles to apply to the component', - }, - }, -}; - -export default meta; -type Story = StoryObj; - -const sanityCheckSingle = async (canvasElement: HTMLElement) => { - const canvas = within(canvasElement); - const input = canvas.getByPlaceholderText('MM/DD/YYYY'); - - // Test typing a date - await userEvent.click(input); - await userEvent.type(input, '12/25/2024'); - await waitFor(async () => { - await expect(input).toHaveValue('12/25/2024'); - }); - - // Test clearing the input - await userEvent.clear(input); - await expect(input).toHaveValue(''); -}; - -const sanityCheckRange = async (canvasElement: HTMLElement) => { - const canvas = within(canvasElement); - const input = canvas.getByPlaceholderText('MM/DD/YYYY - MM/DD/YYYY'); - - // Test typing a date range - await userEvent.click(input); - await userEvent.type(input, '12/01/2024 - 12/31/2024'); - await waitFor(async () => { - await expect(input).toHaveValue('12/01/2024 - 12/31/2024'); - }); -}; - -const checkDisabled = async (canvasElement: HTMLElement) => { - const canvas = within(canvasElement); - const input = canvas.getByPlaceholderText('MM/DD/YYYY'); - const calendarButton = canvas.getByRole('button', { name: /open calendar/i }); - - // Assert that the input and button are disabled - await expect(input).toBeDisabled(); - await expect(calendarButton).toBeDisabled(); - - // Attempt to type into the disabled input - await userEvent.type(input, '12/25/2024'); - - // Assert that the input value remains unchanged - await expect(input.value).toBe(''); -}; - -export const Default: Story = { - args: { - label: 'Event Date', - required: true, - style: { width: '400px' }, - message: 'Select a date for your event', - }, - render: (args) => ( - - - - ), - play: async ({ canvasElement }) => { - await sanityCheckSingle(canvasElement); - }, -}; - -export const WithCustomWidth: Story = { - args: { - label: 'Event Date', - required: true, - style: { width: '400px' }, - }, - render: (args) => ( - - - - ), - play: async ({ canvasElement }) => { - await sanityCheckSingle(canvasElement); - }, -}; - -export const WithCustomStyles: Story = { - args: { - label: 'Event Date', - required: true, - style: { - width: '400px', - padding: '16px', - border: '2px solid #e0e0e0', - borderRadius: '8px', - backgroundColor: '#f9f9f9', - }, - }, - render: (args) => ( - - - - ), - play: async ({ canvasElement }) => { - await sanityCheckSingle(canvasElement); - }, -}; - -export const WithDescription: Story = { - args: { - label: 'Event Date', - required: true, - style: { width: '400px' }, - }, - render: (args) => ( - - - - - - - ), - play: async ({ canvasElement }) => { - await sanityCheckSingle(canvasElement); - }, -}; - -export const WithHelpIcon: Story = { - args: { - label: 'Event Date', - required: true, - style: { width: '400px' }, - slots: { - endAdornment: ( - - ), - }, - }, - 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/index.ts b/packages/design-system/src/index.ts index 2b4d6f0e3..d8d690af1 100644 --- a/packages/design-system/src/index.ts +++ b/packages/design-system/src/index.ts @@ -11,7 +11,6 @@ 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-date-input'; export * from './components/ds-date-picker'; export * from './components/ds-date-range-picker'; export * from './components/ds-divider'; diff --git a/packages/design-system/vitest.config.ts b/packages/design-system/vitest.config.ts index 157ea3628..6d7f23734 100644 --- a/packages/design-system/vitest.config.ts +++ b/packages/design-system/vitest.config.ts @@ -13,16 +13,7 @@ export default defineConfig({ test: { coverage: { include: ['src/**/*.{ts,tsx}'], - exclude: [ - '**/stories/**', - '**/*.stories.*{ts,tsx}', - '**/.storybook/**', - '**/*.scss', - 'dist/**', - - // deprecated components - '**/ds-date-input/**', - ], + 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 0581f6298..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,21 +11,27 @@ const ruleTester = new RuleTester({ }, }); -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, - }, - ], - }, - ], +it('placeholder: RuleTester instantiates', () => { + expect(ruleTester).toBeInstanceOf(RuleTester); }); + +// Example test for the future reference + +// 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, +// }, +// ], +// }, +// ], +// }); diff --git a/packages/eslint-plugin/src/index.ts b/packages/eslint-plugin/src/index.ts index f2f4668fe..1be0fe8bf 100644 --- a/packages/eslint-plugin/src/index.ts +++ b/packages/eslint-plugin/src/index.ts @@ -3,11 +3,12 @@ import { createPlugin } from './create-plugin'; const eslintPlugin = createPlugin( '@drivenets/design-system', - { - 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', From ec6367eb78deceacd024dff8a42a0c19e419b88e Mon Sep 17 00:00:00 2001 From: Michal Murawski Date: Wed, 6 May 2026 16:16:19 +0200 Subject: [PATCH 7/7] chore(design-system): resolve all after rebase issues and migrate to tokens [AR-60245] --- packages/design-system/package.json | 4 +- .../ds-button-legacy.module.scss | 257 -------- .../ds-button-legacy.stories.tsx | 147 ----- .../ds-button-new/ds-button-new.module.scss | 604 ------------------ .../ds-button-new.stories.module.scss | 36 -- .../ds-button-new/ds-button-new.stories.tsx | 213 ------ .../ds-comment-bubble.types.ts | 2 +- .../ds-comment-card/ds-comment-card.types.ts | 6 +- .../ds-comments-drawer/comments-filters.ts | 7 +- .../comments-filters.types.ts | 8 +- .../__tests__/ds-select.browser.test.tsx | 18 +- .../ds-table-bulk-actions.module.scss | 16 +- .../__tests__/ds-workspace.browser.test.tsx | 2 +- .../ds-workspace/ds-workspace.stories.tsx | 32 +- .../src/stories/sample-form/sample-form.tsx | 6 +- pnpm-lock.yaml | 204 +++--- 16 files changed, 144 insertions(+), 1418 deletions(-) delete mode 100644 packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.module.scss delete mode 100644 packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.stories.tsx delete mode 100644 packages/design-system/src/components/ds-button/versions/ds-button-new/ds-button-new.module.scss delete mode 100644 packages/design-system/src/components/ds-button/versions/ds-button-new/ds-button-new.stories.module.scss delete mode 100644 packages/design-system/src/components/ds-button/versions/ds-button-new/ds-button-new.stories.tsx diff --git a/packages/design-system/package.json b/packages/design-system/package.json index 105df8f0a..76e8ba196 100644 --- a/packages/design-system/package.json +++ b/packages/design-system/package.json @@ -89,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-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 65e84c617..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(--action-cta1); -$color-primary-hover: var(--action-hover-light); -$color-primary-active: var(--action-active-light); -$color-primary-disabled: var(--action-disabled); - -$color-secondary-default: var(--action-cta3); -$color-secondary-hover: var(--color-dap-gray-500); -$color-secondary-disabled: var(--action-disabled); - -$color-error-default: var(--background-background-negative); -$color-error-hover: var(--utility-error); -$color-error-active: var(--utility-error); -$color-error-disabled: var(--action-disabled); - -$color-white: var(--color-dap-gray-050); -$color-neutral-6: var(--utility-disabled); -$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.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-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: ( - <> -
); @@ -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/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/pnpm-lock.yaml b/pnpm-lock.yaml index 0afa0499c..9cf012254 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -253,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)) @@ -264,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) @@ -282,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 @@ -334,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) @@ -349,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) @@ -377,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) @@ -386,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: @@ -411,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) @@ -423,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: @@ -439,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) @@ -448,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: @@ -2036,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: @@ -2537,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 @@ -2546,7 +2537,7 @@ packages: postcss-modules: ^6.0.0 sass: '*' sass-embedded: '*' - tsdown: 0.21.7 + tsdown: 0.21.10 peerDependenciesMeta: postcss: optional: true @@ -2589,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==} @@ -2897,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==} @@ -2939,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: @@ -2964,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==} @@ -2979,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==} @@ -7151,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': @@ -7591,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 @@ -7832,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 @@ -7991,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 @@ -8027,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 @@ -8320,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.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.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.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.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.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.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 @@ -8362,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: @@ -8372,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 @@ -8393,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 @@ -8421,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 @@ -8445,8 +8430,6 @@ snapshots: dependencies: tinyspy: 4.0.4 - '@vitest/spy@4.1.3': {} - '@vitest/spy@4.1.5': {} '@vitest/utils@3.2.4': @@ -8455,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 @@ -11655,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 @@ -11675,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: @@ -11932,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)) @@ -11961,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)) @@ -11990,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