diff --git a/.changeset/five-tigers-behave.md b/.changeset/five-tigers-behave.md new file mode 100644 index 000000000..d7dd83bb6 --- /dev/null +++ b/.changeset/five-tigers-behave.md @@ -0,0 +1,7 @@ +--- +'@drivenets/design-system': major +'@drivenets/eslint-plugin-design-system': major +--- + +Remove deprecated components `DsChip` and `DsChipGroup` +Remove deprecation eslint rules for `DsChip` and `DsChipGroup` diff --git a/.changeset/fluffy-clubs-greet.md b/.changeset/fluffy-clubs-greet.md new file mode 100644 index 000000000..9fe45666d --- /dev/null +++ b/.changeset/fluffy-clubs-greet.md @@ -0,0 +1,7 @@ +--- +'@drivenets/design-system': major +'@drivenets/eslint-plugin-design-system': major +--- + +Remove deprecated components `DsDropdownMenuLegacy` and `DsRadioGroupLegacy` +Remove deprecation eslint rules for `DsDropdownMenuLegacy` and `DsRadioGroupLegacy` diff --git a/.changeset/loose-stamps-study.md b/.changeset/loose-stamps-study.md new file mode 100644 index 000000000..28b797bb5 --- /dev/null +++ b/.changeset/loose-stamps-study.md @@ -0,0 +1,7 @@ +--- +'@drivenets/design-system': major +'@drivenets/eslint-plugin-design-system': major +--- + +Remove deprecated component `DsSystemStatus` +Remove deprecation eslint rules for `DsSystemStatus` diff --git a/.changeset/ninety-signs-move.md b/.changeset/ninety-signs-move.md new file mode 100644 index 000000000..a0201eef8 --- /dev/null +++ b/.changeset/ninety-signs-move.md @@ -0,0 +1,8 @@ +--- +'@drivenets/design-system': major +'@drivenets/eslint-plugin-design-system': major +--- + +Remove deprecated `DsButton` and `DsButtonLegacy`. +Rename `DsButtonV3` to `DsButton` (it becomes the default `DsButton` component). +Remove deprecation rules for `DsButtonLegacy`. diff --git a/.changeset/remove-ds-date-input.md b/.changeset/remove-ds-date-input.md new file mode 100644 index 000000000..404ce1fee --- /dev/null +++ b/.changeset/remove-ds-date-input.md @@ -0,0 +1,8 @@ +--- +'@drivenets/design-system': major +'@drivenets/eslint-plugin-design-system': major +--- + +Remove deprecated `DsDateInput`, `DsFormControl.DateInput` components. +Remove deprecation rules for `DsDateInput`. +Replace `DsDateInput` with `DsDatePicker` or `DsDateRangePicker` instead. diff --git a/.changeset/ripe-ravens-sleep.md b/.changeset/ripe-ravens-sleep.md new file mode 100644 index 000000000..522c78ef0 --- /dev/null +++ b/.changeset/ripe-ravens-sleep.md @@ -0,0 +1,7 @@ +--- +'@drivenets/design-system': major +'@drivenets/eslint-plugin-design-system': major +--- + +Removed deprecated components `DsDialog` and `DsConfirm` +Removed deprecation eslint rules for `DsDialog` and `DsConfirm` diff --git a/packages/design-system/package.json b/packages/design-system/package.json index dd2a12047..82767349d 100644 --- a/packages/design-system/package.json +++ b/packages/design-system/package.json @@ -61,11 +61,8 @@ "@dnd-kit/modifiers": "^9.0.0", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", - "@radix-ui/react-collapsible": "^1.1.13", - "@radix-ui/react-dialog": "^1.1.16", - "@radix-ui/react-dropdown-menu": "^2.1.17", + "@radix-ui/react-collapsible": "^1.1.12", "@radix-ui/react-tooltip": "^1.2.9", - "@radix-ui/react-visually-hidden": "^1.2.5", "@tanstack/react-table": "^8.21.3", "@tanstack/react-virtual": "^3.14.2", "@zag-js/react": "^1.41.2", 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 e1bff38e6..eb1545a2e 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' }); @@ -207,9 +207,9 @@ describe('DsButtonV3', () => { it('primary has a non-transparent background', async () => { await page.render( - + Light - , + , ); const el = page.getByRole('button', { name: 'Light' }).element(); @@ -219,9 +219,9 @@ describe('DsButtonV3', () => { it('primary disabled has a non-transparent background', async () => { await page.render( - + Light - , + , ); const el = page.getByRole('button', { name: 'Light' }).element(); @@ -231,9 +231,9 @@ describe('DsButtonV3', () => { it('secondary selected has a non-transparent background', async () => { await page.render( - + Light - , + , ); const el = page.getByRole('button', { name: 'Light' }).element(); diff --git a/packages/design-system/src/components/ds-button-v3/ds-button-v3.figma.ts b/packages/design-system/src/components/ds-button/ds-button.figma.ts similarity index 81% rename from packages/design-system/src/components/ds-button-v3/ds-button-v3.figma.ts rename to packages/design-system/src/components/ds-button/ds-button.figma.ts index 9e1e4d7d3..ecb1def0d 100644 --- a/packages/design-system/src/components/ds-button-v3/ds-button-v3.figma.ts +++ b/packages/design-system/src/components/ds-button/ds-button.figma.ts @@ -1,6 +1,6 @@ // url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=31065-2352 -// source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-button-v3 -// component=DsButtonV3 +// source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-button +// component=DsButton import figma from 'figma'; const instance = figma.selectedInstance; @@ -31,7 +31,7 @@ const iconOnly = instance.getEnum('Icon', { 'icon-only': true, text: false }); const structure = instance.findInstance('.button-structure', { traverseInstances: true }); const showIcon = structure.type === 'INSTANCE' ? structure.getBoolean('Show icon') : false; -// `DsButtonV3.icon` is an icon-name string. Swapped icons keep the placeholder layer +// `DsButton.icon` is an icon-name string. Swapped icons keep the placeholder layer // name (e.g. `DAP_GM_O_check_circle`), so resolve via Code Connect ID or metadata. const toIconName = (name: string): string => name @@ -89,11 +89,11 @@ const iconProp = icon ? figma.code` icon="${icon}"` : ''; export default { example: iconOnly - ? figma.code`` + ? figma.code`` : showIcon && icon - ? figma.code`${label}` - : figma.code`${label}`, - imports: ["import { DsButtonV3 } from '@drivenets/design-system'"], + ? figma.code`${label}` + : figma.code`${label}`, + imports: ["import { DsButton } from '@drivenets/design-system'"], id: 'ds-button-v3', metadata: { nestable: true }, }; diff --git a/packages/design-system/src/components/ds-button-v3/ds-button-v3.module.scss b/packages/design-system/src/components/ds-button/ds-button.module.scss similarity index 100% rename from packages/design-system/src/components/ds-button-v3/ds-button-v3.module.scss rename to packages/design-system/src/components/ds-button/ds-button.module.scss diff --git a/packages/design-system/src/components/ds-button-v3/ds-button-v3.stories.module.scss b/packages/design-system/src/components/ds-button/ds-button.stories.module.scss similarity index 100% rename from packages/design-system/src/components/ds-button-v3/ds-button-v3.stories.module.scss rename to packages/design-system/src/components/ds-button/ds-button.stories.module.scss diff --git a/packages/design-system/src/components/ds-button-v3/ds-button-v3.stories.tsx b/packages/design-system/src/components/ds-button/ds-button.stories.tsx similarity index 79% rename from packages/design-system/src/components/ds-button-v3/ds-button-v3.stories.tsx rename to packages/design-system/src/components/ds-button/ds-button.stories.tsx index 247291e71..6a0aeb4ca 100644 --- a/packages/design-system/src/components/ds-button-v3/ds-button-v3.stories.tsx +++ b/packages/design-system/src/components/ds-button/ds-button.stories.tsx @@ -1,27 +1,26 @@ import type { Meta, StoryObj } from '@storybook/react-vite'; import classNames from 'classnames'; import { fn } from 'storybook/test'; -import DsButtonV3 from './ds-button-v3'; -import { DsButtonV3 as DsButtonV3Wrapped } from './index'; +import { DsButton } from './index'; import { - type ButtonV3Color, - buttonV3Colors, - buttonV3Sizes, - type ButtonV3Variant, - buttonV3Variants, -} from './ds-button-v3.types'; -import storyStyles from './ds-button-v3.stories.module.scss'; - -const meta: Meta = { - title: 'Components/ButtonV3', - component: DsButtonV3, + type ButtonColor, + buttonColors, + buttonSizes, + type ButtonVariant, + buttonVariants, +} from './ds-button.types'; +import storyStyles from './ds-button.stories.module.scss'; + +const meta: Meta = { + title: 'Components/Button', + component: DsButton, parameters: { layout: 'centered', }, argTypes: { - color: { control: 'select', options: buttonV3Colors }, - variant: { control: 'select', options: buttonV3Variants }, - size: { control: 'select', options: buttonV3Sizes }, + color: { control: 'select', options: buttonColors }, + variant: { control: 'select', options: buttonVariants }, + size: { control: 'select', options: buttonSizes }, loading: { control: 'boolean' }, disabled: { control: 'boolean' }, className: { table: { disable: true } }, @@ -32,7 +31,7 @@ const meta: Meta = { }; export default meta; -type Story = StoryObj; +type Story = StoryObj; export const Default: Story = { args: { @@ -45,7 +44,7 @@ export const Default: Story = { }; const matrixRows = [ - ...buttonV3Variants.map((v) => ({ label: v, loading: false })), + ...buttonVariants.map((v) => ({ label: v, loading: false })), { label: 'loading', loading: true }, ]; @@ -69,13 +68,13 @@ const onDarkIconMatrixRows = [ { label: 'loading', icon: 'info', variant: 'primary', color: 'light', loading: true }, ] as const; -const MatrixGrid = ({ color }: { color?: ButtonV3Color }) => { +const MatrixGrid = ({ color }: { color?: ButtonColor }) => { const isOnDark = color === 'light'; return (
- {buttonV3Sizes.map((size) => ( + {buttonSizes.map((size) => ( { {label} - {buttonV3Sizes.map((size) => ( + {buttonSizes.map((size) => (
- {size !== 'tiny' ? 'Button' : undefined} - +
))}
@@ -124,15 +123,15 @@ const IconMatrixGrid = ({ rows: ReadonlyArray<{ label: string; icon: 'check_circle' | 'info' | 'delete' | 'keyboard_arrow_down' | 'home'; - variant: ButtonV3Variant; - color: ButtonV3Color; + variant: ButtonVariant; + color: ButtonColor; loading: boolean; }>; isOnDark?: boolean; }) => (
- {buttonV3Sizes.map((size) => ( + {buttonSizes.map((size) => ( - {buttonV3Sizes.map((size) => { + {buttonSizes.map((size) => { const ariaLabel = `${label} ${size}`; return (
- (
- + lg: large / md: small - + - + lg: medium / md: tiny - + - + static: medium - +
), }; diff --git a/packages/design-system/src/components/ds-button/ds-button.tsx b/packages/design-system/src/components/ds-button/ds-button.tsx index 5482465c3..949bae04d 100644 --- a/packages/design-system/src/components/ds-button/ds-button.tsx +++ b/packages/design-system/src/components/ds-button/ds-button.tsx @@ -1,12 +1,60 @@ -import { DsButtonLegacy } from './versions/ds-button-legacy'; -import { DsButtonNew } from './versions/ds-button-new'; -import type { DsButtonUnifiedProps } from './ds-button.types'; - -const DsButton = (props: DsButtonUnifiedProps) => { - if (props.design === 'v1.2') { - return ; - } - return ; +import classNames from 'classnames'; +import { DsIcon, type IconSize } from '../ds-icon'; +import { DsSpinner } from '../ds-spinner'; +import styles from './ds-button.module.scss'; +import type { ButtonSize, DsButtonProps } from './ds-button.types'; + +const iconSizeMap: Record = Object.freeze({ + large: 'small', + medium: 'tiny', + small: 'tiny', + tiny: 'tiny', +}); + +const DsButton = ({ + ref, + className, + style, + children, + icon, + disabled, + loading = false, + color = 'default', + variant = 'primary', + size = 'medium', + selected = false, + type = 'button', + slotProps, + ...rest +}: DsButtonProps) => { + const isIconOnly = icon !== undefined && !children; + + return ( + + ); }; export default DsButton; diff --git a/packages/design-system/src/components/ds-button/ds-button.types.ts b/packages/design-system/src/components/ds-button/ds-button.types.ts index 76ef2b5ac..78976db08 100644 --- a/packages/design-system/src/components/ds-button/ds-button.types.ts +++ b/packages/design-system/src/components/ds-button/ds-button.types.ts @@ -1,17 +1,63 @@ -import type { DsButtonProps as DsButtonLegacyProps } from './versions/ds-button-legacy/ds-button-legacy.types'; -import type { DsButtonProps as DsButtonNewProps } from './versions/ds-button-new/ds-button-new.types'; - -export type DsButtonUnifiedProps = - | (DsButtonLegacyProps & { - /** - * Selects the legacy button implementation. Accepts `DsButtonLegacyProps`. - * Omit or pass `'legacy'` to opt into this branch. - */ - design?: undefined | 'legacy'; - }) - | (DsButtonNewProps & { - /** - * Selects the new (v1.2) button implementation. Accepts `DsButtonNewProps`. - */ - design: 'v1.2'; - }); +import type { ButtonHTMLAttributes, Ref } from 'react'; +import type { DsIconProps, IconType } from '../ds-icon'; + +export const buttonVariants = ['primary', 'secondary', 'tertiary'] as const; +export type ButtonVariant = (typeof buttonVariants)[number]; + +export const buttonColors = ['default', 'error', 'light'] as const; +export type ButtonColor = (typeof buttonColors)[number]; + +export const buttonSizes = ['large', 'medium', 'small', 'tiny'] as const; +export type ButtonSize = (typeof buttonSizes)[number]; + +export interface DsButtonProps extends ButtonHTMLAttributes { + ref?: Ref; + + /** + * - `default` — standard light-UI palette + * - `error` — destructive / danger palette (red tones) + * - `light` — palette for dark-background surfaces (Figma **Type** onDark) + * @default 'default' + */ + color?: ButtonColor; + + /** + * Visual variant of the button: + * - `primary` — filled, highest-emphasis action + * - `secondary` — outlined, medium-emphasis action + * - `tertiary` — borderless, low-emphasis action (text-like) + * @default 'primary' + */ + variant?: ButtonVariant; + + /** + * Size of the button. Controls height, padding, font size, and icon size. + * @default 'medium' + */ + size?: ButtonSize; + + /** + * Whether the button is in a selected/pressed state. Used for toggle buttons + * and segmented controls. + * @default false + */ + selected?: boolean; + + /** + * Leading icon. When set without children, renders as icon-only (square) layout. + */ + icon?: IconType; + + /** + * Shows a spinner as the leading element and disables interaction. + * @default false + */ + loading?: boolean; + + /** + * Props forwarded to internal slots. Use to override defaults on the leading icon. + */ + slotProps?: { + icon?: Omit, 'icon'>; + }; +} diff --git a/packages/design-system/src/components/ds-button/index.ts b/packages/design-system/src/components/ds-button/index.ts index 26a914360..5a72a4fde 100644 --- a/packages/design-system/src/components/ds-button/index.ts +++ b/packages/design-system/src/components/ds-button/index.ts @@ -1,2 +1,18 @@ -export { default as DsButton } from './ds-button'; -export * from './ds-button.types'; +import type { ComponentProps } from 'react'; +import { withResponsiveProps } from '../../utils/responsive'; +import DsButtonBase from './ds-button'; + +export { + buttonVariants, + buttonColors, + buttonSizes, + type ButtonVariant, + type ButtonColor, + type ButtonSize, +} from './ds-button.types'; + +export const DsButton = withResponsiveProps(DsButtonBase, ['size']); + +DsButton.displayName = 'DsButton'; + +export type DsButtonProps = ComponentProps; diff --git a/packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.module.scss b/packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.module.scss deleted file mode 100644 index 91656561d..000000000 --- a/packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.module.scss +++ /dev/null @@ -1,257 +0,0 @@ -@use '../../../../styles/typography'; - -$color-primary-default: var(--color-dap-blue-600); -$color-primary-hover: var(--background-active-status); -$color-primary-active: var(--color-dap-purple-200); -$color-primary-disabled: var(--color-dap-gray-400); - -$color-secondary-default: var(--color-dap-brand-300); -$color-secondary-hover: var(--color-dap-gray-500); -$color-secondary-disabled: var(--color-dap-gray-400); - -$color-error-default: var(--background-error); -$color-error-hover: var(--color-dap-red-100); -$color-error-active: var(--color-dap-red-100); -$color-error-disabled: var(--color-dap-gray-400); - -$color-white: var(--color-dap-gray-050); -$color-neutral-6: var(--color-dap-gray-200); -$color-neutral-4: var(--color-dap-gray-500); - -@mixin button-filled($background, $hover, $active, $disabled) { - background: $background; - color: $color-white; - - &:disabled { - color: $color-neutral-6; - background: $disabled; - } -} - -@mixin button-ghost($color, $hover, $active, $disabled) { - border: 2px solid $color; - color: $color; - - &:disabled { - color: $disabled; - border-color: $disabled; - } -} - -@mixin button-borderless($color, $hover, $active, $disabled) { - color: $color; - border: none; - - &.content { - gap: 4px; - } - - &:hover { - color: $hover; - } - - &:active { - color: $active; - } - - &:disabled { - color: $disabled; - } -} - -@mixin button-round($background, $hover, $active, $disabled) { - @include button-filled($background, $hover, $active, $disabled); - border-radius: 32px; -} - -.button { - @include typography.regular; - display: flex; - align-items: center; - justify-content: center; - border-radius: 4px; - white-space: nowrap; - cursor: pointer; - border: none; - padding: 0; - font: inherit; - background: none; - - &:disabled { - cursor: not-allowed; - } - - &:not(:disabled) { - cursor: pointer; - } - - .content { - display: flex; - align-items: center; - justify-content: center; - gap: 8px; - } - - &.small { - @include typography.p-base; - padding: 6px 12px; - - .content { - gap: 6px; - } - } - - &.medium { - @include typography.p-base; - @include typography.medium; - padding: 12px; - - .content { - gap: 9px; - } - } - - &.large { - @include typography.p-base; - @include typography.medium; - padding: 14px 12px; - } - - &.primary-filled { - @include button-filled( - $color-primary-default, - $color-primary-hover, - $color-primary-active, - $color-primary-disabled - ); - } - - &.primary-ghost { - @include button-ghost( - $color-primary-default, - $color-primary-hover, - $color-primary-active, - $color-neutral-4 - ); - } - - &.primary-borderless { - @include button-borderless( - $color-primary-default, - $color-primary-hover, - $color-primary-active, - $color-primary-disabled - ); - } - - &.primary-round { - @include button-round( - $color-primary-default, - $color-primary-hover, - $color-primary-active, - $color-primary-disabled - ); - } - - &.primary-dashed { - @include button-ghost( - $color-primary-default, - $color-primary-hover, - $color-primary-active, - $color-neutral-4 - ); - border-style: dashed; - } - - &.secondary-filled { - @include button-filled( - $color-secondary-default, - $color-secondary-hover, - $color-secondary-hover, - $color-secondary-disabled - ); - } - - &.secondary-ghost { - @include button-ghost( - $color-secondary-default, - $color-secondary-hover, - $color-secondary-hover, - $color-secondary-disabled - ); - } - - &.secondary-borderless { - @include button-borderless( - $color-secondary-default, - $color-secondary-hover, - $color-secondary-hover, - $color-secondary-disabled - ); - } - - &.secondary-round { - @include button-round( - $color-secondary-default, - $color-secondary-hover, - $color-secondary-hover, - $color-secondary-disabled - ); - } - - &.secondary-dashed { - @include button-ghost( - $color-secondary-default, - $color-secondary-hover, - $color-secondary-hover, - $color-secondary-disabled - ); - border-style: dashed; - } - - &.error-filled { - @include button-filled( - $color-error-default, - $color-error-hover, - $color-error-active, - $color-error-disabled - ); - } - - &.error-ghost { - @include button-ghost( - $color-error-default, - $color-error-hover, - $color-error-active, - $color-error-disabled - ); - } - - &.error-borderless { - @include button-borderless( - $color-error-default, - $color-error-hover, - $color-error-active, - $color-error-disabled - ); - } - - &.error-round { - @include button-round( - $color-error-default, - $color-error-hover, - $color-error-active, - $color-error-disabled - ); - } - - &.error-dashed { - @include button-ghost( - $color-error-default, - $color-error-hover, - $color-error-active, - $color-error-disabled - ); - border-style: dashed; - } -} diff --git a/packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.stories.module.scss b/packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.stories.module.scss deleted file mode 100644 index a3ea9e1dc..000000000 --- a/packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.stories.module.scss +++ /dev/null @@ -1,62 +0,0 @@ -.row { - display: flex; - gap: 20px; - padding-bottom: 20px; - align-items: center; -} - -.combinationsContainer { - display: flex; - flex-direction: column; - gap: 20px; - margin: 20px; -} - -.schemaContainer { - display: flex; - gap: 20px; - border-bottom: 1px solid #eaeaea; -} - -.iconButton-small { - width: 26px; - height: 26px; -} - -.iconButton-medium { - width: 42px; - height: 42px; -} - -.iconButton-large { - width: 56px; - height: 56px; -} - -.schemaTitle { - font-size: 20px; - font-weight: 600; - margin: 0; - padding-bottom: 8px; - min-width: 150px; - text-transform: capitalize; - align-self: center; -} - -.variantContainer { - display: flex; - flex-direction: column; - gap: 16px; -} - -.variantTitle { - font-size: 16px; - font-weight: 500; - margin: 0; - color: #666; - text-transform: capitalize; -} - -.buttonShowcase { - text-transform: capitalize; -} diff --git a/packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.stories.tsx b/packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.stories.tsx deleted file mode 100644 index 5f8c0bc27..000000000 --- a/packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.stories.tsx +++ /dev/null @@ -1,147 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react-vite'; -import { expect, fn, userEvent, within } from 'storybook/test'; -import classNames from 'classnames'; -import { DsIcon } from '../../../ds-icon'; -import styles from './ds-button-legacy.stories.module.scss'; -import DsButtonLegacy from './ds-button-legacy'; -import { - type ButtonSchema, - type ButtonVariant, - buttonSchemas, - buttonSizes, - buttonVariants, -} from './ds-button-legacy.types'; - -const meta: Meta = { - title: 'Components/ButtonLegacy (Deprecated)', - component: DsButtonLegacy, - parameters: { - layout: 'centered', - }, - tags: ['deprecated'], - argTypes: { - schema: { - control: { type: 'select' }, - options: buttonSchemas, - description: 'Button color schema', - table: { - defaultValue: { - summary: 'primary', - }, - }, - }, - variant: { - control: { type: 'select' }, - options: buttonVariants, - table: { - defaultValue: { - summary: 'filled', - }, - }, - }, - size: { - control: { type: 'select' }, - options: buttonSizes, - table: { - defaultValue: { - summary: 'medium', - }, - }, - }, - }, - // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args - args: { onClick: fn() }, -}; -export default meta; -type Story = StoryObj; - -const defaultButtonText = 'Button Text'; - -export const DefaultButton: Story = { - args: { - schema: 'primary', - variant: 'filled', - disabled: false, - children: defaultButtonText, - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const button = canvas.getByRole('button', { name: defaultButtonText }); - await userEvent.click(button); - await expect(button).toBeInTheDocument(); - }, -}; - -export const Showcase: Story = { - parameters: { - layout: 'fullscreen', - }, - render: function Render() { - const renderButtonRow = ( - schema: ButtonSchema, - variant: ButtonVariant, - disabled?: boolean, - showIcon?: boolean, - showTitle?: boolean, - ) => { - return ( -
- {buttonSizes.map((size) => ( - - {showIcon && } - {showTitle && {size} Button} - - ))} -
- ); - }; - - const renderButtonContainer = ( - schema: ButtonSchema, - variant: ButtonVariant, - disabled?: boolean, - title?: boolean, - ) => { - return ( -
- {title && ( -
-

{variant}

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

{schema}

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

Disabled

- - {buttonVariants.map((variant) => renderButtonContainer('primary', variant, true))} -
-
- ); - }, -}; diff --git a/packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.tsx b/packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.tsx deleted file mode 100644 index 4b98c4b17..000000000 --- a/packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import classNames from 'classnames'; -import type React from 'react'; -import styles from './ds-button-legacy.module.scss'; -import type { DsButtonProps } from './ds-button-legacy.types'; - -/** - * Design system Button component - * @deprecated Use [DsButton](../ds-button-new/ds-button-new.tsx) instead. - */ -const DsButton: React.FC = ({ - schema = 'primary', - variant = 'filled', - size = 'medium', - disabled = false, - className, - contentClassName, - children, - ...props -}) => { - const buttonClass = classNames(styles.button, styles[`${schema}-${variant}`], styles[size], className); - - return ( - - ); -}; - -export default DsButton; diff --git a/packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.types.ts b/packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.types.ts deleted file mode 100644 index e87f759c4..000000000 --- a/packages/design-system/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.types.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type React from 'react'; - -export const buttonSchemas = ['primary', 'secondary', 'error'] as const; -export type ButtonSchema = (typeof buttonSchemas)[number]; - -export const buttonVariants = ['filled', 'ghost', 'borderless', 'round', 'dashed'] as const; -export type ButtonVariant = (typeof buttonVariants)[number]; - -export const buttonSizes = ['large', 'medium', 'small'] as const; -export type ButtonSize = (typeof buttonSizes)[number]; - -export interface DsButtonProps extends React.ButtonHTMLAttributes { - /** - * Color schema of the button - * @default 'primary' - */ - schema?: ButtonSchema; - - /** - * Visual variant of the button - * @default 'filled' - */ - variant?: ButtonVariant; - - /** - * Size of the button - * @default 'medium' - */ - size?: ButtonSize; - - /** - * Whether the button is disabled - * @default false - */ - disabled?: boolean; - - /** - * Class name for the button content - */ - contentClassName?: string; -} diff --git a/packages/design-system/src/components/ds-button/versions/ds-button-legacy/index.ts b/packages/design-system/src/components/ds-button/versions/ds-button-legacy/index.ts deleted file mode 100644 index 46f7b822b..000000000 --- a/packages/design-system/src/components/ds-button/versions/ds-button-legacy/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default as DsButtonLegacy } from './ds-button-legacy'; -export * from './ds-button-legacy.types'; diff --git a/packages/design-system/src/components/ds-button/versions/ds-button-new/__tests__/ds-button.browser.test.tsx b/packages/design-system/src/components/ds-button/versions/ds-button-new/__tests__/ds-button.browser.test.tsx deleted file mode 100644 index 0843774f7..000000000 --- a/packages/design-system/src/components/ds-button/versions/ds-button-new/__tests__/ds-button.browser.test.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { describe, expect, it, vi } from 'vitest'; -import DsButton from '../ds-button-new'; -import { page } from 'vitest/browser'; - -describe('DsButton', () => { - it('should trigger onClick handler when clicked', async () => { - // Arrange. - const onClick = vi.fn(); - - await page.render(Click me); - - // Act. - await page.getByRole('button', { name: 'Click me' }).click(); - - // Assert. - expect(onClick).toHaveBeenCalled(); - }); - - it('should support disabled state', async () => { - // Arrange. - const onClick = vi.fn(); - - await page.render( - - Click me - , - ); - - const button = page.getByRole('button', { name: 'Click me', disabled: true }); - - // Act. - await button.click({ force: true }); - - // Assert. - expect(onClick).not.toHaveBeenCalled(); - await expect.element(button).toBeDisabled(); - }); - - it('should support custom class names', async () => { - // Arrange. - const className = 'custom-class'; - - // Act. - await page.render(Click me); - - // Assert. - await expect.element(page.getByRole('button', { name: 'Click me' })).toHaveClass(className); - }); -}); diff --git a/packages/design-system/src/components/ds-button/versions/ds-button-new/ds-button-new.module.scss b/packages/design-system/src/components/ds-button/versions/ds-button-new/ds-button-new.module.scss deleted file mode 100644 index dc83821ca..000000000 --- a/packages/design-system/src/components/ds-button/versions/ds-button-new/ds-button-new.module.scss +++ /dev/null @@ -1,604 +0,0 @@ -@use '../../../../styles/typography'; - -$button-size-tiny: 16px; -$button-size-small: 28px; -$button-size-default: 32px; -$button-size-large: 40px; - -@mixin content-size-tiny { - @include typography.body-xs-md; - height: $button-size-tiny; - border-radius: var(--4xs); - --icon-width-tiny: 13px; -} - -@mixin content-size-small { - @include typography.body-xs-md; - height: $button-size-small; -} - -@mixin content-size-medium { - height: $button-size-default; -} - -@mixin content-size-large { - @include typography.body-md-md; - height: $button-size-large; -} - -.button { - @include typography.body-sm-md; - display: flex; - align-items: center; - justify-content: center; - white-space: nowrap; - cursor: pointer; - background: none; - outline: none; - padding-block: 0; - padding-inline: 0; - border: 2px solid transparent; - border-radius: var(--3xs); - - .content { - display: flex; - align-items: center; - justify-content: center; - gap: var(--3xs); - padding: 0 var(--sm); - border: 1px solid transparent; - border-radius: var(--3xs); - flex-grow: 1; - } - - &.tiny .content { - @include content-size-tiny; - } - - &.small .content { - @include content-size-small; - } - - &.medium .content { - @include content-size-medium; - } - - &.large .content { - @include content-size-large; - } - - &.iconButton { - aspect-ratio: 1 / 1; - align-items: stretch; - - .content { - flex: 1; - padding: 0; - } - - &.large .content { - height: $button-size-large; - width: $button-size-large; - } - - &.medium .content { - height: $button-size-default; - width: $button-size-default; - } - - &.small .content { - height: $button-size-small; - width: $button-size-small; - } - - &.tiny .content { - height: $button-size-tiny; - width: $button-size-tiny; - } - } - - &.primary-filled { - border: 2px solid transparent; - color: var(--font-on-action); - - .content { - background: var(--background-primary); - } - - &:hover { - .content { - background: var(--background-primary-hover); - } - } - - &:focus-visible { - border-color: var(--background-primary); - - .content { - border-color: var(--background); - border-radius: calc(var(--3xs) / 2); - } - } - - &:disabled { - cursor: not-allowed; - - .content { - background: var(--background-disable); - border-color: var(--background-disable); - } - } - } - - &.secondary-filled { - background: transparent; - border: 2px solid transparent; - color: var(--background-action); - - .content { - background: var(--background); - border-color: var(--border); - } - - &:hover { - &:not(:disabled) { - .content { - border-color: var(--background-action-hover); - background: var(--background-action-hover-weak); - } - } - - &:focus-visible { - background: var(--background); - - .content { - border-color: var(--background-action-secondary); - } - } - } - - &:focus-visible { - background: var(--background); - border-color: var(--background-action-hover); - - .content { - border-color: var(--background); - background: var(--background-action-hover-weak); - } - } - - &:disabled { - cursor: not-allowed; - color: var(--background-disable); - - .content { - border-color: var(--background-disable); - } - } - } - - &.tertiary-filled { - background: transparent; - border: 2px solid transparent; - color: var(--background-action); - - .content { - background: transparent; - border-color: transparent; - } - - &:hover:not(:disabled) { - .content { - border-color: var(--border); - background: var(--background-action-hover-weak); - } - } - - &:active { - .content { - border-color: var(--border); - } - } - - &:focus-visible { - background: var(--background); - border-color: var(--background-action-hover); - - .content { - border-color: var(--background); - background: var(--background-action-hover-weak); - border-radius: calc(var(--3xs) / 2); - } - } - - &:disabled { - cursor: not-allowed; - color: var(--background-disable); - } - } - - &.secondary-ghost { - background: transparent; - border: 2px solid transparent; - color: var(--background-primary); - - .content { - background: var(--background-action-secondary); - border-color: var(--background-primary); - } - - &:hover { - &:not(:disabled) { - .content { - background: var(--background-action-secondary-hover); - } - } - - &:focus-visible { - .content { - border-color: var(--background-action-secondary); - } - } - } - - &:focus-visible { - background: var(--background); - border-color: var(--background-primary); - - .content { - border-color: var(--background); - background: var(--background-action-secondary-hover); - } - } - - &:disabled { - cursor: not-allowed; - color: var(--background-disable); - - .content { - border-color: var(--background-disable); - } - } - } - - &.tertiary-ghost { - background: transparent; - border: 2px solid transparent; - color: var(--background-primary); - - .content { - background: transparent; - border-color: transparent; - } - - &:hover:not(:disabled) { - .content { - background: var(--background-action-secondary-hover); - } - } - - &:active { - .content { - border-color: var(--border); - } - } - - &:focus-visible { - background: var(--background); - border-color: var(--background-primary); - - .content { - border-color: var(--background); - background: var(--background-action-secondary-hover); - } - } - - &:disabled { - cursor: not-allowed; - color: var(--background-disable); - } - } - - &.primary-danger { - border: 2px solid transparent; - color: var(--font-on-action); - - .content { - background: var(--background-error); - } - - &:hover { - .content { - background: var(--background-error-hover); - } - } - - &:focus-visible { - border-color: var(--background-error); - - .content { - border-color: var(--background); - border-radius: calc(var(--3xs) / 2); - } - } - - &:disabled { - cursor: not-allowed; - - .content { - background: var(--background-disable); - border-color: var(--background-disable); - } - } - } - - &.secondary-danger { - background: transparent; - border: 2px solid transparent; - color: var(--background-error); - - .content { - background: var(--background); - border-color: var(--background-error); - } - - &:hover { - &:not(:disabled) { - color: var(--background-error-hover); - - .content { - border-color: var(--background-error-hover); - background: var(--background-error-secondary-hover); - } - } - - &:focus-visible { - .content { - border-color: var(--background-action-secondary); - } - } - } - - &:focus-visible { - background: var(--background); - border-color: var(--background-error); - - .content { - color: var(--background-error-hover); - border-color: var(--background); - background: var(--background-error-secondary-hover); - } - } - - &:disabled { - cursor: not-allowed; - color: var(--background-disable); - - .content { - border-color: var(--background-disable); - } - } - } - - &.tertiary-danger { - background: transparent; - border: 2px solid transparent; - color: var(--background-error); - - .content { - background: transparent; - border-color: transparent; - } - - &:hover:not(:disabled) { - .content { - border-color: var(--background-error-hover); - background: var(--background-error-secondary-hover); - } - - &:focus-visible { - background: var(--background); - - .content { - border-color: var(--background); - } - } - } - - &:focus-visible { - background: var(--background); - border-color: var(--background-error); - - .content { - border-color: var(--background); - background: var(--background-error-secondary-hover); - } - } - - &:disabled { - cursor: not-allowed; - background: var(--background); - color: var(--background-disable); - } - } - - &.primary-dark { - border: 2px solid transparent; - color: var(--font-on-action); - - .content { - background: var(--light-buttons-primary); - } - - &:hover:not(:disabled) { - .content { - background: var(--light-buttons-primary-hover); - } - } - - &:active:not(:disabled) { - .content { - background: var(--light-buttons-primary-hover); - } - } - - &:focus-visible { - background: var(--background-brand); - border-color: var(--light-buttons-primary); - - .content { - border-color: var(--font-on-action); - border-radius: calc(var(--3xs) / 2); - } - } - - &:disabled { - cursor: not-allowed; - - .content { - background: var(--background-action-inverse); - color: var(--background-deselected); - } - } - } - - &.secondary-dark { - background: transparent; - border: 2px solid transparent; - color: var(--light-buttons-primary-hover); - - .content { - background: transparent; - border-color: var(--light-buttons-primary-hover); - } - - &:hover:not(:disabled) { - .content { - background: var(--light-buttons-secondary-background); - border-color: var(--light-buttons-secondary-hover); - color: var(--light-buttons-secondary-hover); - } - } - - &:active:not(:disabled) { - .content { - background: transparent; - border-color: var(--light-buttons-secondary-hover); - color: var(--light-buttons-secondary-hover); - } - } - - &:focus-visible { - background: transparent; - border-color: var(--light-buttons-secondary-hover); - - .content { - border-color: transparent; - background: var(--light-buttons-secondary-background); - color: var(--light-buttons-secondary-hover); - border-radius: calc(var(--3xs) / 2); - } - } - - &:disabled { - cursor: not-allowed; - color: var(--background-deselected); - - .content { - border-color: var(--background-deselected); - } - } - } - - &.secondary-light-dark { - background: transparent; - border: 2px solid transparent; - color: var(--light-buttons-secondary-light); - - .content { - background: transparent; - border-color: var(--light-buttons-secondary-light); - } - - &:hover:not(:disabled) { - .content { - background: var(--light-buttons-secondary-light-hover-background); - border-color: var(--border); - color: var(--light-buttons-tertiary); - } - } - - &:active:not(:disabled) { - .content { - background: transparent; - border-color: var(--background-secondary); - color: var(--font-on-action); - } - } - - &:focus-visible { - background: transparent; - border-color: var(--light-buttons-tertiary); - border-radius: var(--2xs); - - .content { - background: var(--Dark-Buttons-secondary-light-hover-background); - color: var(--light-buttons-tertiary); - border-radius: var(--3xs); - } - } - - &:disabled { - cursor: not-allowed; - color: var(--background-deselected); - - .content { - border-color: var(--background-deselected); - } - } - } - - &.tertiary-dark { - background: transparent; - border: 2px solid transparent; - color: var(--light-buttons-tertiary); - - .content { - background: transparent; - border-color: transparent; - } - - &:hover:not(:disabled) { - .content { - background: var(--light-buttons-secondary-light-hover-background); - border-color: var(--border); - color: var(--light-buttons-tertiary); - } - } - - &:active:not(:disabled) { - .content { - background: transparent; - border-color: var(--background-secondary); - color: var(--font-on-action); - } - } - - &:focus-visible { - background: transparent; - border-color: var(--light-buttons-tertiary); - border-radius: var(--2xs); - - .content { - background: var(--Dark-Buttons-secondary-light-hover-background); - color: var(--light-buttons-tertiary); - border-radius: var(--3xs); - } - } - - &:disabled { - cursor: not-allowed; - color: var(--background-deselected); - } - } -} diff --git a/packages/design-system/src/components/ds-button/versions/ds-button-new/ds-button-new.stories.module.scss b/packages/design-system/src/components/ds-button/versions/ds-button-new/ds-button-new.stories.module.scss deleted file mode 100644 index 408c5afd9..000000000 --- a/packages/design-system/src/components/ds-button/versions/ds-button-new/ds-button-new.stories.module.scss +++ /dev/null @@ -1,36 +0,0 @@ -.showcaseContainer { - background: var(--color-dap-gray-100); - padding: 24px; - overflow: auto; -} - -.showcaseTable { - border-collapse: collapse; - width: 100%; -} - -.showcaseHeader { - text-align: center; - vertical-align: middle; - padding: 16px; -} - -.showcaseCell { - padding: 16px; - text-align: center; - vertical-align: middle; -} - -.showcaseCellBold { - font-weight: bold; - padding-right: 8px; -} - -.showcaseCellInline { - display: inline-block; -} - -.showcaseCellDark { - background: var(--brand-500-default); - color: var(--secondary-050); -} diff --git a/packages/design-system/src/components/ds-button/versions/ds-button-new/ds-button-new.stories.tsx b/packages/design-system/src/components/ds-button/versions/ds-button-new/ds-button-new.stories.tsx deleted file mode 100644 index 7f452df82..000000000 --- a/packages/design-system/src/components/ds-button/versions/ds-button-new/ds-button-new.stories.tsx +++ /dev/null @@ -1,213 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react-vite'; -import { fn } from 'storybook/test'; -import classNames from 'classnames'; -import { DsIcon } from '../../../ds-icon'; -import { DsSpinner } from '../../../ds-spinner'; -import DsButtonNew from './ds-button-new'; -import { buttonSizes, buttonTypes, buttonVariants } from './ds-button-new.types'; -import styles from './ds-button-new.stories.module.scss'; - -const meta: Meta = { - // eslint-disable-next-line @drivenets/ds-internal/consistent-story-titles -- We want to show user something different than code. - title: 'Components/Button', - component: DsButtonNew, - parameters: { - // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout - layout: 'centered', - }, - // More on argTypes: https://storybook.js.org/docs/api/argtypes - argTypes: { - buttonType: { - control: { type: 'select' }, - options: buttonTypes, - description: 'Button type', - table: { - defaultValue: { - summary: 'primary', - }, - }, - }, - variant: { - control: { type: 'select' }, - options: buttonVariants, - table: { - defaultValue: { - summary: 'filled', - }, - }, - }, - size: { - control: { type: 'select' }, - options: buttonSizes, - table: { - defaultValue: { - summary: 'medium', - }, - }, - }, - }, - // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args - args: { onClick: fn() }, -}; -export default meta; -type Story = StoryObj; - -const defaultButtonText = 'Button Text'; - -export const DefaultButton: Story = { - args: { - buttonType: 'primary', - variant: 'filled', - size: 'large', - disabled: false, - children: ( - <> -
- + NetGen - +
@@ -185,7 +185,7 @@ const ContentHeaderActions = () => { return ( <> - + { }, }} /> - + New - + ); }; @@ -285,9 +285,9 @@ export const Empty: Story = { No matching records found. - + Clear filters - + diff --git a/packages/design-system/src/components/ds-chip-group/ds-chip-group.module.scss b/packages/design-system/src/components/ds-chip-group/ds-chip-group.module.scss deleted file mode 100644 index b80bb0971..000000000 --- a/packages/design-system/src/components/ds-chip-group/ds-chip-group.module.scss +++ /dev/null @@ -1,59 +0,0 @@ -.container { - display: flex; - align-items: center; - gap: var(--standard); - padding: var(--xs) var(--standard); - background: var(--background-secondary-hover); - border: 1px solid var(--outline-weak); - border-radius: var(--3xs); -} - -.chipsWrapper { - display: flex; - flex: 1; - flex-wrap: wrap; - align-items: center; - gap: var(--xs); - max-height: var(--3xl); - overflow: hidden; - - .hidden { - visibility: hidden; - position: absolute; - pointer-events: none; - } -} - -.label { - color: var(--font-secondary); - white-space: nowrap; -} - -.expandChip { - cursor: pointer; -} - -.dialog { - position: fixed; - background: var(--background); - border: 1px solid var(--border-secondary); - border-radius: var(--3xs); - box-shadow: 0 5px 15px 0 rgba(0, 0, 0, 0.15); - padding: var(--xs); - max-height: 80vh; - overflow-y: auto; - z-index: 1001; - min-width: 300px; - max-width: 500px; -} - -.dialogContent { - display: flex; - flex-direction: column; - gap: 10px; -} - -.itemCheckbox { - display: flex; - align-items: center; -} diff --git a/packages/design-system/src/components/ds-chip-group/ds-chip-group.stories.module.scss b/packages/design-system/src/components/ds-chip-group/ds-chip-group.stories.module.scss deleted file mode 100644 index 8ccb864c8..000000000 --- a/packages/design-system/src/components/ds-chip-group/ds-chip-group.stories.module.scss +++ /dev/null @@ -1,24 +0,0 @@ -.container { - display: flex; - flex-direction: column; - gap: 16px; -} - -.controlsContainer { - display: flex; - gap: 8px; -} - -.addButton { - padding: 8px 16px; - border-radius: 4px; - border: 1px solid #ccc; - background: white; - cursor: pointer; -} - -.infoText { - font-size: 12px; - color: #666; - align-self: center; -} diff --git a/packages/design-system/src/components/ds-chip-group/ds-chip-group.stories.tsx b/packages/design-system/src/components/ds-chip-group/ds-chip-group.stories.tsx deleted file mode 100644 index 18202d23e..000000000 --- a/packages/design-system/src/components/ds-chip-group/ds-chip-group.stories.tsx +++ /dev/null @@ -1,111 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react-vite'; -import { useState } from 'react'; -import DsChipGroup from './ds-chip-group'; -import type { ChipItem } from './ds-chip-group.types'; -import styles from './ds-chip-group.stories.module.scss'; - -/** - * @deprecated This component is deprecated. Use `DsTagFilter` instead. - * @see {@link ../ds-tag-filter/ds-tag-filter.stories} for examples of the replacement component. - */ -const meta: Meta = { - title: 'Components/ChipGroup (Deprecated)', - component: DsChipGroup, - parameters: { - layout: 'padded', - docs: { - description: { - component: - '**Deprecated**: This component is deprecated. Please use `DsTagFilter` instead. See the TagGroup stories for the replacement component.', - }, - }, - }, - tags: ['deprecated'], - argTypes: { - items: { - control: 'object', - description: 'Array of chip items to display', - }, - onClearAll: { - action: 'clear-all', - description: 'Callback when "Clear all" is clicked', - }, - onItemDelete: { - action: 'delete-item', - description: 'Callback when item is deleted', - }, - }, -}; - -export default meta; -type Story = StoryObj; - -const sampleFilters: ChipItem[] = [ - { id: '1', label: 'Status: Active' }, - { id: '2', label: 'Running: From 100 to 10,000' }, - { id: '3', label: 'Completed from 20,000 to 100,000' }, - { id: '4', label: 'Executor: Category 1, Layer 1 transporter' }, - { id: '5', label: 'Executor: Category 2, Layer 11 transporter' }, - { id: '6', label: 'Executor: Category 2, Layer 12 transporter' }, - { id: '7', label: 'Executor: Category 2, Layer 13 transporter' }, - { id: '8', label: 'Version: 000.0001-3' }, - { id: '9', label: 'Version: 000.0001-4' }, - { id: '10', label: 'Version: 000.0001-5' }, - { id: '11', label: 'Version: 000.0001-6' }, - { id: '12', label: 'Last editor: Mary Levin' }, - { id: '13', label: 'Last editor: Emery Franco' }, -]; - -export const Default: Story = { - render: function Render() { - const [filters, setFilters] = useState(sampleFilters); - - const handleClearAll = () => { - setFilters([]); - }; - - const handleAddFilter = () => { - const newId = `new-${String(Date.now())}`; - setFilters((prev) => [ - ...prev, - { - id: newId, - label: `New Filter ${String(prev.length + 1)}`, - }, - ]); - }; - - const handleFilterDelete = (filter: ChipItem) => { - setFilters((prev) => prev.filter((f) => f.id !== filter.id)); - }; - - const handleFilterSelect = (filter: ChipItem) => { - setFilters((prev) => prev.map((f) => (f.id === filter.id ? { ...f, selected: !f.selected } : f))); - }; - - return ( -
- -
- -

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-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-bubble/ds-comment-bubble.types.ts b/packages/design-system/src/components/ds-comment-bubble/ds-comment-bubble.types.ts index b2aaf4465..8caa822ff 100644 --- a/packages/design-system/src/components/ds-comment-bubble/ds-comment-bubble.types.ts +++ b/packages/design-system/src/components/ds-comment-bubble/ds-comment-bubble.types.ts @@ -91,7 +91,7 @@ export interface DsCommentBubbleProps { */ onMessageResolved?: (messageId: string) => void; /** - * Optional reference chip shown in the bubble header. A string is rendered as a + * Optional reference tag shown in the bubble header. A string is rendered as a * `DsTag` with a sell icon; any other `ReactNode` is rendered verbatim. */ referenceTag?: ReactNode; diff --git a/packages/design-system/src/components/ds-comment-card/ds-comment-card.tsx b/packages/design-system/src/components/ds-comment-card/ds-comment-card.tsx index 8e7020c37..ebf8dac7f 100644 --- a/packages/design-system/src/components/ds-comment-card/ds-comment-card.tsx +++ b/packages/design-system/src/components/ds-comment-card/ds-comment-card.tsx @@ -101,9 +101,7 @@ export const DsCommentCard = ({ {showMoreMenu && ( - - - + {onToggleActionRequired && ( @@ -137,14 +135,12 @@ export const DsCommentCard = ({ {onResolve && ( - - + /> )} diff --git a/packages/design-system/src/components/ds-comment-card/ds-comment-card.types.ts b/packages/design-system/src/components/ds-comment-card/ds-comment-card.types.ts index 0217a7233..2a8c0e8f9 100644 --- a/packages/design-system/src/components/ds-comment-card/ds-comment-card.types.ts +++ b/packages/design-system/src/components/ds-comment-card/ds-comment-card.types.ts @@ -73,16 +73,16 @@ export interface CommentData { */ isResolved: boolean; /** - * Ordered list of messages in the thread, from oldest to newest. The first + * Ordered the list of messages in the thread, from oldest to newest. The first * message is typically the top-level comment body. */ messages: CommentMessage[]; /** - * Optional tag labels used for filtering and grouping in the comments drawer. + * Optional tag labels used for filtering and grouping in the comment's drawer. */ labels?: string[]; /** - * Optional reference chip shown in the card header. A string renders as a tag; + * Optional reference tag shown in the card header. A string renders as a tag; * any other `ReactNode` renders verbatim. */ referenceTag?: ReactNode; diff --git a/packages/design-system/src/components/ds-comments-drawer/comments-filters.ts b/packages/design-system/src/components/ds-comments-drawer/comments-filters.ts index f605955c0..0f3dd1157 100644 --- a/packages/design-system/src/components/ds-comments-drawer/comments-filters.ts +++ b/packages/design-system/src/components/ds-comments-drawer/comments-filters.ts @@ -3,6 +3,15 @@ import type { FilterTag, CommentsFilterState } from './comments-filters.types'; export type { FilterTag, CommentsFilterState } from './comments-filters.types'; +/** Formats using local calendar date (not UTC) to stay consistent with applyFilters, + * which treats dateFrom/dateTo as local dates via setHours(0,0,0,0). */ +const formatFilterDateForTag = (d: Date): string => { + const year = d.getFullYear(); + const month = String(d.getMonth() + 1).padStart(2, '0'); + const day = String(d.getDate()).padStart(2, '0'); + return `${String(year)}-${month}-${day}`; +}; + export const initialFilterState: CommentsFilterState = { authors: [], labels: [], @@ -42,6 +51,7 @@ export const applyFilters = ( if (filters.dateFrom) { const fromDate = new Date(filters.dateFrom); + fromDate.setHours(0, 0, 0, 0); if (comment.createdAt < fromDate) { return false; } @@ -89,8 +99,8 @@ export const filtersToTags = (filters: CommentsFilterState, authorMap: Map = {}): CommentData => ({ id: '1', numericId: 1, author: makeAuthor('user-1', 'Alice'), - createdAt: new Date('2025-06-15'), + createdAt: new Date(2025, 5, 15), isResolved: false, messages: [], ...overrides, @@ -125,11 +125,11 @@ describe('comments-filters', () => { it('should filter by dateFrom', () => { const comments = [ - makeComment({ id: '1', createdAt: new Date('2025-01-01') }), - makeComment({ id: '2', createdAt: new Date('2025-06-15') }), - makeComment({ id: '3', createdAt: new Date('2025-12-31') }), + makeComment({ id: '1', createdAt: new Date(2025, 0, 1) }), + makeComment({ id: '2', createdAt: new Date(2025, 5, 15) }), + makeComment({ id: '3', createdAt: new Date(2025, 11, 31) }), ]; - const filters: CommentsFilterState = { ...emptyFilters, dateFrom: '2025-06-01' }; + const filters: CommentsFilterState = { ...emptyFilters, dateFrom: new Date(2025, 5, 1) }; const result = applyFilters(comments, filters, neverActionRequired); @@ -139,11 +139,11 @@ describe('comments-filters', () => { it('should filter by dateTo (inclusive end of day)', () => { const comments = [ - makeComment({ id: '1', createdAt: new Date('2025-01-01') }), - makeComment({ id: '2', createdAt: new Date('2025-06-15T23:59:59.999') }), - makeComment({ id: '3', createdAt: new Date('2025-12-31') }), + makeComment({ id: '1', createdAt: new Date(2025, 0, 1) }), + makeComment({ id: '2', createdAt: new Date(2025, 5, 15, 23, 59, 59, 999) }), + makeComment({ id: '3', createdAt: new Date(2025, 11, 31) }), ]; - const filters: CommentsFilterState = { ...emptyFilters, dateTo: '2025-06-15' }; + const filters: CommentsFilterState = { ...emptyFilters, dateTo: new Date(2025, 5, 15) }; const result = applyFilters(comments, filters, neverActionRequired); @@ -153,14 +153,14 @@ describe('comments-filters', () => { it('should filter by date range', () => { const comments = [ - makeComment({ id: '1', createdAt: new Date('2025-01-01') }), - makeComment({ id: '2', createdAt: new Date('2025-06-15') }), - makeComment({ id: '3', createdAt: new Date('2025-12-31') }), + makeComment({ id: '1', createdAt: new Date(2025, 0, 1) }), + makeComment({ id: '2', createdAt: new Date(2025, 5, 15) }), + makeComment({ id: '3', createdAt: new Date(2025, 11, 31) }), ]; const filters: CommentsFilterState = { ...emptyFilters, - dateFrom: '2025-03-01', - dateTo: '2025-09-01', + dateFrom: new Date(2025, 2, 1), + dateTo: new Date(2025, 8, 1), }; const result = applyFilters(comments, filters, neverActionRequired); @@ -319,8 +319,8 @@ describe('comments-filters', () => { it('should create date range tag with both dates', () => { const filters: CommentsFilterState = { ...emptyFilters, - dateFrom: '2025-01-01', - dateTo: '2025-12-31', + dateFrom: new Date(2025, 0, 1), + dateTo: new Date(2025, 11, 31), }; const result = filtersToTags(filters, authorMap); @@ -335,7 +335,7 @@ describe('comments-filters', () => { }); it('should create date range tag with only dateFrom', () => { - const filters: CommentsFilterState = { ...emptyFilters, dateFrom: '2025-01-01' }; + const filters: CommentsFilterState = { ...emptyFilters, dateFrom: new Date(2025, 0, 1) }; const result = filtersToTags(filters, authorMap); @@ -344,7 +344,7 @@ describe('comments-filters', () => { }); it('should create date range tag with only dateTo', () => { - const filters: CommentsFilterState = { ...emptyFilters, dateTo: '2025-12-31' }; + const filters: CommentsFilterState = { ...emptyFilters, dateTo: new Date(2025, 11, 31) }; const result = filtersToTags(filters, authorMap); @@ -382,8 +382,8 @@ describe('comments-filters', () => { const filters: CommentsFilterState = { statuses: ['resolved'], authors: ['user-1'], - dateFrom: '2025-01-01', - dateTo: '2025-12-31', + dateFrom: new Date(2025, 0, 1), + dateTo: new Date(2025, 11, 31), labels: ['bug'], }; @@ -444,8 +444,8 @@ describe('comments-filters', () => { it('should clear both dateFrom and dateTo when removing a date tag', () => { const filters: CommentsFilterState = { ...emptyFilters, - dateFrom: '2025-01-01', - dateTo: '2025-12-31', + dateFrom: new Date(2025, 0, 1), + dateTo: new Date(2025, 11, 31), }; const tag: FilterTag = { id: 'date-range', diff --git a/packages/design-system/src/components/ds-comments-drawer/components/comments-filter-modal/comments-filter-modal.tsx b/packages/design-system/src/components/ds-comments-drawer/components/comments-filter-modal/comments-filter-modal.tsx index 8359eacdd..277a6d1d6 100644 --- a/packages/design-system/src/components/ds-comments-drawer/components/comments-filter-modal/comments-filter-modal.tsx +++ b/packages/design-system/src/components/ds-comments-drawer/components/comments-filter-modal/comments-filter-modal.tsx @@ -7,7 +7,7 @@ import { DsTypography } from '../../../ds-typography'; import { DsCheckbox } from '../../../ds-checkbox'; import { DsButton } from '../../../ds-button'; import { DsIcon } from '../../../ds-icon'; -import { DsDateInput } from '../../../ds-date-input'; +import { DsDatePicker } from '../../../ds-date-picker'; export const CommentsFilterModal = ({ open, @@ -42,12 +42,12 @@ export const CommentsFilterModal = ({ }); }; - const handleDateFromChange = (value: string | undefined) => { - onFiltersChange({ ...filters, dateFrom: value }); + const handleDateFromChange = (value: Date | null) => { + onFiltersChange({ ...filters, dateFrom: value ?? undefined }); }; - const handleDateToChange = (value: string | undefined) => { - onFiltersChange({ ...filters, dateTo: value }); + const handleDateToChange = (value: Date | null) => { + onFiltersChange({ ...filters, dateTo: value ?? undefined }); }; const filterCounts = { @@ -160,10 +160,10 @@ export const CommentsFilterModal = ({ From - @@ -172,10 +172,10 @@ export const CommentsFilterModal = ({ To - @@ -204,12 +204,12 @@ export const CommentsFilterModal = ({ - + Clear all - + Apply diff --git a/packages/design-system/src/components/ds-comments-drawer/ds-comments-drawer.stories.tsx b/packages/design-system/src/components/ds-comments-drawer/ds-comments-drawer.stories.tsx index 010c5d038..abdb129e5 100644 --- a/packages/design-system/src/components/ds-comments-drawer/ds-comments-drawer.stories.tsx +++ b/packages/design-system/src/components/ds-comments-drawer/ds-comments-drawer.stories.tsx @@ -465,8 +465,8 @@ Interactive story demonstrating the filter functionality. - **Date range**: Filter by creation date - **Labels**: Filter by tags (Bug, High Priority, Feature Request, Documentation, Enhancement, Question) 3. Click **Apply** to see the filtered results -4. Selected filters appear as chips below the toolbar -5. Click on a chip to remove that filter, or use **Clear all** to remove all filters +4. Selected filters appear as tags below the toolbar +5. Click on a tag to remove that filter, or use **Clear all** to remove all filters **Current mock data:** - 4 unresolved comments with various authors and labels diff --git a/packages/design-system/src/components/ds-comments-drawer/ds-comments-drawer.tsx b/packages/design-system/src/components/ds-comments-drawer/ds-comments-drawer.tsx index 819129864..9a5c835e7 100644 --- a/packages/design-system/src/components/ds-comments-drawer/ds-comments-drawer.tsx +++ b/packages/design-system/src/components/ds-comments-drawer/ds-comments-drawer.tsx @@ -114,9 +114,7 @@ export const DsCommentsDrawer = ({ {commentCount} Comments
- - - +
@@ -136,19 +134,17 @@ export const DsCommentsDrawer = ({ /> - - + /> {hasResolved && ( - + {showResolved ? 'Hide' : 'Show'} resolved ({resolvedCount}) diff --git a/packages/design-system/src/components/ds-confirmation/ds-confirmation.stories.module.scss b/packages/design-system/src/components/ds-confirmation/ds-confirmation.stories.module.scss deleted file mode 100644 index ca51df58e..000000000 --- a/packages/design-system/src/components/ds-confirmation/ds-confirmation.stories.module.scss +++ /dev/null @@ -1,31 +0,0 @@ -.customBodyContainer { - display: flex; - flex-direction: column; - gap: 16px; -} - -.customBodyText { - margin: 0; - color: var(--font-secondary); -} - -.radioGroup { - display: flex; - flex-direction: column; - gap: 8px; -} - -.radioLabel { - display: flex; - align-items: center; - gap: 8px; - cursor: pointer; -} - -.infoNote { - background: var(--background-secondary); - padding: 12px; - border-radius: 6px; - font-size: 14px; - color: var(--font-secondary); -} diff --git a/packages/design-system/src/components/ds-confirmation/ds-confirmation.stories.tsx b/packages/design-system/src/components/ds-confirmation/ds-confirmation.stories.tsx deleted file mode 100644 index 8d9ac6256..000000000 --- a/packages/design-system/src/components/ds-confirmation/ds-confirmation.stories.tsx +++ /dev/null @@ -1,257 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react-vite'; -import { useState } from 'react'; -import { DsConfirmation } from './ds-confirmation'; -import { DsButton } from '../ds-button'; -import styles from './ds-confirmation.stories.module.scss'; - -/** - * @deprecated This component is deprecated. Use DsModal instead. - * @see {@link ../ds-modal/ds-modal.stories} for examples of the replacement component. - */ -const meta: Meta = { - title: 'Components/Confirmation (Deprecated)', - component: DsConfirmation, - tags: ['deprecated'], - parameters: { - layout: 'centered', - }, - argTypes: { - open: { - control: { type: 'boolean' }, - }, - }, -}; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - render: function Render() { - const [open, setOpen] = useState(false); - return ( - <> - setOpen(true)}>Open Confirmation - - - Confirm Action - - - Are you sure you want to proceed with this action? - - - { - console.log('Reject clicked'); - setOpen(false); - }} - > - No - - { - console.log('Accept clicked'); - setOpen(false); - }} - > - Yes - - - - - - ); - }, -}; - -export const WithCancel: Story = { - render: function Render() { - const [open, setOpen] = useState(false); - return ( - <> - setOpen(true)}>Open Confirmation - - - Save Changes - - - Do you want to save your changes before closing? - - { - console.log('Cancel clicked'); - setOpen(false); - }} - > - Cancel - - - { - console.log('Discard clicked'); - setOpen(false); - }} - > - Discard - - { - console.log('Save clicked'); - setOpen(false); - }} - > - Save - - - - - - ); - }, -}; - -export const Danger: Story = { - render: function Render() { - const [open, setOpen] = useState(false); - return ( - <> - setOpen(true)}>Open Confirmation - - - Delete Item - - - - Are you sure you want to delete this item? This action cannot be undone. - - - - { - console.log('Cancel clicked'); - setOpen(false); - }} - > - Cancel - - { - console.log('Delete clicked'); - setOpen(false); - }} - > - Delete - - - - - - ); - }, -}; - -export const CustomBody: Story = { - render: function Render() { - const [open, setOpen] = useState(false); - const [selectedOption, setSelectedOption] = useState('option1'); - - return ( - <> - setOpen(true)}>Open Custom Confirmation - - - Advanced Configuration - - - -
-

Please select your preferred configuration option:

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

Allowed: Current month to next 3 months only

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

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

-

Validation happens on blur

-

Invalid format resets to empty

-

Current value: {JSON.stringify(value)}

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

Default Modal

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

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

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

Custom Modal Demo

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

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

Modal with Icon

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

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

Modal Without Header

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

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

This is a panel

diff --git a/packages/design-system/src/components/ds-panel/ds-panel.stories.tsx b/packages/design-system/src/components/ds-panel/ds-panel.stories.tsx index b27f3f8c7..2a4c71b66 100644 --- a/packages/design-system/src/components/ds-panel/ds-panel.stories.tsx +++ b/packages/design-system/src/components/ds-panel/ds-panel.stories.tsx @@ -1,6 +1,6 @@ import type { Meta, StoryObj } from '@storybook/react-vite'; import { DsPanel } from './'; -import { DsButton } from '../ds-button/'; +import { DsButton } from '../ds-button'; import { DsStepper, DsStep, DsStepContent, DsNextStepButton } from '../ds-stepper'; import { useState } from 'react'; import type { DsPanelVariant } from './ds-panel.types'; diff --git a/packages/design-system/src/components/ds-popover/ds-popover.stories.tsx b/packages/design-system/src/components/ds-popover/ds-popover.stories.tsx index 917f29d40..301bc9b9b 100644 --- a/packages/design-system/src/components/ds-popover/ds-popover.stories.tsx +++ b/packages/design-system/src/components/ds-popover/ds-popover.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from '@storybook/react-vite'; -import { DsButtonV3 } from '../ds-button-v3'; +import { DsButton } from '../ds-button'; import { DsIcon } from '../ds-icon'; import { DsAvatar } from '../ds-avatar'; import { DsDivider } from '../ds-divider'; @@ -51,7 +51,7 @@ export const WithContentItemsAndCTA: Story = { render: (args) => ( - Release lock + Release lock }> @@ -78,9 +78,9 @@ export const WithContentItemsAndCTA: Story = { - + Confirm - +
@@ -91,7 +91,7 @@ export const SingleContentItem: Story = { render: (args) => ( - Set element status + Set element status }> @@ -117,7 +117,7 @@ export const SingleContentItem: Story = { export const Legacy: Story = { tags: ['deprecated'], render: () => ( - Legacy call form}> + Legacy call form}> }> Legacy call form @@ -134,7 +134,7 @@ export const WithImage: Story = { render: (args) => ( - London + London }> diff --git a/packages/design-system/src/components/ds-radio-group/ds-radio-group.tsx b/packages/design-system/src/components/ds-radio-group/ds-radio-group.tsx index 85e199122..0658bfeee 100644 --- a/packages/design-system/src/components/ds-radio-group/ds-radio-group.tsx +++ b/packages/design-system/src/components/ds-radio-group/ds-radio-group.tsx @@ -3,11 +3,7 @@ import { RadioGroup } from '@ark-ui/react/radio-group'; import classNames from 'classnames'; import { DsTypography } from '../ds-typography'; import styles from './ds-radio-group.module.scss'; -import type { - DsRadioGroupItemProps, - DsRadioGroupLegacyProps, - DsRadioGroupRootProps, -} from './ds-radio-group.types'; +import type { DsRadioGroupItemProps, DsRadioGroupRootProps } from './ds-radio-group.types'; /** * Root component - provides radio group context @@ -57,58 +53,6 @@ const Item: React.FC = ({ ); }; -/** - * DEPRECATED: Legacy DsRadioGroup component with options array - * Use compound component pattern instead: DsRadioGroup.Root + DsRadioGroup.Item - * @deprecated - */ -/* c8 ignore start */ -export const DsRadioGroupLegacy: React.FC = ({ - options, - value, - defaultValue, - onValueChange, - disabled, - className, - style, -}) => ( - { - if (details.value !== null) { - onValueChange?.(details.value); - } - }} - > - {options.map((option) => ( - - -
-
-
- - - {option.label} - {!!option.labelInfo && ( - - {option.labelInfo} - - )} - - ))} - -); -/* c8 ignore stop */ - /** * Design system RadioGroup component * diff --git a/packages/design-system/src/components/ds-radio-group/ds-radio-group.types.ts b/packages/design-system/src/components/ds-radio-group/ds-radio-group.types.ts index 26473524a..55c1b1cdd 100644 --- a/packages/design-system/src/components/ds-radio-group/ds-radio-group.types.ts +++ b/packages/design-system/src/components/ds-radio-group/ds-radio-group.types.ts @@ -1,4 +1,3 @@ -import type React from 'react'; import { type RadioGroupItemProps, type RadioGroupRootProps } from '@ark-ui/react/radio-group'; /** @@ -62,63 +61,3 @@ export interface DsRadioGroupItemProps extends Pick< */ labelInfo?: string; } - -/** - * DEPRECATED: Legacy radio option configuration - * Use compound component pattern instead: - * @deprecated - */ -export interface DsRadioOptionLegacy { - /** - * Display label for the radio option - */ - label: string; - /** - * Value associated with this radio option - */ - value: ValueType; - /** - * Additional label info for the radio option - */ - labelInfo?: string; - /** - * Whether this radio option is disabled - */ - disabled?: boolean; -} - -/** - * DEPRECATED: Legacy props for DsRadioGroupLegacy component - * Use compound component pattern instead - * @deprecated - */ -export interface DsRadioGroupLegacyProps { - /** - * The radio group options - */ - options: TOption[]; - /** - * The selected value - */ - value?: TOption['value']; - /** - * The default selected value - */ - defaultValue?: TOption['value']; - /** - * Event handler called when the selected value changes - */ - onValueChange?: (value: TOption['value']) => void; - /** - * Whether the entire radio group is disabled. - */ - disabled?: boolean; - /** - * Additional CSS class names - */ - className?: string; - /** - * Additional inline styles - */ - style?: React.CSSProperties; -} diff --git a/packages/design-system/src/components/ds-radio-group/index.ts b/packages/design-system/src/components/ds-radio-group/index.ts index e531d2179..44a3f31c4 100644 --- a/packages/design-system/src/components/ds-radio-group/index.ts +++ b/packages/design-system/src/components/ds-radio-group/index.ts @@ -1,2 +1,2 @@ -export { DsRadioGroup, DsRadioGroupLegacy } from './ds-radio-group'; +export { DsRadioGroup } from './ds-radio-group'; export * from './ds-radio-group.types'; diff --git a/packages/design-system/src/components/ds-select/__tests__/ds-select.browser.test.tsx b/packages/design-system/src/components/ds-select/__tests__/ds-select.browser.test.tsx index e4321effe..bb6025063 100644 --- a/packages/design-system/src/components/ds-select/__tests__/ds-select.browser.test.tsx +++ b/packages/design-system/src/components/ds-select/__tests__/ds-select.browser.test.tsx @@ -191,7 +191,7 @@ describe('DsSelect', () => { await expect.element(trigger).toHaveTextContent(PLACEHOLDER); }); - it('should select all, expand +N chip, delete chip, and clear all (MultiSelect story)', async () => { + it('should select all, expand +N tag, delete tag, and clear all (MultiSelect story)', async () => { await page.render(); const trigger = page.getByRole('combobox'); @@ -200,12 +200,12 @@ describe('DsSelect', () => { await page.getByRole('option', { name: 'All' }).click(); - const expandChip = page.getByRole('button', { name: /^\+\d+$/ }); - await expandChip.click(); + const expandTag = page.getByRole('button', { name: /^\+\d+$/ }); + await expandTag.click(); for (const option of mockOptions) { - const chip = page.getByRole('button', { name: exactAriaName(option.label) }); - await expect.element(chip).toBeInTheDocument(); + const tag = page.getByRole('button', { name: exactAriaName(option.label) }); + await expect.element(tag).toBeInTheDocument(); } await trigger.click(); @@ -217,11 +217,11 @@ describe('DsSelect', () => { await trigger.click(); const firstOption = mockOptions[0] as DsSelectOption; - const firstOptionChip = page.getByRole('button', { name: exactAriaName(firstOption.label) }); - const deleteButton = firstOptionChip.getByRole('button', { name: 'Delete chip' }); + const firstOptionTag = page.getByRole('button', { name: exactAriaName(firstOption.label) }); + const deleteButton = firstOptionTag.getByRole('button', { name: 'Delete tag' }); await deleteButton.click(); - await expect.element(firstOptionChip).not.toBeInTheDocument(); + await expect.element(firstOptionTag).not.toBeInTheDocument(); await page.getByRole('button', { name: 'Clear All' }).click(); @@ -299,7 +299,7 @@ describe('DsSelect', () => { await expect.element(trigger).toHaveTextContent('France'); }); - it('should render selected items as chips with custom option markup (CustomRenderOptionMultiSelect story)', async () => { + it('should render selected items as tags with custom option markup (CustomRenderOptionMultiSelect story)', async () => { await page.render( setShowAllItems(true)} diff --git a/packages/design-system/src/components/ds-select/ds-select.types.ts b/packages/design-system/src/components/ds-select/ds-select.types.ts index 8e40c24a5..5a8ecd5c3 100644 --- a/packages/design-system/src/components/ds-select/ds-select.types.ts +++ b/packages/design-system/src/components/ds-select/ds-select.types.ts @@ -60,7 +60,7 @@ export type DsSelectProps = { /** * Custom render function for dropdown options. * When provided, replaces the default label text inside each dropdown item. - * The string `label` is still used for search, trigger text, chips, and accessibility. + * The string `label` is still used for search, trigger text, tags, and accessibility. */ renderOption?: (option: DsSelectOption) => ReactNode; } & ( diff --git a/packages/design-system/src/components/ds-select/select-items-chips.tsx b/packages/design-system/src/components/ds-select/select-items-tags.tsx similarity index 66% rename from packages/design-system/src/components/ds-select/select-items-chips.tsx rename to packages/design-system/src/components/ds-select/select-items-tags.tsx index d0147d971..64aab1d53 100644 --- a/packages/design-system/src/components/ds-select/select-items-chips.tsx +++ b/packages/design-system/src/components/ds-select/select-items-tags.tsx @@ -1,18 +1,17 @@ import { useSelectContext, type UseSelectContext } from '@ark-ui/react/select'; import styles from './ds-select.module.scss'; import { DsButton } from '../ds-button'; -// TODO: Use DsTag instead. -import { DsChip } from '../ds-chip'; +import { DsTag } from '../ds-tag'; import type { DsSelectOption, SelectOptionValue } from './ds-select.types'; -type SelectItemsChipsProps = { +type SelectItemsTagsProps = { showAll: boolean; onValueChange?: (value: SelectOptionValue[]) => void; onShowAll: () => void; count: number; }; -export function SelectItemsChips({ showAll, onShowAll, onValueChange, count }: SelectItemsChipsProps) { +export function SelectItemsTags({ showAll, onShowAll, onValueChange, count }: SelectItemsTagsProps) { const { collection, value: selectedItems } = useSelectContext() as UseSelectContext; if (!selectedItems.length) { @@ -36,20 +35,14 @@ export function SelectItemsChips({ showAll, onShowAll, onValueChange, count }: S onValueChange?.(filteredValue); }; - return ; + return ; })} {!showAll && selectedItems.length > count && ( - + )} - onValueChange?.([])} - > + onValueChange?.([])}> Clear All
diff --git a/packages/design-system/src/components/ds-split-button/ds-split-button.figma.ts b/packages/design-system/src/components/ds-split-button/ds-split-button.figma.ts index 678d89048..78bd3390e 100644 --- a/packages/design-system/src/components/ds-split-button/ds-split-button.figma.ts +++ b/packages/design-system/src/components/ds-split-button/ds-split-button.figma.ts @@ -25,7 +25,7 @@ const size = const structure = instance.findInstance('.button-structure', { traverseInstances: true }); const showIcon = structure.type === 'INSTANCE' ? structure.getBoolean('Show icon') : false; -// `DsButtonV3.icon` is an icon-name string. Swapped icons keep the placeholder layer +// `DsButton.icon` is an icon-name string. Swapped icons keep the placeholder layer // name (e.g. `DAP_GM_O_check_circle`), so resolve via Code Connect ID or metadata. const toIconName = (name: string): string => name 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; export interface DsSplitButtonSlotProps { /** - * Props forwarded to the primary action button (`DsButtonV3`). `variant`, `color` + * Props forwarded to the primary action button (`DsButton`). `variant`, `color` * and `size` are controlled by the split button and cannot be overridden here. */ button: ButtonSlotProps; diff --git a/packages/design-system/src/components/ds-status-badge-v2/__tests__/ds-status-badge-v2.browser.test.tsx b/packages/design-system/src/components/ds-status-badge-v2/__tests__/ds-status-badge-v2.browser.test.tsx new file mode 100644 index 000000000..97ec7f203 --- /dev/null +++ b/packages/design-system/src/components/ds-status-badge-v2/__tests__/ds-status-badge-v2.browser.test.tsx @@ -0,0 +1,96 @@ +import { createRef } from 'react'; +import { describe, expect, it } from 'vitest'; +import { page } from 'vitest/browser'; +import DsStatusBadgeV2 from '../ds-status-badge-v2'; +import { DsStatusBadgeV2 as DsStatusBadgeV2Responsive } from '../index'; + +describe('DsStatusBadgeV2', () => { + it('renders with role="status" and aria-label from label', async () => { + await page.render(); + + await expect.element(page.getByRole('status', { name: 'Active' })).toBeVisible(); + }); + + it('renders label text', async () => { + await page.render(); + + await expect.element(page.getByText('Reserved')).toBeVisible(); + }); + + it('renders default phase icon', async () => { + await page.render(); + + const badge = page.getByRole('status').element(); + expect(badge.querySelector('[aria-hidden="true"]')).toBeTruthy(); + }); + + it('hides icon when icon={null} (text-only)', async () => { + await page.render(); + + const badge = page.getByRole('status').element(); + expect(badge.querySelector('[aria-hidden="true"]')).toBeNull(); + + await expect.element(page.getByText('Active')).toBeVisible(); + }); + + it('renders custom icon override', async () => { + await page.render(); + + const badge = page.getByRole('status').element(); + const icon = badge.querySelector('[aria-hidden="true"]'); + expect(icon).toBeTruthy(); + expect(icon?.textContent).toBe('star'); + }); + + it('iconOnly hides label and shows tooltip with label text on hover', async () => { + await page.render(); + + await expect.element(page.getByText('Active')).not.toBeInTheDocument(); + + await page.getByRole('status', { name: 'Active' }).hover(); + await expect.element(page.getByRole('tooltip', { name: 'Active' })).toBeVisible(); + }); + + it('custom aria-label overrides label', async () => { + await page.render(); + + await expect.element(page.getByRole('status', { name: 'Custom status' })).toBeVisible(); + }); + + it('applies secondary variant class', async () => { + await page.render(); + + const badge = page.getByRole('status').element(); + expect(badge.classList.toString()).toMatch(/secondary/); + }); + + it('applies small size class', async () => { + await page.render(); + + const badge = page.getByRole('status').element(); + expect(badge.classList.toString()).toMatch(/small/); + }); + + it('merges className', async () => { + await page.render(); + + await expect.element(page.getByRole('status')).toHaveClass('extra'); + }); + + it('forwards ref', async () => { + const ref = createRef(); + + await page.render(); + + expect(ref.current).toBeInstanceOf(HTMLElement); + expect(ref.current?.getAttribute('role')).toBe('status'); + }); + + it('renders via responsive wrapper', async () => { + await page.render( + , + ); + + await expect.element(page.getByRole('status', { name: 'Testing' })).toBeVisible(); + }); +}); diff --git a/packages/design-system/src/components/ds-stepper/components/ds-step-next-button.tsx b/packages/design-system/src/components/ds-stepper/components/ds-step-next-button.tsx index 62b0c9809..3e030370f 100644 --- a/packages/design-system/src/components/ds-stepper/components/ds-step-next-button.tsx +++ b/packages/design-system/src/components/ds-stepper/components/ds-step-next-button.tsx @@ -1,21 +1,19 @@ import { useStepper } from '../hooks/use-stepper'; -import { DsButton, type DsButtonUnifiedProps } from '../../ds-button'; - -// TODO: Use the new button props once we deprecate the legacy button. -type DsLegacyButtonProps = Extract & { - design?: undefined | 'legacy'; -}; +import { DsButton, type DsButtonProps } from '../../ds-button'; export type DsNextStepButtonProps = Pick< - DsLegacyButtonProps, + DsButtonProps, 'variant' | 'size' | 'className' | 'style' | 'children' >; export function DsNextStepButton({ children, size = 'small', ...rest }: DsNextStepButtonProps) { const context = useStepper(); + // strip color — ark returns `string` which conflicts with DsButton union + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { color, ...triggerProps } = context.stepsApi.getNextTriggerProps(); return ( - + {children} ); diff --git a/packages/design-system/src/components/ds-stepper/ds-stepper.stories.tsx b/packages/design-system/src/components/ds-stepper/ds-stepper.stories.tsx index 15838ce14..454334198 100644 --- a/packages/design-system/src/components/ds-stepper/ds-stepper.stories.tsx +++ b/packages/design-system/src/components/ds-stepper/ds-stepper.stories.tsx @@ -360,7 +360,7 @@ export const CustomizedHorizontal: Story = { activeStep={activeStep} onStepChange={({ step }) => setActiveStep(step)} actions={ - + {activeStep === customSteps.length - 1 ? 'Finish' : 'Continue'} } diff --git a/packages/design-system/src/components/ds-system-status/ds-system-status.module.scss b/packages/design-system/src/components/ds-system-status/ds-system-status.module.scss deleted file mode 100644 index 22f488ffa..000000000 --- a/packages/design-system/src/components/ds-system-status/ds-system-status.module.scss +++ /dev/null @@ -1,87 +0,0 @@ -@use '../../styles/typography'; - -.container { - @include typography.p-special; - @include typography.medium; - display: inline-flex; - align-items: center; - gap: 4px; - padding: 4px 8px; - border-radius: 4px; - width: max-content; - text-transform: uppercase; -} - -.dot { - width: 10px; - height: 10px; - border-radius: 50%; - border: 1px solid var(--color-dap-gray-050); -} - -.label { - @include typography.p-special; -} - -.healthy { - background: var(--color-dap-data-light-green); - color: var(--color-dap-data-green); - - .dot { - background: var(--color-dap-data-green); - } -} - -.neutral { - background: var(--color-dap-blue-200); - color: var(--color-dap-blue-600); - - .dot { - background: var(--color-dap-blue-600); - } -} - -.error { - background: var(--color-dap-red-100); - color: var(--background-error); - - .dot { - background: var(--background-error); - } -} - -.in-progress { - background: var(--schema-light-blue); - color: var(--color-dap-blue-800); - - .dot { - background: var(--color-dap-blue-800); - } -} - -.pending { - background: var(--background-pending); - color: var(--color-dap-data-deep-fuchsia); - - .dot { - background: var(--color-dap-data-deep-fuchsia); - } -} - -.alert { - background: var(--schema-light-amber); - color: var(--color-dap-orange-600); - - .dot { - background: var(--color-dap-orange-600); - } -} - -.disabled { - background: var(--color-dap-gray-200); - color: var(--color-dap-gray-600); - - .dot { - background: var(--color-dap-gray-600); - } -} diff --git a/packages/design-system/src/components/ds-system-status/ds-system-status.stories.ts b/packages/design-system/src/components/ds-system-status/ds-system-status.stories.ts deleted file mode 100644 index ae75fae09..000000000 --- a/packages/design-system/src/components/ds-system-status/ds-system-status.stories.ts +++ /dev/null @@ -1,48 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react-vite'; -import DsSystemStatus from './ds-system-status'; -import { systemStatuses } from './ds-system-status.types'; - -/** - * @deprecated This component is deprecated. Use `DsStatusBadge` instead. - * @see {@link ../ds-status-badge/ds-status-badge.stories} for examples of the replacement component. - */ -const meta: Meta = { - title: 'Components/SystemStatus (Deprecated)', - component: DsSystemStatus, - parameters: { - layout: 'centered', - docs: { - description: { - component: - '**Deprecated**: This component is deprecated. Please use `DsStatusBadge` instead. See the StatusBadge stories for the replacement component.', - }, - }, - }, - tags: ['deprecated'], - argTypes: { - status: { - control: { type: 'select' }, - options: systemStatuses, - }, - label: { - control: 'text', - description: 'Custom label text (optional)', - }, - }, -}; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - status: 'healthy', - }, -}; - -export const CustomLabel: Story = { - args: { - status: 'error', - label: 'Critical Error', - }, -}; diff --git a/packages/design-system/src/components/ds-system-status/ds-system-status.tsx b/packages/design-system/src/components/ds-system-status/ds-system-status.tsx deleted file mode 100644 index ec3e1ef40..000000000 --- a/packages/design-system/src/components/ds-system-status/ds-system-status.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import type React from 'react'; -import classNames from 'classnames'; -import styles from './ds-system-status.module.scss'; -import type { DsSystemStatusProps } from './ds-system-status.types'; - -/** - * @deprecated This component is deprecated. Use `DsStatusBadge` instead. - * @see {@link ../ds-status-badge/ds-status-badge} for the replacement component. - */ -const defaultLabels: Record = { - healthy: 'HEALTHY', - neutral: 'NEUTRAL', - error: 'ERROR', - 'in-progress': 'IN PROGRESS', - pending: 'PENDING', - alert: 'ALERT', - disabled: 'DISABLED', -}; - -/** - * @deprecated This component is deprecated. Use `DsStatusBadge` instead. - * @see {@link ../ds-status-badge/ds-status-badge} for the replacement component. - */ -const DsSystemStatus: React.FC = ({ status, label, className }) => { - return ( -
- - {label || defaultLabels[status]} -
- ); -}; - -export default DsSystemStatus; diff --git a/packages/design-system/src/components/ds-system-status/ds-system-status.types.ts b/packages/design-system/src/components/ds-system-status/ds-system-status.types.ts deleted file mode 100644 index dd6db780b..000000000 --- a/packages/design-system/src/components/ds-system-status/ds-system-status.types.ts +++ /dev/null @@ -1,37 +0,0 @@ -/** - * @deprecated This type is deprecated. Use `DsStatusBadge` and its related types instead. - * @see {@link ../ds-status-badge/ds-status-badge} for the replacement component. - */ -export const systemStatuses = [ - 'healthy', - 'neutral', - 'error', - 'in-progress', - 'pending', - 'alert', - 'disabled', -] as const; - -/** - * @deprecated This type is deprecated. Use `DsStatus` from `ds-status-badge` instead. - */ -export type SystemStatus = (typeof systemStatuses)[number]; - -/** - * @deprecated This interface is deprecated. Use `DsStatusBadgeProps` from `ds-status-badge` instead. - * @see {@link ../ds-status-badge/ds-status-badge.types} for the replacement interface. - */ -export interface DsSystemStatusProps { - /** - * The status of the system - */ - status: SystemStatus; - /** - * The label to be displayed - */ - label?: string; - /** - * Additional CSS class names - */ - className?: string; -} diff --git a/packages/design-system/src/components/ds-system-status/index.ts b/packages/design-system/src/components/ds-system-status/index.ts deleted file mode 100644 index ebe6c6671..000000000 --- a/packages/design-system/src/components/ds-system-status/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * @deprecated DsSystemStatus is deprecated. Use DsStatusBadge instead. - * @see {@link ../ds-status-badge} for the replacement component. - */ -export { default as DsSystemStatus } from './ds-system-status'; -/** - * @deprecated These types are deprecated. Use DsStatusBadge types instead. - * @see {@link ../ds-status-badge} for the replacement types. - */ -export * from './ds-system-status.types'; diff --git a/packages/design-system/src/components/ds-table/__tests__/ds-table-column-filters.browser.test.tsx b/packages/design-system/src/components/ds-table/__tests__/ds-table-column-filters.browser.test.tsx index 9e85f8501..003ba489c 100644 --- a/packages/design-system/src/components/ds-table/__tests__/ds-table-column-filters.browser.test.tsx +++ b/packages/design-system/src/components/ds-table/__tests__/ds-table-column-filters.browser.test.tsx @@ -46,15 +46,15 @@ const baseColumns: ColumnDef[] = [ ]; function TableWithColumnFilters() { - const { columnFilters, filterChips, enhancedColumns, handlers } = useTableFilters({ + const { columnFilters, filterTags, enhancedColumns, handlers } = useTableFilters({ filterAdapters: [typeFilter], baseColumns, }); return (
- {filterChips.length > 0 && ( - + {filterTags.length > 0 && ( + )}
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 232451adf..61e8c736d 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: absolute; bottom: 20px; left: 50%; transform: translateX(-50%); background-color: var(--color-dap-gray-050); - border-radius: 8px; + border-radius: $border-radius; box-shadow: 0 4px 12px #0445cc66; z-index: 1; display: flex; align-items: center; - border-radius: 8px; overflow: hidden; animation-duration: 0.3s; animation-timing-function: ease-out; @@ -29,8 +30,6 @@ .bulkActionsContent { display: flex; align-items: center; - padding-right: 6px; - padding-left: 6px; } .selectedCountContainer { @@ -52,40 +51,60 @@ padding-right: 56px; } +.actionButton { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: var(--3xs); + width: 68px; + height: 48px; + border-radius: 0; +} + .bulkActionsActions { display: flex; align-items: center; - gap: 8px; > .actionButton { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: var(--3xs); - border-right: 1px solid var(--color-dap-gray-400); - width: 68px; - height: 48px; - border-radius: 0; - } + position: relative; + + &::after { + content: ''; + position: absolute; + top: 0; + right: 0; + width: 1px; + height: 100%; + background-color: var(--color-dap-gray-400); + } - > .actionButton:first-child { - border-left: 1px solid var(--color-dap-gray-400); + &:first-child::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 1px; + height: 100%; + background-color: var(--color-dap-gray-400); + } } } -.escapeButtonContent { +.targetButton.escapeButtonContent { display: flex; flex-direction: column; justify-content: center; align-items: center; color: var(--color-dap-gray-600); - gap: 2px !important; - width: 36px; padding-left: 4px; + margin-right: 2px; + background: none; + border-radius: 0 $border-radius $border-radius 0; &:hover { color: var(--color-dap-gray-500); + background: inherit; } } diff --git a/packages/design-system/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.tsx b/packages/design-system/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.tsx index 297000935..86a5b9560 100644 --- a/packages/design-system/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.tsx +++ b/packages/design-system/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.tsx @@ -43,22 +43,25 @@ const DsTableBulkActions: React.FC = ({ numSelectedRows, actio
{actions.map((action, index) => ( - + ))}
- - + slotProps={{ + icon: { + size: 'medium', + }, + }} + />
); diff --git a/packages/design-system/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.tsx b/packages/design-system/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.tsx index 96a366372..12c0cdd61 100644 --- a/packages/design-system/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.tsx +++ b/packages/design-system/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.tsx @@ -1,5 +1,5 @@ import { flexRender } from '@tanstack/react-table'; -import { DsButtonV3 } from '../../../ds-button-v3'; +import { DsButton } from '../../../ds-button'; import { TableHead } from '../core-table'; import { useDsTableContext } from '../../context/ds-table-context'; import { getGroupColumnSizeStyle } from '../../grouping'; @@ -31,7 +31,7 @@ export const DsTableGroupHeaderCell = ({ {flexRender(header.column.columnDef.header, header.getContext())} {isCollapsible && ( - ({ return ( { diff --git a/packages/design-system/src/components/ds-table/components/edit/ds-table-editable-cell/ds-table-editable-cell.tsx b/packages/design-system/src/components/ds-table/components/edit/ds-table-editable-cell/ds-table-editable-cell.tsx index 4a839850d..3372b7138 100644 --- a/packages/design-system/src/components/ds-table/components/edit/ds-table-editable-cell/ds-table-editable-cell.tsx +++ b/packages/design-system/src/components/ds-table/components/edit/ds-table-editable-cell/ds-table-editable-cell.tsx @@ -1,5 +1,5 @@ import type { MouseEvent } from 'react'; -import { DsButtonV3 } from '../../../../ds-button-v3'; +import { DsButton } from '../../../../ds-button'; import { DsIcon } from '../../../../ds-icon'; import { DsTooltip } from '../../../../ds-tooltip'; import styles from './ds-table-editable-cell.module.scss'; @@ -45,7 +45,7 @@ export const DsTableEditableCell = ({ aria-label="Double-click to edit" > {children} - ({ cell }: DsTableEditingCellPr {columnDef.editCell ? flexRender(columnDef.editCell, cell.getContext()) : null} - ({ cell }: DsTableEditingCellPr }} style={{ color: 'var(--icon-error)' }} /> - { /** Custom row renderer; defaults to `item.label`. */ renderer?: (item: CheckboxFilterItem) => ReactNode; /** @default (item) => `${label}: ${item.label}` */ - chipLabelTemplate?: (item: CheckboxFilterItem) => string; + tagLabelTemplate?: (item: CheckboxFilterItem) => string; cellRenderer?: (value: TValue) => ReactNode; /** @default (row) => row.getValue(id) */ getRowValue?: (row: Row) => TValue; @@ -40,7 +40,7 @@ export function createCheckboxFilterAdapter( label, items, renderer, - chipLabelTemplate = (item) => `${label}: ${item.label}`, + tagLabelTemplate = (item) => `${label}: ${item.label}`, cellRenderer, getRowValue = (row) => row.getValue(id), searchable = false, @@ -64,10 +64,10 @@ export function createCheckboxFilterAdapter( cellRenderer, - toChips: (selectedItems) => { + toTags: (selectedItems) => { return selectedItems.map((item) => ({ id: `${id}_${String(item.value)}`, - label: chipLabelTemplate(item), + label: tagLabelTemplate(item), metadata: { key: id, value: item.value, @@ -75,8 +75,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 41c855147..05f619db5 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 @@ -70,8 +70,8 @@ export function createDualRangeFilterAdapter( cellRenderer: undefined, - toChips: (value) => { - const chips: TagFilterItem[] = []; + toTags: (value) => { + const tags: TagFilterItem[] = []; Object.entries(value).forEach(([fieldKey, range]) => { const hasFilter = range.from !== undefined || range.to !== undefined; @@ -81,7 +81,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: { @@ -94,11 +94,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 eb7432898..805f34ab0 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 @@ -8,8 +8,8 @@ export interface FilterAdapterConfig initialValue: TFilterValue; /** TanStack `filterFn` for this column. */ filterFn: FilterAdapter['columnFilterFn']; - toChips: FilterAdapter['toChips']; - fromChip: FilterAdapter['fromChip']; + toTags: FilterAdapter['toTags']; + fromTag: FilterAdapter['fromTag']; getActiveFiltersCount: FilterAdapter['getActiveFiltersCount']; renderFilter: (value: TFilterValue, onChange: (value: TFilterValue) => void) => ReactNode; /** Cell renderer; receives the cell value, not the filter value. */ @@ -19,7 +19,7 @@ export interface FilterAdapterConfig /** * Base factory used by every specialized adapter. Reach for it only when * `createCheckboxFilterAdapter` / `createDualRangeFilterAdapter` don't fit - * (custom UI, mixed sub-filters, bespoke chip logic). + * (custom UI, mixed sub-filters, bespoke tag logic). */ export function createFilterAdapter( config: FilterAdapterConfig, @@ -30,8 +30,8 @@ export function createFilterAdapter( initialValue: config.initialValue, columnFilterFn: config.filterFn, cellRenderer: config.cellRenderer, - toChips: config.toChips, - fromChip: config.fromChip, + toTags: config.toTags, + fromTag: config.fromTag, getActiveFiltersCount: config.getActiveFiltersCount, reset: () => config.initialValue, renderFilter: config.renderFilter, diff --git a/packages/design-system/src/components/ds-table/filters/components/column-filter-popover/column-filter-popover.tsx b/packages/design-system/src/components/ds-table/filters/components/column-filter-popover/column-filter-popover.tsx index ba51c3d47..77770eeee 100644 --- a/packages/design-system/src/components/ds-table/filters/components/column-filter-popover/column-filter-popover.tsx +++ b/packages/design-system/src/components/ds-table/filters/components/column-filter-popover/column-filter-popover.tsx @@ -5,7 +5,7 @@ import classNames from 'classnames'; import styles from './column-filter-popover.module.scss'; import type { DsTableColumnFilterPopoverProps } from './column-filter-popover.types'; import { DsIcon } from '../../../../ds-icon'; -import { DsButtonV3 } from '../../../../ds-button-v3'; +import { DsButton } from '../../../../ds-button'; // React portals bubble events through the React tree, so clicks inside the // popover would otherwise reach the parent `` @@ -53,7 +53,7 @@ export const DsTableColumnFilterPopover = ({ positioning={{ placement: 'bottom-end', gutter: 4 }} > - event.stopPropagation()} > - + @@ -74,12 +74,12 @@ export const DsTableColumnFilterPopover = ({
{children}
- + {clearLabel} - - + + {saveLabel} - +
diff --git a/packages/design-system/src/components/ds-table/filters/components/range-filter/range-filter.tsx b/packages/design-system/src/components/ds-table/filters/components/range-filter/range-filter.tsx index 9d1aaa6fb..52483cec9 100644 --- a/packages/design-system/src/components/ds-table/filters/components/range-filter/range-filter.tsx +++ b/packages/design-system/src/components/ds-table/filters/components/range-filter/range-filter.tsx @@ -47,7 +47,7 @@ export const RangeFilter = ({ label, value, onChange, onClear }: RangeFilterProp
{label} {hasValue && onClear && ( - + Clear )} diff --git a/packages/design-system/src/components/ds-table/filters/hooks/use-table-filters.ts b/packages/design-system/src/components/ds-table/filters/hooks/use-table-filters.ts index b7e568c26..19e327a68 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 @@ -40,8 +40,8 @@ export interface UseTableFiltersResult { filterState: FilterState; /** Applied filters mapped to TanStack's column-filters shape. */ columnFilters: ColumnFilterState[]; - /** Chips for `DsTagFilter`, derived from applied state. */ - filterChips: TagFilterItem[]; + /** Tags for `DsTagFilter`, derived from applied state. */ + filterTags: TagFilterItem[]; /** Filter-nav items with active counts. */ filterNavItems: FilterNavItem[]; /** `baseColumns` with `filterFn`, cell renderer and per-column meta wired in. */ @@ -53,8 +53,8 @@ export interface UseTableFiltersResult { applyFilters: () => void; /** Clear all filters. */ clearAll: () => void; - /** Remove a chip's effect from applied state. */ - deleteChip: (chip: TagFilterItem) => void; + /** Remove a tag's effect from applied state. */ + deleteTag: (tag: TagFilterItem) => void; /** Commit one column's draft (per-column popover Save). */ applyColumnFilter: (filterId: string) => void; /** Discard one column's draft without committing. */ @@ -103,9 +103,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) => ({ @@ -213,8 +213,8 @@ export function useTableFilters({ } }; - const deleteChip = (chip: TagFilterItem) => { - 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; } @@ -229,7 +229,7 @@ export function useTableFilters({ return; } - const newValue = adapter.fromChip(chip, currentValue); + const newValue = adapter.fromTag(tag, currentValue); const newFilters = adapter.getActiveFiltersCount(newValue) === 0 @@ -257,14 +257,14 @@ export function useTableFilters({ return { filterState: draftFilters, columnFilters, - filterChips, + filterTags, filterNavItems, enhancedColumns, handlers: { updateFilter, applyFilters, clearAll, - deleteChip, + deleteTag, applyColumnFilter, cancelColumnFilter, clearColumnFilter, 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 2a24de768..b9ee01952 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 @@ -21,12 +21,12 @@ export interface FilterAdapter { /** Optional cell renderer; receives the cell value, not the filter value. */ cellRenderer?: (value: TCellValue) => ReactNode; /** - * Convert the filter value into chips for `DsTagFilter`. Each chip's - * `metadata.key` / `metadata.value` must let `fromChip` undo it. + * Convert the filter value into tags for `DsTagFilter`. Each tag's + * `metadata.key` / `metadata.value` must let `fromTag` undo it. */ - toChips: (value: TFilterValue) => TagFilterItem[]; - /** Remove a chip's effect from the current value. */ - fromChip: (chip: TagFilterItem, currentValue: TFilterValue) => TFilterValue; + toTags: (value: TFilterValue) => TagFilterItem[]; + /** Remove a tag's effect from the current value. */ + fromTag: (tag: TagFilterItem, currentValue: TFilterValue) => TFilterValue; /** Number of active sub-filters; `0` means the filter is inactive. */ getActiveFiltersCount: (value: TFilterValue) => number; /** Return the initial value. */ diff --git a/packages/design-system/src/components/ds-table/stories/components/progress-infographic/progress-infographic.module.scss b/packages/design-system/src/components/ds-table/stories/components/progress-infographic/progress-infographic.module.scss index 73dabf49e..18b92d647 100644 --- a/packages/design-system/src/components/ds-table/stories/components/progress-infographic/progress-infographic.module.scss +++ b/packages/design-system/src/components/ds-table/stories/components/progress-infographic/progress-infographic.module.scss @@ -17,15 +17,15 @@ transition: width 0.3s ease-in-out; &--low { - background-color: var(--background-error); + background-color: var(--background-background-negative); } &--medium { - background-color: var(--color-dap-blue-600); + background-color: var(--background-background-info-strong); } &--high { - background-color: var(--color-dap-data-green); + background-color: var(--background-background-positive-strong); } } } diff --git a/packages/design-system/src/components/ds-table/stories/ds-table-column-filters.stories.tsx b/packages/design-system/src/components/ds-table/stories/ds-table-column-filters.stories.tsx index e82d413e8..9ff98134b 100644 --- a/packages/design-system/src/components/ds-table/stories/ds-table-column-filters.stories.tsx +++ b/packages/design-system/src/components/ds-table/stories/ds-table-column-filters.stories.tsx @@ -171,18 +171,18 @@ const columns: ColumnDef[] = [ ]; function DevicesTable({ rows }: { rows: DeviceRow[] }) { - const { columnFilters, filterChips, enhancedColumns, handlers } = useTableFilters({ + const { columnFilters, filterTags, enhancedColumns, handlers } = useTableFilters({ filterAdapters: [typeFilter, siteFilter], baseColumns: columns, }); return ( <> - {filterChips.length > 0 && ( + {filterTags.length > 0 && ( )} - {filterChips.length > 0 && ( + {filterTags.length > 0 && ( )} - {filterChips.length > 0 && ( - + {filterTags.length > 0 && ( + )} - {filterChips.length > 0 && ( - + {filterTags.length > 0 && ( + )} - setIsModalOpen(true)} /> + setIsModalOpen(true)}> + + - {filterChips.length > 0 && ( + {filterTags.length > 0 && ( )} @@ -327,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 }); \`\`\` @@ -352,8 +354,8 @@ createCustomFilterAdapter({ label: 'Display Label', initialValue: { /* your state */ }, filterFn: (row, columnId, filterValue) => boolean, - toChips: (value) => TagFilterItem[], - 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 @@ -362,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 @@ -411,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: @@ -424,10 +426,10 @@ This story demonstrates the complete filter system with: \`\`\`typescript const { columnFilters, // Pass to DsTable - filterChips, // Pass to DsTagFilter + 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, @@ -437,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 @@ -488,9 +490,9 @@ const handleValueChange = (value: string | null) => { - Clear all + Clear all - Apply + Apply @@ -504,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! @@ -515,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, @@ -569,17 +571,14 @@ To add a new filter, just add one adapter to \`workflowFilters\` array. No other
{/* Toolbar with filter button */}
- setIsOpen(true)} - /> + setIsOpen(true)}> + +
- {/* Filter chips (automatically generated from filter state) */} - {filterChips.length > 0 && ( - + {/* Filter tags (automatically generated from filter state) */} + {filterTags.length > 0 && ( + )} {/* Table with enhanced columns (includes filter functions) */} @@ -617,13 +616,14 @@ To add a new filter, just add one adapter to \`workflowFilters\` array. No other - + + Clear all - + - + Apply - + @@ -680,7 +680,7 @@ To add a new filter, just add one adapter to \`workflowFilters\` array. No other // 6. Apply filters await userEvent.click(screen.getByRole('button', { name: /apply/i })); - // Verify chips appear + // Verify tags appear await expect(canvas.getByText(/status: active/i)).toBeInTheDocument(); await expect(canvas.getByText(/status: running/i)).toBeInTheDocument(); await expect(canvas.getByText(/running.*0.*50/i)).toBeInTheDocument(); @@ -700,11 +700,11 @@ 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. DsTagFilter renders a `Delete tag` X inside each - // chip; the chip itself is also role="button", so the nested button is + // 9. Delete individual tag. DsTagFilter renders a `Delete tag` X inside each + // tag; the tag itself is also role="button", so the nested button is // excluded from the accessibility tree — query by label directly. - const activeChip = canvas.getByRole('button', { name: /status: active/i }); - const deleteButton = within(activeChip).getByLabelText(/delete tag/i); + 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(); @@ -737,7 +737,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 @@ -754,7 +754,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, @@ -813,16 +813,13 @@ The debug panel below shows the current filter state as JSON.
- setIsOpen(true)} - /> + setIsOpen(true)}> + +
- {filterChips.length > 0 && ( - + {filterTags.length > 0 && ( + )} @@ -858,13 +855,14 @@ The debug panel below shows the current filter state as JSON. - + + Clear all - +
- + Apply - + @@ -896,7 +894,7 @@ The debug panel below shows the current filter state as JSON. // 2. Verify external state is updated (debug panel shows filter) await expect(canvas.getByText(/"status"/)).toBeInTheDocument(); - // 3. Verify chip appears + // 3. Verify tag appears await expect(canvas.getByText(/status: active/i)).toBeInTheDocument(); // 4. Clear all and verify state resets diff --git a/packages/design-system/src/components/ds-table/stories/filters-panel/components/last-edited-filter/last-edited-filter.tsx b/packages/design-system/src/components/ds-table/stories/filters-panel/components/last-edited-filter/last-edited-filter.tsx index d79a2feda..4361c4e54 100644 --- a/packages/design-system/src/components/ds-table/stories/filters-panel/components/last-edited-filter/last-edited-filter.tsx +++ b/packages/design-system/src/components/ds-table/stories/filters-panel/components/last-edited-filter/last-edited-filter.tsx @@ -85,7 +85,7 @@ export const LastEditedFilter = ({ value, onChange, availableEditors }: LastEdit
Editor {value.editors.length > 0 && ( - + Clear )} @@ -126,7 +126,7 @@ export const LastEditedFilter = ({ value, onChange, availableEditors }: LastEdit
Last edited {value.timeRange && ( - + Clear )} diff --git a/packages/design-system/src/components/ds-table/stories/filters-panel/workflow-filters.config.tsx b/packages/design-system/src/components/ds-table/stories/filters-panel/workflow-filters.config.tsx index 3ac723a68..9463acc39 100644 --- a/packages/design-system/src/components/ds-table/stories/filters-panel/workflow-filters.config.tsx +++ b/packages/design-system/src/components/ds-table/stories/filters-panel/workflow-filters.config.tsx @@ -6,18 +6,18 @@ * * 1. **Checkbox Filter** (statusFilterAdapter): * - Multi-select with custom rendering (status badges) - * - Custom chip labels + * - Custom tag labels * - Custom cell renderer * * 2. **Dual-Range Filter** (runningCompletedFilterAdapter): * - Multiple numeric range fields in one filter * - Number formatting - * - Automatic chip generation + * - Automatic tag generation * * 3. **Custom Filter** (lastEditedFilterAdapter): * - Complex filter with editor selection + date range * - Custom render component - * - Multiple chip types from one filter + * - Multiple tag types from one filter * - Custom cell renderer * * ## Usage Pattern: @@ -30,7 +30,7 @@ * export const workflowFilters = [myFilter, ...]; * * // 3. Use in component with useTableFilters hook - * const { columnFilters, filterChips, enhancedColumns, handlers } = + * const { columnFilters, filterTags, enhancedColumns, handlers } = * useTableFilters(workflowFilters, columns); * ``` * @@ -122,20 +122,20 @@ const renderStatusBadge = (status: DsStatus): ReactNode => { * * Demonstrates: * - Custom item rendering with DsStatusBadge - * - Custom chip label template + * - Custom tag label template * - Custom cell renderer for table column * * Features: * - Empty selection = show all (no filter applied) * - Selected items = show only those items - * - Automatic chip generation for each selected status + * - Automatic tag generation for each selected status */ const statusFilterAdapter = createCheckboxFilterAdapter({ id: 'status', label: 'Status', items: statusItems, renderer: (item) => renderStatusBadge(item.value), - chipLabelTemplate: (item) => `Status: ${item.label}`, + tagLabelTemplate: (item) => `Status: ${item.label}`, cellRenderer: (value) => renderStatusBadge(value), }); @@ -150,8 +150,8 @@ const statusFilterAdapter = createCheckboxFilterAdapter({ * Features: * - Each field can have independent from/to ranges * - All ranges must match (AND logic) - * - Automatic chip generation for each active range - * - Formatted numbers in chips + * - Automatic tag generation for each active range + * - Formatted numbers in tags */ const runningCompletedFilterAdapter = createDualRangeFilterAdapter({ id: 'runningCompleted', @@ -233,14 +233,14 @@ const parseTimestamp = (timestamp: string): Date => { * Demonstrates: * - Complex filter state (editor multi-select + time range) * - Custom filter function with multiple conditions - * - Multiple chip types from one filter + * - Multiple tag types from one filter * - Custom filter UI component * - Custom cell renderer * * Features: * - Filter by multiple editors (multi-select) * - Filter by time range (preset options + custom date range) - * - Separate chips for editors and time range + * - Separate tags for editors and time range * - Both conditions must match (AND logic) * * This is a reference implementation for building custom filters @@ -277,19 +277,17 @@ const lastEditedFilterAdapter = createFilterAdapter { - const chips = []; + toTags: (value) => { + const tags = []; - // Editor chips value.editors.forEach((editor) => { - chips.push({ + tags.push({ id: `editor-${editor}`, label: `Editor: ${editor}`, metadata: { key: 'lastEdited', type: 'editor', value: editor }, }); }); - // Time range chip if (value.timeRange) { let label = ''; switch (value.timeRange) { @@ -312,22 +310,22 @@ const lastEditedFilterAdapter = createFilterAdapter { - const { type, value: chipValue } = chip.metadata || {}; + fromTag: (tag, currentValue) => { + const { type, value: tagValue } = tag.metadata || {}; if (type === 'editor') { return { ...currentValue, - editors: currentValue.editors.filter((e) => e !== chipValue), + editors: currentValue.editors.filter((e) => e !== tagValue), }; } diff --git a/packages/design-system/src/components/ds-tag-filter/ds-tag-filter.tsx b/packages/design-system/src/components/ds-tag-filter/ds-tag-filter.tsx index 9b242777d..44680b346 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 @@ -96,11 +96,8 @@ const DsTagFilter = ({
{hasOverflow && ( @@ -110,15 +107,7 @@ const DsTagFilter = ({ )} {onClearAll && ( - + {clearButton} diff --git a/packages/design-system/src/components/ds-time-picker/ds-time-picker.tsx b/packages/design-system/src/components/ds-time-picker/ds-time-picker.tsx index 70c4d0ffa..32ee34ebc 100644 --- a/packages/design-system/src/components/ds-time-picker/ds-time-picker.tsx +++ b/packages/design-system/src/components/ds-time-picker/ds-time-picker.tsx @@ -139,9 +139,8 @@ const DsTimePicker = (props: DsTimePickerProps) => { {showClearButton && ( setValue(null)} @@ -151,14 +150,12 @@ const DsTimePicker = (props: DsTimePickerProps) => { )} - - + /> ), diff --git a/packages/design-system/src/components/ds-toast/ds-toast.stories.tsx b/packages/design-system/src/components/ds-toast/ds-toast.stories.tsx index 7d89da608..832e52626 100644 --- a/packages/design-system/src/components/ds-toast/ds-toast.stories.tsx +++ b/packages/design-system/src/components/ds-toast/ds-toast.stories.tsx @@ -78,7 +78,7 @@ const ToastDemo = ({ return (

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

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

Toast with Action

- + Show Toast with Actions {action &&

{action}

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

Long Content Toast

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

Multiple Toasts Demo

- + Create Multiple Toasts - + Dismiss All
diff --git a/packages/design-system/src/components/ds-workspace-layout/ds-workspace-layout.stories.tsx b/packages/design-system/src/components/ds-workspace-layout/ds-workspace-layout.stories.tsx index 4da795bad..7d0fc2bd9 100644 --- a/packages/design-system/src/components/ds-workspace-layout/ds-workspace-layout.stories.tsx +++ b/packages/design-system/src/components/ds-workspace-layout/ds-workspace-layout.stories.tsx @@ -1,7 +1,7 @@ import { useState } from 'react'; import type { Meta, StoryObj } from '@storybook/react-vite'; import DsWorkspaceLayout from './ds-workspace-layout'; -import { DsButtonV3 } from '../ds-button-v3'; +import { DsButton } from '../ds-button'; import { DsTypography } from '../ds-typography'; import { DsIcon } from '../ds-icon'; import { DsStatusBadge } from '../ds-status-badge'; @@ -78,9 +78,9 @@ export const Default: Story = {
- + Close - +
@@ -94,13 +94,13 @@ export const Default: Story = { Last update: 2d ago
- + Discard - - + + Save project - - + +
@@ -127,12 +127,12 @@ export const Default: Story = {
v1.2.0
- + Help - - + + Feedback - +
@@ -149,9 +149,9 @@ export const WithDrawer: Story = {
- + Close - +
@@ -165,13 +165,13 @@ export const WithDrawer: Story = { Last update: 2d ago
- + Discard - - setDrawerOpen(true)}> + + setDrawerOpen(true)}> Save project - - + +
@@ -203,12 +203,12 @@ export const WithDrawer: Story = { - setDrawerOpen(false)}> + setDrawerOpen(false)}> Cancel - - + + Save - + @@ -233,9 +233,9 @@ export const WithDrawerAndBackdrop: Story = {
- + Close - +
@@ -249,13 +249,13 @@ export const WithDrawerAndBackdrop: Story = { Last update: 2d ago
- + Discard - - setDrawerOpen(true)}> + + setDrawerOpen(true)}> Save project - - + +
@@ -303,9 +303,9 @@ export const FillParent: Story = {
- + Close - +
@@ -319,13 +319,13 @@ export const FillParent: Story = { Last update: 2d ago
- + Discard - - + + Save project - - + +
@@ -352,9 +352,9 @@ export const HeaderOnly: Story = {
- + Close - +
@@ -368,13 +368,13 @@ export const HeaderOnly: Story = { Last update: 2d ago
- + Discard - - + + Save project - - + +
@@ -398,9 +398,9 @@ export const ExtendedStepperBelow: Story = {
- + Close - +
@@ -414,13 +414,13 @@ export const ExtendedStepperBelow: Story = { Last update: 2d ago
- + Discard - - + + Save project - - + +
@@ -429,9 +429,9 @@ export const ExtendedStepperBelow: Story = {
Project workspace - + Edit - +
@@ -468,9 +468,9 @@ export const ExtendedStepperAside: Story = {
- + Close - +
@@ -484,13 +484,13 @@ export const ExtendedStepperAside: Story = { Last update: 2d ago
- + Discard - - + + Save project - - + +
@@ -524,9 +524,9 @@ export const ExtendedStepperAside: Story = {
Project workspace - + Edit - +
@@ -551,9 +551,9 @@ export const ExtendedSideMenuAndLeftPanel: Story = {
- + Close - +
@@ -567,13 +567,13 @@ export const ExtendedSideMenuAndLeftPanel: Story = { Last update: 2d ago
- + Discard - - + + Save project - - + +
@@ -626,9 +626,9 @@ export const ExtendedSideMenuAndLeftPanel: Story = {
Project workspace - + Edit - +
@@ -651,9 +651,9 @@ export const ExtendedWithCanvas: Story = {
- + Close - +
@@ -667,13 +667,13 @@ export const ExtendedWithCanvas: Story = { Last update: 2d ago
- + Discard - - + + Save project - - + +
@@ -681,9 +681,9 @@ export const ExtendedWithCanvas: Story = {
Network topology - + Expand - +
@@ -706,9 +706,9 @@ export const ExtendedSideMenuLeftPanel: Story = {
- + Close - +
@@ -722,13 +722,13 @@ export const ExtendedSideMenuLeftPanel: Story = { Last update: 2d ago
- + Discard - - + + Save project - - + +
@@ -794,9 +794,9 @@ export const ExtendedSideMenuLeftPanel: Story = {
Project workspace - + Edit - +
@@ -823,9 +823,9 @@ export const ExtendedCombined: Story = {
- + Close - +
@@ -839,13 +839,13 @@ export const ExtendedCombined: Story = { Last update: 2d ago
- + Discard - - setDrawerOpen(true)}> + + setDrawerOpen(true)}> Save project - - + +
@@ -911,9 +911,9 @@ export const ExtendedCombined: Story = {
Project workspace - + Edit - +
@@ -950,9 +950,9 @@ export const ExtendedWorkflowInfoPanel: Story = {
- + Close - +
@@ -966,13 +966,13 @@ export const ExtendedWorkflowInfoPanel: Story = { Last update: 2d ago
- + Discard - - + + Save project - - + +
diff --git a/packages/design-system/src/index.ts b/packages/design-system/src/index.ts index bc8e9893d..271762ca1 100644 --- a/packages/design-system/src/index.ts +++ b/packages/design-system/src/index.ts @@ -4,22 +4,16 @@ 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-catalog-layout'; export * from './components/ds-checkbox'; export * from './components/ds-checkbox-group'; -export * from './components/ds-chip'; -export * from './components/ds-chip-group'; export * from './components/ds-comment-bubble'; export * from './components/ds-comment-card'; export * from './components/ds-comment-indicator'; export * from './components/ds-comments-drawer'; -export * from './components/ds-confirmation'; -export * from './components/ds-date-input'; export * from './components/ds-date-picker'; export * from './components/ds-date-range-picker'; -export * from './components/ds-dialog'; export * from './components/ds-divider'; export * from './components/ds-drawer'; export * from './components/ds-dropdown-menu'; @@ -51,7 +45,6 @@ export * from './components/ds-stack'; export * from './components/ds-status-badge'; export * from './components/ds-status-badge-v2'; export * from './components/ds-stepper'; -export * from './components/ds-system-status'; export * from './components/ds-table'; export * from './components/ds-tabs'; export * from './components/ds-tag'; diff --git a/packages/design-system/src/stories/patterns-forms.mdx b/packages/design-system/src/stories/patterns-forms.mdx index 77754397d..41e4cba53 100644 --- a/packages/design-system/src/stories/patterns-forms.mdx +++ b/packages/design-system/src/stories/patterns-forms.mdx @@ -26,7 +26,7 @@ reference see the [`Examples/SampleForm`](?path=/story/examples-sampleform--defa [`DsTimePicker`](?path=/docs/design-system-timepicker--docs) - [`DsRadioGroup`](?path=/docs/design-system-radiogroup--docs) / [`DsCheckbox`](?path=/docs/design-system-checkbox--docs) -- [`DsButtonV3`](?path=/docs/design-system-buttonv3--docs) +- [`DsButtonV`](?path=/docs/design-system-button--docs) - [`DsTypography`](?path=/docs/design-system-typography--docs) ## Stack @@ -258,7 +258,7 @@ Avoid disabling submit purely based on validation state. Users should be able to - Disable submit when no meaningful action can occur (e.g. no changes in edit forms) - Always disable while submitting, and surface the in-flight state through - [`DsButtonV3`](?path=/docs/design-system-buttonv3--docs)'s `loading` prop so the button renders its + [`DsButton`](?path=/docs/design-system-button--docs)'s `loading` prop so the button renders its spinner in place of the label - Do not gate submit on isValid @@ -267,9 +267,9 @@ For edit/update forms, disable until the user has made changes: + code={` Submit -`} +`} /> For create forms, keep submit enabled and rely on validation on submit: @@ -277,9 +277,9 @@ For create forms, keep submit enabled and rely on validation on submit: + code={` Submit -`} +`} /> ## Checklist diff --git a/packages/design-system/src/stories/sample-form/sample-form.tsx b/packages/design-system/src/stories/sample-form/sample-form.tsx index 0b7968c91..bb6a891a6 100644 --- a/packages/design-system/src/stories/sample-form/sample-form.tsx +++ b/packages/design-system/src/stories/sample-form/sample-form.tsx @@ -7,7 +7,7 @@ import { DsCheckbox } from '../../components/ds-checkbox'; import { DsTypography } from '../../components/ds-typography'; import { sampleFormSchema, type SampleFormValues } from './sample-form-schema'; import { useState } from 'react'; -import { DsButtonV3 } from '../../components/ds-button-v3'; +import { DsButton } from '../../components/ds-button'; const defaultValues = { name: '', @@ -235,9 +235,9 @@ const SampleForm = () => { )} /> - + Submit - + ); diff --git a/packages/design-system/src/styles/_colors.scss b/packages/design-system/src/styles/_colors.scss index ac2f8d8e7..cc7f9f5d7 100644 --- a/packages/design-system/src/styles/_colors.scss +++ b/packages/design-system/src/styles/_colors.scss @@ -27,13 +27,6 @@ $colors: ( action-active-light: #c7d8ff, action-hover-light: #e0eaff, action-disabled: #b0b4bf, - // System Status - system-status-ok: #067a00, - system-status-info: #005dcf, - system-status-error: #d70a00, - system-status-alert: #d04d07, - system-status-in-progress: #1513da, - system-status-pending: #8836d6, // Utility utility-ok: #d6f5cf, utility-info: #c4dcff, diff --git a/packages/design-system/vitest.config.ts b/packages/design-system/vitest.config.ts index 0e8cd96f0..6d7f23734 100644 --- a/packages/design-system/vitest.config.ts +++ b/packages/design-system/vitest.config.ts @@ -13,20 +13,7 @@ export default defineConfig({ test: { coverage: { include: ['src/**/*.{ts,tsx}'], - exclude: [ - '**/stories/**', - '**/*.stories.*{ts,tsx}', - '**/.storybook/**', - '**/*.scss', - 'dist/**', - - // deprecated components - '**/ds-chip/**', - '**/ds-chip-group/**', - '**/ds-confirmation/**', - '**/ds-date-input/**', - '**/ds-system-status/**', - ], + exclude: ['**/stories/**', '**/*.stories.*{ts,tsx}', '**/.storybook/**', '**/*.scss', 'dist/**'], thresholds: { lines: 90, }, diff --git a/packages/eslint-plugin/src/__tests__/no-deprecated.test.ts b/packages/eslint-plugin/src/__tests__/no-deprecated.test.ts index bd4dc926f..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,238 +11,27 @@ const ruleTester = new RuleTester({ }, }); -ruleTester.run( - 'no-deprecated-ds-button-legacy-design', - plugin.rules['no-deprecated-ds-button-legacy-design'], - { - valid: ['Click me'], - - invalid: [ - { - code: 'Click me', - errors: [ - { - message: `Using the 'legacy' design for DsButton is deprecated. Use 'v1.2' instead.`, - line: 1, - endLine: 1, - column: 11, - endColumn: 26, - }, - ], - }, - ], - }, -); - -ruleTester.run('no-deprecated-ds-button-empty-design', plugin.rules['no-deprecated-ds-button-empty-design'], { - valid: ['Click me'], - - invalid: [ - { - code: 'Click me', - errors: [ - { - message: `Omitting the design attribute for DsButton is not allowed. Pass 'design="v1.2"' so the new design is used.`, - line: 1, - endLine: 1, - column: 2, - endColumn: 10, - }, - ], - }, - ], -}); - -ruleTester.run('no-deprecated-ds-dialog', plugin.rules['no-deprecated-ds-dialog'], { - valid: ['', '', ''], - - invalid: [ - { - code: 'Click me', - errors: [ - { - message: `DsDialog is deprecated. Use DsModal or DsConfirmation instead.`, - line: 1, - endLine: 1, - column: 2, - endColumn: 10, - }, - ], - }, - ], -}); - -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, - }, - ], - }, - ], +it('placeholder: RuleTester instantiates', () => { + expect(ruleTester).toBeInstanceOf(RuleTester); }); -ruleTester.run('no-deprecated-ds-status-badge', plugin.rules['no-deprecated-ds-status-badge'], { - valid: [''], - - invalid: [ - { - code: '', - errors: [ - { - message: `DsStatusBadge is deprecated. Use DsStatusBadgeV2 instead.`, - line: 1, - endLine: 1, - column: 2, - endColumn: 15, - }, - ], - }, - ], -}); +// Example test for the future reference -ruleTester.run('no-deprecated-ds-system-status', plugin.rules['no-deprecated-ds-system-status'], { - valid: [''], +// ruleTester.run('no-deprecated-ds-date-input', plugin.rules['no-deprecated-ds-date-input'], { +// valid: ['', ''], - invalid: [ - { - code: '', - errors: [ - { - message: `DsSystemStatus is deprecated. Use DsStatusBadgeV2 instead.`, - line: 1, - endLine: 1, - column: 2, - endColumn: 16, - }, - ], - }, - ], -}); - -ruleTester.run( - 'no-deprecated-ds-dropdown-menu-legacy', - plugin.rules['no-deprecated-ds-dropdown-menu-legacy'], - { - valid: [''], - - invalid: [ - { - code: '', - errors: [ - { - message: `DsDropdownMenuLegacy is deprecated. Use DsDropdownMenu instead.`, - line: 1, - endLine: 1, - column: 2, - endColumn: 22, - }, - ], - }, - ], - }, -); - -ruleTester.run('no-deprecated-ds-radio-group-legacy', plugin.rules['no-deprecated-ds-radio-group-legacy'], { - valid: [''], - - invalid: [ - { - code: '', - errors: [ - { - message: `DsRadioGroupLegacy is deprecated. Use DsRadioGroup instead.`, - line: 1, - endLine: 1, - column: 2, - endColumn: 20, - }, - ], - }, - ], -}); - -ruleTester.run('no-deprecated-ds-chip', plugin.rules['no-deprecated-ds-chip'], { - valid: ['', ''], - - invalid: [ - { - code: '', - errors: [ - { - message: `DsChip is deprecated. Use DsTag instead.`, - line: 1, - endLine: 1, - column: 2, - endColumn: 8, - }, - ], - }, - ], -}); - -ruleTester.run('no-deprecated-ds-chip-group', plugin.rules['no-deprecated-ds-chip-group'], { - valid: ['', ''], - - invalid: [ - { - code: '', - errors: [ - { - message: `DsChipGroup is deprecated. Use DsTagFilter instead.`, - line: 1, - endLine: 1, - column: 2, - endColumn: 13, - }, - ], - }, - ], -}); - -ruleTester.run('no-deprecated-ds-date-input', plugin.rules['no-deprecated-ds-date-input'], { - valid: ['', ''], - - invalid: [ - { - code: '', - errors: [ - { - message: `DsDateInput is deprecated. Use DsDatePicker or DsDateRangePicker instead.`, - line: 1, - endLine: 1, - column: 2, - endColumn: 13, - }, - ], - }, - ], -}); - -ruleTester.run('no-deprecated-ds-popover-legacy', plugin.rules['no-deprecated-ds-popover-legacy'], { - valid: ['', '', ''], - - invalid: [ - { - code: '}>Content', - errors: [ - { - message: `The DsPopover callable form (with a 'trigger' prop) is deprecated. Use the compound API: DsPopover.Root with DsPopover.Trigger and DsPopover.Panel.`, - line: 1, - endLine: 1, - column: 2, - endColumn: 11, - }, - ], - }, - ], -}); +// 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 a11469fed..1ee6e03ad 100644 --- a/packages/eslint-plugin/src/index.ts +++ b/packages/eslint-plugin/src/index.ts @@ -3,71 +3,12 @@ import { createPlugin } from './create-plugin'; const eslintPlugin = createPlugin( '@drivenets/design-system', - { - name: 'no-deprecated-ds-button-legacy-design', - selector: JSXElementAttribute('DsButton', 'design', 'legacy'), - message: `Using the 'legacy' design for DsButton is deprecated. Use 'v1.2' instead.`, - }, - - { - name: 'no-deprecated-ds-button-empty-design', - selector: `${JSXElement('DsButton')}:not(:has( > ${JSXAttribute('design')} )) > .name`, - message: `Omitting the design attribute for DsButton is not allowed. Pass 'design="v1.2"' so the new design is used.`, - }, - - { - name: 'no-deprecated-ds-dialog', - selector: JSXElementName('DsDialog'), - message: `DsDialog is deprecated. Use DsModal or DsConfirmation instead.`, - }, - - { - name: 'no-deprecated-ds-confirmation', - selector: JSXElementName('DsConfirmation'), - message: `DsConfirmation is deprecated. Use DsModal instead.`, - }, - - { - name: 'no-deprecated-ds-status-badge', - selector: JSXElementName('DsStatusBadge'), - message: `DsStatusBadge is deprecated. Use DsStatusBadgeV2 instead.`, - }, - - { - name: 'no-deprecated-ds-system-status', - selector: JSXElementName('DsSystemStatus'), - message: `DsSystemStatus is deprecated. Use DsStatusBadgeV2 instead.`, - }, - - { - name: 'no-deprecated-ds-dropdown-menu-legacy', - selector: JSXElementName('DsDropdownMenuLegacy'), - message: `DsDropdownMenuLegacy is deprecated. Use DsDropdownMenu instead.`, - }, - - { - name: 'no-deprecated-ds-radio-group-legacy', - selector: JSXElementName('DsRadioGroupLegacy'), - message: `DsRadioGroupLegacy is deprecated. Use DsRadioGroup instead.`, - }, - - { - name: 'no-deprecated-ds-chip', - selector: JSXElementName('DsChip'), - message: `DsChip is deprecated. Use DsTag instead.`, - }, - - { - name: 'no-deprecated-ds-chip-group', - selector: JSXElementName('DsChipGroup'), - message: `DsChipGroup is deprecated. Use DsTagFilter instead.`, - }, - - { - name: 'no-deprecated-ds-date-input', - selector: JSXElementName('DsDateInput'), - message: `DsDateInput is deprecated. Use DsDatePicker or DsDateRangePicker instead.`, - }, + // Example test for deprecated components, for future reference + // { + // name: 'no-deprecated-ds-date-input', + // selector: JSXElementName('DsDateInput'), + // message: `DsDateInput is deprecated. Use DsDatePicker or DsDateRangePicker instead.`, + // }, { // Matches only the bare callable ``, not the compound `` etc. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 39dfc5c15..5ebb3a878 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -194,20 +194,11 @@ importers: specifier: ^3.0.0 version: 3.12.2 '@radix-ui/react-collapsible': - specifier: ^1.1.13 + specifier: ^1.1.12 version: 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-dialog': - specifier: ^1.1.16 - version: 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-dropdown-menu': - specifier: ^2.1.17 - version: 2.1.17(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@radix-ui/react-tooltip': specifier: ^1.2.9 version: 1.2.9(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-visually-hidden': - specifier: ^1.2.5 - version: 1.2.5(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@tanstack/react-table': specifier: ^8.21.3 version: 8.21.3(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -2290,19 +2281,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-collection@1.1.9': - resolution: {integrity: sha512-zuSVi7ziP7uQRqc+yGxsKJfNkdyHv3ZKDaHe0gzg4dRgws96TPKWIiz84tVHP4GEcEl8bC0mdt17NkcxaJHmaQ==} - 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.3': resolution: {integrity: sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==} peerDependencies: @@ -2321,28 +2299,6 @@ packages: '@types/react': optional: true - '@radix-ui/react-dialog@1.1.16': - resolution: {integrity: sha512-l9ok83YBclEZhbjgzt76Hw733e6cvRKPNgO6GJ/IETlufXG9p+fRu2wlvpImQvR6xdJ8h7J8J2DBvsPEiEsKMw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-direction@1.1.2': - resolution: {integrity: sha512-C3vFhbyi4SW3PmbAi6Awpu4OzJtd0MxGurvSsYtr7p7nM8RNB3VAF3CUmnp2j50knpkrRcB7+ycVXzgLgF6yNA==} - 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.12': resolution: {integrity: sha512-MhoruH6xEzsbvOmo4TNgMfmtvRGyDZw4MDSdf4ybMHfezjqwzv6hyd4lsMzBp8K9Sn6sGzCF62x1I7BYUECXOg==} peerDependencies: @@ -2356,41 +2312,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-dropdown-menu@2.1.17': - resolution: {integrity: sha512-S6b3Jm57sY5EdDyOMLkacbB0qMnKhy1RCKZCt795ZkmtUOAvojYIZ5p7dXHIh5Cyr3jCLLI5/g64V3FKLudZmw==} - 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.4': - resolution: {integrity: sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-focus-scope@1.1.9': - resolution: {integrity: sha512-9Se8t+Zry+1rEOL7Y6l/4ANYU/TOtAtf8O2fKdwLltcaMcm6kOqYGbzO4tMFQ0bvzO920pRAoHpFZ4W85S3keQ==} - 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-id@1.1.2': resolution: {integrity: sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA==} peerDependencies: @@ -2400,19 +2321,6 @@ packages: '@types/react': optional: true - '@radix-ui/react-menu@2.1.17': - resolution: {integrity: sha512-fmbNnFyf+JYCN0DhhWnEdUTDnZD1mXaPQWivdsPIb8oOSbARfD3LIQJbLCG8a8QLCwoMxiJ7GVPIFcC8Dw8v2Q==} - 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-popper@1.3.0': resolution: {integrity: sha512-9PB589e1aWZbrlFUHdz6WiPCL+xLZHQFX7oibqG/6Q0SwOkxDyQX9W/cyPa+sAPPKuC8cpLCpRczE5a/1DiwVQ==} peerDependencies: @@ -2465,19 +2373,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-roving-focus@1.1.12': - resolution: {integrity: sha512-FvgPt1bRmg8Xt2QpF7NUZW3dE0ZQHGm41dAdgT2J2GJPoIXz+9Em3NobAxf4fupcxhgHu03E5CRiU2MWvObXyg==} - 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.5': resolution: {integrity: sha512-rCMO3QsIVKv5JTY5CVbo2MvO77SpEqqYc8AvRE7OWqRDOIqAKjsp+DrmnY9uc8NPdxB5E2z47HTYGeE2+NTptg==} peerDependencies: @@ -3695,10 +3590,6 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - aria-hidden@1.2.6: - resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} - engines: {node: '>=10'} - aria-query@5.3.0: resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} @@ -4156,9 +4047,6 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} - detect-node-es@1.1.0: - resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} - devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} @@ -4551,10 +4439,6 @@ packages: resolution: {integrity: sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==} engines: {node: '>=18'} - get-nonce@1.0.1: - resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} - engines: {node: '>=6'} - get-tsconfig@4.14.0: resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==} @@ -5702,36 +5586,6 @@ packages: react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} - react-remove-scroll-bar@2.3.8: - resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': '*' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - - react-remove-scroll@2.7.2: - resolution: {integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': '*' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - react-style-singleton@2.2.3: - resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': '*' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - react@19.2.6: resolution: {integrity: sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==} engines: {node: '>=0.10.0'} @@ -6481,26 +6335,6 @@ packages: url-parse@1.5.10: resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} - use-callback-ref@1.3.3: - resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': '*' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - use-sidecar@1.1.3: - resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': '*' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - use-sync-external-store@1.6.0: resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} peerDependencies: @@ -8445,18 +8279,6 @@ snapshots: '@types/react': 19.2.15 '@types/react-dom': 19.2.3(@types/react@19.2.15) - '@radix-ui/react-collection@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.15)(react@19.2.6) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.15)(react@19.2.6) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-slot': 1.2.5(@types/react@19.2.15)(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) - optionalDependencies: - '@types/react': 19.2.15 - '@types/react-dom': 19.2.3(@types/react@19.2.15) - '@radix-ui/react-compose-refs@1.1.3(@types/react@19.2.15)(react@19.2.6)': dependencies: react: 19.2.6 @@ -8469,34 +8291,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.15 - '@radix-ui/react-dialog@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': - dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.15)(react@19.2.6) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.15)(react@19.2.6) - '@radix-ui/react-dismissable-layer': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.15)(react@19.2.6) - '@radix-ui/react-focus-scope': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-id': 1.1.2(@types/react@19.2.15)(react@19.2.6) - '@radix-ui/react-portal': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-slot': 1.2.5(@types/react@19.2.15)(react@19.2.6) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.15)(react@19.2.6) - aria-hidden: 1.2.6 - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) - react-remove-scroll: 2.7.2(@types/react@19.2.15)(react@19.2.6) - optionalDependencies: - '@types/react': 19.2.15 - '@types/react-dom': 19.2.3(@types/react@19.2.15) - - '@radix-ui/react-direction@1.1.2(@types/react@19.2.15)(react@19.2.6)': - dependencies: - react: 19.2.6 - optionalDependencies: - '@types/react': 19.2.15 - '@radix-ui/react-dismissable-layer@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/primitive': 1.1.4 @@ -8510,38 +8304,6 @@ snapshots: '@types/react': 19.2.15 '@types/react-dom': 19.2.3(@types/react@19.2.15) - '@radix-ui/react-dropdown-menu@2.1.17(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': - dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.15)(react@19.2.6) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.15)(react@19.2.6) - '@radix-ui/react-id': 1.1.2(@types/react@19.2.15)(react@19.2.6) - '@radix-ui/react-menu': 2.1.17(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.15)(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) - optionalDependencies: - '@types/react': 19.2.15 - '@types/react-dom': 19.2.3(@types/react@19.2.15) - - '@radix-ui/react-focus-guards@1.1.4(@types/react@19.2.15)(react@19.2.6)': - dependencies: - react: 19.2.6 - optionalDependencies: - '@types/react': 19.2.15 - - '@radix-ui/react-focus-scope@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.15)(react@19.2.6) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.15)(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) - optionalDependencies: - '@types/react': 19.2.15 - '@types/react-dom': 19.2.3(@types/react@19.2.15) - '@radix-ui/react-id@1.1.2(@types/react@19.2.15)(react@19.2.6)': dependencies: '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.15)(react@19.2.6) @@ -8549,32 +8311,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.15 - '@radix-ui/react-menu@2.1.17(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': - dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-collection': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.15)(react@19.2.6) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.15)(react@19.2.6) - '@radix-ui/react-direction': 1.1.2(@types/react@19.2.15)(react@19.2.6) - '@radix-ui/react-dismissable-layer': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.15)(react@19.2.6) - '@radix-ui/react-focus-scope': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-id': 1.1.2(@types/react@19.2.15)(react@19.2.6) - '@radix-ui/react-popper': 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-portal': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-roving-focus': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-slot': 1.2.5(@types/react@19.2.15)(react@19.2.6) - '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.15)(react@19.2.6) - aria-hidden: 1.2.6 - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) - react-remove-scroll: 2.7.2(@types/react@19.2.15)(react@19.2.6) - optionalDependencies: - '@types/react': 19.2.15 - '@types/react-dom': 19.2.3(@types/react@19.2.15) - '@radix-ui/react-popper@1.3.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@floating-ui/react-dom': 2.1.8(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -8621,23 +8357,6 @@ snapshots: '@types/react': 19.2.15 '@types/react-dom': 19.2.3(@types/react@19.2.15) - '@radix-ui/react-roving-focus@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': - dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-collection': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.15)(react@19.2.6) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.15)(react@19.2.6) - '@radix-ui/react-direction': 1.1.2(@types/react@19.2.15)(react@19.2.6) - '@radix-ui/react-id': 1.1.2(@types/react@19.2.15)(react@19.2.6) - '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.15)(react@19.2.6) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.15)(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) - optionalDependencies: - '@types/react': 19.2.15 - '@types/react-dom': 19.2.3(@types/react@19.2.15) - '@radix-ui/react-slot@1.2.5(@types/react@19.2.15)(react@19.2.6)': dependencies: '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.15)(react@19.2.6) @@ -10183,10 +9902,6 @@ snapshots: argparse@2.0.1: {} - aria-hidden@1.2.6: - dependencies: - tslib: 2.8.1 - aria-query@5.3.0: dependencies: dequal: 2.0.3 @@ -10666,8 +10381,6 @@ snapshots: detect-libc@2.1.2: {} - detect-node-es@1.1.0: {} - devlop@1.1.0: dependencies: dequal: 2.0.3 @@ -11141,8 +10854,6 @@ snapshots: get-east-asian-width@1.5.0: {} - get-nonce@1.0.1: {} - get-tsconfig@4.14.0: dependencies: resolve-pkg-maps: 1.0.0 @@ -12473,33 +12184,6 @@ snapshots: react-is@17.0.2: {} - react-remove-scroll-bar@2.3.8(@types/react@19.2.15)(react@19.2.6): - dependencies: - react: 19.2.6 - react-style-singleton: 2.2.3(@types/react@19.2.15)(react@19.2.6) - tslib: 2.8.1 - optionalDependencies: - '@types/react': 19.2.15 - - react-remove-scroll@2.7.2(@types/react@19.2.15)(react@19.2.6): - dependencies: - react: 19.2.6 - react-remove-scroll-bar: 2.3.8(@types/react@19.2.15)(react@19.2.6) - react-style-singleton: 2.2.3(@types/react@19.2.15)(react@19.2.6) - tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@19.2.15)(react@19.2.6) - use-sidecar: 1.1.3(@types/react@19.2.15)(react@19.2.6) - optionalDependencies: - '@types/react': 19.2.15 - - react-style-singleton@2.2.3(@types/react@19.2.15)(react@19.2.6): - dependencies: - get-nonce: 1.0.1 - react: 19.2.6 - tslib: 2.8.1 - optionalDependencies: - '@types/react': 19.2.15 - react@19.2.6: {} read-yaml-file@1.1.0: @@ -13281,21 +12965,6 @@ snapshots: querystringify: 2.2.0 requires-port: 1.0.0 - use-callback-ref@1.3.3(@types/react@19.2.15)(react@19.2.6): - dependencies: - react: 19.2.6 - tslib: 2.8.1 - optionalDependencies: - '@types/react': 19.2.15 - - use-sidecar@1.1.3(@types/react@19.2.15)(react@19.2.6): - dependencies: - detect-node-es: 1.1.0 - react: 19.2.6 - tslib: 2.8.1 - optionalDependencies: - '@types/react': 19.2.15 - use-sync-external-store@1.6.0(react@19.2.6): dependencies: react: 19.2.6