Manifest Debugger

Tip: You are using react-docgen (the default). Generation took 4.4s. For higher quality prop types, consider switching to react-docgen-typescript in your main.ts:
typescript: {
  reactDocgen: 'react-docgen-typescript',
}
Note: react-docgen-typescript can be slower. If performance is acceptable for your project, it generally produces better results. Learn more

Components

DsAlertBanner

components-alertbanner-global · ./src/components/ds-alert-banner/stories/ds-alert-banner-global.stories.tsx
Design system AlertBanner component Controlled component that displays alert messages with different variants
Prop types (react-docgen) 9 prop types
Component: src/components/ds-alert-banner/ds-alert-banner.tsx::default
Props:
/**
 * Optional children to be rendered inside the component (typically action buttons)
 */
children?: ReactNode

/**
 * Additional CSS class names
 */
className?: string

/**
 * Whether the alert banner can be closed with an X button
 */
closable?: boolean = false

/**
 * Optional icon to display using DsIcon
 */
icon?: IconName | FunctionComponent<SVGProps<SVGSVGElement>>

/**
 * Whether the alert banner should be inline (normal document flow) instead of global (designed for top of the page)
 * @default false
 */
inline?: boolean = false

/**
 * Callback fired when the alert banner should be closed
 */
onOpenChange: (open: boolean) => void

/**
 * Controls whether the alert banner is visible
 */
open: boolean

/**
 * Additional styles to apply to the component
 */
style?: React.CSSProperties = {}

/**
 * The variant of the alert banner
 * @default 'info-blue'
 */
variant?: (typeof alertBannerVariants)[number] = 'info-blue'
Imports
import { DsAlertBanner, DsButton } from "@drivenets/design-system";
Info Blue story ok
const InfoBlue = function Render() {
    const [open, setOpen] = useState(true);
    return (
        <DsAlertBanner open={open} onOpenChange={setOpen} variant="info-blue" icon="info" closable={true}>
            <DsAlertBanner.Title>Information</DsAlertBanner.Title>
            <DsAlertBanner.Body>
                Aww yeah, you successfully read this important alert message. This example text is going to run a
                bit longer so that you can see how spacing within an alert works with this kind of content.
            </DsAlertBanner.Body>
            <DsAlertBanner.Actions>
                <DsButton design="v1.2" variant="danger" size="small">
                    Proceed
                </DsButton>
                <DsButton design="v1.2" buttonType="secondary" size="small">
                    Skip
                </DsButton>
            </DsAlertBanner.Actions>
        </DsAlertBanner>
    );
};
Info Neutral story ok
const InfoNeutral = function Render() {
    const [open, setOpen] = useState(true);
    return (
        <DsAlertBanner open={open} onOpenChange={setOpen} variant="info-neutral" closable={true}>
            <DsAlertBanner.Title>Information</DsAlertBanner.Title>
            <DsAlertBanner.Body>This is an informational alert message.</DsAlertBanner.Body>
        </DsAlertBanner>
    );
};
Warning story ok
const Warning = function Render() {
    const [open, setOpen] = useState(true);
    return (
        <DsAlertBanner open={open} onOpenChange={setOpen} variant="warning" icon="warning" closable={true}>
            <DsAlertBanner.Title>Warning</DsAlertBanner.Title>
            <DsAlertBanner.Body>This is a warning alert message. Please pay attention.</DsAlertBanner.Body>
        </DsAlertBanner>
    );
};
Error story ok
const Error = function Render() {
    const [open, setOpen] = useState(true);
    return (
        <DsAlertBanner open={open} onOpenChange={setOpen} variant="error" icon="error" closable={true}>
            <DsAlertBanner.Title>Error</DsAlertBanner.Title>
            <DsAlertBanner.Body>Something went wrong. Please try again.</DsAlertBanner.Body>
        </DsAlertBanner>
    );
};
Success story ok
const Success = function Render() {
    const [open, setOpen] = useState(true);
    return (
        <DsAlertBanner open={open} onOpenChange={setOpen} variant="success" icon="check_circle" closable={true}>
            <DsAlertBanner.Title>Success</DsAlertBanner.Title>
            <DsAlertBanner.Body>Your action was completed successfully!</DsAlertBanner.Body>
        </DsAlertBanner>
    );
};
With Actions story ok
const WithActions = function Render() {
    const [open, setOpen] = useState(false);

    return (
        <div>
            <div className={styles.globalMessages}>
                <DsAlertBanner open={open} onOpenChange={setOpen} variant="warning" icon="warning" closable={true}>
                    <DsAlertBanner.Title>Attention needed</DsAlertBanner.Title>
                    <DsAlertBanner.Body>
                        Aww yeah, you successfully read this important alert message. This example text is going to run
                        a bit longer so that you can see how spacing within an alert works with this kind of content.
                    </DsAlertBanner.Body>
                    <DsAlertBanner.Actions>
                        <DsButton design="v1.2" variant="danger" size="small">
                            Proceed
                        </DsButton>
                        <DsButton design="v1.2" buttonType="secondary" size="small">
                            Skip
                        </DsButton>
                    </DsAlertBanner.Actions>
                </DsAlertBanner>
            </div>
            <DsButton className={styles.trigger} onClick={() => setOpen(true)}>
                Show Alert Banner
            </DsButton>
        </div>
    );
};
Custom Body story ok
const CustomBody = function Render() {
    const [open, setOpen] = useState(false);

    return (
        <div>
            <div className={styles.globalMessages}>
                <DsAlertBanner
                    className={styles.customBody}
                    open={open}
                    onOpenChange={setOpen}
                    variant="warning"
                    icon="warning"
                    closable
                >
                    <DsAlertBanner.Title>Security Alert</DsAlertBanner.Title>
                    <DsAlertBanner.Body>
                        <div className={styles.customBodyContainer}>
                            <p className={styles.customBodyText}>
                                Multiple security vulnerabilities have been detected in your system:
                            </p>
                            <div className={styles.securityCardsContainer}>
                                <div className={classNames(styles.securityCard, styles.securityCardCritical)}>
                                    <span className={classNames(styles.securityCardTitle, styles.securityCardTitleCritical)}>
                                        Critical: SQL Injection
                                    </span>
                                    <span className={styles.securityCardScore}>CVSS: 9.8</span>
                                </div>
                                <div className={classNames(styles.securityCard, styles.securityCardHigh)}>
                                    <span className={classNames(styles.securityCardTitle, styles.securityCardTitleHigh)}>
                                        High: XSS Vulnerability
                                    </span>
                                    <span className={styles.securityCardScore}>CVSS: 7.2</span>
                                </div>
                            </div>
                            <div className={styles.recommendationsBox}>
                                <strong>Recommended actions:</strong>
                                <br />• Update all dependencies immediately
                                <br />• Review and sanitize user inputs
                                <br />• Implement additional security headers
                            </div>
                        </div>
                    </DsAlertBanner.Body>
                    <DsAlertBanner.Actions>
                        <DsButton design="v1.2" variant="danger" size="small">
                            Fix Now
                        </DsButton>
                        <DsButton design="v1.2" buttonType="secondary" size="small">
                            View Details
                        </DsButton>
                        <DsButton design="v1.2" buttonType="secondary" size="small">
                            Ignore
                        </DsButton>
                    </DsAlertBanner.Actions>
                </DsAlertBanner>
            </div>
            <DsButton className={styles.trigger} onClick={() => setOpen(true)}>
                Show Security Alert
            </DsButton>
        </div>
    );
};

DsAlertBanner

components-alertbanner-inline · ./src/components/ds-alert-banner/stories/ds-alert-banner-inline.stories.tsx
Design system AlertBanner component Controlled component that displays alert messages with different variants
Prop types (react-docgen) 9 prop types
Component: src/components/ds-alert-banner/ds-alert-banner.tsx::default
Props:
/**
 * Optional children to be rendered inside the component (typically action buttons)
 */
children?: ReactNode

/**
 * Additional CSS class names
 */
className?: string

/**
 * Whether the alert banner can be closed with an X button
 */
closable?: boolean = false

/**
 * Optional icon to display using DsIcon
 */
icon?: IconName | FunctionComponent<SVGProps<SVGSVGElement>>

/**
 * Whether the alert banner should be inline (normal document flow) instead of global (designed for top of the page)
 * @default false
 */
inline?: boolean = false

/**
 * Callback fired when the alert banner should be closed
 */
onOpenChange: (open: boolean) => void

/**
 * Controls whether the alert banner is visible
 */
open: boolean

/**
 * Additional styles to apply to the component
 */
style?: React.CSSProperties = {}

/**
 * The variant of the alert banner
 * @default 'info-blue'
 */
variant?: (typeof alertBannerVariants)[number] = 'info-blue'
Imports
import { DsAlertBanner, DsButton } from "@drivenets/design-system";
Info Blue story ok
const InfoBlue = function Render() {
    const [open, setOpen] = useState(true);
    return (
        <DsAlertBanner
            open={open}
            onOpenChange={setOpen}
            inline={true}
            variant="info-blue"
            icon="info"
            closable={true}
        >
            <DsAlertBanner.Title>Information</DsAlertBanner.Title>
            <DsAlertBanner.Body>This is a blue informational alert message.</DsAlertBanner.Body>
            <DsAlertBanner.Actions>
                <button className={styles.primary}>Action</button>
                <button>Dismiss</button>
            </DsAlertBanner.Actions>
        </DsAlertBanner>
    );
};
Info Neutral story ok
const InfoNeutral = function Render() {
    const [open, setOpen] = useState(true);
    return (
        <DsAlertBanner open={open} onOpenChange={setOpen} inline={true} variant="info-neutral" closable={true}>
            <DsAlertBanner.Title>Information</DsAlertBanner.Title>
            <DsAlertBanner.Body>This is an informational alert message.</DsAlertBanner.Body>
        </DsAlertBanner>
    );
};
Warning story ok
const Warning = function Render() {
    const [open, setOpen] = useState(true);
    return (
        <DsAlertBanner
            open={open}
            onOpenChange={setOpen}
            inline={true}
            variant="warning"
            icon="warning"
            closable={true}
        >
            <DsAlertBanner.Title>Warning</DsAlertBanner.Title>
            <DsAlertBanner.Body>This is a warning alert message. Please pay attention.</DsAlertBanner.Body>
        </DsAlertBanner>
    );
};
Error story ok
const Error = function Render() {
    const [open, setOpen] = useState(true);
    return (
        <DsAlertBanner
            open={open}
            onOpenChange={setOpen}
            inline={true}
            variant="error"
            icon="error"
            closable={true}
        >
            <DsAlertBanner.Title>Error</DsAlertBanner.Title>
            <DsAlertBanner.Body>Something went wrong. Please try again.</DsAlertBanner.Body>
        </DsAlertBanner>
    );
};
Error No Title story ok
const ErrorNoTitle = function Render() {
    const [open, setOpen] = useState(true);
    return (
        <DsAlertBanner
            open={open}
            onOpenChange={setOpen}
            inline={true}
            variant="error"
            icon="error"
            closable={true}
        >
            <DsAlertBanner.Body>Something went wrong. Please try again.</DsAlertBanner.Body>
        </DsAlertBanner>
    );
};
Success story ok
const Success = function Render() {
    const [open, setOpen] = useState(true);
    return (
        <DsAlertBanner
            open={open}
            onOpenChange={setOpen}
            inline={true}
            variant="success"
            icon="check_circle"
            closable={true}
        >
            <DsAlertBanner.Title>Success</DsAlertBanner.Title>
            <DsAlertBanner.Body>Your action was completed successfully!</DsAlertBanner.Body>
        </DsAlertBanner>
    );
};
With Actions story ok
const WithActions = function Render() {
    const [open, setOpen] = useState(false);

    return (
        <div>
            <DsButton className={styles.trigger} onClick={() => setOpen(true)}>
                Show Alert Banner
            </DsButton>
            <DsAlertBanner
                className={styles.inlineAlertBanner}
                open={open}
                onOpenChange={setOpen}
                inline={true}
                variant="warning"
                icon="warning"
                closable={true}
            >
                <DsAlertBanner.Title>Attention needed</DsAlertBanner.Title>
                <DsAlertBanner.Body>
                    Aww yeah, you successfully read this important alert message. This example text is going to run a
                    bit longer so that you can see how spacing within an alert works with this kind of content.
                </DsAlertBanner.Body>
                <DsAlertBanner.Actions>
                    <DsButton design="v1.2" variant="danger" size="small">
                        Proceed
                    </DsButton>
                    <DsButton design="v1.2" buttonType="secondary" size="small">
                        Skip
                    </DsButton>
                </DsAlertBanner.Actions>
            </DsAlertBanner>
        </div>
    );
};
Custom Body story ok
const CustomBody = function Render() {
    const [open, setOpen] = useState(false);

    return (
        <div>
            <DsButton className={styles.trigger} onClick={() => setOpen(true)}>
                Show Custom Alert Banner
            </DsButton>
            <DsAlertBanner
                className={styles.inlineAlertBanner}
                open={open}
                onOpenChange={setOpen}
                inline={true}
                variant="info-blue"
                icon="info"
                closable={true}
            >
                <DsAlertBanner.Title>System Update Available</DsAlertBanner.Title>
                <DsAlertBanner.Body>
                    <div className={styles.customBodyContainer}>
                        <p className={styles.customBodyText}>
                            A new system update is available with the following improvements:
                        </p>
                        <ul className={styles.customBodyList}>
                            <li>Enhanced security features</li>
                            <li>Improved performance optimizations</li>
                            <li>New user interface components</li>
                            <li>Bug fixes and stability improvements</li>
                        </ul>
                        <div className={styles.infoBox}>
                            <strong>Estimated update time:</strong> 5-10 minutes
                            <br />
                            <strong>Maintenance window:</strong> 2:00 AM - 4:00 AM UTC
                        </div>
                    </div>
                </DsAlertBanner.Body>
                <DsAlertBanner.Actions>
                    <DsButton design="v1.2" variant="filled" size="small">
                        Update Now
                    </DsButton>
                    <DsButton design="v1.2" buttonType="secondary" size="small">
                        Schedule Later
                    </DsButton>
                    <DsButton design="v1.2" buttonType="secondary" size="small">
                        Learn More
                    </DsButton>
                </DsAlertBanner.Actions>
            </DsAlertBanner>
        </div>
    );
};

DsAutocomplete

components-autocomplete · ./src/components/ds-autocomplete/ds-autocomplete.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 15 prop types
Component: src/components/ds-autocomplete/ds-autocomplete.tsx::DsAutocomplete
Props:
/**
 * Additional CSS class names
 */
className?: string

/**
 * Whether the autocomplete is disabled
 */
disabled?: boolean = false

/**
 * Whether to highlight the matching text in the dropdown options
 * @default true
 */
highlightMatch?: boolean = true

/**
 * Unique identifier for the autocomplete component
 */
id?: string

/**
 * Whether the autocomplete is in an invalid state
 */
invalid?: boolean = false

/**
 * Whether the autocomplete is in a loading state.
 * When true, a loading message is shown in the dropdown.
 */
loading?: boolean = false

/**
 * Locale strings for the autocomplete component
 */
locale?: {
	/**
	 * Message to display while results are loading
	 * @default 'Loading...'
	 */
	loading?: string;
	/**
	 * Message to display when no options match the input
	 * @default 'No matches found'
	 */
	noMatches?: string;
} = {}

/**
 * Event handler called when the input value changes (on every keystroke)
 */
onInputValueChange?: (value: string) => void

/**
 * Event handler called when the dropdown opens or closes
 */
onOpenChange?: (open: boolean) => void

/**
 * Event handler called when the value changes.
 * Fires with the selected option's value, or an empty string when cleared.
 */
onValueChange?: (value: string) => void

/**
 * Options to display in the dropdown.
 * For async/server-driven search, update this prop with fetched results.
 */
options?: DsAutocompleteOption[] = []

/**
 * Placeholder text to display when input is empty
 */
placeholder?: string = 'Start typing to search...'

/**
 * Whether to show the dropdown trigger (arrow) button.
 * If false, the dropdown will only open on typing.
 * @default true
 */
showTrigger?: boolean = true

/**
 * Content to display at the start of the input (e.g., a search icon).
 */
startAdornment?: React.ReactNode

/**
 * Additional styles to apply to the component
 */
style?: React.CSSProperties
Imports
import { DsAutocomplete, DsIcon } from "@drivenets/design-system";
Default story ok
const Default = () => <DsAutocomplete
    onValueChange={fn()}
    onInputValueChange={fn()}
    options={mockOptions}
    showTrigger
    placeholder="Select or type to search..."
    style={{ width: '300px' }} />;
Search Mode story ok
const SearchMode = () => <DsAutocomplete
    onValueChange={fn()}
    onInputValueChange={fn()}
    options={mockOptions}
    showTrigger={false}
    placeholder="Start typing to search..."
    style={{ width: '300px' }} />;
Search With Icon story ok
const SearchWithIcon = () => <DsAutocomplete
    onValueChange={fn()}
    onInputValueChange={fn()}
    options={countries}
    showTrigger={false}
    startAdornment={<DsIcon icon="search" size="medium" aria-label="search icon" />}
    placeholder="Search countries..."
    style={{ width: '300px' }} />;
All Variants story ok
const AllVariants = () => (
    <div style={{ display: 'flex', flexDirection: 'column', gap: '20px' }}>
        <div style={{ width: '300px' }}>
            <p>Default (with trigger)</p>
            <DsAutocomplete options={mockOptions} showTrigger placeholder="Select or type..." />
        </div>
        <div style={{ width: '300px' }}>
            <p>Search Mode (no trigger)</p>
            <DsAutocomplete options={mockOptions} showTrigger={false} placeholder="Start typing..." />
        </div>
        <div style={{ width: '300px' }}>
            <p>Search with Icon</p>
            <DsAutocomplete
                options={countries}
                showTrigger={false}
                startAdornment={<DsIcon icon="search" size="medium" aria-label="search icon" />}
                placeholder="Search countries..."
            />
        </div>
    </div>
);
States story ok
const States = () => (
    <div style={{ display: 'flex', flexDirection: 'column', gap: '20px' }}>
        <div style={{ width: '300px' }}>
            <p>Disabled</p>
            <DsAutocomplete options={mockOptions} disabled placeholder="Disabled autocomplete..." />
        </div>
        <div style={{ width: '300px' }}>
            <p>Invalid</p>
            <DsAutocomplete options={mockOptions} invalid placeholder="Invalid autocomplete..." />
        </div>
    </div>
);
Async Search story ok
const AsyncSearch = () => {
    const [options, setOptions] = useState<DsAutocompleteOption[]>([]);
    const [loading, setLoading] = useState(false);

    const handleInputValueChange = async (value: string) => {
        args.onInputValueChange?.(value);

        if (!value) {
            setOptions([]);
            return;
        }

        setLoading(true);
        const results = await fetchCountries(value);
        setOptions(results);
        setLoading(false);
    };

    return (
        <DsAutocomplete
            onValueChange={fn()}
            options={options}
            loading={loading}
            onInputValueChange={handleInputValueChange}
            showTrigger={false}
            startAdornment={<DsIcon icon="search" size="medium" aria-label="search icon" />}
            placeholder="Search countries (async)..."
            locale={{ noMatches: 'No results found' }}
            style={{ width: '300px' }} />
    );
};
Async Options story ok
const AsyncOptions = () => {
    const [options, setOptions] = useState<DsAutocompleteOption[]>([]);
    const [loading, setLoading] = useState(true);

    useEffect(() => {
        void fetchAllCountries().then((results) => {
            setOptions(results);
            setLoading(false);
        });
    }, []);

    return (
        <DsAutocomplete
            onValueChange={fn()}
            onInputValueChange={fn()}
            options={options}
            loading={loading}
            placeholder="Select a country..."
            style={{ width: '300px' }} />
    );
};

DsAvatar

components-avatar · ./src/components/ds-avatar/ds-avatar.stories.tsx
DsAvatar component for displaying user profile pictures or initials
Prop types (react-docgen) 8 prop types
Component: src/components/ds-avatar/ds-avatar.tsx::DsAvatar
Props:
/**
 * Alt text for the image
 */
alt?: string

/**
 * Custom class name
 */
className?: string

/**
 * Name to display in tooltip and as initials if image fails
 */
name: string

/**
 * Callback when the status of the image changes
 */
onStatusChange?: (status: 'error' | 'loaded') => void

/**
 * Size of the avatar
 * @default 'regular'
 */
size?: 'xsm' | 'sm' | 'regular' | 'md' | 'lg' | 'xl' = 'regular'

/**
 * Image source URL
 */
src?: string

/**
 * Custom inline styles
 */
style?: CSSProperties

/**
 * Shape of the avatar
 * @default 'circle'
 */
type?: 'circle' | 'rounded' = 'circle'
Imports
import { DsAvatar } from "@drivenets/design-system";
Default story ok
const Default = () => <DsAvatar name="John Doe" size="regular" type="circle" />;
With Image story ok
const WithImage = () => <DsAvatar
    name="Jane Smith"
    src="https://i.pravatar.cc/150?u=jane"
    size="regular"
    type="circle" />;
Sizes story ok
const Sizes = () => <div className={styles.sizesContainer}>
    <DsAvatar size="xsm" name="Extra Small" />
    <DsAvatar size="sm" name="Sam Mitchell" />
    <DsAvatar size="regular" name="Rachel Evans" />
    <DsAvatar size="md" name="Mike Edwards" />
    <DsAvatar size="lg" name="Laura Adams" />
    <DsAvatar size="xl" name="Xavier Lee" />
</div>;
Shapes story ok
const Shapes = () => <div className={styles.shapesContainer}>
    <DsAvatar type="circle" name="Chris Irving" />
    <DsAvatar type="rounded" name="Rose Oliver" />
</div>;

DsAvatarGroup

components-avatargroup · ./src/components/ds-avatar-group/ds-avatar-group.stories.tsx
DsAvatarGroup component for displaying a list of avatars with overlap
Prop types (react-docgen) 5 prop types
Component: src/components/ds-avatar-group/ds-avatar-group.tsx::DsAvatarGroup
Props:
/**
 * Array of avatar items
 */
avatars: Omit<DsAvatarProps, 'size' | 'type'>[]

/**
 * Custom class name
 */
className?: string

/**
 * Maximum number of avatars to show before collapsing
 * @default 5
 */
max?: number = 5

/**
 * Size of the avatars in the group
 * @default 'regular'
 */
size?: 'xsm' | 'sm' | 'regular' | 'md' | 'lg' | 'xl' = 'regular'

/**
 * Shape of the avatars in the group
 * @default 'circle'
 */
type?: 'circle' | 'rounded' = 'circle'
Imports
import { DsAvatarGroup } from "@drivenets/design-system";
Default story ok
const Default = () => <DsAvatarGroup avatars={sampleAvatars} />;
Variants story ok
const Variants = () => (
    <div className={styles.variantsContainer}>
        <div>
            <h3>Default Group (max 5)</h3>
            <DsAvatarGroup avatars={sampleAvatars} />
        </div>
        <div>
            <h3>Small Rounded Group</h3>
            <DsAvatarGroup avatars={sampleAvatars} size="sm" type="rounded" />
        </div>
        <div>
            <h3>Large Group (max 3)</h3>
            <DsAvatarGroup avatars={sampleAvatars} size="lg" max={3} />
        </div>
    </div>
);

DsBreadcrumb

components-breadcrumb · ./src/components/ds-breadcrumb/ds-breadcrumb.stories.tsx
Design system Breadcrumb component
Prop types (react-docgen) 3 prop types
Component: src/components/ds-breadcrumb/ds-breadcrumb.tsx::default
Props:
/**
 * Additional CSS class name applied to the breadcrumb root
 */
className?: string

/**
 * Ordered breadcrumb segments rendered left-to-right. The last item is typically
 * the current page.
 */
items: DsBreadcrumbItem[]

/**
 * Called when the user activates any link segment or picks a destination from a
 * dropdown segment. Receives the selected `href`.
 */
onSelect?: (href: string) => void
Imports
import { DsBreadcrumb } from "@drivenets/design-system";
import { RouterProvider } from "@tanstack/react-router";
Default story ok
const Default = () => <BreadcrumbStory items={defaultItems} />;
With Dropdown story ok
const WithDropdown = () => <BreadcrumbStory items={dropdownItems} />;

DsButtonLegacy

components-buttonlegacy-deprecated · ./src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.stories.tsx
Design system Button component
deprecated: Use [DsButton](../ds-button-new/ds-button-new.tsx) instead.
Prop types (react-docgen) 5 prop types
Component: src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.tsx::default
Props:
/**
 * Class name for the button content
 */
contentClassName?: string

/**
 * Whether the button is disabled
 * @default false
 */
disabled?: boolean = false

/**
 * Color schema of the button
 * @default 'primary'
 */
schema?: (typeof buttonSchemas)[number] = 'primary'

/**
 * Size of the button
 * @default 'medium'
 */
size?: (typeof buttonSizes)[number] = 'medium'

/**
 * Visual variant of the button
 * @default 'filled'
 */
variant?: (typeof buttonVariants)[number] = 'filled'
Imports
import { DsButtonLegacy, DsIcon } from "@drivenets/design-system";
Default Button story ok
const DefaultButton = () => <DsButtonLegacy onClick={fn()} schema="primary" variant="filled" disabled={false}>{defaultButtonText}</DsButtonLegacy>;
Showcase story ok
const Showcase = function Render() {
    const renderButtonRow = (
        schema: ButtonSchema,
        variant: ButtonVariant,
        disabled?: boolean,
        showIcon?: boolean,
        showTitle?: boolean,
    ) => {
        return (
            <div className={classNames(styles.row)}>
                {buttonSizes.map((size) => (
                    <DsButtonLegacy
                        className={classNames(styles.buttonShowcase, {
                            [styles[`iconButton-${size}`]]: !showTitle,
                        })}
                        key={`${schema}-${variant}-${size}`}
                        schema={schema}
                        variant={variant}
                        size={size}
                        disabled={disabled}
                    >
                        {showIcon && <DsIcon key={size} icon="add" size={size} />}
                        {showTitle && <span>{size} Button</span>}
                    </DsButtonLegacy>
                ))}
            </div>
        );
    };

    const renderButtonContainer = (
        schema: ButtonSchema,
        variant: ButtonVariant,
        disabled?: boolean,
        title?: boolean,
    ) => {
        return (
            <div className={classNames(styles.variantContainer)} key={schema + variant}>
                {title && (
                    <div className={classNames(styles.row)}>
                        <h4 className={classNames(styles.variantTitle)}>{variant}</h4>
                    </div>
                )}
                {renderButtonRow(schema, variant, disabled, false, true)}
                {renderButtonRow(schema, variant, disabled, true, false)}
                {renderButtonRow(schema, variant, disabled, true, true)}
            </div>
        );
    };

    return (
        <div className={classNames(styles.combinationsContainer)}>
            {buttonSchemas.map((schema) => (
                <div key={schema} className={classNames(styles.schemaContainer)}>
                    <h3 className={classNames(styles.schemaTitle)}>{schema}</h3>

                    {buttonVariants.map((variant) =>
                        renderButtonContainer(schema, variant, false, schema === 'primary'),
                    )}
                </div>
            ))}
            <div className={classNames(styles.schemaContainer)}>
                <h3 className={classNames(styles.schemaTitle)}>Disabled</h3>

                {buttonVariants.map((variant) => renderButtonContainer('primary', variant, true))}
            </div>
        </div>
    );
};

DsButtonNew

components-button · ./src/components/ds-button/versions/ds-button-new/ds-button-new.stories.tsx
Design system Button component
Prop types (react-docgen) 5 prop types
Component: src/components/ds-button/versions/ds-button-new/ds-button-new.tsx::default
Props:
/**
 * Type of the button
 * @default 'primary'
 */
buttonType?: (typeof buttonTypes)[number]

/**
 * Class name for the button content
 */
contentClassName?: string

/**
 * Whether the button is disabled
 * @default false
 */
disabled?: boolean = false

/**
 * Size of the button
 * @default 'medium'
 */
size?: (typeof buttonSizes)[number] = 'medium'

/**
 * Visual variant of the button
 * @default 'filled'
 */
variant?: (typeof buttonVariants)[number] = 'filled'
Imports
import { DsButtonNew, DsIcon, DsSpinner } from "@drivenets/design-system";
Default Button story ok
const DefaultButton = () => <DsButtonNew
    onClick={fn()}
    buttonType="primary"
    variant="filled"
    size="large"
    disabled={false}>(<>
        <DsIcon icon="check_circle" size="tiny" aria-hidden="true" />
        {defaultButtonText}
        <DsIcon icon="keyboard_arrow_down" size="tiny" aria-hidden="true" />
    </>)</DsButtonNew>;
With Spinner story ok
const WithSpinner = () => <DsButtonNew
    onClick={fn()}
    buttonType="primary"
    variant="filled"
    size="large"
    disabled={false}>(<>
        <DsSpinner />
        {defaultButtonText}
        <DsIcon icon="keyboard_arrow_down" size="tiny" aria-hidden="true" />
    </>)</DsButtonNew>;
Showcase story ok
const Showcase = function Render() {
    const defaultButtonChildren = (
        <>
            <DsIcon icon="check_circle" size="tiny" />
            {defaultButtonText}
            <DsIcon icon="keyboard_arrow_down" size="tiny" />
        </>
    );
    const iconButtonChildren = <DsIcon icon="check_circle" size="tiny" />;

    // Helper to check if a buttonType-variant combo is supported (based on SCSS class existence)
    const supportedCombos = [
        'primary-filled',
        // 'primary-ghost',
        'primary-danger',
        'secondary-filled',
        'secondary-ghost',
        'secondary-danger',
        'tertiary-filled',
        'tertiary-ghost',
        'tertiary-danger',
        'tertiary-dark',
        'primary-dark',
        'secondary-dark',
        'secondary-light-dark',
    ];
    const isSupported = (buttonType: string, variant: string) =>
        supportedCombos.includes(`${buttonType}-${variant}`);

    const rowDefs = [
        { label: 'Primary', buttonType: 'primary', icon: false },
        { label: 'Secondary', buttonType: 'secondary', icon: false },
        { label: 'Secondary-Light', buttonType: 'secondary-light', icon: false, variant: 'dark' },
        { label: 'Tertiary', buttonType: 'tertiary', icon: false },
        { label: 'Icon Primary', buttonType: 'primary', icon: true },
        { label: 'Icon Secondary', buttonType: 'secondary', icon: true },
        { label: 'Icon Tertiary', buttonType: 'tertiary', icon: true },
        { label: 'Icon Primary Dark', buttonType: 'primary', icon: true, variant: 'dark' },
        { label: 'Icon Secondary Dark', buttonType: 'secondary', icon: true, variant: 'dark' },
        { label: 'Icon Tertiary Dark', buttonType: 'tertiary', icon: true, variant: 'dark' },
    ];

    const variants = buttonVariants;
    const sizes = buttonSizes;
    const states = [false, true]; // false = default, true = disabled

    return (
        <div className={styles.showcaseContainer}>
            <table className={styles.showcaseTable}>
                <thead>
                    <tr>
                        <th className={styles.showcaseHeader}></th>
                        {variants.map((variant) => (
                            <th key={variant} colSpan={sizes.length * states.length} className={styles.showcaseHeader}>
                                {variant.charAt(0).toUpperCase() + variant.slice(1)}
                            </th>
                        ))}
                    </tr>
                    <tr>
                        <th className={styles.showcaseHeader}></th>
                        {variants.map((variant) =>
                            sizes.map((size) =>
                                states.map((disabled) => (
                                    <th
                                        key={`${variant}-${size}-${disabled ? 'disabled' : 'default'}`}
                                        className={styles.showcaseHeader}
                                    >
                                        {`${size}${disabled ? ' (disabled)' : ''}`}
                                    </th>
                                )),
                            ),
                        )}
                    </tr>
                </thead>
                <tbody>
                    {rowDefs.map((row) => (
                        <tr key={row.label}>
                            <td className={styles.showcaseCellBold}>{row.label}</td>
                            {variants
                                .map((variant) =>
                                    sizes.map((size) =>
                                        states.map((disabled) => {
                                            if (!isSupported(row.buttonType, variant)) {
                                                return (
                                                    <td
                                                        key={`${row.label}-${variant}-${size}-${disabled ? 'disabled' : 'default'}`}
                                                        className={styles.showcaseCell}
                                                    ></td>
                                                );
                                            }
                                            return (
                                                <td
                                                    key={`${row.label}-${variant}-${size}-${disabled ? 'disabled' : 'default'}`}
                                                    className={classNames(styles.showcaseCell, {
                                                        [styles.showcaseCellDark]: variant === 'dark',
                                                    })}
                                                >
                                                    <div className={styles.showcaseCellInline}>
                                                        <DsButtonNew
                                                            buttonType={row.buttonType as (typeof buttonTypes)[number]}
                                                            variant={variant}
                                                            size={size}
                                                            disabled={disabled}
                                                        >
                                                            {row.icon ? iconButtonChildren : defaultButtonChildren}
                                                        </DsButtonNew>
                                                    </div>
                                                </td>
                                            );
                                        }),
                                    ),
                                )
                                .flat()}
                        </tr>
                    ))}
                </tbody>
            </table>
        </div>
    );
};

DsButtonV3

components-buttonv3 · ./src/components/ds-button-v3/ds-button-v3.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 8 prop types
Component: src/components/ds-button-v3/ds-button-v3.tsx::default
Props:
/**
 * - `default` — standard light-UI palette
 * - `error` — destructive / danger palette (red tones)
 * - `light` — palette for dark-background surfaces (Figma **Type** onDark)
 * @default 'default'
 */
color?: (typeof buttonV3Colors)[number] = 'default'

/**
 * Leading icon. When set without children, renders as icon-only (square) layout.
 */
icon?: IconName | FunctionComponent<SVGProps<SVGSVGElement>>

/**
 * Shows a spinner as the leading element and disables interaction.
 * @default false
 */
loading?: boolean = false

ref?: Ref<HTMLButtonElement>

/**
 * Whether the button is in a selected/pressed state. Used for toggle buttons
 * and segmented controls.
 * @default false
 */
selected?: boolean = false

/**
 * Size of the button. Controls height, padding, font size, and icon size.
 * @default 'medium'
 */
size?: (typeof buttonV3Sizes)[number] = 'medium'

type?: any = 'button'

/**
 * 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?: (typeof buttonV3Variants)[number] = 'primary'
Imports
import { DsButtonV3, DsButtonV3 as DsButtonV3Wrapped } from "@drivenets/design-system";
Default story ok
const Default = () => <DsButtonV3
    onClick={fn()}
    color="default"
    variant="primary"
    size="medium"
    icon="check_circle">Button</DsButtonV3>;
Matrix Default story ok
const MatrixDefault = () => (
    <div className={storyStyles.matrix}>
        <p className={storyStyles.sectionTitle}>Default</p>
        <MatrixGrid color="default" />
    </div>
);
Matrix Error story ok
const MatrixError = () => (
    <div className={storyStyles.matrix}>
        <p className={storyStyles.sectionTitle}>Error</p>
        <MatrixGrid color="error" />
    </div>
);
Matrix On Dark story ok
const MatrixOnDark = () => (
    <div className={storyStyles.matrix}>
        <div className={storyStyles.onDark}>
            <p className={classNames(storyStyles.sectionTitle, storyStyles.onDarkSectionTitle)}>
                On Dark — Default
            </p>
            <MatrixGrid color="light" />
        </div>
    </div>
);
Matrix Icons story ok
const MatrixIcons = () => (
    <div className={storyStyles.matrix}>
        <p className={storyStyles.sectionTitle}>Icons — Default</p>
        <IconMatrixGrid rows={defaultIconMatrixRows} />

        <div className={storyStyles.onDark}>
            <p
                className={classNames(
                    storyStyles.sectionTitle,
                    storyStyles.onDarkSectionTitle,
                    storyStyles.sectionTitleSpaced,
                )}
            >
                Icons — On Dark
            </p>
            <IconMatrixGrid rows={onDarkIconMatrixRows} isOnDark />
        </div>
    </div>
);
Responsive Size story ok
const ResponsiveSize = () => (
    <div className={storyStyles.responsiveRow}>
        <DsButtonV3Wrapped size={{ lg: 'large', md: 'small' }} icon="check_circle" onClick={fn()}>
            lg: large / md: small
        </DsButtonV3Wrapped>

        <DsButtonV3Wrapped size={{ lg: 'medium', md: 'tiny' }} icon="check_circle" onClick={fn()}>
            lg: medium / md: tiny
        </DsButtonV3Wrapped>

        <DsButtonV3Wrapped size="medium" icon="check_circle" onClick={fn()}>
            static: medium
        </DsButtonV3Wrapped>
    </div>
);

DsCard.Root

components-card · ./src/components/ds-card/ds-card.stories.tsx
Prop type error
File: /opt/build/repo/packages/design-system/src/components/ds-card/index.ts
Error:
No suitable component definition found.
You can debug your component file in this playground: https://react-docgen.dev/playground
Code:
export { DsCard } from './ds-card';
export type {
	DsCardRootProps,
	DsCardHeaderProps,
	DsCardBodyProps,
	DsCardFooterProps,
	DsCardSize,
} from './ds-card.types';
export { cardSizes } from './ds-card.types';


File: /opt/build/repo/packages/design-system/src/components/ds-card/ds-card.tsx
Error:
No suitable component definition found.
You can debug your component file in this playground: https://react-docgen.dev/playground
Code:
import classNames from 'classnames';

import styles from './ds-card.module.scss';
import type { DsCardRootProps, DsCardHeaderProps, DsCardBodyProps, DsCardFooterProps } from './ds-card.types';

/**
 * Card root component - container with optional selectable behavior.
 * Use composition with DsCard.Header, DsCard.Body, DsCard.Footer for full flexibility.
 */
const Root = ({
	ref,
	size = 'medium',
	selectable = false,
	selected = false,
	highlightSelected = false,
	disabled = false,
	className,
	style,
	children,
	onClick,
	onKeyDown,
	...rest
}: DsCardRootProps) => {
	const interactiveProps = selectable
		? {
				role: 'button' as const,
				tabIndex: disabled ? -1 : 0,
				'aria-pressed': selected,
				'aria-disabled': disabled || undefined,
			}
		: {};

	return (
		<div
			ref={ref}
			{...rest}
			{...interactiveProps}
			onClick={disabled ? undefined : onClick}
			onKeyDown={disabled ? undefined : onKeyDown}
			data-size={size}
			data-selectable={selectable || undefined}
			data-selected={selected || undefined}
			data-disabled={disabled || undefined}
			data-highlight={(selected && highlightSelected) || undefined}
			className={classNames(styles.root, className)}
			style={style}
		>
			{children}
		</div>
	);
};

/**
 * Card header - contains title and extra content.
 * Use with DsCard.Title and DsCard.Extra for standard layout,
 * or provide custom children for full flexibility.
 */
const Header = ({ className, children, ref, ...rest }: DsCardHeaderProps) => (
	<div ref={ref} className={classNames(styles.header, className)} {...rest}>
		{children}
	</div>
);

/**
 * Card body - main content area.
 */
const Body = ({ className, children, ref, ...rest }: DsCardBodyProps) => (
	<div ref={ref} className={classNames(styles.body, className)} {...rest}>
		{children}
	</div>
);

/**
 * Card footer - bottom section for actions or additional info.
 */
const Footer = ({ className, children, ref, ...rest }: DsCardFooterProps) => (
	<div ref={ref} className={classNames(styles.footer, className)} {...rest}>
		{children}
	</div>
);

export const DsCard = {
	Root,
	Header,
	Body,
	Footer,
};


File: /opt/build/repo/packages/design-system/src/components/ds-card/ds-card.types.ts
Error:
No suitable component definition found.
You can debug your component file in this playground: https://react-docgen.dev/playground
Code:
import type { CSSProperties, HTMLAttributes, ReactNode, Ref } from 'react';

export const cardSizes = ['small', 'medium', 'large'] as const;
export type DsCardSize = (typeof cardSizes)[number];

/**
 * Props for the Card root component
 */
export interface DsCardRootProps extends HTMLAttributes<HTMLDivElement> {
	/**
	 * Forwarded ref for the card element
	 */
	ref?: Ref<HTMLDivElement>;

	/**
	 * Card size variant
	 * @default 'medium'
	 */
	size?: DsCardSize;

	/**
	 * Enable selectable/interactive mode. When false, the card is a static display container.
	 * @default false
	 */
	selectable?: boolean;

	/**
	 * Selected state (controlled). Required when selectable is true.
	 * @default false
	 */
	selected?: boolean;

	/**
	 * When true, shows a highlighted background when selected
	 * @default false
	 */
	highlightSelected?: boolean;

	/**
	 * Whether the card is disabled. Prevents selection and click interactions and
	 * visually dims the card.
	 * @default false
	 */
	disabled?: boolean;
}

/**
 * Common props for card slot components
 */
export interface DsCardSlotProps extends HTMLAttributes<HTMLDivElement> {
	/**
	 * Additional CSS class names
	 */
	className?: string;

	/**
	 * Additional styles to apply
	 */
	style?: CSSProperties;

	/**
	 * Slot content
	 */
	children?: ReactNode;

	/**
	 * Forwarded ref
	 */
	ref?: Ref<HTMLDivElement>;
}

export type DsCardHeaderProps = DsCardSlotProps;
export type DsCardBodyProps = DsCardSlotProps;
export type DsCardFooterProps = DsCardSlotProps;


File: /opt/build/repo/packages/design-system/src/components/ds-card/ds-card.types.ts
Error:
No suitable component definition found.
You can debug your component file in this playground: https://react-docgen.dev/playground
Code:
import type { CSSProperties, HTMLAttributes, ReactNode, Ref } from 'react';

export const cardSizes = ['small', 'medium', 'large'] as const;
export type DsCardSize = (typeof cardSizes)[number];

/**
 * Props for the Card root component
 */
export interface DsCardRootProps extends HTMLAttributes<HTMLDivElement> {
	/**
	 * Forwarded ref for the card element
	 */
	ref?: Ref<HTMLDivElement>;

	/**
	 * Card size variant
	 * @default 'medium'
	 */
	size?: DsCardSize;

	/**
	 * Enable selectable/interactive mode. When false, the card is a static display container.
	 * @default false
	 */
	selectable?: boolean;

	/**
	 * Selected state (controlled). Required when selectable is true.
	 * @default false
	 */
	selected?: boolean;

	/**
	 * When true, shows a highlighted background when selected
	 * @default false
	 */
	highlightSelected?: boolean;

	/**
	 * Whether the card is disabled. Prevents selection and click interactions and
	 * visually dims the card.
	 * @default false
	 */
	disabled?: boolean;
}

/**
 * Common props for card slot components
 */
export interface DsCardSlotProps extends HTMLAttributes<HTMLDivElement> {
	/**
	 * Additional CSS class names
	 */
	className?: string;

	/**
	 * Additional styles to apply
	 */
	style?: CSSProperties;

	/**
	 * Slot content
	 */
	children?: ReactNode;

	/**
	 * Forwarded ref
	 */
	ref?: Ref<HTMLDivElement>;
}

export type DsCardHeaderProps = DsCardSlotProps;
export type DsCardBodyProps = DsCardSlotProps;
export type DsCardFooterProps = DsCardSlotProps;
Info
No description found. Write a jsdoc comment such as /** Component description */.
Imports
import { DsCard, DsIcon, DsStatusBadge, DsTypography } from "@drivenets/design-system";
Default story ok
const Default = () => <DsCard.Root size="medium">
    <DsCard.Header>Card Title</DsCard.Header>
    <DsCard.Body>Card content goes here</DsCard.Body>
</DsCard.Root>;
Sizes story ok
const Sizes = () => (
    <div className={styles.container}>
        <DsCard.Root size="small">
            <DsCard.Header>Small Card</DsCard.Header>
            <DsCard.Body>Small content</DsCard.Body>
        </DsCard.Root>

        <DsCard.Root size="medium">
            <DsCard.Header>Medium Card</DsCard.Header>
            <DsCard.Body>Medium content</DsCard.Body>
        </DsCard.Root>

        <DsCard.Root size="large">
            <DsCard.Header>Large Card</DsCard.Header>
            <DsCard.Body>Large content</DsCard.Body>
        </DsCard.Root>
    </div>
);
With Header And Footer story ok
const WithHeaderAndFooter = () => (
    <DsCard.Root size="large">
        <DsCard.Header className={styles.headerRow}>
            <DsTypography variant="heading3">Card Title</DsTypography>
            <DsStatusBadge icon="check_circle" status="active" ghost />
        </DsCard.Header>
        <DsCard.Body className={styles.statsBlock}>
            <DsTypography variant="body-md-bold">12 of 12 Devices</DsTypography>
            <DsTypography variant="body-sm-reg">Success 10 | Failed 1 | Skipped 1</DsTypography>
        </DsCard.Body>
        <DsCard.Footer className={styles.footer}>
            <DsTypography variant="body-sm-reg">Last updated: 2 min ago</DsTypography>
        </DsCard.Footer>
    </DsCard.Root>
);
Step Card story ok
const StepCard = () => (
    <DsCard.Root size="large">
        <DsCard.Header className={styles.headerRow}>
            <DsTypography variant="heading3">Canary</DsTypography>
            <DsStatusBadge icon="check_circle" status="active" label="Complete" />
        </DsCard.Header>
        <DsCard.Body className={styles.statsBlock}>
            <DsTypography variant="body-md-bold">12 of 12 Devices</DsTypography>
            <DsTypography variant="body-sm-reg" className={styles.textSecondary}>
                Success 10 | Failed 1 | Skipped 1
            </DsTypography>
        </DsCard.Body>
        <DsCard.Body className={styles.dataList}>
            <DsTypography variant="body-sm-reg">Config Push</DsTypography>
            <DsTypography variant="body-sm-reg" className={styles.textSuccess}>
                Complete
            </DsTypography>
            <DsTypography variant="body-sm-reg">Dwell Time (60 min.)</DsTypography>
            <DsTypography variant="body-sm-reg" className={styles.textSuccess}>
                Complete
            </DsTypography>
            <DsTypography variant="body-sm-reg">Failed</DsTypography>
            <DsTypography variant="body-sm-reg" className={styles.textSecondary}>
                1 (8%)
            </DsTypography>
            <DsTypography variant="body-sm-reg">Failure threshold</DsTypography>
            <DsTypography variant="body-sm-reg" className={styles.textSecondary}>
                5 or 10%
            </DsTypography>
            <DsTypography variant="body-sm-reg">Threshold state</DsTypography>
            <DsTypography variant="body-sm-reg" className={styles.textSecondary}>
                Normal
            </DsTypography>
        </DsCard.Body>
    </DsCard.Root>
);
Selectable story ok
const Selectable = () => <DsCard.Root selectable selected={false} onClick={fn()}>
    <DsCard.Header>Selectable Card</DsCard.Header>
    <DsCard.Body>Click to select this card</DsCard.Body>
</DsCard.Root>;
Highlight Selected story ok
const HighlightSelected = () => <DsCard.Root selectable selected highlightSelected>
    <DsCard.Header>Highlighted Card</DsCard.Header>
    <DsCard.Body>This card has a highlighted background when selected</DsCard.Body>
</DsCard.Root>;
Selectable Controlled story ok
const SelectableControlled = function Render() {
    const [selected, setSelected] = useState(false);

    return (
        <DsCard.Root selectable selected={selected} onClick={() => setSelected(!selected)}>
            <DsCard.Header>Controlled Card</DsCard.Header>
            <DsCard.Body>{selected ? 'Selected! Click to deselect.' : 'Click to select.'}</DsCard.Body>
        </DsCard.Root>
    );
};
Disabled story ok
const Disabled = () => <DsCard.Root size="large" selectable disabled onClick={fn()}>
    <DsCard.Header className={styles.headerRow}>
        <DsTypography variant="heading3">Canary</DsTypography>
        <DsStatusBadge icon="check_circle" status="active" label="Complete" />
    </DsCard.Header>
    <DsCard.Body className={styles.statsBlock}>
        <DsTypography variant="body-md-bold">12 of 12 Devices</DsTypography>
        <DsTypography variant="body-sm-reg" className={styles.textSecondary}>Success 10 | Failed 1 | Skipped 1
                            </DsTypography>
    </DsCard.Body>
    <DsCard.Body className={styles.dataList}>
        <DsTypography variant="body-sm-reg">Config Push</DsTypography>
        <DsTypography variant="body-sm-reg" className={styles.textSuccess}>Complete
                            </DsTypography>
        <DsTypography variant="body-sm-reg">Dwell Time (60 min.)</DsTypography>
        <DsTypography variant="body-sm-reg" className={styles.textSuccess}>Complete
                            </DsTypography>
        <DsTypography variant="body-sm-reg">Failed</DsTypography>
        <DsTypography variant="body-sm-reg" className={styles.textSecondary}>1 (8%)
                            </DsTypography>
        <DsTypography variant="body-sm-reg">Failure threshold</DsTypography>
        <DsTypography variant="body-sm-reg" className={styles.textSecondary}>5 or 10%
                            </DsTypography>
        <DsTypography variant="body-sm-reg">Threshold state</DsTypography>
        <DsTypography variant="body-sm-reg" className={styles.textSecondary}>Normal
                            </DsTypography>
    </DsCard.Body>
</DsCard.Root>;
Collapsible story ok
const Collapsible = function Render() {
    const [expanded, setExpanded] = useState(true);

    return (
        <DsCard.Root size="large" className={styles.collapseRoot}>
            <DsCard.Header className={styles.collapseHeader}>
                <button
                    type="button"
                    className={styles.collapsibleButton}
                    onClick={() => setExpanded(!expanded)}
                    aria-expanded={expanded}
                >
                    <DsIcon
                        icon="expand_more"
                        style={{
                            transform: expanded ? 'rotate(180deg)' : 'rotate(0deg)',
                            transition: 'transform 150ms ease-in-out',
                        }}
                    />
                </button>
                <DsTypography variant="heading3">Collapsible Card</DsTypography>
            </DsCard.Header>
            <DsCard.Body className={styles.collapsibleContent} data-collapsed={!expanded}>
                <div className={styles.collapsibleContentInner}>
                    <DsTypography variant="body-md-reg">
                        This content can be collapsed by clicking the header. The height animates smoothly using CSS
                        Grid.
                    </DsTypography>
                </div>
            </DsCard.Body>
        </DsCard.Root>
    );
};

DsCheckbox

components-checkbox · ./src/components/ds-checkbox/ds-checkbox.stories.tsx
Design system Checkbox component
Prop types (react-docgen) 6 prop types
Component: src/components/ds-checkbox/ds-checkbox.tsx::default
Props:
/**
 * Disables the checkbox, preventing user interaction
 */
disabled?: boolean

/**
 * Label for the checkbox
 */
label?: ReactNode

/**
 * Additional label info for the checkbox
 */
labelInfo?: ReactNode

/**
 * Callback invoked when the checked state changes
 */
onCheckedChange?: (checked: boolean | 'indeterminate') => void

/**
 * Value when used in a group or for form submission. Coerced to string for the native control.
 */
value?: string | number

/**
 * Visual variant of the checkbox
 */
variant?: (typeof checkboxVariants)[number] = 'default'
Imports
import { DsCheckbox, DsCheckboxGroup, DsStack } from "@drivenets/design-system";
Default story ok
const Default = () => <DsCheckbox label={label} labelInfo={labelInfo} className="custom-checkbox" />;
Indeterminate story ok
const Indeterminate = function Render() {
    const [checked, setChecked] = useState<boolean | 'indeterminate'>('indeterminate');

    return (
        <DsCheckbox
            label={label}
            labelInfo={labelInfo}
            checked={checked}
            onCheckedChange={(newState) => setChecked(newState)}
        />
    );
};
Disabled story ok
const Disabled = () => (
    <DsStack gap="var(--3xs)">
        <DsCheckbox label={label} labelInfo={labelInfo} disabled />
        <DsCheckbox label={label} labelInfo={labelInfo} disabled checked />
        <DsCheckbox label={label} labelInfo={labelInfo} disabled checked="indeterminate" />
    </DsStack>
);
Read Only story ok
const ReadOnly = () => <DsCheckbox label={label} labelInfo={labelInfo} readOnly checked />;
Warning story ok
const Warning = () => <DsCheckbox variant="warning" />;
Warning With Label story ok
const WarningWithLabel = () => <DsCheckbox variant="warning" label={label} labelInfo={labelInfo} />;
Warning Indeterminate story ok
const WarningIndeterminate = function Render() {
    const [checked, setChecked] = useState<boolean | 'indeterminate'>('indeterminate');

    return (
        <DsCheckbox
            variant="warning"
            label={label}
            labelInfo={labelInfo}
            checked={checked}
            onCheckedChange={(newState) => setChecked(newState)}
        />
    );
};
Group story ok
const Group = function Render() {
    const [vertical, setVertical] = useState<string[]>(['react']);
    const [horizontal, setHorizontal] = useState<string[]>(['react']);

    return (
        <DsStack gap="var(--3xs)">
            <DsStack gap="var(--3xs)">
                <strong>Vertical</strong>
                <output className={styles.output}>Selected: {vertical.join(', ')}</output>
                <DsCheckboxGroup
                    orientation="vertical"
                    value={vertical}
                    onValueChange={setVertical}
                    name="framework-vertical"
                >
                    {frameworkItems.map((item) => (
                        <DsCheckbox
                            key={item.value}
                            label={item.label}
                            value={item.value}
                            className={styles.groupItem}
                        />
                    ))}
                </DsCheckboxGroup>
            </DsStack>

            <DsStack gap="var(--3xs)">
                <strong>Horizontal</strong>
                <output className={styles.output}>Selected: {horizontal.join(', ')}</output>
                <DsCheckboxGroup
                    orientation="horizontal"
                    value={horizontal}
                    onValueChange={setHorizontal}
                    name="framework-horizontal"
                >
                    {frameworkItems.map((item) => (
                        <DsCheckbox
                            key={item.value}
                            label={item.label}
                            value={item.value}
                            className={styles.groupItem}
                        />
                    ))}
                </DsCheckboxGroup>
            </DsStack>
        </DsStack>
    );
};
Select All story ok
const SelectAll = function Render() {
    const [value, setValue] = useState<string[]>([]);
    const { selectAllState, onSelectAllChange } = useCheckboxSelectAll({
        value,
        allValues: frameworkValues,
        onValueChange: setValue,
    });

    return (
        <DsStack gap="var(--3xs)">
            <DsCheckbox label="Select all" checked={selectAllState} onCheckedChange={onSelectAllChange} />
            <DsCheckboxGroup
                className={styles.groupIndented}
                value={value}
                onValueChange={setValue}
                name="framework"
            >
                {frameworkItems.map((item) => (
                    <DsCheckbox key={item.value} label={item.label} value={item.value} className={styles.groupItem} />
                ))}
            </DsCheckboxGroup>
        </DsStack>
    );
};

DsChip

components-chip-deprecated · ./src/components/ds-chip/ds-chip.stories.ts
deprecated: This component is deprecated. Use `DsTag` instead.see: {@link ../ds-tag/ds-tag.stories} for examples of the replacement component.
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 9 prop types
Component: src/components/ds-chip/ds-chip.tsx::default
Props:
/**
 * Additional CSS class names
 */
className?: string

/**
 * Custom delete icon element
 */
deleteIcon?: IconName | FunctionComponent<SVGProps<SVGSVGElement>>

/**
 * The label text to display in the chip
 */
label: string

/**
 * Optional click handler
 */
onClick?: (event: MouseEvent<HTMLElement>) => void

/**
 * Callback function when delete icon is clicked
 */
onDelete?: (event: MouseEvent<HTMLElement> | KeyboardEvent<HTMLElement>) => void

/**
 * Ref to the chip element
 */
ref?: Ref<HTMLElement>

/**
 * Whether the chip is in a selected/pressed state
 * @default false
 */
selected?: boolean = false

/**
 * Size of the chip
 * @default 'medium'
 */
size?: (typeof chipSizes)[number] = 'medium'

/**
 * Additional styles to apply to the component
 */
style?: CSSProperties = {}
Imports
import { DsChip } from "@drivenets/design-system";
Default story ok
const Default = () => <DsChip label="Default Label" />;

DsChipGroup

components-chipgroup-deprecated · ./src/components/ds-chip-group/ds-chip-group.stories.tsx
deprecated: This component is deprecated. Use `DsTagFilter` instead.see: {@link ../ds-tag-filter/ds-tag-filter.stories} for examples of the replacement component.
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 7 prop types
Component: src/components/ds-chip-group/ds-chip-group.tsx::default
Props:
/**
 * Additional CSS class names
 */
className?: string

/**
 * Array of chip items to display
 */
items: ChipItem[]

/**
 * Label text to display before the chips (e.g., "Filtered by:")
 * @default "Filtered by:"
 */
label?: string = 'Filtered by:'

/**
 * 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 styles to apply to the component
 */
style?: CSSProperties
Imports
import { DsChipGroup } from "@drivenets/design-system";
Default story ok
const Default = 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 (
        <div className={styles.container}>
            <DsChipGroup
                items={filters}
                onClearAll={handleClearAll}
                onItemDelete={handleFilterDelete}
                onItemSelect={handleFilterSelect}
            />
            <div className={styles.controlsContainer}>
                <button onClick={handleAddFilter} className={styles.addButton}>
                    Add Filter
                </button>
                <p className={styles.infoText}>Total filters: {filters.length}</p>
                <p className={styles.infoText}>
                    Selected filters: [
                    {filters
                        .filter((filter) => filter.selected)
                        .map((filter) => `"${filter.label}"`)
                        .join(', ')}
                    ]
                </p>
            </div>
        </div>
    );
};

DsCommentBubble

components-comments-commentbubble · ./src/components/ds-comment-bubble/ds-comment-bubble.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 23 prop types
Component: src/components/ds-comment-bubble/ds-comment-bubble.tsx::DsCommentBubble
Props:
/**
 * Whether the comment is flagged as requiring action. Drives the action-required
 * highlight and the checkbox state.
 * @default false
 */
actionRequired?: boolean = false

/**
 * Additional CSS class name applied to the bubble root
 */
className?: string

/**
 * Existing comment thread rendered inside the bubble. Omit to render an empty
 * bubble for composing a new comment.
 */
comment?: CommentData

/**
 * The user currently viewing the bubble. Used to decide whether thread messages
 * can be edited or deleted by the viewer.
 */
currentUser?: CommentAuthor

/**
 * Hide the "action required" checkbox and the associated visual highlight.
 * @default false
 */
hideActionRequired?: boolean = false

/**
 * Called when the "action required" checkbox is toggled. Receives the new checked state.
 */
onActionRequiredChange?: (checked: boolean) => void

/**
 * Called when the bubble should be dismissed (e.g., clicking outside an empty bubble
 * or pressing the close affordance).
 */
onClose?: () => void

/**
 * Called when the user copies a link to the top-level comment.
 */
onCopyLink?: () => void

/**
 * Called when the user deletes the top-level comment.
 */
onDelete?: () => void

/**
 * Called when a thread message is deleted. Receives the message id.
 */
onDeleteMessage?: (messageId: string) => void

/**
 * Called when a thread message is edited. Receives the message id and the new content.
 */
onEditMessage?: (messageId: string, newContent: string) => void

/**
 * Called when the user forwards the top-level comment from the bubble header menu.
 */
onForward?: () => void

/**
 * Called when the user marks the top-level comment as unread from the bubble header menu.
 */
onMarkUnread?: () => void

/**
 * Called when a thread message is marked as unread. Receives the message id.
 */
onMessageMarkUnread?: (messageId: string) => void

/**
 * Called when a thread message is resolved. Receives the message id.
 */
onMessageResolved?: (messageId: string) => void

/**
 * Called when the user resolves the top-level comment from the bubble header menu.
 */
onResolve?: () => void

/**
 * Called when the user submits the textarea content (clicks send or presses Enter).
 * Receives the message content and the current action-required flag.
 */
onSend?: (content: string, actionRequired: boolean) => void

/**
 * Called when the user toggles the top-level comment's action-required flag from
 * the bubble header menu.
 */
onToggleActionRequired?: () => void

/**
 * Called whenever the textarea value changes. Receives the new string value.
 */
onValueChange?: (value: string) => void

/**
 * Ref forwarded to the bubble root element
 */
ref?: Ref<HTMLDivElement>

/**
 * Optional reference chip shown in the bubble header. A string is rendered as a
 * `DsTag` with a sell icon; any other `ReactNode` is rendered verbatim.
 */
referenceTag?: ReactNode

/**
 * Inline styles applied to the bubble root
 */
style?: CSSProperties

/**
 * Current value of the reply / new-comment textarea (controlled)
 */
value?: string = ''
Imports
import { DsCommentBubble } from "@drivenets/design-system";
Initial story ok
const Initial = () => <DsCommentBubble
    referenceTag="Resource allocation"
    onSend={fn()}
    onClose={fn()}
    onResolve={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    onActionRequiredChange={fn()}
    onValueChange={fn()}
    onEditMessage={fn()}
    onDeleteMessage={fn()}
    onMessageMarkUnread={fn()}
    onMessageResolved={fn()}
    value="" />;
Typing story ok
const Typing = () => <DsCommentBubble
    referenceTag="Resource allocation"
    onSend={fn()}
    onClose={fn()}
    onResolve={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    onActionRequiredChange={fn()}
    onValueChange={fn()}
    onEditMessage={fn()}
    onDeleteMessage={fn()}
    onMessageMarkUnread={fn()}
    onMessageResolved={fn()}
    value="This is a new comment..." />;
Typing With Action Required story ok
const TypingWithActionRequired = () => <DsCommentBubble
    referenceTag="Resource allocation"
    onSend={fn()}
    onClose={fn()}
    onResolve={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    onActionRequiredChange={fn()}
    onValueChange={fn()}
    onEditMessage={fn()}
    onDeleteMessage={fn()}
    onMessageMarkUnread={fn()}
    onMessageResolved={fn()}
    value="This needs attention!"
    actionRequired />;
Typing Without Action Required story ok
const TypingWithoutActionRequired = () => <DsCommentBubble
    referenceTag="Resource allocation"
    onSend={fn()}
    onClose={fn()}
    onResolve={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    onActionRequiredChange={fn()}
    onValueChange={fn()}
    onEditMessage={fn()}
    onDeleteMessage={fn()}
    onMessageMarkUnread={fn()}
    onMessageResolved={fn()}
    value="This is a new comment..."
    hideActionRequired />;
Thread story ok
const Thread = () => <DsCommentBubble
    referenceTag="Resource allocation"
    onSend={fn()}
    onClose={fn()}
    onResolve={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    onActionRequiredChange={fn()}
    onValueChange={fn()}
    onEditMessage={fn()}
    onDeleteMessage={fn()}
    onMessageMarkUnread={fn()}
    onMessageResolved={fn()}
    comment={createMockComment()}
    currentUser={currentUser} />;
Thread With Action Required story ok
const ThreadWithActionRequired = () => <DsCommentBubble
    referenceTag="Resource allocation"
    onSend={fn()}
    onClose={fn()}
    onResolve={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    onActionRequiredChange={fn()}
    onValueChange={fn()}
    onEditMessage={fn()}
    onDeleteMessage={fn()}
    onMessageMarkUnread={fn()}
    onMessageResolved={fn()}
    comment={createMockComment()}
    actionRequired
    currentUser={currentUser} />;
Thread Without Action Required story ok
const ThreadWithoutActionRequired = () => <DsCommentBubble
    referenceTag="Resource allocation"
    onSend={fn()}
    onClose={fn()}
    onResolve={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    onActionRequiredChange={fn()}
    onValueChange={fn()}
    onEditMessage={fn()}
    onDeleteMessage={fn()}
    onMessageMarkUnread={fn()}
    onMessageResolved={fn()}
    comment={createMockComment()}
    currentUser={currentUser}
    hideActionRequired
    actionRequired />;
Send Button Click story ok
const SendButtonClick = () => <DsCommentBubble
    referenceTag="Resource allocation"
    onSend={fn()}
    onClose={fn()}
    onResolve={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    onActionRequiredChange={fn()}
    onValueChange={fn()}
    onEditMessage={fn()}
    onDeleteMessage={fn()}
    onMessageMarkUnread={fn()}
    onMessageResolved={fn()}
    value="Test message to send" />;
Send With Enter Key story ok
const SendWithEnterKey = () => <DsCommentBubble
    referenceTag="Resource allocation"
    onSend={fn()}
    onClose={fn()}
    onResolve={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    onActionRequiredChange={fn()}
    onValueChange={fn()}
    onEditMessage={fn()}
    onDeleteMessage={fn()}
    onMessageMarkUnread={fn()}
    onMessageResolved={fn()}
    value="Enter key message" />;
Send Disabled When Empty story ok
const SendDisabledWhenEmpty = () => <DsCommentBubble
    referenceTag="Resource allocation"
    onSend={fn()}
    onClose={fn()}
    onResolve={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    onActionRequiredChange={fn()}
    onValueChange={fn()}
    onEditMessage={fn()}
    onDeleteMessage={fn()}
    onMessageMarkUnread={fn()}
    onMessageResolved={fn()}
    value="" />;
Thread Send Disabled When Empty story ok
const ThreadSendDisabledWhenEmpty = () => <DsCommentBubble
    referenceTag="Resource allocation"
    onSend={fn()}
    onClose={fn()}
    onResolve={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    onActionRequiredChange={fn()}
    onValueChange={fn()}
    onEditMessage={fn()}
    onDeleteMessage={fn()}
    onMessageMarkUnread={fn()}
    onMessageResolved={fn()}
    comment={createMockComment()}
    currentUser={currentUser}
    value="" />;
Thread Send Enabled story ok
const ThreadSendEnabled = () => <DsCommentBubble
    referenceTag="Resource allocation"
    onSend={fn()}
    onClose={fn()}
    onResolve={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    onActionRequiredChange={fn()}
    onValueChange={fn()}
    onEditMessage={fn()}
    onDeleteMessage={fn()}
    onMessageMarkUnread={fn()}
    onMessageResolved={fn()}
    comment={createMockComment()}
    currentUser={currentUser}
    value="A reply" />;
Thread Close Button story ok
const ThreadCloseButton = () => <DsCommentBubble
    referenceTag="Resource allocation"
    onSend={fn()}
    onClose={fn()}
    onResolve={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    onActionRequiredChange={fn()}
    onValueChange={fn()}
    onEditMessage={fn()}
    onDeleteMessage={fn()}
    onMessageMarkUnread={fn()}
    onMessageResolved={fn()}
    comment={createMockComment()}
    currentUser={currentUser} />;
Thread Resolve Button story ok
const ThreadResolveButton = () => <DsCommentBubble
    referenceTag="Resource allocation"
    onSend={fn()}
    onClose={fn()}
    onResolve={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    onActionRequiredChange={fn()}
    onValueChange={fn()}
    onEditMessage={fn()}
    onDeleteMessage={fn()}
    onMessageMarkUnread={fn()}
    onMessageResolved={fn()}
    comment={createMockComment()}
    currentUser={currentUser} />;
Textarea Value Change story ok
const TextareaValueChange = () => <DsCommentBubble
    referenceTag="Resource allocation"
    onSend={fn()}
    onClose={fn()}
    onResolve={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    onActionRequiredChange={fn()}
    onValueChange={fn()}
    onEditMessage={fn()}
    onDeleteMessage={fn()}
    onMessageMarkUnread={fn()}
    onMessageResolved={fn()}
    comment={createMockComment()}
    currentUser={currentUser}
    value="" />;
Initial With Reference Tag story ok
const InitialWithReferenceTag = () => <DsCommentBubble
    referenceTag="My tag"
    onSend={fn()}
    onClose={fn()}
    onResolve={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    onActionRequiredChange={fn()}
    onValueChange={fn()}
    onEditMessage={fn()}
    onDeleteMessage={fn()}
    onMessageMarkUnread={fn()}
    onMessageResolved={fn()} />;
Thread With Reference Tag story ok
const ThreadWithReferenceTag = () => <DsCommentBubble
    referenceTag="Resource allocation"
    onSend={fn()}
    onClose={fn()}
    onResolve={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    onActionRequiredChange={fn()}
    onValueChange={fn()}
    onEditMessage={fn()}
    onDeleteMessage={fn()}
    onMessageMarkUnread={fn()}
    onMessageResolved={fn()}
    comment={createMockComment()}
    currentUser={currentUser} />;
Full Interactive Flow story ok
const FullInteractiveFlow = function FullFlowStory() {
    const [value, setValue] = useState('');
    const [actionRequired, setActionRequired] = useState(false);
    const [comment, setComment] = useState<CommentData | undefined>(undefined);

    const handleSend = (content: string, isActionRequired: boolean) => {
        if (!comment) {
            const newComment: CommentData = {
                id: 'comment-1',
                numericId: 63,
                author: currentUser,
                createdAt: new Date(),
                isResolved: false,
                messages: [
                    {
                        id: 'msg-1',
                        author: currentUser,
                        content,
                        createdAt: new Date(),
                        isInitialMessage: true,
                    },
                ],
            };
            setComment(newComment);
            setActionRequired(isActionRequired);
        } else {
            const newMessage = {
                id: `msg-${String(Date.now())}`,
                author: currentUser,
                content,
                createdAt: new Date(),
            };

            setComment((prev) => {
                if (!prev) {
                    return prev;
                }
                return {
                    ...prev,
                    messages: [...prev.messages, newMessage],
                };
            });
        }
        setValue('');
    };

    const handleEditMessage = (messageId: string, newContent: string) => {
        if (!comment) {
            return;
        }
        setComment((prev) => {
            if (!prev) {
                return prev;
            }
            return {
                ...prev,
                messages: prev.messages.map((msg) =>
                    msg.id === messageId ? { ...msg, content: newContent } : msg,
                ),
            };
        });
    };

    const handleDeleteMessage = (messageId: string) => {
        if (!comment) {
            return;
        }
        setComment((prev) => {
            if (!prev) {
                return prev;
            }
            return {
                ...prev,
                messages: prev.messages.filter((msg) => msg.id !== messageId),
            };
        });
    };

    const handleClose = () => {
        setComment(undefined);
        setValue('');
        setActionRequired(false);
    };

    return (
        <div className={styles.interactiveContainer}>
            <p className={styles.instructions}>
                <strong>Full Flow Test:</strong>
                <br />
                1. Start by typing in the bubble - it will expand and show the footer
                <br />
                2. Send your first comment to create a thread
                <br />
                3. Add more replies to see the thread grow
                <br />
                4. Close to reset and start over
            </p>
            <DsCommentBubble
                comment={comment}
                currentUser={currentUser}
                referenceTag="Resource allocation"
                value={value}
                onValueChange={setValue}
                actionRequired={actionRequired}
                onActionRequiredChange={setActionRequired}
                onSend={handleSend}
                onEditMessage={handleEditMessage}
                onDeleteMessage={handleDeleteMessage}
                onMessageMarkUnread={(id) => console.log('Mark unread message:', id)}
                onMessageResolved={(id) => console.log('Resolved message:', id)}
                onClose={handleClose}
                onResolve={() => console.log('Resolve clicked')}
                onToggleActionRequired={() => console.log('Toggle action required')}
                onForward={() => console.log('Forward')}
                onMarkUnread={() => console.log('Mark unread')}
                onCopyLink={() => console.log('Copy link')}
                onDelete={() => console.log('Delete')}
            />
        </div>
    );
};
Interactive Thread story ok
const InteractiveThread = function InteractiveThreadStory() {
    const [value, setValue] = useState('');
    const [actionRequired, setActionRequired] = useState(false);
    const [comment, setComment] = useState(createMockComment());

    const handleSend = (content: string) => {
        const newMessage = {
            id: `msg-${String(Date.now())}`,
            author: currentUser,
            content,
            createdAt: new Date(),
        };

        setComment((prev) => ({
            ...prev,
            messages: [...prev.messages, newMessage],
        }));
        setValue('');
    };

    const handleEditMessage = (messageId: string, newContent: string) => {
        setComment((prev) => ({
            ...prev,
            messages: prev.messages.map((msg) => (msg.id === messageId ? { ...msg, content: newContent } : msg)),
        }));
    };

    const handleDeleteMessage = (messageId: string) => {
        setComment((prev) => ({
            ...prev,
            messages: prev.messages.filter((msg) => msg.id !== messageId),
        }));
    };

    return (
        <DsCommentBubble
            comment={comment}
            currentUser={currentUser}
            referenceTag="Resource allocation"
            value={value}
            onValueChange={setValue}
            actionRequired={actionRequired}
            onActionRequiredChange={setActionRequired}
            onSend={handleSend}
            onEditMessage={handleEditMessage}
            onDeleteMessage={handleDeleteMessage}
            onMessageMarkUnread={(id) => console.log('Mark unread message:', id)}
            onMessageResolved={(id) => console.log('Resolved message:', id)}
            onClose={() => console.log('Close clicked')}
            onResolve={() => console.log('Resolve clicked')}
            onToggleActionRequired={() => console.log('Toggle action required')}
            onForward={() => console.log('Forward')}
            onMarkUnread={() => console.log('Mark unread')}
            onCopyLink={() => console.log('Copy link')}
            onDelete={() => console.log('Delete')}
        />
    );
};
All Types story ok
const AllTypes = () => (
    <div className={styles.grid}>
        <div className={styles.column}>
            <h4 className={styles.heading}>Initial</h4>
            <DsCommentBubble />
        </div>

        <div className={styles.column}>
            <h4 className={styles.heading}>Typing</h4>
            <DsCommentBubble value="This is a comment..." />
        </div>

        <div className={styles.column}>
            <h4 className={styles.heading}>Typing (Action Required)</h4>
            <DsCommentBubble value="This needs attention!" actionRequired />
        </div>

        <div className={styles.column}>
            <h4 className={styles.heading}>Thread</h4>
            <DsCommentBubble comment={createMockComment()} currentUser={currentUser} />
        </div>

        <div className={styles.column}>
            <h4 className={styles.heading}>Thread (Action Required)</h4>
            <DsCommentBubble comment={createMockComment()} currentUser={currentUser} actionRequired />
        </div>
    </div>
);

DsCommentCard

components-comments-commentcard · ./src/components/ds-comment-card/ds-comment-card.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 14 prop types
Component: src/components/ds-comment-card/ds-comment-card.tsx::DsCommentCard
Props:
/**
 * Additional CSS class name applied to the card root
 */
className?: string

/**
 * The comment thread rendered by the card
 */
comment: CommentData

/**
 * Whether the card is disabled and cannot be clicked
 * @default false
 */
disabled?: boolean = false

/**
 * Formatter for the card's timestamp. Receives the `createdAt` Date and returns
 * the string to display. Lets consumers localize or swap the relative/absolute format.
 */
formatTimestamp?: (date: Date) => string = (date: Date): string => {
	const now = new Date();
	const diffMs = now.getTime() - date.getTime();

	if (diffMs < 0) {
		return 'Just now';
	}

	const diffSeconds = Math.floor(diffMs / MS_PER_SECOND);
	const diffMinutes = Math.floor(diffSeconds / SECONDS_PER_MINUTE);
	const diffHours = Math.floor(diffMinutes / MINUTES_PER_HOUR);
	const diffDays = Math.floor(diffHours / HOURS_PER_DAY);
	const diffWeeks = Math.floor(diffDays / DAYS_PER_WEEK);
	const diffMonths = Math.floor(diffDays / DAYS_PER_MONTH);
	const diffYears = Math.floor(diffMonths / MONTHS_PER_YEAR);

	if (diffSeconds < JUST_NOW_THRESHOLD_SECONDS) {
		return 'Just now';
	}

	if (diffMinutes < MINUTES_PER_HOUR) {
		return `${String(diffMinutes)}m ago`;
	}

	if (diffHours < HOURS_PER_DAY) {
		return `${String(diffHours)}h ago`;
	}

	if (diffDays < DAYS_PER_WEEK) {
		return `${String(diffDays)}d ago`;
	}

	if (diffWeeks < 4) {
		return `${String(diffWeeks)}w ago`;
	}

	if (diffYears >= 1) {
		return `${String(diffYears)}y ago`;
	}

	return `${String(diffMonths)}mo ago`;
}

/**
 * Called when the card body is clicked (typically opens the thread in a bubble/drawer).
 */
onClick?: () => void

/**
 * Called when the user copies a link to the comment from the overflow menu.
 */
onCopyLink?: () => void

/**
 * Called when the user deletes the comment from the overflow menu.
 */
onDelete?: () => void

/**
 * Called when the user forwards the comment from the overflow menu.
 */
onForward?: () => void

/**
 * Called when the user marks the comment as unread from the overflow menu.
 */
onMarkUnread?: () => void

/**
 * Called when the user resolves the comment from the card's overflow menu.
 */
onResolve?: () => void

/**
 * Called when the user toggles the comment's action-required flag from the overflow menu.
 */
onToggleActionRequired?: () => void

/**
 * How long message content is rendered:
 * - `hidden` (default): truncate to a preview with ellipsis
 * - `displayed`: show the full message content without truncation
 * @default hidden
 */
overflow?: 'hidden' | 'displayed' = 'hidden'

/**
 * Ref forwarded to the card root button element
 */
ref?: Ref<HTMLButtonElement>

/**
 * Inline styles applied to the card root
 */
style?: CSSProperties
Imports
import { DsCommentCard } from "@drivenets/design-system";
Default Card story ok
const DefaultCard = () => <DsCommentCard
    onClick={fn()}
    onResolve={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    comment={createMockComment()}
    overflow="hidden" />;
Action Required story ok
const ActionRequired = () => <DsCommentCard
    onClick={fn()}
    onResolve={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    comment={createMockComment({ isActionRequired: true })}
    overflow="hidden" />;
Disabled State story ok
const DisabledState = () => <DsCommentCard
    onClick={fn()}
    onResolve={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    comment={createMockComment()}
    disabled
    overflow="hidden" />;
Full Message story ok
const FullMessage = () => <DsCommentCard
    onClick={fn()}
    onResolve={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    comment={createMockComment()}
    overflow="displayed" />;
Single Message story ok
const SingleMessage = () => <DsCommentCard
    onClick={fn()}
    onResolve={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    comment={createMockComment({
        messages: [
            {
                id: 'msg-1',
                author: {
                    id: 'user-1',
                    name: 'Karen J.',
                    avatarSrc: 'https://i.pravatar.cc/40?img=1',
                },
                content: 'This is a short single message comment.',
                createdAt: new Date(Date.now() - 24 * 60 * 60 * 1000),
                isInitialMessage: true,
            },
        ],
    })}
    overflow="hidden" />;
Default story ok
const Default = () => (
    <div className={styles.grid}>
        <div className={styles.column}>
            <h4 className={styles.heading}>Default</h4>
            <DsCommentCard comment={createMockComment()} overflow="hidden" />
        </div>

        <div className={styles.column}>
            <h4 className={styles.heading}>Action Required</h4>
            <DsCommentCard comment={createMockComment()} overflow="hidden" />
        </div>

        <div className={styles.column}>
            <h4 className={styles.heading}>Disabled</h4>
            <DsCommentCard comment={createMockComment()} disabled={true} overflow="hidden" />
        </div>

        <div className={styles.column}>
            <h4 className={styles.heading}>Full Message</h4>
            <DsCommentCard comment={createMockComment()} overflow="displayed" />
        </div>
    </div>
);
With Reference Tag story ok
const WithReferenceTag = () => <DsCommentCard
    onClick={fn()}
    onResolve={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    comment={createMockComment({ referenceTag: 'Resource allocation' })}
    overflow="hidden" />;
With Callbacks story ok
const WithCallbacks = () => <DsCommentCard
    onClick={fn()}
    onResolve={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    comment={createMockComment()}
    overflow="hidden" />;
Single Reply story ok
const SingleReply = () => <DsCommentCard
    onClick={fn()}
    onResolve={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    comment={createMockComment({
        messages: [
            {
                id: 'msg-1',
                author: { id: 'user-1', name: 'Karen J.' },
                content: 'Initial message',
                createdAt: new Date(Date.now() - 24 * 60 * 60 * 1000),
                isInitialMessage: true,
            },
            {
                id: 'msg-2',
                author: { id: 'user-2', name: 'John D.' },
                content: 'One reply',
                createdAt: new Date(Date.now() - 12 * 60 * 60 * 1000),
            },
        ],
    })}
    overflow="hidden" />;
Custom Formatter story ok
const CustomFormatter = () => <DsCommentCard
    onClick={fn()}
    onResolve={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    comment={createMockComment()}
    overflow="hidden"
    formatTimestamp={(date: Date) => {
        const options: Intl.DateTimeFormatOptions = {
            year: 'numeric',
            month: 'short',
            day: 'numeric',
            hour: '2-digit',
            minute: '2-digit',
        };
        return date.toLocaleDateString('en-US', options);
    }} />;

DsCommentIndicator

components-comments-commentindicator · ./src/components/ds-comment-indicator/ds-comment-indicator.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 7 prop types
Component: src/components/ds-comment-indicator/ds-comment-indicator.tsx::DsCommentIndicator
Props:
/**
 * Author display name used to derive fallback avatar initials when `avatarSrc`
 * is not provided
 */
avatarName?: string = ''

/**
 * Optional URL to the author's avatar image shown inside the pin
 */
avatarSrc?: string

/**
 * Additional CSS class name applied to the indicator root
 */
className?: string

/**
 * Called when the indicator is clicked (e.g., to open the comment bubble)
 */
onClick?: () => void

/**
 * Ref forwarded to the indicator root button element
 */
ref?: Ref<HTMLButtonElement>

/**
 * Inline styles applied to the indicator root
 */
style?: CSSProperties

/**
 * Visual state of the indicator:
 * - `placeholder`: dashed outline for "click here to add a comment" affordances
 * - `default`: filled pin showing an existing comment
 * - `action-required`: emphasized pin for comments flagged as requiring action
 * @default default
 */
type?: 'placeholder' | 'default' | 'action-required' = 'placeholder'
Imports
import { DsCommentBubble, DsCommentIndicator } from "@drivenets/design-system";
Placeholder story ok
Placeholder indicator shows a "+" icon for adding new comments. This appears when hovering over entities that can have comments.
const Placeholder = () => <DsCommentIndicator type="placeholder" onClick={fn()} />;
Default Indicator story ok
Default indicator shows the avatar of the comment initiator. Used when there are comments but no action is required.
const DefaultIndicator = () => <DsCommentIndicator type="default" avatarSrc="https://i.pravatar.cc/40?img=1" onClick={fn()} />;
Action Required story ok
Action required indicator shows the avatar with an orange/red background. Used when a comment requires user action or response.
const ActionRequired = () => <DsCommentIndicator
    type="action-required"
    avatarSrc="https://i.pravatar.cc/40?img=2"
    onClick={fn()} />;
Default story ok
const Default = () => (
    <div className={styles.container}>
        <div className={styles.header}>
            <div className={styles.headerCell}>Placeholder</div>
            <div className={styles.headerCell}>No action required</div>
            <div className={styles.headerCell}>Action Required</div>
        </div>
        <div className={styles.row}>
            <DsCommentIndicator type="placeholder" onClick={fn()} />
            <DsCommentIndicator type="default" avatarSrc="https://i.pravatar.cc/40?img=1" onClick={fn()} />
            <DsCommentIndicator
                type="action-required"
                avatarSrc="https://i.pravatar.cc/40?img=2"
                onClick={fn()}
            />
        </div>
    </div>
);
With Empty Bubble story ok
Interactive story showing a placeholder indicator that opens an empty comment bubble when clicked. Demonstrates the complete flow from empty bubble to thread with multiple messages. Click the "+" icon to add a new comment, then add replies to see the full thread.
const WithEmptyBubble = function WithEmptyBubbleStory() {
    const [isOpen, setIsOpen] = useState(false);
    const [value, setValue] = useState('');
    const [actionRequired, setActionRequired] = useState(false);
    const [comment, setComment] = useState<CommentData | undefined>(undefined);

    const handleSend = (content: string, isActionRequired: boolean) => {
        if (!comment) {
            const newComment: CommentData = {
                id: 'comment-1',
                numericId: 42,
                author: currentUser,
                createdAt: new Date(),
                isResolved: false,
                messages: [
                    {
                        id: 'msg-1',
                        author: currentUser,
                        content,
                        createdAt: new Date(),
                        isInitialMessage: true,
                    },
                ],
            };
            setComment(newComment);
            setActionRequired(isActionRequired);
        } else {
            const newMessage = {
                id: `msg-${String(Date.now())}`,
                author: currentUser,
                content,
                createdAt: new Date(),
            };

            setComment((prev) => {
                if (!prev) {
                    return prev;
                }
                return {
                    ...prev,
                    messages: [...prev.messages, newMessage],
                };
            });
        }
        setValue('');
    };

    const handleEditMessage = (messageId: string, newContent: string) => {
        if (!comment) {
            return;
        }
        setComment((prev) => {
            if (!prev) {
                return prev;
            }
            return {
                ...prev,
                messages: prev.messages.map((msg) =>
                    msg.id === messageId ? { ...msg, content: newContent } : msg,
                ),
            };
        });
    };

    const handleDeleteMessage = (messageId: string) => {
        if (!comment) {
            return;
        }
        setComment((prev) => {
            if (!prev) {
                return prev;
            }
            return {
                ...prev,
                messages: prev.messages.filter((msg) => msg.id !== messageId),
            };
        });
    };

    const handleClose = () => {
        setComment(undefined);
        setValue('');
        setActionRequired(false);
    };

    return (
        <div className={styles.interactiveContainer}>
            <p className={styles.instructions}>
                Click the <strong>+</strong> icon to open an empty comment bubble, then follow the complete flow:
                <br />
                1. Type a message → Typing mode appears
                <br />
                2. Send → Creates thread with your first comment
                <br />
                3. Add replies → Thread grows with multiple messages
            </p>

            <div className={styles.indicatorWrapper}>
                <DsCommentIndicator type="placeholder" onClick={() => setIsOpen(!isOpen)} />

                {isOpen && (
                    <div className={styles.bubbleWrapper}>
                        <DsCommentBubble
                            comment={comment}
                            currentUser={currentUser}
                            referenceTag="Resource allocation"
                            value={value}
                            onValueChange={setValue}
                            actionRequired={actionRequired}
                            onActionRequiredChange={setActionRequired}
                            onSend={handleSend}
                            onEditMessage={handleEditMessage}
                            onDeleteMessage={handleDeleteMessage}
                            onMessageMarkUnread={(id) => console.log('Mark unread:', id)}
                            onMessageResolved={(id) => console.log('Resolved:', id)}
                            onClose={comment ? handleClose : () => setIsOpen(false)}
                            onResolve={() => console.log('Resolve clicked')}
                            onToggleActionRequired={() => console.log('Toggle action required')}
                            onForward={() => console.log('Forward')}
                            onMarkUnread={() => console.log('Mark unread')}
                            onCopyLink={() => console.log('Copy link')}
                            onDelete={() => console.log('Delete')}
                        />
                    </div>
                )}
            </div>
        </div>
    );
};
With Existing Comments story ok
Interactive story showing an indicator with an avatar that opens a comment bubble with existing comments when clicked. The bubble displays a full comment thread.
const WithExistingComments = function WithExistingCommentsStory() {
    const [isOpen, setIsOpen] = useState(false);
    const [value, setValue] = useState('');
    const [actionRequired, setActionRequired] = useState(false);
    const [comment, setComment] = useState(createMockComment());

    const handleSend = (content: string) => {
        const newMessage = {
            id: `msg-${String(Date.now())}`,
            author: currentUser,
            content,
            createdAt: new Date(),
        };

        setComment((prev) => ({
            ...prev,
            messages: [...prev.messages, newMessage],
        }));
        setValue('');
    };

    const handleEditMessage = (messageId: string, newContent: string) => {
        setComment((prev) => ({
            ...prev,
            messages: prev.messages.map((msg) => (msg.id === messageId ? { ...msg, content: newContent } : msg)),
        }));
    };

    const handleDeleteMessage = (messageId: string) => {
        setComment((prev) => ({
            ...prev,
            messages: prev.messages.filter((msg) => msg.id !== messageId),
        }));
    };

    return (
        <div className={styles.interactiveContainer}>
            <p className={styles.instructions}>
                Click the <strong>avatar</strong> to view existing comments and add replies
            </p>

            <div className={styles.indicatorWrapper}>
                <DsCommentIndicator
                    type="default"
                    avatarSrc={comment.author.avatarSrc}
                    onClick={() => setIsOpen(!isOpen)}
                />

                {isOpen && (
                    <div className={styles.bubbleWrapper}>
                        <DsCommentBubble
                            comment={comment}
                            currentUser={currentUser}
                            referenceTag="Resource allocation"
                            value={value}
                            onValueChange={setValue}
                            actionRequired={actionRequired}
                            onActionRequiredChange={setActionRequired}
                            onSend={handleSend}
                            onEditMessage={handleEditMessage}
                            onDeleteMessage={handleDeleteMessage}
                            onMessageMarkUnread={(id) => console.log('Mark unread:', id)}
                            onMessageResolved={(id) => console.log('Resolved:', id)}
                            onClose={() => setIsOpen(false)}
                            onResolve={() => console.log('Resolve clicked')}
                            onToggleActionRequired={() => console.log('Toggle action required')}
                            onForward={() => console.log('Forward')}
                            onMarkUnread={() => console.log('Mark unread')}
                            onCopyLink={() => console.log('Copy link')}
                            onDelete={() => console.log('Delete')}
                        />
                    </div>
                )}
            </div>
        </div>
    );
};
With Action Required story ok
Interactive story showing an action-required indicator that opens a comment bubble with existing comments marked as requiring action.
const WithActionRequired = function WithActionRequiredStory() {
    const [isOpen, setIsOpen] = useState(false);
    const [value, setValue] = useState('');
    const [actionRequired, setActionRequired] = useState(true);
    const [comment, setComment] = useState(createMockComment());

    const handleSend = (content: string) => {
        const newMessage = {
            id: `msg-${String(Date.now())}`,
            author: currentUser,
            content,
            createdAt: new Date(),
        };

        setComment((prev) => ({
            ...prev,
            messages: [...prev.messages, newMessage],
        }));
        setValue('');
    };

    return (
        <div className={styles.interactiveContainer}>
            <p className={styles.instructions}>
                Click the <strong>action required avatar</strong> to view urgent comments
            </p>

            <div className={styles.indicatorWrapper}>
                <DsCommentIndicator
                    type="action-required"
                    avatarSrc={comment.author.avatarSrc}
                    onClick={() => setIsOpen(!isOpen)}
                />

                {isOpen && (
                    <div className={styles.bubbleWrapper}>
                        <DsCommentBubble
                            comment={comment}
                            currentUser={currentUser}
                            referenceTag="Resource allocation"
                            value={value}
                            onValueChange={setValue}
                            actionRequired={actionRequired}
                            onActionRequiredChange={setActionRequired}
                            onSend={handleSend}
                            onClose={() => setIsOpen(false)}
                            onResolve={() => console.log('Resolve clicked')}
                            onToggleActionRequired={() => console.log('Toggle action required')}
                            onForward={() => console.log('Forward')}
                            onMarkUnread={() => console.log('Mark unread')}
                            onCopyLink={() => console.log('Copy link')}
                            onDelete={() => console.log('Delete')}
                        />
                    </div>
                )}
            </div>
        </div>
    );
};

DsCommentsDrawer

components-comments-commentsdrawer · ./src/components/ds-comments-drawer/ds-comments-drawer.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 18 prop types
Component: src/components/ds-comments-drawer/ds-comments-drawer.tsx::DsCommentsDrawer
Props:
/**
 * Additional CSS class name applied to the drawer root
 */
className?: string

/**
 * List of comment threads to render in the drawer
 */
comments: CommentData[]

/**
 * Custom empty-state content shown when the comments list is empty after
 * applying filters and search. Defaults to a built-in placeholder when omitted.
 */
noCommentsMessage?: ReactNode

/**
 * Called when a comment card is clicked. Receives the full comment so consumers
 * can open the thread in a bubble or scroll to its anchor.
 */
onCommentClick?: (comment: CommentData) => void

/**
 * Called when a link to the comment is copied from its card overflow menu.
 * Receives the comment id.
 */
onCopyLink?: (commentId: string) => void

/**
 * Called when a comment is deleted from its card overflow menu. Receives the
 * comment id.
 */
onDelete?: (commentId: string) => void

/**
 * Called when a comment is forwarded from its card overflow menu. Receives the
 * comment id.
 */
onForward?: (commentId: string) => void

/**
 * Called when a comment is marked as unread from its card overflow menu. Receives
 * the comment id.
 */
onMarkUnread?: (commentId: string) => void

/**
 * Called when the drawer should open or close (e.g., backdrop click, close button,
 * escape key). Receives the next `open` state.
 */
onOpenChange: (open: boolean) => void

/**
 * Called when a comment is resolved from its card overflow menu. Receives the
 * comment id.
 */
onResolveComment?: (commentId: string) => void

/**
 * Called when the search query changes. Receives the new query string.
 */
onSearchChange?: (query: string) => void

/**
 * Called when the "show resolved" toggle changes. Receives the new boolean value.
 */
onShowResolvedChange?: (show: boolean) => void

/**
 * Called when a comment's action-required flag is toggled from its card overflow
 * menu. Receives the comment id.
 */
onToggleActionRequired?: (commentId: string) => void

/**
 * Whether the drawer is visible
 */
open: boolean

/**
 * Ref forwarded to the drawer root element
 */
ref?: Ref<HTMLElement>

/**
 * Current value of the search input (controlled)
 */
searchQuery?: string = ''

/**
 * Whether resolved comments are currently included in the visible list.
 * @default false
 */
showResolved?: boolean = false

/**
 * Inline styles applied to the drawer root
 */
style?: CSSProperties
Imports
import { DsButton, DsCommentsDrawer } from "@drivenets/design-system";
Default story ok
const Default = () => <DsCommentsDrawer
    onOpenChange={fn()}
    onShowResolvedChange={fn()}
    onSearchChange={fn()}
    onCommentClick={fn()}
    onResolveComment={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    open
    comments={createMockComments()} />;
With Search story ok
const WithSearch = () => <DsCommentsDrawer
    onOpenChange={fn()}
    onShowResolvedChange={fn()}
    onSearchChange={fn()}
    onCommentClick={fn()}
    onResolveComment={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    open
    comments={createMockComments()}
    searchQuery="Karen" />;
Empty story ok
const Empty = () => <DsCommentsDrawer
    onOpenChange={fn()}
    onShowResolvedChange={fn()}
    onSearchChange={fn()}
    onCommentClick={fn()}
    onResolveComment={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    open
    comments={[]} />;
Show Resolved Toggle story ok
const ShowResolvedToggle = () => <DsCommentsDrawer
    onOpenChange={fn()}
    onShowResolvedChange={fn()}
    onSearchChange={fn()}
    onCommentClick={fn()}
    onResolveComment={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    open
    comments={createMockComments()}
    showResolved={false} />;
Hide Resolved Toggle story ok
const HideResolvedToggle = () => <DsCommentsDrawer
    onOpenChange={fn()}
    onShowResolvedChange={fn()}
    onSearchChange={fn()}
    onCommentClick={fn()}
    onResolveComment={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    open
    comments={createMockComments()}
    showResolved />;
Comment Card Click story ok
const CommentCardClick = () => <DsCommentsDrawer
    onOpenChange={fn()}
    onShowResolvedChange={fn()}
    onSearchChange={fn()}
    onCommentClick={fn()}
    onResolveComment={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    open
    comments={createMockComments()} />;
Search Filtering story ok
const SearchFiltering = () => <DsCommentsDrawer
    onOpenChange={fn()}
    onShowResolvedChange={fn()}
    onSearchChange={fn()}
    onCommentClick={fn()}
    onResolveComment={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    open
    comments={createMockComments()}
    searchQuery="dark mode" />;
Search By Id story ok
const SearchById = () => <DsCommentsDrawer
    onOpenChange={fn()}
    onShowResolvedChange={fn()}
    onSearchChange={fn()}
    onCommentClick={fn()}
    onResolveComment={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    open
    comments={createMockComments()}
    searchQuery="#65" />;
Custom Empty Message story ok
const CustomEmptyMessage = () => <DsCommentsDrawer
    onOpenChange={fn()}
    onShowResolvedChange={fn()}
    onSearchChange={fn()}
    onCommentClick={fn()}
    onResolveComment={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    open
    comments={[]}
    noCommentsMessage="Nothing to see here!" />;
No Resolved Comments story ok
const NoResolvedComments = () => <DsCommentsDrawer
    onOpenChange={fn()}
    onShowResolvedChange={fn()}
    onSearchChange={fn()}
    onCommentClick={fn()}
    onResolveComment={fn()}
    onToggleActionRequired={fn()}
    onForward={fn()}
    onMarkUnread={fn()}
    onCopyLink={fn()}
    onDelete={fn()}
    open
    comments={createMockComments().filter((c) => !c.isResolved)} />;
Interactive story ok
const Interactive = function InteractiveStory() {
    const [open, setOpen] = useState(false);
    const [searchQuery, setSearchQuery] = useState('');
    const [showResolved, setShowResolved] = useState(false);
    const [comments, setComments] = useState(createMockComments());

    const handleResolve = (commentId: string) => {
        setComments((prev) => prev.map((c) => (c.id === commentId ? { ...c, isResolved: true } : c)));
    };

    return (
        <div className={styles.storyWrapper}>
            <DsButton onClick={() => setOpen(true)}>Open Comments Drawer</DsButton>

            <DsCommentsDrawer
                open={open}
                onOpenChange={setOpen}
                comments={comments}
                searchQuery={searchQuery}
                onSearchChange={setSearchQuery}
                showResolved={showResolved}
                onShowResolvedChange={setShowResolved}
                onCommentClick={(comment) => console.log('Comment clicked:', comment.id)}
                onResolveComment={handleResolve}
                onToggleActionRequired={(commentId) => console.log('Toggle action required:', commentId)}
                onForward={(commentId) => console.log('Forward:', commentId)}
                onMarkUnread={(commentId) => console.log('Mark unread:', commentId)}
                onCopyLink={(commentId) => console.log('Copy link:', commentId)}
                onDelete={(commentId) => console.log('Delete:', commentId)}
            />
        </div>
    );
};
With Filters (Interactive) story ok
const WithFilters = function WithFiltersStory() {
    const [open, setOpen] = useState(true);
    const [searchQuery, setSearchQuery] = useState('');
    const [showResolved, setShowResolved] = useState(false);
    const [comments] = useState(createMockComments());

    return (
        <div className={styles.storyWrapper}>
            <div className={styles.filterDemo}>
                <h3 className={styles.filterDemoTitle}>Filter Demonstration</h3>
                <p className={styles.filterDemoText}>
                    The drawer is open by default. Click the filter icon to explore filtering options.
                </p>
                <p className={styles.filterDemoText}>
                    <strong>Try filtering by:</strong>
                </p>
                <ul className={styles.filterDemoList}>
                    <li>Author: &quot;Karen J.&quot; to see 2 comments</li>
                    <li>Label: &quot;Bug&quot; to see 2 comments</li>
                    <li>Status: &quot;Action required&quot; to see 1 comment</li>
                    <li>Multiple filters at once (e.g., Author + Label)</li>
                </ul>
            </div>

            <DsCommentsDrawer
                open={open}
                onOpenChange={setOpen}
                comments={comments}
                searchQuery={searchQuery}
                onSearchChange={setSearchQuery}
                showResolved={showResolved}
                onShowResolvedChange={setShowResolved}
                onCommentClick={(comment) => console.log('Comment clicked:', comment.id)}
                onResolveComment={(commentId) => console.log('Resolve:', commentId)}
                onToggleActionRequired={(commentId) => console.log('Toggle action required:', commentId)}
                onForward={(commentId) => console.log('Forward:', commentId)}
                onMarkUnread={(commentId) => console.log('Mark unread:', commentId)}
                onCopyLink={(commentId) => console.log('Copy link:', commentId)}
                onDelete={(commentId) => console.log('Delete:', commentId)}
            />
        </div>
    );
};

DsConfirmation

components-confirmation-deprecated · ./src/components/ds-confirmation/ds-confirmation.stories.tsx
deprecated: This component is deprecated. Use DsModal instead.see: {@link ../ds-modal/ds-modal.stories} for examples of the replacement component.
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 5 prop types
Component: src/components/ds-confirmation/ds-confirmation.tsx::DsConfirmation
Props:
/**
 * Modal body content
 */
children: ReactNode

/**
 * Additional CSS class name
 */
className?: string

/**
 * Callback when modal open state changes
 * @param open - whether the modal is open
 */
onOpenChange: (open: boolean) => void

/**
 * Whether the modal is open
 */
open: boolean

/**
 * Additional CSS styles
 */
style?: CSSProperties
Imports
import { DsButton, DsConfirmation } from "@drivenets/design-system";
Default story ok
const Default = function Render() {
    const [open, setOpen] = useState(false);
    return (
        <>
            <DsButton onClick={() => setOpen(true)}>Open Confirmation</DsButton>
            <DsConfirmation open={open} onOpenChange={setOpen}>
                <DsConfirmation.Header>
                    <DsConfirmation.Title>Confirm Action</DsConfirmation.Title>
                    <DsConfirmation.CloseTrigger />
                </DsConfirmation.Header>
                <DsConfirmation.Body>Are you sure you want to proceed with this action?</DsConfirmation.Body>
                <DsConfirmation.Footer>
                    <DsConfirmation.Actions>
                        <DsButton
                            design="v1.2"
                            buttonType="secondary"
                            size="large"
                            onClick={() => {
                                console.log('Reject clicked');
                                setOpen(false);
                            }}
                        >
                            No
                        </DsButton>
                        <DsButton
                            design="v1.2"
                            variant="filled"
                            size="large"
                            onClick={() => {
                                console.log('Accept clicked');
                                setOpen(false);
                            }}
                        >
                            Yes
                        </DsButton>
                    </DsConfirmation.Actions>
                </DsConfirmation.Footer>
            </DsConfirmation>
        </>
    );
};
With Cancel story ok
const WithCancel = function Render() {
    const [open, setOpen] = useState(false);
    return (
        <>
            <DsButton onClick={() => setOpen(true)}>Open Confirmation</DsButton>
            <DsConfirmation open={open} onOpenChange={setOpen}>
                <DsConfirmation.Header>
                    <DsConfirmation.Title>Save Changes</DsConfirmation.Title>
                    <DsConfirmation.CloseTrigger />
                </DsConfirmation.Header>
                <DsConfirmation.Body>Do you want to save your changes before closing?</DsConfirmation.Body>
                <DsConfirmation.Footer>
                    <DsButton
                        design="v1.2"
                        buttonType="tertiary"
                        size="large"
                        onClick={() => {
                            console.log('Cancel clicked');
                            setOpen(false);
                        }}
                    >
                        Cancel
                    </DsButton>
                    <DsConfirmation.Actions>
                        <DsButton
                            design="v1.2"
                            buttonType="secondary"
                            size="large"
                            onClick={() => {
                                console.log('Discard clicked');
                                setOpen(false);
                            }}
                        >
                            Discard
                        </DsButton>
                        <DsButton
                            design="v1.2"
                            variant="filled"
                            size="large"
                            onClick={() => {
                                console.log('Save clicked');
                                setOpen(false);
                            }}
                        >
                            Save
                        </DsButton>
                    </DsConfirmation.Actions>
                </DsConfirmation.Footer>
            </DsConfirmation>
        </>
    );
};
Danger story ok
const Danger = function Render() {
    const [open, setOpen] = useState(false);
    return (
        <>
            <DsButton onClick={() => setOpen(true)}>Open Confirmation</DsButton>
            <DsConfirmation open={open} onOpenChange={setOpen}>
                <DsConfirmation.Header>
                    <DsConfirmation.Title>Delete Item</DsConfirmation.Title>
                    <DsConfirmation.CloseTrigger />
                </DsConfirmation.Header>
                <DsConfirmation.Body>
                    Are you sure you want to delete this item? This action cannot be undone.
                </DsConfirmation.Body>
                <DsConfirmation.Footer>
                    <DsConfirmation.Actions>
                        <DsButton
                            design="v1.2"
                            buttonType="secondary"
                            size="large"
                            onClick={() => {
                                console.log('Cancel clicked');
                                setOpen(false);
                            }}
                        >
                            Cancel
                        </DsButton>
                        <DsButton
                            design="v1.2"
                            variant="danger"
                            size="large"
                            onClick={() => {
                                console.log('Delete clicked');
                                setOpen(false);
                            }}
                        >
                            Delete
                        </DsButton>
                    </DsConfirmation.Actions>
                </DsConfirmation.Footer>
            </DsConfirmation>
        </>
    );
};
Custom Body story ok
const CustomBody = function Render() {
    const [open, setOpen] = useState(false);
    const [selectedOption, setSelectedOption] = useState('option1');

    return (
        <>
            <DsButton onClick={() => setOpen(true)}>Open Custom Confirmation</DsButton>
            <DsConfirmation style={{ maxBlockSize: 'none' }} open={open} onOpenChange={setOpen}>
                <DsConfirmation.Header>
                    <DsConfirmation.Title>Advanced Configuration</DsConfirmation.Title>
                    <DsConfirmation.CloseTrigger />
                </DsConfirmation.Header>
                <DsConfirmation.Body>
                    <div className={styles.customBodyContainer}>
                        <p className={styles.customBodyText}>Please select your preferred configuration option:</p>
                        <div className={styles.radioGroup}>
                            <label className={styles.radioLabel}>
                                <input
                                    type="radio"
                                    name="config-option"
                                    value="option1"
                                    checked={selectedOption === 'option1'}
                                    onChange={(e) => setSelectedOption(e.target.value)}
                                />
                                <span>Standard configuration (recommended)</span>
                            </label>
                            <label className={styles.radioLabel}>
                                <input
                                    type="radio"
                                    name="config-option"
                                    value="option2"
                                    checked={selectedOption === 'option2'}
                                    onChange={(e) => setSelectedOption(e.target.value)}
                                />
                                <span>Advanced configuration with custom settings</span>
                            </label>
                            <label className={styles.radioLabel}>
                                <input
                                    type="radio"
                                    name="config-option"
                                    value="option3"
                                    checked={selectedOption === 'option3'}
                                    onChange={(e) => setSelectedOption(e.target.value)}
                                />
                                <span>Custom configuration (manual setup)</span>
                            </label>
                        </div>
                        <div className={styles.infoNote}>
                            <strong>Note:</strong> This action will apply the selected configuration to your current
                            project. You can change this setting later in the project settings.
                        </div>
                    </div>
                </DsConfirmation.Body>
                <DsConfirmation.Footer>
                    <DsConfirmation.Actions>
                        <DsButton
                            design="v1.2"
                            buttonType="secondary"
                            size="large"
                            onClick={() => {
                                console.log('Cancel clicked');
                                setOpen(false);
                            }}
                        >
                            Cancel
                        </DsButton>
                        <DsButton
                            design="v1.2"
                            variant="filled"
                            size="large"
                            onClick={() => {
                                console.log('Apply configuration:', selectedOption);
                                setOpen(false);
                            }}
                        >
                            Apply Configuration
                        </DsButton>
                    </DsConfirmation.Actions>
                </DsConfirmation.Footer>
            </DsConfirmation>
        </>
    );
};

DsDateInput

components-dateinput-deprecated · ./src/components/ds-date-input/ds-date-input.stories.tsx
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.
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 13 prop types
Component: src/components/ds-date-input/ds-date-input.tsx::default
Props:
/**
 * Custom class name for the root container
 */
className?: string

/**
 * Whether the input is disabled and cannot be interacted with
 * @default false
 */
disabled?: boolean

/**
 * Whether to disable the portal for the calendar content
 * @default false
 */
disablePortal?: boolean = false

/**
 * Whether to hide the clear button
 * @default false
 */
hideClearButton?: boolean = false

/**
 * Unique identifier for the input field
 */
id?: string

/**
 * Maximum allowed date as ISO string (YYYY-MM-DD)
 * @example '2024-12-31'
 */
max?: string

/**
 * Minimum allowed date as ISO string (YYYY-MM-DD)
 * @example '2024-01-01'
 */
min?: string

/**
 * HTML name attribute for the input field, used in form submissions
 */
name?: string

/**
 * Callback when the open state changes
 * @param open - Whether the calendar is open
 */
onOpenChange?: (open: boolean) => void

/**
 * Whether the calendar popover is open (controlled). Pair with `onOpenChange`.
 */
open?: boolean

/**
 * Placeholder text for the input
 * @default 'MM/DD/YYYY' for single date, 'MM/DD/YYYY - MM/DD/YYYY' for range
 */
placeholder?: string

range?: any = false

/**
 * Whether the input is read-only. The field is focusable but the value cannot
 * be changed.
 * @default false
 */
readOnly?: boolean
Imports
import { DsDateInput } from "@drivenets/design-system";
Default story ok
Basic single date input with default configuration
const Default = function Render() {
    const [value, setValue] = useState<string>();

    return <DsDateInput className={styles.containerSingle} value={value} onValueChange={setValue} />;
};
Range Mode story ok
Date range input - unified input field for start and end dates
const RangeMode = function Render() {
    const [value, setValue] = useState<[string, string]>();

    return <DsDateInput className={styles.containerRange} value={value} onValueChange={setValue} range />;
};
With Default Value story ok
Single date input with pre-filled value
const WithDefaultValue = () => <DsDateInput className={styles.containerSingle} defaultValue="2024-12-25" />;
Range With Default Value story ok
Range input with pre-filled values
const RangeWithDefaultValue = () => <DsDateInput
    className={styles.containerRange}
    defaultValue={['2024-12-01', '2024-12-31']}
    range />;
With Min Max story ok
Date input with min and max constraints
const WithMinMax = function Render() {
    const [value, setValue] = useState<string>();
    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 (
        <div className={styles.containerSingle}>
            <DsDateInput
                value={value}
                onValueChange={setValue}
                placeholder="MM/DD/YYYY"
                min={minDate}
                max={maxDate}
            />
            <p className={styles.helperText}>Allowed: Current month to next 3 months only</p>
        </div>
    );
};
Disabled story ok
Disabled date input
const Disabled = () => <DsDateInput className={styles.containerSingle} value="2024-12-25" disabled />;
Read Only story ok
Read-only date input
const ReadOnly = () => <DsDateInput className={styles.containerSingle} value="2024-12-25" readOnly />;
Hide Clear Button story ok
Date input with hidden clear button
const HideClearButton = function Render() {
    const [value, setValue] = useState<string>();

    return (
        <DsDateInput
            className={styles.containerSingle}
            value={value}
            onValueChange={setValue}
            hideClearButton
        />
    );
};
Controlled Open story ok
Controlled open state
const ControlledOpen = function Render() {
    const [value, setValue] = useState<string>();
    const [open, setOpen] = useState(false);

    return (
        <div className={styles.containerSingle}>
            <div className={styles.buttonContainer}>
                <button onClick={() => setOpen(!open)}>{open ? 'Close' : 'Open'} Calendar</button>
            </div>
            <DsDateInput value={value} onValueChange={setValue} open={open} onOpenChange={setOpen} />
        </div>
    );
};
Uncontrolled story ok
Uncontrolled with defaultValue
const Uncontrolled = function Render() {
    return (
        <div className={styles.containerSingle}>
            <DsDateInput defaultValue="2024-12-25" />
        </div>
    );
};
Manual Typing story ok
Manual typing demonstration
const ManualTyping = function Render() {
    const [value, setValue] = useState<[string, string]>();

    return (
        <div className={styles.containerRange}>
            <DsDateInput value={value} onValueChange={setValue} range />
            <div className={styles.infoContainer}>
                <p>Try typing: &#34;12/01/2024 - 12/31/2024&#34;</p>
                <p>Validation happens on blur</p>
                <p>Invalid format resets to empty</p>
                <p>Current value: {JSON.stringify(value)}</p>
            </div>
        </div>
    );
};

DsDatePicker

components-datepicker · ./src/components/ds-date-picker/ds-date-picker.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 15 prop types
Component: src/components/ds-date-picker/ds-date-picker.tsx::default
Props:
className?: string

/**
 * Whether to close the calendar when a date is selected. This is ignored when withTime is true.
 * @default true
 */
closeOnSelect?: boolean = true

/**
 * Default value for uncontrolled mode
 */
defaultValue?: Date

/**
 * Whether to disable the portal for the calendar content
 * @default false
 */
disablePortal?: boolean = false

/**
 * Whether to hide the clear button
 * @default true
 */
hideClearButton?: boolean = false

/**
 * Localizable aria-labels and text
 */
locale?: DsDatePickerLocale

/**
 * Maximum allowed datetime as Date object
 */
max?: Date

/**
 * Minimum allowed datetime as Date object
 */
min?: Date

/**
 * Callback when the input is blurred
 */
onBlur?: () => void

/**
 * Callback when the value changes
 * @param value - Date object or null when cleared
 */
onChange?: (value: Date | null) => void

/**
 * Callback when the calendar open state changes
 */
onOpenChange?: (open: boolean) => void

/**
 * Placeholder text for the input
 * @default 'mm/dd/yyyy'
 */
placeholder?: string

/**
 * Props forwarded to sub-component slots
 */
slotProps?: DsDatePickerSlotProps

/**
 * Controlled value as Date object.
 * `null` means the date is empty.
 * `undefined` means value is not provided, so component will be uncontrolled.
 */
value?: Date | null

/**
 * Whether to show the time picker alongside the date picker
 * @default false
 */
withTime?: boolean = false
Imports
import { DsDatePicker } from "@drivenets/design-system";
Default story ok
const Default = () => {
    const [value, setValue] = useState<Date | null>(null);

    return <DsDatePicker className={styles.container} value={value} onChange={setValue} />;
};
With Time story ok
const WithTime = () => {
    const [value, setValue] = useState<Date | null>(null);

    return <DsDatePicker withTime className={styles.container} value={value} onChange={setValue} />;
};
With Default Value story ok
const WithDefaultValue = () => <DsDatePicker
    className={styles.container}
    withTime
    defaultValue={new Date('2024-12-25T14:30:00')} />;
Controlled story ok
const Controlled = () => {
    const [value, setValue] = useState<Date | null>(new Date('2026-01-15T09:45:00'));

    return (
        <div className={styles.container}>
            <DsDatePicker withTime value={value} onChange={setValue} />
            <p className={styles.infoContainer}>Value: {value?.toLocaleString() ?? 'undefined'}</p>
        </div>
    );
};
Disabled story ok
const Disabled = () => <DsDatePicker
    className={styles.container}
    value={new Date('2024-12-25T14:30:00')}
    disabled />;
Read Only story ok
const ReadOnly = () => <DsDatePicker
    className={styles.container}
    value={new Date('2024-12-25T14:30:00')}
    readOnly />;
With Min Max story ok
const WithMinMax = () => {
    const [value, setValue] = useState<Date | null>(null);

    return (
        <div className={styles.container}>
            <DsDatePicker
                withTime
                min={(() => {
                    const date = new Date();
                    return new Date(date.getFullYear(), date.getMonth(), 1, 0, 30);
                })()}
                max={(() => {
                    const date = new Date();
                    return new Date(date.getFullYear(), date.getMonth() + 2, 0, 23, 20); // last day of next month
                })()}
                value={value}
                onChange={setValue} />
            <p className={styles.helperText}>Allowed: {args.min?.toLocaleString()}- {args.max?.toLocaleString()}
            </p>
        </div>
    );
};

DsDateRangePicker

components-daterangepicker · ./src/components/ds-date-range-picker/ds-date-range-picker.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 15 prop types
Component: src/components/ds-date-range-picker/ds-date-range-picker.tsx::default
Props:
/**
 * Additional CSS class name applied to the range picker wrapper.
 */
className?: string

/**
 * Whether to close the calendar when a date is selected. This is ignored when withTime is true.
 * @default true
 */
closeOnSelect?: boolean = true

/**
 * Default value for uncontrolled mode
 */
defaultValue?: [Date | null, Date | null] = [null, null]

/**
 * Whether both pickers are disabled.
 * @default false
 */
disabled?: boolean

/**
 * Whether to disable the portal for both calendar popups
 * @default false
 */
disablePortal?: boolean

/**
 * Whether to hide the "Clear all" button
 * @default false
 */
hideClearAll?: boolean = false

/**
 * Localizable labels and text
 */
locale?: DsDateRangePickerLocale

/**
 * Maximum allowed datetime for both pickers
 */
max?: Date

/**
 * Minimum allowed datetime for both pickers
 */
min?: Date

/**
 * Callback when either date changes
 */
onChange?: (value: DateRangeValue) => void

/**
 * Layout orientation of the two pickers
 * @default 'horizontal'
 */
orientation?: (typeof dateRangePickerOrientations)[number] = 'horizontal'

/**
 * Whether both pickers are read-only. Inputs are focusable but values cannot
 * be changed.
 * @default false
 */
readOnly?: boolean

/**
 * Props forwarded to sub-component slots
 */
slotProps?: DsDateRangePickerSlotProps

/**
 * Controlled value as a tuple of [startDate, endDate].
 * `null` for either element means that date is empty.
 * `undefined` means the component is uncontrolled.
 */
value?: [Date | null, Date | null]

/**
 * Whether to show date pickers with time
 * @default false
 */
withTime?: boolean = false
Imports
import { DsDatePicker, DsDateRangePicker, DsSegmentGroup } from "@drivenets/design-system";
Default story ok
const Default = () => {
    const [value, setValue] = useState<DateRangeValue>([null, null]);

    return <DsDateRangePicker className={styles.container} value={value} onChange={setValue} />;
};
With Time story ok
const WithTime = () => {
    const [value, setValue] = useState<DateRangeValue>([null, null]);

    return <DsDateRangePicker withTime className={styles.container} value={value} onChange={setValue} />;
};
Vertical story ok
const Vertical = () => {
    const [value, setValue] = useState<DateRangeValue>([null, null]);

    return (
        <DsDateRangePicker
            orientation="vertical"
            className={styles.verticalContainer}
            value={value}
            onChange={setValue} />
    );
};
With Min Max story ok
const WithMinMax = () => {
    const [value, setValue] = useState<DateRangeValue>([null, null]);

    return (
        <div>
            <DsDateRangePicker
                withTime
                min={(() => {
                    const date = new Date();
                    return new Date(date.getFullYear(), date.getMonth(), 1, 0, 30);
                })()}
                max={(() => {
                    const date = new Date();
                    return new Date(date.getFullYear(), date.getMonth() + 2, 0, 23, 20); // last day of next month
                })()}
                className={styles.container}
                value={value}
                onChange={setValue} />
            <p className={styles.helperText}>Allowed: {args.min?.toLocaleString()}- {args.max?.toLocaleString()}
            </p>
        </div>
    );
};
Disabled story ok
const Disabled = () => <DsDateRangePicker
    className={styles.container}
    value={[new Date('2026-01-10T00:00:00'), new Date('2026-01-20T00:00:00')]}
    disabled />;
Hidden Clear All story ok
const HiddenClearAll = () => {
    const [value, setValue] = useState<DateRangeValue>([
        new Date('2026-01-10T00:00:00'),
        new Date('2026-01-20T00:00:00'),
    ]);

    return (
        <DsDateRangePicker
            hideClearAll
            className={styles.container}
            value={value}
            onChange={setValue} />
    );
};
Date Or Range story ok
const DateOrRange = function Render(args) {
    const [mode, setMode] = useState('date');
    const [dateValue, setDateValue] = useState<Date | null>(null);
    const [rangeValue, setRangeValue] = useState<DateRangeValue>([null, null]);

    return (
        <div className={styles.dateOrRangeContainer}>
            <DsSegmentGroup.Root value={mode} onValueChange={(v) => setMode(v ?? 'date')} size="default">
                <DsSegmentGroup.Item value="date" label="Date" />
                <DsSegmentGroup.Item value="range" label="Range" />
            </DsSegmentGroup.Root>

            {mode === 'date' ? (
                <DsDatePicker
                    value={dateValue}
                    onChange={(v) => {
                        setDateValue(v);
                        args.onChange?.([v, null]);
                    }}
                    min={args.min}
                    max={args.max}
                    withTime={args.withTime}
                    disabled={args.disabled}
                    readOnly={args.readOnly}
                />
            ) : (
                <DsDateRangePicker
                    {...args}
                    value={rangeValue}
                    onChange={(v) => {
                        setRangeValue(v);
                        args.onChange?.(v);
                    }}
                />
            )}
        </div>
    );
};

DsDialog

components-dialog · ./src/components/ds-dialog/ds-dialog.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 11 prop types
Component: src/components/ds-dialog/ds-dialog.tsx::default
Props:
/**
 * Ref to the element the dialog should be anchored to (for relative placement)
 */
anchorRef?: React.RefObject<HTMLElement>

/**
 * The content to render inside the dialog.
 */
children: React.ReactNode

/**
 * Additional CSS class names for the dialog container.
 */
className?: string

/**
 * 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;
}

/**
 * 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

/**
 * If true, the dialog title will be visually hidden but accessible to screen readers.
 */
hideTitle?: boolean

/**
 * If true, show modal overlay and center dialog (default: true)
 */
modal?: boolean = true

/**
 * Callback fired when the open state should change.
 */
onOpenChange?: (open: boolean) => void

/**
 * Controls whether the dialog is open.
 */
open: boolean

/**
 * The accessible title for the dialog. Required for accessibility.
 */
title: string
Imports
import { DsButton, DsDialog, DsIcon } from "@drivenets/design-system";
Centered story ok
const Centered = () => {
    const [open, setOpen] = React.useState(false);

    return (
        <>
            <DsButton onClick={() => setOpen(true)}>Open Dialog</DsButton>
            <DsDialog
                open={open}
                onOpenChange={setOpen}
                title="Centered Dialog"
                description="This dialog appears in the center of the screen">
                <div className={styles.dialogContent}>This is a centered dialog example</div>
            </DsDialog>
        </>
    );
};
Custom Position story ok
const CustomPosition = () => {
    const [open, setOpen] = React.useState(false);

    return (
        <>
            <DsIcon
                className={styles.menuIcon}
                icon="menu"
                size="large"
                onClick={() => setOpen(true)} />
            <DsDialog
                open={open}
                onOpenChange={setOpen}
                title="Custom Position Dialog"
                description="This dialog appears relative to the menu icon"
                customPosition={{ top: 60, left: 20 }}>
                <div className={styles.dialogContent}>This is a custom positioned dialog example</div>
            </DsDialog>
        </>
    );
};

DsDivider

components-divider · ./src/components/ds-divider/ds-divider.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 4 prop types
Component: src/components/ds-divider/ds-divider.tsx::default
Props:
/**
 * Additional CSS classes.
 */
className?: string

/**
 * Underlying element/component to render.
 * Examples: "hr", "div", "span", Link, etc.
 */
component?: React.ElementType

/**
 * Controls orientation of the divider. Horizontal by default.
 */
orientation?: 'horizontal' | 'vertical' = 'horizontal'

/**
 * Additional CSS styles.
 */
style?: React.CSSProperties
Imports
import { DsDivider } from "@drivenets/design-system";
Default story ok
const Default = (args) => (
    <div
        className={classNames(styles.storyContainer, {
            [styles.storyContainerVertical]: args.orientation === 'vertical',
        })}
    >
        {args.orientation === 'vertical' ? (
            <>
                <div className={styles.storyContentSide}>Left content</div>
                <DsDivider {...args} />
                <div className={styles.storyContentSide}>Right content</div>
            </>
        ) : (
            <div className={styles.storyContent}>
                <div className={styles.storyText}>Top content</div>
                <DsDivider {...args} />
                <div className={styles.storyText}>Bottom content</div>
            </div>
        )}
    </div>
);
Showcase story ok
const Showcase = () => (
    <div className={styles.showcaseContainer}>
        <table className={styles.showcaseTable}>
            <thead>
                <tr>
                    <th className={styles.showcaseHeader}>Variant</th>
                    <th className={styles.showcaseHeader}>Preview</th>
                </tr>
            </thead>

            <tbody>
                <tr>
                    <td className={styles.showcaseCell}>
                        <span className={styles.showcaseCellBold}>Horizontal</span>
                        <span className={styles.showcaseCellInline}>default</span>
                    </td>
                    <td className={styles.showcaseCell}>
                        <div className={styles.horizontalDividerWrapper}>
                            <DsDivider />
                        </div>
                    </td>
                </tr>

                <tr>
                    <td className={styles.showcaseCell}>
                        <span className={styles.showcaseCellBold}>Vertical</span>
                        <span className={styles.showcaseCellInline}>default</span>
                    </td>
                    <td className={styles.showcaseCell}>
                        <div className={styles.verticalDividerContainer}>
                            <div className={styles.verticalDividerContent}>Left</div>
                            <DsDivider orientation="vertical" />
                            <div className={styles.verticalDividerContent}>Right</div>
                        </div>
                    </td>
                </tr>

                <tr>
                    <td className={styles.showcaseCell}>
                        <span className={styles.showcaseCellBold}>Custom</span>
                        <span className={styles.showcaseCellInline}>component=&quot;span&quot;</span>
                    </td>
                    <td className={styles.showcaseCell}>
                        <div className={styles.horizontalDividerWrapper}>
                            <DsDivider component="span" />
                        </div>
                    </td>
                </tr>
            </tbody>
        </table>
    </div>
);
Horizontal story ok
const Horizontal = () => <div className={styles.horizontalContainer}>
    <div className={styles.storyText}>Above</div>
    <DsDivider orientation="horizontal" />
    <div className={styles.storyText}>Below</div>
</div>;
Vertical story ok
const Vertical = () => <div
    className={classNames(styles.storyContainer, styles.storyContainerVertical)}>
    <div className={styles.storyContentSide}>Left</div>
    <DsDivider orientation="vertical" />
    <div className={styles.storyContentSide}>Right</div>
</div>;
With Custom Component story ok
const WithCustomComponent = () => <div className={styles.horizontalContainer}>
    <div className={styles.storyText}>Above</div>
    <DsDivider orientation="horizontal" component="span" />
    <div className={styles.storyText}>Below</div>
</div>;

DsDrawer

components-drawer · ./src/components/ds-drawer/ds-drawer.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 11 prop types
Component: src/components/ds-drawer/ds-drawer.tsx::default
Props:
/**
 * Whether to show backdrop overlay
 * @default false
 */
backdrop?: boolean = false

/**
 * Children components
 */
children: ReactNode

/**
 * Additional CSS class names
 */
className?: string

closeOnEscape?: any = true

closeOnInteractOutside?: any = false

/**
 * Number of grid columns the drawer should span (1-12).
 * Accepts a responsive value, e.g. `{ lg: 4, md: 6 }`.
 * @default 4
 */
columns?: T | Responsive<T> = 4

/**
 * Callback when the drawer open state changes
 */
onOpenChange: (open: boolean) => void

/**
 * Whether the drawer is open
 */
open: boolean

/**
 * Whether to append the drawer to the body instead of rendering in place
 * @default false
 */
portal?: boolean = false

/**
 * Position of the drawer
 * @default 'end'
 */
position?: 'start' | 'end' = 'end'

/**
 * Additional CSS styles
 */
style?: CSSProperties
Imports
import { DsButton, DsDrawer, DsIcon, DsSystemStatus, DsTextInput, DsTypography } from "@drivenets/design-system";
Default story ok
const Default = () => {
    const [isOpen, setIsOpen] = useState(false);

    return (
        <div className={styles.storyWrapper}>
            <DsButton onClick={() => setIsOpen(true)}>Open Drawer</DsButton>
            <DsDrawer open={isOpen} onOpenChange={setIsOpen}>
                (<>
                    <DsDrawer.Header>
                        <DsDrawer.Title>
                            Default Drawer <DsSystemStatus status="healthy" label="Active" />
                        </DsDrawer.Title>
                        <div className={styles.headerActions}>
                            <button className={styles.expand} aria-label="Expand">
                                <DsIcon icon="open_in_full" size="tiny" />
                            </button>
                            <div className={styles.divider} />
                            <DsDrawer.CloseTrigger />
                        </div>
                        <DsTypography className={styles.description} variant="body-xs-reg">
                            This is a description caption under a title.
                        </DsTypography>
                    </DsDrawer.Header>
                    <DsDrawer.Toolbar>
                        <DsTextInput
                            placeholder="Search..."
                            style={{ flex: 1 }}
                            slots={{ startAdornment: <DsIcon icon="search" size="tiny" /> }}
                        />
                        <DsIcon icon="filter_list" size="tiny" />
                    </DsDrawer.Toolbar>
                    <DsDrawer.Body className={styles.body}>
                        <div className={styles.section}>
                            <DsTypography className={styles.sectionHeader} variant="body-md-semi-bold">
                                Drawer content header
                            </DsTypography>
                            <DsTypography variant="heading2" className={styles.sectionContent}>
                                Out of scope section
                            </DsTypography>
                        </div>
                        <div className={styles.section}>
                            <DsTypography className={styles.sectionHeader} variant="body-md-semi-bold">
                                Drawer content header
                            </DsTypography>
                            <DsTypography variant="heading2" className={styles.sectionContent}>
                                Out of scope section
                            </DsTypography>
                            <DsTypography variant="heading2" className={styles.sectionContent}>
                                Out of scope section
                            </DsTypography>
                        </div>
                    </DsDrawer.Body>
                    <DsDrawer.Footer>
                        <DsDrawer.Actions>
                            <DsButton design="v1.2" buttonType="tertiary" size="large">
                                Cancel
                            </DsButton>
                            <DsButton design="v1.2" size="large">
                                Save
                            </DsButton>
                        </DsDrawer.Actions>
                    </DsDrawer.Footer>
                </>)
            </DsDrawer>
        </div>
    );
};
With Tabs story ok
const WithTabs = () => {
    const [isOpen, setIsOpen] = useState(false);

    return (
        <div className={styles.storyWrapper}>
            <DsButton onClick={() => setIsOpen(true)}>Open Drawer</DsButton>
            <DsDrawer columns={8} open={isOpen} onOpenChange={setIsOpen}>
                (<>
                    <DsDrawer.Header>
                        <DsDrawer.Title>
                            Drawer with Tabs <DsSystemStatus status="healthy" label="Active" />
                        </DsDrawer.Title>
                        <div className={styles.headerActions}>
                            <button className={styles.expand} aria-label="Expand">
                                <DsIcon icon="open_in_full" size="tiny" />
                            </button>
                            <div className={styles.divider} />
                            <DsDrawer.CloseTrigger />
                        </div>
                        <DsTypography className={styles.description} variant="body-xs-reg">
                            This is a description caption under a title.
                        </DsTypography>
                    </DsDrawer.Header>
                    <DsDrawer.Body className={styles.body}>
                        <div style={{ flex: 0 }} className={styles.section}>
                            <Tabs />
                        </div>
                        <div className={styles.section}>
                            <DsTypography className={styles.sectionHeader} variant="body-md-semi-bold">
                                Drawer content header
                            </DsTypography>
                            <DsTypography variant="heading2" className={styles.sectionContent}>
                                Out of scope section
                            </DsTypography>
                        </div>
                    </DsDrawer.Body>
                    <DsDrawer.Footer>
                        <DsDrawer.Actions>
                            <DsButton design="v1.2" buttonType="tertiary" size="large">
                                Cancel
                            </DsButton>
                            <DsButton design="v1.2" size="large">
                                Save
                            </DsButton>
                        </DsDrawer.Actions>
                    </DsDrawer.Footer>
                </>)
            </DsDrawer>
        </div>
    );
};
With Backdrop And Scroll story ok
const WithBackdropAndScroll = () => {
    const [isOpen, setIsOpen] = useState(false);

    return (
        <div className={styles.storyWrapper}>
            <DsButton onClick={() => setIsOpen(true)}>Open Drawer</DsButton>
            <DsDrawer backdrop open={isOpen} onOpenChange={setIsOpen}>
                (<>
                    <DsDrawer.Header>
                        <DsDrawer.Title>Basic Drawer</DsDrawer.Title>
                        <DsDrawer.CloseTrigger />
                    </DsDrawer.Header>
                    <DsDrawer.Body className={styles.body}>
                        <div className={styles.section}>
                            <DsTypography className={styles.sectionHeader} variant="body-md-semi-bold">
                                Drawer content header
                            </DsTypography>
                            <DsTypography style={{ minHeight: '300px' }} variant="heading2" className={styles.sectionContent}>
                                Out of scope section
                            </DsTypography>
                        </div>
                        <div className={styles.section}>
                            <DsTypography className={styles.sectionHeader} variant="body-md-semi-bold">
                                Drawer content header
                            </DsTypography>
                            <DsTypography style={{ minHeight: '200px' }} variant="heading2" className={styles.sectionContent}>
                                Out of scope section
                            </DsTypography>
                            <DsTypography style={{ minHeight: '500px' }} variant="heading2" className={styles.sectionContent}>
                                Out of scope section
                            </DsTypography>
                        </div>
                    </DsDrawer.Body>
                </>)
            </DsDrawer>
        </div>
    );
};
Dock To Start story ok
const DockToStart = () => {
    const [isOpen, setIsOpen] = useState(false);

    return (
        <div className={styles.storyWrapper}>
            <DsButton onClick={() => setIsOpen(true)}>Open Drawer</DsButton>
            <DsDrawer position="start" open={isOpen} onOpenChange={setIsOpen}>
                (<>
                    <DsDrawer.Header>
                        <DsDrawer.Title>Basic Drawer</DsDrawer.Title>
                        <DsDrawer.CloseTrigger />
                    </DsDrawer.Header>
                    <DsDrawer.Body className={styles.body}>
                        <div className={styles.section}>
                            <DsTypography className={styles.sectionHeader} variant="body-md-semi-bold">
                                Drawer content header
                            </DsTypography>
                            <DsTypography variant="heading2" className={styles.sectionContent}>
                                Out of scope section
                            </DsTypography>
                        </div>
                        <div className={styles.section}>
                            <DsTypography className={styles.sectionHeader} variant="body-md-semi-bold">
                                Drawer content header
                            </DsTypography>
                            <DsTypography variant="heading2" className={styles.sectionContent}>
                                Out of scope section
                            </DsTypography>
                            <DsTypography variant="heading2" className={styles.sectionContent}>
                                Out of scope section
                            </DsTypography>
                        </div>
                    </DsDrawer.Body>
                </>)
            </DsDrawer>
        </div>
    );
};
With Grid Content story ok
const WithGridContent = () => {
    const [isOpen, setIsOpen] = useState(false);

    return (
        <div className={styles.storyWrapper}>
            <DsButton onClick={() => setIsOpen(true)}>Open Drawer</DsButton>
            <DsDrawer columns={10} open={isOpen} onOpenChange={setIsOpen}>
                (<>
                    <DsDrawer.Header>
                        <DsDrawer.Title>Basic Drawer</DsDrawer.Title>
                        <DsDrawer.CloseTrigger />
                    </DsDrawer.Header>
                    <DsDrawer.Body className={styles.bodyGrid}>
                        <div style={{ gridRow: 'span 2' }} className={styles.section}>
                            <DsTypography className={styles.sectionHeader} variant="body-md-semi-bold">
                                Drawer content header
                            </DsTypography>
                            <DsTypography variant="heading2" className={styles.sectionContent}>
                                Out of scope section
                            </DsTypography>
                        </div>
                        <div className={styles.section}>
                            <DsTypography className={styles.sectionHeader} variant="body-md-semi-bold">
                                Drawer content header
                            </DsTypography>
                            <DsTypography variant="heading2" className={styles.sectionContent}>
                                Out of scope section
                            </DsTypography>
                        </div>
                        <div className={styles.section}>
                            <DsTypography className={styles.sectionHeader} variant="body-md-semi-bold">
                                Drawer content header
                            </DsTypography>
                            <DsTypography variant="heading2" className={styles.sectionContent}>
                                Out of scope section
                            </DsTypography>
                        </div>
                    </DsDrawer.Body>
                </>)
            </DsDrawer>
        </div>
    );
};
Responsive story ok
Right drawer responsiveness: the columns prop accepts a responsive value `{ lg, md }`. On screens < 1440px the drawer automatically switches to the `md` column count. Recommended responsive mappings for end-positioned drawers: - 3 cols → `{ lg: 3, md: 4 }` - 4–5 cols → `{ lg: 4, md: 6 }` / `{ lg: 5, md: 6 }` - 6+ cols → `{ lg: 6, md: 10 }` (up to 10)
const Responsive = function Render() {
    const [openDrawer, setOpenDrawer] = useState<string | null>(null);

    const close = () => setOpenDrawer(null);

    const variants = [
        { label: '3 cols → 4 on md', columns: { lg: 3, md: 4 } },
        { label: '4 cols → 6 on md', columns: { lg: 4, md: 6 } },
        { label: '5 cols → 6 on md', columns: { lg: 5, md: 6 } },
        { label: '6 cols → 10 on md', columns: { lg: 6, md: 10 } },
        { label: '8 cols → 10 on md', columns: { lg: 8, md: 10 } },
    ] satisfies Array<{ label: string; columns: ResponsiveValue<DsDrawerColumns> }>;

    return (
        <div className={styles.storyWrapper}>
            <DsTypography variant="body-md-semi-bold">
                Resize the window below 1440 px to see the responsive column change.
            </DsTypography>

            <div className={styles.responsiveButtons}>
                {variants.map(({ label }) => (
                    <DsButton key={label} onClick={() => setOpenDrawer(label)}>
                        {label}
                    </DsButton>
                ))}
            </div>

            {variants.map(({ label, columns }) => (
                <DsDrawer
                    key={label}
                    open={openDrawer === label}
                    onOpenChange={(open) => !open && close()}
                    columns={columns}
                >
                    <DsDrawer.Header>
                        <DsDrawer.Title>{label}</DsDrawer.Title>
                        <DsDrawer.CloseTrigger />
                    </DsDrawer.Header>
                    <DsDrawer.Body className={styles.body}>
                        <div className={styles.section}>
                            <DsTypography className={styles.sectionHeader} variant="body-md-semi-bold">
                                lg: {columns.lg} columns · md: {columns.md} columns
                            </DsTypography>
                            <DsTypography variant="heading2" className={styles.sectionContent}>
                                Drawer content
                            </DsTypography>
                        </div>
                    </DsDrawer.Body>
                </DsDrawer>
            ))}
        </div>
    );
};
Toggle Full Size story ok
const ToggleFullSize = () => {
    const [isOpen, setIsOpen] = useState(false);
    const [isFullScreen, setIsFullScreen] = useState(false);

    const toggleFullScreen = () => {
        setIsFullScreen(!isFullScreen);
    };

    return (
        <div className={styles.storyWrapper}>
            <DsButton onClick={() => setIsOpen(true)}>Open Drawer</DsButton>
            <DsDrawer
                open={isOpen}
                onOpenChange={setIsOpen}
                columns={isFullScreen ? 12 : args.columns || 4}>
                <DsDrawer.Header>
                    <DsDrawer.Title>Expandable Drawer</DsDrawer.Title>
                    <div className={styles.headerActions}>
                        <button
                            className={styles.expand}
                            aria-label={isFullScreen ? 'Collapse' : 'Expand'}
                            onClick={toggleFullScreen}>
                            <DsIcon icon={isFullScreen ? 'close_fullscreen' : 'open_in_full'} size="tiny" />
                        </button>
                        <div className={styles.divider} />
                        <DsDrawer.CloseTrigger />
                    </div>
                </DsDrawer.Header>
                <DsDrawer.Body className={styles.body}>
                    <div className={styles.section}>
                        <DsTypography className={styles.sectionHeader} variant="body-md-semi-bold">Drawer content header
                                                        </DsTypography>
                        <DsTypography variant="heading2" className={styles.sectionContent}>Out of scope section
                                                        </DsTypography>
                    </div>
                </DsDrawer.Body>
            </DsDrawer>
        </div>
    );
};

DsDropdownMenu.Root

components-dropdownmenu · ./src/components/ds-dropdown-menu/ds-dropdown-menu.stories.tsx
DEPRECATED: Legacy DsDropdownMenu component with options array Use compound component pattern instead
deprecated:
Prop types (react-docgen) 11 prop types
Component: src/components/ds-dropdown-menu/ds-dropdown-menu.tsx::DsDropdownMenuLegacy
Props:
/**
 * The alignment of the dropdown content
 * @default 'center'
 */
align?: 'start' | 'center' | 'end' = 'center'

/**
 * Optional children to be rendered inside the component
 * Typically used for the trigger element
 */
children?: ReactNode | undefined

/**
 * Additional CSS class names
 */
className?: string

/**
 * The gap between the trigger and dropdown content in pixels
 * @default 0
 */
contentGap?: number = 0

/**
 * Whether to render in place instead of using portals
 * @default false
 */
disablePortal?: boolean = false

/**
 * Whether to disable the search functionality
 * @default false
 */
disableSearch?: boolean = true

/**
 * Callback when an option with a value is selected
 */
onSelect?: (value: string) => void

/**
 * The options to be displayed in the dropdown menu
 */
options: DsDropdownMenuOptionLegacy[]

/**
 * Currently selected value (for selection tracking)
 */
selected?: string

/**
 * The side of the dropdown content
 * @default 'bottom'
 */
side?: 'top' | 'right' | 'bottom' | 'left' = 'bottom'

/**
 * Optional inline styles to apply to the component
 */
style?: CSSProperties
Imports
import {
    DsButton,
    DsCheckbox,
    DsDropdownMenu,
    DsIcon,
    DsRadioGroup,
    DsTextInput,
    DsTypography,
} from "@drivenets/design-system";
Default story ok
const Default = () => {
    const handleEdit = () => console.log('Edit clicked');
    const handleDuplicate = () => console.log('Duplicate clicked');
    const handleShare = () => console.log('Share clicked');
    const handleDelete = () => console.log('Delete clicked');

    return (
        <DsDropdownMenu.Root>
            <DsDropdownMenu.Trigger className="trigger">
                <span>Actions</span>
                <DsIcon icon="more_vert" />
            </DsDropdownMenu.Trigger>
            <DsDropdownMenu.Content>
                <DsDropdownMenu.Item value="edit" onSelect={handleEdit}>
                    <DsIcon icon="edit" />
                    <span>Edit</span>
                </DsDropdownMenu.Item>
                <DsDropdownMenu.Item value="duplicate" onSelect={handleDuplicate}>
                    <DsIcon icon="content_copy" />
                    <span>Duplicate</span>
                </DsDropdownMenu.Item>
                <DsDropdownMenu.Item value="share" onSelect={handleShare}>
                    <DsIcon icon="share" />
                    <span>Share</span>
                </DsDropdownMenu.Item>
                <DsDropdownMenu.Separator />
                <DsDropdownMenu.Item value="delete" onSelect={handleDelete} className="danger">
                    <DsIcon icon="delete" />
                    <span>Delete</span>
                </DsDropdownMenu.Item>
                <DsDropdownMenu.Item value="disabled" disabled>
                    <DsIcon icon="block" />
                    <span>Disabled Option</span>
                </DsDropdownMenu.Item>
            </DsDropdownMenu.Content>
        </DsDropdownMenu.Root>
    );
};
Selectable List with Search story ok
const SelectableList = function Render() {
    const [search, setSearch] = useState('');
    const [selected, setSelected] = useState<string | undefined>('option1');

    const options = [
        { value: 'option1', label: 'Option 1' },
        { value: 'option2', label: 'Option 2' },
        { value: 'option3', label: 'Option 3' },
        { value: 'option4', label: 'Option 4' },
    ];

    const selectedOption = options.find((opt) => opt.value === selected)?.label;
    const filteredOptions = options.filter((opt) => opt.label.toLowerCase().includes(search.toLowerCase()));

    return (
        <DsDropdownMenu.Root onSelect={setSelected} positioning={{ sameWidth: true }}>
            <DsDropdownMenu.Trigger className="trigger fixedWidth">
                <span>{selectedOption || 'Select an option'}</span>
                <DsIcon icon="arrow_drop_down" />
            </DsDropdownMenu.Trigger>
            <DsDropdownMenu.Content>
                <DsDropdownMenu.Header>
                    <DsTextInput
                        placeholder="Search"
                        value={search}
                        onValueChange={setSearch}
                        onKeyDown={(e) => e.stopPropagation()}
                        slots={{
                            startAdornment: <DsIcon icon="search" size="tiny" />,
                        }}
                    />
                </DsDropdownMenu.Header>
                {filteredOptions.map((option) => (
                    <DsDropdownMenu.Item key={option.value} value={option.value} selected={selected === option.value}>
                        {option.label}
                        {selected === option.value && <DsDropdownMenu.ItemIndicator />}
                    </DsDropdownMenu.Item>
                ))}
            </DsDropdownMenu.Content>
        </DsDropdownMenu.Root>
    );
};
Checkbox List with Groups story ok
const CheckboxList = function Render() {
    const [open, setOpen] = useState(false);
    const [search, setSearch] = useState('');
    const [selected, setSelected] = useState(new Set(['item1']));

    const items = [
        { id: 'item1', label: 'Menu text 1', description: 'Info Text' },
        { id: 'item2', label: 'Menu text 2', description: 'Info Text' },
        {
            id: 'item-error',
            label: 'Error item',
            description: 'Something went wrong',
            variant: 'error' as const,
        },
    ];

    const groupedItems = [
        { id: 'item3', label: 'Menu text 3', description: 'Info Text' },
        { id: 'item4', label: 'Menu text 4', description: 'Info Text' },
        { id: 'item5', label: 'Menu text 5', description: 'Info Text' },
        { id: 'item6', label: 'Menu text 6', description: 'Info Text' },
        { id: 'item7', label: 'Menu text 7', description: 'Info Text' },
    ];

    const filteredItems = items.filter((item) => item.label.toLowerCase().includes(search.toLowerCase()));
    const filteredGroupedItems = groupedItems.filter((item) =>
        item.label.toLowerCase().includes(search.toLowerCase()),
    );

    const toggleSelection = (id: string) => {
        const newSelected = new Set(selected);
        if (newSelected.has(id)) {
            newSelected.delete(id);
        } else {
            newSelected.add(id);
        }
        setSelected(newSelected);
    };

    const handleApply = () => {
        console.log('Applied selections:', Array.from(selected));
    };

    const handleCancel = () => {
        console.log('Cancelled');
        setOpen(false);
        setSearch('');
    };

    return (
        <DsDropdownMenu.Root
            open={open}
            onOpenChange={setOpen}
            onSelect={toggleSelection}
            positioning={{ sameWidth: true }}
            preventCloseOnSelect
        >
            <DsDropdownMenu.Trigger className="trigger fixedWidth">
                <span>Multi Select ({selected.size})</span>
                <DsIcon icon="arrow_drop_down" />
            </DsDropdownMenu.Trigger>
            <DsDropdownMenu.Content>
                <DsDropdownMenu.Header>
                    <DsTextInput
                        placeholder="Search"
                        value={search}
                        onValueChange={setSearch}
                        onKeyDown={(e) => e.stopPropagation()}
                        slots={{
                            startAdornment: <DsIcon icon="search" size="tiny" />,
                        }}
                    />
                </DsDropdownMenu.Header>
                {filteredItems.map((item) => {
                    const isError = 'variant' in item && item.variant === 'error';
                    const errorStyle = isError ? { color: 'var(--font-error)' } : undefined;

                    return (
                        <DsDropdownMenu.Item key={item.id} value={item.id} variant={isError ? 'error' : undefined}>
                            {isError ? (
                                <DsIcon icon="search" size="tiny" />
                            ) : (
                                <DsCheckbox
                                    tabIndex={-1}
                                    checked={selected.has(item.id)}
                                    onCheckedChange={() => toggleSelection(item.id)}
                                />
                            )}
                            <div className="item-content">
                                <DsTypography className="item-label" variant="body-sm-reg" style={errorStyle}>
                                    {item.label}
                                </DsTypography>
                                <DsTypography className="item-description" variant="body-xs-reg" style={errorStyle}>
                                    {item.description}
                                </DsTypography>
                            </div>
                        </DsDropdownMenu.Item>
                    );
                })}
                {!!filteredGroupedItems.length && (
                    <DsDropdownMenu.ItemGroup>
                        <DsDropdownMenu.ItemGroupLabel>Group Name</DsDropdownMenu.ItemGroupLabel>
                        <DsDropdownMenu.ItemGroupContent>
                            {filteredGroupedItems.map((item) => (
                                <DsDropdownMenu.Item key={item.id} value={item.id}>
                                    <DsCheckbox
                                        tabIndex={-1}
                                        checked={selected.has(item.id)}
                                        onCheckedChange={() => toggleSelection(item.id)}
                                    />
                                    <div className="item-content">
                                        <DsTypography className="item-label" variant="body-sm-reg">
                                            {item.label}
                                        </DsTypography>
                                        <DsTypography className="item-description" variant="body-xs-reg">
                                            {item.description}
                                        </DsTypography>
                                    </div>
                                </DsDropdownMenu.Item>
                            ))}
                        </DsDropdownMenu.ItemGroupContent>
                    </DsDropdownMenu.ItemGroup>
                )}
                <DsDropdownMenu.Actions>
                    <DsButton design="v1.2" buttonType="secondary" size="small" onClick={handleCancel}>
                        Cancel
                    </DsButton>
                    <DsButton design="v1.2" buttonType="primary" size="small" onClick={handleApply}>
                        Apply
                    </DsButton>
                </DsDropdownMenu.Actions>
            </DsDropdownMenu.Content>
        </DsDropdownMenu.Root>
    );
};
Collapsible Group (Controlled) story ok
const CollapsibleGroupControlled = function Render(args) {
    const [collapsed, setCollapsed] = useState(false);

    const handleCollapsedChange = (newCollapsed: boolean) => {
        setCollapsed(newCollapsed);
        args.onCollapsedChange?.(newCollapsed);
    };

    return (
        <DsDropdownMenu.Root positioning={{ sameWidth: true }}>
            <DsDropdownMenu.Trigger className="trigger fixedWidth">
                <span>Controlled Group</span>
                <DsIcon icon="arrow_drop_down" />
            </DsDropdownMenu.Trigger>
            <DsDropdownMenu.Content>
                <DsDropdownMenu.ItemGroup collapsed={collapsed} onCollapsedChange={handleCollapsedChange}>
                    <DsDropdownMenu.ItemGroupLabel>Settings</DsDropdownMenu.ItemGroupLabel>
                    <DsDropdownMenu.ItemGroupContent>
                        <DsDropdownMenu.Item value="profile">
                            <DsIcon icon="person" />
                            <span>Profile</span>
                        </DsDropdownMenu.Item>
                        <DsDropdownMenu.Item value="preferences">
                            <DsIcon icon="settings" />
                            <span>Preferences</span>
                        </DsDropdownMenu.Item>
                        <DsDropdownMenu.Item value="notifications">
                            <DsIcon icon="notifications" />
                            <span>Notifications</span>
                        </DsDropdownMenu.Item>
                    </DsDropdownMenu.ItemGroupContent>
                </DsDropdownMenu.ItemGroup>
            </DsDropdownMenu.Content>
        </DsDropdownMenu.Root>
    );
};
Radio List with Actions story ok
const RadioList = function Render() {
    const [open, setOpen] = useState(false);
    const [search, setSearch] = useState('');
    const [tempSelected, setTempSelected] = useState<string | null>(null);

    const options = [
        { value: 'option1', label: 'Menu text 1', description: 'Info Text' },
        { value: 'option2', label: 'Menu text 2', description: 'Info Text' },
        { value: 'option3', label: 'Menu text 3', description: 'Info Text' },
        { value: 'option4', label: 'Menu text 4', description: 'Info Text' },
    ];

    const filteredOptions = options.filter((opt) => opt.label.toLowerCase().includes(search.toLowerCase()));

    const handleApply = () => {
        setOpen(false);
        console.log(`Applied: ${JSON.stringify(tempSelected)}`);
    };

    const handleCancel = () => {
        console.log('Cancelled');
        setOpen(false);
        setSearch('');
    };

    const handleReset = () => {
        console.log('Reset');
        setTempSelected('');
        setOpen(false);
        setSearch('');
    };

    return (
        <DsDropdownMenu.Root
            open={open}
            onOpenChange={setOpen}
            onSelect={setTempSelected}
            positioning={{ sameWidth: true }}
            preventCloseOnSelect
        >
            <DsDropdownMenu.Trigger className="trigger fixedWidth">
                <span>{tempSelected || 'Select an option'}</span>
                <DsIcon icon="arrow_drop_down" />
            </DsDropdownMenu.Trigger>
            <DsDropdownMenu.Content>
                <DsDropdownMenu.Header>
                    <DsTextInput
                        placeholder="Search"
                        value={search}
                        onValueChange={setSearch}
                        onKeyDown={(e) => e.stopPropagation()}
                        slots={{
                            startAdornment: <DsIcon icon="search" size="tiny" />,
                        }}
                    />
                </DsDropdownMenu.Header>
                <DsRadioGroup.Root className="radio-group" value={tempSelected} onValueChange={setTempSelected}>
                    {filteredOptions.map((option) => (
                        <DsDropdownMenu.Item
                            key={option.value}
                            value={option.value}
                            className={tempSelected === option.value ? 'radio-selected' : ''}
                        >
                            <DsRadioGroup.Item value={option.value} />
                            <div className="item-content">
                                <DsTypography className="item-label" variant="body-sm-reg">
                                    {option.label}
                                </DsTypography>
                                <DsTypography className="item-description" variant="body-xs-reg">
                                    {option.description}
                                </DsTypography>
                            </div>
                        </DsDropdownMenu.Item>
                    ))}
                </DsRadioGroup.Root>
                <DsDropdownMenu.Actions>
                    <DsButton design="v1.2" variant="danger" size="small" onClick={handleReset}>
                        Reset
                    </DsButton>
                    <DsButton design="v1.2" buttonType="secondary" size="small" onClick={handleCancel}>
                        Cancel
                    </DsButton>
                    <DsButton design="v1.2" buttonType="primary" size="small" onClick={handleApply}>
                        Apply
                    </DsButton>
                </DsDropdownMenu.Actions>
            </DsDropdownMenu.Content>
        </DsDropdownMenu.Root>
    );
};
Action Menu story ok
const ActionMenu = () => {
    const handleEdit = () => console.log('Edit clicked');
    const handleDuplicate = () => console.log('Duplicate clicked');
    const handleShareEmail = () => console.log('Share via Email clicked');
    const handleShareLink = () => console.log('Copy Link clicked');
    const handleShareSocial = () => console.log('Share to Social Media clicked');
    const handleDelete = () => console.log('Delete item clicked');

    return (
        <DsDropdownMenu.Root>
            <DsDropdownMenu.Trigger asChild>
                <DsButton design="v1.2" buttonType="secondary">
                    <DsIcon icon="more_vert" />
                </DsButton>
            </DsDropdownMenu.Trigger>
            <DsDropdownMenu.Content>
                <DsDropdownMenu.Item value="edit" onSelect={handleEdit}>
                    <DsIcon icon="edit" />
                    <span>Edit</span>
                </DsDropdownMenu.Item>
                <DsDropdownMenu.Item value="duplicate" onSelect={handleDuplicate}>
                    <DsIcon icon="content_copy" />
                    <span>Duplicate</span>
                </DsDropdownMenu.Item>
                <DsDropdownMenu.Root positioning={{ placement: 'right-start' }}>
                    <DsDropdownMenu.TriggerItem className="action-menu-item">
                        <DsIcon icon="share" />
                        <span>Share</span>
                    </DsDropdownMenu.TriggerItem>
                    <DsDropdownMenu.Content>
                        <DsDropdownMenu.Item value="share-email" onSelect={handleShareEmail}>
                            <DsIcon icon="mail" />
                            <span>Email</span>
                        </DsDropdownMenu.Item>
                        <DsDropdownMenu.Item value="share-link" onSelect={handleShareLink}>
                            <DsIcon icon="link" />
                            <span>Copy Link</span>
                        </DsDropdownMenu.Item>
                        <DsDropdownMenu.Item value="share-social" onSelect={handleShareSocial}>
                            <DsIcon icon="public" />
                            <span>Social Media</span>
                        </DsDropdownMenu.Item>
                    </DsDropdownMenu.Content>
                </DsDropdownMenu.Root>
                <DsDropdownMenu.Separator />
                <DsDropdownMenu.Item value="delete" onSelect={handleDelete} className="danger">
                    <DsIcon icon="delete" />
                    <span>Delete item</span>
                </DsDropdownMenu.Item>
            </DsDropdownMenu.Content>
        </DsDropdownMenu.Root>
    );
};

DsDropdownMenuLegacy

components-dropdownmenulegacy-deprecated · ./src/components/ds-dropdown-menu/ds-dropdown-menu-legacy.stories.tsx
DEPRECATED: Legacy DsDropdownMenu component with options array Use compound component pattern instead
deprecated:
Prop types (react-docgen) 11 prop types
Component: src/components/ds-dropdown-menu/ds-dropdown-menu.tsx::DsDropdownMenuLegacy
Props:
/**
 * The alignment of the dropdown content
 * @default 'center'
 */
align?: 'start' | 'center' | 'end' = 'center'

/**
 * Optional children to be rendered inside the component
 * Typically used for the trigger element
 */
children?: ReactNode | undefined

/**
 * Additional CSS class names
 */
className?: string

/**
 * The gap between the trigger and dropdown content in pixels
 * @default 0
 */
contentGap?: number = 0

/**
 * Whether to render in place instead of using portals
 * @default false
 */
disablePortal?: boolean = false

/**
 * Whether to disable the search functionality
 * @default false
 */
disableSearch?: boolean = true

/**
 * Callback when an option with a value is selected
 */
onSelect?: (value: string) => void

/**
 * The options to be displayed in the dropdown menu
 */
options: DsDropdownMenuOptionLegacy[]

/**
 * Currently selected value (for selection tracking)
 */
selected?: string

/**
 * The side of the dropdown content
 * @default 'bottom'
 */
side?: 'top' | 'right' | 'bottom' | 'left' = 'bottom'

/**
 * Optional inline styles to apply to the component
 */
style?: CSSProperties
Imports
import { DsDropdownMenuLegacy, DsIcon } from "@drivenets/design-system";
Default story ok
const Default = () => {
    return (
        <DsDropdownMenuLegacy
            options={[
                { label: 'Edit', icon: 'edit', onClick: () => console.log('Edit clicked') },
                { label: 'Delete', icon: 'delete', onClick: () => console.log('Delete clicked') },
                { label: 'Share', icon: 'share', onClick: () => console.log('Share clicked') },
                {
                    label: 'Disabled Option',
                    icon: 'block',
                    disabled: true,
                    onClick: () => console.log('Disabled clicked'),
                },
            ]}
            contentGap={4}>
            <div className="trigger" role="button">
                <span className="label">Actions</span>
                <DsIcon className="arrow" icon="more_vert" />
            </div>
        </DsDropdownMenuLegacy>
    );
};

DsExpandableTextInput

components-expandabletextinput · ./src/components/ds-expandable-text-input/ds-expandable-text-input.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 3 prop types
Component: src/components/ds-expandable-text-input/ds-expandable-text-input.tsx::DsExpandableTextInput
Props:
/**
 * The icon to display inside the expandable input trigger button
 */
icon: IconName | FunctionComponent<SVGProps<SVGSVGElement>>

/**
 * Callback fired when the clear action is triggered
 */
onClear?: () => void

/**
 * Callback fired when the expanded state changes
 */
onExpandChange?: (expanded: boolean) => void
Imports
import { DsButton, DsExpandableTextInput, DsTable } from "@drivenets/design-system";
Default story ok
const Default = () => <DsExpandableTextInput icon="search" />;
Small Size story ok
const SmallSize = () => <DsExpandableTextInput icon="search" size="small" />;
Custom Icon story ok
const CustomIcon = () => <DsExpandableTextInput icon="search_insights" />;
Placeholder story ok
const Placeholder = () => <DsExpandableTextInput icon="search" placeholder="Type to search" />;
Controlled story ok
const Controlled = function Render(args) {
    const [value, setValue] = useState(args.value);

    return (
        <DsExpandableTextInput
            icon="search"
            value={value}
            onChange={(e) => setValue(e.target.value)}
            onClear={() => setValue('')}
        />
    );
};
Default Value story ok
const DefaultValue = () => <DsExpandableTextInput icon="search" defaultValue="initial search" />;
With Table story ok
const WithTable = function Render() {
    const [search, setSearch] = useState('');

    return (
        <div style={{ width: '500px' }}>
            <div style={{ marginBottom: '20px', display: 'flex', justifyContent: 'end', gap: '8px' }}>
                <DsExpandableTextInput
                    icon="search"
                    placeholder="Type to search..."
                    value={search}
                    onChange={(e) => setSearch(e.target.value)}
                    onClear={() => setSearch('')}
                />

                <DsButton size="small">Click</DsButton>
            </div>
            <DsTable columns={columns} data={people} columnFilters={[{ id: 'firstName', value: search }]} />
        </div>
    );
};

DsFileUpload

components-fileupload · ./src/components/ds-file-upload/ds-file-upload.stories.tsx
Design system File Upload component (with state management)
Prop types (react-docgen) 12 prop types
Component: src/components/ds-file-upload/ds-file-upload.tsx::default
Props:
/**
 * Upload adapter implementation
 */
adapter: FileUploadAdapter

/**
 * Whether to start upload automatically when files are added
 * @default true
 */
autoUpload?: boolean = true

/**
 * Maximum number of concurrent uploads
 */
maxConcurrent?: number = 3

/**
 * Additional metadata to attach to uploads
 */
metadata?: Record<string, unknown>

/**
 * Called when all file uploads are complete
 */
onAllFileUploadsComplete?: () => void

/**
 * Called when a file is deleted
 */
onFileDeleted?: (fileId: string) => void

/**
 * Called when a file is removed
 */
onFileRemoved?: (fileId: string) => void

/**
 * Called when files are added (before upload)
 */
onFilesAdded?: (files: File[]) => void

/**
 * Called when a file upload is canceled
 */
onFileUploadCanceled?: (fileId: string) => void

/**
 * Called when a file upload completes successfully
 */
onFileUploadComplete?: (fileId: string, result: FileUploadResult) => void

/**
 * Called when a file upload fails
 */
onFileUploadError?: (fileId: string, error: string) => void

/**
 * Called when a file upload is retried
 */
onFileUploadRetried?: (fileId: string) => void
Imports
import { DsButton, DsFileUpload, FileUpload } from "@drivenets/design-system";
Default story ok
Default auto-upload behavior Files automatically upload when dropped or selected
const Default = () => <DsFileUpload
    adapter={MockAdapterPresets.normal()}
    style={{ width: '500px' }}
    onFilesAdded={(files) => {
        console.log(
            'Files added:',
            files.map((f) => f.name),
        );
    }}
    onFileUploadComplete={(fileId, result) => {
        console.log('File upload complete:', fileId, result.url);
    }}
    onFileUploadError={(fileId, error) => {
        console.error('File upload failed:', fileId, error);
    }}
    onFileRemoved={(fileId) => {
        console.log('File removed:', fileId);
    }}
    onFileDeleted={(fileId) => {
        console.log('File deleted:', fileId);
    }}
    onFileUploadCanceled={(fileId) => {
        console.log('File upload canceled:', fileId);
    }}
    onFileUploadRetried={(fileId) => {
        console.log('File upload retried:', fileId);
    }}
    onAllFileUploadsComplete={() => {
        console.log('All file uploads complete!');
    }} />;
Manual story ok
Manual upload mode - files must be uploaded manually Good for review workflows or batch operations Advance use case which demonstrates use of (base) FileUpload with useFileUpload
const Manual = () => {
    const { getProps, files, uploadAll, clearFiles } = useFileUpload({
        adapter: args.adapter,
        autoUpload: args.autoUpload,
        onFileUploadComplete: args.onFileUploadComplete,
        onAllFileUploadsComplete: args.onAllFileUploadsComplete,
    });

    const isUploading = files.some((file) => file.status === 'uploading');
    const hasFiles = files.length > 0;

    return (
        <div style={{ width: '500px' }}>
            <FileUpload
                {...getProps({ onFilesAdded: args.onFilesAdded })}
                adapter={MockAdapterPresets.fast()}
                autoUpload={false}
                hideProgress={false}
                style={{ width: '500px' }}
                onFilesAdded={fn()}
                onFileUploadComplete={fn()}
                onAllFileUploadsComplete={fn()} />
            {hasFiles && (
                <div style={{ marginTop: '16px', display: 'flex', gap: '8px' }}>
                    <DsButton design="v1.2" size="small" onClick={() => uploadAll()} disabled={isUploading}>
                        {isUploading ? 'Uploading...' : 'Upload All'}
                    </DsButton>
                    <DsButton
                        design="v1.2"
                        variant="ghost"
                        size="small"
                        onClick={() => clearFiles()}
                        disabled={isUploading}
                    >
                        Clear All
                    </DsButton>
                </div>
            )}
        </div>
    );
};
Compact story ok
const Compact = () => <DsFileUpload
    adapter={MockAdapterPresets.fast()}
    compact
    maxFiles={1}
    accept={[
        'application/pdf',
        'text/csv',
        {
            mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
            extensions: ['.xlsx'],
        },
    ]}
    dropzoneText="Drag and drop your document here or"
    triggerText="Choose document"
    style={{ width: '400px' }}
    onFilesAdded={fn()} />;
Disabled story ok
Disabled state
const Disabled = () => <DsFileUpload adapter={MockAdapterPresets.normal()} disabled style={{ width: '500px' }} />;
Upload Error story ok
Upload error scenario - file fails validation immediately
const UploadError = () => <DsFileUpload
    adapter={MockAdapterPresets.error('Unsupported file type')}
    style={{ width: '500px' }} />;
Upload Interrupted story ok
Upload interrupted scenario - network fails mid-upload Demonstrates retry functionality
const UploadInterrupted = () => <DsFileUpload adapter={MockAdapterPresets.interrupted(30)} style={{ width: '500px' }} />;
Max Files story ok
const MaxFiles = () => <DsFileUpload
    adapter={MockAdapterPresets.fast()}
    maxFiles={1}
    style={{ width: '400px' }} />;
Duplicate Files story ok
Duplicate files scenario - uploading the same file twice Demonstrates duplicate detection and FILE_EXISTS error
const DuplicateFiles = () => <DsFileUpload
    adapter={MockAdapterPresets.fast()}
    style={{ width: '500px' }}
    onFilesAdded={fn()} />;
Hidden Info Text story ok
Hidden info text - hides the file type and size limit information
const HiddenInfoText = () => <DsFileUpload
    adapter={MockAdapterPresets.fast()}
    hideInfoText
    style={{ width: '500px' }} />;
Cancel Upload story ok
Cancel upload scenario - cancel an ongoing upload Demonstrates upload cancellation functionality
const CancelUpload = () => <DsFileUpload
    adapter={MockAdapterPresets.slow()}
    style={{ width: '500px' }}
    onFileUploadCanceled={fn()} />;

DsFilterStatusIcon

components-filterstatusicon · ./src/components/ds-filter-status-icon/ds-filter-status-icon.stories.tsx
Design system Filter Status Icon component Status icons for toggle filter buttons to help users quickly distinguish states
Prop types (react-docgen) 5 prop types
Component: src/components/ds-filter-status-icon/ds-filter-status-icon.tsx::DsFilterStatusIcon
Props:
/**
 * Whether the status icon is active or non-active
 * @default true
 */
active?: boolean = true

/**
 * Additional CSS class names
 */
className?: string

/**
 * Icon size
 * @default 'small'
 */
size?: (typeof iconSizes)[number] = 'small'

/**
 * The filter status type
 */
status: (typeof filterStatuses)[number]

/**
 * Additional styles to apply to the component
 */
style?: CSSProperties
Imports
import { DsFilterStatusIcon } from "@drivenets/design-system";
Default story ok
const Default = () => <DsFilterStatusIcon status="running" active />;
All story ok
const All = () => {
    return (
        <div className={styles.storiesContainer}>
            <div className={styles.row}>
                <div className={styles.label}></div>
                {filterStatuses.map((status) => (
                    <div key={status} className={styles.label}>
                        {status}
                    </div>
                ))}
            </div>

            <div className={styles.row}>
                <div className={styles.label}>active</div>
                {filterStatuses.map((status) => (
                    <div key={`active-${status}`} className={styles.cell}>
                        <DsFilterStatusIcon status={status} active />
                    </div>
                ))}
            </div>

            <div className={styles.row}>
                <div className={styles.label}>non-active</div>
                {filterStatuses.map((status) => (
                    <div key={`inactive-${status}`} className={styles.cell}>
                        <DsFilterStatusIcon status={status} active={false} />
                    </div>
                ))}
            </div>
        </div>
    );
};

DsFormControl

components-formcontrol-datepicker · ./src/components/ds-form-control/stories/ds-form-control-date-picker.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 9 prop types
Component: src/components/ds-form-control/ds-form-control.tsx::default
Props:
/**
 * Additional CSS class names
 */
className?: string

/**
 * Unique identifier for the control
 */
id?: string

/**
 * Label text
 */
label: string

/**
 * Message under the control
 */
message?: string

/**
 * Icon shown next to message
 */
messageIcon?: IconName | FunctionComponent<SVGProps<SVGSVGElement>> = 'info'

/**
 * Marks the field as required
 */
required?: boolean = false

/**
 * Optional render slots for customizing parts of the control.
 */
slots?: {
	/**
	 * Adornment to display at the end of the label
	 */
	endAdornment?: React.ReactNode;
}

/**
 * Visual status
 */
status?: (typeof controlStatuses)[number]

/**
 * Additional styles to apply to the component
 */
style?: React.CSSProperties
Imports
import { DefaultDescription, DsFormControl } from "@drivenets/design-system";
Default story ok
const Default = () => <DsFormControl
    label="Event Date"
    required
    message="Select a date for your event"
    style={{ width: '300px' }}><DsFormControl.DatePicker /></DsFormControl>;
With Time story ok
const WithTime = () => <DsFormControl
    label="Appointment"
    required
    message="Select date and time"
    style={{ width: '300px' }}><DsFormControl.DatePicker withTime /></DsFormControl>;
With Description story ok
const WithDescription = () => <DsFormControl label="Event Date" required style={{ width: '300px' }}>(<>
        <DsFormControl.Description>
            <DefaultDescription />
        </DsFormControl.Description>
        <DsFormControl.DatePicker />
    </>)</DsFormControl>;
Error story ok
const Error = () => <DsFormControl
    status="error"
    label="Event Date"
    required
    message="Date is required."
    messageIcon="error"
    style={{ width: '300px' }}>(<>
        <DsFormControl.Description>
            <DefaultDescription />
        </DsFormControl.Description>
        <DsFormControl.DatePicker />
    </>)</DsFormControl>;
Disabled story ok
const Disabled = () => <DsFormControl label="Event Date" style={{ width: '300px' }}><DsFormControl.DatePicker disabled /></DsFormControl>;
With Validation story ok
const WithValidation = function Render() {
    const [value, setValue] = useState<Date | null>(null);
    const [touched, setTouched] = useState(false);
    const error = touched && !value ? 'Date is required' : undefined;

    return (
        <DsFormControl
            label="Event Date"
            required
            status={error ? 'error' : undefined}
            messageIcon="cancel"
            message={error}
            style={{ width: '300px' }}
        >
            <DsFormControl.DatePicker
                value={value}
                onChange={(v) => {
                    setValue(v);
                    setTouched(true);
                }}
            />
        </DsFormControl>
    );
};

DsFormControl

components-formcontrol-number · ./src/components/ds-form-control/stories/ds-form-control-input-number.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 9 prop types
Component: src/components/ds-form-control/ds-form-control.tsx::default
Props:
/**
 * Additional CSS class names
 */
className?: string

/**
 * Unique identifier for the control
 */
id?: string

/**
 * Label text
 */
label: string

/**
 * Message under the control
 */
message?: string

/**
 * Icon shown next to message
 */
messageIcon?: IconName | FunctionComponent<SVGProps<SVGSVGElement>> = 'info'

/**
 * Marks the field as required
 */
required?: boolean = false

/**
 * Optional render slots for customizing parts of the control.
 */
slots?: {
	/**
	 * Adornment to display at the end of the label
	 */
	endAdornment?: React.ReactNode;
}

/**
 * Visual status
 */
status?: (typeof controlStatuses)[number]

/**
 * Additional styles to apply to the component
 */
style?: React.CSSProperties
Imports
import { DefaultDescription, DsFormControl, DsIcon } from "@drivenets/design-system";
Default story ok
const Default = () => <DsFormControl label="Input label" required message="This is a message">(<DsFormControl.NumberInput placeholder="Enter number" min={1} max={100} step={1} defaultValue={10} />)</DsFormControl>;
With Custom Width story ok
const WithCustomWidth = () => <DsFormControl label="Input label" required style={{ width: '300px' }}>(<DsFormControl.NumberInput placeholder="Enter number" min={1} max={100} step={1} defaultValue={10} />)</DsFormControl>;
With Custom Styles story ok
const WithCustomStyles = () => <DsFormControl
    label="Input label"
    required
    style={{
        width: '400px',
        padding: '16px',
        border: '2px solid #e0e0e0',
        borderRadius: '8px',
        backgroundColor: '#f9f9f9',
    }}>(<DsFormControl.NumberInput placeholder="Enter number" min={1} max={100} step={1} defaultValue={10} />)</DsFormControl>;
With Description story ok
const WithDescription = () => <DsFormControl
    label="Input label"
    required
    style={{
        width: '300px',
    }}>(<>
        <DsFormControl.Description>
            <DefaultDescription />
        </DsFormControl.Description>
        <DsFormControl.NumberInput placeholder="Enter number" min={1} max={100} step={1} defaultValue={10} />
    </>)</DsFormControl>;
With Help Icon story ok
const WithHelpIcon = () => <DsFormControl
    label="Input label"
    required
    slots={{
        endAdornment: (
            <button
                type="button"
                className={styles.helpIcon}
                onClick={() => alert('Help clicked!')}
                aria-label="Help"
            >
                <DsIcon icon="info" size="small" />
            </button>
        ),
    }}>(<>
        <DsFormControl.Description>
            <DefaultDescription />
        </DsFormControl.Description>
        <DsFormControl.NumberInput placeholder="Enter number" min={1} max={100} step={1} defaultValue={10} />
    </>)</DsFormControl>;
Success story ok
const Success = () => <DsFormControl
    status="success"
    label="Input label"
    message="This is a success caption under a number input."
    messageIcon="check_circle">(<>
        <DsFormControl.Description>
            <DefaultDescription />
        </DsFormControl.Description>
        <DsFormControl.NumberInput placeholder="Enter number" min={1} max={100} step={1} defaultValue={10} />
    </>)</DsFormControl>;
Error story ok
const Error = () => <DsFormControl
    status="error"
    label="Input label"
    message="This is an error caption under a number input."
    messageIcon="error">(<>
        <DsFormControl.Description>
            <DefaultDescription />
        </DsFormControl.Description>
        <DsFormControl.NumberInput placeholder="Enter number" min={1} max={100} step={1} defaultValue={10} />
    </>)</DsFormControl>;
Warning story ok
const Warning = () => <DsFormControl
    status="warning"
    label="Input label"
    message="This is a warning caption under a number input."
    messageIcon="info">(<>
        <DsFormControl.Description>
            <DefaultDescription />
        </DsFormControl.Description>
        <DsFormControl.NumberInput placeholder="Enter number" min={1} max={100} step={1} defaultValue={10} />
    </>)</DsFormControl>;
Disabled story ok
const Disabled = () => <DsFormControl label="Input label">(<>
        <DsFormControl.Description>
            <DefaultDescription />
        </DsFormControl.Description>
        <DsFormControl.NumberInput
            placeholder="Disabled Input"
            disabled
            min={1}
            max={100}
            step={1}
            defaultValue={10}
        />
    </>)</DsFormControl>;

DsFormControl

components-formcontrol-password · ./src/components/ds-form-control/stories/ds-form-control-input-password.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 9 prop types
Component: src/components/ds-form-control/ds-form-control.tsx::default
Props:
/**
 * Additional CSS class names
 */
className?: string

/**
 * Unique identifier for the control
 */
id?: string

/**
 * Label text
 */
label: string

/**
 * Message under the control
 */
message?: string

/**
 * Icon shown next to message
 */
messageIcon?: IconName | FunctionComponent<SVGProps<SVGSVGElement>> = 'info'

/**
 * Marks the field as required
 */
required?: boolean = false

/**
 * Optional render slots for customizing parts of the control.
 */
slots?: {
	/**
	 * Adornment to display at the end of the label
	 */
	endAdornment?: React.ReactNode;
}

/**
 * Visual status
 */
status?: (typeof controlStatuses)[number]

/**
 * Additional styles to apply to the component
 */
style?: React.CSSProperties
Imports
import { DefaultDescription, DsFormControl, DsIcon } from "@drivenets/design-system";
Default story ok
const Default = () => <DsFormControl label="Input label" required message="This is a message"><DsFormControl.PasswordInput placeholder="Enter password" /></DsFormControl>;
With Custom Width story ok
const WithCustomWidth = () => <DsFormControl label="Input label" required style={{ width: '300px' }}><DsFormControl.PasswordInput placeholder="Enter password" /></DsFormControl>;
With Custom Styles story ok
const WithCustomStyles = () => <DsFormControl
    label="Input label"
    required
    style={{
        width: '400px',
        padding: '16px',
        border: '2px solid #e0e0e0',
        borderRadius: '8px',
        backgroundColor: '#f9f9f9',
    }}><DsFormControl.PasswordInput placeholder="Password input with custom styling" /></DsFormControl>;
With Description story ok
const WithDescription = () => <DsFormControl
    label="Input label"
    required
    style={{
        width: '300px',
    }}>(<>
        <DsFormControl.Description>
            <DefaultDescription />
        </DsFormControl.Description>
        <DsFormControl.PasswordInput placeholder="Enter password" />
    </>)</DsFormControl>;
With Help Icon story ok
const WithHelpIcon = () => <DsFormControl
    label="Input label"
    required
    slots={{
        endAdornment: (
            <button
                type="button"
                className={styles.helpIcon}
                onClick={() => alert('Help clicked!')}
                aria-label="Help"
            >
                <DsIcon icon="info" size="small" />
            </button>
        ),
    }}>(<>
        <DsFormControl.Description>
            <DefaultDescription />
        </DsFormControl.Description>
        <DsFormControl.PasswordInput placeholder="Search" />
    </>)</DsFormControl>;
Success story ok
const Success = () => <DsFormControl
    status="success"
    label="Input label"
    message="This is a success caption under a password input."
    messageIcon="check_circle">(<>
        <DsFormControl.Description>
            <DefaultDescription />
        </DsFormControl.Description>
        <DsFormControl.PasswordInput />
    </>)</DsFormControl>;
Error story ok
const Error = () => <DsFormControl
    status="error"
    label="Input label"
    message="This is an error caption under a password input."
    messageIcon="error">(<>
        <DsFormControl.Description>
            <DefaultDescription />
        </DsFormControl.Description>
        <DsFormControl.PasswordInput />
    </>)</DsFormControl>;
Warning story ok
const Warning = () => <DsFormControl
    status="warning"
    label="Input label"
    message="This is a warning caption under a password input."
    messageIcon="info">(<>
        <DsFormControl.Description>
            <DefaultDescription />
        </DsFormControl.Description>
        <DsFormControl.PasswordInput />
    </>)</DsFormControl>;
Disabled story ok
const Disabled = () => <DsFormControl label="Input label">(<>
        <DsFormControl.Description>
            <DefaultDescription />
        </DsFormControl.Description>
        <DsFormControl.PasswordInput placeholder="Disabled Input" disabled />
    </>)</DsFormControl>;

DsFormControl

components-formcontrol-select · ./src/components/ds-form-control/stories/ds-form-control-select.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 9 prop types
Component: src/components/ds-form-control/ds-form-control.tsx::default
Props:
/**
 * Additional CSS class names
 */
className?: string

/**
 * Unique identifier for the control
 */
id?: string

/**
 * Label text
 */
label: string

/**
 * Message under the control
 */
message?: string

/**
 * Icon shown next to message
 */
messageIcon?: IconName | FunctionComponent<SVGProps<SVGSVGElement>> = 'info'

/**
 * Marks the field as required
 */
required?: boolean = false

/**
 * Optional render slots for customizing parts of the control.
 */
slots?: {
	/**
	 * Adornment to display at the end of the label
	 */
	endAdornment?: React.ReactNode;
}

/**
 * Visual status
 */
status?: (typeof controlStatuses)[number]

/**
 * Additional styles to apply to the component
 */
style?: React.CSSProperties
Imports
import { DefaultDescription, DsFormControl, DsIcon } from "@drivenets/design-system";
Default story ok
const Default = function Render() {
    const [value, setValue] = useState('');

    return (
        <DsFormControl label="Input" required={true} message="This is a message">
            <DsFormControl.Select
                placeholder="Select an option"
                value={value}
                onValueChange={setValue}
                clearable
                options={[
                    { label: 'Option 1', value: 'option1', icon: 'download' },
                    { label: 'Option 2', value: 'option2', icon: 'save' },
                    { label: 'Option 3', value: 'option3', icon: 'description' },
                ]}
            />
        </DsFormControl>
    );
};
With Custom Width story ok
const WithCustomWidth = function Render() {
    const [value, setValue] = useState('');

    return (
        <DsFormControl label="Input" required={true} style={{ width: '300px' }}>
            <DsFormControl.Select
                placeholder="Select an option"
                value={value}
                onValueChange={setValue}
                clearable
                options={[
                    { label: 'Option 1', value: 'option1', icon: 'download' },
                    { label: 'Option 2', value: 'option2', icon: 'save' },
                    { label: 'Option 3', value: 'option3', icon: 'description' },
                ]}
            />
        </DsFormControl>
    );
};
With Custom Styles story ok
const WithCustomStyles = function Render() {
    const [value, setValue] = useState('');

    return (
        <DsFormControl
            label="Input"
            required={true}
            style={{
                width: '400px',
                padding: '16px',
                border: '2px solid #e0e0e0',
                borderRadius: '8px',
                backgroundColor: '#f9f9f9',
            }}
        >
            <DsFormControl.Select
                placeholder="Select an option"
                value={value}
                onValueChange={setValue}
                clearable
                options={[
                    { label: 'Option 1', value: 'option1', icon: 'download' },
                    { label: 'Option 2', value: 'option2', icon: 'save' },
                    { label: 'Option 3', value: 'option3', icon: 'description' },
                ]}
            />
        </DsFormControl>
    );
};
With Description story ok
const WithDescription = function Render() {
    const [value, setValue] = useState('');

    return (
        <DsFormControl label="Input" required={true} style={{ width: '300px' }}>
            <DsFormControl.Description>
                <DefaultDescription />
            </DsFormControl.Description>
            <DsFormControl.Select
                placeholder="Select an option"
                value={value}
                onValueChange={setValue}
                clearable
                options={[
                    { label: 'Option 1', value: 'option1', icon: 'download' },
                    { label: 'Option 2', value: 'option2', icon: 'save' },
                    { label: 'Option 3', value: 'option3', icon: 'description' },
                ]}
            />
        </DsFormControl>
    );
};
With Help Icon story ok
const WithHelpIcon = function Render() {
    const [value, setValue] = useState('');

    return (
        <DsFormControl
            label="Input"
            required={true}
            slots={{
                endAdornment: (
                    <button
                        type="button"
                        className={styles.helpIcon}
                        onClick={() => alert('Help clicked!')}
                        aria-label="Help"
                    >
                        <DsIcon icon="info" size="small" />
                    </button>
                ),
            }}
        >
            <DsFormControl.Description>
                <DefaultDescription />
            </DsFormControl.Description>
            <DsFormControl.Select
                value={value}
                onValueChange={setValue}
                clearable
                options={[
                    { value: 'option1', label: 'Option 1' },
                    { value: 'option2', label: 'Option 2' },
                    { value: 'option3', label: 'Option 3' },
                ]}
                placeholder="Select an option"
            />
        </DsFormControl>
    );
};
Success story ok
const Success = function Render() {
    const [value, setValue] = useState('');

    return (
        <DsFormControl
            status="success"
            label="Input"
            message="This is a success caption under a select input."
            messageIcon="check_circle"
        >
            <DsFormControl.Description>
                <DefaultDescription />
            </DsFormControl.Description>
            <DsFormControl.Select
                placeholder="Select an option"
                value={value}
                onValueChange={setValue}
                clearable
                options={[
                    { label: 'Option 1', value: 'option1', icon: 'download' },
                    { label: 'Option 2', value: 'option2', icon: 'save' },
                    { label: 'Option 3', value: 'option3', icon: 'description' },
                ]}
            />
        </DsFormControl>
    );
};
Error story ok
const Error = function Render() {
    const [value, setValue] = useState('');

    return (
        <DsFormControl
            status="error"
            label="Input"
            message="This is an error caption under a select input."
            messageIcon="error"
        >
            <DsFormControl.Description>
                <DefaultDescription />
            </DsFormControl.Description>
            <DsFormControl.Select
                placeholder="Select an option"
                value={value}
                onValueChange={setValue}
                clearable
                options={[
                    { label: 'Option 1', value: 'option1', icon: 'download' },
                    { label: 'Option 2', value: 'option2', icon: 'save' },
                    { label: 'Option 3', value: 'option3', icon: 'description' },
                ]}
            />
        </DsFormControl>
    );
};
Warning story ok
const Warning = function Render() {
    const [value, setValue] = useState('');

    return (
        <DsFormControl
            status="warning"
            label="Input"
            message="This is a warning caption under a select input."
            messageIcon="info"
        >
            <DsFormControl.Description>
                <DefaultDescription />
            </DsFormControl.Description>
            <DsFormControl.Select
                placeholder="Select an option"
                value={value}
                onValueChange={setValue}
                clearable
                options={[
                    { label: 'Option 1', value: 'option1', icon: 'download' },
                    { label: 'Option 2', value: 'option2', icon: 'save' },
                    { label: 'Option 3', value: 'option3', icon: 'description' },
                ]}
            />
        </DsFormControl>
    );
};
Disabled story ok
const Disabled = function Render() {
    const [value, setValue] = useState('');

    return (
        <DsFormControl label="Input">
            <DsFormControl.Description>
                <DefaultDescription />
            </DsFormControl.Description>
            <DsFormControl.Select
                placeholder="Select an option"
                value={value}
                onValueChange={setValue}
                clearable
                options={[
                    { label: 'Option 1', value: 'option1', icon: 'download' },
                    { label: 'Option 2', value: 'option2', icon: 'save' },
                    { label: 'Option 3', value: 'option3', icon: 'description' },
                ]}
                disabled
            />
        </DsFormControl>
    );
};

DsFormControl

components-formcontrol-textarea · ./src/components/ds-form-control/stories/ds-form-control-textarea.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 9 prop types
Component: src/components/ds-form-control/ds-form-control.tsx::default
Props:
/**
 * Additional CSS class names
 */
className?: string

/**
 * Unique identifier for the control
 */
id?: string

/**
 * Label text
 */
label: string

/**
 * Message under the control
 */
message?: string

/**
 * Icon shown next to message
 */
messageIcon?: IconName | FunctionComponent<SVGProps<SVGSVGElement>> = 'info'

/**
 * Marks the field as required
 */
required?: boolean = false

/**
 * Optional render slots for customizing parts of the control.
 */
slots?: {
	/**
	 * Adornment to display at the end of the label
	 */
	endAdornment?: React.ReactNode;
}

/**
 * Visual status
 */
status?: (typeof controlStatuses)[number]

/**
 * Additional styles to apply to the component
 */
style?: React.CSSProperties
Imports
import { DefaultDescription, DsFormControl, DsIcon } from "@drivenets/design-system";
Default story ok
const Default = () => <DsFormControl label="Input label" required message="This is a message"><DsFormControl.Textarea placeholder="Input" /></DsFormControl>;
With Custom Width story ok
const WithCustomWidth = () => <DsFormControl label="Input label" required style={{ width: '300px' }}><DsFormControl.Textarea placeholder="Input with custom width" /></DsFormControl>;
With Custom Styles story ok
const WithCustomStyles = () => <DsFormControl
    label="Input label"
    required
    style={{
        width: '400px',
        padding: '16px',
        border: '2px solid #e0e0e0',
        borderRadius: '8px',
        backgroundColor: '#f9f9f9',
    }}><DsFormControl.Textarea placeholder="Input with custom styling" /></DsFormControl>;
With Description story ok
const WithDescription = () => <DsFormControl
    label="Input label"
    required
    style={{
        width: '300px',
    }}>(<>
        <DsFormControl.Description>
            <DefaultDescription />
        </DsFormControl.Description>
        <DsFormControl.Textarea placeholder="Search" />
    </>)</DsFormControl>;
With Help Icon story ok
const WithHelpIcon = () => <DsFormControl
    label="Input label"
    required
    slots={{
        endAdornment: (
            <button
                type="button"
                className={styles.helpIcon}
                onClick={() => alert('Help clicked!')}
                aria-label="Help"
            >
                <DsIcon icon="info" size="small" />
            </button>
        ),
    }}>(<>
        <DsFormControl.Description>
            <DefaultDescription />
        </DsFormControl.Description>
        <DsFormControl.Textarea placeholder="Search" />
    </>)</DsFormControl>;
Success story ok
const Success = () => <DsFormControl
    status="success"
    label="Input label"
    message="This is a success caption under a text input."
    messageIcon="check_circle">(<>
        <DsFormControl.Description>
            <DefaultDescription />
        </DsFormControl.Description>
        <DsFormControl.Textarea />
    </>)</DsFormControl>;
Error story ok
const Error = () => <DsFormControl
    status="error"
    label="Input label"
    message="This is an error caption under a text input."
    messageIcon="error">(<>
        <DsFormControl.Description>
            <DefaultDescription />
        </DsFormControl.Description>
        <DsFormControl.Textarea />
    </>)</DsFormControl>;
Warning story ok
const Warning = () => <DsFormControl
    status="warning"
    label="Input label"
    message="This is a warning caption under a text input."
    messageIcon="info">(<>
        <DsFormControl.Description>
            <DefaultDescription />
        </DsFormControl.Description>
        <DsFormControl.Textarea />
    </>)</DsFormControl>;
Disabled story ok
const Disabled = () => <DsFormControl label="Input label">(<>
        <DsFormControl.Description>
            <DefaultDescription />
        </DsFormControl.Description>
        <DsFormControl.Textarea placeholder="Disabled Input" disabled />
    </>)</DsFormControl>;

DsFormControl

components-formcontrol-timepicker · ./src/components/ds-form-control/stories/ds-form-control-time-picker.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 9 prop types
Component: src/components/ds-form-control/ds-form-control.tsx::default
Props:
/**
 * Additional CSS class names
 */
className?: string

/**
 * Unique identifier for the control
 */
id?: string

/**
 * Label text
 */
label: string

/**
 * Message under the control
 */
message?: string

/**
 * Icon shown next to message
 */
messageIcon?: IconName | FunctionComponent<SVGProps<SVGSVGElement>> = 'info'

/**
 * Marks the field as required
 */
required?: boolean = false

/**
 * Optional render slots for customizing parts of the control.
 */
slots?: {
	/**
	 * Adornment to display at the end of the label
	 */
	endAdornment?: React.ReactNode;
}

/**
 * Visual status
 */
status?: (typeof controlStatuses)[number]

/**
 * Additional styles to apply to the component
 */
style?: React.CSSProperties
Imports
import { DefaultDescription, DsFormControl } from "@drivenets/design-system";
Default story ok
const Default = () => <DsFormControl
    label="Start Time"
    required
    message="Select a time"
    style={{ width: '300px' }}><DsFormControl.TimePicker /></DsFormControl>;
With Description story ok
const WithDescription = () => <DsFormControl label="Start Time" required style={{ width: '300px' }}>(<>
        <DsFormControl.Description>
            <DefaultDescription />
        </DsFormControl.Description>
        <DsFormControl.TimePicker />
    </>)</DsFormControl>;
Error story ok
const Error = () => <DsFormControl
    status="error"
    label="Start Time"
    required
    message="Time is required."
    messageIcon="error"
    style={{ width: '300px' }}>(<>
        <DsFormControl.Description>
            <DefaultDescription />
        </DsFormControl.Description>
        <DsFormControl.TimePicker />
    </>)</DsFormControl>;
Disabled story ok
const Disabled = () => <DsFormControl label="Start Time" style={{ width: '300px' }}><DsFormControl.TimePicker disabled /></DsFormControl>;
With Min Max story ok
const WithMinMax = () => {
    const [value, setValue] = useState<Date | null>(null);

    return (
        <DsFormControl
            label="Business Hours"
            message="Select a time between 9:00 AM and 5:00 PM"
            style={{ width: '300px' }}>
            <DsFormControl.TimePicker
                value={value}
                onChange={setValue}
                min={new Date('2026-01-15T09:00:00')}
                max={new Date('2026-01-15T17:00:00')} />
        </DsFormControl>
    );
};
With Validation story ok
const WithValidation = function Render() {
    const [value, setValue] = useState<Date | null>(null);
    const [touched, setTouched] = useState(false);
    const error = touched && !value ? 'Time is required' : undefined;

    return (
        <DsFormControl
            label="Start Time"
            required
            status={error ? 'error' : undefined}
            messageIcon="cancel"
            message={error}
            style={{ width: '300px' }}
        >
            <DsFormControl.TimePicker
                value={value}
                onChange={(v) => {
                    setValue(v);
                    setTouched(true);
                }}
            />
        </DsFormControl>
    );
};

DsFormControl

components-formcontrol-text · ./src/components/ds-form-control/stories/ds-form-control.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 9 prop types
Component: src/components/ds-form-control/ds-form-control.tsx::default
Props:
/**
 * Additional CSS class names
 */
className?: string

/**
 * Unique identifier for the control
 */
id?: string

/**
 * Label text
 */
label: string

/**
 * Message under the control
 */
message?: string

/**
 * Icon shown next to message
 */
messageIcon?: IconName | FunctionComponent<SVGProps<SVGSVGElement>> = 'info'

/**
 * Marks the field as required
 */
required?: boolean = false

/**
 * Optional render slots for customizing parts of the control.
 */
slots?: {
	/**
	 * Adornment to display at the end of the label
	 */
	endAdornment?: React.ReactNode;
}

/**
 * Visual status
 */
status?: (typeof controlStatuses)[number]

/**
 * Additional styles to apply to the component
 */
style?: React.CSSProperties
Imports
import { DsFormControl, DsIcon } from "@drivenets/design-system";
Default story ok
const Default = () => <DsFormControl label="Input label" required message="This is a message"><DsFormControl.TextInput placeholder="Input" /></DsFormControl>;
With Custom Width story ok
const WithCustomWidth = () => <DsFormControl label="Input label" required style={{ width: '300px' }}><DsFormControl.TextInput placeholder="Input with custom width" /></DsFormControl>;
With Custom Styles story ok
const WithCustomStyles = () => <DsFormControl
    label="Input label"
    required
    style={{
        width: '400px',
        padding: '16px',
        border: '2px solid #e0e0e0',
        borderRadius: '8px',
        backgroundColor: '#f9f9f9',
    }}><DsFormControl.TextInput placeholder="Input with custom styling" /></DsFormControl>;
With Description story ok
const WithDescription = () => <DsFormControl
    label="Input label"
    required
    style={{
        width: '300px',
    }}>(<>
        <DsFormControl.Description>
            This is a description text. It&apos;s an optional and will not exceeds more than 2 rows. A{' '}
            <button
                type="button"
                style={{
                    background: 'none',
                    border: 'none',
                    padding: 0,
                    margin: 0,
                    cursor: 'pointer',
                    color: 'var(--color-dap-blue-600)',
                    textDecoration: 'underline',
                    fontSize: 'inherit',
                    fontFamily: 'inherit',
                }}
                onClick={() => alert('Learn more clicked!')}
            >
                Learn more
            </button>{' '}
            can be added.
        </DsFormControl.Description>
        <DsFormControl.TextInput
            placeholder="Search"
            slots={{ startAdornment: <DsIcon icon="search" size="tiny" /> }}
        />
    </>)</DsFormControl>;
With Help Icon story ok
const WithHelpIcon = () => <DsFormControl
    label="Input label"
    required
    slots={{
        endAdornment: (
            <button
                type="button"
                onClick={() => alert('Help clicked!')}
                aria-label="Help"
                style={{
                    background: 'none',
                    border: 'none',
                    padding: '4px',
                    cursor: 'pointer',
                    color: 'var(--color-dap-gray-500)',
                    display: 'flex',
                    alignItems: 'center',
                    justifyContent: 'center',
                    width: '16px',
                    height: '16px',
                    borderRadius: '50%',
                }}
            >
                <DsIcon icon="info" size="small" />
            </button>
        ),
    }}>(<>
        <DsFormControl.Description>
            This is a description text. It&apos;s an optional and will not exceeds more than 2 rows. A{' '}
            <button
                type="button"
                style={{
                    background: 'none',
                    border: 'none',
                    padding: 0,
                    margin: 0,
                    cursor: 'pointer',
                    color: 'var(--color-dap-blue-600)',
                    textDecoration: 'underline',
                    fontSize: 'inherit',
                    fontFamily: 'inherit',
                }}
                onClick={() => alert('Learn more clicked!')}
            >
                Learn more
            </button>{' '}
            can be added.
        </DsFormControl.Description>
        <DsFormControl.TextInput
            placeholder="Search"
            slots={{ startAdornment: <DsIcon icon="search" size="tiny" /> }}
        />
    </>)</DsFormControl>;
With Icon story ok
const WithIcon = () => <DsFormControl label="Input label" required message="This is a message">(<DsFormControl.TextInput
        placeholder="Input"
        slots={{ startAdornment: <DsIcon icon="call" size="tiny" /> }}
    />)</DsFormControl>;
Success story ok
const Success = () => <DsFormControl
    status="success"
    label="Input label"
    message="This is a success caption under a text input."
    messageIcon="check_circle">(<>
        <DsFormControl.Description>
            This is a description text. It&apos;s an optional and will not exceeds more than 2 rows. A{' '}
            <button
                type="button"
                style={{
                    background: 'none',
                    border: 'none',
                    padding: 0,
                    margin: 0,
                    cursor: 'pointer',
                    color: 'var(--color-dap-blue-600)',
                    textDecoration: 'underline',
                    fontSize: 'inherit',
                    fontFamily: 'inherit',
                }}
                onClick={() => alert('Learn more clicked!')}
            >
                Learn more
            </button>{' '}
            can be added.
        </DsFormControl.Description>
        <DsFormControl.TextInput
            type="text"
            slots={{ endAdornment: <DsIcon icon="visibility" size="tiny" /> }}
        />
    </>)</DsFormControl>;
Error story ok
const Error = () => <DsFormControl
    status="error"
    label="Input label"
    message="This is an error caption under a text input."
    messageIcon="error">(<>
        <DsFormControl.Description>
            This is a description text. It&apos;s an optional and will not exceeds more than 2 rows. A{' '}
            <button
                type="button"
                style={{
                    background: 'none',
                    border: 'none',
                    padding: 0,
                    margin: 0,
                    cursor: 'pointer',
                    color: 'var(--color-dap-blue-600)',
                    textDecoration: 'underline',
                    fontSize: 'inherit',
                    fontFamily: 'inherit',
                }}
                onClick={() => alert('Learn more clicked!')}
            >
                Learn more
            </button>{' '}
            can be added.
        </DsFormControl.Description>
        <DsFormControl.TextInput
            slots={{
                startAdornment: <DsIcon icon="search" size="tiny" />,
                endAdornment: <DsIcon icon="error" size="tiny" />,
            }}
        />
    </>)</DsFormControl>;
Warning story ok
const Warning = () => <DsFormControl
    status="warning"
    label="Input label"
    message="This is a warning caption under a text input."
    messageIcon="info">(<>
        <DsFormControl.Description>
            This is a description text. It&apos;s an optional and will not exceeds more than 2 rows. A{' '}
            <button
                type="button"
                style={{
                    background: 'none',
                    border: 'none',
                    padding: 0,
                    margin: 0,
                    cursor: 'pointer',
                    color: 'var(--color-dap-blue-600)',
                    textDecoration: 'underline',
                    fontSize: 'inherit',
                    fontFamily: 'inherit',
                }}
                onClick={() => alert('Learn more clicked!')}
            >
                Learn more
            </button>{' '}
            can be added.
        </DsFormControl.Description>
        <DsFormControl.TextInput />
    </>)</DsFormControl>;
Disabled story ok
const Disabled = () => <DsFormControl label="Input label" required><DsFormControl.TextInput placeholder="Disabled Input" disabled /></DsFormControl>;

DsFormControl.DateInput

components-formcontrol-dateinput-deprecated · ./src/components/ds-form-control/stories/ds-form-control-date-input.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 9 prop types
Component: src/components/ds-form-control/ds-form-control.tsx::default
Props:
/**
 * Additional CSS class names
 */
className?: string

/**
 * Unique identifier for the control
 */
id?: string

/**
 * Label text
 */
label: string

/**
 * Message under the control
 */
message?: string

/**
 * Icon shown next to message
 */
messageIcon?: IconName | FunctionComponent<SVGProps<SVGSVGElement>> = 'info'

/**
 * Marks the field as required
 */
required?: boolean = false

/**
 * Optional render slots for customizing parts of the control.
 */
slots?: {
	/**
	 * Adornment to display at the end of the label
	 */
	endAdornment?: React.ReactNode;
}

/**
 * Visual status
 */
status?: (typeof controlStatuses)[number]

/**
 * Additional styles to apply to the component
 */
style?: React.CSSProperties
Imports
import { DefaultDescription, DsFormControl, DsIcon } from "@drivenets/design-system";
Default story ok
const Default = () => <DsFormControl
    label="Event Date"
    required
    style={{ width: '400px' }}
    message="Select a date for your event">
    <DsFormControl.DateInput />
</DsFormControl>;
With Custom Width story ok
const WithCustomWidth = () => <DsFormControl label="Event Date" required style={{ width: '400px' }}>
    <DsFormControl.DateInput />
</DsFormControl>;
With Custom Styles story ok
const WithCustomStyles = () => <DsFormControl
    label="Event Date"
    required
    style={{
        width: '400px',
        padding: '16px',
        border: '2px solid #e0e0e0',
        borderRadius: '8px',
        backgroundColor: '#f9f9f9',
    }}>
    <DsFormControl.DateInput />
</DsFormControl>;
With Description story ok
const WithDescription = () => <DsFormControl label="Event Date" required style={{ width: '400px' }}>
    <DsFormControl.Description>
        <DefaultDescription />
    </DsFormControl.Description>
    <DsFormControl.DateInput />
</DsFormControl>;
With Help Icon story ok
const WithHelpIcon = () => <DsFormControl
    label="Event Date"
    required
    style={{ width: '400px' }}
    slots={{
        endAdornment: (
            <button
                type="button"
                className={styles.helpIcon}
                onClick={() => alert('Help clicked!')}
                aria-label="Help"
            >
                <DsIcon icon="info" size="small" />
            </button>
        ),
    }}>
    <DsFormControl.Description>
        <DefaultDescription />
    </DsFormControl.Description>
    <DsFormControl.DateInput />
</DsFormControl>;
Success story ok
const Success = function Render() {
    const [value] = useState('2024-12-25');

    return (
        <div style={{ width: '400px' }}>
            <DsFormControl
                status="success"
                label="Event Date"
                message="Valid date selected."
                messageIcon="check_circle"
            >
                <DsFormControl.Description>
                    <DefaultDescription />
                </DsFormControl.Description>
                <DsFormControl.DateInput value={value} />
            </DsFormControl>
        </div>
    );
};
Error story ok
const Error = () => <DsFormControl
    status="error"
    label="Event Date"
    required
    message="Date is required."
    messageIcon="error"
    style={{ width: '400px' }}>
    <DsFormControl.Description>
        <DefaultDescription />
    </DsFormControl.Description>
    <DsFormControl.DateInput />
</DsFormControl>;
Warning story ok
const Warning = function Render() {
    const [value] = useState('2024-12-25');

    return (
        <div style={{ width: '400px' }}>
            <DsFormControl
                status="warning"
                label="Event Date"
                message="Date is approaching deadline."
                messageIcon="info"
            >
                <DsFormControl.Description>
                    <DefaultDescription />
                </DsFormControl.Description>
                <DsFormControl.DateInput value={value} />
            </DsFormControl>
        </div>
    );
};
Disabled story ok
const Disabled = () => <DsFormControl label="Event Date" style={{ width: '400px' }}>
    <DsFormControl.Description>
        <DefaultDescription />
    </DsFormControl.Description>
    <DsFormControl.DateInput disabled />
</DsFormControl>;
Range Mode story ok
const RangeMode = () => <DsFormControl
    label="Date Range"
    required
    message="Select start and end dates"
    style={{ width: '400px' }}>
    <DsFormControl.DateInput range />
</DsFormControl>;
Range With Validation story ok
const RangeWithValidation = 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 (
        <div style={{ width: '400px' }}>
            <DsFormControl
                label="Date Range"
                required
                status={error ? 'error' : undefined}
                messageIcon="cancel"
                message={error}
            >
                <DsFormControl.DateInput
                    value={value}
                    onValueChange={(value) => {
                        setValue(value);
                        setTouched(true);
                    }}
                    range
                />
            </DsFormControl>
        </div>
    );
};
With Validation story ok
const WithValidation = function Render() {
    const [value, setValue] = useState<string>();
    const [touched, setTouched] = useState(false);
    const error = touched && !value ? 'Date is required' : undefined;

    return (
        <div style={{ width: '400px' }}>
            <DsFormControl
                label="Event Date"
                required
                status={error ? 'error' : undefined}
                messageIcon="cancel"
                message={error}
            >
                <DsFormControl.DateInput
                    value={value}
                    onValueChange={(value) => {
                        setValue(value);
                        setTouched(true);
                    }}
                />
            </DsFormControl>
        </div>
    );
};

DsGrid

components-grid · ./src/components/ds-grid/ds-grid.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 7 prop types
Component: src/components/ds-grid/ds-grid.tsx::DsGridBase
Props:
children: ReactNode

className?: string

/**
 * Number of columns in the grid (defaults to 12)
 */
columns?: 2 | 4 | 6 | 8 | 10 | 12

/**
 * Gap between grid cells.
 * A number is treated as px. A string is used as-is (e.g. `"1rem"`, `"var(--grid-gutter)"`).
 * CSS default (when omitted): `16px` — set by `$gutter` in `_grid-variables.scss`.
 */
gutter?: number | string

/**
 * Padding around the grid container.
 * A number is treated as px (uniform). A string is used as-is (e.g. `"16px 20px"`, `"var(--grid-margin)"`).
 * CSS default (when omitted): `16px 20px` — set by `$margin-y $margin-x` in `_grid-variables.scss`.
 */
margin?: number | string

/**
 * Number of rows in the grid (defaults to 1)
 */
rows?: 1 | 2 | 4 | 6 | 8

style?: CSSProperties
Imports
import { DsButton, DsGrid, DsGridItem } from "@drivenets/design-system";
Default story ok
const Default = () => <DsGrid className="my-grid" rows={6}>(<>
        <DsGridItem className="card" colSpan={4}>
            <div>Element 1</div>
        </DsGridItem>
        <DsGridItem className="card" colSpan={4} rowSpan={2}>
            <div>Element 2</div>
        </DsGridItem>
        <DsGridItem className="card" colSpan={4} rowSpan={2}>
            <div>Element 3</div>
        </DsGridItem>
        <DsGridItem className="card" colSpan={4} rowSpan={2}>
            <div>Element 4</div>
        </DsGridItem>
        <DsGridItem className="card" colSpan={4} rowSpan={2} rowStart={5} colStart={2}>
            <div>Element 4</div>
        </DsGridItem>
    </>)</DsGrid>;
Responsive story ok
const Responsive = () => <DsGrid
    className="my-grid"
    gutter={{ lg: 16, md: 8 }}
    margin={{ lg: '16px 20px', md: 8 }}
    rows={4}>(<>
        <DsGridItem className="card" colSpan={{ lg: 4, md: 6 }}>
            <div>Card 1 — lg:4 md:6</div>
        </DsGridItem>
        <DsGridItem className="card" colSpan={{ lg: 4, md: 6 }}>
            <div>Card 2 — lg:4 md:6</div>
        </DsGridItem>
        <DsGridItem className="card" colSpan={{ lg: 4, md: 12 }}>
            <div>Card 3 — lg:4 md:12</div>
        </DsGridItem>
        <DsGridItem className="card" colSpan={{ lg: 6, md: 12 }} rowSpan={{ lg: 2, md: 1 }}>
            <div>Card 4 — lg:6×2 md:12×1</div>
        </DsGridItem>
        <DsGridItem className="card" colSpan={{ lg: 6, md: 12 }}>
            <div>Card 5 — lg:6 md:12</div>
        </DsGridItem>
    </>)</DsGrid>;
Navigation Layout story ok
const NavigationLayout = () => {
    const [isCollapsed, setIsCollapsed] = useState(false);

    return (
        <div className="layout">
            <div className="header">
                <div className="logo">
                    <DsButton
                        className="toggle"
                        design="v1.2"
                        onClick={() => setIsCollapsed(!isCollapsed)}>Toggle
                                                </DsButton>
                </div>
                <div className="title" />
            </div>
            <div className="main">
                <div className={`sidebar ${isCollapsed ? 'collapsed' : ''}`}>
                    <div className="sidebar-content" />
                </div>
                <main className="content-wrapper">
                    <DsGrid className="navigation-grid" rows={4}><>
                            <DsGridItem className="card" colSpan={4}>
                                <div>Dashboard Card 1</div>
                            </DsGridItem>
                            <DsGridItem className="card" colSpan={4}>
                                <div>Dashboard Card 2</div>
                            </DsGridItem>
                            <DsGridItem className="card" colSpan={4}>
                                <div>Dashboard Card 3</div>
                            </DsGridItem>
                            <DsGridItem className="card" colSpan={6}>
                                <div>Dashboard Card 4</div>
                            </DsGridItem>
                            <DsGridItem className="card" colSpan={6}>
                                <div>Dashboard Card 5</div>
                            </DsGridItem>
                        </></DsGrid>
                </main>
            </div>
        </div>
    );
};

DsIcon

components-icon · ./src/components/ds-icon/ds-icon.stories.tsx
Design system Icon component that renders Google Material Icons, custom SVG icons, or inline SVGs
Prop types (react-docgen) 8 prop types
Component: src/components/ds-icon/ds-icon.tsx::default
Props:
/**
 * Additional CSS class names
 */
className?: string = ''

/**
 * Icon color. Semantic names map to `--icon-*` tokens (e.g. `'error'` →
 * `var(--icon-error)`); raw CSS values (hex, rgb, hsl, CSS variables)
 * pass through unchanged. Omit to inherit from parent.
 */
color?: IconColor | (string & {})

/**
 * Whether the icon should be filled
 * @default false
 */
filled?: boolean

/**
 * The icon to display. This can be a Material Icon name (e.g., 'home') or an SVG component.
 */
icon: IconName | FunctionComponent<SVGProps<SVGSVGElement>>

/**
 * Optional click handler
 */
onClick?: (event: MouseEvent<HTMLSpanElement | SVGSVGElement>) => void

/**
 * The size of the icon
 * @default 'medium'
 */
size?: (typeof iconSizes)[number] = 'medium'

/**
 * Additional styles to apply to the icon
 */
style?: CSSProperties = {}

/**
 * The variant of the Material Icon
 * @default 'outlined'
 */
variant?: (typeof iconVariants)[number] = 'outlined'
Imports
import { DsIcon } from "@drivenets/design-system";
Default story ok
const Default = () => <DsIcon icon="home" size="medium" />;
Colored story ok
const Colored = () => {
    return (
        <div style={{ display: 'flex', gap: 16 }}>
            <DsIcon size="medium" filled icon="check_circle" color="success" />
            <DsIcon size="medium" filled icon="error" color="error" />
            <DsIcon size="medium" filled icon="warning" color="warning" />
            <DsIcon size="medium" filled icon="info" color="information-main" />
            <DsIcon size="medium" filled icon="special-market" color="action" />
        </div>
    );
};
Showcase story ok
const Showcase = function Render(args) {
    const { size, variant, filled } = args;
    const [searchTerm, setSearchTerm] = useState('');
    const [iconsByCategory, setIconsByCategory] = useState<IconsByCategory>({});
    const [isLoading, setIsLoading] = useState(true);
    const [usedFallback, setUsedFallback] = useState(false);

    // Process icon data into categories
    const processIconData = (data: Record<string, number>) => {
        const categorizedIcons: IconsByCategory = {};

        Object.keys(data).forEach((key) => {
            // Skip any entries starting with "symbols::"
            if (key.startsWith('symbols::')) {
                return;
            }

            // Key format is "category::iconName"
            const [category, iconName] = key.split('::');

            if (!category || !iconName) {
                return;
            }

            if (!categorizedIcons[category]) {
                categorizedIcons[category] = [];
            }

            if (iconName) {
                categorizedIcons[category].push(iconName as IconName);
            }
        });

        // Sort icon names within each category
        Object.values(categorizedIcons).forEach((icons) => {
            icons.sort();
        });

        return categorizedIcons;
    };

    useEffect(() => {
        const fetchIcons = async () => {
            try {
                setIsLoading(true);

                // Try to fetch from GitHub first
                const response = await fetch(ICONS_URL);

                if (response.ok) {
                    const data = (await response.json()) as Record<string, number>;
                    setIconsByCategory(processIconData(data));
                } else {
                    throw new Error('Failed to fetch icons from GitHub');
                }
            } catch (err) {
                console.error('Error fetching icons:', err);

                // Use fallback data if fetch fails (CORS or other issues)
                setIconsByCategory(processIconData(materialIcons));
                setUsedFallback(true);
            } finally {
                setIsLoading(false);
            }
        };

        void fetchIcons();
    }, []);

    const filteredCategories = useMemo(() => {
        const result: IconsByCategory = {};

        Object.entries(iconsByCategory).forEach(([category, icons]) => {
            const filteredIcons = icons.filter(
                (icon) =>
                    icon.toLowerCase().includes(searchTerm.toLowerCase()) ||
                    category.toLowerCase().includes(searchTerm.toLowerCase()),
            );

            if (filteredIcons.length > 0) {
                result[category] = filteredIcons;
            }
        });

        return result;
    }, [iconsByCategory, searchTerm]);

    const handleIconClick = async (iconName: string) => {
        // Copy icon name to clipboard
        await navigator.clipboard.writeText(iconName);

        // Create a temporary element for notification
        const notification = document.createElement('div');
        notification.className = 'copy-notification';
        notification.textContent = `Copied: ${iconName}`;
        document.body.appendChild(notification);

        // Remove after animation
        setTimeout(() => {
            document.body.removeChild(notification);
        }, 2000);
    };

    if (isLoading) {
        return <div className="loading">Loading material icons...</div>;
    }

    return (
        <div className="container">
            <div className="search-container">
                <input
                    type="text"
                    placeholder="Search icons by name or category..."
                    className="search"
                    value={searchTerm}
                    onChange={(e) => setSearchTerm(e.target.value)}
                />
                <div className="icon-count">
                    {Object.values(filteredCategories).reduce((total, icons) => total + icons.length, 0)} icons found
                </div>
            </div>

            {usedFallback && (
                <div className="fallback-notice">
                    Using limited icon set due to CORS restrictions. For production use, consider creating a proxy API
                    endpoint or copying the icon data to your project.
                </div>
            )}

            {Object.keys(filteredCategories).length === 0 ? (
                <div className="no-results">No icons found matching &quot;{searchTerm}&quot;</div>
            ) : (
                Object.entries(filteredCategories)
                    .sort(([a], [b]) => a.localeCompare(b))
                    .map(([category, icons]) => (
                        <div key={category} className="category-section">
                            <h2 className="category-title">{category}</h2>
                            <div className="results">
                                {icons.map((iconName) => (
                                    <button
                                        key={`${category}-${iconName}`}
                                        className="icon-wrapper"
                                        onClick={() => handleIconClick(iconName)}
                                        title={`Click to copy: ${iconName}`}
                                    >
                                        <DsIcon icon={iconName} size={size} variant={variant} filled={filled} />
                                        <span className="icon-name">{iconName}</span>
                                    </button>
                                ))}
                            </div>
                        </div>
                    ))
            )}
        </div>
    );
};
Custom Icons story ok
const CustomIcons = function Render(args) {
    const { size } = args;
    const [searchTerm, setSearchTerm] = useState('');

    const filteredCategories = useMemo(() => {
        const result: Record<string, CustomIconName[]> = {};

        Object.entries(customIconCategories).forEach(([category, icons]) => {
            const filteredIcons = icons.filter(
                (icon) =>
                    icon.toLowerCase().includes(searchTerm.toLowerCase()) ||
                    category.toLowerCase().includes(searchTerm.toLowerCase()),
            );

            if (filteredIcons.length > 0) {
                result[category] = filteredIcons;
            }
        });

        return result;
    }, [searchTerm]);

    const handleIconClick = async (iconName: string) => {
        await navigator.clipboard.writeText(iconName);

        const notification = document.createElement('div');
        notification.className = 'copy-notification';
        notification.textContent = `Copied: ${iconName}`;
        document.body.appendChild(notification);

        setTimeout(() => {
            document.body.removeChild(notification);
        }, 2000);
    };

    const totalIcons = Object.keys(customIcons).length;

    return (
        <div className="container">
            <div className="search-container">
                <input
                    type="text"
                    placeholder="Search custom icons..."
                    className="search"
                    value={searchTerm}
                    onChange={(e) => setSearchTerm(e.target.value)}
                />
                <div className="icon-count">
                    {Object.values(filteredCategories).reduce((total, icons) => total + icons.length, 0)} of{' '}
                    {totalIcons} custom icons
                </div>
            </div>

            <div className="fallback-notice">
                These are custom DriveNets-specific icons. Use the &quot;special-&quot; prefix to distinguish them
                from Material Icons.
            </div>

            {Object.keys(filteredCategories).length === 0 ? (
                <div className="no-results">No icons found matching &quot;{searchTerm}&quot;</div>
            ) : (
                Object.entries(filteredCategories).map(([category, icons]) => (
                    <div key={category} className="category-section">
                        <h2 className="category-title">{category}</h2>
                        <div className="results">
                            {icons.map((iconName) => (
                                <button
                                    key={iconName}
                                    className="icon-wrapper"
                                    onClick={() => handleIconClick(iconName)}
                                    title={`Click to copy: ${iconName}`}
                                >
                                    <DsIcon icon={iconName} size={size} />
                                    <span className="icon-name">{iconName}</span>
                                </button>
                            ))}
                        </div>
                    </div>
                ))
            )}
        </div>
    );
};

DsKeyValuePair

components-keyvaluepair · ./src/components/ds-key-value-pair/ds-key-value-pair.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 8 prop types
Component: src/components/ds-key-value-pair/ds-key-value-pair.tsx::default
Props:
className?: string

/**
 * Editor content revealed on hover/focus when not read-only.
 */
editInput?: ReactNode

/**
 * Label rendered for the key side of the pair (left or top depending on `orientation`).
 */
keyLabel: ReactNode

/**
 * Layout orientation:
 * - `vertical`: key stacked above value
 * - `horizontal`: key and value side-by-side
 * @default 'vertical'
 */
orientation?: (typeof dsKeyValuePairOrientations)[number] = 'vertical'

/**
 * When `true`, the pair is rendered as a static display and the editor is never
 * revealed (even on hover/focus).
 * @default false
 */
readOnly?: boolean = false

ref?: Ref<HTMLDivElement>

style?: CSSProperties

/**
 * Shown when the editor is not active (read-only or not hovered).
 */
value?: ReactNode
Imports
import {
    DsIcon,
    DsKeyValuePair,
    DsSelect,
    DsTag,
    DsTextarea,
    DsTextInput,
    DsTooltip,
} from "@drivenets/design-system";
Read Only Vertical story ok
const ReadOnlyVertical = () => <DsKeyValuePair
    keyLabel="Start time"
    value="2024-05-23 16:47"
    readOnly
    orientation="vertical" />;
Read Only Horizontal story ok
const ReadOnlyHorizontal = () => <DsKeyValuePair
    keyLabel="MAC"
    value="00:1A:2B:3C:4D:5E"
    readOnly
    orientation="horizontal" />;
Custom Label story ok
const CustomLabel = () => <DsKeyValuePair
    keyLabel={(<span className={storyStyles.iconLabel}>
        <DsIcon icon="info" size="tiny" />Serial Number
                    </span>)}
    value="99887766"
    readOnly
    orientation="horizontal" />;
Editable Vertical story ok
const EditableVertical = function Render() {
    const [serial, setSerial] = useState('99887766');

    return (
        <DsKeyValuePair
            keyLabel="Serial Number"
            value={serial}
            orientation="vertical"
            className={storyStyles.editableVerticalDemo}
            editInput={<DsTextInput value={serial} onValueChange={setSerial} size="small" />}
        />
    );
};
Editable Horizontal story ok
const EditableHorizontal = function Render() {
    const [model, setModel] = useState('Cisco RTR-X2000');

    return (
        <DsKeyValuePair
            keyLabel="Model"
            value={model}
            orientation="horizontal"
            className={storyStyles.editableHorizontalDemo}
            editInput={<DsTextInput value={model} onValueChange={setModel} size="small" />}
        />
    );
};
Editable with trailing icon + tooltip story ok
const WithTrailingIcon = function Render() {
    const [val, setVal] = useState('Editable value');

    return (
        <DsKeyValuePair
            keyLabel="Editable"
            orientation="horizontal"
            className={storyStyles.editableHorizontalDemo}
            value={
                <span className={storyStyles.valueWithIcon}>
                    {val}
                    <DsTooltip content="Additional info about this field">
                        <DsIcon icon="info" size="tiny" />
                    </DsTooltip>
                </span>
            }
            editInput={
                <span className={storyStyles.valueWithIcon}>
                    <DsTextInput
                        value={val}
                        onValueChange={setVal}
                        size="small"
                        className={storyStyles.mediumInput}
                    />
                    <DsTooltip content="Additional info about this field">
                        <DsIcon icon="info" size="tiny" />
                    </DsTooltip>
                </span>
            }
        />
    );
};
Group story ok
const Group = function Render() {
    const [serial, setSerial] = useState('99887766');
    const [manufacturer, setManufacturer] = useState('cisco');

    return (
        <div className={storyStyles.pairsColumn}>
            <DsKeyValuePair keyLabel="MAC" value="00:1A:2B:3C:4D:5E" readOnly orientation="horizontal" />
            <DsKeyValuePair
                keyLabel="SN"
                value={serial}
                orientation="horizontal"
                editInput={
                    <DsTextInput
                        value={serial}
                        onValueChange={setSerial}
                        size="small"
                        className={storyStyles.narrowInput}
                    />
                }
            />
            <DsKeyValuePair keyLabel="Model" value="Cisco RTR-X2000" readOnly orientation="horizontal" />
            <DsKeyValuePair
                keyLabel="MFR"
                value={MANUFACTURER_OPTIONS.find((o) => o.value === manufacturer)?.label ?? manufacturer}
                orientation="horizontal"
                editInput={
                    <DsSelect
                        options={MANUFACTURER_OPTIONS}
                        value={manufacturer}
                        onValueChange={setManufacturer}
                        size="small"
                    />
                }
            />
        </div>
    );
};
Responsive container width story ok
const ResponsiveWidth = function Render() {
    const [width, setWidth] = useState(DEFAULT_WIDTH);
    const [serial, setSerial] = useState('99887766');
    const [description, setDescription] = useState(LONG_TEXT);

    return (
        <div className={storyStyles.responsiveDemo}>
            <div className={storyStyles.responsiveSlider}>
                <span>{MIN_WIDTH}px</span>
                <input
                    type="range"
                    min={MIN_WIDTH}
                    max={MAX_WIDTH}
                    value={width}
                    onChange={(e) => setWidth(Number(e.target.value))}
                    className={storyStyles.responsiveSliderInput}
                />
                <span>{MAX_WIDTH}px</span>
                <span>({width}px)</span>
            </div>

            <div className={storyStyles.responsivePairs} style={{ width }}>
                <DsKeyValuePair keyLabel="MAC" value="00:1A:2B:3C:4D:5E" readOnly orientation="horizontal" />
                <DsKeyValuePair
                    keyLabel="Serial Number"
                    value={serial}
                    orientation="horizontal"
                    editInput={
                        <DsTextInput
                            value={serial}
                            onValueChange={setSerial}
                            size="small"
                            className={storyStyles.narrowInput}
                        />
                    }
                />
                <DsKeyValuePair keyLabel="Model" value="Cisco RTR-X2000" readOnly orientation="horizontal" />
                <DsKeyValuePair
                    keyLabel="Firmware Version"
                    value="v4.2.1-build.2847"
                    readOnly
                    orientation="horizontal"
                />
                <DsKeyValuePair
                    keyLabel="Description"
                    value={description}
                    orientation="horizontal"
                    editInput={
                        <DsTextarea
                            value={description}
                            onValueChange={setDescription}
                            rows={4}
                            className={storyStyles.descriptionTextarea}
                        />
                    }
                />
            </div>
        </div>
    );
};
Value types (Figma reference) story ok
const ValueTypes = function Render() {
    const [editable, setEditable] = useState('Editable value');
    const [manufacturer, setManufacturer] = useState('cisco');
    const [description, setDescription] = useState(LONG_TEXT);
    const [empty, setEmpty] = useState('');

    return (
        <div className={storyStyles.pairsColumn}>
            <DsKeyValuePair keyLabel="Read-only" value="Read only value" readOnly orientation="horizontal" />

            <DsKeyValuePair
                keyLabel="Editable"
                value={editable}
                orientation="horizontal"
                editInput={
                    <DsTextInput
                        value={editable}
                        onValueChange={setEditable}
                        size="small"
                        className={storyStyles.mediumInput}
                    />
                }
            />

            <DsKeyValuePair
                keyLabel="MFR"
                value={MANUFACTURER_OPTIONS.find((o) => o.value === manufacturer)?.label ?? manufacturer}
                orientation="horizontal"
                editInput={
                    <DsSelect
                        options={MANUFACTURER_OPTIONS}
                        value={manufacturer}
                        onValueChange={setManufacturer}
                        size="small"
                    />
                }
            />

            <DsKeyValuePair
                keyLabel="Status"
                value={
                    <span className={storyStyles.statusBadge}>
                        <DsIcon icon="check_circle" size="tiny" />
                        Active
                    </span>
                }
                readOnly
                orientation="horizontal"
            />

            <DsKeyValuePair
                keyLabel="Tags"
                value={
                    <span className={storyStyles.tagGroup}>
                        <DsTag label="Tag-name" size="small" />
                        <DsTag label="Tag-name" size="small" />
                        <DsTag label="Tag-name" size="small" />
                    </span>
                }
                readOnly
                orientation="horizontal"
            />

            <DsKeyValuePair
                keyLabel="Description"
                value={<span className={storyStyles.fullTextValue}>{description}</span>}
                orientation="horizontal"
                editInput={
                    <DsTextarea
                        value={description}
                        onValueChange={setDescription}
                        rows={4}
                        className={storyStyles.descriptionTextarea}
                    />
                }
            />

            <DsKeyValuePair
                keyLabel="Empty Value"
                value={empty || undefined}
                orientation="horizontal"
                editInput={<DsTextInput value={empty} onValueChange={setEmpty} size="small" />}
            />
        </div>
    );
};

DsLoader

components-loader · ./src/components/ds-loader/ds-loader.stories.tsx
Design system Loader component
Prop types (react-docgen) 4 prop types
Component: src/components/ds-loader/ds-loader.tsx::default
Props:
/**
 * Additional CSS class names
 */
className?: string

/**
 * Ref to the loader container element
 */
ref?: React.Ref<HTMLDivElement>

/**
 * Additional styles to apply to the component
 */
style?: React.CSSProperties

/**
 * Loader variant
 * @default 'spinner'
 */
variant?: (typeof loaderVariants)[number] = 'spinner'
Imports
import { DsLoader } from "@drivenets/design-system";
Default story ok
Default loader - rotating spinner arc
const Default = () => <DsLoader />;
Pulsing story ok
Pulsing loader - inner circle stays static, outer circle pulsates
const Pulsing = () => <DsLoader variant="pulsing" />;
Inline With Text story ok
Loader displayed inline with text
const InlineWithText = () => (
    <div className={styles.inlineContainer}>
        <DsLoader />
        <span>Loading...</span>
    </div>
);
Variant Comparison story ok
Comparison of both loader variants
const VariantComparison = () => (
    <div className={styles.variantComparisonContainer}>
        <div className={styles.variantItem}>
            <DsLoader variant="spinner" />
            <span className={styles.variantLabel}>Spinner</span>
        </div>
        <div className={styles.variantItem}>
            <DsLoader variant="pulsing" />
            <span className={styles.variantLabel}>Pulsing</span>
        </div>
    </div>
);
Usage Examples story ok
Multiple loaders in different contexts
const UsageExamples = () => (
    <div className={styles.usageExamplesContainer}>
        <div>
            <h4 className={styles.usageSection}>Inline with text</h4>
            <div className={styles.inlineContainer}>
                <DsLoader />
                <span>Processing request...</span>
            </div>
        </div>
        <div>
            <h4 className={styles.usageSection}>Centered in container</h4>
            <div className={styles.centeredContainer}>
                <DsLoader />
            </div>
        </div>
        <div>
            <h4 className={styles.usageSection}>Multiple loaders</h4>
            <div className={styles.multipleLoadersContainer}>
                <DsLoader />
                <DsLoader />
                <DsLoader />
            </div>
        </div>
    </div>
);
Custom Props story ok
Test that custom props (className, style) are forwarded to the container
const CustomProps = () => <DsLoader className={styles.customPropsLoader} />;

DsModal

components-modal · ./src/components/ds-modal/ds-modal.stories.tsx
Composable modal dialog. Supports custom header, footer, and body content with grid-based sizing. Use columns prop to control modal width (1-12 grid columns).
Prop types (react-docgen) 10 prop types
Component: src/components/ds-modal/ds-modal.tsx::default
Props:
/**
 * Modal body content
 */
children: ReactNode

/**
 * Additional CSS class name
 */
className?: string

/**
 * Whether pressing the Escape key closes the modal.
 * @default true
 */
closeOnEscape?: DialogProps['closeOnEscape']

/**
 * Whether clicking or focusing outside the modal closes it.
 * @default true
 */
closeOnInteractOutside?: DialogProps['closeOnInteractOutside'] = false

/**
 * Number of grid columns for modal width
 * @default 6
 */
columns?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 = 6

/**
 * Show full-width dividers between header, body, and footer sections
 * @default false
 */
dividers?: boolean = false

/**
 * Whether the modal traps focus and blocks interaction with the page behind the
 * backdrop. When `false`, users can continue interacting with the underlying page.
 * @default true
 */
modal?: DialogProps['modal'] = true

/**
 * Callback when modal open state changes
 * @param open - whether the modal is open
 */
onOpenChange: (open: boolean) => void

/**
 * Whether the modal is open
 */
open: boolean

/**
 * Optional inline styles to apply to the component
 */
style?: CSSProperties
Imports
import { Controller, FormProvider } from "react-hook-form";
import { Dialog, DsButton, DsCheckbox, DsFormControl, DsIcon, DsModal, DsRadioGroup } from "@drivenets/design-system";
Default story ok
const Default = function Render() {
    const [isOpen, setIsOpen] = useState(false);

    return (
        <div className={styles.storyContainer}>
            <div className={styles.storyHeader}>
                <h2>Default Modal</h2>
                <p>The default variant has an inset header underline and no footer border.</p>
                <DsButton design="v1.2" size="large" onClick={() => setIsOpen(true)}>
                    Open Modal
                </DsButton>
            </div>

            <DsModal open={isOpen} columns={4} onOpenChange={setIsOpen}>
                <DsModal.Header>
                    <DsModal.Title>Modal Title</DsModal.Title>
                    <DsModal.CloseTrigger />
                </DsModal.Header>
                <DsModal.Body>
                    <p>This is the default modal variant with simple content.</p>
                </DsModal.Body>
                <DsModal.Footer>
                    <DsModal.Actions>
                        <DsButton design="v1.2" buttonType="secondary" size="large" onClick={() => setIsOpen(false)}>
                            Cancel
                        </DsButton>
                        <DsButton design="v1.2" variant="filled" size="large" onClick={() => setIsOpen(false)}>
                            Confirm
                        </DsButton>
                    </DsModal.Actions>
                </DsModal.Footer>
            </DsModal>
        </div>
    );
};
Divided story ok
const Divided = function Render() {
    const [isOpen, setIsOpen] = useState(false);
    const [submittedData, setSubmittedData] = useState<ModalFormValues | null>(null);

    const methods = useForm<ModalFormValues>({
        resolver: zodResolver(modalFormSchema),
        defaultValues: defaultFormValues,
        mode: 'onChange',
    });

    const {
        register,
        handleSubmit,
        formState: { errors, isValid, touchedFields, isDirty },
        setValue,
        watch,
        trigger,
        reset,
        control,
    } = methods;

    const onSubmit: SubmitHandler<ModalFormValues> = (data: ModalFormValues) => {
        setSubmittedData(data);
        setIsOpen(false);
        reset(defaultFormValues);
    };

    const handleReset = () => {
        reset(defaultFormValues);
    };

    const handleValueChange = (field: keyof ModalFormValues, value: string | boolean | null) => {
        setValue(field, value === 'indeterminate' ? false : (value as ModalFormValues[typeof field]), {
            shouldValidate: true,
            shouldTouch: true,
            shouldDirty: true,
        });
    };

    return (
        <div className={styles.storyContainer}>
            <div className={styles.storyHeader}>
                <h2>Interactive Form Modal Demo</h2>
                <p>
                    Click the button below to open a form modal. Fill out the form and click &#34;Save Changes&#34; to
                    see the results displayed here.
                </p>
                <DsButton design="v1.2" size="large" onClick={() => setIsOpen(true)}>
                    Open Form Modal
                </DsButton>
            </div>

            {submittedData && (
                <div className={styles.formResults}>
                    <h3 className={styles.formResultsTitle}>Form Results:</h3>
                    <div className={styles.formResultsGrid}>
                        <div>
                            <strong>Name:</strong> {submittedData.name}
                        </div>
                        <div>
                            <strong>Email:</strong> {submittedData.email}
                        </div>
                        <div>
                            <strong>Department:</strong> {submittedData.department}
                        </div>
                        <div>
                            <strong>Role:</strong> {submittedData.role || 'Not specified'}
                        </div>
                        <div>
                            <strong>Description:</strong> {submittedData.description}
                        </div>
                        <div>
                            <strong>Subscription:</strong> {submittedData.subscription}
                        </div>
                        <div>
                            <strong>Terms Accepted:</strong> {submittedData.acceptTerms ? 'Yes' : 'No'}
                        </div>
                    </div>
                    <DsButton
                        design="v1.2"
                        buttonType="secondary"
                        size="small"
                        onClick={() => setSubmittedData(null)}
                        className={styles.clearResultsButton}
                    >
                        Clear Results
                    </DsButton>
                </div>
            )}

            <DsModal open={isOpen} columns={8} dividers onOpenChange={setIsOpen}>
                <DsModal.Header>
                    <DsModal.Title>User Profile Form</DsModal.Title>
                    <DsModal.CloseTrigger />
                </DsModal.Header>
                <DsModal.Body>
                    <FormProvider {...methods}>
                        <div className={styles.formSection}>
                            {/* Basic Information Section */}
                            <div>
                                <h3 className={styles.formSectionTitle}>Basic Information</h3>
                                <div className={styles.formGrid}>
                                    <DsFormControl
                                        label="Full Name"
                                        required
                                        status="error"
                                        messageIcon="cancel"
                                        message={touchedFields.name ? errors.name?.message : undefined}
                                    >
                                        <Controller
                                            name="name"
                                            control={control}
                                            render={({ field }) => (
                                                <DsFormControl.TextInput
                                                    value={field.value}
                                                    placeholder="Enter full name"
                                                    onChange={(event) => handleValueChange('name', event.target.value)}
                                                    onBlur={(event) => handleValueChange('name', event.target.value)}
                                                />
                                            )}
                                        />
                                    </DsFormControl>
                                    <DsFormControl
                                        label="Email Address"
                                        required
                                        status="error"
                                        messageIcon="cancel"
                                        message={touchedFields.email ? errors.email?.message : undefined}
                                    >
                                        <Controller
                                            name="email"
                                            control={control}
                                            render={({ field }) => (
                                                <DsFormControl.TextInput
                                                    type="email"
                                                    value={field.value}
                                                    placeholder="Enter email address"
                                                    onChange={(event) => handleValueChange('email', event.target.value)}
                                                    onBlur={(event) => handleValueChange('email', event.target.value)}
                                                />
                                            )}
                                        />
                                    </DsFormControl>
                                </div>
                            </div>

                            {/* Work Information Section */}
                            <div>
                                <h3 className={styles.formSectionTitle}>Work Information</h3>
                                <div className={styles.formGrid}>
                                    <DsFormControl
                                        label="Department"
                                        required
                                        status="error"
                                        messageIcon="cancel"
                                        message={touchedFields.department ? errors.department?.message : undefined}
                                    >
                                        <Controller
                                            name="department"
                                            control={control}
                                            render={({ field }) => (
                                                <DsFormControl.Select
                                                    value={field.value}
                                                    placeholder="Select a department"
                                                    options={[
                                                        { label: 'Engineering', value: 'engineering' },
                                                        { label: 'Product', value: 'product' },
                                                        { label: 'Design', value: 'design' },
                                                        { label: 'Marketing', value: 'marketing' },
                                                        { label: 'Sales', value: 'sales' },
                                                        { label: 'HR', value: 'hr' },
                                                    ]}
                                                    clearable
                                                    onValueChange={(value) => handleValueChange('department', value)}
                                                    onBlur={() => handleValueChange('department', field.value)}
                                                />
                                            )}
                                        />
                                    </DsFormControl>
                                    <DsFormControl
                                        label="Role"
                                        required
                                        status="error"
                                        messageIcon="cancel"
                                        message={touchedFields.role ? errors.role?.message : undefined}
                                    >
                                        <Controller
                                            name="role"
                                            control={control}
                                            render={({ field }) => (
                                                <DsFormControl.Select
                                                    value={field.value}
                                                    placeholder="Select a role"
                                                    options={[
                                                        { label: 'Manager', value: 'manager' },
                                                        { label: 'Senior', value: 'senior' },
                                                        { label: 'Mid-level', value: 'mid' },
                                                        { label: 'Junior', value: 'junior' },
                                                        { label: 'Intern', value: 'intern' },
                                                    ]}
                                                    clearable
                                                    onValueChange={(value) => handleValueChange('role', value)}
                                                    onBlur={() => handleValueChange('role', field.value)}
                                                />
                                            )}
                                        />
                                    </DsFormControl>
                                </div>
                            </div>

                            {/* Subscription Section */}
                            <div>
                                <h3 className={styles.formSectionTitle}>Subscription Plan</h3>
                                <DsRadioGroup.Root
                                    value={watch('subscription')}
                                    onValueChange={(value) => handleValueChange('subscription', value)}
                                >
                                    <DsRadioGroup.Item value="basic" label="Basic" />
                                    <DsRadioGroup.Item value="pro" label="Pro" />
                                    <DsRadioGroup.Item value="enterprise" label="Enterprise" />
                                </DsRadioGroup.Root>
                                {errors.subscription && (
                                    <span className={styles.errorMessage}>{errors.subscription.message}</span>
                                )}
                            </div>

                            {/* Additional Information Section */}
                            <div>
                                <h3 className={styles.formSectionTitle}>Additional Information</h3>
                                <DsFormControl
                                    label="Description"
                                    required
                                    status="error"
                                    messageIcon="cancel"
                                    message={touchedFields.description ? errors.description?.message : undefined}
                                >
                                    <DsFormControl.Textarea
                                        placeholder="Enter a brief description about the user (min. 20 characters)..."
                                        {...register('description', {
                                            onBlur: () => trigger('description'),
                                            onChange: () => trigger('description'),
                                        })}
                                    />
                                </DsFormControl>
                            </div>

                            {/* Terms and Conditions */}
                            <div>
                                <DsCheckbox
                                    label="I accept the terms and conditions"
                                    checked={watch('acceptTerms')}
                                    onCheckedChange={(value) => handleValueChange('acceptTerms', value)}
                                />
                                {errors.acceptTerms && (
                                    <span className={styles.errorMessage}>{errors.acceptTerms.message}</span>
                                )}
                            </div>
                        </div>
                    </FormProvider>
                </DsModal.Body>
                <DsModal.Footer>
                    <DsModal.Actions>
                        <DsButton design="v1.2" buttonType="secondary" size="large" onClick={handleReset}>
                            Reset
                        </DsButton>
                        <DsButton
                            design="v1.2"
                            size="large"
                            disabled={!isDirty || !isValid}
                            onClick={handleSubmit(onSubmit)}
                        >
                            Save Changes
                        </DsButton>
                    </DsModal.Actions>
                </DsModal.Footer>
            </DsModal>
        </div>
    );
};
Custom story ok
const Custom = function Render() {
    const [isOpen, setIsOpen] = useState(false);

    return (
        <div className={styles.storyContainer}>
            <div className={styles.storyHeader}>
                <h2>Custom Modal Demo</h2>
                <p>Click the button below to open a custom modal with custom header and footer content.</p>
                <DsButton design="v1.2" size="large" onClick={() => setIsOpen(true)}>
                    Open Custom Modal
                </DsButton>
            </div>

            <DsModal open={isOpen} columns={4} dividers onOpenChange={setIsOpen}>
                <DsModal.Header>
                    <div className={styles.customHeader}>
                        <button className={styles.headerButton}>⋯</button>
                        <Dialog.CloseTrigger className={styles.headerButton}>✕</Dialog.CloseTrigger>
                    </div>
                </DsModal.Header>
                <DsModal.Body>
                    <div className={styles.formSection}>
                        {/* First Section */}
                        <div>
                            <h3 className={styles.formSectionTitle}>Project Details</h3>
                            <div className={styles.formGrid}>
                                <div>
                                    <label htmlFor="project-name" className={styles.inputLabel}>
                                        Project Name
                                    </label>
                                    <input
                                        id="project-name"
                                        type="text"
                                        placeholder="Enter project name"
                                        className={styles.inputField}
                                    />
                                </div>
                                <div>
                                    <label htmlFor="category" className={styles.inputLabel}>
                                        Category
                                    </label>
                                    <input
                                        id="category"
                                        type="text"
                                        placeholder="Select category"
                                        className={styles.inputField}
                                    />
                                </div>
                            </div>
                        </div>

                        {/* Second Section */}
                        <div>
                            <h3 className={styles.formSectionTitle}>Team Information</h3>
                            <div className={styles.formGrid}>
                                <div>
                                    <label htmlFor="team-lead" className={styles.inputLabel}>
                                        Team Lead
                                    </label>
                                    <input
                                        id="team-lead"
                                        type="text"
                                        placeholder="Enter team lead name"
                                        className={styles.inputField}
                                    />
                                </div>
                                <div>
                                    <label htmlFor="team-size" className={styles.inputLabel}>
                                        Team Size
                                    </label>
                                    <input
                                        id="team-size"
                                        type="text"
                                        placeholder="Number of team members"
                                        className={styles.inputField}
                                    />
                                </div>
                            </div>
                        </div>

                        {/* Third Group */}
                        <div>
                            <div className={styles.formGridThree}>
                                <div>
                                    <label htmlFor="budget" className={styles.inputLabel}>
                                        Budget
                                    </label>
                                    <input
                                        id="budget"
                                        type="text"
                                        placeholder="Enter budget amount"
                                        className={styles.inputField}
                                    />
                                </div>
                                <div>
                                    <label htmlFor="timeline" className={styles.inputLabel}>
                                        Timeline
                                    </label>
                                    <input
                                        id="timeline"
                                        type="text"
                                        placeholder="Project duration"
                                        className={styles.inputField}
                                    />
                                </div>
                                <button className={styles.addButton}>+</button>
                            </div>
                        </div>

                        {/* File Upload Section */}
                        <div>
                            <h3 className={styles.formSectionTitle}>Project Documents</h3>
                            <div className={styles.fileUploadArea}>
                                <button className={styles.uploadButton}>☁️ Upload file</button>
                                <div className={styles.uploadText}>or drag and drop to upload</div>
                            </div>
                        </div>
                    </div>
                </DsModal.Body>
                <DsModal.Footer>
                    <div className={styles.customFooter}>
                        <div className={styles.statusBadge}>All done</div>
                        <DsModal.Actions className={styles.customActions}>
                            <button className={styles.footerButton}>Discard</button>
                            <button className={styles.footerButtonPrimary}>Save Changes</button>
                        </DsModal.Actions>
                    </div>
                </DsModal.Footer>
            </DsModal>
        </div>
    );
};
With Icon story ok
const WithIcon = function Render() {
    const [isOpen, setIsOpen] = useState(false);

    return (
        <div className={styles.storyContainer}>
            <div className={styles.storyHeader}>
                <h2>Modal with Icon</h2>
                <p>Example showing how to add an icon to the modal header.</p>
                <DsButton design="v1.2" size="large" onClick={() => setIsOpen(true)}>
                    Open Modal
                </DsButton>
            </div>

            <DsModal open={isOpen} columns={4} onOpenChange={setIsOpen}>
                <DsModal.Header>
                    <DsIcon icon="info" size="small" />
                    <DsModal.Title>Session Timeout</DsModal.Title>
                    <DsModal.CloseTrigger />
                </DsModal.Header>
                <DsModal.Body>
                    <p>Your session will expire in 5 minutes due to inactivity.</p>
                </DsModal.Body>
            </DsModal>
        </div>
    );
};
Without Header story ok
const WithoutHeader = function Render() {
    const [isOpen, setIsOpen] = useState(false);

    return (
        <div className={styles.storyContainer}>
            <div className={styles.storyHeader}>
                <h2>Modal Without Header</h2>
                <p>Modal with only body and footer content, no header section.</p>
                <DsButton design="v1.2" size="large" onClick={() => setIsOpen(true)}>
                    Open Modal
                </DsButton>
            </div>

            <DsModal open={isOpen} columns={4} onOpenChange={setIsOpen}>
                <DsModal.Body>
                    <p>
                        This modal has no header section. Use this pattern when you want a cleaner look without the
                        header underline.
                    </p>
                </DsModal.Body>
                <DsModal.Footer>
                    <DsModal.Actions>
                        <DsButton design="v1.2" buttonType="secondary" size="large" onClick={() => setIsOpen(false)}>
                            Close
                        </DsButton>
                        <DsButton design="v1.2" variant="filled" size="large" onClick={() => setIsOpen(false)}>
                            Continue
                        </DsButton>
                    </DsModal.Actions>
                </DsModal.Footer>
            </DsModal>
        </div>
    );
};

DsNumberInput

components-numberinput · ./src/components/ds-number-input/ds-number-input.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 16 prop types
Component: src/components/ds-number-input/ds-number-input.tsx::default
Props:
/**
 * Additional CSS class names
 */
className?: string

/**
 * The initial value of the input when rendered.
 * Use when you don't need to control the value of the input.
 */
defaultValue?: number | undefined

/**
 * Whether the number input is disabled
 */
disabled?: boolean = false

/**
 * Unique identifier for the input field
 */
id?: string

/**
 * The maximum value of the number input
 * @default Number.MAX_SAFE_INTEGER
 */
max?: number | undefined

/**
 * The minimum value of the number input
 */
min?: number | undefined

/**
 * The name of the input field
 */
name?: string

/**
 * Event handler called when the input field loses focus
 * 
 * @param event
 */
onBlur?: (event: React.FocusEvent<HTMLInputElement>) => void

/**
 * Callback when the value changes (native input onChange event)
 * This is provided for compatibility but onValueChange is preferred
 */
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void

/**
 * Value change event handler (provides just the number value)
 * This is the preferred way to handle value changes
 */
onValueChange?: (value: number) => void

/**
 * The placeholder text
 */
placeholder?: string

/**
 * The ref to the input field
 */
ref?: React.Ref<HTMLInputElement>

/**
 * The size of the input field
 * @default default
 */
size?: 'small' | 'default' = 'default'

/**
 * The amount to increment or decrement the value by
 * @default 1
 */
step?: number | undefined

/**
 * Additional styles to apply to the component
 */
style?: React.CSSProperties = {}

/**
 * The controlled value of the input
 */
value?: number | undefined
Imports
import { DsNumberInput } from "@drivenets/design-system";
Default story ok
const Default = () => <DsNumberInput placeholder="Enter number" defaultValue={0} style={{ width: '200px' }} />;
With Min Max story ok
const WithMinMax = () => <DsNumberInput
    placeholder="Enter number"
    defaultValue={50}
    min={0}
    max={100}
    step={1}
    style={{ width: '200px' }} />;
Controlled story ok
const Controlled = () => {
    const [value, setValue] = useState(42);

    return (
        <div
            style={{ display: 'flex', flexDirection: 'column', gap: '16px', alignItems: 'center' }}>
            <DsNumberInput
                placeholder="Enter number"
                min={0}
                max={100}
                step={1}
                style={{ width: '200px' }}
                value={value}
                defaultValue={0}
                onValueChange={(newValue) => setValue(newValue)} />
            <div>Current value: {value}</div>
            <button onClick={() => setValue(0)}>Reset to 0</button>
            <button onClick={() => setValue(100)}>Set to 100</button>
        </div>
    );
};
Small story ok
const Small = () => <DsNumberInput
    size="small"
    placeholder="Small number input"
    defaultValue={10}
    style={{ width: '150px' }} />;
Disabled story ok
const Disabled = () => <DsNumberInput
    placeholder="Disabled input"
    defaultValue={25}
    disabled
    style={{ width: '200px' }} />;

DsPanel

components-panel · ./src/components/ds-panel/ds-panel.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 6 prop types
Component: src/components/ds-panel/ds-panel.tsx::DsPanelBase
Props:
/**
 * Disable the default inner padding so the panel content can bleed to the edges.
 * @default false
 */
disablePadding?: boolean = false

/**
 * Whether the panel edge can be dragged to resize its width.
 * @default false
 */
draggable?: boolean = false

/**
 * Called when the panel's open (expanded) state changes. Receives the next
 * boolean state.
 */
onOpenChange?: (open: boolean) => void

/**
 * Props forwarded to nested sub-components.
 */
slotProps?: {
	/**
	 * Props forwarded to the collapse/expand button shown on the panel edge.
	 */
	collapseButton?: DsPanelCollapseButtonProps;
}

/**
 * Visual treatment of the panel:
 * - `docked`: sits flush within the surrounding layout (default)
 * - `floating`: elevated card with shadow, detached from the layout
 * @default docked
 */
variant?: 'docked' | 'floating' = 'docked'

/**
 * Width of the panel in pixels when expanded.
 */
width?: number
Imports
import { DsButton, DsNextStepButton, DsPanel, DsStep, DsStepContent, DsStepper } from "@drivenets/design-system";
Default story ok
const Default = function Render({ variant }) {
    const [open, setOpen] = useState(true);

    return (
        <>
            {!open && <DsButton onClick={() => setOpen(true)}>Open Panel</DsButton>}

            <DsPanel open={open} onOpenChange={setOpen} variant={variant}>
                <p>
                    This is a panel. It can contain any content you like, such as text, images, or other components.
                </p>

                <p>It is collapsible. Hover it to see the trigger button.</p>

                <DsButton size="small">Primary Action</DsButton>
            </DsPanel>
        </>
    );
};
Responsive story ok
const Responsive = function Render() {
    const [open, setOpen] = useState(true);

    return (
        <>
            {!open && <DsButton onClick={() => setOpen(true)}>Open Panel</DsButton>}

            <DsPanel open={open} onOpenChange={setOpen} width={{ lg: 480, md: 240 }}>
                <p>This panel uses a responsive width.</p>
                <p>Large screens: 480px. Medium screens: 240px.</p>

                <DsButton size="small">Primary Action</DsButton>
            </DsPanel>
        </>
    );
};
Draggable story ok
const Draggable = function Render() {
    const [panelVariant, setPanelVariant] = useState<DsPanelVariant>('docked');
    const [activeStep, setActiveStep] = useState(0);

    const isFloating = panelVariant === 'floating';

    const togglePanelVariant = () => {
        setPanelVariant(isFloating ? 'docked' : 'floating');
    };

    const steps = [
        { label: 'Configure network', description: 'Set up interfaces and routing policies' },
        { label: 'Assign resources', description: 'Allocate compute and storage for the deployment' },
        { label: 'Review & deploy', description: 'Verify configuration and launch' },
    ];

    return (
        <div style={{ position: 'relative', width: 600, height: 500 }}>
            <DsPanel
                open
                variant={panelVariant}
                draggable={isFloating}
                disablePadding={isFloating}
                slotProps={{
                    collapseButton: {
                        onClick: togglePanelVariant,
                        collapsed: isFloating,
                    },
                }}
            >
                <DsStepper
                    count={steps.length}
                    activeStep={activeStep}
                    onStepChange={({ step }) => setActiveStep(step)}
                    variant={isFloating ? 'single' : undefined}
                    floating={isFloating}
                >
                    {steps.map((s, index) => (
                        <DsStep index={index} key={index}>
                            <DsStepContent
                                index={index}
                                label={s.label}
                                description={s.description}
                                actions={
                                    <DsNextStepButton>{index === steps.length - 1 ? 'Deploy' : 'Next'}</DsNextStepButton>
                                }
                            />
                        </DsStep>
                    ))}
                </DsStepper>
            </DsPanel>
        </div>
    );
};

DsPasswordInput

components-passwordinput · ./src/components/ds-password-input/ds-password-input.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 13 prop types
Component: src/components/ds-password-input/ds-password-input.tsx::default
Props:
/**
 * Additional CSS class names
 */
className?: string

/**
 * The initial value of the input when rendered.
 * Use when you don't need to control the value of the input.
 */
defaultValue?: string

/**
 * Whether the password input is disabled
 */
disabled?: boolean = false

/**
 * Unique identifier for the input field
 */
id?: string

/**
 * The name of the input field
 */
name?: string

/**
 * Event handler called when the input field loses focus
 * 
 * @param event
 */
onBlur?: (event: React.FocusEvent<HTMLInputElement>) => void

/**
 * Callback when the value changes (native input onChange event)
 * This is provided for compatibility but onValueChange is preferred
 */
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void

/**
 * Value change event handler (provides just the string value)
 * This is the preferred way to handle value changes
 */
onValueChange?: (value: string) => void

/**
 * The placeholder text
 */
placeholder?: string

/**
 * The ref to the input field
 */
ref?: React.Ref<HTMLInputElement>

/**
 * The size of the input field
 * @default default
 */
size?: 'small' | 'default' = 'default'

/**
 * Additional styles to apply to the component
 */
style?: React.CSSProperties = {}

/**
 * The current value (for controlled usage)
 */
value?: string
Imports
import { DsPasswordInput } from "@drivenets/design-system";
Default story ok
const Default = () => <DsPasswordInput placeholder="Enter password" style={{ width: '200px' }} />;
Controlled story ok
const Controlled = () => {
    const [value, setValue] = useState('initial-password');

    return (
        <div
            style={{ display: 'flex', flexDirection: 'column', gap: '16px', alignItems: 'center' }}>
            <DsPasswordInput
                placeholder="Enter password"
                style={{ width: '200px' }}
                value={value}
                onValueChange={(newValue) => setValue(newValue)} />
            <div>Current value: {value}</div>
            <button onClick={() => setValue('new-password')}>Set new password</button>
            <button onClick={() => setValue('')}>Clear password</button>
        </div>
    );
};

DsPopover

components-popover · ./src/components/ds-popover/ds-popover.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 5 prop types
Component: src/components/ds-popover/ds-popover.tsx::default
Props:
/**
 * The alignment of the popover content
 */
align?: 'start' | 'center' | 'end' = 'center'

/**
 * The content to be rendered inside the popover
 */
children: React.ReactNode

/**
 * Additional CSS class names
 */
className?: string

/**
 * The position of the popover relative to the trigger element
 */
side?: 'top' | 'right' | 'bottom' | 'left' = 'top'

/**
 * The element that triggers the popover
 */
trigger: React.ReactNode
Imports
import { DsButton, DsIcon, DsPopover } from "@drivenets/design-system";
import { Close as PopoverClose } from "@radix-ui/react-popover";
Default story ok
const Default = () => <DsPopover
    trigger={(<DsButton>
        <DsIcon icon="info" />Open Popover
                    </DsButton>)}>(<div>
        <strong>ID:</strong>123456, Dallas, USA
                        <p>Facility Types: Tier 1, Tier 2</p>
        <p>Devices: 11, 2898</p>
        <div style={{ display: 'flex', justifyContent: 'flex-end', gap: '8px' }}>
            <PopoverClose asChild>
                <DsIcon className="action-icon" icon="fullscreen" />
            </PopoverClose>
            <PopoverClose asChild>
                <DsIcon className="action-icon" icon="view_list" />
            </PopoverClose>
            <PopoverClose asChild>
                <DsIcon className="action-icon" icon="star" />
            </PopoverClose>
        </div>
    </div>)</DsPopover>;

DsProgressArc

components-progressarc · ./src/components/ds-progress-arc/ds-progress-arc.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 5 prop types
Component: src/components/ds-progress-arc/ds-progress-arc.tsx::default
Props:
/**
 * Custom content to display in the center of the arc, overriding the default text or icon
 */
children?: ReactNode

ref?: Ref<HTMLElement>

/**
 * Size of the arc progress indicator
 * @default 'medium'
 */
size?: (typeof progressArcSizes)[number] = 'medium'

/**
 * Progress value between 0 and 100
 * @default 0
 */
value?: number = 0

/**
 * Visual variant of the arc progress indicator
 * @default 'default'
 */
variant?: (typeof progressArcVariants)[number] = 'default'
Imports
import { DsIcon, DsProgressArc } from "@drivenets/design-system";
Default story ok
const Default = () => <DsProgressArc value={50} />;
All Variants story ok
const AllVariants = () => (
    <div className={styles.grid}>
        <div className={styles.cell}>
            <DsProgressArc size="small" value={50} />
            <span className={styles.label}>Small / Default</span>
        </div>
        <div className={styles.cell}>
            <DsProgressArc size="small" variant="success" />
            <span className={styles.label}>Small / Success</span>
        </div>
        <div className={styles.cell}>
            <DsProgressArc size="small" variant="error" value={50} />
            <span className={styles.label}>Small / Error</span>
        </div>

        <div className={styles.cell}>
            <DsProgressArc size="medium" value={50} />
            <span className={styles.label}>Medium / Default</span>
        </div>
        <div className={styles.cell}>
            <DsProgressArc size="medium" variant="success" />
            <span className={styles.label}>Medium / Success</span>
        </div>
        <div className={styles.cell}>
            <DsProgressArc size="medium" variant="error" value={50} />
            <span className={styles.label}>Medium / Error</span>
        </div>
    </div>
);
Sizes story ok
const Sizes = () => (
    <div className={styles.row}>
        <div className={styles.cell}>
            <DsProgressArc size="small" value={75} />
            <span className={styles.label}>Small</span>
        </div>
        <div className={styles.cell}>
            <DsProgressArc size="medium" value={75} />
            <span className={styles.label}>Medium</span>
        </div>
    </div>
);
Success story ok
const Success = () => <DsProgressArc variant="success" />;
Error story ok
const Error = () => <DsProgressArc variant="error" value={50} />;
Custom Icon story ok
const CustomIcon = () => <DsProgressArc value={80}><DsIcon icon="warning" size="small" /></DsProgressArc>;
Zero Progress story ok
const ZeroProgress = () => <DsProgressArc value={0} />;
Full Progress story ok
const FullProgress = () => <DsProgressArc value={100} />;

DsProgressDonut

components-progressdonut · ./src/components/ds-progress-donut/ds-progress-donut.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 7 prop types
Component: src/components/ds-progress-donut/ds-progress-donut.tsx::default
Props:
/**
 * Custom content to display in the center of the donut, overriding the default text or icon
 */
children?: ReactNode

className?: string

ref?: Ref<HTMLElement>

/**
 * Size of the donut progress indicator
 * @default 'medium'
 */
size?: (typeof progressDonutSizes)[number] = 'medium'

style?: CSSProperties

/**
 * Progress value between 0 and 100
 * @default 0
 */
value?: number = 0

/**
 * Visual variant of the donut progress indicator
 * @default 'default'
 */
variant?: (typeof progressDonutVariants)[number] = 'default'
Imports
import { DsIcon, DsProgressDonut } from "@drivenets/design-system";
Default story ok
const Default = () => <DsProgressDonut value={50} />;
All Variants story ok
const AllVariants = () => (
    <div className={styles.grid}>
        <div className={styles.cell}>
            <DsProgressDonut size="small" value={50} />
            <span className={styles.label}>Small / Default</span>
        </div>
        <div className={styles.cell}>
            <DsProgressDonut size="small" variant="success" />
            <span className={styles.label}>Small / Success</span>
        </div>
        <div className={styles.cell}>
            <DsProgressDonut size="small" variant="error" value={50} />
            <span className={styles.label}>Small / Error</span>
        </div>

        <div className={styles.cell}>
            <DsProgressDonut size="medium" value={50} />
            <span className={styles.label}>Medium / Default</span>
        </div>
        <div className={styles.cell}>
            <DsProgressDonut size="medium" variant="success" />
            <span className={styles.label}>Medium / Success</span>
        </div>
        <div className={styles.cell}>
            <DsProgressDonut size="medium" variant="error" value={50} />
            <span className={styles.label}>Medium / Error</span>
        </div>
    </div>
);
Sizes story ok
const Sizes = () => (
    <div className={styles.row}>
        <div className={styles.cell}>
            <DsProgressDonut size="small" value={75} />
            <span className={styles.label}>Small</span>
        </div>
        <div className={styles.cell}>
            <DsProgressDonut size="medium" value={75} />
            <span className={styles.label}>Medium</span>
        </div>
    </div>
);
Success story ok
const Success = () => <DsProgressDonut variant="success" />;
Error story ok
const Error = () => <DsProgressDonut variant="error" value={50} />;
Custom Icon story ok
const CustomIcon = () => <DsProgressDonut value={80}><DsIcon icon="warning" size="small" /></DsProgressDonut>;
Zero Progress story ok
const ZeroProgress = () => <DsProgressDonut value={0} />;
Full Progress story ok
const FullProgress = () => <DsProgressDonut value={100} />;

DsProgressLinear

components-progresslinear · ./src/components/ds-progress-linear/ds-progress-linear.stories.tsx
Design system ProgressLinear component
Prop types (react-docgen) 11 prop types
Component: src/components/ds-progress-linear/ds-progress-linear.tsx::default
Props:
/**
 * Caption/helper text below the progress bar.
 * When a string is provided, it is auto-styled with an icon and color based on the variant.
 * When a ReactNode is provided, it is rendered as-is.
 */
caption?: React.ReactNode | string

/**
 * Additional CSS class names
 */
className?: string

/**
 * Label displayed above the progress bar (left side)
 */
label?: React.ReactNode

/**
 * The maximum value
 * @default 100
 */
max?: number = 100

/**
 * The minimum value
 * @default 0
 */
min?: number = 0

/**
 * Ref to the root element
 */
ref?: React.Ref<HTMLDivElement>

/**
 * Whether to show the percentage value text (right side of label row)
 * @default true
 */
showValue?: boolean = true

/**
 * The size of the progress bar track
 * @default 'medium'
 */
size?: (typeof progressLinearSizes)[number] = 'medium'

/**
 * Additional styles to apply to the component
 */
style?: React.CSSProperties

/**
 * The current progress value (0–100)
 * @default 0
 */
value?: number = 0

/**
 * The visual variant controlling bar color and caption styling
 * @default 'progress'
 */
variant?: (typeof progressLinearVariants)[number] = 'progress'
Imports
import { DsProgressLinear } from "@drivenets/design-system";
Default story ok
const Default = () => <DsProgressLinear value={35} label="File Upload" caption="Uploading..." />;
All Variants story ok
const AllVariants = () => (
    <>
        <DsProgressLinear variant="initial" value={0} label="File Upload" caption="Waiting to start..." />
        <DsProgressLinear variant="progress" value={35} label="File Upload" caption="Uploading..." />
        <DsProgressLinear variant="interrupted" value={35} label="File Upload" caption="Upload interrupted." />
        <DsProgressLinear variant="success" value={100} label="File Upload" caption="Upload complete." />
        <DsProgressLinear
            variant="error"
            value={0}
            label="File Upload"
            caption="Error: File exceeds size limit."
        />
    </>
);
Sizes story ok
const Sizes = () => (
    <>
        <DsProgressLinear size="small" value={50} label="File Upload" caption="Uploading..." />
        <DsProgressLinear size="medium" value={50} label="File Upload" caption="Uploading..." />
        <DsProgressLinear size="large" value={50} label="File Upload" caption="Uploading..." />
    </>
);
With Custom Caption story ok
const WithCustomCaption = () => <DsProgressLinear
    value={60}
    label="Processing"
    caption={(<span>Step <strong>3</strong>of <strong>5</strong>
    </span>)} />;
Bar Only story ok
const BarOnly = () => <DsProgressLinear value={70} showValue={false} />;
Controlled story ok
const Controlled = () => {
    const [value, setValue] = useState(args.value ?? 0);

    return (
        <>
            <input
                type="range"
                min={0}
                max={100}
                value={value}
                onChange={(e) => setValue(Number(e.target.value))} />
            <DsProgressLinear label="File Upload" caption="Uploading..." value={value} />
        </>
    );
};
Full Matrix story ok
const FullMatrix = () => (
    <>
        <DsProgressLinear
            size="small"
            variant="initial"
            value={0}
            label="File Upload"
            caption="Waiting to start..."
        />
        <DsProgressLinear
            size="small"
            variant="progress"
            value={35}
            label="File Upload"
            caption="Uploading..."
        />
        <DsProgressLinear
            size="small"
            variant="interrupted"
            value={35}
            label="File Upload"
            caption="Upload interrupted."
        />
        <DsProgressLinear
            size="small"
            variant="success"
            value={100}
            label="File Upload"
            caption="Upload complete."
        />
        <DsProgressLinear
            size="small"
            variant="error"
            value={0}
            label="File Upload"
            caption="Error: File exceeds size limit."
        />

        <DsProgressLinear
            size="medium"
            variant="initial"
            value={0}
            label="File Upload"
            caption="Waiting to start..."
        />
        <DsProgressLinear
            size="medium"
            variant="progress"
            value={35}
            label="File Upload"
            caption="Uploading..."
        />
        <DsProgressLinear
            size="medium"
            variant="interrupted"
            value={35}
            label="File Upload"
            caption="Upload interrupted."
        />
        <DsProgressLinear
            size="medium"
            variant="success"
            value={100}
            label="File Upload"
            caption="Upload complete."
        />
        <DsProgressLinear
            size="medium"
            variant="error"
            value={0}
            label="File Upload"
            caption="Error: File exceeds size limit."
        />

        <DsProgressLinear
            size="large"
            variant="initial"
            value={0}
            label="File Upload"
            caption="Waiting to start..."
        />
        <DsProgressLinear
            size="large"
            variant="progress"
            value={35}
            label="File Upload"
            caption="Uploading..."
        />
        <DsProgressLinear
            size="large"
            variant="interrupted"
            value={35}
            label="File Upload"
            caption="Upload interrupted."
        />
        <DsProgressLinear
            size="large"
            variant="success"
            value={100}
            label="File Upload"
            caption="Upload complete."
        />
        <DsProgressLinear
            size="large"
            variant="error"
            value={0}
            label="File Upload"
            caption="Error: File exceeds size limit."
        />
    </>
);

DsRadioGroup.Root

components-radiogroup · ./src/components/ds-radio-group/ds-radio-group.stories.tsx
DEPRECATED: Legacy DsRadioGroup component with options array Use compound component pattern instead: DsRadioGroup.Root + DsRadioGroup.Item
deprecated:
Prop types (react-docgen) 7 prop types
Component: src/components/ds-radio-group/ds-radio-group.tsx::DsRadioGroupLegacy
Props:
/**
 * Additional CSS class names
 */
className?: string

/**
 * The default selected value
 */
defaultValue?: TOption['value']

/**
 * Whether the entire radio group is disabled.
 */
disabled?: boolean

/**
 * Event handler called when the selected value changes
 */
onValueChange?: (value: TOption['value']) => void

/**
 * The radio group options
 */
options: TOption[]

/**
 * Additional inline styles
 */
style?: React.CSSProperties

/**
 * The selected value
 */
value?: TOption['value']
Imports
import { DsRadioGroup } from "@drivenets/design-system";
Default story ok
const Default = function Render() {
    const [value, setValue] = useState<string | null>('option2');

    return (
        <DsRadioGroup.Root value={value} onValueChange={setValue}>
            <DsRadioGroup.Item value="option1" label="Option 1" />
            <DsRadioGroup.Item value="option2" label="Option 2" />
            <DsRadioGroup.Item value="option3" label="Option 3" />
        </DsRadioGroup.Root>
    );
};
With Disabled Items story ok
const WithDisabledItems = function Render() {
    const [value, setValue] = useState<string | null>('option2');

    return (
        <DsRadioGroup.Root value={value} onValueChange={setValue}>
            <DsRadioGroup.Item
                value="option1"
                label="Disabled Option"
                labelInfo="This option is disabled"
                disabled
            />
            <DsRadioGroup.Item value="option2" label="Option 2" labelInfo="Available option" />
            <DsRadioGroup.Item value="option3" label="Option 3" />
        </DsRadioGroup.Root>
    );
};
Custom Composition story ok
const CustomComposition = function Render() {
    const [value, setValue] = useState<string | null>('custom2');

    return (
        <DsRadioGroup.Root value={value} onValueChange={setValue}>
            <DsRadioGroup.Item value="custom1" className={styles.customItem}>
                <div className={styles.customLabel}>
                    <div className={styles.customLabelTitle}>Custom Layout 1</div>
                    <div className={styles.customLabelDescription}>With custom HTML structure</div>
                </div>
            </DsRadioGroup.Item>
            <DsRadioGroup.Item value="custom2" className={styles.customItem}>
                <div className={styles.customLabel}>
                    <div className={styles.customLabelTitle}>Custom Layout 2</div>
                    <div className={styles.customLabelDescription}>Complete control over rendering</div>
                </div>
            </DsRadioGroup.Item>
        </DsRadioGroup.Root>
    );
};

DsSegmentGroup.Root

components-segmentgroup · ./src/components/ds-segment-group/ds-segment-group.stories.tsx
Prop type error
File: /opt/build/repo/packages/design-system/src/components/ds-segment-group/ds-segment-group.tsx
Error:
No suitable component definition found.
You can debug your component file in this playground: https://react-docgen.dev/playground
Code:
import type React from 'react';
import { SegmentGroup } from '@ark-ui/react/segment-group';
import classNames from 'classnames';
import styles from './ds-segment-group.module.scss';
import { DsTypography } from '../ds-typography';
import {
	type DsSegmentGroupItemProps,
	type DsSegmentGroupItemTextProps,
	type DsSegmentGroupRootProps,
} from './ds-segment-group.types';

/**
 * Root component - provides segment group context
 */
const Root: React.FC<DsSegmentGroupRootProps> = ({
	className,
	children,
	onValueChange,
	size = 'default',
	...props
}) => (
	<SegmentGroup.Root
		className={classNames(styles.segmentGroupRoot, size === 'small' && styles.small, className)}
		onValueChange={(details) => onValueChange?.(details.value)}
		{...props}
	>
		<SegmentGroup.Indicator className={styles.segmentIndicator} />
		{children}
	</SegmentGroup.Root>
);

/**
 * Item component - renders a single segment button
 */
const Item: React.FC<DsSegmentGroupItemProps> = ({ value, label, className, style, children, ...props }) => {
	return (
		<SegmentGroup.Item
			value={value}
			className={classNames(styles.segmentItem, className)}
			style={style}
			{...props}
		>
			{label ? <ItemText>{label}</ItemText> : children}
			<SegmentGroup.ItemControl />
			<SegmentGroup.ItemHiddenInput />
		</SegmentGroup.Item>
	);
};

/**
 * ItemText component - renders text content within an item
 */
const ItemText: React.FC<DsSegmentGroupItemTextProps> = ({ className, children, ...props }) => (
	<SegmentGroup.ItemText className={classNames(styles.segmentItemText, className)} {...props} asChild>
		<DsTypography variant="body-sm-reg">{children}</DsTypography>
	</SegmentGroup.ItemText>
);

/**
 * Design system SegmentGroup component
 *
 * @example
 * <DsSegmentGroup.Root value={value} onValueChange={setValue} size="default">
 *   <DsSegmentGroup.Indicator />
 *   <DsSegmentGroup.Item value="option1" label="Option 1" />
 *   <DsSegmentGroup.Item value="option2">
 *     <DsIcon icon="settings" />
 *     <DsSegmentGroup.ItemText>Option 2</DsSegmentGroup.ItemText>
 *   </DsSegmentGroup.Item>
 * </DsSegmentGroup.Root>
 */
export const DsSegmentGroup = {
	Root,
	Item,
	ItemText,
};
Info
No description found. Write a jsdoc comment such as /** Component description */.
Imports
import { DsIcon, DsSegmentGroup } from "@drivenets/design-system";
Default story ok
const Default = function Render() {
    const [value, setValue] = useState<string | null>('react');

    return (
        <DsSegmentGroup.Root value={value} onValueChange={setValue}>
            <DsSegmentGroup.Item value="react" label="React" />
            <DsSegmentGroup.Item value="vue" label="Vue" />
            <DsSegmentGroup.Item value="angular" label="Angular" />
            <DsSegmentGroup.Item value="svelte" label="Svelte" />
        </DsSegmentGroup.Root>
    );
};
Small story ok
const Small = function Render() {
    const [value, setValue] = useState<string | null>('list');

    return (
        <DsSegmentGroup.Root value={value} onValueChange={setValue} size="small">
            <DsSegmentGroup.Item value="list" label="List" />
            <DsSegmentGroup.Item value="grid" label="Grid" />
            <DsSegmentGroup.Item value="table" label="Table" />
        </DsSegmentGroup.Root>
    );
};
With Icons story ok
const WithIcons = function Render() {
    const [value, setValue] = useState<string | null>('day');

    return (
        <DsSegmentGroup.Root value={value} onValueChange={setValue}>
            <DsSegmentGroup.Item value="day">
                <DsIcon icon="wb_sunny" size="tiny" />
                <DsSegmentGroup.ItemText>Day</DsSegmentGroup.ItemText>
            </DsSegmentGroup.Item>
            <DsSegmentGroup.Item value="week">
                <DsIcon icon="date_range" size="tiny" />
                <DsSegmentGroup.ItemText>Week</DsSegmentGroup.ItemText>
            </DsSegmentGroup.Item>
            <DsSegmentGroup.Item value="month">
                <DsIcon icon="calendar_month" size="tiny" />
                <DsSegmentGroup.ItemText>Month</DsSegmentGroup.ItemText>
            </DsSegmentGroup.Item>
        </DsSegmentGroup.Root>
    );
};
Icon Only story ok
const IconOnly = function Render() {
    const [value, setValue] = useState<string | null>('list');

    return (
        <DsSegmentGroup.Root value={value} onValueChange={setValue} size="small">
            <DsSegmentGroup.Item value="list">
                <DsIcon icon="view_list" size="tiny" />
            </DsSegmentGroup.Item>
            <DsSegmentGroup.Item value="grid">
                <DsIcon icon="grid_view" size="tiny" />
            </DsSegmentGroup.Item>
            <DsSegmentGroup.Item value="kanban">
                <DsIcon icon="view_kanban" size="tiny" />
            </DsSegmentGroup.Item>
            <DsSegmentGroup.Item value="timeline">
                <DsIcon icon="timeline" size="tiny" />
            </DsSegmentGroup.Item>
        </DsSegmentGroup.Root>
    );
};
With Disabled Items story ok
const WithDisabledItems = function Render() {
    const [value, setValue] = useState<string | null>('option2');

    return (
        <DsSegmentGroup.Root value={value} onValueChange={setValue}>
            <DsSegmentGroup.Item value="option1" label="Disabled" disabled />
            <DsSegmentGroup.Item value="option2" label="Available" />
            <DsSegmentGroup.Item value="option3" label="Also Available" />
        </DsSegmentGroup.Root>
    );
};
Uncontrolled story ok
const Uncontrolled = function Render() {
    return (
        <DsSegmentGroup.Root defaultValue="option2">
            <DsSegmentGroup.Item value="option1" label="Option 1" />
            <DsSegmentGroup.Item value="option2" label="Option 2" />
            <DsSegmentGroup.Item value="option3" label="Option 3" />
        </DsSegmentGroup.Root>
    );
};
Controlled story ok
const Controlled = function Render() {
    const [value, setValue] = useState<string | null>('option2');

    return (
        <div className={styles.controlledContainer}>
            <DsSegmentGroup.Root value={value} onValueChange={setValue}>
                <DsSegmentGroup.Item value="option1" label="Option 1" />
                <DsSegmentGroup.Item value="option2" label="Option 2" />
                <DsSegmentGroup.Item value="option3" label="Option 3" />
            </DsSegmentGroup.Root>
            <div className={styles.selectedValue}>Selected: {value || 'None'}</div>
        </div>
    );
};
Two Options story ok
const TwoOptions = function Render() {
    const [value, setValue] = useState<string | null>('on');

    return (
        <DsSegmentGroup.Root value={value} onValueChange={setValue}>
            <DsSegmentGroup.Item value="on" label="On" />
            <DsSegmentGroup.Item value="off" label="Off" />
        </DsSegmentGroup.Root>
    );
};

DsSelect

components-select · ./src/components/ds-select/ds-select.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 9 prop types
Component: src/components/ds-select/ds-select.tsx::default
Props:
/**
 * Additional CSS class names
 */
className?: string

/**
 * Whether the select is disabled
 */
disabled?: boolean

/**
 * Unique identifier for the select component
 */
id?: string

/**
 * Event handler called when the select loses focus
 * 
 * @param event
 */
onBlur?: (event: React.FocusEvent) => void

/**
 * Options to display in the select dropdown
 */
options: DsSelectOption[]

/**
 * Placeholder text to display when no option is selected
 */
placeholder?: string = 'Click to select a value'

/**
 * 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.
 */
renderOption?: (option: DsSelectOption) => ReactNode

/**
 * The size of the select component
 * @default 'default'
 */
size?: 'default' | 'small'

/**
 * Additional styles to apply to the icon
 */
style?: React.CSSProperties
Imports
import { DsIcon, DsSelect, DsStatusBadge, DsTag } from "@drivenets/design-system";
Default story ok
const Default = () => <ControlledSelectWrapper
    options={mockOptions}
    clearable
    style={{
        width: '200px',
    }} />;
With Icons story ok
const WithIcons = () => <ControlledSelectWrapper
    options={mockOptions.slice(0, 3).map((item) => ({
        ...item,
        icon: 'nutrition',
    }))}
    style={{
        width: '200px',
    }}
    clearable />;
Sizes story ok
const Sizes = () => <div className={styles.sizesContainer}>
    <div className={styles.sizeItem}>
        <div className={styles.sizeLabel}>Default</div>
        <ControlledSelectWrapper
            options={mockOptions.slice(0, 5)}
            clearable
            style={{
                width: '200px',
            }}
            size="default" />
    </div>
    <div className={styles.sizeItem}>
        <div className={styles.sizeLabel}>Small</div>
        <ControlledSelectWrapper
            options={mockOptions.slice(0, 5)}
            clearable
            style={{
                width: '200px',
            }}
            size="small" />
    </div>
</div>;
With Search story ok
const WithSearch = () => <ControlledSelectWrapper
    options={[
        ...mockOptions,
        {
            value: 'nectarine',
            label: 'Nectarine',
        },
    ]}
    clearable
    style={{
        width: '200px',
    }} />;
Multi Select story ok
const MultiSelect = () => <ControlledSelectWrapper
    options={mockOptions}
    style={{
        width: '250px',
    }}
    multiple
    clearable />;
Multi Select With Search story ok
const MultiSelectWithSearch = () => <ControlledSelectWrapper
    options={[
        ...mockOptions,
        {
            value: 'nectarine',
            label: 'Nectarine',
        },
    ]}
    style={{
        width: '250px',
    }}
    multiple
    clearable />;
Custom Render Option story ok
const CustomRenderOption = () => <ControlledSelectWrapper
    options={countryOptions}
    renderOption={renderCountryOption}
    clearable
    style={{
        width: '250px',
    }} />;
Custom Render Option Multi Select story ok
const CustomRenderOptionMultiSelect = () => <ControlledSelectWrapper
    options={countryOptions}
    renderOption={renderCountryOption}
    multiple
    clearable
    style={{
        width: '300px',
    }} />;
Custom Render Option With Search story ok
const CustomRenderOptionWithSearch = () => <ControlledSelectWrapper
    options={[...mockOptions, ...countryOptions]}
    renderOption={(option) => (
        <span className={styles.customOption}>
            <DsIcon icon="public" size="tiny" />
            {option.label}
        </span>
    )}
    clearable
    style={{
        width: '300px',
    }} />;
Custom Render Value story ok
const CustomRenderValue = () => {
    const [value, setValue] = useState('');

    const renderValue = (selected: DsSelectOption) => {
        const info = versionStatusMap[selected.value];

        return (
            <span className={styles.customOption}>
                {selected.label}
                {info && (
                    <DsStatusBadge status={info.status} label={info.label} size="small" ghost icon="check_circle" />
                )}
            </span>
        );
    };

    return (
        <DsSelect
            options={versionOptions}
            value={value}
            onValueChange={setValue}
            renderValue={renderValue}
            clearable
            style={{ width: '250px' }}
        />
    );
};
Custom Render Value Multi Select story ok
const CustomRenderValueMultiSelect = () => {
    const [value, setValue] = useState<string[]>([]);

    const renderOption = (option: DsSelectOption) => (
        <span className={styles.customOption}>
            <DsTag label={option.value.toUpperCase()} size="small" />
            {option.label}
        </span>
    );

    const renderValue = (selected: DsSelectOption[]) => (
        <span className={styles.customOption}>
            {selected.map((opt) => (
                <DsTag key={opt.value} label={opt.value.toUpperCase()} size="small" />
            ))}
        </span>
    );

    return (
        <DsSelect
            options={countryOptions}
            value={value}
            onValueChange={setValue}
            renderOption={renderOption}
            renderValue={renderValue}
            multiple
            clearable
            style={{ width: '300px' }}
        />
    );
};
Custom Render Value And Option story ok
const CustomRenderValueAndOption = () => {
    const [value, setValue] = useState('');

    const renderOption = (option: DsSelectOption) => {
        const info = versionStatusMap[option.value];

        if (!info) {
            return option.label;
        }

        return (
            <span className={styles.customOption}>
                {option.label}
                <DsStatusBadge status={info.status} label={info.label} size="small" ghost icon="check_circle" />
            </span>
        );
    };

    const renderValue = (selected: DsSelectOption) => {
        const info = versionStatusMap[selected.value];

        return (
            <span className={styles.customOption}>
                {selected.label}
                {info && (
                    <DsStatusBadge status={info.status} label={info.label} size="small" ghost icon="check_circle" />
                )}
            </span>
        );
    };

    return (
        <DsSelect
            options={versionOptions}
            value={value}
            onValueChange={setValue}
            renderOption={renderOption}
            renderValue={renderValue}
            clearable
            style={{ width: '250px' }}
        />
    );
};
Custom Render Value And Option Multi Select story ok
const CustomRenderValueAndOptionMultiSelect = () => {
    const [value, setValue] = useState<string[]>([]);

    const renderOption = (option: DsSelectOption) => {
        const info = versionStatusMap[option.value];

        if (!info) {
            return option.label;
        }

        return (
            <span className={styles.customOption}>
                {option.label}
                <DsStatusBadge status={info.status} label={info.label} size="small" ghost icon="check_circle" />
            </span>
        );
    };

    const renderValue = (selected: DsSelectOption[]) => {
        const option = selected[0];
        const info = option ? versionStatusMap[option.value] : undefined;

        return (
            <span className={styles.customOption}>
                {option?.label}
                {info && (
                    <DsStatusBadge status={info.status} label={info.label} size="small" ghost icon="check_circle" />
                )}
                {selected.length > 1 && ` +${String(selected.length - 1)}`}
            </span>
        );
    };

    return (
        <DsSelect
            options={versionOptions}
            value={value}
            onValueChange={setValue}
            renderOption={renderOption}
            renderValue={renderValue}
            multiple
            clearable
            style={{ width: '300px' }}
        />
    );
};
Keyboard Interactions story ok
const KeyboardInteractions = () => <ControlledSelectWrapper
    options={[
        ...mockOptions,
        {
            value: 'nectarine',
            label: 'Nectarine',
        },
    ]}
    clearable
    style={{
        width: '250px',
    }} />;

DsSkeleton.Rect

components-skeleton · ./src/components/ds-skeleton/ds-skeleton.stories.tsx
Text skeleton component - matches typography variants It renders pill-shaped lines that match the height of text content
Prop types (react-docgen) 8 prop types
Component: src/components/ds-skeleton/ds-skeleton-text.tsx::default
Props:
/**
 * Additional CSS class names
 */
className?: string

/**
 * Color variant of the skeleton
 * @default 'gray'
 */
color?: (typeof skeletonColorVariants)[number] = 'gray'

/**
 * Number of lines to render
 * @default 1
 */
lines?: number = 1

/**
 * Border radius: 'default' (4px), 'round' (pill-shaped), or custom px value
 * @default 'default'
 */
radius?: SkeletonRadiusVariant | number = 'default'

/**
 * Ref to the skeleton element
 */
ref?: Ref<HTMLSpanElement>

/**
 * Additional inline styles
 */
style?: CSSProperties

/**
 * Typography variant - determines the height of the skeleton
 * @default 'body-sm-reg'
 */
typographyVariant?: keyof typeof typographyVariantConfig = 'body-sm-reg'

/**
 * Width of skeleton. Last line will have varied width if not specified.
 * Can be a number (px) or string ('50%', '200px')
 * @default '100%' (last line varies between 40-80%)
 */
width?: string | number
Imports
import { DsSkeleton, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@drivenets/design-system";
Color Variants story ok
Color variants - gray (default) and blue
const ColorVariants = () => (
    <div className={styles.verticalStack} data-testid="color-variants">
        <div>
            <h4 className={styles.sectionLabel}>gray (default)</h4>
            <DsSkeleton.Text color="gray" />
            <DsSkeleton.Circle color="gray" />
            <DsSkeleton.Rect width={40} height={40} color="gray" />
        </div>
        <div>
            <h4 className={styles.sectionLabel}>Blue</h4>
            <DsSkeleton.Text color="blue" />
            <DsSkeleton.Circle color="blue" />
            <DsSkeleton.Rect width={40} height={40} color="blue" />
        </div>
    </div>
);
Text Variants story ok
Text skeleton - typography variants, multiple lines, width, and radius options
const TextVariants = () => (
    <div className={styles.verticalStack}>
        <div>
            <h4 className={styles.sectionLabel}>Typography Variants</h4>
            <div className={styles.section}>
                <DsSkeleton.Text typographyVariant="heading1" />
                <DsSkeleton.Text typographyVariant="heading3" />
                <DsSkeleton.Text typographyVariant="body-md-reg" />
                <DsSkeleton.Text typographyVariant="body-sm-reg" />
            </div>
        </div>
        <div>
            <h4 className={styles.sectionLabel}>Multiple Lines</h4>
            <DsSkeleton.Text typographyVariant="body-md-reg" lines={3} />
        </div>
        <div>
            <h4 className={styles.sectionLabel}>Custom Width</h4>
            <div className={styles.sectionSmall}>
                <DsSkeleton.Text typographyVariant="body-md-reg" width="80%" />
                <DsSkeleton.Text typographyVariant="body-md-reg" width={200} />
            </div>
        </div>
        <div>
            <h4 className={styles.sectionLabel}>Border Radius</h4>
            <div className={styles.sectionSmall}>
                <DsSkeleton.Text typographyVariant="body-md-reg" radius="round" />
                <DsSkeleton.Text typographyVariant="body-md-reg" radius="default" />
                <DsSkeleton.Text typographyVariant="body-md-reg" radius={12} />
            </div>
        </div>
    </div>
);
Circle Sizes story ok
Circle skeleton with avatar sizes
const CircleSizes = () => (
    <div className={styles.sizesRow} data-testid="circle-sizes">
        <div className={styles.sizeItem}>
            <DsSkeleton.Circle size="xsm" />
            <p className={styles.label}>xsm (24px)</p>
        </div>
        <div className={styles.sizeItem}>
            <DsSkeleton.Circle size="sm" />
            <p className={styles.label}>sm (32px)</p>
        </div>
        <div className={styles.sizeItem}>
            <DsSkeleton.Circle size="regular" />
            <p className={styles.label}>regular (40px)</p>
        </div>
        <div className={styles.sizeItem}>
            <DsSkeleton.Circle size="md" />
            <p className={styles.label}>md (48px)</p>
        </div>
        <div className={styles.sizeItem}>
            <DsSkeleton.Circle size="lg" />
            <p className={styles.label}>lg (64px)</p>
        </div>
        <div className={styles.sizeItem}>
            <DsSkeleton.Circle size="xl" />
            <p className={styles.label}>xl (80px)</p>
        </div>
        <div className={styles.sizeItem}>
            <DsSkeleton.Circle size={100} />
            <p className={styles.label}>custom (100px)</p>
        </div>
    </div>
);
Rectangle Shapes story ok
Rectangle skeleton - buttons, badges, images
const RectangleShapes = () => (
    <div className={styles.section} data-testid="rectangle-shapes">
        <div className={styles.shapeRow}>
            <DsSkeleton.Rect width={120} height={40} />
            <span className={styles.label}>Button</span>
        </div>
        <div className={styles.shapeRow}>
            <DsSkeleton.Rect width={80} height={24} radius="round" />
            <span className={styles.label}>Badge</span>
        </div>
        <div className={styles.shapeRow}>
            <DsSkeleton.Rect width={200} height={150} radius={8} />
            <span className={styles.label}>Image</span>
        </div>
    </div>
);
Card Skeleton story ok
Card skeleton composition example
const CardSkeleton = () => (
    <div className={styles.cardContainer}>
        <div className={styles.cardHeader}>
            <DsSkeleton.Circle size="lg" />
            <div className={styles.cardHeaderContent}>
                <DsSkeleton.Text typographyVariant="heading4" width="60%" />
                <div className={styles.cardHeaderSubtitle}>
                    <DsSkeleton.Text typographyVariant="body-sm-reg" width="80%" />
                </div>
            </div>
        </div>
        <DsSkeleton.Text typographyVariant="body-md-reg" lines={3} />
        <div className={styles.cardActions}>
            <DsSkeleton.Rect width={100} height={36} radius={4} />
            <DsSkeleton.Rect width={100} height={36} radius={4} />
        </div>
    </div>
);
Table Skeleton story ok
Table skeleton composition - shows a loading state for tabular data
const TableSkeleton = () => (
    <div className={styles.tableContainer}>
        <Table>
            <TableHeader>
                <TableRow>
                    <TableHead>Name</TableHead>
                    <TableHead>Status</TableHead>
                    <TableHead>Progress</TableHead>
                    <TableHead>Actions</TableHead>
                </TableRow>
            </TableHeader>
            <TableBody>
                {Array.from({ length: 5 }).map((_, i) => (
                    <TableRow key={i}>
                        <TableCell>
                            <div className={styles.tableNameCell}>
                                <DsSkeleton.Circle size="sm" />
                                <DsSkeleton.Text width={120} />
                            </div>
                        </TableCell>
                        <TableCell>
                            <DsSkeleton.Rect width={80} height={24} radius="round" />
                        </TableCell>
                        <TableCell>
                            <DsSkeleton.Rect width={100} height={8} radius={4} />
                        </TableCell>
                        <TableCell>
                            <DsSkeleton.Rect width={32} height={32} radius={4} />
                        </TableCell>
                    </TableRow>
                ))}
            </TableBody>
        </Table>
    </div>
);

DsSmartTabs

components-smarttabs · ./src/components/ds-smart-tabs/ds-smart-tabs.stories.tsx
Design system SmartTabs component
Prop types (react-docgen) 5 prop types
Component: src/components/ds-smart-tabs/ds-smart-tabs.tsx::default
Props:
/**
 * Currently active tab value
 */
activeTab: string

/**
 * The tab components as children
 */
children: ReactNode

/**
 * Additional CSS class names
 */
className?: string

/**
 * Callback function when a tab is clicked
 */
onTabClick: (value: string) => void

/**
 * Additional styles to apply to the component
 */
style?: React.CSSProperties = {}
Imports
import { DsSmartTabs } from "@drivenets/design-system";
Default story ok
const Default = function Render() {
    const [activeTab, setActiveTab] = useState('all');

    return (
        <div>
            <DsSmartTabs activeTab={activeTab} onTabClick={setActiveTab}>
                <DsSmartTabs.Tab label="All" value="all" icon="view_apps" color="dark-blue" content="747" />
                <DsSmartTabs.Tab label="Active" value="active" icon="check_circle" color="green" content="198" />
                <DsSmartTabs.Tab
                    label="Deprecated"
                    value="deprecated"
                    icon="notifications"
                    color="red"
                    content="202"
                />
                <DsSmartTabs.Tab
                    label="Inactive"
                    value="inactive"
                    icon="stop_circle"
                    color="gray"
                    content="347"
                    disabled
                />
            </DsSmartTabs>
            <div
                role="status"
                aria-label="Active tab"
                style={{ marginTop: '20px', padding: '16px', background: '#f5f5f5', borderRadius: '4px' }}
            >
                <p>
                    Active tab: <strong>{activeTab}</strong>
                </p>
            </div>
        </div>
    );
};

DsSpinner

components-spinner · ./src/components/ds-spinner/ds-spinner.stories.tsx
Design system Spinner component
Prop types (react-docgen) 3 prop types
Component: src/components/ds-spinner/ds-spinner.tsx::default
Props:
/**
 * Additional CSS class names
 */
className?: string

/**
 * The size of the spinner
 * @default 'medium'
 */
size?: (typeof spinnerSizes)[number] = 'medium'

/**
 * Additional styles to apply to the component
 */
style?: React.CSSProperties = {}
Imports
import { DsSpinner } from "@drivenets/design-system";
Default story ok
const Default = () => <DsSpinner />;
All Sizes story ok
const AllSizes = () => (
    <div style={{ display: 'flex', gap: '24px', alignItems: 'center' }}>
        <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '8px' }}>
            <DsSpinner size="small" />
            <span>Small</span>
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '8px' }}>
            <DsSpinner size="medium" />
            <span>Default</span>
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '8px' }}>
            <DsSpinner size="large" />
            <span>Large</span>
        </div>
    </div>
);
Modal Loading story ok
const ModalLoading = () => (
    <div className={styles.modalOverlay}>
        <div className={styles.modalContent}>
            <DsSpinner />
            <div className={styles.modalText}>
                <p className={styles.modalTextPrimary}>Explanation text will describe the process.</p>
                <p className={styles.modalTextSecondary}>Two lines will be aimed for this.</p>
            </div>
        </div>
    </div>
);

DsSplitButton

components-splitbutton · ./src/components/ds-split-button/ds-split-button.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 6 prop types
Component: src/components/ds-split-button/ds-split-button.tsx::default
Props:
/**
 * Additional CSS class name applied to the wrapper element
 */
className?: string

/**
 * Whether both the button and the select are disabled.
 * @default false
 */
disabled?: boolean

/**
 * Ref forwarded to the split-button wrapper element
 */
ref?: Ref<HTMLDivElement>

/**
 * Visual size applied to both the button and select slots.
 * @default medium
 */
size?: (typeof splitButtonSizes)[number] = 'medium'

/**
 * Props forwarded to the internal button and select slots.
 */
slotProps: DsSplitButtonSlotProps

/**
 * Inline styles applied to the wrapper element
 */
style?: CSSProperties
Imports
import { DsSplitButton } from "@drivenets/design-system";
Default story ok
const Default = () => {
    const [value, setValue] = useState('30');
    const [loading, setLoading] = useState(false);

    const handleAction = () => {
        setLoading(true);
        setTimeout(() => setLoading(false), 2000);
    };

    return (
        <DsSplitButton
            size="medium"
            disabled={false}
            slotProps={{
                button: {
                    ...args.slotProps.button,
                    loading,
                    icon: 'refresh',
                    onClick: handleAction,
                },
                select: {
                    ...args.slotProps.select,
                    value,
                    onValueChange: setValue,
                } as DsSelectProps,
            }} />
    );
};
Loading story ok
const Loading = () => <DsSplitButton
    size="medium"
    disabled={false}
    slotProps={{
        button: {
            loading: true,
        },
        select: {
            options: refreshOptions,
            value: '30',
            onValueChange: fn(),
        },
    }} />;
Disabled story ok
const Disabled = () => <DsSplitButton
    size="medium"
    disabled
    slotProps={{
        button: { icon: 'refresh' },
        select: {
            options: refreshOptions,
            value: '30',
            onValueChange: fn(),
            multiple: false,
        },
    }} />;

DsStack

components-stack · ./src/components/ds-stack/ds-stack.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 11 prop types
Component: src/components/ds-stack/ds-stack.tsx::DsStack
Props:
/**
 * Cross-axis alignment of children (maps to CSS `align-items`).
 */
alignItems?: CSSProps['alignItems']

children?: ReactNode

className?: string

/**
 * Flex direction of the stack (maps to CSS `flex-direction`).
 * @default row
 */
direction?: CSSProps['flexDirection']

/**
 * Flex shorthand applied to the stack itself (maps to CSS `flex`).
 */
flex?: CSSProps['flex']

/**
 * Whether and how children wrap onto multiple lines (maps to CSS `flex-wrap`).
 */
flexWrap?: CSSProps['flexWrap']

/**
 * Space between children (maps to CSS `gap`). Accepts any valid CSS gap value,
 * e.g. `8`, `'1rem'`, `'var(--ds-space-2)'`.
 */
gap?: CSSProps['gap']

/**
 * Main-axis alignment of children (maps to CSS `justify-content`).
 */
justifyContent?: CSSProps['justifyContent']

ref?: Ref<HTMLDivElement>

style?: CSSProperties

/**
 * Width of the stack container (maps to CSS `width`).
 */
width?: CSSProps['width']
Imports
import { DsStack } from "@drivenets/design-system";
Default story ok
const Default = () => <DsStack direction="column" gap={8}>
    <Box>Item 1</Box>
    <Box>Item 2</Box>
    <Box>Item 3</Box>
</DsStack>;
Row story ok
const Row = () => <DsStack direction="row" gap={16} alignItems="center">
    <Box>Item 1</Box>
    <Box>Item 2</Box>
    <Box>Item 3</Box>
</DsStack>;
Responsive story ok
const Responsive = () => <DsStack
    direction={{ md: 'column', lg: 'row' }}
    gap={{ md: 8, lg: 24 }}
    alignItems="center"
    className={styles.container}>
    <Box>Item 1</Box>
    <Box>Item 2</Box>
    <Box>Item 3</Box>
</DsStack>;
Space Between story ok
const SpaceBetween = () => <DsStack
    direction="row"
    justifyContent="space-between"
    alignItems="center"
    width="100%"
    className={styles.container}>
    <Box>Left</Box>
    <Box>Right</Box>
</DsStack>;
Wrapping story ok
const Wrapping = () => <DsStack direction="row" gap={8} flexWrap="wrap" className={styles.container}>
    {Array.from({ length: 10 }, (_, i) => (
        <Box key={i}>Item {i + 1}</Box>
    ))}
</DsStack>;
Nested story ok
const Nested = () => (
    <DsStack gap={24}>
        <DsStack direction="row" gap={16} alignItems="center">
            <Box>Row 1 - A</Box>
            <Box>Row 1 - B</Box>
            <Box>Row 1 - C</Box>
        </DsStack>

        <DsStack direction="row" gap={16} alignItems="center">
            <Box>Row 2 - A</Box>
            <Box>Row 2 - B</Box>
        </DsStack>
    </DsStack>
);

DsStatusBadge

components-statusbadge · ./src/components/ds-status-badge/ds-status-badge.stories.tsx
Design system StatusBadge component
Prop types (react-docgen) 8 prop types
Component: src/components/ds-status-badge/ds-status-badge.tsx::default
Props:
/**
 * Accessible label for screen readers, if provided, will override default label/status text.
 */
aria-label?: string = label = status

/**
 * Additional CSS class names
 */
className?: string

/**
 * Whether the status badge should use ghost style (light background)
 * @default false
 */
ghost?: boolean = false

/**
 * The icon of the status badge
 */
icon?: IconName | FunctionComponent<SVGProps<SVGSVGElement>>

/**
 * Optional label to display instead of the default status text
 */
label?: string = status

/**
 * Size of the status badge
 * @default 'medium'
 */
size?: (typeof statusBadgeSizes)[number] = 'medium'

/**
 * The value of the status badge
 */
status: (typeof dsStatuses)[number]

/**
 * Additional styles to apply to the component
 */
style?: CSSProperties
Imports
import { DsStatusBadge } from "@drivenets/design-system";
Default story ok
const Default = () => <DsStatusBadge icon="check_circle" status="active" />;
All story ok
const All = () => {
    const statuses: Record<DsStatus, IconType> = {
        active: 'check_circle',
        running: 'change_circle',
        pending: 'pause_circle',
        draft: 'stylus_note',
        inactive: 'stop_circle',
        warning: 'warning',
        failed: 'cancel',
    };

    return (
        <div className={styles.storiesContainer}>
            <div className={styles.storiesRow}>
                {/* Filled variants - Default */}
                <div>
                    <div className={styles.sectionTitle}>Filled</div>
                    <div className={styles.storiesList}>
                        {dsStatuses.map((status) => (
                            <DsStatusBadge key={`filled-24-${status}`} icon={statuses[status]} status={status} />
                        ))}
                    </div>
                </div>

                {/* Ghost variants - Default */}
                <div>
                    <div className={styles.sectionTitle}>Ghost</div>
                    <div className={styles.storiesList}>
                        {dsStatuses.map((status) => (
                            <DsStatusBadge
                                key={`ghost-24-${status}`}
                                icon={statuses[status]}
                                status={status}
                                ghost={true}
                            />
                        ))}
                    </div>
                </div>
            </div>

            <div className={styles.storiesRow}>
                {/* Filled variants - Small */}
                <div>
                    <div className={styles.sectionTitle}>Filled - Small</div>
                    <div className={styles.storiesList}>
                        {dsStatuses.map((status) => (
                            <DsStatusBadge
                                key={`filled-20-${status}`}
                                icon={statuses[status]}
                                status={status}
                                size="small"
                            />
                        ))}
                    </div>
                </div>

                {/* Ghost variants - Small */}
                <div>
                    <div className={styles.sectionTitle}>Ghost - Small</div>
                    <div className={styles.storiesList}>
                        {dsStatuses.map((status) => (
                            <DsStatusBadge
                                key={`ghost-20-${status}`}
                                icon={statuses[status]}
                                status={status}
                                ghost={true}
                                size="small"
                            />
                        ))}
                    </div>
                </div>
            </div>
        </div>
    );
};

DsStatusBadgeV2

components-statusbadgev2 · ./src/components/ds-status-badge-v2/ds-status-badge-v2.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 9 prop types
Component: src/components/ds-status-badge-v2/ds-status-badge-v2.tsx::default
Props:
aria-label?: string

className?: string

iconOnly?: any = false

label: string

/**
 * Lifecycle phase that determines color and default icon.
 * Each phase maps to a color palette and a default Material Symbols icon.
 */
phase: (typeof statusBadgeV2Phases)[number]

ref?: Ref<HTMLElement>

/**
 * Badge size controlling height, padding, and icon dimensions. Responsive.
 * @default 'medium'
 */
size?: (typeof statusBadgeV2Sizes)[number] = 'medium'

style?: CSSProperties

/**
 * Visual variant controlling background treatment.
 * - `'primary'`: tinted background pill or circle
 * - `'secondary'`: no background, inline presentation
 * @default 'primary'
 */
variant?: (typeof statusBadgeV2Variants)[number] = 'primary'
Imports
import {
    DsIcon,
    DsStack,
    DsStatusBadgeV2,
    DsStatusBadgeV2 as DsStatusBadgeV2Responsive,
} from "@drivenets/design-system";
Default story ok
const Default = () => <DsStatusBadgeV2 phase="active" label="Active" />;
All Phases story ok
const AllPhases = () => (
    <DsStack direction="column" gap="var(--xl)">
        <DsStack direction="column" gap="var(--xs)">
            <div className={styles.sectionTitle}>Primary — Medium</div>
            <DsStack direction="row" flexWrap="wrap" gap="var(--standard)" alignItems="center">
                <DsStatusBadgeV2 phase="not-started" label={phaseLabels['not-started']} />
                <DsStatusBadgeV2 phase="temporary" label={phaseLabels.temporary} />
                <DsStatusBadgeV2 phase="in-review" label={phaseLabels['in-review']} />
                <DsStatusBadgeV2 phase="pending" label={phaseLabels.pending} />
                <DsStatusBadgeV2 phase="active" label={phaseLabels.active} />
                <DsStatusBadgeV2 phase="execution" label={phaseLabels.execution} />
                <DsStatusBadgeV2 phase="result-succeeded" label={phaseLabels['result-succeeded']} />
                <DsStatusBadgeV2 phase="result-warning" label={phaseLabels['result-warning']} />
                <DsStatusBadgeV2 phase="result-failed" label={phaseLabels['result-failed']} />
                <DsStatusBadgeV2 phase="deprecated" label={phaseLabels.deprecated} />
            </DsStack>
        </DsStack>

        <DsStack direction="column" gap="var(--xs)">
            <div className={styles.sectionTitle}>Primary — Small</div>
            <DsStack direction="row" flexWrap="wrap" gap="var(--standard)" alignItems="center">
                <DsStatusBadgeV2 phase="not-started" label={phaseLabels['not-started']} size="small" />
                <DsStatusBadgeV2 phase="temporary" label={phaseLabels.temporary} size="small" />
                <DsStatusBadgeV2 phase="in-review" label={phaseLabels['in-review']} size="small" />
                <DsStatusBadgeV2 phase="pending" label={phaseLabels.pending} size="small" />
                <DsStatusBadgeV2 phase="active" label={phaseLabels.active} size="small" />
                <DsStatusBadgeV2 phase="execution" label={phaseLabels.execution} size="small" />
                <DsStatusBadgeV2 phase="result-succeeded" label={phaseLabels['result-succeeded']} size="small" />
                <DsStatusBadgeV2 phase="result-warning" label={phaseLabels['result-warning']} size="small" />
                <DsStatusBadgeV2 phase="result-failed" label={phaseLabels['result-failed']} size="small" />
                <DsStatusBadgeV2 phase="deprecated" label={phaseLabels.deprecated} size="small" />
            </DsStack>
        </DsStack>

        <DsStack direction="column" gap="var(--xs)">
            <div className={styles.sectionTitle}>Secondary — Medium</div>
            <DsStack direction="row" flexWrap="wrap" gap="var(--standard)" alignItems="center">
                <DsStatusBadgeV2 phase="not-started" label={phaseLabels['not-started']} variant="secondary" />
                <DsStatusBadgeV2 phase="temporary" label={phaseLabels.temporary} variant="secondary" />
                <DsStatusBadgeV2 phase="in-review" label={phaseLabels['in-review']} variant="secondary" />
                <DsStatusBadgeV2 phase="pending" label={phaseLabels.pending} variant="secondary" />
                <DsStatusBadgeV2 phase="active" label={phaseLabels.active} variant="secondary" />
                <DsStatusBadgeV2 phase="execution" label={phaseLabels.execution} variant="secondary" />
                <DsStatusBadgeV2
                    phase="result-succeeded"
                    label={phaseLabels['result-succeeded']}
                    variant="secondary"
                />
                <DsStatusBadgeV2 phase="result-warning" label={phaseLabels['result-warning']} variant="secondary" />
                <DsStatusBadgeV2 phase="result-failed" label={phaseLabels['result-failed']} variant="secondary" />
                <DsStatusBadgeV2 phase="deprecated" label={phaseLabels.deprecated} variant="secondary" />
            </DsStack>
        </DsStack>

        <DsStack direction="column" gap="var(--xs)">
            <div className={styles.sectionTitle}>Secondary — Small</div>
            <DsStack direction="row" flexWrap="wrap" gap="var(--standard)" alignItems="center">
                <DsStatusBadgeV2
                    phase="not-started"
                    label={phaseLabels['not-started']}
                    variant="secondary"
                    size="small"
                />
                <DsStatusBadgeV2 phase="temporary" label={phaseLabels.temporary} variant="secondary" size="small" />
                <DsStatusBadgeV2
                    phase="in-review"
                    label={phaseLabels['in-review']}
                    variant="secondary"
                    size="small"
                />
                <DsStatusBadgeV2 phase="pending" label={phaseLabels.pending} variant="secondary" size="small" />
                <DsStatusBadgeV2 phase="active" label={phaseLabels.active} variant="secondary" size="small" />
                <DsStatusBadgeV2 phase="execution" label={phaseLabels.execution} variant="secondary" size="small" />
                <DsStatusBadgeV2
                    phase="result-succeeded"
                    label={phaseLabels['result-succeeded']}
                    variant="secondary"
                    size="small"
                />
                <DsStatusBadgeV2
                    phase="result-warning"
                    label={phaseLabels['result-warning']}
                    variant="secondary"
                    size="small"
                />
                <DsStatusBadgeV2
                    phase="result-failed"
                    label={phaseLabels['result-failed']}
                    variant="secondary"
                    size="small"
                />
                <DsStatusBadgeV2
                    phase="deprecated"
                    label={phaseLabels.deprecated}
                    variant="secondary"
                    size="small"
                />
            </DsStack>
        </DsStack>
    </DsStack>
);
Icon Only story ok
const IconOnly = () => (
    <DsStack direction="column" gap="var(--xl)">
        <DsStack direction="column" gap="var(--xs)">
            <div className={styles.sectionTitle}>Icon-only (hover for tooltip)</div>
            <DsStack direction="row" flexWrap="wrap" gap="var(--standard)" alignItems="center">
                <DsStatusBadgeV2 phase="not-started" label={phaseLabels['not-started']} iconOnly />
                <DsStatusBadgeV2 phase="temporary" label={phaseLabels.temporary} iconOnly />
                <DsStatusBadgeV2 phase="in-review" label={phaseLabels['in-review']} iconOnly />
                <DsStatusBadgeV2 phase="pending" label={phaseLabels.pending} iconOnly />
                <DsStatusBadgeV2 phase="active" label={phaseLabels.active} iconOnly />
                <DsStatusBadgeV2 phase="execution" label={phaseLabels.execution} iconOnly />
                <DsStatusBadgeV2 phase="result-succeeded" label={phaseLabels['result-succeeded']} iconOnly />
                <DsStatusBadgeV2 phase="result-warning" label={phaseLabels['result-warning']} iconOnly />
                <DsStatusBadgeV2 phase="result-failed" label={phaseLabels['result-failed']} iconOnly />
                <DsStatusBadgeV2 phase="deprecated" label={phaseLabels.deprecated} iconOnly />
            </DsStack>
        </DsStack>

        <DsStack direction="column" gap="var(--xs)">
            <div className={styles.sectionTitle}>Icon-only — Small</div>
            <DsStack direction="row" flexWrap="wrap" gap="var(--standard)" alignItems="center">
                <DsStatusBadgeV2 phase="not-started" label={phaseLabels['not-started']} iconOnly size="small" />
                <DsStatusBadgeV2 phase="temporary" label={phaseLabels.temporary} iconOnly size="small" />
                <DsStatusBadgeV2 phase="in-review" label={phaseLabels['in-review']} iconOnly size="small" />
                <DsStatusBadgeV2 phase="pending" label={phaseLabels.pending} iconOnly size="small" />
                <DsStatusBadgeV2 phase="active" label={phaseLabels.active} iconOnly size="small" />
                <DsStatusBadgeV2 phase="execution" label={phaseLabels.execution} iconOnly size="small" />
                <DsStatusBadgeV2
                    phase="result-succeeded"
                    label={phaseLabels['result-succeeded']}
                    iconOnly
                    size="small"
                />
                <DsStatusBadgeV2
                    phase="result-warning"
                    label={phaseLabels['result-warning']}
                    iconOnly
                    size="small"
                />
                <DsStatusBadgeV2 phase="result-failed" label={phaseLabels['result-failed']} iconOnly size="small" />
                <DsStatusBadgeV2 phase="deprecated" label={phaseLabels.deprecated} iconOnly size="small" />
            </DsStack>
        </DsStack>

        <DsStack direction="column" gap="var(--xs)">
            <div className={styles.sectionTitle}>Icon-only Secondary</div>
            <DsStack direction="row" flexWrap="wrap" gap="var(--standard)" alignItems="center">
                <DsStatusBadgeV2
                    phase="not-started"
                    label={phaseLabels['not-started']}
                    iconOnly
                    variant="secondary"
                />
                <DsStatusBadgeV2 phase="temporary" label={phaseLabels.temporary} iconOnly variant="secondary" />
                <DsStatusBadgeV2 phase="in-review" label={phaseLabels['in-review']} iconOnly variant="secondary" />
                <DsStatusBadgeV2 phase="pending" label={phaseLabels.pending} iconOnly variant="secondary" />
                <DsStatusBadgeV2 phase="active" label={phaseLabels.active} iconOnly variant="secondary" />
                <DsStatusBadgeV2 phase="execution" label={phaseLabels.execution} iconOnly variant="secondary" />
                <DsStatusBadgeV2
                    phase="result-succeeded"
                    label={phaseLabels['result-succeeded']}
                    iconOnly
                    variant="secondary"
                />
                <DsStatusBadgeV2
                    phase="result-warning"
                    label={phaseLabels['result-warning']}
                    iconOnly
                    variant="secondary"
                />
                <DsStatusBadgeV2
                    phase="result-failed"
                    label={phaseLabels['result-failed']}
                    iconOnly
                    variant="secondary"
                />
                <DsStatusBadgeV2 phase="deprecated" label={phaseLabels.deprecated} iconOnly variant="secondary" />
            </DsStack>
        </DsStack>
    </DsStack>
);
Text Only story ok
const TextOnly = () => (
    <DsStack direction="column" gap="var(--xs)">
        <div className={styles.sectionTitle}>Text-only (icon=null)</div>
        <DsStack direction="row" flexWrap="wrap" gap="var(--standard)" alignItems="center">
            <DsStatusBadgeV2 phase="not-started" label={phaseLabels['not-started']} icon={null} />
            <DsStatusBadgeV2 phase="temporary" label={phaseLabels.temporary} icon={null} />
            <DsStatusBadgeV2 phase="in-review" label={phaseLabels['in-review']} icon={null} />
            <DsStatusBadgeV2 phase="pending" label={phaseLabels.pending} icon={null} />
            <DsStatusBadgeV2 phase="active" label={phaseLabels.active} icon={null} />
            <DsStatusBadgeV2 phase="execution" label={phaseLabels.execution} icon={null} />
            <DsStatusBadgeV2 phase="result-succeeded" label={phaseLabels['result-succeeded']} icon={null} />
            <DsStatusBadgeV2 phase="result-warning" label={phaseLabels['result-warning']} icon={null} />
            <DsStatusBadgeV2 phase="result-failed" label={phaseLabels['result-failed']} icon={null} />
            <DsStatusBadgeV2 phase="deprecated" label={phaseLabels.deprecated} icon={null} />
        </DsStack>
    </DsStack>
);
Phase Reference story ok
const PhaseReference = () => (
    <DsStack direction="column" gap="var(--xs)">
        <table className={styles.docsTable}>
            <thead>
                <tr>
                    <th>Phase</th>
                    <th>Badge</th>
                    <th>Icon</th>
                    <th>Example Domain Statuses</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td>
                        <code>not-started</code>
                    </td>
                    <td>
                        <DsStatusBadgeV2 phase="not-started" label={phaseLabels['not-started']} />
                    </td>
                    <td>
                        <DsIcon icon={phaseIconMap['not-started']} size="tiny" filled />
                    </td>
                    <td>{phaseExamples['not-started']}</td>
                </tr>
                <tr>
                    <td>
                        <code>temporary</code>
                    </td>
                    <td>
                        <DsStatusBadgeV2 phase="temporary" label={phaseLabels.temporary} />
                    </td>
                    <td>
                        <DsIcon icon={phaseIconMap.temporary} size="tiny" filled />
                    </td>
                    <td>{phaseExamples.temporary}</td>
                </tr>
                <tr>
                    <td>
                        <code>in-review</code>
                    </td>
                    <td>
                        <DsStatusBadgeV2 phase="in-review" label={phaseLabels['in-review']} />
                    </td>
                    <td>
                        <DsIcon icon={phaseIconMap['in-review']} size="tiny" filled />
                    </td>
                    <td>{phaseExamples['in-review']}</td>
                </tr>
                <tr>
                    <td>
                        <code>pending</code>
                    </td>
                    <td>
                        <DsStatusBadgeV2 phase="pending" label={phaseLabels.pending} />
                    </td>
                    <td>
                        <DsIcon icon={phaseIconMap.pending} size="tiny" filled />
                    </td>
                    <td>{phaseExamples.pending}</td>
                </tr>
                <tr>
                    <td>
                        <code>active</code>
                    </td>
                    <td>
                        <DsStatusBadgeV2 phase="active" label={phaseLabels.active} />
                    </td>
                    <td>
                        <DsIcon icon={phaseIconMap.active} size="tiny" filled />
                    </td>
                    <td>{phaseExamples.active}</td>
                </tr>
                <tr>
                    <td>
                        <code>execution</code>
                    </td>
                    <td>
                        <DsStatusBadgeV2 phase="execution" label={phaseLabels.execution} />
                    </td>
                    <td>
                        <DsIcon icon={phaseIconMap.execution} size="tiny" filled />
                    </td>
                    <td>{phaseExamples.execution}</td>
                </tr>
                <tr>
                    <td>
                        <code>result-succeeded</code>
                    </td>
                    <td>
                        <DsStatusBadgeV2 phase="result-succeeded" label={phaseLabels['result-succeeded']} />
                    </td>
                    <td>
                        <DsIcon icon={phaseIconMap['result-succeeded']} size="tiny" filled />
                    </td>
                    <td>{phaseExamples['result-succeeded']}</td>
                </tr>
                <tr>
                    <td>
                        <code>result-warning</code>
                    </td>
                    <td>
                        <DsStatusBadgeV2 phase="result-warning" label={phaseLabels['result-warning']} />
                    </td>
                    <td>
                        <DsIcon icon={phaseIconMap['result-warning']} size="tiny" filled />
                    </td>
                    <td>{phaseExamples['result-warning']}</td>
                </tr>
                <tr>
                    <td>
                        <code>result-failed</code>
                    </td>
                    <td>
                        <DsStatusBadgeV2 phase="result-failed" label={phaseLabels['result-failed']} />
                    </td>
                    <td>
                        <DsIcon icon={phaseIconMap['result-failed']} size="tiny" filled />
                    </td>
                    <td>{phaseExamples['result-failed']}</td>
                </tr>
                <tr>
                    <td>
                        <code>deprecated</code>
                    </td>
                    <td>
                        <DsStatusBadgeV2 phase="deprecated" label={phaseLabels.deprecated} />
                    </td>
                    <td>
                        <DsIcon icon={phaseIconMap.deprecated} size="tiny" filled />
                    </td>
                    <td>{phaseExamples.deprecated}</td>
                </tr>
            </tbody>
        </table>
    </DsStack>
);
Responsive Size story ok
const ResponsiveSize = () => (
    <DsStack direction="row" gap="var(--sm)" alignItems="center" flexWrap="wrap">
        <DsStatusBadgeV2Responsive
            phase="active"
            label="lg: medium / md: small"
            size={{ lg: 'medium', md: 'small' }}
        />
        <DsStatusBadgeV2Responsive
            phase="pending"
            label="lg: small / md: medium"
            size={{ lg: 'small', md: 'medium' }}
        />
        <DsStatusBadgeV2Responsive phase="execution" label="static: medium" size="medium" />
    </DsStack>
);

DsStepper

components-stepper · ./src/components/ds-stepper/ds-stepper.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 2 prop types
Component: src/components/ds-stepper/ds-stepper.tsx::DsStepper
Props:
floating?: any = false

orientation?: any = 'vertical'
Imports
import { DsIcon, DsNextStepButton, DsPanel, DsStep, DsStepContent, DsStepper } from "@drivenets/design-system";
Default story ok
const Default = function Render() {
    return (
        <div style={{ width: 300 }}>
            <DsStepper count={steps.length}>
                {steps.map((step, index) => (
                    <DsStep index={index} key={index}>
                        <DsStepContent
                            index={index}
                            label={step.label}
                            description={step.description}
                            actions={
                                <DsNextStepButton>{index === steps.length - 1 ? 'Finish' : 'Next'}</DsNextStepButton>
                            }
                        />
                    </DsStep>
                ))}
            </DsStepper>
        </div>
    );
};
Compact story ok
const Compact = function Render() {
    return (
        <div style={{ width: 300 }}>
            <DsStepper count={steps.length}>
                {steps.map((step, index) => (
                    <DsStep index={index} key={index}>
                        <DsStepContent
                            index={index}
                            label={step.label}
                            actions={
                                <DsNextStepButton>{index === steps.length - 1 ? 'Finish' : 'Next'}</DsNextStepButton>
                            }
                        />
                    </DsStep>
                ))}
            </DsStepper>
        </div>
    );
};
With Panel story ok
const WithPanel = function Render() {
    const [activeStep, setActiveStep] = useState(0);
    const [panelVariant, setPanelVariant] = useState<DsPanelVariant>('docked');

    const isFloating = panelVariant === 'floating';

    const togglePanelVariant = () => {
        setPanelVariant(isFloating ? 'docked' : 'floating');
    };

    return (
        <DsPanel
            open
            variant={panelVariant}
            draggable={isFloating}
            disablePadding={isFloating}
            slotProps={{
                collapseButton: {
                    onClick: togglePanelVariant,
                    collapsed: isFloating,
                },
            }}
        >
            <DsStepper
                count={steps.length}
                activeStep={activeStep}
                onStepChange={({ step }) => setActiveStep(step)}
                variant={isFloating ? 'single' : undefined}
                floating={isFloating}
            >
                {steps.map((step, index) => (
                    <DsStep index={index} key={index}>
                        <DsStepContent
                            index={index}
                            label={step.label}
                            description={step.description}
                            actions={
                                <DsNextStepButton>{index === steps.length - 1 ? 'Finish' : 'Next'}</DsNextStepButton>
                            }
                        />
                    </DsStep>
                ))}
            </DsStepper>
        </DsPanel>
    );
};
Horizontal story ok
const Horizontal = function Render() {
    return (
        <DsStepper
            count={horizontalSteps.length}
            orientation="horizontal"
            actions={<DsNextStepButton>Next</DsNextStepButton>}
        >
            {horizontalSteps.map((step, index) => (
                <DsStep index={index} key={index}>
                    <DsStepContent index={index} label={step.label} description={step.description} />
                </DsStep>
            ))}
        </DsStepper>
    );
};
Horizontal Few Steps story ok
const HorizontalFewSteps = function Render() {
    return (
        <DsStepper
            count={fewSteps.length}
            orientation="horizontal"
            actions={<DsNextStepButton>Next</DsNextStepButton>}
        >
            {fewSteps.map((step, index) => (
                <DsStep index={index} key={index}>
                    <DsStepContent index={index} label={step.label} description={step.description} />
                </DsStep>
            ))}
        </DsStepper>
    );
};
Horizontal Compact Few Steps story ok
const HorizontalCompactFewSteps = function Render() {
    return (
        <DsStepper
            count={fewCompactSteps.length}
            orientation="horizontal"
            actions={<DsNextStepButton>Next</DsNextStepButton>}
        >
            {fewCompactSteps.map((step, index) => (
                <DsStep index={index} key={index}>
                    <DsStepContent index={index} label={step.label} />
                </DsStep>
            ))}
        </DsStepper>
    );
};
Customized Horizontal story ok
const CustomizedHorizontal = function Render() {
    const [activeStep, setActiveStep] = useState(0);

    return (
        <DsStepper
            count={customSteps.length}
            orientation="horizontal"
            activeStep={activeStep}
            onStepChange={({ step }) => setActiveStep(step)}
            actions={
                <DsNextStepButton variant="ghost">
                    {activeStep === customSteps.length - 1 ? 'Finish' : 'Continue'}
                </DsNextStepButton>
            }
        >
            {customSteps.map((step, index) => (
                <DsStep index={index} key={index} slots={{ indicator: <DsIcon icon={step.icon} size="small" /> }}>
                    <DsStepContent index={index} label={step.label} description={step.description} />
                </DsStep>
            ))}
        </DsStepper>
    );
};
Customized Vertical story ok
const CustomizedVertical = function Render() {
    const [activeStep, setActiveStep] = useState(0);

    return (
        <div style={{ width: 350 }}>
            <DsStepper count={3} activeStep={activeStep} onStepChange={({ step }) => setActiveStep(step)}>
                <DsStep index={0}>
                    <DsStepContent
                        index={0}
                        label="Project details"
                        description="Enter project name and basic configuration"
                        actions={<DsNextStepButton>Next</DsNextStepButton>}
                    />
                </DsStep>

                <DsStep
                    index={1}
                    className={activeStep === 1 ? styles.approveStep : undefined}
                    slots={{
                        indicator: <DsIcon icon="monitor_heart" size="small" />,
                    }}
                    slotProps={{
                        indicator: {
                            className: activeStep === 1 ? styles.approveIcon : undefined,
                        },
                    }}
                >
                    <DsStepContent
                        index={1}
                        label={
                            <span className={activeStep === 1 ? styles.approveTitle : undefined}>Verify health</span>
                        }
                        description="Confirm all services report healthy status"
                        actions={
                            <DsNextStepButton className={activeStep === 1 ? styles.approveButton : undefined}>
                                Approve
                            </DsNextStepButton>
                        }
                    />
                </DsStep>

                <DsStep index={2}>
                    <DsStepContent
                        index={2}
                        label="Design policy"
                        description="Define the design constraints and rules"
                        actions={<DsNextStepButton>Finish</DsNextStepButton>}
                    />
                </DsStep>
            </DsStepper>
        </div>
    );
};
With Disabled Steps story ok
const WithDisabledSteps = function Render() {
    return (
        <div style={{ width: 350 }}>
            <DsStepper count={4}>
                <DsStep index={0}>
                    <DsStepContent
                        index={0}
                        label="Basic information"
                        description="Enter your project details"
                        actions={<DsNextStepButton>Next</DsNextStepButton>}
                    />
                </DsStep>

                <DsStep index={1} disabled>
                    <DsStepContent
                        index={1}
                        label="Advanced settings"
                        description="Configure advanced options (requires approval)"
                        actions={<DsNextStepButton>Next</DsNextStepButton>}
                    />
                </DsStep>

                <DsStep index={2}>
                    <DsStepContent
                        index={2}
                        label="Review"
                        description="Review your configuration"
                        actions={<DsNextStepButton>Next</DsNextStepButton>}
                    />
                </DsStep>

                <DsStep index={3} disabled>
                    <DsStepContent
                        index={3}
                        label="Deploy"
                        description="Deploy to production (requires elevated permissions)"
                        actions={<DsNextStepButton>Finish</DsNextStepButton>}
                    />
                </DsStep>
            </DsStepper>
        </div>
    );
};
With Error Step story ok
const WithErrorStep = function Render() {
    return (
        <div style={{ width: 350 }}>
            <DsStepper count={4}>
                <DsStep index={0}>
                    <DsStepContent
                        index={0}
                        label="Configuration"
                        description="Enter deployment configuration"
                        actions={<DsNextStepButton>Next</DsNextStepButton>}
                    />
                </DsStep>

                <DsStep index={1} variant="error">
                    <DsStepContent
                        index={1}
                        label="Validation"
                        description="Configuration validation failed"
                        actions={<DsNextStepButton>Retry</DsNextStepButton>}
                    />
                </DsStep>

                <DsStep index={2}>
                    <DsStepContent
                        index={2}
                        label="Review"
                        description="Review and confirm changes"
                        actions={<DsNextStepButton>Next</DsNextStepButton>}
                    />
                </DsStep>

                <DsStep index={3}>
                    <DsStepContent
                        index={3}
                        label="Complete"
                        description="Finalize deployment"
                        actions={<DsNextStepButton>Finish</DsNextStepButton>}
                    />
                </DsStep>
            </DsStepper>
        </div>
    );
};

DsSystemStatus

components-systemstatus-deprecated · ./src/components/ds-system-status/ds-system-status.stories.ts
deprecated: This component is deprecated. Use `DsStatusBadge` instead.see: {@link ../ds-status-badge/ds-status-badge.stories} for examples of the replacement component.
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 3 prop types
Component: src/components/ds-system-status/ds-system-status.tsx::default
Props:
/**
 * Additional CSS class names
 */
className?: string

/**
 * The label to be displayed
 */
label?: string

/**
 * The status of the system
 */
status: (typeof systemStatuses)[number]
Imports
import { DsSystemStatus } from "@drivenets/design-system";
Default story ok
const Default = () => <DsSystemStatus status="healthy" />;
Custom Label story ok
const CustomLabel = () => <DsSystemStatus status="error" label="Critical Error" />;

DsTable

components-table · ./src/components/ds-table/stories/ds-table.stories.tsx
Design system Table component
Prop types (react-docgen) 33 prop types
Component: src/components/ds-table/ds-table.tsx::default
Props:
/**
 * Actions to be shown in the bulk actions
 */
actions?: Action<TData>[] = []

/**
 * ID of the currently active row (useful for showing drawer/panel associations)
 * When set, the row with this ID will be visually highlighted with a persistent active state
 * @example
 * ```tsx
 * const [activeRowId, setActiveRowId] = useState<string | null>(null);
 * const [isDrawerOpen, setIsDrawerOpen] = useState(false);
 * 
 * <DsTable
 *   activeRowId={isDrawerOpen ? activeRowId : null}
 *   onRowClick={(row) => {
 *     setActiveRowId(row.id);
 *     setIsDrawerOpen(true);
 *   }}
 * />
 * ```
 */
activeRowId?: string | null

/**
 * Whether the table has bordered cells
 */
bordered?: boolean = true

/**
 * Class name of the table
 */
className?: string

/**
 * External column filters state
 */
columnFilters?: ColumnFiltersState

/**
 * Columns of the table
 */
columns: ColumnDef<TData, TValue>[]

/**
 * External column visibility state
 * @example
 * ```tsx
 * const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({
 *   age: false, // hide age column
 *   status: true, // show status column
 * });
 * 
 * <DsTable
 *   columnVisibility={columnVisibility}
 *   onColumnVisibilityChange={setColumnVisibility}
 * />
 * ```
 */
columnVisibility?: VisibilityState

/**
 * Data of the table
 */
data: TData[]

/**
 * Empty state of the table
 */
emptyState?: React.ReactNode

/**
 * Whether the table is expandable or if an individual row should be expandable
 */
expandable?: boolean | ((row: TData) => boolean) = false

/**
 * Whether the table is full width
 */
fullWidth?: boolean = true

/**
 * Configures infinite scroll for the virtualized table. The Table owns viewport/scroll
 * detection and the auto-fill loop; the consumer owns fetching, pagination state,
 * error handling, and retry.
 * 
 * Only takes effect when `virtualized` is `true`. Passing this prop without
 * `virtualized` is ignored.
 * 
 * @example
 * ```tsx
 * <DsTable
 *   virtualized
 *   data={rows}
 *   columns={columns}
 *   infiniteScroll={{
 *     hasMore,
 *     isLoadingMore,
 *     onLoadMore: fetchNextPage,
 *   }}
 * />
 * ```
 */
infiniteScroll?: InfiniteScrollConfig

/**
 * Callback when column filters change
 */
onColumnFiltersChange?: (filters: ColumnFiltersState) => void

/**
 * Callback when column visibility changes
 */
onColumnVisibilityChange?: (visibility: VisibilityState) => void

/**
 * Callback when the order of rows changes via drag & drop
 */
onOrderChange?: (newData: TData[]) => void

/**
 * Function to handle row click
 */
onRowClick?: (row: TData) => void

/**
 * Function to handle row double click
 */
onRowDoubleClick?: (row: TData) => void

/**
 * Function to handle scroll events in virtualized tables.
 * Called when the user scrolls within the table container.
 * 
 * @param params - Scroll parameters containing scroll position and dimensions
 * 
 * @example
 * ```tsx
 * const handleScroll = ({ scrollOffset, totalContentHeight, viewportHeight }) => {
 *   const distanceFromBottom = totalContentHeight - scrollOffset - viewportHeight;
 *   if (distanceFromBottom < 500) {
 *     // Fetch more data when within 500px of bottom
 *     fetchNextPage();
 *   }
 * };
 * ```
 */
onScroll?: (params: ScrollParams) => void

/**
 * Function to handle selection change
 */
onSelectionChange?: (selectedRows: Record<string, boolean>) => void

/**
 * Function to handle sorting change
 */
onSortingChange?: (sorting: SortingState) => void

/**
 * Function to handle table creation
 */
onTableCreated?: (table: Table<TData>) => void

/**
 * Primary actions to be shown on each row (on hover)
 */
primaryRowActions?: RowAction<TData>[] = []

/**
 * Ref to the table API
 */
ref?: React.RefObject<DsTableApi<TData> | null>

/**
 * Function to render the expanded row
 */
renderExpandedRow?: (row: TData) => React.ReactNode

/**
 * Whether the table rows are reorderable via drag & drop
 * @note This feature does not work when virtualization is enabled
 */
reorderable?: boolean = false

/**
 * Row size variant (small: 36px, medium: 48px, large: 64px)
 * @default 'medium'
 */
rowSize?: 'small' | 'medium' | 'large' = 'medium'

/**
 * Secondary actions to be shown in a dropdown on each row (on hover)
 */
secondaryRowActions?: SecondaryRowAction<TData>[]

/**
 * Whether the table rows are selectable. Can be a boolean or a function that receives row data and returns whether it can be selected.
 * @default false
 * @example
 * ```tsx
 * // Enable selection for all rows
 * selectable={true}
 * 
 * // Disable selection for rows where status is 'archived'
 * selectable={(rowData) => rowData.status !== 'archived'}
 * 
 * // Limit selection to max 3 rows (note: use state to track current selection)
 * selectable={(rowData) => {
 *   // This will be called for each row during render
 *   const selectedIds = Object.keys(rowSelection).filter(id => rowSelection[id]);
 *   return selectedIds.includes(rowData.id) || selectedIds.length < 3;
 * }}
 * ```
 */
selectable?: boolean | ((rowData: TData) => boolean) = false

/**
 * Whether to show the select/deselect all checkbox in the header
 * @default true
 */
showSelectAllCheckbox?: boolean = true

/**
 * Whether the table has sticky header
 */
stickyHeader?: boolean = true

/**
 * Whether the table is virtualized
 * @default false
 */
virtualized?: boolean = false

/**
 * Options for the virtualized table
 */
virtualizedOptions?: {
	/**
	 * Estimate size of the table
	 */
	estimateSize?: number;

	/**
	 * Overscan of the table
	 */
	overscan?: number;
}

/**
 * Whether the table has zebra stripes
 */
zebra?: boolean
Imports
import { DsTable, TableEmptyState } from "@drivenets/design-system";
Default story ok
const Default = () => <DsTable
    columns={columns}
    stickyHeader
    bordered
    fullWidth
    expandable={false}
    emptyState={<TableEmptyState />}
    onRowClick={fn()}
    data={defaultData} />;
Empty State story ok
const EmptyState = () => <DsTable
    columns={columns}
    stickyHeader
    bordered
    fullWidth
    expandable={false}
    emptyState={<TableEmptyState />}
    onRowClick={fn()}
    data={[]} />;
No Border story ok
const NoBorder = () => <DsTable
    columns={columns}
    stickyHeader
    bordered={false}
    fullWidth
    expandable={false}
    emptyState={<TableEmptyState />}
    onRowClick={fn()}
    data={defaultData} />;

DsTable

components-table-active-row · ./src/components/ds-table/stories/ds-table-active-row.stories.tsx
Design system Table component
Prop types (react-docgen) 33 prop types
Component: src/components/ds-table/ds-table.tsx::default
Props:
/**
 * Actions to be shown in the bulk actions
 */
actions?: Action<TData>[] = []

/**
 * ID of the currently active row (useful for showing drawer/panel associations)
 * When set, the row with this ID will be visually highlighted with a persistent active state
 * @example
 * ```tsx
 * const [activeRowId, setActiveRowId] = useState<string | null>(null);
 * const [isDrawerOpen, setIsDrawerOpen] = useState(false);
 * 
 * <DsTable
 *   activeRowId={isDrawerOpen ? activeRowId : null}
 *   onRowClick={(row) => {
 *     setActiveRowId(row.id);
 *     setIsDrawerOpen(true);
 *   }}
 * />
 * ```
 */
activeRowId?: string | null

/**
 * Whether the table has bordered cells
 */
bordered?: boolean = true

/**
 * Class name of the table
 */
className?: string

/**
 * External column filters state
 */
columnFilters?: ColumnFiltersState

/**
 * Columns of the table
 */
columns: ColumnDef<TData, TValue>[]

/**
 * External column visibility state
 * @example
 * ```tsx
 * const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({
 *   age: false, // hide age column
 *   status: true, // show status column
 * });
 * 
 * <DsTable
 *   columnVisibility={columnVisibility}
 *   onColumnVisibilityChange={setColumnVisibility}
 * />
 * ```
 */
columnVisibility?: VisibilityState

/**
 * Data of the table
 */
data: TData[]

/**
 * Empty state of the table
 */
emptyState?: React.ReactNode

/**
 * Whether the table is expandable or if an individual row should be expandable
 */
expandable?: boolean | ((row: TData) => boolean) = false

/**
 * Whether the table is full width
 */
fullWidth?: boolean = true

/**
 * Configures infinite scroll for the virtualized table. The Table owns viewport/scroll
 * detection and the auto-fill loop; the consumer owns fetching, pagination state,
 * error handling, and retry.
 * 
 * Only takes effect when `virtualized` is `true`. Passing this prop without
 * `virtualized` is ignored.
 * 
 * @example
 * ```tsx
 * <DsTable
 *   virtualized
 *   data={rows}
 *   columns={columns}
 *   infiniteScroll={{
 *     hasMore,
 *     isLoadingMore,
 *     onLoadMore: fetchNextPage,
 *   }}
 * />
 * ```
 */
infiniteScroll?: InfiniteScrollConfig

/**
 * Callback when column filters change
 */
onColumnFiltersChange?: (filters: ColumnFiltersState) => void

/**
 * Callback when column visibility changes
 */
onColumnVisibilityChange?: (visibility: VisibilityState) => void

/**
 * Callback when the order of rows changes via drag & drop
 */
onOrderChange?: (newData: TData[]) => void

/**
 * Function to handle row click
 */
onRowClick?: (row: TData) => void

/**
 * Function to handle row double click
 */
onRowDoubleClick?: (row: TData) => void

/**
 * Function to handle scroll events in virtualized tables.
 * Called when the user scrolls within the table container.
 * 
 * @param params - Scroll parameters containing scroll position and dimensions
 * 
 * @example
 * ```tsx
 * const handleScroll = ({ scrollOffset, totalContentHeight, viewportHeight }) => {
 *   const distanceFromBottom = totalContentHeight - scrollOffset - viewportHeight;
 *   if (distanceFromBottom < 500) {
 *     // Fetch more data when within 500px of bottom
 *     fetchNextPage();
 *   }
 * };
 * ```
 */
onScroll?: (params: ScrollParams) => void

/**
 * Function to handle selection change
 */
onSelectionChange?: (selectedRows: Record<string, boolean>) => void

/**
 * Function to handle sorting change
 */
onSortingChange?: (sorting: SortingState) => void

/**
 * Function to handle table creation
 */
onTableCreated?: (table: Table<TData>) => void

/**
 * Primary actions to be shown on each row (on hover)
 */
primaryRowActions?: RowAction<TData>[] = []

/**
 * Ref to the table API
 */
ref?: React.RefObject<DsTableApi<TData> | null>

/**
 * Function to render the expanded row
 */
renderExpandedRow?: (row: TData) => React.ReactNode

/**
 * Whether the table rows are reorderable via drag & drop
 * @note This feature does not work when virtualization is enabled
 */
reorderable?: boolean = false

/**
 * Row size variant (small: 36px, medium: 48px, large: 64px)
 * @default 'medium'
 */
rowSize?: 'small' | 'medium' | 'large' = 'medium'

/**
 * Secondary actions to be shown in a dropdown on each row (on hover)
 */
secondaryRowActions?: SecondaryRowAction<TData>[]

/**
 * Whether the table rows are selectable. Can be a boolean or a function that receives row data and returns whether it can be selected.
 * @default false
 * @example
 * ```tsx
 * // Enable selection for all rows
 * selectable={true}
 * 
 * // Disable selection for rows where status is 'archived'
 * selectable={(rowData) => rowData.status !== 'archived'}
 * 
 * // Limit selection to max 3 rows (note: use state to track current selection)
 * selectable={(rowData) => {
 *   // This will be called for each row during render
 *   const selectedIds = Object.keys(rowSelection).filter(id => rowSelection[id]);
 *   return selectedIds.includes(rowData.id) || selectedIds.length < 3;
 * }}
 * ```
 */
selectable?: boolean | ((rowData: TData) => boolean) = false

/**
 * Whether to show the select/deselect all checkbox in the header
 * @default true
 */
showSelectAllCheckbox?: boolean = true

/**
 * Whether the table has sticky header
 */
stickyHeader?: boolean = true

/**
 * Whether the table is virtualized
 * @default false
 */
virtualized?: boolean = false

/**
 * Options for the virtualized table
 */
virtualizedOptions?: {
	/**
	 * Estimate size of the table
	 */
	estimateSize?: number;

	/**
	 * Overscan of the table
	 */
	overscan?: number;
}

/**
 * Whether the table has zebra stripes
 */
zebra?: boolean
Imports
import { DsDrawer, DsIcon, DsTable, ProgressInfographic, TableEmptyState } from "@drivenets/design-system";
Active Row with Drawer story ok
const WithDrawerAndActiveRow = () => {
    const [selectedPerson, setSelectedPerson] = useState<Person | null>(null);

    const activeRowId = selectedPerson?.id;
    const isDrawerOpen = !!activeRowId;

    const handleRowClick = (person: Person) => {
        const isSameRow = activeRowId === person.id;

        setSelectedPerson(isSameRow ? null : person);
    };

    return (
        <div>
            <div className={styles.programmaticSelectionDemo}>
                <h4 className={styles.programmaticSelectionDemo__title}>Active Row with Drawer Demo</h4>
                <p className={styles.programmaticSelectionDemo__description}>Click on any row to open a drawer with detailed information. The clicked row will remain
                                            highlighted to indicate which record the drawer is displaying.
                                        </p>
            </div>
            <DsTable
                columns={columns}
                data={defaultData.slice(0, 10)}
                stickyHeader
                bordered
                fullWidth
                expandable={false}
                emptyState={<TableEmptyState />}
                activeRowId={activeRowId}
                onRowClick={handleRowClick} />
            <DsDrawer
                open={isDrawerOpen}
                onOpenChange={(open) => {
                    if (!open) {
                        setSelectedPerson(null);
                    }
                }}
                columns={4}
                position="end">
                {selectedPerson && (
                    <div className={styles.drawerContent}>
                        <div className={styles.drawerHeader}>
                            <h2 className={styles.drawerTitle}>Person Details</h2>
                            <button
                                onClick={() => setSelectedPerson(null)}
                                className={styles.drawerCloseButton}
                                aria-label="Close drawer"
                            >
                                <DsIcon icon="close" size="medium" />
                            </button>
                        </div>

                        <div className={styles.drawerDetails}>
                            <div className={styles.drawerDetailItem}>
                                <strong className={styles.drawerDetailLabel}>Full Name</strong>
                                <p className={styles.drawerDetailValue}>
                                    {selectedPerson.firstName} {selectedPerson.lastName}
                                </p>
                            </div>

                            <div className={styles.drawerDetailItem}>
                                <strong className={styles.drawerDetailLabel}>Age</strong>
                                <p className={styles.drawerDetailValue}>{selectedPerson.age} years old</p>
                            </div>

                            <div className={styles.drawerDetailItem}>
                                <strong className={styles.drawerDetailLabel}>Visits</strong>
                                <p className={styles.drawerDetailValue}>{selectedPerson.visits} visits</p>
                            </div>

                            <div className={styles.drawerDetailItem}>
                                <strong className={styles.drawerDetailLabel}>Status</strong>
                                <p className={classnames(styles.drawerDetailValue, styles.drawerDetailValueCapitalized)}>
                                    {selectedPerson.status}
                                </p>
                            </div>

                            <div className={styles.drawerDetailItem}>
                                <strong className={styles.drawerDetailLabel}>Profile Progress</strong>
                                <div className={styles.drawerProgressContainer}>
                                    <ProgressInfographic value={selectedPerson.progress} />
                                </div>
                            </div>

                            <div className={styles.drawerNote}>
                                <p>
                                    <strong>Note:</strong> The row in the table remains highlighted while this drawer is open,
                                    helping you keep track of which record you&#39;re viewing.
                                </p>
                            </div>
                        </div>
                    </div>
                )}
            </DsDrawer>
        </div>
    );
};

DsTable

components-table-columns · ./src/components/ds-table/stories/ds-table-columns.stories.tsx
Design system Table component
Prop types (react-docgen) 33 prop types
Component: src/components/ds-table/ds-table.tsx::default
Props:
/**
 * Actions to be shown in the bulk actions
 */
actions?: Action<TData>[] = []

/**
 * ID of the currently active row (useful for showing drawer/panel associations)
 * When set, the row with this ID will be visually highlighted with a persistent active state
 * @example
 * ```tsx
 * const [activeRowId, setActiveRowId] = useState<string | null>(null);
 * const [isDrawerOpen, setIsDrawerOpen] = useState(false);
 * 
 * <DsTable
 *   activeRowId={isDrawerOpen ? activeRowId : null}
 *   onRowClick={(row) => {
 *     setActiveRowId(row.id);
 *     setIsDrawerOpen(true);
 *   }}
 * />
 * ```
 */
activeRowId?: string | null

/**
 * Whether the table has bordered cells
 */
bordered?: boolean = true

/**
 * Class name of the table
 */
className?: string

/**
 * External column filters state
 */
columnFilters?: ColumnFiltersState

/**
 * Columns of the table
 */
columns: ColumnDef<TData, TValue>[]

/**
 * External column visibility state
 * @example
 * ```tsx
 * const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({
 *   age: false, // hide age column
 *   status: true, // show status column
 * });
 * 
 * <DsTable
 *   columnVisibility={columnVisibility}
 *   onColumnVisibilityChange={setColumnVisibility}
 * />
 * ```
 */
columnVisibility?: VisibilityState

/**
 * Data of the table
 */
data: TData[]

/**
 * Empty state of the table
 */
emptyState?: React.ReactNode

/**
 * Whether the table is expandable or if an individual row should be expandable
 */
expandable?: boolean | ((row: TData) => boolean) = false

/**
 * Whether the table is full width
 */
fullWidth?: boolean = true

/**
 * Configures infinite scroll for the virtualized table. The Table owns viewport/scroll
 * detection and the auto-fill loop; the consumer owns fetching, pagination state,
 * error handling, and retry.
 * 
 * Only takes effect when `virtualized` is `true`. Passing this prop without
 * `virtualized` is ignored.
 * 
 * @example
 * ```tsx
 * <DsTable
 *   virtualized
 *   data={rows}
 *   columns={columns}
 *   infiniteScroll={{
 *     hasMore,
 *     isLoadingMore,
 *     onLoadMore: fetchNextPage,
 *   }}
 * />
 * ```
 */
infiniteScroll?: InfiniteScrollConfig

/**
 * Callback when column filters change
 */
onColumnFiltersChange?: (filters: ColumnFiltersState) => void

/**
 * Callback when column visibility changes
 */
onColumnVisibilityChange?: (visibility: VisibilityState) => void

/**
 * Callback when the order of rows changes via drag & drop
 */
onOrderChange?: (newData: TData[]) => void

/**
 * Function to handle row click
 */
onRowClick?: (row: TData) => void

/**
 * Function to handle row double click
 */
onRowDoubleClick?: (row: TData) => void

/**
 * Function to handle scroll events in virtualized tables.
 * Called when the user scrolls within the table container.
 * 
 * @param params - Scroll parameters containing scroll position and dimensions
 * 
 * @example
 * ```tsx
 * const handleScroll = ({ scrollOffset, totalContentHeight, viewportHeight }) => {
 *   const distanceFromBottom = totalContentHeight - scrollOffset - viewportHeight;
 *   if (distanceFromBottom < 500) {
 *     // Fetch more data when within 500px of bottom
 *     fetchNextPage();
 *   }
 * };
 * ```
 */
onScroll?: (params: ScrollParams) => void

/**
 * Function to handle selection change
 */
onSelectionChange?: (selectedRows: Record<string, boolean>) => void

/**
 * Function to handle sorting change
 */
onSortingChange?: (sorting: SortingState) => void

/**
 * Function to handle table creation
 */
onTableCreated?: (table: Table<TData>) => void

/**
 * Primary actions to be shown on each row (on hover)
 */
primaryRowActions?: RowAction<TData>[] = []

/**
 * Ref to the table API
 */
ref?: React.RefObject<DsTableApi<TData> | null>

/**
 * Function to render the expanded row
 */
renderExpandedRow?: (row: TData) => React.ReactNode

/**
 * Whether the table rows are reorderable via drag & drop
 * @note This feature does not work when virtualization is enabled
 */
reorderable?: boolean = false

/**
 * Row size variant (small: 36px, medium: 48px, large: 64px)
 * @default 'medium'
 */
rowSize?: 'small' | 'medium' | 'large' = 'medium'

/**
 * Secondary actions to be shown in a dropdown on each row (on hover)
 */
secondaryRowActions?: SecondaryRowAction<TData>[]

/**
 * Whether the table rows are selectable. Can be a boolean or a function that receives row data and returns whether it can be selected.
 * @default false
 * @example
 * ```tsx
 * // Enable selection for all rows
 * selectable={true}
 * 
 * // Disable selection for rows where status is 'archived'
 * selectable={(rowData) => rowData.status !== 'archived'}
 * 
 * // Limit selection to max 3 rows (note: use state to track current selection)
 * selectable={(rowData) => {
 *   // This will be called for each row during render
 *   const selectedIds = Object.keys(rowSelection).filter(id => rowSelection[id]);
 *   return selectedIds.includes(rowData.id) || selectedIds.length < 3;
 * }}
 * ```
 */
selectable?: boolean | ((rowData: TData) => boolean) = false

/**
 * Whether to show the select/deselect all checkbox in the header
 * @default true
 */
showSelectAllCheckbox?: boolean = true

/**
 * Whether the table has sticky header
 */
stickyHeader?: boolean = true

/**
 * Whether the table is virtualized
 * @default false
 */
virtualized?: boolean = false

/**
 * Options for the virtualized table
 */
virtualizedOptions?: {
	/**
	 * Estimate size of the table
	 */
	estimateSize?: number;

	/**
	 * Overscan of the table
	 */
	overscan?: number;
}

/**
 * Whether the table has zebra stripes
 */
zebra?: boolean
Imports
import { DsCheckbox, DsTable, ProgressInfographic, TableEmptyState } from "@drivenets/design-system";
Progress as Infographic story ok
const WithProgressInfographic = () => <DsTable
    columns={columns.map((col) => {
        if ('accessorKey' in col && col.accessorKey === 'progress') {
            return {
                ...col,
                header: 'Profile Progress',
                cell: (info) => <ProgressInfographic value={info.getValue() as number} />,
            } as ColumnDef<Person>;
        } else if ('accessorKey' in col && col.accessorKey === 'status') {
            return {
                ...col,
                header: 'Status',
                cell: (info) => (
                    <span
                        className={classnames(styles.statusCell, styles[`statusCell--${info.getValue() as Status}`])}
                    >
                        {info.getValue() as string}
                    </span>
                ),
            } as ColumnDef<Person>;
        }
        return col;
    })}
    data={defaultData}
    stickyHeader
    bordered
    fullWidth
    expandable={false}
    emptyState={<TableEmptyState />}
    onRowClick={(row) => console.log('Row clicked:', row)} />;
Column Hiding story ok
const ColumnHiding = () => {
    const columnsToToggle = [
        { id: 'age', label: 'Age' },
        { id: 'visits', label: 'Visits' },
        { id: 'status', label: 'Status' },
        { id: 'progress', label: 'Profile Progress' },
    ];
    const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({
        age: true,
        visits: true,
        status: true,
        progress: true,
    });

    const toggleColumn = (columnId: string) => {
        setColumnVisibility((prev) => ({
            ...prev,
            [columnId]: !prev[columnId],
        }));
    };

    return (
        <div>
            <div className={styles.programmaticSelectionDemo}>
                <h4 className={styles.programmaticSelectionDemo__title}>Column Hiding Demo</h4>
                <p className={styles.programmaticSelectionDemo__description}>Use the checkboxes below to show or hide specific columns dynamically. This is useful for
                                            customizable table views or responsive layouts.
                                        </p>
            </div>
            <div className={styles.programmaticSelectionControls}>
                {columnsToToggle.map((column) => (
                    <DsCheckbox
                        key={column.id}
                        label={column.label}
                        checked={columnVisibility[column.id]}
                        onCheckedChange={() => toggleColumn(column.id)}
                    />
                ))}
            </div>
            <DsTable
                columns={columns}
                data={defaultData}
                stickyHeader
                bordered
                fullWidth
                expandable={false}
                emptyState={<TableEmptyState />}
                onRowClick={(row) => console.log('Row clicked:', row)}
                columnVisibility={columnVisibility}
                onColumnVisibilityChange={setColumnVisibility} />
        </div>
    );
};

DsTable

components-table-expansion · ./src/components/ds-table/stories/ds-table-expansion.stories.tsx
Design system Table component
Prop types (react-docgen) 33 prop types
Component: src/components/ds-table/ds-table.tsx::default
Props:
/**
 * Actions to be shown in the bulk actions
 */
actions?: Action<TData>[] = []

/**
 * ID of the currently active row (useful for showing drawer/panel associations)
 * When set, the row with this ID will be visually highlighted with a persistent active state
 * @example
 * ```tsx
 * const [activeRowId, setActiveRowId] = useState<string | null>(null);
 * const [isDrawerOpen, setIsDrawerOpen] = useState(false);
 * 
 * <DsTable
 *   activeRowId={isDrawerOpen ? activeRowId : null}
 *   onRowClick={(row) => {
 *     setActiveRowId(row.id);
 *     setIsDrawerOpen(true);
 *   }}
 * />
 * ```
 */
activeRowId?: string | null

/**
 * Whether the table has bordered cells
 */
bordered?: boolean = true

/**
 * Class name of the table
 */
className?: string

/**
 * External column filters state
 */
columnFilters?: ColumnFiltersState

/**
 * Columns of the table
 */
columns: ColumnDef<TData, TValue>[]

/**
 * External column visibility state
 * @example
 * ```tsx
 * const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({
 *   age: false, // hide age column
 *   status: true, // show status column
 * });
 * 
 * <DsTable
 *   columnVisibility={columnVisibility}
 *   onColumnVisibilityChange={setColumnVisibility}
 * />
 * ```
 */
columnVisibility?: VisibilityState

/**
 * Data of the table
 */
data: TData[]

/**
 * Empty state of the table
 */
emptyState?: React.ReactNode

/**
 * Whether the table is expandable or if an individual row should be expandable
 */
expandable?: boolean | ((row: TData) => boolean) = false

/**
 * Whether the table is full width
 */
fullWidth?: boolean = true

/**
 * Configures infinite scroll for the virtualized table. The Table owns viewport/scroll
 * detection and the auto-fill loop; the consumer owns fetching, pagination state,
 * error handling, and retry.
 * 
 * Only takes effect when `virtualized` is `true`. Passing this prop without
 * `virtualized` is ignored.
 * 
 * @example
 * ```tsx
 * <DsTable
 *   virtualized
 *   data={rows}
 *   columns={columns}
 *   infiniteScroll={{
 *     hasMore,
 *     isLoadingMore,
 *     onLoadMore: fetchNextPage,
 *   }}
 * />
 * ```
 */
infiniteScroll?: InfiniteScrollConfig

/**
 * Callback when column filters change
 */
onColumnFiltersChange?: (filters: ColumnFiltersState) => void

/**
 * Callback when column visibility changes
 */
onColumnVisibilityChange?: (visibility: VisibilityState) => void

/**
 * Callback when the order of rows changes via drag & drop
 */
onOrderChange?: (newData: TData[]) => void

/**
 * Function to handle row click
 */
onRowClick?: (row: TData) => void

/**
 * Function to handle row double click
 */
onRowDoubleClick?: (row: TData) => void

/**
 * Function to handle scroll events in virtualized tables.
 * Called when the user scrolls within the table container.
 * 
 * @param params - Scroll parameters containing scroll position and dimensions
 * 
 * @example
 * ```tsx
 * const handleScroll = ({ scrollOffset, totalContentHeight, viewportHeight }) => {
 *   const distanceFromBottom = totalContentHeight - scrollOffset - viewportHeight;
 *   if (distanceFromBottom < 500) {
 *     // Fetch more data when within 500px of bottom
 *     fetchNextPage();
 *   }
 * };
 * ```
 */
onScroll?: (params: ScrollParams) => void

/**
 * Function to handle selection change
 */
onSelectionChange?: (selectedRows: Record<string, boolean>) => void

/**
 * Function to handle sorting change
 */
onSortingChange?: (sorting: SortingState) => void

/**
 * Function to handle table creation
 */
onTableCreated?: (table: Table<TData>) => void

/**
 * Primary actions to be shown on each row (on hover)
 */
primaryRowActions?: RowAction<TData>[] = []

/**
 * Ref to the table API
 */
ref?: React.RefObject<DsTableApi<TData> | null>

/**
 * Function to render the expanded row
 */
renderExpandedRow?: (row: TData) => React.ReactNode

/**
 * Whether the table rows are reorderable via drag & drop
 * @note This feature does not work when virtualization is enabled
 */
reorderable?: boolean = false

/**
 * Row size variant (small: 36px, medium: 48px, large: 64px)
 * @default 'medium'
 */
rowSize?: 'small' | 'medium' | 'large' = 'medium'

/**
 * Secondary actions to be shown in a dropdown on each row (on hover)
 */
secondaryRowActions?: SecondaryRowAction<TData>[]

/**
 * Whether the table rows are selectable. Can be a boolean or a function that receives row data and returns whether it can be selected.
 * @default false
 * @example
 * ```tsx
 * // Enable selection for all rows
 * selectable={true}
 * 
 * // Disable selection for rows where status is 'archived'
 * selectable={(rowData) => rowData.status !== 'archived'}
 * 
 * // Limit selection to max 3 rows (note: use state to track current selection)
 * selectable={(rowData) => {
 *   // This will be called for each row during render
 *   const selectedIds = Object.keys(rowSelection).filter(id => rowSelection[id]);
 *   return selectedIds.includes(rowData.id) || selectedIds.length < 3;
 * }}
 * ```
 */
selectable?: boolean | ((rowData: TData) => boolean) = false

/**
 * Whether to show the select/deselect all checkbox in the header
 * @default true
 */
showSelectAllCheckbox?: boolean = true

/**
 * Whether the table has sticky header
 */
stickyHeader?: boolean = true

/**
 * Whether the table is virtualized
 * @default false
 */
virtualized?: boolean = false

/**
 * Options for the virtualized table
 */
virtualizedOptions?: {
	/**
	 * Estimate size of the table
	 */
	estimateSize?: number;

	/**
	 * Overscan of the table
	 */
	overscan?: number;
}

/**
 * Whether the table has zebra stripes
 */
zebra?: boolean
Imports
import { DsTable, TableEmptyState } from "@drivenets/design-system";
Expandable story ok
const Expandable = () => <DsTable
    columns={columns}
    data={defaultData.slice(0, 5)}
    stickyHeader
    bordered
    fullWidth
    expandable={(row) => row.firstName !== 'Tanner'}
    emptyState={<TableEmptyState />}
    onRowClick={fn()}
    renderExpandedRow={(row) => (
        <>
            <div className={styles.expandedRowDetails}>
                <h4>Expanded Details for {row.firstName}</h4>
                <p>ID: {row.id}</p>
                <p>
                    Full Name: {row.firstName} {row.lastName}
                </p>
                <p>Status: {row.status}</p>
            </div>

            <DsTable
                columns={[
                    {
                        accessorKey: 'id',
                        header: 'ID',
                    },
                    {
                        accessorKey: 'firstName',
                        header: 'First Name',
                    },
                    {
                        accessorKey: 'lastName',
                        header: 'Last Name',
                    },
                ]}
                data={defaultData.slice(0, 3)}
            />
        </>
    )} />;
Programmatic Expansion story ok
const ProgrammaticExpansion = () => {
    const tableRef = useRef<DsTableApi<Person>>(null);
    const [expandedRows, setExpandedRows] = useState<string[]>([]);

    const expandRow = (rowId: string) => {
        tableRef.current?.expandRow(rowId);
        setExpandedRows((prev) => (prev.includes(rowId) ? prev : [...prev, rowId]));
    };

    const expandAllRows = () => {
        tableRef.current?.expandAllRows();
        const expandableRowIds = defaultData
            .slice(0, 5)
            .filter((row) => row.firstName !== 'Tanner')
            .map((row) => row.id);
        setExpandedRows(expandableRowIds);
    };

    const collapseAllRows = () => {
        tableRef.current?.collapseAllRows();
        setExpandedRows([]);
    };

    const expandFirstThreeRows = () => {
        const firstThreeIds = ['2', '3', '4'];
        tableRef.current?.expandRows(firstThreeIds);
        setExpandedRows(firstThreeIds);
    };

    return (
        <div>
            <div className={styles.programmaticSelectionDemo}>
                <h4 className={styles.programmaticSelectionDemo__title}>Programmatic Row Expansion Demo</h4>
                <p className={styles.programmaticSelectionDemo__description}>Use the buttons below to programmatically control row expansion using TanStack Table v8 APIs.
                                        </p>
                <p className={styles.programmaticSelectionDemo__selectedRows}>Expanded rows: {expandedRows.length > 0 ? expandedRows.join(', ') : 'None'}
                </p>
            </div>
            <div className={styles.programmaticSelectionControls}>
                <button
                    onClick={() => expandRow('2')}
                    className={styles.programmaticSelectionButton}>Expand Kevin
                                        </button>
                <button
                    onClick={() => expandRow('3')}
                    className={styles.programmaticSelectionButton}>Expand John
                                        </button>
                <button
                    onClick={() => expandRow('4')}
                    className={styles.programmaticSelectionButton}>Expand Jane
                                        </button>
                <button onClick={expandAllRows} className={styles.programmaticSelectionButton}>Expand All
                                        </button>
                <button onClick={collapseAllRows} className={styles.programmaticSelectionButton}>Collapse All
                                        </button>
                <button
                    onClick={expandFirstThreeRows}
                    className={styles.programmaticSelectionButton}>Expand First 3 Expandable
                                        </button>
            </div>
            <DsTable
                columns={columns}
                data={defaultData.slice(0, 5)}
                stickyHeader
                bordered
                fullWidth
                expandable={(row) => row.firstName !== 'Tanner'}
                emptyState={<TableEmptyState />}
                onRowClick={fn()}
                renderExpandedRow={(row) => (
                    <div className={styles.expandedRowDetails}>
                        <h4>Expanded Details for {row.firstName}</h4>
                        <p>ID: {row.id}</p>
                        <p>
                            Full Name: {row.firstName} {row.lastName}
                        </p>
                        <p>Status: {row.status}</p>
                    </div>
                )}
                ref={tableRef} />
        </div>
    );
};

DsTable

components-table-row-actions · ./src/components/ds-table/stories/ds-table-row-actions.stories.tsx
Design system Table component
Prop types (react-docgen) 33 prop types
Component: src/components/ds-table/ds-table.tsx::default
Props:
/**
 * Actions to be shown in the bulk actions
 */
actions?: Action<TData>[] = []

/**
 * ID of the currently active row (useful for showing drawer/panel associations)
 * When set, the row with this ID will be visually highlighted with a persistent active state
 * @example
 * ```tsx
 * const [activeRowId, setActiveRowId] = useState<string | null>(null);
 * const [isDrawerOpen, setIsDrawerOpen] = useState(false);
 * 
 * <DsTable
 *   activeRowId={isDrawerOpen ? activeRowId : null}
 *   onRowClick={(row) => {
 *     setActiveRowId(row.id);
 *     setIsDrawerOpen(true);
 *   }}
 * />
 * ```
 */
activeRowId?: string | null

/**
 * Whether the table has bordered cells
 */
bordered?: boolean = true

/**
 * Class name of the table
 */
className?: string

/**
 * External column filters state
 */
columnFilters?: ColumnFiltersState

/**
 * Columns of the table
 */
columns: ColumnDef<TData, TValue>[]

/**
 * External column visibility state
 * @example
 * ```tsx
 * const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({
 *   age: false, // hide age column
 *   status: true, // show status column
 * });
 * 
 * <DsTable
 *   columnVisibility={columnVisibility}
 *   onColumnVisibilityChange={setColumnVisibility}
 * />
 * ```
 */
columnVisibility?: VisibilityState

/**
 * Data of the table
 */
data: TData[]

/**
 * Empty state of the table
 */
emptyState?: React.ReactNode

/**
 * Whether the table is expandable or if an individual row should be expandable
 */
expandable?: boolean | ((row: TData) => boolean) = false

/**
 * Whether the table is full width
 */
fullWidth?: boolean = true

/**
 * Configures infinite scroll for the virtualized table. The Table owns viewport/scroll
 * detection and the auto-fill loop; the consumer owns fetching, pagination state,
 * error handling, and retry.
 * 
 * Only takes effect when `virtualized` is `true`. Passing this prop without
 * `virtualized` is ignored.
 * 
 * @example
 * ```tsx
 * <DsTable
 *   virtualized
 *   data={rows}
 *   columns={columns}
 *   infiniteScroll={{
 *     hasMore,
 *     isLoadingMore,
 *     onLoadMore: fetchNextPage,
 *   }}
 * />
 * ```
 */
infiniteScroll?: InfiniteScrollConfig

/**
 * Callback when column filters change
 */
onColumnFiltersChange?: (filters: ColumnFiltersState) => void

/**
 * Callback when column visibility changes
 */
onColumnVisibilityChange?: (visibility: VisibilityState) => void

/**
 * Callback when the order of rows changes via drag & drop
 */
onOrderChange?: (newData: TData[]) => void

/**
 * Function to handle row click
 */
onRowClick?: (row: TData) => void

/**
 * Function to handle row double click
 */
onRowDoubleClick?: (row: TData) => void

/**
 * Function to handle scroll events in virtualized tables.
 * Called when the user scrolls within the table container.
 * 
 * @param params - Scroll parameters containing scroll position and dimensions
 * 
 * @example
 * ```tsx
 * const handleScroll = ({ scrollOffset, totalContentHeight, viewportHeight }) => {
 *   const distanceFromBottom = totalContentHeight - scrollOffset - viewportHeight;
 *   if (distanceFromBottom < 500) {
 *     // Fetch more data when within 500px of bottom
 *     fetchNextPage();
 *   }
 * };
 * ```
 */
onScroll?: (params: ScrollParams) => void

/**
 * Function to handle selection change
 */
onSelectionChange?: (selectedRows: Record<string, boolean>) => void

/**
 * Function to handle sorting change
 */
onSortingChange?: (sorting: SortingState) => void

/**
 * Function to handle table creation
 */
onTableCreated?: (table: Table<TData>) => void

/**
 * Primary actions to be shown on each row (on hover)
 */
primaryRowActions?: RowAction<TData>[] = []

/**
 * Ref to the table API
 */
ref?: React.RefObject<DsTableApi<TData> | null>

/**
 * Function to render the expanded row
 */
renderExpandedRow?: (row: TData) => React.ReactNode

/**
 * Whether the table rows are reorderable via drag & drop
 * @note This feature does not work when virtualization is enabled
 */
reorderable?: boolean = false

/**
 * Row size variant (small: 36px, medium: 48px, large: 64px)
 * @default 'medium'
 */
rowSize?: 'small' | 'medium' | 'large' = 'medium'

/**
 * Secondary actions to be shown in a dropdown on each row (on hover)
 */
secondaryRowActions?: SecondaryRowAction<TData>[]

/**
 * Whether the table rows are selectable. Can be a boolean or a function that receives row data and returns whether it can be selected.
 * @default false
 * @example
 * ```tsx
 * // Enable selection for all rows
 * selectable={true}
 * 
 * // Disable selection for rows where status is 'archived'
 * selectable={(rowData) => rowData.status !== 'archived'}
 * 
 * // Limit selection to max 3 rows (note: use state to track current selection)
 * selectable={(rowData) => {
 *   // This will be called for each row during render
 *   const selectedIds = Object.keys(rowSelection).filter(id => rowSelection[id]);
 *   return selectedIds.includes(rowData.id) || selectedIds.length < 3;
 * }}
 * ```
 */
selectable?: boolean | ((rowData: TData) => boolean) = false

/**
 * Whether to show the select/deselect all checkbox in the header
 * @default true
 */
showSelectAllCheckbox?: boolean = true

/**
 * Whether the table has sticky header
 */
stickyHeader?: boolean = true

/**
 * Whether the table is virtualized
 * @default false
 */
virtualized?: boolean = false

/**
 * Options for the virtualized table
 */
virtualizedOptions?: {
	/**
	 * Estimate size of the table
	 */
	estimateSize?: number;

	/**
	 * Overscan of the table
	 */
	overscan?: number;
}

/**
 * Whether the table has zebra stripes
 */
zebra?: boolean
Imports
import { DsTable, TableEmptyState } from "@drivenets/design-system";
Reorderable story ok
const Reorderable = () => <DsTable
    columns={columns}
    data={defaultData.slice(0, 5)}
    stickyHeader
    bordered
    fullWidth
    expandable={false}
    emptyState={<TableEmptyState />}
    onRowClick={(row) => console.log('Row clicked:', row)}
    reorderable
    onOrderChange={fn()} />;
With Row Actions story ok
const WithRowActions = () => <DsTable
    columns={columns}
    data={defaultData}
    stickyHeader
    bordered
    fullWidth
    expandable={false}
    emptyState={<TableEmptyState />}
    onRowClick={fn()}
    primaryRowActions={[
        {
            icon: 'edit',
            label: 'Edit',
            onClick: editClickHandler,
        },
        {
            icon: 'open_in_new',
            label: 'Open in New Window',
            disabled: (data) => data.firstName === 'Tanner',
            onClick: openInNewWindowClickHandler,
        },
    ]}
    secondaryRowActions={[
        {
            icon: 'delete_outline',
            label: 'Delete',
            tooltip: 'Delete this row',
            disabled: (data) => data.status === 'single',
            className: styles.destructiveAction,
            onClick: fn(),
        },
        {
            icon: 'info',
            label: 'Details',
            tooltip: 'Show details',
            onClick: fn(),
        },
        {
            icon: 'call',
            label: (row) => `Call ${row.firstName}`,
            onClick: fn(),
        },
    ]} />;
With Conditionally Hidden Actions story ok
const WithConditionallyHiddenActions = () => <DsTable
    columns={columns}
    data={defaultData}
    stickyHeader
    bordered
    fullWidth
    expandable={false}
    emptyState={<TableEmptyState />}
    onRowClick={fn()}
    primaryRowActions={[
        {
            icon: 'edit',
            label: 'Edit',
            onClick: fn(),
        },
        {
            icon: 'open_in_new',
            label: 'Open in New Window',
            // hidden on 'complicated' rows (e.g. cannot open a record in a bad state)
            hidden: (data) => data.status === 'complicated',
            onClick: fn(),
        },
    ]}
    secondaryRowActions={[
        {
            icon: 'check_circle',
            label: 'Approve',
            // only shown on 'single' rows (pending approval)
            hidden: (data) => data.status !== 'single',
            onClick: fn(),
        },
        {
            icon: 'inventory_2',
            label: 'Archive',
            // only shown on 'relationship' rows (live records)
            hidden: (data) => data.status !== 'relationship',
            onClick: fn(),
        },
        {
            icon: 'delete_outline',
            label: 'Delete',
            // hidden on 'relationship' rows (cannot delete live records)
            hidden: (data) => data.status === 'relationship',
            className: styles.destructiveAction,
            onClick: fn(),
        },
        {
            icon: 'info',
            label: 'Details',
            onClick: fn(),
        },
    ]} />;
With Conditionally Disabled Actions story ok
const WithConditionallyDisabledActions = () => <DsTable
    columns={columns}
    data={defaultData}
    stickyHeader
    bordered
    fullWidth
    expandable={false}
    emptyState={<TableEmptyState />}
    onRowClick={fn()}
    primaryRowActions={[
        {
            icon: 'edit',
            label: 'Edit',
            onClick: fn(),
        },
        {
            icon: 'open_in_new',
            label: 'Open in New Window',
            // disabled on Tanner's row (item stays visible but greyed out)
            disabled: (data) => data.firstName === 'Tanner',
            onClick: fn(),
        },
    ]}
    secondaryRowActions={[
        {
            icon: 'delete_outline',
            label: 'Delete',
            tooltip: 'Delete this row',
            // disabled on 'single' rows (destructive action guarded)
            disabled: (data) => data.status === 'single',
            className: styles.destructiveAction,
            onClick: fn(),
        },
        {
            icon: 'info',
            label: 'Details',
            tooltip: 'Show details',
            onClick: fn(),
        },
    ]} />;
With Bulk Actions story ok
const WithBulkActions = () => <DsTable
    columns={columns}
    data={defaultData}
    stickyHeader
    bordered
    fullWidth
    expandable={false}
    emptyState={<TableEmptyState />}
    onRowClick={(row) => console.log('Row clicked:', row)}
    selectable
    actions={[
        {
            icon: 'alarm',
            label: 'Notify',
            onClick: fn(),
        },
        {
            icon: 'folder_open',
            label: 'Folder',
            onClick: fn(),
        },
        {
            icon: 'delete_outline',
            label: 'Delete',
            onClick: fn(),
        },
    ]} />;

DsTable

components-table-search-and-filtering · ./src/components/ds-table/stories/ds-table-search.stories.tsx
Design system Table component
Prop types (react-docgen) 33 prop types
Component: src/components/ds-table/ds-table.tsx::default
Props:
/**
 * Actions to be shown in the bulk actions
 */
actions?: Action<TData>[] = []

/**
 * ID of the currently active row (useful for showing drawer/panel associations)
 * When set, the row with this ID will be visually highlighted with a persistent active state
 * @example
 * ```tsx
 * const [activeRowId, setActiveRowId] = useState<string | null>(null);
 * const [isDrawerOpen, setIsDrawerOpen] = useState(false);
 * 
 * <DsTable
 *   activeRowId={isDrawerOpen ? activeRowId : null}
 *   onRowClick={(row) => {
 *     setActiveRowId(row.id);
 *     setIsDrawerOpen(true);
 *   }}
 * />
 * ```
 */
activeRowId?: string | null

/**
 * Whether the table has bordered cells
 */
bordered?: boolean = true

/**
 * Class name of the table
 */
className?: string

/**
 * External column filters state
 */
columnFilters?: ColumnFiltersState

/**
 * Columns of the table
 */
columns: ColumnDef<TData, TValue>[]

/**
 * External column visibility state
 * @example
 * ```tsx
 * const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({
 *   age: false, // hide age column
 *   status: true, // show status column
 * });
 * 
 * <DsTable
 *   columnVisibility={columnVisibility}
 *   onColumnVisibilityChange={setColumnVisibility}
 * />
 * ```
 */
columnVisibility?: VisibilityState

/**
 * Data of the table
 */
data: TData[]

/**
 * Empty state of the table
 */
emptyState?: React.ReactNode

/**
 * Whether the table is expandable or if an individual row should be expandable
 */
expandable?: boolean | ((row: TData) => boolean) = false

/**
 * Whether the table is full width
 */
fullWidth?: boolean = true

/**
 * Configures infinite scroll for the virtualized table. The Table owns viewport/scroll
 * detection and the auto-fill loop; the consumer owns fetching, pagination state,
 * error handling, and retry.
 * 
 * Only takes effect when `virtualized` is `true`. Passing this prop without
 * `virtualized` is ignored.
 * 
 * @example
 * ```tsx
 * <DsTable
 *   virtualized
 *   data={rows}
 *   columns={columns}
 *   infiniteScroll={{
 *     hasMore,
 *     isLoadingMore,
 *     onLoadMore: fetchNextPage,
 *   }}
 * />
 * ```
 */
infiniteScroll?: InfiniteScrollConfig

/**
 * Callback when column filters change
 */
onColumnFiltersChange?: (filters: ColumnFiltersState) => void

/**
 * Callback when column visibility changes
 */
onColumnVisibilityChange?: (visibility: VisibilityState) => void

/**
 * Callback when the order of rows changes via drag & drop
 */
onOrderChange?: (newData: TData[]) => void

/**
 * Function to handle row click
 */
onRowClick?: (row: TData) => void

/**
 * Function to handle row double click
 */
onRowDoubleClick?: (row: TData) => void

/**
 * Function to handle scroll events in virtualized tables.
 * Called when the user scrolls within the table container.
 * 
 * @param params - Scroll parameters containing scroll position and dimensions
 * 
 * @example
 * ```tsx
 * const handleScroll = ({ scrollOffset, totalContentHeight, viewportHeight }) => {
 *   const distanceFromBottom = totalContentHeight - scrollOffset - viewportHeight;
 *   if (distanceFromBottom < 500) {
 *     // Fetch more data when within 500px of bottom
 *     fetchNextPage();
 *   }
 * };
 * ```
 */
onScroll?: (params: ScrollParams) => void

/**
 * Function to handle selection change
 */
onSelectionChange?: (selectedRows: Record<string, boolean>) => void

/**
 * Function to handle sorting change
 */
onSortingChange?: (sorting: SortingState) => void

/**
 * Function to handle table creation
 */
onTableCreated?: (table: Table<TData>) => void

/**
 * Primary actions to be shown on each row (on hover)
 */
primaryRowActions?: RowAction<TData>[] = []

/**
 * Ref to the table API
 */
ref?: React.RefObject<DsTableApi<TData> | null>

/**
 * Function to render the expanded row
 */
renderExpandedRow?: (row: TData) => React.ReactNode

/**
 * Whether the table rows are reorderable via drag & drop
 * @note This feature does not work when virtualization is enabled
 */
reorderable?: boolean = false

/**
 * Row size variant (small: 36px, medium: 48px, large: 64px)
 * @default 'medium'
 */
rowSize?: 'small' | 'medium' | 'large' = 'medium'

/**
 * Secondary actions to be shown in a dropdown on each row (on hover)
 */
secondaryRowActions?: SecondaryRowAction<TData>[]

/**
 * Whether the table rows are selectable. Can be a boolean or a function that receives row data and returns whether it can be selected.
 * @default false
 * @example
 * ```tsx
 * // Enable selection for all rows
 * selectable={true}
 * 
 * // Disable selection for rows where status is 'archived'
 * selectable={(rowData) => rowData.status !== 'archived'}
 * 
 * // Limit selection to max 3 rows (note: use state to track current selection)
 * selectable={(rowData) => {
 *   // This will be called for each row during render
 *   const selectedIds = Object.keys(rowSelection).filter(id => rowSelection[id]);
 *   return selectedIds.includes(rowData.id) || selectedIds.length < 3;
 * }}
 * ```
 */
selectable?: boolean | ((rowData: TData) => boolean) = false

/**
 * Whether to show the select/deselect all checkbox in the header
 * @default true
 */
showSelectAllCheckbox?: boolean = true

/**
 * Whether the table has sticky header
 */
stickyHeader?: boolean = true

/**
 * Whether the table is virtualized
 * @default false
 */
virtualized?: boolean = false

/**
 * Options for the virtualized table
 */
virtualizedOptions?: {
	/**
	 * Estimate size of the table
	 */
	estimateSize?: number;

	/**
	 * Overscan of the table
	 */
	overscan?: number;
}

/**
 * Whether the table has zebra stripes
 */
zebra?: boolean
Imports
import { DsSmartTabs, DsTable, StatusItem, TableEmptyState } from "@drivenets/design-system";
With External Global Search story ok
const AdvancedSearch = () => {
    const [globalFilter, setGlobalFilter] = useState('');

    const filteredData = useMemo(() => {
        if (!globalFilter) {
            return args.data;
        }

        const lowercasedFilter = globalFilter.toLowerCase();

        return args.data.filter((row) => {
            return Object.values(row).some((value) => String(value).toLowerCase().includes(lowercasedFilter));
        });
    }, [globalFilter, args.data]);

    return (
        <div>
            <div style={{ marginBottom: '1rem' }}>
                <input
                    type="text"
                    value={globalFilter}
                    onChange={(e) => setGlobalFilter(e.target.value)}
                    placeholder="Search all columns..."
                    style={{ padding: '0.5rem', width: '300px' }} />
            </div>
            <DsTable
                columns={columns}
                stickyHeader
                bordered
                fullWidth
                expandable={false}
                emptyState={<TableEmptyState />}
                onRowClick={(row) => console.log('Row clicked:', row)}
                data={filteredData} />
        </div>
    );
};
With Tab Filters story ok
const TabFilters = () => {
    const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
    const [activeTab, setActiveTab] = useState<Status | 'all'>('all');

    const handleTabClick = (tabValue: string) => {
        const typedValue = tabValue as Status | 'all';
        setActiveTab(typedValue);
        if (typedValue === 'all') {
            setColumnFilters([]);
        } else {
            setColumnFilters([{ id: 'status', value: typedValue }]);
        }
    };

    const getStatusIcon = (status: Status): IconType => {
        switch (status) {
            case 'relationship':
                return 'favorite';
            case 'complicated':
                return 'psychology';
            default:
                return 'person';
        }
    };

    const statusColumnDef: ColumnDef<Person> = {
        accessorKey: 'status',
        header: 'Status',
        cell: (info) => {
            const status = info.getValue() as Status;
            const icon = getStatusIcon(status);
            return <StatusItem icon={icon} label={status} />;
        },
    };

    const tableColumns = args.columns.map((col) =>
        (col as { accessorKey: string }).accessorKey === 'status' ? statusColumnDef : col,
    );

    return (
        <div className={styles.tableFilterContainer}>
            <DsSmartTabs activeTab={activeTab} onTabClick={handleTabClick}>
                <DsSmartTabs.Tab
                    label="All People"
                    value="all"
                    icon="groups"
                    color="dark-blue"
                    content={defaultData.length} />
                <DsSmartTabs.Tab
                    label="In a Relationship"
                    value={'relationship'}
                    icon="favorite"
                    color="green"
                    content={defaultData.filter((row) => row.status === 'relationship').length} />
                <DsSmartTabs.Tab
                    label="It's Complicated"
                    value={'complicated'}
                    icon="psychology"
                    color="red"
                    content={defaultData.filter((row) => row.status === 'complicated').length} />
                <DsSmartTabs.Tab
                    label="Single"
                    value={'single'}
                    icon="person"
                    color="gray"
                    content={defaultData.filter((row) => row.status === 'single').length} />
            </DsSmartTabs>
            <DsTable
                data={defaultData}
                stickyHeader
                bordered
                fullWidth
                expandable={false}
                emptyState={<TableEmptyState />}
                onRowClick={(row) => console.log('Row clicked:', row)}
                columns={tableColumns}
                columnFilters={columnFilters}
                onColumnFiltersChange={setColumnFilters} />
        </div>
    );
};

DsTable

components-table-selection · ./src/components/ds-table/stories/ds-table-selection.stories.tsx
Design system Table component
Prop types (react-docgen) 33 prop types
Component: src/components/ds-table/ds-table.tsx::default
Props:
/**
 * Actions to be shown in the bulk actions
 */
actions?: Action<TData>[] = []

/**
 * ID of the currently active row (useful for showing drawer/panel associations)
 * When set, the row with this ID will be visually highlighted with a persistent active state
 * @example
 * ```tsx
 * const [activeRowId, setActiveRowId] = useState<string | null>(null);
 * const [isDrawerOpen, setIsDrawerOpen] = useState(false);
 * 
 * <DsTable
 *   activeRowId={isDrawerOpen ? activeRowId : null}
 *   onRowClick={(row) => {
 *     setActiveRowId(row.id);
 *     setIsDrawerOpen(true);
 *   }}
 * />
 * ```
 */
activeRowId?: string | null

/**
 * Whether the table has bordered cells
 */
bordered?: boolean = true

/**
 * Class name of the table
 */
className?: string

/**
 * External column filters state
 */
columnFilters?: ColumnFiltersState

/**
 * Columns of the table
 */
columns: ColumnDef<TData, TValue>[]

/**
 * External column visibility state
 * @example
 * ```tsx
 * const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({
 *   age: false, // hide age column
 *   status: true, // show status column
 * });
 * 
 * <DsTable
 *   columnVisibility={columnVisibility}
 *   onColumnVisibilityChange={setColumnVisibility}
 * />
 * ```
 */
columnVisibility?: VisibilityState

/**
 * Data of the table
 */
data: TData[]

/**
 * Empty state of the table
 */
emptyState?: React.ReactNode

/**
 * Whether the table is expandable or if an individual row should be expandable
 */
expandable?: boolean | ((row: TData) => boolean) = false

/**
 * Whether the table is full width
 */
fullWidth?: boolean = true

/**
 * Configures infinite scroll for the virtualized table. The Table owns viewport/scroll
 * detection and the auto-fill loop; the consumer owns fetching, pagination state,
 * error handling, and retry.
 * 
 * Only takes effect when `virtualized` is `true`. Passing this prop without
 * `virtualized` is ignored.
 * 
 * @example
 * ```tsx
 * <DsTable
 *   virtualized
 *   data={rows}
 *   columns={columns}
 *   infiniteScroll={{
 *     hasMore,
 *     isLoadingMore,
 *     onLoadMore: fetchNextPage,
 *   }}
 * />
 * ```
 */
infiniteScroll?: InfiniteScrollConfig

/**
 * Callback when column filters change
 */
onColumnFiltersChange?: (filters: ColumnFiltersState) => void

/**
 * Callback when column visibility changes
 */
onColumnVisibilityChange?: (visibility: VisibilityState) => void

/**
 * Callback when the order of rows changes via drag & drop
 */
onOrderChange?: (newData: TData[]) => void

/**
 * Function to handle row click
 */
onRowClick?: (row: TData) => void

/**
 * Function to handle row double click
 */
onRowDoubleClick?: (row: TData) => void

/**
 * Function to handle scroll events in virtualized tables.
 * Called when the user scrolls within the table container.
 * 
 * @param params - Scroll parameters containing scroll position and dimensions
 * 
 * @example
 * ```tsx
 * const handleScroll = ({ scrollOffset, totalContentHeight, viewportHeight }) => {
 *   const distanceFromBottom = totalContentHeight - scrollOffset - viewportHeight;
 *   if (distanceFromBottom < 500) {
 *     // Fetch more data when within 500px of bottom
 *     fetchNextPage();
 *   }
 * };
 * ```
 */
onScroll?: (params: ScrollParams) => void

/**
 * Function to handle selection change
 */
onSelectionChange?: (selectedRows: Record<string, boolean>) => void

/**
 * Function to handle sorting change
 */
onSortingChange?: (sorting: SortingState) => void

/**
 * Function to handle table creation
 */
onTableCreated?: (table: Table<TData>) => void

/**
 * Primary actions to be shown on each row (on hover)
 */
primaryRowActions?: RowAction<TData>[] = []

/**
 * Ref to the table API
 */
ref?: React.RefObject<DsTableApi<TData> | null>

/**
 * Function to render the expanded row
 */
renderExpandedRow?: (row: TData) => React.ReactNode

/**
 * Whether the table rows are reorderable via drag & drop
 * @note This feature does not work when virtualization is enabled
 */
reorderable?: boolean = false

/**
 * Row size variant (small: 36px, medium: 48px, large: 64px)
 * @default 'medium'
 */
rowSize?: 'small' | 'medium' | 'large' = 'medium'

/**
 * Secondary actions to be shown in a dropdown on each row (on hover)
 */
secondaryRowActions?: SecondaryRowAction<TData>[]

/**
 * Whether the table rows are selectable. Can be a boolean or a function that receives row data and returns whether it can be selected.
 * @default false
 * @example
 * ```tsx
 * // Enable selection for all rows
 * selectable={true}
 * 
 * // Disable selection for rows where status is 'archived'
 * selectable={(rowData) => rowData.status !== 'archived'}
 * 
 * // Limit selection to max 3 rows (note: use state to track current selection)
 * selectable={(rowData) => {
 *   // This will be called for each row during render
 *   const selectedIds = Object.keys(rowSelection).filter(id => rowSelection[id]);
 *   return selectedIds.includes(rowData.id) || selectedIds.length < 3;
 * }}
 * ```
 */
selectable?: boolean | ((rowData: TData) => boolean) = false

/**
 * Whether to show the select/deselect all checkbox in the header
 * @default true
 */
showSelectAllCheckbox?: boolean = true

/**
 * Whether the table has sticky header
 */
stickyHeader?: boolean = true

/**
 * Whether the table is virtualized
 * @default false
 */
virtualized?: boolean = false

/**
 * Options for the virtualized table
 */
virtualizedOptions?: {
	/**
	 * Estimate size of the table
	 */
	estimateSize?: number;

	/**
	 * Overscan of the table
	 */
	overscan?: number;
}

/**
 * Whether the table has zebra stripes
 */
zebra?: boolean
Imports
import { DsTable, TableEmptyState } from "@drivenets/design-system";
Selectable story ok
const Selectable = () => <DsTable
    columns={columns}
    data={defaultData}
    stickyHeader
    bordered
    fullWidth
    expandable={false}
    emptyState={<TableEmptyState />}
    onRowClick={(row) => console.log('Row clicked:', row)}
    selectable
    onSelectionChange={fn()} />;
Programmatic Row Selection story ok
const ProgrammaticRowSelection = () => {
    const tableRef = useRef<DsTableApi<Person>>(null);
    const [selectedRows, setSelectedRows] = useState<string[]>([]);

    const selectRow = (rowId: string) => {
        tableRef.current?.selectRow(rowId);
    };

    const selectAllRows = () => {
        tableRef.current?.selectAllRows();
    };

    const deselectAllRows = () => {
        tableRef.current?.deselectAllRows();
    };

    const selectSpecificRows = () => {
        tableRef.current?.selectRows(['1', '2', '3']);
    };

    const handleSelectionChange = (selection: Record<string, boolean>) => {
        const selectedIds = Object.keys(selection);
        setSelectedRows(selectedIds);
    };

    return (
        <div>
            <div className={styles.programmaticSelectionDemo}>
                <h4 className={styles.programmaticSelectionDemo__title}>Programmatic Row Selection Demo</h4>
                <p className={styles.programmaticSelectionDemo__description}>Use the buttons below to programmatically control row selection using TanStack Table v8 APIs.
                                        </p>
                <p className={styles.programmaticSelectionDemo__selectedRows}>Selected rows: {selectedRows.length > 0 ? selectedRows.join(', ') : 'None'}
                </p>
            </div>
            <div className={styles.programmaticSelectionControls}>
                <button
                    onClick={() => selectRow('1')}
                    className={styles.programmaticSelectionButton}>Select Row 1
                                        </button>
                <button
                    onClick={() => selectRow('2')}
                    className={styles.programmaticSelectionButton}>Select Row 2
                                        </button>
                <button
                    onClick={() => selectRow('3')}
                    className={styles.programmaticSelectionButton}>Select Row 3
                                        </button>
                <button onClick={selectAllRows} className={styles.programmaticSelectionButton}>Select All
                                        </button>
                <button onClick={deselectAllRows} className={styles.programmaticSelectionButton}>Deselect All
                                        </button>
                <button
                    onClick={selectSpecificRows}
                    className={styles.programmaticSelectionButton}>Select First 3 Rows
                                        </button>
            </div>
            <DsTable
                columns={columns}
                data={defaultData}
                stickyHeader
                bordered
                fullWidth
                expandable={false}
                emptyState={<TableEmptyState />}
                onRowClick={(row) => console.log('Row clicked:', row)}
                selectable
                showSelectAllCheckbox={false}
                ref={tableRef}
                onSelectionChange={handleSelectionChange} />
        </div>
    );
};
Max N Selections story ok
const MaxSelectionLimit = () => {
    const [rowSelection, setRowSelection] = useState<Record<string, boolean>>({});
    const maxSelections = 2;

    const selectedCount = Object.keys(rowSelection).filter((id) => rowSelection[id]).length;

    const handleSelectionChange = (selection: Record<string, boolean>) => {
        setRowSelection(selection);
        args.onSelectionChange?.(selection);
    };

    return (
        <div>
            <div className={styles.programmaticSelectionDemo}>
                <h4 className={styles.programmaticSelectionDemo__title}>Max Selection Limit Demo</h4>
                <p className={styles.programmaticSelectionDemo__description}>You can select at most {maxSelections}rows. Once the limit is reached, checkboxes for other rows
                                            are disabled.
                                        </p>
                <p className={styles.programmaticSelectionDemo__selectedRows}>Selected: {selectedCount}/ {maxSelections}
                </p>
            </div>
            <DsTable
                columns={columns}
                data={defaultData}
                stickyHeader
                bordered
                fullWidth
                expandable={false}
                emptyState={<TableEmptyState />}
                onRowClick={(row) => console.log('Row clicked:', row)}
                showSelectAllCheckbox={false}
                onSelectionChange={handleSelectionChange}
                selectable={(rowData) => {
                    return rowSelection[rowData.id] || selectedCount < maxSelections;
                }} />
        </div>
    );
};

DsTable

components-table-virtualized · ./src/components/ds-table/stories/ds-table-virtualized.stories.tsx
Design system Table component
Prop types (react-docgen) 33 prop types
Component: src/components/ds-table/ds-table.tsx::default
Props:
/**
 * Actions to be shown in the bulk actions
 */
actions?: Action<TData>[] = []

/**
 * ID of the currently active row (useful for showing drawer/panel associations)
 * When set, the row with this ID will be visually highlighted with a persistent active state
 * @example
 * ```tsx
 * const [activeRowId, setActiveRowId] = useState<string | null>(null);
 * const [isDrawerOpen, setIsDrawerOpen] = useState(false);
 * 
 * <DsTable
 *   activeRowId={isDrawerOpen ? activeRowId : null}
 *   onRowClick={(row) => {
 *     setActiveRowId(row.id);
 *     setIsDrawerOpen(true);
 *   }}
 * />
 * ```
 */
activeRowId?: string | null

/**
 * Whether the table has bordered cells
 */
bordered?: boolean = true

/**
 * Class name of the table
 */
className?: string

/**
 * External column filters state
 */
columnFilters?: ColumnFiltersState

/**
 * Columns of the table
 */
columns: ColumnDef<TData, TValue>[]

/**
 * External column visibility state
 * @example
 * ```tsx
 * const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({
 *   age: false, // hide age column
 *   status: true, // show status column
 * });
 * 
 * <DsTable
 *   columnVisibility={columnVisibility}
 *   onColumnVisibilityChange={setColumnVisibility}
 * />
 * ```
 */
columnVisibility?: VisibilityState

/**
 * Data of the table
 */
data: TData[]

/**
 * Empty state of the table
 */
emptyState?: React.ReactNode

/**
 * Whether the table is expandable or if an individual row should be expandable
 */
expandable?: boolean | ((row: TData) => boolean) = false

/**
 * Whether the table is full width
 */
fullWidth?: boolean = true

/**
 * Configures infinite scroll for the virtualized table. The Table owns viewport/scroll
 * detection and the auto-fill loop; the consumer owns fetching, pagination state,
 * error handling, and retry.
 * 
 * Only takes effect when `virtualized` is `true`. Passing this prop without
 * `virtualized` is ignored.
 * 
 * @example
 * ```tsx
 * <DsTable
 *   virtualized
 *   data={rows}
 *   columns={columns}
 *   infiniteScroll={{
 *     hasMore,
 *     isLoadingMore,
 *     onLoadMore: fetchNextPage,
 *   }}
 * />
 * ```
 */
infiniteScroll?: InfiniteScrollConfig

/**
 * Callback when column filters change
 */
onColumnFiltersChange?: (filters: ColumnFiltersState) => void

/**
 * Callback when column visibility changes
 */
onColumnVisibilityChange?: (visibility: VisibilityState) => void

/**
 * Callback when the order of rows changes via drag & drop
 */
onOrderChange?: (newData: TData[]) => void

/**
 * Function to handle row click
 */
onRowClick?: (row: TData) => void

/**
 * Function to handle row double click
 */
onRowDoubleClick?: (row: TData) => void

/**
 * Function to handle scroll events in virtualized tables.
 * Called when the user scrolls within the table container.
 * 
 * @param params - Scroll parameters containing scroll position and dimensions
 * 
 * @example
 * ```tsx
 * const handleScroll = ({ scrollOffset, totalContentHeight, viewportHeight }) => {
 *   const distanceFromBottom = totalContentHeight - scrollOffset - viewportHeight;
 *   if (distanceFromBottom < 500) {
 *     // Fetch more data when within 500px of bottom
 *     fetchNextPage();
 *   }
 * };
 * ```
 */
onScroll?: (params: ScrollParams) => void

/**
 * Function to handle selection change
 */
onSelectionChange?: (selectedRows: Record<string, boolean>) => void

/**
 * Function to handle sorting change
 */
onSortingChange?: (sorting: SortingState) => void

/**
 * Function to handle table creation
 */
onTableCreated?: (table: Table<TData>) => void

/**
 * Primary actions to be shown on each row (on hover)
 */
primaryRowActions?: RowAction<TData>[] = []

/**
 * Ref to the table API
 */
ref?: React.RefObject<DsTableApi<TData> | null>

/**
 * Function to render the expanded row
 */
renderExpandedRow?: (row: TData) => React.ReactNode

/**
 * Whether the table rows are reorderable via drag & drop
 * @note This feature does not work when virtualization is enabled
 */
reorderable?: boolean = false

/**
 * Row size variant (small: 36px, medium: 48px, large: 64px)
 * @default 'medium'
 */
rowSize?: 'small' | 'medium' | 'large' = 'medium'

/**
 * Secondary actions to be shown in a dropdown on each row (on hover)
 */
secondaryRowActions?: SecondaryRowAction<TData>[]

/**
 * Whether the table rows are selectable. Can be a boolean or a function that receives row data and returns whether it can be selected.
 * @default false
 * @example
 * ```tsx
 * // Enable selection for all rows
 * selectable={true}
 * 
 * // Disable selection for rows where status is 'archived'
 * selectable={(rowData) => rowData.status !== 'archived'}
 * 
 * // Limit selection to max 3 rows (note: use state to track current selection)
 * selectable={(rowData) => {
 *   // This will be called for each row during render
 *   const selectedIds = Object.keys(rowSelection).filter(id => rowSelection[id]);
 *   return selectedIds.includes(rowData.id) || selectedIds.length < 3;
 * }}
 * ```
 */
selectable?: boolean | ((rowData: TData) => boolean) = false

/**
 * Whether to show the select/deselect all checkbox in the header
 * @default true
 */
showSelectAllCheckbox?: boolean = true

/**
 * Whether the table has sticky header
 */
stickyHeader?: boolean = true

/**
 * Whether the table is virtualized
 * @default false
 */
virtualized?: boolean = false

/**
 * Options for the virtualized table
 */
virtualizedOptions?: {
	/**
	 * Estimate size of the table
	 */
	estimateSize?: number;

	/**
	 * Overscan of the table
	 */
	overscan?: number;
}

/**
 * Whether the table has zebra stripes
 */
zebra?: boolean
Imports
import { DsSpinner, DsTable, TableEmptyState } from "@drivenets/design-system";
Empty State story ok
const EmptyState = () => <DsTable
    columns={columns}
    data={[]}
    stickyHeader
    bordered
    fullWidth
    expandable={false}
    emptyState={<TableEmptyState />}
    onRowClick={(row) => console.log('Row clicked:', row)}
    virtualized />;
Virtualized Selectable Table story ok
const VirtualizedSelectable = () => {
    const pageSize = 10;
    const [sorting, setSorting] = useState<SortingState>([]);

    const {
        data: infiniteQueryData,
        fetchNextPage,
        isFetching,
        isLoading,
    } = useInfiniteQuery(
        {
            queryKey: ['people', sorting],
            queryFn: async ({ pageParam }) => {
                const start = pageParam * pageSize;
                return await fetchData(start, pageSize, sorting);
            },
            initialPageParam: 0,
            getNextPageParam: (_lastGroup, groups) => groups.length,
            placeholderData: keepPreviousData,
        },
        queryClient,
    );

    const flatData = useMemo(
        () => infiniteQueryData?.pages.flatMap((page) => page.data) ?? [],
        [infiniteQueryData],
    );

    const totalRows = infiniteQueryData?.pages[0]?.meta.totalRowCount ?? 0;
    const hasMore = flatData.length < totalRows;

    return (
        <div className={styles.virtualizedDemoContainer}>
            <div className={styles.virtualizedDemoHeader}>
                <h4 className={styles.virtualizedDemoHeader__title}>Virtualized Table Demo</h4>
                <p className={styles.virtualizedDemoHeader__description}>This table uses infinite query to fetch data as you scroll, making it performant even with large
                                            datasets. Try scrolling to see the data loading!
                                        </p>
                <p className={styles.virtualizedDemoHeader__stats}>({flatData.length}of {totalRows}rows fetched)
                                        </p>
            </div>
            {import.meta.env.NODE_ENV === 'development' && (
                <p className={styles.developmentNotice}>
                    <strong>Notice:</strong> You are currently running React in development mode. Virtualized
                    rendering performance will be slightly degraded until this application is built for production.
                </p>
            )}
            <div className={styles.virtualizedTableWrapper}>
                <DsTable
                    columns={columns.map((col) => {
                        if ('accessorKey' in col && col.accessorKey === 'age') {
                            return {
                                ...col,
                                size: 100,
                            } as ColumnDef<Person>;
                        }
                        return col;
                    })}
                    stickyHeader
                    bordered
                    fullWidth
                    expandable={false}
                    emptyState={<TableEmptyState />}
                    onRowClick={(row) => console.log('Row clicked:', row)}
                    selectable
                    onScroll={fn()}
                    data={flatData}
                    onSortingChange={setSorting}
                    virtualized={true}
                    infiniteScroll={{
                        hasMore,
                        isLoadingMore: isFetching,
                        onLoadMore: fetchNextPage,
                    }} />
                {isLoading && (
                    <div className={styles.loadingOverlay}>
                        <div className={styles.loadingContent}>
                            <DsSpinner size="small" />
                            <span className={styles.loadingText}>Loading data...</span>
                        </div>
                    </div>
                )}
            </div>
        </div>
    );
};
Virtualized Expandable Table story ok
const VirtualizedExpandable = () => {
    const pageSize = 10;
    const [sorting, setSorting] = useState<SortingState>([]);

    const {
        data: infiniteQueryData,
        fetchNextPage,
        isFetching,
        isLoading,
    } = useInfiniteQuery(
        {
            queryKey: ['people-expandable', sorting],
            queryFn: async ({ pageParam }) => {
                const start = pageParam * pageSize;
                return await fetchData(start, pageSize, sorting);
            },
            initialPageParam: 0,
            getNextPageParam: (_lastGroup, groups) => groups.length,
            placeholderData: keepPreviousData,
        },
        queryClient,
    );

    const flatData = useMemo(
        () => infiniteQueryData?.pages.flatMap((page) => page.data) ?? [],
        [infiniteQueryData],
    );

    const totalRows = infiniteQueryData?.pages[0]?.meta.totalRowCount ?? 0;
    const hasMore = flatData.length < totalRows;

    return (
        <div className={styles.virtualizedDemoContainer}>
            <div className={styles.virtualizedDemoHeader}>
                <h4 className={styles.virtualizedDemoHeader__title}>Virtualized Table with Expandable Rows</h4>
                <p className={styles.virtualizedDemoHeader__description}>This table combines virtualization for large datasets with expandable rows. Click the chevron to
                                            expand rows and see additional details.
                                        </p>
                <p className={styles.virtualizedDemoHeader__stats}>({flatData.length}of {totalRows}rows fetched)
                                        </p>
            </div>
            {import.meta.env.NODE_ENV === 'development' && (
                <p className={styles.developmentNotice}>
                    <strong>Notice:</strong> You are currently running React in development mode. Virtualized
                    rendering performance will be slightly degraded until this application is built for production.
                </p>
            )}
            <div className={styles.virtualizedTableWrapper}>
                <DsTable
                    columns={columns.map((col) => {
                        if ('accessorKey' in col && col.accessorKey === 'age') {
                            return {
                                ...col,
                                size: 100,
                            } as ColumnDef<Person>;
                        }
                        return col;
                    })}
                    stickyHeader
                    bordered
                    fullWidth
                    emptyState={<TableEmptyState />}
                    onRowClick={(row) => console.log('Row clicked:', row)}
                    onScroll={fn()}
                    data={flatData}
                    onSortingChange={setSorting}
                    virtualized={true}
                    expandable={true}
                    infiniteScroll={{
                        hasMore,
                        isLoadingMore: isFetching,
                        onLoadMore: fetchNextPage,
                    }}
                    renderExpandedRow={(row) => (
                        <>
                            <div className={styles.expandedRowDetails}>
                                <h4>Expanded Details for {row.firstName}</h4>
                                <p>ID: {row.id}</p>
                                <p>
                                    Full Name: {row.firstName} {row.lastName}
                                </p>
                                <p>Status: {row.status}</p>
                            </div>

                            <DsTable
                                columns={[
                                    {
                                        accessorKey: 'id',
                                        header: 'ID',
                                    },
                                    {
                                        accessorKey: 'firstName',
                                        header: 'First Name',
                                    },
                                    {
                                        accessorKey: 'lastName',
                                        header: 'Last Name',
                                    },
                                ]}
                                data={defaultData.slice(0, 3)}
                            />
                        </>
                    )} />
                {isLoading && (
                    <div className={styles.loadingOverlay}>
                        <div className={styles.loadingContent}>
                            <DsSpinner size="small" />
                            <span className={styles.loadingText}>Loading data...</span>
                        </div>
                    </div>
                )}
            </div>
        </div>
    );
};
Virtualized Infinite Scroll story ok
const InfiniteScroll = () => {
    const pageSize = 5;
    const totalRows = 60;
    const [sorting, setSorting] = useState<SortingState>([]);

    const {
        data: infiniteQueryData,
        fetchNextPage,
        isFetching,
    } = useInfiniteQuery(
        {
            queryKey: ['people-autofill', sorting],
            queryFn: async ({ pageParam }) => {
                const start = pageParam * pageSize;
                return await fetchData(start, pageSize, sorting, totalRows);
            },
            initialPageParam: 0,
            getNextPageParam: (_lastGroup, groups) => groups.length,
            placeholderData: keepPreviousData,
        },
        queryClient,
    );

    const flatData = useMemo(
        () => infiniteQueryData?.pages.flatMap((page) => page.data) ?? [],
        [infiniteQueryData],
    );

    const fetchedTotal = infiniteQueryData?.pages[0]?.meta.totalRowCount ?? totalRows;
    const hasMore = flatData.length < fetchedTotal;

    return (
        <div className={styles.virtualizedDemoContainer}>
            <div className={styles.virtualizedDemoHeader}>
                <h4 className={styles.virtualizedDemoHeader__title}>Virtualized Infinite Scroll</h4>
                <p className={styles.virtualizedDemoHeader__description}>The first page only returns {pageSize}rows - too few to fill the viewport. With{' '}
                    <code>autoFill: true</code>(the default), the Table keeps requesting pages until the content
                                            becomes scrollable.
                                        </p>
                <p className={styles.virtualizedDemoHeader__stats}>({flatData.length}of {fetchedTotal}rows fetched)
                                        </p>
            </div>
            <div className={styles.virtualizedTableWrapper}>
                <DsTable
                    columns={columns}
                    stickyHeader
                    bordered
                    fullWidth
                    expandable={false}
                    emptyState={<TableEmptyState />}
                    onRowClick={(row) => console.log('Row clicked:', row)}
                    data={flatData}
                    onSortingChange={setSorting}
                    virtualized={true}
                    infiniteScroll={{
                        hasMore,
                        isLoadingMore: isFetching,
                        onLoadMore: fetchNextPage,
                    }} />
            </div>
        </div>
    );
};

DsTable

components-table-filters · ./src/components/ds-table/stories/filters-panel.stories.tsx
Design system Table component
Prop types (react-docgen) 33 prop types
Component: src/components/ds-table/ds-table.tsx::default
Props:
/**
 * Actions to be shown in the bulk actions
 */
actions?: Action<TData>[] = []

/**
 * ID of the currently active row (useful for showing drawer/panel associations)
 * When set, the row with this ID will be visually highlighted with a persistent active state
 * @example
 * ```tsx
 * const [activeRowId, setActiveRowId] = useState<string | null>(null);
 * const [isDrawerOpen, setIsDrawerOpen] = useState(false);
 * 
 * <DsTable
 *   activeRowId={isDrawerOpen ? activeRowId : null}
 *   onRowClick={(row) => {
 *     setActiveRowId(row.id);
 *     setIsDrawerOpen(true);
 *   }}
 * />
 * ```
 */
activeRowId?: string | null

/**
 * Whether the table has bordered cells
 */
bordered?: boolean = true

/**
 * Class name of the table
 */
className?: string

/**
 * External column filters state
 */
columnFilters?: ColumnFiltersState

/**
 * Columns of the table
 */
columns: ColumnDef<TData, TValue>[]

/**
 * External column visibility state
 * @example
 * ```tsx
 * const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({
 *   age: false, // hide age column
 *   status: true, // show status column
 * });
 * 
 * <DsTable
 *   columnVisibility={columnVisibility}
 *   onColumnVisibilityChange={setColumnVisibility}
 * />
 * ```
 */
columnVisibility?: VisibilityState

/**
 * Data of the table
 */
data: TData[]

/**
 * Empty state of the table
 */
emptyState?: React.ReactNode

/**
 * Whether the table is expandable or if an individual row should be expandable
 */
expandable?: boolean | ((row: TData) => boolean) = false

/**
 * Whether the table is full width
 */
fullWidth?: boolean = true

/**
 * Configures infinite scroll for the virtualized table. The Table owns viewport/scroll
 * detection and the auto-fill loop; the consumer owns fetching, pagination state,
 * error handling, and retry.
 * 
 * Only takes effect when `virtualized` is `true`. Passing this prop without
 * `virtualized` is ignored.
 * 
 * @example
 * ```tsx
 * <DsTable
 *   virtualized
 *   data={rows}
 *   columns={columns}
 *   infiniteScroll={{
 *     hasMore,
 *     isLoadingMore,
 *     onLoadMore: fetchNextPage,
 *   }}
 * />
 * ```
 */
infiniteScroll?: InfiniteScrollConfig

/**
 * Callback when column filters change
 */
onColumnFiltersChange?: (filters: ColumnFiltersState) => void

/**
 * Callback when column visibility changes
 */
onColumnVisibilityChange?: (visibility: VisibilityState) => void

/**
 * Callback when the order of rows changes via drag & drop
 */
onOrderChange?: (newData: TData[]) => void

/**
 * Function to handle row click
 */
onRowClick?: (row: TData) => void

/**
 * Function to handle row double click
 */
onRowDoubleClick?: (row: TData) => void

/**
 * Function to handle scroll events in virtualized tables.
 * Called when the user scrolls within the table container.
 * 
 * @param params - Scroll parameters containing scroll position and dimensions
 * 
 * @example
 * ```tsx
 * const handleScroll = ({ scrollOffset, totalContentHeight, viewportHeight }) => {
 *   const distanceFromBottom = totalContentHeight - scrollOffset - viewportHeight;
 *   if (distanceFromBottom < 500) {
 *     // Fetch more data when within 500px of bottom
 *     fetchNextPage();
 *   }
 * };
 * ```
 */
onScroll?: (params: ScrollParams) => void

/**
 * Function to handle selection change
 */
onSelectionChange?: (selectedRows: Record<string, boolean>) => void

/**
 * Function to handle sorting change
 */
onSortingChange?: (sorting: SortingState) => void

/**
 * Function to handle table creation
 */
onTableCreated?: (table: Table<TData>) => void

/**
 * Primary actions to be shown on each row (on hover)
 */
primaryRowActions?: RowAction<TData>[] = []

/**
 * Ref to the table API
 */
ref?: React.RefObject<DsTableApi<TData> | null>

/**
 * Function to render the expanded row
 */
renderExpandedRow?: (row: TData) => React.ReactNode

/**
 * Whether the table rows are reorderable via drag & drop
 * @note This feature does not work when virtualization is enabled
 */
reorderable?: boolean = false

/**
 * Row size variant (small: 36px, medium: 48px, large: 64px)
 * @default 'medium'
 */
rowSize?: 'small' | 'medium' | 'large' = 'medium'

/**
 * Secondary actions to be shown in a dropdown on each row (on hover)
 */
secondaryRowActions?: SecondaryRowAction<TData>[]

/**
 * Whether the table rows are selectable. Can be a boolean or a function that receives row data and returns whether it can be selected.
 * @default false
 * @example
 * ```tsx
 * // Enable selection for all rows
 * selectable={true}
 * 
 * // Disable selection for rows where status is 'archived'
 * selectable={(rowData) => rowData.status !== 'archived'}
 * 
 * // Limit selection to max 3 rows (note: use state to track current selection)
 * selectable={(rowData) => {
 *   // This will be called for each row during render
 *   const selectedIds = Object.keys(rowSelection).filter(id => rowSelection[id]);
 *   return selectedIds.includes(rowData.id) || selectedIds.length < 3;
 * }}
 * ```
 */
selectable?: boolean | ((rowData: TData) => boolean) = false

/**
 * Whether to show the select/deselect all checkbox in the header
 * @default true
 */
showSelectAllCheckbox?: boolean = true

/**
 * Whether the table has sticky header
 */
stickyHeader?: boolean = true

/**
 * Whether the table is virtualized
 * @default false
 */
virtualized?: boolean = false

/**
 * Options for the virtualized table
 */
virtualizedOptions?: {
	/**
	 * Estimate size of the table
	 */
	estimateSize?: number;

	/**
	 * Overscan of the table
	 */
	overscan?: number;
}

/**
 * Whether the table has zebra stripes
 */
zebra?: boolean
Imports
import {
    DsButton,
    DsChipGroup,
    DsIcon,
    DsModal,
    DsTable,
    DsTypography,
    DsVerticalTabs,
} from "@drivenets/design-system";
With Filters Panel story ok
const FiltersPanel = () => {
    // useTableFilters hook orchestrates all filter logic
    const { columnFilters, filterChips, filterNavItems, enhancedColumns, handlers, renderFilterContent } =
        useTableFilters({
            filterAdapters: workflowFilters,
            baseColumns: args.columns,
        });

    const [isOpen, setIsOpen] = useState(false);
    const [selectedFilterId, setSelectedFilterId] = useState(filterNavItems[0]?.id || '');

    // Set initial selected filter when modal opens
    const handleOpenChange = (open: boolean) => {
        if (open && !selectedFilterId && filterNavItems.length > 0) {
            setSelectedFilterId(filterNavItems[0]?.id || '');
        }
        setIsOpen(open);
    };

    const handleValueChange = (value: string | null) => {
        if (value) {
            setSelectedFilterId(value);
        }
    };

    const handleApply = () => {
        handlers.applyFilters();
        setIsOpen(false);
    };

    const handleClearAll = () => {
        handlers.clearAll();
        setIsOpen(false);
    };

    // Helper component for filter tab content (label + count badge)
    const TabLabel = ({ item }: { item: FilterNavItem }) => (
        <>
            <DsTypography variant="body-sm-md" className={styles.filterTabLabel}>
                {item.label}
            </DsTypography>
            {!!item.count && (
                <div className={styles.filterTabBadge}>
                    <span className={styles.filterTabDot} />
                    <DsTypography variant="body-sm-reg" className={styles.filterTabCount}>
                        {item.count}
                    </DsTypography>
                </div>
            )}
        </>
    );

    return (
        <div className={styles.tableFilterContainer}>
            {/* Toolbar with filter button */}
            <div className={styles.toolbar}>
                <DsButton design="v1.2" buttonType="secondary" onClick={() => setIsOpen(true)}>
                    <DsIcon size="tiny" icon="filter_list" />
                </DsButton>
            </div>
            {/* Filter chips (automatically generated from filter state) */}
            {filterChips.length > 0 && (
                <DsChipGroup items={filterChips} onClearAll={handleClearAll} onItemDelete={handlers.deleteChip} />
            )}
            {/* Table with enhanced columns (includes filter functions) */}
            <DsTable
                data={defaultData}
                stickyHeader
                bordered
                fullWidth
                expandable={false}
                emptyState={<div>No data available</div>}
                onRowClick={(row) => console.log('Row clicked:', row)}
                columns={enhancedColumns}
                columnFilters={columnFilters} />
            {/* Filter modal with two-column layout pattern */}
            <DsModal style={{ height: '600px' }} open={isOpen} onOpenChange={handleOpenChange}>
                <DsModal.Header className={styles.filterHeader}>
                    <div className={styles.headerLeft}>
                        <DsIcon icon="filter_list" size="small" />
                        <DsModal.Title>Filters</DsModal.Title>
                    </div>
                    <DsModal.CloseTrigger />
                </DsModal.Header>
                <DsModal.Body className={styles.filterBody}>
                    <DsVerticalTabs
                        className={styles.filterTabs}
                        value={selectedFilterId}
                        onValueChange={handleValueChange}>
                        <DsVerticalTabs.List className={styles.filterTabList}>
                            {filterNavItems.map((item) => (
                                <DsVerticalTabs.Tab key={item.id} value={item.id} disabled={item.disabled}>
                                    <TabLabel item={item} />
                                </DsVerticalTabs.Tab>
                            ))}
                        </DsVerticalTabs.List>
                        {filterNavItems.map((item) => (
                            <DsVerticalTabs.Content key={item.id} value={item.id} className={styles.filterContent}>
                                {renderFilterContent(item)}
                            </DsVerticalTabs.Content>
                        ))}
                    </DsVerticalTabs>
                </DsModal.Body>
                <DsModal.Footer className={styles.filterFooter}>
                    <DsButton
                        design="v1.2"
                        variant="filled"
                        buttonType="secondary"
                        onClick={handleClearAll}>
                        <DsIcon icon="close" size="tiny" />Clear all
                                                </DsButton>
                    <DsModal.Actions>
                        <DsButton design="v1.2" variant="filled" buttonType="primary" onClick={handleApply}>Apply
                                                        </DsButton>
                    </DsModal.Actions>
                </DsModal.Footer>
            </DsModal>
        </div>
    );
};
Controlled Mode story ok
const Controlled = () => {
    // External filter state (controlled mode)
    const [appliedFilters, setAppliedFilters] = useState<Record<string, unknown>>({});

    const { columnFilters, filterChips, filterNavItems, enhancedColumns, handlers, renderFilterContent } =
        useTableFilters({
            filterAdapters: workflowFilters,
            baseColumns: args.columns,
            appliedFilters,
            onFiltersChange: setAppliedFilters,
        });

    const [isOpen, setIsOpen] = useState(false);
    const [selectedFilterId, setSelectedFilterId] = useState(filterNavItems[0]?.id || '');

    const handleOpenChange = (open: boolean) => {
        if (open && !selectedFilterId && filterNavItems.length > 0) {
            setSelectedFilterId(filterNavItems[0]?.id || '');
        }
        setIsOpen(open);
    };

    const handleValueChange = (value: string | null) => {
        if (value) {
            setSelectedFilterId(value);
        }
    };

    const handleApply = () => {
        handlers.applyFilters();
        setIsOpen(false);
    };

    const handleClearAll = () => {
        handlers.clearAll();
        setIsOpen(false);
    };

    const TabLabel = ({ item }: { item: FilterNavItem }) => (
        <>
            <DsTypography variant="body-sm-md" className={styles.filterTabLabel}>
                {item.label}
            </DsTypography>
            {!!item.count && (
                <div className={styles.filterTabBadge}>
                    <span className={styles.filterTabDot} />
                    <DsTypography variant="body-sm-reg" className={styles.filterTabCount}>
                        {item.count}
                    </DsTypography>
                </div>
            )}
        </>
    );

    return (
        <div className={styles.tableFilterContainer}>
            {/* Debug panel showing external state */}
            <div className={styles.debugPanel}>
                <DsTypography variant="body-sm-md">External Filter State (controlled):</DsTypography>
                <pre className={styles.debugCode}>{JSON.stringify(appliedFilters, null, 2) || '{}'}</pre>
            </div>
            <div className={styles.toolbar}>
                <DsButton design="v1.2" buttonType="secondary" onClick={() => setIsOpen(true)}>
                    <DsIcon size="tiny" icon="filter_list" />
                </DsButton>
            </div>
            {filterChips.length > 0 && (
                <DsChipGroup items={filterChips} onClearAll={handleClearAll} onItemDelete={handlers.deleteChip} />
            )}
            <DsTable
                data={defaultData}
                stickyHeader
                bordered
                fullWidth
                expandable={false}
                emptyState={<div>No data available</div>}
                onRowClick={(row) => console.log('Row clicked:', row)}
                columns={enhancedColumns}
                columnFilters={columnFilters} />
            <DsModal style={{ height: '600px' }} open={isOpen} onOpenChange={handleOpenChange}>
                <DsModal.Header className={styles.filterHeader}>
                    <div className={styles.headerLeft}>
                        <DsIcon icon="filter_list" size="small" />
                        <DsModal.Title>Filters</DsModal.Title>
                    </div>
                    <DsModal.CloseTrigger />
                </DsModal.Header>
                <DsModal.Body className={styles.filterBody}>
                    <DsVerticalTabs
                        className={styles.filterTabs}
                        value={selectedFilterId}
                        onValueChange={handleValueChange}>
                        <DsVerticalTabs.List className={styles.filterTabList}>
                            {filterNavItems.map((item) => (
                                <DsVerticalTabs.Tab key={item.id} value={item.id} disabled={item.disabled}>
                                    <TabLabel item={item} />
                                </DsVerticalTabs.Tab>
                            ))}
                        </DsVerticalTabs.List>
                        {filterNavItems.map((item) => (
                            <DsVerticalTabs.Content key={item.id} value={item.id} className={styles.filterContent}>
                                {renderFilterContent(item)}
                            </DsVerticalTabs.Content>
                        ))}
                    </DsVerticalTabs>
                </DsModal.Body>
                <DsModal.Footer className={styles.filterFooter}>
                    <DsButton
                        design="v1.2"
                        variant="filled"
                        buttonType="secondary"
                        onClick={handleClearAll}>
                        <DsIcon icon="close" size="tiny" />Clear all
                                                </DsButton>
                    <DsModal.Actions>
                        <DsButton design="v1.2" variant="filled" buttonType="primary" onClick={handleApply}>Apply
                                                        </DsButton>
                    </DsModal.Actions>
                </DsModal.Footer>
            </DsModal>
        </div>
    );
};

DsTabs.Root

components-tabs · ./src/components/ds-tabs/ds-tabs.stories.tsx
Prop type error
File: /opt/build/repo/packages/design-system/src/components/ds-tabs/ds-tabs.tsx
Error:
No suitable component definition found.
You can debug your component file in this playground: https://react-docgen.dev/playground
Code:
import { Tabs } from '@ark-ui/react/tabs';
import classNames from 'classnames';
import { DsTabsList } from './components/ds-tabs-list';
import { DsTabsTab } from './components/ds-tabs-tab';
import { DsTabsContent } from './components/ds-tabs-content';
import type { DsTabsProps } from './ds-tabs.types';
import styles from './ds-tabs.module.scss';

const DsTabsRoot = ({
	value,
	defaultValue,
	onValueChange,
	orientation = 'horizontal',
	size = 'medium',
	className,
	style,
	children,
}: DsTabsProps) => {
	const handleValueChange = (details: { value: string | null }) => {
		onValueChange?.(details.value);
	};

	return (
		<Tabs.Root
			orientation={orientation}
			value={value}
			defaultValue={defaultValue}
			onValueChange={handleValueChange}
			activationMode="manual"
			lazyMount
			unmountOnExit
			className={classNames(styles.root, className)}
			style={style}
			data-size={size}
		>
			{children}
		</Tabs.Root>
	);
};

export const DsTabs = {
	Root: DsTabsRoot,
	List: DsTabsList,
	Tab: DsTabsTab,
	Content: DsTabsContent,
};
Info
No description found. Write a jsdoc comment such as /** Component description */.
Imports
import { DsTabs } from "@drivenets/design-system";
Default story ok
const Default = () => {
    const [selected, setSelected] = useState('overview');

    return (
        <div className={styles.container}>
            <DsTabs.Root
                orientation="horizontal"
                size="medium"
                value={selected}
                onValueChange={(val: string | null) => setSelected(val ?? 'overview')}>
                <DsTabs.List>
                    <DsTabs.Tab value="overview" label="Overview" icon="dashboard" />
                    <DsTabs.Tab value="analytics" label="Analytics" icon="analytics" badge={12} />
                    <DsTabs.Tab value="reports" label="Reports" icon="description" badge={5} />
                    <DsTabs.Tab value="settings" label="Settings" icon="settings" />
                </DsTabs.List>
                <DsTabs.Content value="overview">
                    <div className={styles.content}>
                        <h3>Overview</h3>
                        <p>View your dashboard overview and key metrics.</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="analytics">
                    <div className={styles.content}>
                        <h3>Analytics</h3>
                        <p>Detailed analytics and performance data (12 new insights).</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="reports">
                    <div className={styles.content}>
                        <h3>Reports</h3>
                        <p>Access and manage your reports (5 pending).</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="settings">
                    <div className={styles.content}>
                        <h3>Settings</h3>
                        <p>Configure your application settings.</p>
                    </div>
                </DsTabs.Content>
            </DsTabs.Root>
        </div>
    );
};
Horizontal Small story ok
const HorizontalSmall = () => {
    const [selected, setSelected] = useState('dashboard');

    return (
        <div className={styles.container}>
            <DsTabs.Root
                size="small"
                orientation="horizontal"
                value={selected}
                onValueChange={(val: string | null) => setSelected(val ?? 'dashboard')}>
                <DsTabs.List>
                    <DsTabs.Tab value="dashboard" label="Dashboard" icon="dashboard" />
                    <DsTabs.Tab value="analytics" label="Analytics" icon="bar_chart" badge={12} />
                    <DsTabs.Tab value="reports" label="Reports" icon="description" badge={5} />
                    <DsTabs.Tab value="settings" label="Settings" icon="settings" />
                </DsTabs.List>
                <DsTabs.Content value="dashboard">
                    <div className={styles.content}>
                        <h3>Dashboard</h3>
                        <p>Welcome to your dashboard overview.</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="analytics">
                    <div className={styles.content}>
                        <h3>Analytics</h3>
                        <p>View analytics and performance data. 12 new insights available.</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="reports">
                    <div className={styles.content}>
                        <h3>Reports</h3>
                        <p>View and generate reports. 5 new reports available.</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="settings">
                    <div className={styles.content}>
                        <h3>Settings</h3>
                        <p>Configure your application settings.</p>
                    </div>
                </DsTabs.Content>
            </DsTabs.Root>
        </div>
    );
};
With Menu Actions story ok
const WithMenuActions = () => {
    const [selected, setSelected] = useState('tab1');

    const menuActions: DsTabsMenuActionItem[] = [
        { value: 'edit', label: 'Edit' },
        { value: 'duplicate', label: 'Duplicate' },
        { value: 'share', label: 'Share' },
        { value: 'delete', label: 'Delete' },
    ];

    return (
        <div className={styles.container}>
            <DsTabs.Root
                orientation="horizontal"
                size="medium"
                value={selected}
                onValueChange={(val: string | null) => setSelected(val ?? 'tab1')}>
                <DsTabs.List>
                    <DsTabs.Tab
                        value="tab1"
                        label="Projects"
                        icon="folder"
                        badge={5}
                        menuActionItems={menuActions}
                        onMenuActionSelect={handleMenuActionMock} />
                    <DsTabs.Tab
                        value="tab2"
                        label="Documents"
                        icon="description"
                        badge={12}
                        menuActionItems={menuActions}
                        onMenuActionSelect={handleMenuActionMock} />
                    <DsTabs.Tab
                        value="tab3"
                        label="Settings"
                        icon="settings"
                        menuActionItems={menuActions}
                        onMenuActionSelect={handleMenuActionMock} />
                </DsTabs.List>
                <DsTabs.Content value="tab1">
                    <div className={styles.content}>
                        <h3>Projects</h3>
                        <p>Click the dropdown icon on tabs to see menu actions</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="tab2">
                    <div className={styles.content}>
                        <h3>Documents</h3>
                        <p>12 documents available</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="tab3">
                    <div className={styles.content}>
                        <h3>Settings</h3>
                        <p>Configure your preferences</p>
                    </div>
                </DsTabs.Content>
            </DsTabs.Root>
        </div>
    );
};
Vertical story ok
const Vertical = () => {
    const [selected, setSelected] = useState('profile');

    return (
        <div className={styles.verticalContainer}>
            <DsTabs.Root
                orientation="vertical"
                size="medium"
                value={selected}
                onValueChange={(val: string | null) => setSelected(val ?? 'profile')}>
                <DsTabs.List>
                    <DsTabs.Tab value="profile" label="Profile" icon="person" />
                    <DsTabs.Tab value="security" label="Security" icon="lock" badge={3} />
                    <DsTabs.Tab
                        value="notifications"
                        label="Notifications"
                        icon="notifications"
                        badge={15} />
                    <DsTabs.Tab value="billing" label="Billing" icon="credit_card" />
                    <DsTabs.Tab value="team" label="Team" icon="group" />
                </DsTabs.List>
                <DsTabs.Content value="profile">
                    <div className={styles.content}>
                        <h3>Profile Settings</h3>
                        <p>Manage your profile information and preferences.</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="security">
                    <div className={styles.content}>
                        <h3>Security</h3>
                        <p>Configure security settings and two-factor authentication (3 recommendations).</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="notifications">
                    <div className={styles.content}>
                        <h3>Notifications</h3>
                        <p>Manage notification preferences and channels (15 unread).</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="billing">
                    <div className={styles.content}>
                        <h3>Billing</h3>
                        <p>View invoices and manage payment methods.</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="team">
                    <div className={styles.content}>
                        <h3>Team Management</h3>
                        <p>Invite team members and manage permissions.</p>
                    </div>
                </DsTabs.Content>
            </DsTabs.Root>
        </div>
    );
};
Vertical Small story ok
const VerticalSmall = () => {
    const [selected, setSelected] = useState('general');

    return (
        <div className={styles.verticalContainer}>
            <DsTabs.Root
                orientation="vertical"
                size="small"
                value={selected}
                onValueChange={(val: string | null) => setSelected(val ?? 'general')}>
                <DsTabs.List>
                    <DsTabs.Tab value="general" label="General" icon="settings" />
                    <DsTabs.Tab value="account" label="Account" icon="person" />
                    <DsTabs.Tab value="privacy" label="Privacy" icon="lock" badge={2} />
                    <DsTabs.Tab value="appearance" label="Appearance" icon="palette" />
                    <DsTabs.Tab value="advanced" label="Advanced" icon="tune" />
                </DsTabs.List>
                <DsTabs.Content value="general">
                    <div className={styles.content}>
                        <h3>General Settings</h3>
                        <p>Configure general application settings and preferences.</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="account">
                    <div className={styles.content}>
                        <h3>Account</h3>
                        <p>Manage your account details and information.</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="privacy">
                    <div className={styles.content}>
                        <h3>Privacy</h3>
                        <p>Control your privacy settings and data sharing (2 recommendations).</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="appearance">
                    <div className={styles.content}>
                        <h3>Appearance</h3>
                        <p>Customize the look and feel of the application.</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="advanced">
                    <div className={styles.content}>
                        <h3>Advanced</h3>
                        <p>Advanced configuration options for power users.</p>
                    </div>
                </DsTabs.Content>
            </DsTabs.Root>
        </div>
    );
};
Vertical With Menu Actions story ok
const VerticalWithMenuActions = () => {
    const [selected, setSelected] = useState('profile');

    const menuActions: DsTabsMenuActionItem[] = [
        { value: 'edit', label: 'Edit' },
        { value: 'duplicate', label: 'Duplicate' },
        { value: 'archive', label: 'Archive' },
        { value: 'delete', label: 'Delete' },
    ];

    const handleMenuAction = (action: string) => {
        console.log('Menu action selected:', action);
    };

    return (
        <div className={styles.verticalContainer}>
            <DsTabs.Root
                orientation="vertical"
                size="medium"
                value={selected}
                onValueChange={(val: string | null) => setSelected(val ?? 'profile')}>
                <DsTabs.List>
                    <DsTabs.Tab
                        value="profile"
                        label="Profile"
                        icon="person"
                        menuActionItems={menuActions}
                        onMenuActionSelect={handleMenuAction} />
                    <DsTabs.Tab
                        value="security"
                        label="Security"
                        icon="lock"
                        badge={3}
                        menuActionItems={menuActions}
                        onMenuActionSelect={handleMenuAction} />
                    <DsTabs.Tab
                        value="notifications"
                        label="Notifications"
                        icon="notifications"
                        badge={15}
                        menuActionItems={menuActions}
                        onMenuActionSelect={handleMenuAction} />
                    <DsTabs.Tab
                        value="billing"
                        label="Billing"
                        icon="credit_card"
                        menuActionItems={menuActions}
                        onMenuActionSelect={handleMenuAction} />
                    <DsTabs.Tab
                        value="team"
                        label="Team"
                        icon="group"
                        menuActionItems={menuActions}
                        onMenuActionSelect={handleMenuAction} />
                </DsTabs.List>
                <DsTabs.Content value="profile">
                    <div className={styles.content}>
                        <h3>Profile Settings</h3>
                        <p>Manage your profile information and preferences. Click the dropdown icon on tabs to see menu
                                                            actions.
                                                        </p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="security">
                    <div className={styles.content}>
                        <h3>Security</h3>
                        <p>Configure security settings and two-factor authentication (3 recommendations).</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="notifications">
                    <div className={styles.content}>
                        <h3>Notifications</h3>
                        <p>Manage notification preferences and channels (15 unread).</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="billing">
                    <div className={styles.content}>
                        <h3>Billing</h3>
                        <p>View invoices and manage payment methods.</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="team">
                    <div className={styles.content}>
                        <h3>Team Management</h3>
                        <p>Invite team members and manage permissions.</p>
                    </div>
                </DsTabs.Content>
            </DsTabs.Root>
        </div>
    );
};
With Disabled story ok
const WithDisabled = () => {
    const [selected, setSelected] = useState('active1');

    return (
        <div className={styles.container}>
            <DsTabs.Root
                value={selected}
                onValueChange={(val: string | null) => setSelected(val ?? 'active1')}>
                <DsTabs.List>
                    <DsTabs.Tab value="active1" label="Active" icon="check_circle" />
                    <DsTabs.Tab value="disabled1" label="Disabled" icon="block" disabled />
                    <DsTabs.Tab value="active2" label="Active" icon="check_circle" badge={5} />
                    <DsTabs.Tab value="disabled2" label="Disabled" icon="block" disabled />
                </DsTabs.List>
                <DsTabs.Content value="active1">
                    <div className={styles.content}>Active tab 1 content</div>
                </DsTabs.Content>
                <DsTabs.Content value="disabled1">
                    <div className={styles.content}>This content should not be accessible</div>
                </DsTabs.Content>
                <DsTabs.Content value="active2">
                    <div className={styles.content}>Active tab 2 content</div>
                </DsTabs.Content>
                <DsTabs.Content value="disabled2">
                    <div className={styles.content}>This content should not be accessible</div>
                </DsTabs.Content>
            </DsTabs.Root>
        </div>
    );
};
Text Only story ok
const TextOnly = () => {
    const [selected, setSelected] = useState('home');

    return (
        <div className={styles.container}>
            <DsTabs.Root
                value={selected}
                onValueChange={(val: string | null) => setSelected(val ?? 'home')}>
                <DsTabs.List>
                    <DsTabs.Tab value="home" label="Home" />
                    <DsTabs.Tab value="products" label="Products" badge={23} />
                    <DsTabs.Tab value="services" label="Services" />
                    <DsTabs.Tab value="contact" label="Contact" />
                </DsTabs.List>
                <DsTabs.Content value="home">
                    <div className={styles.content}>
                        <h3>Home</h3>
                        <p>Welcome to the home page.</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="products">
                    <div className={styles.content}>
                        <h3>Products</h3>
                        <p>Browse our product catalog (23 items).</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="services">
                    <div className={styles.content}>
                        <h3>Services</h3>
                        <p>Learn about our services.</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="contact">
                    <div className={styles.content}>
                        <h3>Contact Us</h3>
                        <p>Get in touch with our team.</p>
                    </div>
                </DsTabs.Content>
            </DsTabs.Root>
        </div>
    );
};
With Tooltips story ok
const WithTooltips = () => {
    const [selected, setSelected] = useState('dashboard');

    return (
        <div className={styles.container}>
            <DsTabs.Root
                value={selected}
                onValueChange={(val: string | null) => setSelected(val ?? 'dashboard')}>
                <DsTabs.List>
                    <DsTabs.Tab
                        value="dashboard"
                        label="Dashboard"
                        icon="dashboard"
                        tooltip="View your dashboard overview" />
                    <DsTabs.Tab
                        value="analytics"
                        label="Analytics"
                        icon="analytics"
                        badge={12}
                        tooltip="Analytics and insights" />
                    <DsTabs.Tab
                        value="reports"
                        label="Reports"
                        icon="description"
                        badge={5}
                        tooltip="Generate and view reports" />
                    <DsTabs.Tab
                        value="settings"
                        label="Settings"
                        icon="settings"
                        tooltip="Configure application settings" />
                </DsTabs.List>
                <DsTabs.Content value="dashboard">
                    <div className={styles.content}>
                        <h3>Dashboard</h3>
                        <p>View your dashboard overview (hover tabs to see tooltips)</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="analytics">
                    <div className={styles.content}>
                        <h3>Analytics</h3>
                        <p>12 new insights available</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="reports">
                    <div className={styles.content}>
                        <h3>Reports</h3>
                        <p>5 reports pending review</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="settings">
                    <div className={styles.content}>
                        <h3>Settings</h3>
                        <p>Configure your application</p>
                    </div>
                </DsTabs.Content>
            </DsTabs.Root>
        </div>
    );
};

DsTag

components-tag · ./src/components/ds-tag/ds-tag.stories.tsx
Design system Tag component
Prop types (react-docgen) 12 prop types
Component: src/components/ds-tag/ds-tag.tsx::default
Props:
/**
 * Additional CSS class names
 */
className?: string

/**
 * Whether the tag is disabled
 * @default false
 */
disabled?: boolean = false

/**
 * The label text to display in the tag
 */
label: ReactNode

/**
 * Locale object (you can pass custom strings for localization)
 */
locale?: {
	/**
	 * aria-label for the delete button (shown when `onDelete` is provided).
	 */
	deleteAriaLabel?: string;
} = {}

/**
 * Optional click handler, if not present Tag will not be in "clickable" state
 */
onClick?: (event: MouseEvent<HTMLElement> | KeyboardEvent<HTMLElement>) => void

/**
 * Callback function when the delete icon is clicked
 */
onDelete?: (event: MouseEvent<HTMLElement> | KeyboardEvent<HTMLElement>) => void

/**
 * Ref to the tag element
 */
ref?: Ref<HTMLElement>

/**
 * Whether the tag is in a selected/pressed state
 * @default false
 */
selected?: boolean = false

/**
 * Size of the tag
 * @default 'medium'
 */
size?: (typeof tagSizes)[number] = 'medium'

/**
 * Optional render slots for customizing parts of the tag.
 */
slots?: {
	/**
	 * Icon to display at the start of the input
	 */
	icon?: ReactNode;
}

/**
 * Additional styles to apply to the component
 */
style?: CSSProperties = {}

/**
 * Variant of the tag
 * @default 'default'
 */
variant?: (typeof tagVariants)[number] = 'default'
Imports
import { DsIcon, DsTag } from "@drivenets/design-system";
Default story ok
const Default = () => <DsTag label="Default Tag" onClick={undefined} />;
Clickable story ok
const Clickable = () => <DsTag label="Clickable Tag" onClick={fn()} />;
Controlled story ok
const Controlled = function Render() {
    const [deleted, setDeleted] = useState(false);
    const [selected, setSelected] = useState(true);

    if (deleted) {
        return <span>Poof! Deleted!</span>;
    }

    return (
        <DsTag
            selected={selected}
            label="Controlled"
            onDelete={() => setDeleted(true)}
            onClick={() => setSelected(!selected)}
        />
    );
};
Include story ok
const Include = () => <DsTag label="Include Tag" variant="include" onDelete={fn()} />;
Exclude story ok
const Exclude = () => <DsTag label="Exclude Tag" variant="exclude" onDelete={fn()} />;
Small story ok
const Small = () => <DsTag label="Small Tag" size="small" />;
Disabled story ok
const Disabled = () => <DsTag label="Disabled Tag" selected disabled onClick={fn()} onDelete={fn()} />;
Custom Icon story ok
const CustomIcon = () => <DsTag
    label="Custom Icon Tag"
    variant="include"
    slots={{
        icon: <DsIcon icon="star" size="tiny" />,
    }} />;
Keyboard Interaction story ok
const KeyboardInteraction = () => <DsTag label="Keyboard Tag" onClick={fn()} onDelete={fn()} />;

DsTagFilter

components-tagfilter · ./src/components/ds-tag-filter/ds-tag-filter.stories.tsx
Design system TagFilter component A component for displaying active filters as tags with overflow handling. Non-tag elements (label, expand/collapse, clear) sit in a header row. Tags occupy the full width below, wrapping as needed.
Prop types (react-docgen) 8 prop types
Component: src/components/ds-tag-filter/ds-tag-filter.tsx::default
Props:
/**
 * Additional CSS class names
 */
className?: string

/**
 * Array of tag items to display
 */
items: TagFilterItem[]

/**
 * Locale object (you can pass custom strings for localization)
 */
locale?: {
	/**
	 * Heading label shown above the tag list (e.g., "Filters").
	 */
	label?: string;
	/**
	 * Label for the "Clear all" button.
	 */
	clearButton?: string;
	/**
	 * Label for the button that reveals hidden tags when the list overflows.
	 */
	showMore?: string;
	/**
	 * Label for the button that collapses the expanded list back to the preview.
	 */
	showLess?: string;
} = {}

/**
 * Callback when "Clear all" is clicked
 */
onClearAll?: () => void

/**
 * Callback when the Expand button is clicked
 */
onExpand?: (expanded?: boolean) => void

/**
 * Callback when a tag is deleted/removed
 */
onItemDelete?: (item: TagFilterItem) => void

/**
 * Callback when a tag is selected
 */
onItemSelect?: (item: TagFilterItem) => void

/**
 * Additional styles to apply to the component
 */
style?: CSSProperties
Imports
import { DsTagFilter } from "@drivenets/design-system";
Default story ok
Default story demonstrating the TagFilter component with interactive controls. Try adding, removing, and selecting filters to see the component in action.
const Default = () => {
    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: TagFilterItem) => {
        setFilters((prev) => prev.filter((f) => f.id !== filter.id));
    };

    const handleFilterSelect = (filter: TagFilterItem) => {
        setFilters((prev) => prev.map((f) => (f.id === filter.id ? { ...f, selected: !f.selected } : f)));
    };

    return (
        <div className={styles.container}>
            <DsTagFilter
                items={filters}
                onClearAll={handleClearAll}
                onItemDelete={handleFilterDelete}
                onItemSelect={handleFilterSelect} />
            <div className={styles.controlsContainer}>
                <button type="button" onClick={handleAddFilter} className={styles.addButton}>Add Filter
                                        </button>
                <p className={styles.infoText}>Total filters: {filters.length}</p>
                <p className={styles.infoText}>Selected filters: [
                                            {filters
                        .filter((filter) => filter.selected)
                        .map((filter) => `"${filter.label}"`)
                        .join(', ')}]
                                        </p>
            </div>
        </div>
    );
};
Few Filters story ok
Story showing fewer filters that fit within the visible area without overflow.
const FewFilters = () => {
    const [filters, setFilters] = useState<TagFilterItem[]>([
        { id: '1', label: 'Status: Active' },
        { id: '2', label: 'Version: 1.0.0' },
        { id: '3', label: 'Author: John Doe' },
    ]);

    const handleClearAll = () => {
        setFilters([]);
    };

    const handleFilterDelete = (filter: TagFilterItem) => {
        setFilters((prev) => prev.filter((f) => f.id !== filter.id));
    };

    const handleFilterSelect = (filter: TagFilterItem) => {
        setFilters((prev) => prev.map((f) => (f.id === filter.id ? { ...f, selected: !f.selected } : f)));
    };

    return (
        <DsTagFilter
            items={filters}
            onClearAll={handleClearAll}
            onItemDelete={handleFilterDelete}
            onItemSelect={handleFilterSelect} />
    );
};
Without Clear All story ok
Story showing TagFilter without the "Clear all" button.
const WithoutClearAll = () => {
    const [filters, setFilters] = useState(sampleFilters.slice(0, 5));

    const handleFilterDelete = (filter: TagFilterItem) => {
        setFilters((prev) => prev.filter((f) => f.id !== filter.id));
    };

    return <DsTagFilter items={filters} onClearAll={undefined} onItemDelete={handleFilterDelete} />;
};
Read Only story ok
Story showing TagFilter without delete functionality (read-only tags).
const ReadOnly = () => {
    const filters: TagFilterItem[] = sampleFilters.slice(0, 5);

    return (
        <DsTagFilter
            items={filters}
            onClearAll={undefined}
            onItemDelete={undefined}
            onItemSelect={undefined}
            locale={{ label: 'Applied filters:' }} />
    );
};
Without Label story ok
Story showing TagFilter without a label.
const WithoutLabel = () => {
    const [filters, setFilters] = useState(sampleFilters.slice(0, 5));

    const handleClearAll = () => {
        setFilters([]);
    };

    const handleFilterDelete = (filter: TagFilterItem) => {
        setFilters((prev) => prev.filter((f) => f.id !== filter.id));
    };

    return (
        <DsTagFilter
            items={filters}
            locale={{ label: '' }}
            onClearAll={handleClearAll}
            onItemDelete={handleFilterDelete} />
    );
};
Custom Locale story ok
Story demonstrating full locale customization with both label and clearButton.
const CustomLocale = () => {
    const [filters, setFilters] = useState(sampleFilters);

    const handleClearAll = () => {
        setFilters([]);
    };

    const handleFilterDelete = (filter: TagFilterItem) => {
        setFilters((prev) => prev.filter((f) => f.id !== filter.id));
    };

    return (
        <DsTagFilter
            items={filters}
            locale={{
                // cspell:disable-next-line
                label: 'Aktywne filtry:',
                // cspell:disable-next-line
                clearButton: 'Zresetuj',
                // cspell:disable-next-line
                showMore: 'Pokaż więcej',
                // cspell:disable-next-line
                showLess: 'Pokaż mniej',
            }}
            onClearAll={handleClearAll}
            onItemDelete={handleFilterDelete} />
    );
};
Expand Collapse story ok
Story testing the expand/collapse functionality and onExpand callback.
const ExpandCollapse = () => <DsTagFilter items={sampleFilters} onExpand={fn()} />;
Small Size story ok
Story showing TagFilter with small tags via slotProps.tag on each item.
const SmallSize = () => {
    const smallFilters: TagFilterItem[] = sampleFilters.slice(0, 6).map((item) => ({
        ...item,
        slotProps: { tag: { size: 'small' } },
    }));

    const [filters, setFilters] = useState(smallFilters);

    const handleClearAll = () => {
        setFilters([]);
    };

    const handleFilterDelete = (filter: TagFilterItem) => {
        setFilters((prev) => prev.filter((f) => f.id !== filter.id));
    };

    return (
        <DsTagFilter
            items={filters}
            onClearAll={handleClearAll}
            onItemDelete={handleFilterDelete} />
    );
};
With Pre Selected Items story ok
Story showing TagFilter with pre-selected items.
const WithPreSelectedItems = () => {
    const [filters, setFilters] = useState<TagFilterItem[]>([
        { id: '1', label: 'Status: Active', selected: true },
        { id: '2', label: 'Running: From 100 to 10,000', selected: false },
        { id: '3', label: 'Completed from 20,000 to 100,000', selected: true },
        { id: '4', label: 'Executor: Category 1', selected: false },
        { id: '5', label: 'Version: 1.0.0', selected: true },
    ]);

    const handleClearAll = () => {
        setFilters([]);
    };

    const handleFilterDelete = (filter: TagFilterItem) => {
        setFilters((prev) => prev.filter((f) => f.id !== filter.id));
    };

    const handleFilterSelect = (filter: TagFilterItem) => {
        setFilters((prev) => prev.map((f) => (f.id === filter.id ? { ...f, selected: !f.selected } : f)));
    };

    return (
        <DsTagFilter
            items={filters}
            onClearAll={handleClearAll}
            onItemDelete={handleFilterDelete}
            onItemSelect={handleFilterSelect} />
    );
};
Empty State story ok
Story verifying the component renders nothing when items is empty.
const EmptyState = () => <DsTagFilter items={[]} />;

DsTextarea

components-textarea · ./src/components/ds-textarea/ds-textarea.stories.ts
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 15 prop types
Component: src/components/ds-textarea/ds-textarea.tsx::default
Props:
/**
 * Additional CSS class names
 */
className?: string

/**
 * The initial value of the input when rendered.
 * Use when you don't need to control the value of the input.
 */
defaultValue?: string

/**
 * Whether the textarea is disabled
 */
disabled?: boolean = false

/**
 * Unique identifier for the textarea element
 */
id?: string

/**
 * The maximum number of characters
 */
maxLength?: number

/**
 * The minimum number of characters
 */
minLength?: number

/**
 * The name of the textarea
 */
name?: string

/**
 * Event handler called when the textarea loses focus
 * 
 * @param event
 */
onBlur?: (event: React.FocusEvent<HTMLTextAreaElement>) => void

/**
 * Callback when the value changes
 */
onChange?: (event: React.ChangeEvent<HTMLTextAreaElement>) => void

/**
 * Value change event handler (provides just the value)
 */
onValueChange?: (value: string) => void

/**
 * The placeholder text
 */
placeholder?: string

/**
 * The ref to the textarea element
 */
ref?: React.Ref<HTMLTextAreaElement>

/**
 * The number of visible text lines
 */
rows?: number = 3

/**
 * Additional styles to apply to the component
 */
style?: React.CSSProperties = {}

/**
 * The current value
 */
value?: string
Imports
import { DsTextarea } from "@drivenets/design-system";
Default story ok
const Default = () => <DsTextarea placeholder="Enter your text here..." rows={3} />;
Disabled story ok
const Disabled = () => <DsTextarea
    value="This textarea is disabled"
    disabled
    placeholder="Disabled textarea" />;
Max Length story ok
const MaxLength = () => <DsTextarea placeholder="Maximum 50 characters allowed" maxLength={50} rows={3} />;

DsTextInput

components-textinput · ./src/components/ds-text-input/ds-text-input.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 23 prop types
Component: src/components/ds-text-input/ds-text-input.tsx::default
Props:
/**
 * Additional CSS class names
 */
className?: string

/**
 * The initial value of the input when rendered.
 * Use when you don't need to control the value of the input.
 */
defaultValue?: string

/**
 * Whether the input is disabled
 */
disabled?: boolean = false

/**
 * Unique identifier for the input field
 */
id?: string

/**
 * The maximum value of the input
 */
max?: number | string

/**
 * The maximum number of characters
 */
maxLength?: number

/**
 * The minimum value of the input
 */
min?: number | string

/**
 * The minimum number of characters
 */
minLength?: number

/**
 * The name of the input field
 */
name?: string

/**
 * Event handler called when the input field loses focus
 * 
 * @param event
 */
onBlur?: (event: React.FocusEvent<HTMLInputElement>) => void

/**
 * Callback when the value changes
 */
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void

/**
 * Event handler called when the input field receives focus
 */
onFocus?: (event: React.FocusEvent<HTMLInputElement>) => void

/**
 * Callback when a key is pressed down
 */
onKeyDown?: (event: React.KeyboardEvent<HTMLInputElement>) => void

/**
 * Value change event handler (provides just the value)
 */
onValueChange?: (value: string) => void

/**
 * The placeholder text
 */
placeholder?: string

/**
 * Whether the input is read only
 */
readOnly?: boolean = false

/**
 * The ref to the input field
 */
ref?: React.Ref<HTMLInputElement>

/**
 * The size of the input field
 * @default default
 */
size?: (typeof textInputSizes)[number] = 'default'

/**
 * Optional render slots for customizing parts of the input.
 */
slots?: {
	/**
	 * Custom component to wrap the input element
	 */
	inputWrapper?: ComponentType<{ children: React.ReactNode }>;

	/**
	 * Adornment to display at the start of the input
	 */
	startAdornment?: React.ReactNode;
	/**
	 * Adornment to display at the end of the input
	 */
	endAdornment?: React.ReactNode;
}

/**
 * Additional styles to apply to the component
 */
style?: React.CSSProperties = {}

/**
 * The tabIndex of the input
 */
tabIndex?: number

/**
 * The type of the input field
 * @default text
 */
type?: string = 'text'

/**
 * The current value
 */
value?: string
Imports
import { DsIcon, DsTextInput } from "@drivenets/design-system";
Default story ok
const Default = () => <DsTextInput placeholder="Enter text..." size="default" style={{ width: '200px' }} />;
Small story ok
const Small = () => <DsTextInput size="small" placeholder="Small input..." style={{ width: '150px' }} />;
With Value story ok
const WithValue = () => <DsTextInput value="Hello World" placeholder="Enter text..." />;
Disabled story ok
const Disabled = () => <DsTextInput placeholder="Disabled input" disabled style={{ width: '200px' }} />;
Controlled story ok
const Controlled = () => {
    const [value, setValue] = useState('initial value');

    return (
        <div
            style={{ display: 'flex', flexDirection: 'column', gap: '16px', alignItems: 'center' }}>
            <DsTextInput
                type="text"
                placeholder="Controlled input"
                style={{ width: '200px' }}
                value={value}
                onValueChange={(newValue) => setValue(newValue)} />
            <div>Current value: {value}</div>
            <button onClick={() => setValue('updated value')}>Update value</button>
            <button onClick={() => setValue('')}>Clear value</button>
        </div>
    );
};
With Start Adornment story ok
const WithStartAdornment = () => <DsTextInput
    placeholder="Enter amount..."
    slots={{
        startAdornment: (
            <span
                style={{
                    color: 'var(--font-secondary)',
                    fontSize: '12px',
                    fontWeight: 'bold',
                }}
            >
                $
            </span>
        ),
    }}
    style={{ width: '200px' }} />;
With End Adornment story ok
const WithEndAdornment = function Render() {
    const [value, setValue] = useState('');

    return (
        <DsTextInput
            placeholder="Enter text..."
            value={value}
            onValueChange={setValue}
            slots={{
                endAdornment: (
                    <button type="button" onClick={() => setValue('')}>
                        <DsIcon icon="close" size="tiny" />
                    </button>
                ),
            }}
            style={{ width: '200px' }}
        />
    );
};
With Both Adornments story ok
const WithBothAdornments = function Render() {
    const [value, setValue] = useState('');

    return (
        <DsTextInput
            placeholder="Search..."
            value={value}
            onValueChange={setValue}
            slots={{
                startAdornment: <DsIcon icon="search" size="tiny" />,
                endAdornment: (
                    <button type="button" onClick={() => setValue('')}>
                        <DsIcon icon="close" size="tiny" />
                    </button>
                ),
            }}
            style={{ width: '200px' }}
        />
    );
};
Custom Adornments (Email) story ok
const CustomEmailAdornments = function Render() {
    const [value, setValue] = useState('');

    return (
        <DsTextInput
            size="small"
            type="email"
            placeholder="Enter email address..."
            value={value}
            onValueChange={setValue}
            slots={{
                startAdornment: (
                    <span
                        style={{
                            backgroundColor: 'var(--color-background-action-weak)',
                            borderRadius: '4px',
                            padding: '2px 6px',
                            fontSize: '12px',
                            color: 'var(--font-secondary)',
                            fontWeight: 'bold',
                        }}
                    >
                        @
                    </span>
                ),
                endAdornment: (
                    <button type="button" onClick={() => console.log('Send clicked')}>
                        <DsIcon icon="send" size="tiny" filled={!!value} />
                    </button>
                ),
            }}
            style={{ width: '250px' }}
        />
    );
};
Disabled Adornments story ok
const DisabledAdornments = () => <DsTextInput
    value="Disabled value"
    disabled
    slots={{
        startAdornment: (
            <button type="button" disabled={true}>
                <DsIcon icon="lock" size="tiny" />
            </button>
        ),
        endAdornment: (
            <button type="button" disabled={true}>
                <DsIcon icon="visibility" size="tiny" />
            </button>
        ),
    }}
    style={{ width: '200px' }} />;
Interactive story ok
const Interactive = function Render() {
    const [value, setValue] = useState('');
    const showClear = !!value;

    const handleValueChange = (newValue: string) => {
        setValue(newValue);
    };

    const handleClear = () => {
        setValue('');
    };

    return (
        <>
            <DsTextInput
                placeholder="Type something..."
                value={value}
                onValueChange={handleValueChange}
                slots={{
                    startAdornment: <DsIcon icon="search" size="tiny" />,
                    endAdornment: showClear && (
                        <button type="button" onClick={handleClear}>
                            <DsIcon icon="close" size="tiny" />
                        </button>
                    ),
                }}
                style={{ width: '200px' }}
            />
            <div style={{ marginTop: '8px', fontSize: '12px', color: 'var(--font-secondary)' }}>
                Character count: {value.length}
            </div>
        </>
    );
};

DsThreadItem

components-comments-threaditem · ./src/components/ds-comment-bubble/components/ds-thread-item/ds-thread-item.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 11 prop types
Component: src/components/ds-comment-bubble/components/ds-thread-item/ds-thread-item.tsx::DsThreadItem
Props:
/**
 * Author of the message
 */
author: CommentAuthor

/**
 * Whether the current viewer can edit or delete this message. Typically true
 * only when the viewer is the message's author.
 * @default false
 */
canModify?: boolean = false

/**
 * Additional CSS class name applied to the item root
 */
className?: string

/**
 * Text content of the message
 */
content: string

/**
 * Timestamp when the message was created
 */
createdAt: Date

/**
 * Stable identifier for the message. Passed to callbacks to identify which
 * message the user acted on.
 */
id: string

/**
 * Whether this message was posted by the author of the top-level comment.
 * Styled slightly differently from replies by other participants.
 * @default false
 */
isCommentAuthorMessage?: boolean = false

/**
 * Called when the viewer deletes the message. Receives the message id.
 */
onDelete?: (messageId: string) => void

/**
 * Called when the viewer saves an edit. Receives the message id and the new content.
 */
onEdit?: (messageId: string, newContent: string) => void

/**
 * Called when the viewer marks the message as unread. Receives the message id.
 */
onMarkUnread?: (messageId: string) => void

/**
 * Called when the viewer resolves the message. Receives the message id.
 */
onResolved?: (messageId: string) => void
Imports
import { DsButton, DsThreadItem } from "@drivenets/design-system";
Default story ok
const Default = () => <DsThreadItem />;
Current User Message story ok
const CurrentUserMessage = () => <DsThreadItem
    id="msg-2"
    isCommentAuthorMessage={false}
    canModify={false}
    content="This is my message, so it appears aligned to the right." />;
Long Message story ok
const LongMessage = () => <DsThreadItem
    id="msg-3"
    content="I think we should consider adjusting the timeline to ensure we have enough resources for the development phase. This will help us maintain quality standards and meet all the project requirements." />;
No Avatar story ok
const NoAvatar = () => <DsThreadItem
    id="msg-4"
    author={{
        id: 'user-2',
        name: 'John Doe',
        avatarSrc: undefined,
    }}
    content="Message from a user without an avatar." />;
Recent Message story ok
const RecentMessage = () => <DsThreadItem
    id="msg-5"
    createdAt={new Date(Date.now() - 30000)}
    content="Just posted this message." />;
Old Message story ok
const OldMessage = () => <DsThreadItem
    id="msg-6"
    createdAt={new Date(Date.now() - 86400000 * 3)}
    content="This message was posted a few days ago." />;
Multiline Message story ok
const MultilineMessage = () => <DsThreadItem
    id="msg-7"
    content={`Line 1: First line of the message
Line 2: Second line with more details
Line 3: Final line with conclusion`} />;
Edit And Save story ok
const EditAndSave = () => <DsThreadItem />;
Edit Save Disabled When Empty story ok
const EditSaveDisabledWhenEmpty = () => <DsThreadItem />;
Edit Save Disabled When Unchanged story ok
const EditSaveDisabledWhenUnchanged = () => <DsThreadItem />;
Delete Action story ok
const DeleteAction = () => <DsThreadItem />;
Mark Unread Action story ok
const MarkUnreadAction = () => <DsThreadItem />;
Resolved Action story ok
const ResolvedAction = () => <DsThreadItem />;
No Actions When Cannot Modify story ok
const NoActionsWhenCannotModify = () => <DsThreadItem
    canModify={false}
    onEdit={undefined}
    onDelete={undefined}
    onMarkUnread={undefined}
    onResolved={undefined} />;
Content Change While Not Editing story ok
const ContentChangeWhileNotEditing = function Render() {
    const [content, setContent] = useState(INITIAL_CONTENT);

    return (
        <>
            <DsThreadItem
                id="msg-ext-1"
                author={mockAuthor}
                content={content}
                createdAt={new Date(Date.now() - 3600000)}
                isCommentAuthorMessage
                canModify
                onEdit={fn()}
                onDelete={fn()}
            />

            <DsButton design="v1.2" size="small" onClick={() => setContent(UPDATED_CONTENT)}>
                Simulate external update
            </DsButton>
        </>
    );
};
Content Change While Editing story ok
const ContentChangeWhileEditing = function Render() {
    const [content, setContent] = useState(INITIAL_CONTENT);

    return (
        <>
            <DsThreadItem
                id="msg-ext-2"
                author={mockAuthor}
                content={content}
                createdAt={new Date(Date.now() - 3600000)}
                isCommentAuthorMessage
                canModify
                onEdit={fn()}
                onDelete={fn()}
            />

            <DsButton design="v1.2" size="small" onClick={() => setContent(UPDATED_CONTENT)}>
                Simulate external update
            </DsButton>
        </>
    );
};
Content Change While Editing Then Cancel story ok
const ContentChangeWhileEditingThenCancel = function Render() {
    const [content, setContent] = useState(INITIAL_CONTENT);

    return (
        <>
            <DsThreadItem
                id="msg-ext-3"
                author={mockAuthor}
                content={content}
                createdAt={new Date(Date.now() - 3600000)}
                isCommentAuthorMessage
                canModify
                onEdit={fn()}
                onDelete={fn()}
            />

            <DsButton design="v1.2" size="small" onClick={() => setContent(UPDATED_CONTENT)}>
                Simulate external update
            </DsButton>
        </>
    );
};

DsTimePicker

components-timepicker · ./src/components/ds-time-picker/ds-time-picker.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 19 prop types
Component: src/components/ds-time-picker/ds-time-picker.tsx::default
Props:
/**
 * Additional CSS class name applied to the root element
 */
className?: string

/**
 * Whether the popover is initially open when uncontrolled.
 * @default false
 */
defaultOpen?: boolean

/**
 * Initial time when uncontrolled.
 */
defaultValue?: Date

/**
 * Whether the picker is disabled and cannot be interacted with.
 * @default false
 */
disabled?: boolean

/**
 * Whether to disable the portal for the popover content
 * @default false
 */
disablePortal?: boolean

/**
 * Whether to hide the clear button
 * @default false
 */
hideClearButton?: boolean

/**
 * Unique identifier for the input field
 */
id?: string

/**
 * Localized labels used in the picker UI and for accessibility.
 */
locale?: DsTimePickerLocale

/**
 * Latest selectable time. Times after this are disabled.
 */
max?: Date

/**
 * Earliest selectable time. Times before this are disabled.
 */
min?: Date

/**
 * Called when the input is blurred
 */
onBlur?: () => void

/**
 * Called when the selected time changes. Receives the new Date or `null` when
 * cleared.
 */
onChange?: (value: Date | null) => void

/**
 * Called when the popover opens or closes. Receives the next `open` state.
 */
onOpenChange?: (open: boolean) => void

/**
 * Whether the popover is open (controlled). Pair with `onOpenChange`.
 */
open?: boolean

/**
 * Placeholder text shown in the input when no time is selected.
 */
placeholder?: string

/**
 * Whether the picker is read-only. The input is focusable but the value cannot
 * be changed.
 * @default false
 */
readOnly?: boolean

/**
 * Ref forwarded to the time picker root element
 */
ref?: RefObject<HTMLDivElement>

/**
 * Props forwarded to the internal input and popover slots.
 */
slotProps?: DsTimePickerSlotProps

/**
 * Current selected time (controlled). Use `null` to represent no selection.
 * Pair with `onChange`.
 */
value?: Date | null
Imports
import { DsTimePicker } from "@drivenets/design-system";
Default story ok
const Default = () => {
    const [value, setValue] = useState<Date | null>();

    return (
        <DsTimePicker
            className={styles.container}
            value={value}
            onChange={(v) => {
                setValue(v);
                args.onChange?.(v);
            }} />
    );
};
With Default Value story ok
const WithDefaultValue = () => <DsTimePicker className={styles.container} defaultValue={createTime(14, 30)} />;
Controlled story ok
const Controlled = () => {
    const defaultDate = createTime(9, 45);

    const [value, setValue] = useState<Date | null>(defaultDate);

    useEffect(() => {
        const interval = setInterval(() => {
            const date = createTime(Math.floor(Math.random() * 24), Math.floor(Math.random() * 60));
            setValue(date);
        }, 5000);

        return () => clearInterval(interval);
    }, []);

    return (
        <div>
            <DsTimePicker
                className={styles.container}
                value={value}
                onChange={(v) => {
                    setValue(v);
                    args.onChange?.(v);
                }} />
            <p className={styles.infoContainer}>In this example:</p>
            <p className={styles.infoContainer}>Value is randomly changing every 5 seconds. As user is typing the input do not show the new value
                                    until input loses focus.
                                </p>
            <p className={styles.infoContainer}>Value:{' '}
                {value
                    ? `${String(value.getHours()).padStart(2, '0')}:${String(value.getMinutes()).padStart(2, '0')}`
                    : 'undefined'}
            </p>
        </div>
    );
};
Disabled story ok
const Disabled = () => <DsTimePicker className={styles.container} value={createTime(14, 30)} disabled />;
Read Only story ok
const ReadOnly = () => <DsTimePicker className={styles.container} value={createTime(14, 30)} readOnly />;
With Min Max story ok
const WithMinMax = () => {
    const [value, setValue] = useState<Date | null>(createTime(13, 50));

    return (
        <div>
            <DsTimePicker
                min={createTime(9, 30)}
                max={createTime(17, 40)}
                className={styles.container}
                value={value}
                onChange={(v) => {
                    setValue(v);
                    args.onChange?.(v);
                }} />
            <p className={styles.infoContainer}>Value:{' '}
                {value
                    ? `${String(value.getHours()).padStart(2, '0')}:${String(value.getMinutes()).padStart(2, '0')}`
                    : 'none'}
            </p>
            <p className={styles.infoContainer}>Range: {args.min?.toLocaleTimeString()}– {args.max?.toLocaleTimeString()}
            </p>
        </div>
    );
};

DsToast

components-toast · ./src/components/ds-toast/ds-toast.stories.tsx
Design system Toast component This component is used within the Toaster render function
Prop types (react-docgen) 7 prop types
Component: src/components/ds-toast/ds-toast.tsx::DsToast
Props:
/**
 * Optional action buttons to be rendered inside the component
 */
actions?: ReactNode

/**
 * Additional CSS class names
 */
className?: string

/**
 * Toast description/message
 */
description?: ReactNode

/**
 * Callback when toast is dismissed
 */
onDismiss?: () => void

/**
 * Additional styles to apply to the component
 */
style?: CSSProperties

/**
 * Toast title
 */
title?: ReactNode

/**
 * Toast type that determines styling and icon
 */
variant?: (typeof toastVariants)[number] = 'info'
Imports
import { DsButton, DsToast, DsToastProvider } from "@drivenets/design-system";
Success story ok
const Success = () => (
    <DsToastProvider>
        <ToastDemo variant="success" title="Success!" description="Your action was completed successfully." />
    </DsToastProvider>
);
Info story ok
const Info = () => (
    <DsToastProvider>
        <ToastDemo variant="info" title="Information" description="Here is some helpful information for you." />
    </DsToastProvider>
);
Warning story ok
const Warning = () => (
    <DsToastProvider>
        <ToastDemo variant="warning" title="Warning" description="Please be aware of this important notice." />
    </DsToastProvider>
);
Warning No Title story ok
const WarningNoTitle = () => (
    <DsToastProvider>
        <ToastDemo variant="warning" description="Something went wrong. Please try again." persistent />
    </DsToastProvider>
);
Warning No Title Action story ok
const WarningNoTitleAction = () => (
    <DsToastProvider>
        <ToastDemo
            variant="warning"
            description="Something went wrong. Please try again."
            actions={
                <DsButton design="v1.2" variant="danger">
                    Restart
                </DsButton>
            }
            persistent
        ></ToastDemo>
    </DsToastProvider>
);
Warning With Actions story ok
const WarningWithActions = () => {
    const WithActionsDemo = () => {
        const { createToast, dismissToast } = useToaster();
        const [action, setAction] = useState<string>();

        const showToastWithAction = () => {
            const toastId = createToast({
                title: 'File upload failed',
                description: 'Your file could not be uploaded.',
                variant: 'warning',
                actions: (
                    <div className={styles.actionButtonsContainer}>
                        <DsButton
                            data-testid="abort-button"
                            design="v1.2"
                            onClick={() => {
                                setAction('abort');
                                dismissToast(toastId);
                            }}
                            variant="ghost"
                        >
                            Abort
                        </DsButton>
                        <DsButton
                            data-testid="retry-button"
                            design="v1.2"
                            onClick={() => {
                                setAction('retry');
                                dismissToast(toastId);
                            }}
                            variant="danger"
                        >
                            Re-try
                        </DsButton>
                    </div>
                ),
                persistent: true, // No auto-dismiss
            });
        };

        return (
            <div className={styles.demoContainer}>
                <h3>Toast with Action</h3>
                <DsButton
                    data-testid="show-toast-button"
                    design="v1.2"
                    onClick={showToastWithAction}
                    variant="filled"
                >
                    Show Toast with Actions
                </DsButton>
                {action && <p data-testid="action-result">{action}</p>}
            </div>
        );
    };

    return (
        <DsToastProvider>
            <WithActionsDemo />
        </DsToastProvider>
    );
};
Error story ok
const Error = () => (
    <DsToastProvider>
        <ToastDemo
            variant="error"
            title="Error"
            description="Something went wrong. Please try again."
            persistent // No auto-dismiss
        />
    </DsToastProvider>
);
Long Content story ok
const LongContent = () => {
    const LongContentDemo = () => {
        const { createToast } = useToaster();

        const showLongToast = () => {
            createToast({
                title: 'Important Notice',
                description:
                    'This is a longer message that demonstrates how the toast component handles extended content. The text will wrap appropriately and maintain good readability while staying within the toast boundaries.',
                variant: 'warning',
                duration: 8000,
            });
        };

        return (
            <div className={styles.demoContainer}>
                <h3>Long Content Toast</h3>
                <DsButton design="v1.2" onClick={showLongToast} variant="filled">
                    Show Long Content Toast
                </DsButton>
            </div>
        );
    };

    return (
        <DsToastProvider>
            <LongContentDemo />
        </DsToastProvider>
    );
};
Multiple Toasts story ok
const MultipleToasts = () => (
    <DsToastProvider>
        <MultipleToastsDemo />
    </DsToastProvider>
);

DsToggle

components-toggle · ./src/components/ds-toggle/ds-toggle.stories.tsx
Design system Toggle component
Prop types (react-docgen) 9 prop types
Component: src/components/ds-toggle/ds-toggle.tsx::default
Props:
/**
 * Controlled checked state
 */
checked?: boolean

/**
 * Additional CSS class names
 */
className?: string

/**
 * Whether the toggle is disabled
 */
disabled?: boolean

/**
 * Optional form field name
 */
name?: string

/**
 * Event handler called on user interaction.
 * Note: prefer `onValueChange` for checked-state changes.
 */
onChange?: (event: MouseEvent<HTMLElement>) => void

/**
 * Event handler called when the checked state changes
 */
onValueChange?: (checked: boolean) => void

/**
 * Forwarded ref for the toggle label element
 */
ref?: Ref<HTMLElement>

/**
 * Visual size of the toggle
 */
size?: (typeof toggleSizes)[number] = 'default'

/**
 * Additional styles to apply to the component
 */
style?: CSSProperties = {}
Imports
import { DsToggle } from "@drivenets/design-system";
Default story ok
const Default = () => <DsToggle label={label} labelInfo={labelInfo} className="custom-toggle" />;
Controlled story ok
const Controlled = function Render() {
    const [checked, setChecked] = useState(true);

    return <DsToggle label={label} labelInfo={labelInfo} checked={checked} onValueChange={setChecked} />;
};
Small story ok
const Small = function Render() {
    return <DsToggle label={label} labelInfo={labelInfo} size="small" />;
};
Disabled story ok
const Disabled = () => <DsToggle label={label} labelInfo={labelInfo} disabled />;
Children Custom Labels story ok
const ChildrenCustomLabels = function Render() {
    return (
        <DsToggle size="small">
            <span
                style={{
                    color: 'red',
                }}
            >
                Custom label totally!
            </span>
        </DsToggle>
    );
};

DsTooltip

components-tooltip · ./src/components/ds-tooltip/ds-tooltip.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 3 prop types
Component: src/components/ds-tooltip/ds-tooltip.tsx::default
Props:
/**
 * The content to be rendered inside the tooltip
 */
children: ReactNode

/**
 * Tooltip content
 */
content?: string | ReactNode

/**
 * Props forwarded to nested sub-components.
 */
slotProps?: {
	/**
	 * Props forwarded to the tooltip content popover (className / inline styles).
	 */
	content?: {
		className?: string;
		style?: CSSProperties;
	};
}
Imports
import { DsIcon, DsTooltip } from "@drivenets/design-system";
Default story ok
const Default = () => <DsTooltip content="This is the mouse over tooltip message."><DsIcon icon="info" /></DsTooltip>;
Long Text story ok
const LongText = () => <DsTooltip
    content="This tooltip contains a long message that spans multiple lines to verify the content is fully visible without truncation. The tooltip should expand vertically to accommodate all text, regardless of length. Users rely on tooltips to reveal information that may be clipped elsewhere in the interface, so cutting off tooltip content defeats the purpose."><DsIcon icon="info" /></DsTooltip>;
Rich Content story ok
const RichContent = () => <DsTooltip
    content={(<div>
        <strong>Multi-line</strong>tooltip with <em>JSX</em>
        <br />
        <span style={{ color: '#9cdcfe' }}>No truncation should occur.</span>
    </div>)}><DsIcon icon="info" /></DsTooltip>;
Custom Width With Ellipsis story ok
const CustomWidthWithEllipsis = () => <DsTooltip
    content="Narrow tooltip with custom max-width and text overflow ellipsis applied via slotProps."
    slotProps={{
        content: {
            style: {
                maxWidth: 200,
                overflow: 'hidden',
                textOverflow: 'ellipsis',
                whiteSpace: 'nowrap',
            },
        },
    }}><DsIcon icon="info" /></DsTooltip>;

DsTree.Root

components-tree · ./src/components/ds-tree/ds-tree.stories.tsx
Prop type error
File: /opt/build/repo/packages/design-system/src/components/ds-tree/ds-tree.tsx
Error:
No suitable component definition found.
You can debug your component file in this playground: https://react-docgen.dev/playground
Code:
import { TreeView } from '@ark-ui/react/tree-view';
import classNames from 'classnames';

import { DsIcon } from '../ds-icon';
import styles from './ds-tree.module.scss';
import type {
	DsTreeBranchContentProps,
	DsTreeBranchControlProps,
	DsTreeBranchIndentGuideProps,
	DsTreeBranchIndicatorProps,
	DsTreeBranchProps,
	DsTreeBranchTextProps,
	DsTreeItemActionProps,
	DsTreeItemIndicatorProps,
	DsTreeItemProps,
	DsTreeItemTextProps,
	DsTreeNode,
	DsTreeNodeCheckboxProps,
	DsTreeRootProps,
	DsTreeTreeProps,
} from './ds-tree.types';

const DsTreeRoot = <T extends DsTreeNode = DsTreeNode>({
	collection,
	size = 'medium',
	ref,
	className,
	style,
	children,
	selectedValue,
	defaultSelectedValue,
	onSelectionChange,
	selectionMode = 'single',
	expandedValue,
	defaultExpandedValue,
	onExpandedChange,
	expandOnClick = true,
	checkedValue,
	defaultCheckedValue,
	onCheckedChange,
	typeahead = true,
	lazyMount = true,
	unmountOnExit,
}: DsTreeRootProps<T>) => (
	<TreeView.Root
		ref={ref}
		collection={collection}
		selectedValue={selectedValue}
		defaultSelectedValue={defaultSelectedValue}
		onSelectionChange={onSelectionChange}
		selectionMode={selectionMode}
		expandedValue={expandedValue}
		defaultExpandedValue={defaultExpandedValue}
		onExpandedChange={onExpandedChange}
		expandOnClick={expandOnClick}
		checkedValue={checkedValue}
		defaultCheckedValue={defaultCheckedValue}
		onCheckedChange={onCheckedChange}
		typeahead={typeahead}
		lazyMount={lazyMount}
		unmountOnExit={unmountOnExit}
		className={classNames(styles.root, className)}
		style={style}
		data-size={size}
	>
		{children}
	</TreeView.Root>
);

const DsTreeTree = ({ className, style, children }: DsTreeTreeProps) => (
	<TreeView.Tree className={classNames(styles.tree, className)} style={style}>
		{children}
	</TreeView.Tree>
);

const DsTreeBranch = ({ className, style, children }: DsTreeBranchProps) => (
	<TreeView.Branch className={classNames(styles.branch, className)} style={style}>
		{children}
	</TreeView.Branch>
);

const DsTreeBranchControl = ({ className, style, children }: DsTreeBranchControlProps) => (
	<TreeView.BranchControl className={classNames(styles.branchControl, className)} style={style}>
		{children}
	</TreeView.BranchControl>
);

const DsTreeBranchIndicator = ({ className, style, children }: DsTreeBranchIndicatorProps) => (
	<TreeView.BranchIndicator className={classNames(styles.branchIndicator, className)} style={style}>
		{children ?? <DsIcon icon="keyboard_arrow_right" size="tiny" />}
	</TreeView.BranchIndicator>
);

const DsTreeBranchText = ({ className, style, children }: DsTreeBranchTextProps) => (
	<TreeView.BranchText className={classNames(styles.branchText, className)} style={style}>
		{children}
	</TreeView.BranchText>
);

const DsTreeBranchContent = ({ className, style, children }: DsTreeBranchContentProps) => (
	<TreeView.BranchContent className={classNames(styles.branchContent, className)} style={style}>
		{children}
	</TreeView.BranchContent>
);

const DsTreeBranchIndentGuide = ({ className, style }: DsTreeBranchIndentGuideProps) => (
	<TreeView.BranchIndentGuide className={classNames(styles.branchIndentGuide, className)} style={style} />
);

const DsTreeItem = ({ className, style, children, onClick }: DsTreeItemProps) => (
	<TreeView.Item className={classNames(styles.item, className)} style={style} onClick={onClick}>
		{children}
	</TreeView.Item>
);

const DsTreeItemText = ({ className, style, children }: DsTreeItemTextProps) => (
	<TreeView.ItemText className={classNames(styles.itemText, className)} style={style}>
		{children}
	</TreeView.ItemText>
);

const DsTreeItemIndicator = ({ className, style }: DsTreeItemIndicatorProps) => (
	<span className={classNames(styles.itemIndicator, className)} style={style} aria-hidden="true">
		<span className={styles.itemDot} />
	</span>
);

const DsTreeNodeCheckbox = ({ className, style }: DsTreeNodeCheckboxProps) => (
	<TreeView.NodeCheckbox className={classNames(styles.nodeCheckbox, className)} style={style}>
		<div className={styles.nodeCheckboxBox}>
			<TreeView.NodeCheckboxIndicator
				className={styles.nodeCheckboxIndicator}
				indeterminate={<DsIcon icon="check_indeterminate_small" size="tiny" variant="outlined" />}
			>
				<DsIcon icon="check_small" size="tiny" variant="outlined" />
			</TreeView.NodeCheckboxIndicator>
		</div>
	</TreeView.NodeCheckbox>
);

const DsTreeItemAction = ({ className, style, children, onClick }: DsTreeItemActionProps) => (
	<button
		className={classNames(styles.itemAction, className)}
		style={style}
		type="button"
		tabIndex={-1}
		onClick={(e) => {
			e.stopPropagation();
			onClick?.(e);
		}}
	>
		{children}
	</button>
);

export const DsTree = {
	Root: DsTreeRoot,
	Tree: DsTreeTree,
	NodeProvider: TreeView.NodeProvider,
	NodeContext: TreeView.NodeContext,
	Branch: DsTreeBranch,
	BranchControl: DsTreeBranchControl,
	BranchIndicator: DsTreeBranchIndicator,
	BranchText: DsTreeBranchText,
	BranchContent: DsTreeBranchContent,
	BranchIndentGuide: DsTreeBranchIndentGuide,
	Item: DsTreeItem,
	ItemText: DsTreeItemText,
	ItemIndicator: DsTreeItemIndicator,
	ItemAction: DsTreeItemAction,
	NodeCheckbox: DsTreeNodeCheckbox,
};
Info
No description found. Write a jsdoc comment such as /** Component description */.
Imports
import { DsFilterStatusIcon, DsIcon, DsTree } from "@drivenets/design-system";
Default story ok
const Default = () => {
    const collection = createDsTreeCollection(sideNavNodes);

    return (
        <DsTree.Root
            collection={collection}
            defaultExpandedValue={['network']}
            size="medium"
            onSelectionChange={fn()}
            onExpandedChange={fn()}>
            <DsTree.Tree>
                {collection.rootNode.children?.map((node, index) => (
                    <SideNavDsTreeNode key={node.id} node={node} indexPath={[index]} />
                ))}
            </DsTree.Tree>
        </DsTree.Root>
    );
};
Controlled story ok
const Controlled = () => {
    const collection = createDsTreeCollection(sideNavNodes);
    const [selectedValue, setSelectedValue] = useState<string[]>([]);
    const [expandedValue, setExpandedValue] = useState(['network']);

    return (
        <div>
            <div>Selected: {selectedValue.length > 0 ? selectedValue.join(', ') : 'none'}</div>
            <div>Expanded: {expandedValue.length > 0 ? expandedValue.join(', ') : 'none'}</div>
            <DsTree.Root
                size="medium"
                collection={collection}
                selectedValue={selectedValue}
                onSelectionChange={(details: { selectedValue: string[] }) =>
                    setSelectedValue(details.selectedValue)
                }
                expandedValue={expandedValue}
                onExpandedChange={(details: { expandedValue: string[] }) => setExpandedValue(details.expandedValue)}>
                <DsTree.Tree>
                    {collection.rootNode.children?.map((node, index) => (
                        <SideNavDsTreeNode key={node.id} node={node} indexPath={[index]} />
                    ))}
                </DsTree.Tree>
            </DsTree.Root>
        </div>
    );
};
Checkbox With Icons story ok
const CheckboxWithIcons = () => {
    const collection = createDsTreeCollection(mapLayersNodes);

    return (
        <DsTree.Root
            size={args.size}
            collection={collection}
            defaultCheckedValue={['data-centers', 'dc-east', 'dc-west', 'oms']}
            defaultExpandedValue={['devices', 'optical']}
            onCheckedChange={fn()}
            onExpandedChange={fn()}>
            <DsTree.Tree>
                {collection.rootNode.children?.map((node, index) => (
                    <CheckboxWithIconsNode key={node.id} node={node} indexPath={[index]} />
                ))}
            </DsTree.Tree>
        </DsTree.Root>
    );
};
With Status Icons story ok
const WithStatusIcons = () => {
    const collection = createDsTreeCollection(workflowNodes);
    const onNavigate = fn();

    return (
        <DsTree.Root
            size="medium"
            collection={collection}
            defaultExpandedValue={['workflow-1234', 'sw-running-02', 'sw-running-06']}
            onSelectionChange={fn()}
            onExpandedChange={fn()}>
            <DsTree.Tree>
                {collection.rootNode.children?.map((node, index) => (
                    <WorkflowDsTreeNode
                        key={node.id}
                        node={node}
                        indexPath={[index]}
                        onNavigate={onNavigate}
                        size={args.size}
                    />
                ))}
            </DsTree.Tree>
        </DsTree.Root>
    );
};

DsTypography

components-typography · ./src/components/ds-typography/ds-typography.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 7 prop types
Component: src/components/ds-typography/ds-typography.tsx::default
Props:
/**
 * When true, composes typography styles onto the child element without extra DOM nodes
 */
asChild?: boolean = false

/**
 * The content of the component
 */
children: React.ReactNode

/**
 * Additional CSS class names
 */
className?: string

/**
 * Text color. Semantic names map to `--font-*` tokens; raw CSS color values
 * (hex, rgb, hsl, CSS variables, named colors) pass through unchanged.
 * Omit to inherit.
 */
color?: TypographyColor | (string & {})

/**
 * Ref to the rendered element.
 */
ref?: Ref<HTMLElement>

/**
 * Additional styles to apply to the typography component
 */
style?: React.CSSProperties

/**
 * The semantic variant of the component
 */
variant: keyof typeof typographyVariantConfig
Imports
import { DsStack, DsTypography } from "@drivenets/design-system";
Default story ok
const Default = () => <DsTypography variant="body-md-reg" color="main">{sample}</DsTypography>;
Variants story ok
const Variants = () => (
    <DsStack direction="column" gap="var(--sm)">
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>heading1</code>
            <DsTypography variant="heading1">{sample}</DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>heading2</code>
            <DsTypography variant="heading2">{sample}</DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>heading3</code>
            <DsTypography variant="heading3">{sample}</DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>heading4</code>
            <DsTypography variant="heading4">{sample}</DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>body-md-reg</code>
            <DsTypography variant="body-md-reg">{sample}</DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>body-md-md</code>
            <DsTypography variant="body-md-md">{sample}</DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>body-md-semi-bold</code>
            <DsTypography variant="body-md-semi-bold">{sample}</DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>body-md-bold</code>
            <DsTypography variant="body-md-bold">{sample}</DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>body-md-link</code>
            <DsTypography variant="body-md-link">{sample}</DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>body-sm-reg</code>
            <DsTypography variant="body-sm-reg">{sample}</DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>body-sm-md</code>
            <DsTypography variant="body-sm-md">{sample}</DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>body-sm-semi-bold</code>
            <DsTypography variant="body-sm-semi-bold">{sample}</DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>body-sm-bold</code>
            <DsTypography variant="body-sm-bold">{sample}</DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>body-sm-link</code>
            <DsTypography variant="body-sm-link">{sample}</DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>body-xs-reg</code>
            <DsTypography variant="body-xs-reg">{sample}</DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>body-xs-md</code>
            <DsTypography variant="body-xs-md">{sample}</DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>body-xs-semi-bold</code>
            <DsTypography variant="body-xs-semi-bold">{sample}</DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>body-xs-bold</code>
            <DsTypography variant="body-xs-bold">{sample}</DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>body-xs-link</code>
            <DsTypography variant="body-xs-link">{sample}</DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>code-sm-reg</code>
            <DsTypography variant="code-sm-reg">{sample}</DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>code-sm-semi-bold</code>
            <DsTypography variant="code-sm-semi-bold">{sample}</DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>code-xs-reg</code>
            <DsTypography variant="code-xs-reg">{sample}</DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>code-xs-semi-bold</code>
            <DsTypography variant="code-xs-semi-bold">{sample}</DsTypography>
        </div>
    </DsStack>
);
Colors story ok
const Colors = () => (
    <DsStack direction="column" gap="var(--sm)">
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>main</code>
            <DsTypography variant="body-md-md" color="main">
                {sample}
            </DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>secondary</code>
            <DsTypography variant="body-md-md" color="secondary">
                {sample}
            </DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>action</code>
            <DsTypography variant="body-md-md" color="action">
                {sample}
            </DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>action-hover</code>
            <DsTypography variant="body-md-md" color="action-hover">
                {sample}
            </DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>action-secondary</code>
            <DsTypography variant="body-md-md" color="action-secondary">
                {sample}
            </DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>action-secondary-hover</code>
            <DsTypography variant="body-md-md" color="action-secondary-hover">
                {sample}
            </DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>disabled</code>
            <DsTypography variant="body-md-md" color="disabled">
                {sample}
            </DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>placeholder</code>
            <DsTypography variant="body-md-md" color="placeholder">
                {sample}
            </DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>highlight</code>
            <DsTypography variant="body-md-md" color="highlight">
                {sample}
            </DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>success</code>
            <DsTypography variant="body-md-md" color="success">
                {sample}
            </DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>warning</code>
            <DsTypography variant="body-md-md" color="warning">
                {sample}
            </DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>error</code>
            <DsTypography variant="body-md-md" color="error">
                {sample}
            </DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>code</code>
            <DsTypography variant="body-md-md" color="code">
                {sample}
            </DsTypography>
        </div>
    </DsStack>
);
Colors On Dark story ok
const ColorsOnDark = () => (
    <div className={storyStyles.onDark}>
        <div className={storyStyles.row}>
            <code className={classNames(storyStyles.label, storyStyles.onDarkLabel)}>on-action</code>
            <DsTypography variant="body-md-md" color="on-action">
                {sample}
            </DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={classNames(storyStyles.label, storyStyles.onDarkLabel)}>on-disabled</code>
            <DsTypography variant="body-md-md" color="on-disabled">
                {sample}
            </DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={classNames(storyStyles.label, storyStyles.onDarkLabel)}>light-disabled</code>
            <DsTypography variant="body-md-md" color="light-disabled">
                {sample}
            </DsTypography>
        </div>
    </div>
);
Escape Hatch story ok
const EscapeHatch = () => (
    <DsStack direction="column" gap="var(--sm)">
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>{'color="success"'}</code>
            <DsTypography variant="body-md-md" color="success">
                Semantic token.
            </DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>{'color="var(--color-dap-purple-600)"'}</code>
            <DsTypography variant="body-md-md" color="var(--color-dap-purple-600)">
                Custom primitive token via color prop.
            </DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>{'color="#ff8800"'}</code>
            <DsTypography variant="body-md-md" color="#ff8800">
                Inline hex color via color prop.
            </DsTypography>
        </div>
        <div className={storyStyles.row}>
            <code className={storyStyles.label}>style overrides color prop</code>
            <DsTypography variant="body-md-md" color="success" style={{ color: '#d70a00' }}>
                Inline style still wins when both are set.
            </DsTypography>
        </div>
    </DsStack>
);
As Child story ok
const AsChild = () => (
    <DsStack direction="column" gap="var(--sm)">
        <DsTypography variant="heading3" asChild>
            <a href="#anchor">Heading rendered as anchor via asChild</a>
        </DsTypography>
        <DsTypography variant="body-md-md" color="action" asChild>
            <button type="button">Button styled like body-md-md / action</button>
        </DsTypography>
    </DsStack>
);

DsVerticalTabs

components-verticaltabs · ./src/components/ds-vertical-tabs/ds-vertical-tabs.stories.tsx
Design system vertical tabs component - compound component for flexible vertical navigation
example: ```tsx <DsVerticalTabs value={selected} onValueChange={handleChange}> <DsVerticalTabs.List> <DsVerticalTabs.Tab value="status"> <span>Status</span> <span className={styles.badge}>5</span> </DsVerticalTabs.Tab> <DsVerticalTabs.Tab value="date">Date</DsVerticalTabs.Tab> </DsVerticalTabs.List> <DsVerticalTabs.Content value="status">Status filters...</DsVerticalTabs.Content> <DsVerticalTabs.Content value="date">Date filters...</DsVerticalTabs.Content> </DsVerticalTabs> ```
Prop types (react-docgen) 5 prop types
Component: src/components/ds-vertical-tabs/ds-vertical-tabs.tsx::default
Props:
/**
 * Child components (DsVerticalTabs.List, etc.)
 */
children: ReactNode

/**
 * Additional CSS class names
 */
className?: string

/**
 * Callback when selected tab changes
 */
onValueChange?: (value: string | null) => void

/**
 * Optional inline styles
 */
style?: CSSProperties

/**
 * Currently selected tab value
 */
value?: string
Imports
import { DsTypography, DsVerticalTabs } from "@drivenets/design-system";
Default story ok
const Default = () => <div className={styles.storyContainer}>
    <DsVerticalTabs onValueChange={fn()}>
        <DsVerticalTabs.List>
            {sampleItems.map((item) => (
                <DsVerticalTabs.Tab key={item.id} value={item.id} disabled={item.disabled}>
                    <TabLabel item={item} />
                </DsVerticalTabs.Tab>
            ))}
        </DsVerticalTabs.List>
        {sampleItems.map((item) => (
            <DsVerticalTabs.Content key={item.id} value={item.id}>
                Selected tab content: {item.id}
            </DsVerticalTabs.Content>
        ))}
    </DsVerticalTabs>
</div>;
With Disabled Items story ok
const WithDisabledItems = () => <div className={styles.storyContainer}>
    <DsVerticalTabs onValueChange={fn()}>
        <DsVerticalTabs.List>
            {sampleItemsWithDisabled.map((item) => (
                <DsVerticalTabs.Tab key={item.id} value={item.id} disabled={item.disabled}>
                    <TabLabel item={item} />
                </DsVerticalTabs.Tab>
            ))}
        </DsVerticalTabs.List>
        {sampleItemsWithDisabled.map((item) => (
            <DsVerticalTabs.Content key={item.id} value={item.id}>
                Selected tab content: {item.id}
            </DsVerticalTabs.Content>
        ))}
    </DsVerticalTabs>
</div>;
Long Labels story ok
const LongLabels = () => {
    return (
        <div className={styles.storyContainerShort}>
            <DsVerticalTabs onValueChange={fn()}>
                <DsVerticalTabs.List>
                    {itemsWithLongLabels.map((item) => (
                        <DsVerticalTabs.Tab key={item.id} value={item.id} disabled={item.disabled}>
                            <TabLabel item={item} />
                        </DsVerticalTabs.Tab>
                    ))}
                </DsVerticalTabs.List>
                {itemsWithLongLabels.map((item) => (
                    <DsVerticalTabs.Content key={item.id} value={item.id}>
                        Selected tab content: {item.id}
                    </DsVerticalTabs.Content>
                ))}
            </DsVerticalTabs>
        </div>
    );
};
High Counts story ok
const HighCounts = () => {
    return (
        <div className={styles.storyContainerShort}>
            <DsVerticalTabs onValueChange={fn()}>
                <DsVerticalTabs.List>
                    {itemsWithHighCounts.map((item) => (
                        <DsVerticalTabs.Tab key={item.id} value={item.id} disabled={item.disabled}>
                            <TabLabel item={item} />
                        </DsVerticalTabs.Tab>
                    ))}
                </DsVerticalTabs.List>
                {itemsWithHighCounts.map((item) => (
                    <DsVerticalTabs.Content key={item.id} value={item.id}>
                        Selected tab content: {item.id}
                    </DsVerticalTabs.Content>
                ))}
            </DsVerticalTabs>
        </div>
    );
};

DsWorkspace

components-workspace · ./src/components/ds-workspace/ds-workspace.stories.tsx
Info
No description found. Write a jsdoc comment such as /** Component description */.
Prop types (react-docgen) 1 prop type
Component: src/components/ds-workspace/ds-workspace.tsx::default
Props:
/**
 * Whether the workspace should fill its parent height (`100%`) instead of the viewport (`100vh`).
 * @default false
 */
fillParent?: boolean = false
Imports
import { DsButtonV3, DsDrawer, DsIcon, DsStatusBadge, DsTypography, DsWorkspace } from "@drivenets/design-system";
Default story ok
const Default = () => (
    <DsWorkspace>
        <DsWorkspace.Header>
            <WorkspaceHeader />
        </DsWorkspace.Header>

        <DsWorkspace.SubHeader>
            <div className={styles.subHeaderContent}>
                <DsTypography variant="body-sm-semi-bold">Dashboard</DsTypography>
                <DsTypography variant="body-xs-reg">Last updated 2 min ago</DsTypography>
            </div>
        </DsWorkspace.SubHeader>

        <DsWorkspace.Content>
            <div className={styles.mainContent}>
                <div className={styles.card}>
                    <DsTypography variant="heading3">Welcome</DsTypography>
                    <DsTypography variant="body-md-reg">This is the main content area of the workspace.</DsTypography>
                </div>
                <div className={styles.card}>
                    <DsTypography variant="heading3">Section 2</DsTypography>
                    <DsTypography variant="body-md-reg">Another content section.</DsTypography>
                </div>
            </div>
        </DsWorkspace.Content>

        <DsWorkspace.Footer>
            <div className={styles.footerContent}>
                <span>v1.2.0</span>
                <div className={styles.footerActions}>
                    <DsButtonV3 variant="tertiary" size="small">
                        Help
                    </DsButtonV3>
                    <DsButtonV3 variant="tertiary" size="small">
                        Feedback
                    </DsButtonV3>
                </div>
            </div>
        </DsWorkspace.Footer>
    </DsWorkspace>
);
With Drawer story ok
const WithDrawer = function Render() {
    const [drawerOpen, setDrawerOpen] = useState(false);

    return (
        <DsWorkspace>
            <DsWorkspace.Header>
                <WorkspaceHeader onAction={() => setDrawerOpen(true)} />
            </DsWorkspace.Header>

            <DsWorkspace.SubHeader>
                <div className={styles.subHeaderContent}>
                    <DsTypography variant="body-sm-semi-bold">Dashboard</DsTypography>
                </div>
            </DsWorkspace.SubHeader>

            <DsWorkspace.Content>
                <div className={styles.mainContent}>
                    <div className={styles.card}>
                        <DsTypography variant="heading3">Drawer containment</DsTypography>
                        <DsTypography variant="body-md-reg">
                            Click &quot;Save project&quot; in the header to open the drawer. It renders inside Content —
                            below the header/subheader and above the footer.
                        </DsTypography>
                    </div>
                </div>

                <DsDrawer open={drawerOpen} onOpenChange={setDrawerOpen} columns={4}>
                    <DsDrawer.Header>
                        <DsDrawer.Title>Details</DsDrawer.Title>
                        <DsDrawer.CloseTrigger />
                    </DsDrawer.Header>
                    <DsDrawer.Body>
                        <div className={styles.mainContent}>
                            <DsTypography variant="body-md-reg">
                                This drawer is contained within the content area.
                            </DsTypography>
                        </div>
                    </DsDrawer.Body>
                    <DsDrawer.Footer>
                        <DsDrawer.Actions>
                            <DsButtonV3 variant="tertiary" size="large" onClick={() => setDrawerOpen(false)}>
                                Cancel
                            </DsButtonV3>
                            <DsButtonV3 variant="primary" size="large">
                                Save
                            </DsButtonV3>
                        </DsDrawer.Actions>
                    </DsDrawer.Footer>
                </DsDrawer>
            </DsWorkspace.Content>

            <DsWorkspace.Footer>
                <div className={styles.footerContent}>
                    <span>v1.2.0</span>
                </div>
            </DsWorkspace.Footer>
        </DsWorkspace>
    );
};
With Drawer And Backdrop story ok
const WithDrawerAndBackdrop = function Render() {
    const [drawerOpen, setDrawerOpen] = useState(false);

    return (
        <DsWorkspace>
            <DsWorkspace.Header>
                <WorkspaceHeader onAction={() => setDrawerOpen(true)} />
            </DsWorkspace.Header>

            <DsWorkspace.SubHeader>
                <div className={styles.subHeaderContent}>
                    <DsTypography variant="body-sm-semi-bold">Dashboard</DsTypography>
                </div>
            </DsWorkspace.SubHeader>

            <DsWorkspace.Content>
                <div className={styles.mainContent}>
                    <div className={styles.card}>
                        <DsTypography variant="heading3">Backdrop containment</DsTypography>
                        <DsTypography variant="body-md-reg">
                            Click &quot;Save project&quot; to open the drawer. The backdrop only covers the content area,
                            not the header or footer.
                        </DsTypography>
                    </div>
                </div>

                <DsDrawer open={drawerOpen} onOpenChange={setDrawerOpen} columns={4} backdrop>
                    <DsDrawer.Header>
                        <DsDrawer.Title>Modal Drawer</DsDrawer.Title>
                        <DsDrawer.CloseTrigger />
                    </DsDrawer.Header>
                    <DsDrawer.Body>
                        <div className={styles.mainContent}>
                            <DsTypography variant="body-md-reg">The backdrop is scoped to the content area.</DsTypography>
                        </div>
                    </DsDrawer.Body>
                </DsDrawer>
            </DsWorkspace.Content>

            <DsWorkspace.Footer>
                <div className={styles.footerContent}>
                    <span>v1.2.0</span>
                </div>
            </DsWorkspace.Footer>
        </DsWorkspace>
    );
};
Fill Parent story ok
const FillParent = () => (
    <div style={{ height: 400, border: '2px dashed var(--border)' }}>
        <DsWorkspace fillParent>
            <DsWorkspace.Header>
                <WorkspaceHeader />
            </DsWorkspace.Header>

            <DsWorkspace.Content>
                <div className={styles.mainContent}>
                    <DsTypography variant="body-md-reg">
                        This workspace fills its parent container (400px) instead of the viewport.
                    </DsTypography>
                </div>
            </DsWorkspace.Content>

            <DsWorkspace.Footer>
                <div className={styles.footerContent}>
                    <span>v1.2.0</span>
                </div>
            </DsWorkspace.Footer>
        </DsWorkspace>
    </div>
);
Header Only story ok
const HeaderOnly = () => (
    <DsWorkspace>
        <DsWorkspace.Header>
            <WorkspaceHeader />
        </DsWorkspace.Header>

        <DsWorkspace.Content>
            <div className={styles.mainContent}>
                <div className={styles.card}>
                    <DsTypography variant="heading3">No SubHeader or Footer</DsTypography>
                    <DsTypography variant="body-md-reg">
                        All sub-components are optional. Use only what you need.
                    </DsTypography>
                </div>
            </div>
        </DsWorkspace.Content>
    </DsWorkspace>
);

SampleForm

examples-sampleform · ./src/stories/sample-form/sample-form.stories.ts
Info
No description found. Write a jsdoc comment such as /** Component description */.
Imports
import { SampleForm } from "@drivenets/design-system";
Default story ok
const Default = () => <SampleForm />;

Scrollbars

components-scrollbars · ./src/stories/scrollbars/scrollbars.stories.tsx
Prop type error
We could not detect the component from your story file. Specify meta.component.
   5 |
   6 | // eslint-disable-next-line @drivenets/ds-internal/require-story-meta-annotations -- component prop is not required here.
>  7 | const meta: Meta = {
     | ^
   8 | 	title: 'Components/Scrollbars',
   9 | 	parameters: {
  10 | 		layout: 'padded',

./src/stories/scrollbars/scrollbars.stories.tsx:
import type { Meta, StoryObj } from '@storybook/react-vite';
import { Fragment } from 'react';
import classNames from 'classnames';
import styles from './scrollbars.stories.module.scss';

// eslint-disable-next-line @drivenets/ds-internal/require-story-meta-annotations -- component prop is not required here.
const meta: Meta = {
	title: 'Components/Scrollbars',
	parameters: {
		layout: 'padded',
		docs: {
			description: {
				component: `
All scrollbars are styled automatically by the design system.
You can add a \`.scrollbar-thin\` class to the element in order to make its scrollbar thin.
				`,
			},
		},
	},
};

export default meta;
type Story = StoryObj;

// Helper to generate content
const generateContent = (count: number, direction: 'vertical' | 'horizontal') => {
	const items = Array.from({ length: count }, (_, i) => (
		<div key={i} className={direction === 'horizontal' ? styles.contentItemHorizontal : styles.contentItem}>
			<h3>Item {i + 1}</h3>
			<p>Content for item {i + 1}</p>
		</div>
	));

	return <div className={direction === 'horizontal' ? styles.contentContainer : undefined}>{items}</div>;
};

export const DefaultScrollbar: Story = {
	render: () => (
		<div className={styles.container}>
			<div className={styles.section}>
				<h3>Vertical Scrollbar (Default)</h3>
				<div className={styles.scrollableContainer}>{generateContent(20, 'vertical')}</div>
			</div>

			<div className={styles.sectionWide}>
				<h3>Horizontal Scrollbar (Default)</h3>
				<div className={styles.scrollableContainer}>{generateContent(15, 'horizontal')}</div>
			</div>
		</div>
	),
	parameters: {
		docs: {
			description: {
				story: 'Default scrollbars for both vertical and horizontal overflow.',
			},
		},
	},
};

export const SmallScrollbar: Story = {
	render: () => (
		<div className={styles.container}>
			<div className={styles.section}>
				<h3>Vertical Scrollbar (Thin)</h3>
				<div className={classNames('scrollbar-thin', styles.scrollableContainer)}>
					{generateContent(20, 'vertical')}
				</div>
			</div>

			<div className={styles.sectionWide}>
				<h3>Horizontal Scrollbar (Thin)</h3>
				<div className={classNames('scrollbar-thin', styles.scrollableContainer)}>
					{generateContent(15, 'horizontal')}
				</div>
			</div>
		</div>
	),
	parameters: {
		docs: {
			description: {
				story: 'Small (thin) scrollbars for both vertical and horizontal overflow.',
			},
		},
	},
};

export const CombinedExample: Story = {
	render: () => (
		<div className={styles.sectionExtraWide}>
			<h3>Combined Example - Both X and Y Overflow</h3>
			<div className={styles.scrollableContainerTall}>
				<div className={styles.wideContent}>
					<h2>Wide Content</h2>
					<p>This container has both vertical and horizontal overflow, showing both scrollbars.</p>
					{Array.from({ length: 25 }, (_, i) => (
						<Fragment key={i}>{generateContent(25, 'horizontal')}</Fragment>
					))}
				</div>
			</div>
		</div>
	),
	parameters: {
		docs: {
			description: {
				story: 'Example showing both vertical and horizontal scrollbars on the same container.',
			},
		},
	},
};
Info
No description found. Write a jsdoc comment such as /** Component description */.
Imports
import { Fragment } from "react";
Default Scrollbar story ok
const DefaultScrollbar = () => (
    <div className={styles.container}>
        <div className={styles.section}>
            <h3>Vertical Scrollbar (Default)</h3>
            <div className={styles.scrollableContainer}>{generateContent(20, 'vertical')}</div>
        </div>

        <div className={styles.sectionWide}>
            <h3>Horizontal Scrollbar (Default)</h3>
            <div className={styles.scrollableContainer}>{generateContent(15, 'horizontal')}</div>
        </div>
    </div>
);
Small Scrollbar story ok
const SmallScrollbar = () => (
    <div className={styles.container}>
        <div className={styles.section}>
            <h3>Vertical Scrollbar (Thin)</h3>
            <div className={classNames('scrollbar-thin', styles.scrollableContainer)}>
                {generateContent(20, 'vertical')}
            </div>
        </div>

        <div className={styles.sectionWide}>
            <h3>Horizontal Scrollbar (Thin)</h3>
            <div className={classNames('scrollbar-thin', styles.scrollableContainer)}>
                {generateContent(15, 'horizontal')}
            </div>
        </div>
    </div>
);
Combined Example story ok
const CombinedExample = () => (
    <div className={styles.sectionExtraWide}>
        <h3>Combined Example - Both X and Y Overflow</h3>
        <div className={styles.scrollableContainerTall}>
            <div className={styles.wideContent}>
                <h2>Wide Content</h2>
                <p>This container has both vertical and horizontal overflow, showing both scrollbars.</p>
                {Array.from({ length: 25 }, (_, i) => (
                    <Fragment key={i}>{generateContent(25, 'horizontal')}</Fragment>
                ))}
            </div>
        </div>
    </div>
);

DsCard.Root

components-card · ./src/components/ds-card/ds-card.stories.tsx
Prop type error
File: /opt/build/repo/packages/design-system/src/components/ds-card/index.ts
Error:
No suitable component definition found.
You can debug your component file in this playground: https://react-docgen.dev/playground
Code:
export { DsCard } from './ds-card';
export type {
	DsCardRootProps,
	DsCardHeaderProps,
	DsCardBodyProps,
	DsCardFooterProps,
	DsCardSize,
} from './ds-card.types';
export { cardSizes } from './ds-card.types';


File: /opt/build/repo/packages/design-system/src/components/ds-card/ds-card.tsx
Error:
No suitable component definition found.
You can debug your component file in this playground: https://react-docgen.dev/playground
Code:
import classNames from 'classnames';

import styles from './ds-card.module.scss';
import type { DsCardRootProps, DsCardHeaderProps, DsCardBodyProps, DsCardFooterProps } from './ds-card.types';

/**
 * Card root component - container with optional selectable behavior.
 * Use composition with DsCard.Header, DsCard.Body, DsCard.Footer for full flexibility.
 */
const Root = ({
	ref,
	size = 'medium',
	selectable = false,
	selected = false,
	highlightSelected = false,
	disabled = false,
	className,
	style,
	children,
	onClick,
	onKeyDown,
	...rest
}: DsCardRootProps) => {
	const interactiveProps = selectable
		? {
				role: 'button' as const,
				tabIndex: disabled ? -1 : 0,
				'aria-pressed': selected,
				'aria-disabled': disabled || undefined,
			}
		: {};

	return (
		<div
			ref={ref}
			{...rest}
			{...interactiveProps}
			onClick={disabled ? undefined : onClick}
			onKeyDown={disabled ? undefined : onKeyDown}
			data-size={size}
			data-selectable={selectable || undefined}
			data-selected={selected || undefined}
			data-disabled={disabled || undefined}
			data-highlight={(selected && highlightSelected) || undefined}
			className={classNames(styles.root, className)}
			style={style}
		>
			{children}
		</div>
	);
};

/**
 * Card header - contains title and extra content.
 * Use with DsCard.Title and DsCard.Extra for standard layout,
 * or provide custom children for full flexibility.
 */
const Header = ({ className, children, ref, ...rest }: DsCardHeaderProps) => (
	<div ref={ref} className={classNames(styles.header, className)} {...rest}>
		{children}
	</div>
);

/**
 * Card body - main content area.
 */
const Body = ({ className, children, ref, ...rest }: DsCardBodyProps) => (
	<div ref={ref} className={classNames(styles.body, className)} {...rest}>
		{children}
	</div>
);

/**
 * Card footer - bottom section for actions or additional info.
 */
const Footer = ({ className, children, ref, ...rest }: DsCardFooterProps) => (
	<div ref={ref} className={classNames(styles.footer, className)} {...rest}>
		{children}
	</div>
);

export const DsCard = {
	Root,
	Header,
	Body,
	Footer,
};


File: /opt/build/repo/packages/design-system/src/components/ds-card/ds-card.types.ts
Error:
No suitable component definition found.
You can debug your component file in this playground: https://react-docgen.dev/playground
Code:
import type { CSSProperties, HTMLAttributes, ReactNode, Ref } from 'react';

export const cardSizes = ['small', 'medium', 'large'] as const;
export type DsCardSize = (typeof cardSizes)[number];

/**
 * Props for the Card root component
 */
export interface DsCardRootProps extends HTMLAttributes<HTMLDivElement> {
	/**
	 * Forwarded ref for the card element
	 */
	ref?: Ref<HTMLDivElement>;

	/**
	 * Card size variant
	 * @default 'medium'
	 */
	size?: DsCardSize;

	/**
	 * Enable selectable/interactive mode. When false, the card is a static display container.
	 * @default false
	 */
	selectable?: boolean;

	/**
	 * Selected state (controlled). Required when selectable is true.
	 * @default false
	 */
	selected?: boolean;

	/**
	 * When true, shows a highlighted background when selected
	 * @default false
	 */
	highlightSelected?: boolean;

	/**
	 * Whether the card is disabled. Prevents selection and click interactions and
	 * visually dims the card.
	 * @default false
	 */
	disabled?: boolean;
}

/**
 * Common props for card slot components
 */
export interface DsCardSlotProps extends HTMLAttributes<HTMLDivElement> {
	/**
	 * Additional CSS class names
	 */
	className?: string;

	/**
	 * Additional styles to apply
	 */
	style?: CSSProperties;

	/**
	 * Slot content
	 */
	children?: ReactNode;

	/**
	 * Forwarded ref
	 */
	ref?: Ref<HTMLDivElement>;
}

export type DsCardHeaderProps = DsCardSlotProps;
export type DsCardBodyProps = DsCardSlotProps;
export type DsCardFooterProps = DsCardSlotProps;


File: /opt/build/repo/packages/design-system/src/components/ds-card/ds-card.types.ts
Error:
No suitable component definition found.
You can debug your component file in this playground: https://react-docgen.dev/playground
Code:
import type { CSSProperties, HTMLAttributes, ReactNode, Ref } from 'react';

export const cardSizes = ['small', 'medium', 'large'] as const;
export type DsCardSize = (typeof cardSizes)[number];

/**
 * Props for the Card root component
 */
export interface DsCardRootProps extends HTMLAttributes<HTMLDivElement> {
	/**
	 * Forwarded ref for the card element
	 */
	ref?: Ref<HTMLDivElement>;

	/**
	 * Card size variant
	 * @default 'medium'
	 */
	size?: DsCardSize;

	/**
	 * Enable selectable/interactive mode. When false, the card is a static display container.
	 * @default false
	 */
	selectable?: boolean;

	/**
	 * Selected state (controlled). Required when selectable is true.
	 * @default false
	 */
	selected?: boolean;

	/**
	 * When true, shows a highlighted background when selected
	 * @default false
	 */
	highlightSelected?: boolean;

	/**
	 * Whether the card is disabled. Prevents selection and click interactions and
	 * visually dims the card.
	 * @default false
	 */
	disabled?: boolean;
}

/**
 * Common props for card slot components
 */
export interface DsCardSlotProps extends HTMLAttributes<HTMLDivElement> {
	/**
	 * Additional CSS class names
	 */
	className?: string;

	/**
	 * Additional styles to apply
	 */
	style?: CSSProperties;

	/**
	 * Slot content
	 */
	children?: ReactNode;

	/**
	 * Forwarded ref
	 */
	ref?: Ref<HTMLDivElement>;
}

export type DsCardHeaderProps = DsCardSlotProps;
export type DsCardBodyProps = DsCardSlotProps;
export type DsCardFooterProps = DsCardSlotProps;
Info
No description found. Write a jsdoc comment such as /** Component description */.
Imports
import { DsCard, DsIcon, DsStatusBadge, DsTypography } from "@drivenets/design-system";
Default story ok
const Default = () => <DsCard.Root size="medium">
    <DsCard.Header>Card Title</DsCard.Header>
    <DsCard.Body>Card content goes here</DsCard.Body>
</DsCard.Root>;
Sizes story ok
const Sizes = () => (
    <div className={styles.container}>
        <DsCard.Root size="small">
            <DsCard.Header>Small Card</DsCard.Header>
            <DsCard.Body>Small content</DsCard.Body>
        </DsCard.Root>

        <DsCard.Root size="medium">
            <DsCard.Header>Medium Card</DsCard.Header>
            <DsCard.Body>Medium content</DsCard.Body>
        </DsCard.Root>

        <DsCard.Root size="large">
            <DsCard.Header>Large Card</DsCard.Header>
            <DsCard.Body>Large content</DsCard.Body>
        </DsCard.Root>
    </div>
);
With Header And Footer story ok
const WithHeaderAndFooter = () => (
    <DsCard.Root size="large">
        <DsCard.Header className={styles.headerRow}>
            <DsTypography variant="heading3">Card Title</DsTypography>
            <DsStatusBadge icon="check_circle" status="active" ghost />
        </DsCard.Header>
        <DsCard.Body className={styles.statsBlock}>
            <DsTypography variant="body-md-bold">12 of 12 Devices</DsTypography>
            <DsTypography variant="body-sm-reg">Success 10 | Failed 1 | Skipped 1</DsTypography>
        </DsCard.Body>
        <DsCard.Footer className={styles.footer}>
            <DsTypography variant="body-sm-reg">Last updated: 2 min ago</DsTypography>
        </DsCard.Footer>
    </DsCard.Root>
);
Step Card story ok
const StepCard = () => (
    <DsCard.Root size="large">
        <DsCard.Header className={styles.headerRow}>
            <DsTypography variant="heading3">Canary</DsTypography>
            <DsStatusBadge icon="check_circle" status="active" label="Complete" />
        </DsCard.Header>
        <DsCard.Body className={styles.statsBlock}>
            <DsTypography variant="body-md-bold">12 of 12 Devices</DsTypography>
            <DsTypography variant="body-sm-reg" className={styles.textSecondary}>
                Success 10 | Failed 1 | Skipped 1
            </DsTypography>
        </DsCard.Body>
        <DsCard.Body className={styles.dataList}>
            <DsTypography variant="body-sm-reg">Config Push</DsTypography>
            <DsTypography variant="body-sm-reg" className={styles.textSuccess}>
                Complete
            </DsTypography>
            <DsTypography variant="body-sm-reg">Dwell Time (60 min.)</DsTypography>
            <DsTypography variant="body-sm-reg" className={styles.textSuccess}>
                Complete
            </DsTypography>
            <DsTypography variant="body-sm-reg">Failed</DsTypography>
            <DsTypography variant="body-sm-reg" className={styles.textSecondary}>
                1 (8%)
            </DsTypography>
            <DsTypography variant="body-sm-reg">Failure threshold</DsTypography>
            <DsTypography variant="body-sm-reg" className={styles.textSecondary}>
                5 or 10%
            </DsTypography>
            <DsTypography variant="body-sm-reg">Threshold state</DsTypography>
            <DsTypography variant="body-sm-reg" className={styles.textSecondary}>
                Normal
            </DsTypography>
        </DsCard.Body>
    </DsCard.Root>
);
Selectable story ok
const Selectable = () => <DsCard.Root selectable selected={false} onClick={fn()}>
    <DsCard.Header>Selectable Card</DsCard.Header>
    <DsCard.Body>Click to select this card</DsCard.Body>
</DsCard.Root>;
Highlight Selected story ok
const HighlightSelected = () => <DsCard.Root selectable selected highlightSelected>
    <DsCard.Header>Highlighted Card</DsCard.Header>
    <DsCard.Body>This card has a highlighted background when selected</DsCard.Body>
</DsCard.Root>;
Selectable Controlled story ok
const SelectableControlled = function Render() {
    const [selected, setSelected] = useState(false);

    return (
        <DsCard.Root selectable selected={selected} onClick={() => setSelected(!selected)}>
            <DsCard.Header>Controlled Card</DsCard.Header>
            <DsCard.Body>{selected ? 'Selected! Click to deselect.' : 'Click to select.'}</DsCard.Body>
        </DsCard.Root>
    );
};
Disabled story ok
const Disabled = () => <DsCard.Root size="large" selectable disabled onClick={fn()}>
    <DsCard.Header className={styles.headerRow}>
        <DsTypography variant="heading3">Canary</DsTypography>
        <DsStatusBadge icon="check_circle" status="active" label="Complete" />
    </DsCard.Header>
    <DsCard.Body className={styles.statsBlock}>
        <DsTypography variant="body-md-bold">12 of 12 Devices</DsTypography>
        <DsTypography variant="body-sm-reg" className={styles.textSecondary}>Success 10 | Failed 1 | Skipped 1
                            </DsTypography>
    </DsCard.Body>
    <DsCard.Body className={styles.dataList}>
        <DsTypography variant="body-sm-reg">Config Push</DsTypography>
        <DsTypography variant="body-sm-reg" className={styles.textSuccess}>Complete
                            </DsTypography>
        <DsTypography variant="body-sm-reg">Dwell Time (60 min.)</DsTypography>
        <DsTypography variant="body-sm-reg" className={styles.textSuccess}>Complete
                            </DsTypography>
        <DsTypography variant="body-sm-reg">Failed</DsTypography>
        <DsTypography variant="body-sm-reg" className={styles.textSecondary}>1 (8%)
                            </DsTypography>
        <DsTypography variant="body-sm-reg">Failure threshold</DsTypography>
        <DsTypography variant="body-sm-reg" className={styles.textSecondary}>5 or 10%
                            </DsTypography>
        <DsTypography variant="body-sm-reg">Threshold state</DsTypography>
        <DsTypography variant="body-sm-reg" className={styles.textSecondary}>Normal
                            </DsTypography>
    </DsCard.Body>
</DsCard.Root>;
Collapsible story ok
const Collapsible = function Render() {
    const [expanded, setExpanded] = useState(true);

    return (
        <DsCard.Root size="large" className={styles.collapseRoot}>
            <DsCard.Header className={styles.collapseHeader}>
                <button
                    type="button"
                    className={styles.collapsibleButton}
                    onClick={() => setExpanded(!expanded)}
                    aria-expanded={expanded}
                >
                    <DsIcon
                        icon="expand_more"
                        style={{
                            transform: expanded ? 'rotate(180deg)' : 'rotate(0deg)',
                            transition: 'transform 150ms ease-in-out',
                        }}
                    />
                </button>
                <DsTypography variant="heading3">Collapsible Card</DsTypography>
            </DsCard.Header>
            <DsCard.Body className={styles.collapsibleContent} data-collapsed={!expanded}>
                <div className={styles.collapsibleContentInner}>
                    <DsTypography variant="body-md-reg">
                        This content can be collapsed by clicking the header. The height animates smoothly using CSS
                        Grid.
                    </DsTypography>
                </div>
            </DsCard.Body>
        </DsCard.Root>
    );
};

DsSegmentGroup.Root

components-segmentgroup · ./src/components/ds-segment-group/ds-segment-group.stories.tsx
Prop type error
File: /opt/build/repo/packages/design-system/src/components/ds-segment-group/ds-segment-group.tsx
Error:
No suitable component definition found.
You can debug your component file in this playground: https://react-docgen.dev/playground
Code:
import type React from 'react';
import { SegmentGroup } from '@ark-ui/react/segment-group';
import classNames from 'classnames';
import styles from './ds-segment-group.module.scss';
import { DsTypography } from '../ds-typography';
import {
	type DsSegmentGroupItemProps,
	type DsSegmentGroupItemTextProps,
	type DsSegmentGroupRootProps,
} from './ds-segment-group.types';

/**
 * Root component - provides segment group context
 */
const Root: React.FC<DsSegmentGroupRootProps> = ({
	className,
	children,
	onValueChange,
	size = 'default',
	...props
}) => (
	<SegmentGroup.Root
		className={classNames(styles.segmentGroupRoot, size === 'small' && styles.small, className)}
		onValueChange={(details) => onValueChange?.(details.value)}
		{...props}
	>
		<SegmentGroup.Indicator className={styles.segmentIndicator} />
		{children}
	</SegmentGroup.Root>
);

/**
 * Item component - renders a single segment button
 */
const Item: React.FC<DsSegmentGroupItemProps> = ({ value, label, className, style, children, ...props }) => {
	return (
		<SegmentGroup.Item
			value={value}
			className={classNames(styles.segmentItem, className)}
			style={style}
			{...props}
		>
			{label ? <ItemText>{label}</ItemText> : children}
			<SegmentGroup.ItemControl />
			<SegmentGroup.ItemHiddenInput />
		</SegmentGroup.Item>
	);
};

/**
 * ItemText component - renders text content within an item
 */
const ItemText: React.FC<DsSegmentGroupItemTextProps> = ({ className, children, ...props }) => (
	<SegmentGroup.ItemText className={classNames(styles.segmentItemText, className)} {...props} asChild>
		<DsTypography variant="body-sm-reg">{children}</DsTypography>
	</SegmentGroup.ItemText>
);

/**
 * Design system SegmentGroup component
 *
 * @example
 * <DsSegmentGroup.Root value={value} onValueChange={setValue} size="default">
 *   <DsSegmentGroup.Indicator />
 *   <DsSegmentGroup.Item value="option1" label="Option 1" />
 *   <DsSegmentGroup.Item value="option2">
 *     <DsIcon icon="settings" />
 *     <DsSegmentGroup.ItemText>Option 2</DsSegmentGroup.ItemText>
 *   </DsSegmentGroup.Item>
 * </DsSegmentGroup.Root>
 */
export const DsSegmentGroup = {
	Root,
	Item,
	ItemText,
};
Info
No description found. Write a jsdoc comment such as /** Component description */.
Imports
import { DsIcon, DsSegmentGroup } from "@drivenets/design-system";
Default story ok
const Default = function Render() {
    const [value, setValue] = useState<string | null>('react');

    return (
        <DsSegmentGroup.Root value={value} onValueChange={setValue}>
            <DsSegmentGroup.Item value="react" label="React" />
            <DsSegmentGroup.Item value="vue" label="Vue" />
            <DsSegmentGroup.Item value="angular" label="Angular" />
            <DsSegmentGroup.Item value="svelte" label="Svelte" />
        </DsSegmentGroup.Root>
    );
};
Small story ok
const Small = function Render() {
    const [value, setValue] = useState<string | null>('list');

    return (
        <DsSegmentGroup.Root value={value} onValueChange={setValue} size="small">
            <DsSegmentGroup.Item value="list" label="List" />
            <DsSegmentGroup.Item value="grid" label="Grid" />
            <DsSegmentGroup.Item value="table" label="Table" />
        </DsSegmentGroup.Root>
    );
};
With Icons story ok
const WithIcons = function Render() {
    const [value, setValue] = useState<string | null>('day');

    return (
        <DsSegmentGroup.Root value={value} onValueChange={setValue}>
            <DsSegmentGroup.Item value="day">
                <DsIcon icon="wb_sunny" size="tiny" />
                <DsSegmentGroup.ItemText>Day</DsSegmentGroup.ItemText>
            </DsSegmentGroup.Item>
            <DsSegmentGroup.Item value="week">
                <DsIcon icon="date_range" size="tiny" />
                <DsSegmentGroup.ItemText>Week</DsSegmentGroup.ItemText>
            </DsSegmentGroup.Item>
            <DsSegmentGroup.Item value="month">
                <DsIcon icon="calendar_month" size="tiny" />
                <DsSegmentGroup.ItemText>Month</DsSegmentGroup.ItemText>
            </DsSegmentGroup.Item>
        </DsSegmentGroup.Root>
    );
};
Icon Only story ok
const IconOnly = function Render() {
    const [value, setValue] = useState<string | null>('list');

    return (
        <DsSegmentGroup.Root value={value} onValueChange={setValue} size="small">
            <DsSegmentGroup.Item value="list">
                <DsIcon icon="view_list" size="tiny" />
            </DsSegmentGroup.Item>
            <DsSegmentGroup.Item value="grid">
                <DsIcon icon="grid_view" size="tiny" />
            </DsSegmentGroup.Item>
            <DsSegmentGroup.Item value="kanban">
                <DsIcon icon="view_kanban" size="tiny" />
            </DsSegmentGroup.Item>
            <DsSegmentGroup.Item value="timeline">
                <DsIcon icon="timeline" size="tiny" />
            </DsSegmentGroup.Item>
        </DsSegmentGroup.Root>
    );
};
With Disabled Items story ok
const WithDisabledItems = function Render() {
    const [value, setValue] = useState<string | null>('option2');

    return (
        <DsSegmentGroup.Root value={value} onValueChange={setValue}>
            <DsSegmentGroup.Item value="option1" label="Disabled" disabled />
            <DsSegmentGroup.Item value="option2" label="Available" />
            <DsSegmentGroup.Item value="option3" label="Also Available" />
        </DsSegmentGroup.Root>
    );
};
Uncontrolled story ok
const Uncontrolled = function Render() {
    return (
        <DsSegmentGroup.Root defaultValue="option2">
            <DsSegmentGroup.Item value="option1" label="Option 1" />
            <DsSegmentGroup.Item value="option2" label="Option 2" />
            <DsSegmentGroup.Item value="option3" label="Option 3" />
        </DsSegmentGroup.Root>
    );
};
Controlled story ok
const Controlled = function Render() {
    const [value, setValue] = useState<string | null>('option2');

    return (
        <div className={styles.controlledContainer}>
            <DsSegmentGroup.Root value={value} onValueChange={setValue}>
                <DsSegmentGroup.Item value="option1" label="Option 1" />
                <DsSegmentGroup.Item value="option2" label="Option 2" />
                <DsSegmentGroup.Item value="option3" label="Option 3" />
            </DsSegmentGroup.Root>
            <div className={styles.selectedValue}>Selected: {value || 'None'}</div>
        </div>
    );
};
Two Options story ok
const TwoOptions = function Render() {
    const [value, setValue] = useState<string | null>('on');

    return (
        <DsSegmentGroup.Root value={value} onValueChange={setValue}>
            <DsSegmentGroup.Item value="on" label="On" />
            <DsSegmentGroup.Item value="off" label="Off" />
        </DsSegmentGroup.Root>
    );
};

DsTabs.Root

components-tabs · ./src/components/ds-tabs/ds-tabs.stories.tsx
Prop type error
File: /opt/build/repo/packages/design-system/src/components/ds-tabs/ds-tabs.tsx
Error:
No suitable component definition found.
You can debug your component file in this playground: https://react-docgen.dev/playground
Code:
import { Tabs } from '@ark-ui/react/tabs';
import classNames from 'classnames';
import { DsTabsList } from './components/ds-tabs-list';
import { DsTabsTab } from './components/ds-tabs-tab';
import { DsTabsContent } from './components/ds-tabs-content';
import type { DsTabsProps } from './ds-tabs.types';
import styles from './ds-tabs.module.scss';

const DsTabsRoot = ({
	value,
	defaultValue,
	onValueChange,
	orientation = 'horizontal',
	size = 'medium',
	className,
	style,
	children,
}: DsTabsProps) => {
	const handleValueChange = (details: { value: string | null }) => {
		onValueChange?.(details.value);
	};

	return (
		<Tabs.Root
			orientation={orientation}
			value={value}
			defaultValue={defaultValue}
			onValueChange={handleValueChange}
			activationMode="manual"
			lazyMount
			unmountOnExit
			className={classNames(styles.root, className)}
			style={style}
			data-size={size}
		>
			{children}
		</Tabs.Root>
	);
};

export const DsTabs = {
	Root: DsTabsRoot,
	List: DsTabsList,
	Tab: DsTabsTab,
	Content: DsTabsContent,
};
Info
No description found. Write a jsdoc comment such as /** Component description */.
Imports
import { DsTabs } from "@drivenets/design-system";
Default story ok
const Default = () => {
    const [selected, setSelected] = useState('overview');

    return (
        <div className={styles.container}>
            <DsTabs.Root
                orientation="horizontal"
                size="medium"
                value={selected}
                onValueChange={(val: string | null) => setSelected(val ?? 'overview')}>
                <DsTabs.List>
                    <DsTabs.Tab value="overview" label="Overview" icon="dashboard" />
                    <DsTabs.Tab value="analytics" label="Analytics" icon="analytics" badge={12} />
                    <DsTabs.Tab value="reports" label="Reports" icon="description" badge={5} />
                    <DsTabs.Tab value="settings" label="Settings" icon="settings" />
                </DsTabs.List>
                <DsTabs.Content value="overview">
                    <div className={styles.content}>
                        <h3>Overview</h3>
                        <p>View your dashboard overview and key metrics.</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="analytics">
                    <div className={styles.content}>
                        <h3>Analytics</h3>
                        <p>Detailed analytics and performance data (12 new insights).</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="reports">
                    <div className={styles.content}>
                        <h3>Reports</h3>
                        <p>Access and manage your reports (5 pending).</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="settings">
                    <div className={styles.content}>
                        <h3>Settings</h3>
                        <p>Configure your application settings.</p>
                    </div>
                </DsTabs.Content>
            </DsTabs.Root>
        </div>
    );
};
Horizontal Small story ok
const HorizontalSmall = () => {
    const [selected, setSelected] = useState('dashboard');

    return (
        <div className={styles.container}>
            <DsTabs.Root
                size="small"
                orientation="horizontal"
                value={selected}
                onValueChange={(val: string | null) => setSelected(val ?? 'dashboard')}>
                <DsTabs.List>
                    <DsTabs.Tab value="dashboard" label="Dashboard" icon="dashboard" />
                    <DsTabs.Tab value="analytics" label="Analytics" icon="bar_chart" badge={12} />
                    <DsTabs.Tab value="reports" label="Reports" icon="description" badge={5} />
                    <DsTabs.Tab value="settings" label="Settings" icon="settings" />
                </DsTabs.List>
                <DsTabs.Content value="dashboard">
                    <div className={styles.content}>
                        <h3>Dashboard</h3>
                        <p>Welcome to your dashboard overview.</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="analytics">
                    <div className={styles.content}>
                        <h3>Analytics</h3>
                        <p>View analytics and performance data. 12 new insights available.</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="reports">
                    <div className={styles.content}>
                        <h3>Reports</h3>
                        <p>View and generate reports. 5 new reports available.</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="settings">
                    <div className={styles.content}>
                        <h3>Settings</h3>
                        <p>Configure your application settings.</p>
                    </div>
                </DsTabs.Content>
            </DsTabs.Root>
        </div>
    );
};
With Menu Actions story ok
const WithMenuActions = () => {
    const [selected, setSelected] = useState('tab1');

    const menuActions: DsTabsMenuActionItem[] = [
        { value: 'edit', label: 'Edit' },
        { value: 'duplicate', label: 'Duplicate' },
        { value: 'share', label: 'Share' },
        { value: 'delete', label: 'Delete' },
    ];

    return (
        <div className={styles.container}>
            <DsTabs.Root
                orientation="horizontal"
                size="medium"
                value={selected}
                onValueChange={(val: string | null) => setSelected(val ?? 'tab1')}>
                <DsTabs.List>
                    <DsTabs.Tab
                        value="tab1"
                        label="Projects"
                        icon="folder"
                        badge={5}
                        menuActionItems={menuActions}
                        onMenuActionSelect={handleMenuActionMock} />
                    <DsTabs.Tab
                        value="tab2"
                        label="Documents"
                        icon="description"
                        badge={12}
                        menuActionItems={menuActions}
                        onMenuActionSelect={handleMenuActionMock} />
                    <DsTabs.Tab
                        value="tab3"
                        label="Settings"
                        icon="settings"
                        menuActionItems={menuActions}
                        onMenuActionSelect={handleMenuActionMock} />
                </DsTabs.List>
                <DsTabs.Content value="tab1">
                    <div className={styles.content}>
                        <h3>Projects</h3>
                        <p>Click the dropdown icon on tabs to see menu actions</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="tab2">
                    <div className={styles.content}>
                        <h3>Documents</h3>
                        <p>12 documents available</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="tab3">
                    <div className={styles.content}>
                        <h3>Settings</h3>
                        <p>Configure your preferences</p>
                    </div>
                </DsTabs.Content>
            </DsTabs.Root>
        </div>
    );
};
Vertical story ok
const Vertical = () => {
    const [selected, setSelected] = useState('profile');

    return (
        <div className={styles.verticalContainer}>
            <DsTabs.Root
                orientation="vertical"
                size="medium"
                value={selected}
                onValueChange={(val: string | null) => setSelected(val ?? 'profile')}>
                <DsTabs.List>
                    <DsTabs.Tab value="profile" label="Profile" icon="person" />
                    <DsTabs.Tab value="security" label="Security" icon="lock" badge={3} />
                    <DsTabs.Tab
                        value="notifications"
                        label="Notifications"
                        icon="notifications"
                        badge={15} />
                    <DsTabs.Tab value="billing" label="Billing" icon="credit_card" />
                    <DsTabs.Tab value="team" label="Team" icon="group" />
                </DsTabs.List>
                <DsTabs.Content value="profile">
                    <div className={styles.content}>
                        <h3>Profile Settings</h3>
                        <p>Manage your profile information and preferences.</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="security">
                    <div className={styles.content}>
                        <h3>Security</h3>
                        <p>Configure security settings and two-factor authentication (3 recommendations).</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="notifications">
                    <div className={styles.content}>
                        <h3>Notifications</h3>
                        <p>Manage notification preferences and channels (15 unread).</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="billing">
                    <div className={styles.content}>
                        <h3>Billing</h3>
                        <p>View invoices and manage payment methods.</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="team">
                    <div className={styles.content}>
                        <h3>Team Management</h3>
                        <p>Invite team members and manage permissions.</p>
                    </div>
                </DsTabs.Content>
            </DsTabs.Root>
        </div>
    );
};
Vertical Small story ok
const VerticalSmall = () => {
    const [selected, setSelected] = useState('general');

    return (
        <div className={styles.verticalContainer}>
            <DsTabs.Root
                orientation="vertical"
                size="small"
                value={selected}
                onValueChange={(val: string | null) => setSelected(val ?? 'general')}>
                <DsTabs.List>
                    <DsTabs.Tab value="general" label="General" icon="settings" />
                    <DsTabs.Tab value="account" label="Account" icon="person" />
                    <DsTabs.Tab value="privacy" label="Privacy" icon="lock" badge={2} />
                    <DsTabs.Tab value="appearance" label="Appearance" icon="palette" />
                    <DsTabs.Tab value="advanced" label="Advanced" icon="tune" />
                </DsTabs.List>
                <DsTabs.Content value="general">
                    <div className={styles.content}>
                        <h3>General Settings</h3>
                        <p>Configure general application settings and preferences.</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="account">
                    <div className={styles.content}>
                        <h3>Account</h3>
                        <p>Manage your account details and information.</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="privacy">
                    <div className={styles.content}>
                        <h3>Privacy</h3>
                        <p>Control your privacy settings and data sharing (2 recommendations).</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="appearance">
                    <div className={styles.content}>
                        <h3>Appearance</h3>
                        <p>Customize the look and feel of the application.</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="advanced">
                    <div className={styles.content}>
                        <h3>Advanced</h3>
                        <p>Advanced configuration options for power users.</p>
                    </div>
                </DsTabs.Content>
            </DsTabs.Root>
        </div>
    );
};
Vertical With Menu Actions story ok
const VerticalWithMenuActions = () => {
    const [selected, setSelected] = useState('profile');

    const menuActions: DsTabsMenuActionItem[] = [
        { value: 'edit', label: 'Edit' },
        { value: 'duplicate', label: 'Duplicate' },
        { value: 'archive', label: 'Archive' },
        { value: 'delete', label: 'Delete' },
    ];

    const handleMenuAction = (action: string) => {
        console.log('Menu action selected:', action);
    };

    return (
        <div className={styles.verticalContainer}>
            <DsTabs.Root
                orientation="vertical"
                size="medium"
                value={selected}
                onValueChange={(val: string | null) => setSelected(val ?? 'profile')}>
                <DsTabs.List>
                    <DsTabs.Tab
                        value="profile"
                        label="Profile"
                        icon="person"
                        menuActionItems={menuActions}
                        onMenuActionSelect={handleMenuAction} />
                    <DsTabs.Tab
                        value="security"
                        label="Security"
                        icon="lock"
                        badge={3}
                        menuActionItems={menuActions}
                        onMenuActionSelect={handleMenuAction} />
                    <DsTabs.Tab
                        value="notifications"
                        label="Notifications"
                        icon="notifications"
                        badge={15}
                        menuActionItems={menuActions}
                        onMenuActionSelect={handleMenuAction} />
                    <DsTabs.Tab
                        value="billing"
                        label="Billing"
                        icon="credit_card"
                        menuActionItems={menuActions}
                        onMenuActionSelect={handleMenuAction} />
                    <DsTabs.Tab
                        value="team"
                        label="Team"
                        icon="group"
                        menuActionItems={menuActions}
                        onMenuActionSelect={handleMenuAction} />
                </DsTabs.List>
                <DsTabs.Content value="profile">
                    <div className={styles.content}>
                        <h3>Profile Settings</h3>
                        <p>Manage your profile information and preferences. Click the dropdown icon on tabs to see menu
                                                            actions.
                                                        </p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="security">
                    <div className={styles.content}>
                        <h3>Security</h3>
                        <p>Configure security settings and two-factor authentication (3 recommendations).</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="notifications">
                    <div className={styles.content}>
                        <h3>Notifications</h3>
                        <p>Manage notification preferences and channels (15 unread).</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="billing">
                    <div className={styles.content}>
                        <h3>Billing</h3>
                        <p>View invoices and manage payment methods.</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="team">
                    <div className={styles.content}>
                        <h3>Team Management</h3>
                        <p>Invite team members and manage permissions.</p>
                    </div>
                </DsTabs.Content>
            </DsTabs.Root>
        </div>
    );
};
With Disabled story ok
const WithDisabled = () => {
    const [selected, setSelected] = useState('active1');

    return (
        <div className={styles.container}>
            <DsTabs.Root
                value={selected}
                onValueChange={(val: string | null) => setSelected(val ?? 'active1')}>
                <DsTabs.List>
                    <DsTabs.Tab value="active1" label="Active" icon="check_circle" />
                    <DsTabs.Tab value="disabled1" label="Disabled" icon="block" disabled />
                    <DsTabs.Tab value="active2" label="Active" icon="check_circle" badge={5} />
                    <DsTabs.Tab value="disabled2" label="Disabled" icon="block" disabled />
                </DsTabs.List>
                <DsTabs.Content value="active1">
                    <div className={styles.content}>Active tab 1 content</div>
                </DsTabs.Content>
                <DsTabs.Content value="disabled1">
                    <div className={styles.content}>This content should not be accessible</div>
                </DsTabs.Content>
                <DsTabs.Content value="active2">
                    <div className={styles.content}>Active tab 2 content</div>
                </DsTabs.Content>
                <DsTabs.Content value="disabled2">
                    <div className={styles.content}>This content should not be accessible</div>
                </DsTabs.Content>
            </DsTabs.Root>
        </div>
    );
};
Text Only story ok
const TextOnly = () => {
    const [selected, setSelected] = useState('home');

    return (
        <div className={styles.container}>
            <DsTabs.Root
                value={selected}
                onValueChange={(val: string | null) => setSelected(val ?? 'home')}>
                <DsTabs.List>
                    <DsTabs.Tab value="home" label="Home" />
                    <DsTabs.Tab value="products" label="Products" badge={23} />
                    <DsTabs.Tab value="services" label="Services" />
                    <DsTabs.Tab value="contact" label="Contact" />
                </DsTabs.List>
                <DsTabs.Content value="home">
                    <div className={styles.content}>
                        <h3>Home</h3>
                        <p>Welcome to the home page.</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="products">
                    <div className={styles.content}>
                        <h3>Products</h3>
                        <p>Browse our product catalog (23 items).</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="services">
                    <div className={styles.content}>
                        <h3>Services</h3>
                        <p>Learn about our services.</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="contact">
                    <div className={styles.content}>
                        <h3>Contact Us</h3>
                        <p>Get in touch with our team.</p>
                    </div>
                </DsTabs.Content>
            </DsTabs.Root>
        </div>
    );
};
With Tooltips story ok
const WithTooltips = () => {
    const [selected, setSelected] = useState('dashboard');

    return (
        <div className={styles.container}>
            <DsTabs.Root
                value={selected}
                onValueChange={(val: string | null) => setSelected(val ?? 'dashboard')}>
                <DsTabs.List>
                    <DsTabs.Tab
                        value="dashboard"
                        label="Dashboard"
                        icon="dashboard"
                        tooltip="View your dashboard overview" />
                    <DsTabs.Tab
                        value="analytics"
                        label="Analytics"
                        icon="analytics"
                        badge={12}
                        tooltip="Analytics and insights" />
                    <DsTabs.Tab
                        value="reports"
                        label="Reports"
                        icon="description"
                        badge={5}
                        tooltip="Generate and view reports" />
                    <DsTabs.Tab
                        value="settings"
                        label="Settings"
                        icon="settings"
                        tooltip="Configure application settings" />
                </DsTabs.List>
                <DsTabs.Content value="dashboard">
                    <div className={styles.content}>
                        <h3>Dashboard</h3>
                        <p>View your dashboard overview (hover tabs to see tooltips)</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="analytics">
                    <div className={styles.content}>
                        <h3>Analytics</h3>
                        <p>12 new insights available</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="reports">
                    <div className={styles.content}>
                        <h3>Reports</h3>
                        <p>5 reports pending review</p>
                    </div>
                </DsTabs.Content>
                <DsTabs.Content value="settings">
                    <div className={styles.content}>
                        <h3>Settings</h3>
                        <p>Configure your application</p>
                    </div>
                </DsTabs.Content>
            </DsTabs.Root>
        </div>
    );
};

DsTree.Root

components-tree · ./src/components/ds-tree/ds-tree.stories.tsx
Prop type error
File: /opt/build/repo/packages/design-system/src/components/ds-tree/ds-tree.tsx
Error:
No suitable component definition found.
You can debug your component file in this playground: https://react-docgen.dev/playground
Code:
import { TreeView } from '@ark-ui/react/tree-view';
import classNames from 'classnames';

import { DsIcon } from '../ds-icon';
import styles from './ds-tree.module.scss';
import type {
	DsTreeBranchContentProps,
	DsTreeBranchControlProps,
	DsTreeBranchIndentGuideProps,
	DsTreeBranchIndicatorProps,
	DsTreeBranchProps,
	DsTreeBranchTextProps,
	DsTreeItemActionProps,
	DsTreeItemIndicatorProps,
	DsTreeItemProps,
	DsTreeItemTextProps,
	DsTreeNode,
	DsTreeNodeCheckboxProps,
	DsTreeRootProps,
	DsTreeTreeProps,
} from './ds-tree.types';

const DsTreeRoot = <T extends DsTreeNode = DsTreeNode>({
	collection,
	size = 'medium',
	ref,
	className,
	style,
	children,
	selectedValue,
	defaultSelectedValue,
	onSelectionChange,
	selectionMode = 'single',
	expandedValue,
	defaultExpandedValue,
	onExpandedChange,
	expandOnClick = true,
	checkedValue,
	defaultCheckedValue,
	onCheckedChange,
	typeahead = true,
	lazyMount = true,
	unmountOnExit,
}: DsTreeRootProps<T>) => (
	<TreeView.Root
		ref={ref}
		collection={collection}
		selectedValue={selectedValue}
		defaultSelectedValue={defaultSelectedValue}
		onSelectionChange={onSelectionChange}
		selectionMode={selectionMode}
		expandedValue={expandedValue}
		defaultExpandedValue={defaultExpandedValue}
		onExpandedChange={onExpandedChange}
		expandOnClick={expandOnClick}
		checkedValue={checkedValue}
		defaultCheckedValue={defaultCheckedValue}
		onCheckedChange={onCheckedChange}
		typeahead={typeahead}
		lazyMount={lazyMount}
		unmountOnExit={unmountOnExit}
		className={classNames(styles.root, className)}
		style={style}
		data-size={size}
	>
		{children}
	</TreeView.Root>
);

const DsTreeTree = ({ className, style, children }: DsTreeTreeProps) => (
	<TreeView.Tree className={classNames(styles.tree, className)} style={style}>
		{children}
	</TreeView.Tree>
);

const DsTreeBranch = ({ className, style, children }: DsTreeBranchProps) => (
	<TreeView.Branch className={classNames(styles.branch, className)} style={style}>
		{children}
	</TreeView.Branch>
);

const DsTreeBranchControl = ({ className, style, children }: DsTreeBranchControlProps) => (
	<TreeView.BranchControl className={classNames(styles.branchControl, className)} style={style}>
		{children}
	</TreeView.BranchControl>
);

const DsTreeBranchIndicator = ({ className, style, children }: DsTreeBranchIndicatorProps) => (
	<TreeView.BranchIndicator className={classNames(styles.branchIndicator, className)} style={style}>
		{children ?? <DsIcon icon="keyboard_arrow_right" size="tiny" />}
	</TreeView.BranchIndicator>
);

const DsTreeBranchText = ({ className, style, children }: DsTreeBranchTextProps) => (
	<TreeView.BranchText className={classNames(styles.branchText, className)} style={style}>
		{children}
	</TreeView.BranchText>
);

const DsTreeBranchContent = ({ className, style, children }: DsTreeBranchContentProps) => (
	<TreeView.BranchContent className={classNames(styles.branchContent, className)} style={style}>
		{children}
	</TreeView.BranchContent>
);

const DsTreeBranchIndentGuide = ({ className, style }: DsTreeBranchIndentGuideProps) => (
	<TreeView.BranchIndentGuide className={classNames(styles.branchIndentGuide, className)} style={style} />
);

const DsTreeItem = ({ className, style, children, onClick }: DsTreeItemProps) => (
	<TreeView.Item className={classNames(styles.item, className)} style={style} onClick={onClick}>
		{children}
	</TreeView.Item>
);

const DsTreeItemText = ({ className, style, children }: DsTreeItemTextProps) => (
	<TreeView.ItemText className={classNames(styles.itemText, className)} style={style}>
		{children}
	</TreeView.ItemText>
);

const DsTreeItemIndicator = ({ className, style }: DsTreeItemIndicatorProps) => (
	<span className={classNames(styles.itemIndicator, className)} style={style} aria-hidden="true">
		<span className={styles.itemDot} />
	</span>
);

const DsTreeNodeCheckbox = ({ className, style }: DsTreeNodeCheckboxProps) => (
	<TreeView.NodeCheckbox className={classNames(styles.nodeCheckbox, className)} style={style}>
		<div className={styles.nodeCheckboxBox}>
			<TreeView.NodeCheckboxIndicator
				className={styles.nodeCheckboxIndicator}
				indeterminate={<DsIcon icon="check_indeterminate_small" size="tiny" variant="outlined" />}
			>
				<DsIcon icon="check_small" size="tiny" variant="outlined" />
			</TreeView.NodeCheckboxIndicator>
		</div>
	</TreeView.NodeCheckbox>
);

const DsTreeItemAction = ({ className, style, children, onClick }: DsTreeItemActionProps) => (
	<button
		className={classNames(styles.itemAction, className)}
		style={style}
		type="button"
		tabIndex={-1}
		onClick={(e) => {
			e.stopPropagation();
			onClick?.(e);
		}}
	>
		{children}
	</button>
);

export const DsTree = {
	Root: DsTreeRoot,
	Tree: DsTreeTree,
	NodeProvider: TreeView.NodeProvider,
	NodeContext: TreeView.NodeContext,
	Branch: DsTreeBranch,
	BranchControl: DsTreeBranchControl,
	BranchIndicator: DsTreeBranchIndicator,
	BranchText: DsTreeBranchText,
	BranchContent: DsTreeBranchContent,
	BranchIndentGuide: DsTreeBranchIndentGuide,
	Item: DsTreeItem,
	ItemText: DsTreeItemText,
	ItemIndicator: DsTreeItemIndicator,
	ItemAction: DsTreeItemAction,
	NodeCheckbox: DsTreeNodeCheckbox,
};
Info
No description found. Write a jsdoc comment such as /** Component description */.
Imports
import { DsFilterStatusIcon, DsIcon, DsTree } from "@drivenets/design-system";
Default story ok
const Default = () => {
    const collection = createDsTreeCollection(sideNavNodes);

    return (
        <DsTree.Root
            collection={collection}
            defaultExpandedValue={['network']}
            size="medium"
            onSelectionChange={fn()}
            onExpandedChange={fn()}>
            <DsTree.Tree>
                {collection.rootNode.children?.map((node, index) => (
                    <SideNavDsTreeNode key={node.id} node={node} indexPath={[index]} />
                ))}
            </DsTree.Tree>
        </DsTree.Root>
    );
};
Controlled story ok
const Controlled = () => {
    const collection = createDsTreeCollection(sideNavNodes);
    const [selectedValue, setSelectedValue] = useState<string[]>([]);
    const [expandedValue, setExpandedValue] = useState(['network']);

    return (
        <div>
            <div>Selected: {selectedValue.length > 0 ? selectedValue.join(', ') : 'none'}</div>
            <div>Expanded: {expandedValue.length > 0 ? expandedValue.join(', ') : 'none'}</div>
            <DsTree.Root
                size="medium"
                collection={collection}
                selectedValue={selectedValue}
                onSelectionChange={(details: { selectedValue: string[] }) =>
                    setSelectedValue(details.selectedValue)
                }
                expandedValue={expandedValue}
                onExpandedChange={(details: { expandedValue: string[] }) => setExpandedValue(details.expandedValue)}>
                <DsTree.Tree>
                    {collection.rootNode.children?.map((node, index) => (
                        <SideNavDsTreeNode key={node.id} node={node} indexPath={[index]} />
                    ))}
                </DsTree.Tree>
            </DsTree.Root>
        </div>
    );
};
Checkbox With Icons story ok
const CheckboxWithIcons = () => {
    const collection = createDsTreeCollection(mapLayersNodes);

    return (
        <DsTree.Root
            size={args.size}
            collection={collection}
            defaultCheckedValue={['data-centers', 'dc-east', 'dc-west', 'oms']}
            defaultExpandedValue={['devices', 'optical']}
            onCheckedChange={fn()}
            onExpandedChange={fn()}>
            <DsTree.Tree>
                {collection.rootNode.children?.map((node, index) => (
                    <CheckboxWithIconsNode key={node.id} node={node} indexPath={[index]} />
                ))}
            </DsTree.Tree>
        </DsTree.Root>
    );
};
With Status Icons story ok
const WithStatusIcons = () => {
    const collection = createDsTreeCollection(workflowNodes);
    const onNavigate = fn();

    return (
        <DsTree.Root
            size="medium"
            collection={collection}
            defaultExpandedValue={['workflow-1234', 'sw-running-02', 'sw-running-06']}
            onSelectionChange={fn()}
            onExpandedChange={fn()}>
            <DsTree.Tree>
                {collection.rootNode.children?.map((node, index) => (
                    <WorkflowDsTreeNode
                        key={node.id}
                        node={node}
                        indexPath={[index]}
                        onNavigate={onNavigate}
                        size={args.size}
                    />
                ))}
            </DsTree.Tree>
        </DsTree.Root>
    );
};

Scrollbars

components-scrollbars · ./src/stories/scrollbars/scrollbars.stories.tsx
Prop type error
We could not detect the component from your story file. Specify meta.component.
   5 |
   6 | // eslint-disable-next-line @drivenets/ds-internal/require-story-meta-annotations -- component prop is not required here.
>  7 | const meta: Meta = {
     | ^
   8 | 	title: 'Components/Scrollbars',
   9 | 	parameters: {
  10 | 		layout: 'padded',

./src/stories/scrollbars/scrollbars.stories.tsx:
import type { Meta, StoryObj } from '@storybook/react-vite';
import { Fragment } from 'react';
import classNames from 'classnames';
import styles from './scrollbars.stories.module.scss';

// eslint-disable-next-line @drivenets/ds-internal/require-story-meta-annotations -- component prop is not required here.
const meta: Meta = {
	title: 'Components/Scrollbars',
	parameters: {
		layout: 'padded',
		docs: {
			description: {
				component: `
All scrollbars are styled automatically by the design system.
You can add a \`.scrollbar-thin\` class to the element in order to make its scrollbar thin.
				`,
			},
		},
	},
};

export default meta;
type Story = StoryObj;

// Helper to generate content
const generateContent = (count: number, direction: 'vertical' | 'horizontal') => {
	const items = Array.from({ length: count }, (_, i) => (
		<div key={i} className={direction === 'horizontal' ? styles.contentItemHorizontal : styles.contentItem}>
			<h3>Item {i + 1}</h3>
			<p>Content for item {i + 1}</p>
		</div>
	));

	return <div className={direction === 'horizontal' ? styles.contentContainer : undefined}>{items}</div>;
};

export const DefaultScrollbar: Story = {
	render: () => (
		<div className={styles.container}>
			<div className={styles.section}>
				<h3>Vertical Scrollbar (Default)</h3>
				<div className={styles.scrollableContainer}>{generateContent(20, 'vertical')}</div>
			</div>

			<div className={styles.sectionWide}>
				<h3>Horizontal Scrollbar (Default)</h3>
				<div className={styles.scrollableContainer}>{generateContent(15, 'horizontal')}</div>
			</div>
		</div>
	),
	parameters: {
		docs: {
			description: {
				story: 'Default scrollbars for both vertical and horizontal overflow.',
			},
		},
	},
};

export const SmallScrollbar: Story = {
	render: () => (
		<div className={styles.container}>
			<div className={styles.section}>
				<h3>Vertical Scrollbar (Thin)</h3>
				<div className={classNames('scrollbar-thin', styles.scrollableContainer)}>
					{generateContent(20, 'vertical')}
				</div>
			</div>

			<div className={styles.sectionWide}>
				<h3>Horizontal Scrollbar (Thin)</h3>
				<div className={classNames('scrollbar-thin', styles.scrollableContainer)}>
					{generateContent(15, 'horizontal')}
				</div>
			</div>
		</div>
	),
	parameters: {
		docs: {
			description: {
				story: 'Small (thin) scrollbars for both vertical and horizontal overflow.',
			},
		},
	},
};

export const CombinedExample: Story = {
	render: () => (
		<div className={styles.sectionExtraWide}>
			<h3>Combined Example - Both X and Y Overflow</h3>
			<div className={styles.scrollableContainerTall}>
				<div className={styles.wideContent}>
					<h2>Wide Content</h2>
					<p>This container has both vertical and horizontal overflow, showing both scrollbars.</p>
					{Array.from({ length: 25 }, (_, i) => (
						<Fragment key={i}>{generateContent(25, 'horizontal')}</Fragment>
					))}
				</div>
			</div>
		</div>
	),
	parameters: {
		docs: {
			description: {
				story: 'Example showing both vertical and horizontal scrollbars on the same container.',
			},
		},
	},
};
Info
No description found. Write a jsdoc comment such as /** Component description */.
Imports
import { Fragment } from "react";
Default Scrollbar story ok
const DefaultScrollbar = () => (
    <div className={styles.container}>
        <div className={styles.section}>
            <h3>Vertical Scrollbar (Default)</h3>
            <div className={styles.scrollableContainer}>{generateContent(20, 'vertical')}</div>
        </div>

        <div className={styles.sectionWide}>
            <h3>Horizontal Scrollbar (Default)</h3>
            <div className={styles.scrollableContainer}>{generateContent(15, 'horizontal')}</div>
        </div>
    </div>
);
Small Scrollbar story ok
const SmallScrollbar = () => (
    <div className={styles.container}>
        <div className={styles.section}>
            <h3>Vertical Scrollbar (Thin)</h3>
            <div className={classNames('scrollbar-thin', styles.scrollableContainer)}>
                {generateContent(20, 'vertical')}
            </div>
        </div>

        <div className={styles.sectionWide}>
            <h3>Horizontal Scrollbar (Thin)</h3>
            <div className={classNames('scrollbar-thin', styles.scrollableContainer)}>
                {generateContent(15, 'horizontal')}
            </div>
        </div>
    </div>
);
Combined Example story ok
const CombinedExample = () => (
    <div className={styles.sectionExtraWide}>
        <h3>Combined Example - Both X and Y Overflow</h3>
        <div className={styles.scrollableContainerTall}>
            <div className={styles.wideContent}>
                <h2>Wide Content</h2>
                <p>This container has both vertical and horizontal overflow, showing both scrollbars.</p>
                {Array.from({ length: 25 }, (_, i) => (
                    <Fragment key={i}>{generateContent(25, 'horizontal')}</Fragment>
                ))}
            </div>
        </div>
    </div>
);

Unattached Docs

Overview

overview--docs · ./src/stories/overview.mdx
import { Meta, Source } from '@storybook/addon-docs/blocks';
import { DsTypography } from '../components/ds-typography';
import { DsIcon } from '../components/ds-icon';
import { DsDivider } from '../components/ds-divider';
import { DsCard } from '../components/ds-card';
import { DsButton } from '../components/ds-button';
import styles from './overview.module.scss';
import githubIconUrl from './assets/github-icon-title.svg';
import figmaIconUrl from './assets/figma.svg';
import logoUrl from './assets/logo.svg';

<Meta title="Overview" />

<div className={`${styles.root} sb-unstyled`}>

<div className={styles.hero}>
	<div className={styles.heroHeader}>
		<img src={logoUrl} className={styles.logo} alt="DriveNets" />
		<DsTypography variant="heading1">DriveNets Design System</DsTypography>
	</div>
	<DsTypography variant="body-md-reg" asChild className={styles.heroDescription}>
		<span>
			The official component library for building consistent, scalable UI across DriveNets applications.
			Browse components in the sidebar, copy code from the docs, and ship faster.
		</span>
	</DsTypography>
</div>

<div className={styles.links}>
	<a
		href="https://github.com/drivenets/design-system"
		target="_blank"
		rel="noopener noreferrer"
		className={styles.link}
	>
		<img src={githubIconUrl} className={styles.githubIcon} alt="GitHub" />
	</a>
	<a
		href="https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2"
		target="_blank"
		rel="noopener noreferrer"
		className={styles.link}
	>
		<img src={figmaIconUrl} className={styles.figmaIcon} alt="Figma" />
	</a>
</div>

<DsDivider />

<div>
  <div className={styles.sectionHeader}>
    <DsIcon icon="rocket_launch" size="medium" />
    <DsTypography variant="heading2">Getting Started</DsTypography>
  </div>

  <div className={styles.steps}>
    <div className={styles.step}>
      <DsTypography variant="heading4">1. Install the package</DsTypography>
      <Source dark code="pnpm install @drivenets/design-system" language="bash" />
    </div>

    <div className={styles.step}>
      <DsTypography variant="heading4">2. Import styles</DsTypography>
      <DsTypography variant="body-sm-reg" className={styles.stepHint}>
        Add this to your application root (e.g. main.tsx or App.tsx):
      </DsTypography>
      <Source dark code="import '@drivenets/design-system/index.min.css';" language="typescript" />
    </div>

    <div className={styles.step}>
      <DsTypography variant="heading4">3. Add fonts</DsTypography>
      <DsTypography variant="body-sm-reg" className={styles.stepHint}>
        Use the Vite plugin for automatic font injection:
      </DsTypography>
      <Source dark code="pnpm install -D @drivenets/vite-plugin-design-system" language="bash" />
    </div>

    <div className={styles.step}>
      <DsTypography variant="heading4">4. Use components</DsTypography>
      <Source dark code={`import { DsButton } from '@drivenets/design-system';

export const App = () => (
    <DsButton onClick={() => alert('Clicked!')}>Click Me</DsButton>
);`} language="tsx" />
    </div>
  </div>
</div>

<DsDivider />

<div>
  <div className={styles.sectionHeader}>
    <DsIcon icon="verified" size="medium" />
    <DsTypography variant="heading2">ESLint Plugin</DsTypography>
  </div>

<DsTypography variant="body-md-reg" asChild className={styles.sectionDescription}>
	<span>
		Enforce design system consistency and catch deprecated components early with the ESLint plugin. It
		prevents usage of native HTML elements and MUI components, ensuring your codebase stays aligned with the
		design system.
	</span>
</DsTypography>

  <div className={styles.steps}>
    <div className={styles.step}>
      <DsTypography variant="heading4">1. Install the plugin</DsTypography>
      <Source dark code="pnpm install -D @drivenets/eslint-plugin-design-system" language="bash" />
    </div>

    <div className={styles.step}>
      <DsTypography variant="heading4">2. Add to ESLint config</DsTypography>
      <DsTypography variant="body-sm-reg" className={styles.stepHint}>
        Import and use the recommended preset in your ESLint config:
      </DsTypography>
      <Source dark code={`import designSystem from '@drivenets/eslint-plugin-design-system';

export default [
    // your config
    designSystem.configs.recommended,
];`} language="typescript" />
    </div>
  </div>
</div>

</div>

Guidelines/Colors

guidelines-colors--docs · ./src/stories/colors.mdx
import { Meta } from '@storybook/addon-docs/blocks';
import { useState } from 'react';
import { DsTypography } from '../components/ds-typography';
import styles from './colors.module.scss';

<Meta title="Guidelines/Colors" />

export const getCssVariable = (name) => {
	if (typeof window === 'undefined') return '';
	const value = getComputedStyle(document.documentElement).getPropertyValue(`--${name}`).trim();
	return /^#([0-9a-f]{3,8})$/i.test(value) ? value.toUpperCase() : value;
};

export const Swatch = ({ variable, label }) => {
	const [copied, setCopied] = useState(false);
	const handleCopy = async () => {
		const value = getCssVariable(variable);
		if (!value) return;
		await navigator.clipboard.writeText(`var(--${variable})`);
		setCopied(true);
		setTimeout(() => setCopied(false), 1200);
	};

    const value = getCssVariable(variable);

    return (
    	<div className={styles.swatchCol}>
    		<button
    			type="button"
    			className={styles.swatchItem}
    			style={{ background: `var(--${variable})` }}
    			onClick={handleCopy}
    			title={`Click to copy var(--${variable})`}
    			aria-label={`${label} ${value}`}
    		>
    			<span className={styles.toneCode}>{value}</span>
    			{copied && <span className={styles.copiedTooltip}>Copied</span>}
    		</button>
    		<span className={styles.toneLabel}>{label}</span>
    		<span className={styles.toneVariable}>--{variable}</span>
    	</div>
    );

};

export const SwatchGroup = ({ label, tones }) => (
	<div className={`${styles.group} sb-unstyled`}>
		<DsTypography variant="heading4" className={styles.groupLabel}>
			{label}
		</DsTypography>
		<div className={styles.swatchRow}>
			{tones.map((tone) => (
				<Swatch key={tone.variable} variable={tone.variable} label={tone.label} />
			))}
		</div>
	</div>
);

# Colors

Reference CSS variables instead of hard-coding hex values.

## Which layer should I use?

Prefer the highest layer that fits: **semantic > alias > primitive**.

- **Component code (SCSS / inline styles):** always use semantic tokens (`--background-action`,
  `--font-main`, `--border-field`).
- **Building a new semantic token:** compose from alias tokens (`--primary-700-default`,
  `--info-600-default`).
- **Building a new alias token:** reach for primitives (`--color-dap-blue-500`) only here.

If you are editing a component and find yourself typing `--color-dap-*` or a bare alias, stop and look
for a semantic token first.

## 1. Primitive (DAP-root-colors)

The raw palette. See [Which layer should I use?](#which-layer-should-i-use) — only reach for primitive
tokens when building a new alias.

<div className={styles.layer}>

<SwatchGroup
	label="Blue"
	tones={[
		{ label: '050', variable: 'color-dap-blue-050' },
		{ label: '075', variable: 'color-dap-blue-075' },
		{ label: '100', variable: 'color-dap-blue-100' },
		{ label: '200', variable: 'color-dap-blue-200' },
		{ label: '300', variable: 'color-dap-blue-300' },
		{ label: '400', variable: 'color-dap-blue-400' },
		{ label: '500', variable: 'color-dap-blue-500' },
		{ label: '600', variable: 'color-dap-blue-600' },
		{ label: '700', variable: 'color-dap-blue-700' },
		{ label: '800', variable: 'color-dap-blue-800' },
		{ label: '900', variable: 'color-dap-blue-900' },
		{ label: '950', variable: 'color-dap-blue-950' },
	]}
/>

<SwatchGroup
	label="Brand"
	tones={[
		{ label: '50', variable: 'color-dap-brand-50' },
		{ label: '100', variable: 'color-dap-brand-100' },
		{ label: '200', variable: 'color-dap-brand-200' },
		{ label: '300', variable: 'color-dap-brand-300' },
		{ label: '400', variable: 'color-dap-brand-400' },
		{ label: '500', variable: 'color-dap-brand-500' },
		{ label: '600', variable: 'color-dap-brand-600' },
		{ label: '700', variable: 'color-dap-brand-700' },
	]}
/>

<SwatchGroup
	label="Dark Blue"
	tones={[
		{ label: '050', variable: 'color-dap-dark-blue-050' },
		{ label: '100', variable: 'color-dap-dark-blue-100' },
		{ label: '150', variable: 'color-dap-dark-blue-150' },
		{ label: '200', variable: 'color-dap-dark-blue-200' },
		{ label: '300', variable: 'color-dap-dark-blue-300' },
		{ label: '400', variable: 'color-dap-dark-blue-400' },
		{ label: '500', variable: 'color-dap-dark-blue-500' },
		{ label: '600', variable: 'color-dap-dark-blue-600' },
		{ label: '700', variable: 'color-dap-dark-blue-700' },
	]}
/>

<SwatchGroup
	label="Dark Gray"
	tones={[
		{ label: '050', variable: 'color-dap-dark-gray-050' },
		{ label: '100', variable: 'color-dap-dark-gray-100' },
		{ label: '200', variable: 'color-dap-dark-gray-200' },
	]}
/>

<SwatchGroup
	label="Gray"
	tones={[
		{ label: '050', variable: 'color-dap-gray-050' },
		{ label: '100', variable: 'color-dap-gray-100' },
		{ label: '200', variable: 'color-dap-gray-200' },
		{ label: '300', variable: 'color-dap-gray-300' },
		{ label: '400', variable: 'color-dap-gray-400' },
		{ label: '500', variable: 'color-dap-gray-500' },
		{ label: '600', variable: 'color-dap-gray-600' },
		{ label: '700', variable: 'color-dap-gray-700' },
		{ label: '800', variable: 'color-dap-gray-800' },
		{ label: '900', variable: 'color-dap-gray-900' },
	]}
/>

<SwatchGroup
	label="Green"
	tones={[
		{ label: '050', variable: 'color-dap-green-050' },
		{ label: '075', variable: 'color-dap-green-075' },
		{ label: '100', variable: 'color-dap-green-100' },
		{ label: '200', variable: 'color-dap-green-200' },
		{ label: '300', variable: 'color-dap-green-300' },
		{ label: '400', variable: 'color-dap-green-400' },
		{ label: '500', variable: 'color-dap-green-500' },
		{ label: '600', variable: 'color-dap-green-600' },
		{ label: '700', variable: 'color-dap-green-700' },
		{ label: '800', variable: 'color-dap-green-800' },
		{ label: '900', variable: 'color-dap-green-900' },
	]}
/>

<SwatchGroup
	label="Orange"
	tones={[
		{ label: '050', variable: 'color-dap-orange-050' },
		{ label: '100', variable: 'color-dap-orange-100' },
		{ label: '200', variable: 'color-dap-orange-200' },
		{ label: '300', variable: 'color-dap-orange-300' },
		{ label: '400', variable: 'color-dap-orange-400' },
		{ label: '500', variable: 'color-dap-orange-500' },
		{ label: '600', variable: 'color-dap-orange-600' },
		{ label: '700', variable: 'color-dap-orange-700' },
		{ label: '800', variable: 'color-dap-orange-800' },
		{ label: '900', variable: 'color-dap-orange-900' },
	]}
/>

<SwatchGroup
	label="Purple"
	tones={[
		{ label: '050', variable: 'color-dap-purple-050' },
		{ label: '100', variable: 'color-dap-purple-100' },
		{ label: '200', variable: 'color-dap-purple-200' },
		{ label: '300', variable: 'color-dap-purple-300' },
		{ label: '400', variable: 'color-dap-purple-400' },
		{ label: '500', variable: 'color-dap-purple-500' },
		{ label: '600', variable: 'color-dap-purple-600' },
		{ label: '700', variable: 'color-dap-purple-700' },
		{ label: '800', variable: 'color-dap-purple-800' },
		{ label: '900', variable: 'color-dap-purple-900' },
		{ label: '950', variable: 'color-dap-purple-950' },
	]}
/>

<SwatchGroup
	label="Red"
	tones={[
		{ label: '050', variable: 'color-dap-red-050' },
		{ label: '100', variable: 'color-dap-red-100' },
		{ label: '200', variable: 'color-dap-red-200' },
		{ label: '300', variable: 'color-dap-red-300' },
		{ label: '400', variable: 'color-dap-red-400' },
		{ label: '500', variable: 'color-dap-red-500' },
		{ label: '600', variable: 'color-dap-red-600' },
		{ label: '700', variable: 'color-dap-red-700' },
	]}
/>

<SwatchGroup
	label="Data"
	tones={[
		{ label: 'amber', variable: 'color-dap-data-amber' },
		{ label: 'blue', variable: 'color-dap-data-blue' },
		{ label: 'deep-fuchsia', variable: 'color-dap-data-deep-fuchsia' },
		{ label: 'deep-purple', variable: 'color-dap-data-deep-purple' },
		{ label: 'error', variable: 'color-dap-data-error' },
		{ label: 'fuchsia', variable: 'color-dap-data-fuchsia' },
		{ label: 'green', variable: 'color-dap-data-green' },
		{ label: 'lime', variable: 'color-dap-data-lime' },
		{ label: 'orange', variable: 'color-dap-data-orange' },
		{ label: 'pink', variable: 'color-dap-data-pink' },
		{ label: 'purple', variable: 'color-dap-data-purple' },
		{ label: 'rose', variable: 'color-dap-data-rose' },
		{ label: 'teal', variable: 'color-dap-data-teal' },
		{ label: 'yellow', variable: 'color-dap-data-yellow' },
		{ label: 'light-amber', variable: 'color-dap-data-light-amber' },
		{ label: 'light-blue', variable: 'color-dap-data-light-blue' },
		{ label: 'light-deep-fuchsia', variable: 'color-dap-data-light-deep-fuchsia' },
		{ label: 'light-deep-purple', variable: 'color-dap-data-light-deep-purple' },
		{ label: 'light-error', variable: 'color-dap-data-light-error' },
		{ label: 'light-fuchsia', variable: 'color-dap-data-light-fuchsia' },
		{ label: 'light-green', variable: 'color-dap-data-light-green' },
		{ label: 'light-lime', variable: 'color-dap-data-light-lime' },
		{ label: 'light-orange', variable: 'color-dap-data-light-orange' },
		{ label: 'light-pink', variable: 'color-dap-data-light-pink' },
		{ label: 'light-purple', variable: 'color-dap-data-light-purple' },
		{ label: 'light-rose', variable: 'color-dap-data-light-rose' },
		{ label: 'light-teal', variable: 'color-dap-data-light-teal' },
		{ label: 'light-yellow', variable: 'color-dap-data-light-yellow' },
	]}
/>

</div>

## 2. Alias

Intent-based aliases that point at the primitive palette. See [Which layer should I use?](#which-layer-should-i-use) —
reach for these only when building a new semantic token (or extending one).

<div className={styles.layer}>

<SwatchGroup
	label="Brand"
	tones={[
		{ label: '050', variable: 'brand-050' },
		{ label: '100', variable: 'brand-100' },
		{ label: '200', variable: 'brand-200' },
		{ label: '300', variable: 'brand-300' },
		{ label: '400', variable: 'brand-400' },
		{ label: '500 (default)', variable: 'brand-500-default' },
		{ label: '600', variable: 'brand-600' },
		{ label: '700', variable: 'brand-700' },
	]}
/>

<SwatchGroup
	label="Darks"
	tones={[
		{ label: '050', variable: 'darks-050' },
		{ label: '100', variable: 'darks-100' },
		{ label: '200', variable: 'darks-200' },
		{ label: '300', variable: 'darks-300' },
		{ label: '400', variable: 'darks-400' },
		{ label: '500', variable: 'darks-500' },
		{ label: '600', variable: 'darks-600' },
		{ label: '700', variable: 'darks-700' },
	]}
/>

<SwatchGroup
	label="Info"
	tones={[
		{ label: '050', variable: 'info-050' },
		{ label: '100', variable: 'info-100' },
		{ label: '200', variable: 'info-200' },
		{ label: '300', variable: 'info-300' },
		{ label: '400', variable: 'info-400' },
		{ label: '500', variable: 'info-500' },
		{ label: '600 (default)', variable: 'info-600-default' },
		{ label: '700', variable: 'info-700' },
		{ label: '800 (progress)', variable: 'info-800-progress' },
		{ label: '900', variable: 'info-900' },
		{ label: '950', variable: 'info-950' },
	]}
/>

<SwatchGroup
	label="Negative"
	tones={[
		{ label: '050', variable: 'negative-050' },
		{ label: '100', variable: 'negative-100' },
		{ label: '200', variable: 'negative-200' },
		{ label: '300', variable: 'negative-300' },
		{ label: '400 (default)', variable: 'negative-400-default' },
		{ label: '500', variable: 'negative-500' },
		{ label: '600', variable: 'negative-600' },
		{ label: '700', variable: 'negative-700' },
	]}
/>

<SwatchGroup label="Paused" tones={[{ label: '600', variable: 'paused-600' }]} />

<SwatchGroup
	label="Positive"
	tones={[
		{ label: '050', variable: 'positive-050' },
		{ label: '100', variable: 'positive-100' },
		{ label: '200', variable: 'positive-200' },
		{ label: '300', variable: 'positive-300' },
		{ label: '400', variable: 'positive-400' },
		{ label: '500', variable: 'positive-500' },
		{ label: '600', variable: 'positive-600' },
		{ label: '700 (default)', variable: 'positive-700-default' },
		{ label: '800', variable: 'positive-800' },
		{ label: '900', variable: 'positive-900' },
	]}
/>

<SwatchGroup
	label="Primary"
	tones={[
		{ label: '050', variable: 'primary-050' },
		{ label: '100', variable: 'primary-100' },
		{ label: '200', variable: 'primary-200' },
		{ label: '300', variable: 'primary-300' },
		{ label: '400', variable: 'primary-400' },
		{ label: '500', variable: 'primary-500' },
		{ label: '600', variable: 'primary-600' },
		{ label: '700 (default)', variable: 'primary-700-default' },
		{ label: '800', variable: 'primary-800' },
		{ label: '900', variable: 'primary-900' },
		{ label: '950', variable: 'primary-950' },
	]}
/>

<SwatchGroup
	label="Secondary"
	tones={[
		{ label: '050', variable: 'secondary-050' },
		{ label: '100', variable: 'secondary-100' },
		{ label: '200', variable: 'secondary-200' },
		{ label: '300', variable: 'secondary-300' },
		{ label: '400', variable: 'secondary-400' },
		{ label: '500 (net-4)', variable: 'secondary-500-net-4' },
		{ label: '600 (net-2)', variable: 'secondary-600-net-2' },
		{ label: '700 (net-1)', variable: 'secondary-700-net-1' },
		{ label: '800', variable: 'secondary-800' },
		{ label: '900', variable: 'secondary-900' },
	]}
/>

<SwatchGroup
	label="Warning"
	tones={[
		{ label: '050', variable: 'warning-050' },
		{ label: '100', variable: 'warning-100' },
		{ label: '200', variable: 'warning-200' },
		{ label: '300', variable: 'warning-300' },
		{ label: '400', variable: 'warning-400' },
		{ label: '500', variable: 'warning-500' },
		{ label: '600 (default)', variable: 'warning-600-default' },
		{ label: '700', variable: 'warning-700' },
		{ label: '800', variable: 'warning-800' },
		{ label: '900', variable: 'warning-900' },
	]}
/>

</div>

## 3. Semantic (DAP uses)

Role-based tokens bound to a UI purpose (background, border, font, icon, outline, schema, shadow, status).
**This is the layer to use in component code.** See [Which layer should I use?](#which-layer-should-i-use).

<div className={styles.layer}>

<SwatchGroup
	label="Background"
	tones={[
		{ label: 'background', variable: 'background' },
		{ label: 'action', variable: 'background-action' },
		{ label: 'action-hover', variable: 'background-action-hover' },
		{ label: 'action-hover-weak', variable: 'background-action-hover-weak' },
		{ label: 'action-inverse', variable: 'background-action-inverse' },
		{ label: 'action-secondary', variable: 'background-action-secondary' },
		{ label: 'action-secondary-hover', variable: 'background-action-secondary-hover' },
		{ label: 'action-tertiary-hover', variable: 'background-action-tertiary-hover' },
		{ label: 'active-status', variable: 'background-active-status' },
		{ label: 'active-moderate', variable: 'background-active-moderate' },
		{ label: 'brand', variable: 'background-brand' },
		{ label: 'deselected', variable: 'background-deselected' },
		{ label: 'deselected-hover', variable: 'background-deselected-hover' },
		{ label: 'disable', variable: 'background-disable' },
		{ label: 'error', variable: 'background-error' },
		{ label: 'error-hover', variable: 'background-error-hover' },
		{ label: 'error-secondary-hover', variable: 'background-error-secondary-hover' },
		{ label: 'error-secondary-selected', variable: 'background-error-secondary-selected' },
		{ label: 'error-selected', variable: 'background-error-selected' },
		{ label: 'execution', variable: 'background-execution' },
		{ label: 'info', variable: 'background-info' },
		{ label: 'info-strong', variable: 'background-info-strong' },
		{ label: 'light', variable: 'background-light' },
		{ label: 'light-disabled', variable: 'background-light-disabled' },
		{ label: 'light-primary', variable: 'background-light-primary' },
		{ label: 'light-primary-hover', variable: 'background-light-primary-hover' },
		{ label: 'light-primary-selected', variable: 'background-light-primary-selected' },
		{ label: 'light-secondary-selected', variable: 'background-light-secondary-selected' },
		{ label: 'neutral', variable: 'background-neutral' },
		{ label: 'overlay', variable: 'background-overlay' },
		{ label: 'page', variable: 'background-page' },
		{ label: 'pending', variable: 'background-pending' },
		{ label: 'primary', variable: 'background-primary' },
		{ label: 'primary-hover', variable: 'background-primary-hover' },
		{ label: 'primary-selected', variable: 'background-primary-selected' },
		{ label: 'secondary', variable: 'background-secondary' },
		{ label: 'secondary-hover', variable: 'background-secondary-hover' },
		{ label: 'secondary-selected-weak', variable: 'background-secondary-selected-weak' },
		{ label: 'success', variable: 'background-success' },
		{ label: 'success-strong', variable: 'background-success-strong' },
		{ label: 'success-strong-hover', variable: 'background-success-strong-hover' },
		{ label: 'tertiary', variable: 'background-tertiary' },
		{ label: 'tertiary-selected-weak', variable: 'background-tertiary-selected-weak' },
		{ label: 'transparent-active', variable: 'background-transparent-active' },
		{ label: 'transparent-hover', variable: 'background-transparent-hover' },
		{ label: 'warning', variable: 'background-warning' },
		{ label: 'warning-strong', variable: 'background-warning-strong' },
	]}
/>

<SwatchGroup
	label="Border"
	tones={[
		{ label: 'border', variable: 'border' },
		{ label: 'action-primary', variable: 'border-action-primary' },
		{ label: 'action-primary-hover', variable: 'border-action-primary-hover' },
		{ label: 'action-secondary', variable: 'border-action-secondary' },
		{ label: 'action-secondary-hover', variable: 'border-action-secondary-hover' },
		{ label: 'contrast', variable: 'border-contrast' },
		{ label: 'disabled', variable: 'border-disabled' },
		{ label: 'error', variable: 'border-error' },
		{ label: 'error-hover', variable: 'border-error-hover' },
		{ label: 'error-weak', variable: 'border-error-weak' },
		{ label: 'field', variable: 'border-field' },
		{ label: 'field-focus', variable: 'border-field-focus' },
		{ label: 'field-hover', variable: 'border-field-hover' },
		{ label: 'inverse', variable: 'border-inverse' },
		{ label: 'light-disabled', variable: 'border-light-disabled' },
		{ label: 'light-secondary', variable: 'border-light-secondary' },
		{ label: 'secondary', variable: 'border-secondary' },
		{ label: 'secondary-hover', variable: 'border-secondary-hover' },
		{ label: 'success', variable: 'border-success' },
		{ label: 'tertiary', variable: 'border-tertiary' },
		{ label: 'warning-strong', variable: 'border-warning-strong' },
	]}
/>

<SwatchGroup
	label="Font"
	tones={[
		{ label: 'action', variable: 'font-action' },
		{ label: 'action-hover', variable: 'font-action-hover' },
		{ label: 'action-secondary', variable: 'font-action-secondary' },
		{ label: 'action-secondary-hover', variable: 'font-action-secondary-hover' },
		{ label: 'code', variable: 'font-code' },
		{ label: 'disabled', variable: 'font-disabled' },
		{ label: 'error', variable: 'font-error' },
		{ label: 'highlight', variable: 'font-highlight' },
		{ label: 'light-disabled', variable: 'font-light-disabled' },
		{ label: 'main', variable: 'font-main' },
		{ label: 'on-action', variable: 'font-on-action' },
		{ label: 'on-disabled', variable: 'font-on-disabled' },
		{ label: 'placeholder', variable: 'font-placeholder' },
		{ label: 'secondary', variable: 'font-secondary' },
		{ label: 'success', variable: 'font-success' },
		{ label: 'warning', variable: 'font-warning' },
	]}
/>

<SwatchGroup
	label="Icon"
	tones={[
		{ label: 'on-button', variable: 'icon-on-button' },
		{ label: 'action', variable: 'icon-action' },
		{ label: 'action-hover', variable: 'icon-action-hover' },
		{ label: 'action-secondary', variable: 'icon-action-secondary' },
		{ label: 'disabled', variable: 'icon-disabled' },
		{ label: 'error', variable: 'icon-error' },
		{ label: 'execution', variable: 'icon-execution' },
		{ label: 'information-main', variable: 'icon-information-main' },
		{ label: 'information-secondary', variable: 'icon-information-secondary' },
		{ label: 'inverse', variable: 'icon-inverse' },
		{ label: 'main', variable: 'icon-main' },
		{ label: 'on-dark-disabled', variable: 'icon-on-dark-disabled' },
		{ label: 'pause', variable: 'icon-pause' },
		{ label: 'pending', variable: 'icon-pending' },
		{ label: 'secondary', variable: 'icon-secondary' },
		{ label: 'success', variable: 'icon-success' },
		{ label: 'success-light', variable: 'icon-success-light' },
		{ label: 'warning', variable: 'icon-warning' },
		{ label: 'warning-light', variable: 'icon-warning-light' },
	]}
/>

<SwatchGroup
	label="Outline"
	tones={[
		{ label: 'extreme', variable: 'outline-extreme' },
		{ label: 'inverse', variable: 'outline-inverse' },
		{ label: 'moderate', variable: 'outline-moderate' },
		{ label: 'strong', variable: 'outline-strong' },
		{ label: 'weak', variable: 'outline-weak' },
	]}
/>

<SwatchGroup
	label="Schema (data visualization)"
	tones={[
		{ label: 'amber', variable: 'schema-amber' },
		{ label: 'blue', variable: 'schema-blue' },
		{ label: 'deep-fuchsia', variable: 'schema-deep-fuchsia' },
		{ label: 'deep-purple', variable: 'schema-deep-purple' },
		{ label: 'error', variable: 'schema-error' },
		{ label: 'fuchsia', variable: 'schema-fuchsia' },
		{ label: 'green', variable: 'schema-green' },
		{ label: 'lime', variable: 'schema-lime' },
		{ label: 'orange', variable: 'schema-orange' },
		{ label: 'pink', variable: 'schema-pink' },
		{ label: 'purple', variable: 'schema-purple' },
		{ label: 'rose', variable: 'schema-rose' },
		{ label: 'teal', variable: 'schema-teal' },
		{ label: 'yellow', variable: 'schema-yellow' },
		{ label: 'light-amber', variable: 'schema-light-amber' },
		{ label: 'light-blue', variable: 'schema-light-blue' },
		{ label: 'light-deep-fuchsia', variable: 'schema-light-deep-fuchsia' },
		{ label: 'light-deep-purple', variable: 'schema-light-deep-purple' },
		{ label: 'light-error', variable: 'schema-light-error' },
		{ label: 'light-fuchsia', variable: 'schema-light-fuchsia' },
		{ label: 'light-green', variable: 'schema-light-green' },
		{ label: 'light-lime', variable: 'schema-light-lime' },
		{ label: 'light-orange', variable: 'schema-light-orange' },
		{ label: 'light-pink', variable: 'schema-light-pink' },
		{ label: 'light-purple', variable: 'schema-light-purple' },
		{ label: 'light-rose', variable: 'schema-light-rose' },
		{ label: 'light-teal', variable: 'schema-light-teal' },
		{ label: 'light-yellow', variable: 'schema-light-yellow' },
	]}
/>

<SwatchGroup
	label="Shadow"
	tones={[
		{ label: 'moderate', variable: 'shadow-moderate' },
		{ label: 'strong', variable: 'shadow-strong' },
		{ label: 'weak', variable: 'shadow-weak' },
	]}
/>

<SwatchGroup
	label="Status"
	tones={[
		{ label: 'bg-inactive', variable: 'status-bg-inactive' },
		{ label: 'bg-neutral', variable: 'status-bg-neutral' },
		{ label: 'bg-pending', variable: 'status-bg-pending' },
		{ label: 'bg-progress', variable: 'status-bg-progress' },
		{ label: 'bg-success', variable: 'status-bg-success' },
		{ label: 'bg-warning', variable: 'status-bg-warning' },
	]}
/>

<SwatchGroup
	label="Net-gen"
	tones={[
		{ label: 'net-gen-1', variable: 'net-gen-1' },
		{ label: 'net-gen-2', variable: 'net-gen-2' },
		{ label: 'net-gen-hover-1', variable: 'net-gen-hover-1' },
		{ label: 'net-gen-hover-2', variable: 'net-gen-hover-2' },
		{ label: 'logo-dark-grey', variable: 'net-gen-logo-dark-grey' },
		{ label: 'logo-dodger-blue', variable: 'net-gen-logo-dodger-blue' },
		{ label: 'logo-light-grey', variable: 'net-gen-logo-light-grey' },
		{ label: 'logo-ultramarine-blue', variable: 'net-gen-logo-ultramarine-blue' },
	]}
/>

<SwatchGroup
	label="Light buttons"
	tones={[
		{ label: 'primary', variable: 'light-buttons-primary' },
		{ label: 'primary-hover', variable: 'light-buttons-primary-hover' },
		{ label: 'secondary-background', variable: 'light-buttons-secondary-background' },
		{ label: 'secondary-hover', variable: 'light-buttons-secondary-hover' },
		{ label: 'secondary-light', variable: 'light-buttons-secondary-light' },
		{ label: 'secondary-light-hover-bg', variable: 'light-buttons-secondary-light-hover-background' },
		{ label: 'tertiary', variable: 'light-buttons-tertiary' },
		{ label: 'tertiary-hover-bg', variable: 'light-buttons-tertiary-hover-background' },
	]}
/>

<SwatchGroup
	label="Brand accents"
	tones={[
		{ label: 'AT&T', variable: 'at-t-brand' },
		{ label: 'Nokia', variable: 'nokia-brand' },
		{ label: 'Primary green', variable: 'primary-green' },
		{ label: 'Error', variable: 'error' },
		{ label: 'Text on button', variable: 'text-on-button' },
	]}
/>

</div>

Guidelines/Typography

guidelines-typography--docs · ./src/stories/typography.mdx
import { Meta } from '@storybook/addon-docs/blocks';
import { DsTypography } from '../components/ds-typography';
import { typographyColors, typographyVariantConfig } from '../components/ds-typography/ds-typography.config';
import styles from './typography.module.scss';

<Meta title="Guidelines/Typography" />

export const sampleText = 'Almost before we knew it, we had left the ground.';

export const getCssVariable = (name) => {
	if (typeof window === 'undefined') return '—';
	const value = getComputedStyle(document.documentElement).getPropertyValue(`--${name}`).trim();
	return value || '—';
};

export const allVariants = Object.keys(typographyVariantConfig);

export const VariantShowcase = () => {
	const sections = [
		{
			title: 'Headings',
			variants: allVariants.filter((key) => key.startsWith('heading')),
		},
		{
			title: 'Body',
			variants: allVariants.filter((key) => key.startsWith('body')),
		},
		{
			title: 'Code',
			variants: allVariants.filter((key) => key.startsWith('code')),
		},
	];

    return (
    	<div className="sb-unstyled">
    		{sections.map((section) => (
    			<section key={section.title} className={styles.variantGroup}>
    				<DsTypography variant="heading3" className={styles.variantGroupTitle}>
    					{section.title}
    				</DsTypography>
    				{section.variants.map((variant) => (
    					<div key={variant} className={styles.comparisonItem}>
    						<DsTypography variant="code-sm-reg" className={styles.comparisonLabel}>
    							{variant}
    						</DsTypography>
    						<DsTypography variant={variant}>
    							{sampleText} ({typographyVariantConfig[variant].element})
    						</DsTypography>
    					</div>
    				))}
    			</section>
    		))}
    	</div>
    );

};

export const ColorShowcase = () => (
	<div className="sb-unstyled">
		{typographyColors.map((color) => (
			<div key={color} className={styles.comparisonItem}>
				<DsTypography variant="code-sm-reg" className={styles.comparisonLabel}>
					{color}
				</DsTypography>
				<DsTypography variant="body-md-md" color={color}>
					{sampleText}
				</DsTypography>
			</div>
		))}
	</div>
);

# Typography

Use [`DsTypography`](?path=/docs/design-system-typography--docs) with a `variant` prop to render text with
the correct size, weight, line height, and semantic HTML element.

## Which API should I use?

Prefer the `variant` prop on `DsTypography` in component code. Reach for the raw typography tokens
(`--body-font-size-md`, `--heading-line-height-2xl`, `--font-weight-semi-bold`) only in SCSS where a
variant cannot express what you need (for example, a custom inline chart label).

## Variants

The curated set of styles exposed through `DsTypography`. Each row shows the variant key and the
semantic element it renders.

<VariantShowcase />

## Color

Pass a semantic `color` prop to tint text from the `--font-*` token family. Omit to inherit from
the surrounding context (recommended). For one-off accents, the same prop accepts any CSS color
value: hex, `rgb()`, `hsl()`, a CSS variable, or a named color — for example
`color="var(--color-dap-purple-600)"` or `color="#ff8800"`.

<ColorShowcase />

## Tokens

Low-level CSS custom properties backing the variants above. Reach for these only when a variant cannot
express what you need.

## Font family

| Token                | Value                                |
| -------------------- | ------------------------------------ |
| `--font-family-base` | {getCssVariable('font-family-base')} |
| `--font-family-code` | {getCssVariable('font-family-code')} |

## Font weight

| Token                      | Value                                      |
| -------------------------- | ------------------------------------------ |
| `--font-weight-regular`    | {getCssVariable('font-weight-regular')}    |
| `--font-weight-medium`     | {getCssVariable('font-weight-medium')}     |
| `--font-weight-semi-bold`  | {getCssVariable('font-weight-semi-bold')}  |
| `--font-weight-bold`       | {getCssVariable('font-weight-bold')}       |
| `--font-weight-extra-bold` | {getCssVariable('font-weight-extra-bold')} |

## Body type primitives

| Token                          | Value                                          |
| ------------------------------ | ---------------------------------------------- |
| `--body-font-size-xs`          | {getCssVariable('body-font-size-xs')}          |
| `--body-font-size-sm`          | {getCssVariable('body-font-size-sm')}          |
| `--body-font-size-md`          | {getCssVariable('body-font-size-md')}          |
| `--body-line-height-xs`        | {getCssVariable('body-line-height-xs')}        |
| `--body-line-height-small`     | {getCssVariable('body-line-height-small')}     |
| `--body-line-height-md`        | {getCssVariable('body-line-height-md')}        |
| `--body-paragraph-space-xs`    | {getCssVariable('body-paragraph-space-xs')}    |
| `--body-paragraph-space-small` | {getCssVariable('body-paragraph-space-small')} |
| `--body-paragraph-space-md`    | {getCssVariable('body-paragraph-space-md')}    |

## Heading type primitives

| Token                             | Value                                             |
| --------------------------------- | ------------------------------------------------- |
| `--heading-font-size-large`       | {getCssVariable('heading-font-size-large')}       |
| `--heading-font-size-xl`          | {getCssVariable('heading-font-size-xl')}          |
| `--heading-font-size-2xl`         | {getCssVariable('heading-font-size-2xl')}         |
| `--heading-font-size-3xl`         | {getCssVariable('heading-font-size-3xl')}         |
| `--heading-line-height-large`     | {getCssVariable('heading-line-height-large')}     |
| `--heading-line-height-xl`        | {getCssVariable('heading-line-height-xl')}        |
| `--heading-line-height-2xl`       | {getCssVariable('heading-line-height-2xl')}       |
| `--heading-line-height-3xl`       | {getCssVariable('heading-line-height-3xl')}       |
| `--heading-paragraph-space-large` | {getCssVariable('heading-paragraph-space-large')} |
| `--heading-paragraph-space-xl`    | {getCssVariable('heading-paragraph-space-xl')}    |
| `--heading-paragraph-space-2xl`   | {getCssVariable('heading-paragraph-space-2xl')}   |
| `--heading-paragraph-space-3xl`   | {getCssVariable('heading-paragraph-space-3xl')}   |

## Color tokens

| Token                           | Value                                           |
| ------------------------------- | ----------------------------------------------- |
| `--font-main`                   | {getCssVariable('font-main')}                   |
| `--font-secondary`              | {getCssVariable('font-secondary')}              |
| `--font-action`                 | {getCssVariable('font-action')}                 |
| `--font-action-hover`           | {getCssVariable('font-action-hover')}           |
| `--font-action-secondary`       | {getCssVariable('font-action-secondary')}       |
| `--font-action-secondary-hover` | {getCssVariable('font-action-secondary-hover')} |
| `--font-disabled`               | {getCssVariable('font-disabled')}               |
| `--font-light-disabled`         | {getCssVariable('font-light-disabled')}         |
| `--font-on-action`              | {getCssVariable('font-on-action')}              |
| `--font-on-disabled`            | {getCssVariable('font-on-disabled')}            |
| `--font-placeholder`            | {getCssVariable('font-placeholder')}            |
| `--font-highlight`              | {getCssVariable('font-highlight')}              |
| `--font-success`                | {getCssVariable('font-success')}                |
| `--font-warning`                | {getCssVariable('font-warning')}                |
| `--font-error`                  | {getCssVariable('font-error')}                  |
| `--font-code`                   | {getCssVariable('font-code')}                   |

Guidelines/Layouts

guidelines-layouts--docs · ./src/stories/patterns-layouts.mdx
import { Meta, Source } from '@storybook/addon-docs/blocks';

<Meta title="Guidelines/Layouts" />

# Layouts

Compose layouts from design-system primitives instead of bespoke CSS. Pick the right primitive
for the job:

- [`DsStack`](?path=/docs/design-system-stack--docs) — 1D arrangement (form fields, toolbars, action
  rows).
- [`DsGrid`](?path=/docs/design-system-grid--docs) / `DsGridItem` — 2D structure on a 12-column grid
  (page layouts, two-column forms).
- [`DsCard`](?path=/docs/design-system-card--docs) — grouped surface with header/body/footer slots
  (section grouping, side-panels).
- [`DsDivider`](?path=/docs/design-system-divider--docs) — horizontal or vertical separation between
  sections.

All layout props accept responsive values so a single tree adapts across breakpoints.

## When to use which

| Primitive    | Use for                                                                            | Key props                                                                       |
| ------------ | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| `DsStack`    | Vertical stacks of form fields, horizontal rows of actions, anything flex-based    | `direction`, `gap`, `alignItems`, `justifyContent`, `flexWrap`, `flex`, `width` |
| `DsGrid`     | Page layouts, side-by-side form columns, any 2D placement                          | `columns` (2–12), `rows` (1–8), `gutter`, `margin`                              |
| `DsGridItem` | Cells inside `DsGrid`                                                              | `colSpan`, `colStart`, `rowSpan`, `rowStart`                                    |
| `DsCard`     | Grouping a logical chunk (a section of a form, a summary panel, a selectable tile) | `size`, `selectable`, `selected`, `highlightSelected`, `disabled`               |
| `DsDivider`  | Separating sections inside a stack or card                                         | `orientation`                                                                   |

Reach for `DsStack` first. Only step up to `DsGrid` when you need two
dimensions or precise column alignment across rows.

---

## Composing forms

Forms are stacks of [`DsFormControl`](?path=/docs/design-system-formcontrol-text--docs)s. Use `DsStack` for the column of fields
and for the action row; use `DsGrid` when fields pair up side-by-side; use

`DsCard` to group related fields into visually distinct sections.

### Single-column form

The canonical pattern: a vertical stack of fields, a divider, then a right-aligned action row.

<Source
	dark
	language="tsx"
	code={`<form onSubmit={handleSubmit(onSubmit)}>
    <DsStack direction="column" gap="16px" width="320px">
        <DsFormControl label="Name" required>
            <DsFormControl.TextInput placeholder="Enter your name" />
        </DsFormControl>

        <DsFormControl label="Email" required>
            <DsFormControl.TextInput type="email" placeholder="Enter your email" />
        </DsFormControl>

        <DsFormControl label="Description">
            <DsFormControl.Textarea placeholder="Tell us more" />
        </DsFormControl>

        <DsDivider />

        <DsStack direction="row" justifyContent="flex-end" gap="8px">
            <DsButton schema="secondary" variant="ghost">Cancel</DsButton>
            <DsButton type="submit">Submit</DsButton>
        </DsStack>
    </DsStack>

</form>`}
/>

### Two-column form

Pair related fields (first/last name, start/end date) with `DsGrid` and

`DsGridItem colSpan={6}`. Fields that should span the full row use
`colSpan="full"`.

<Source
	dark
	language="tsx"
	code={`<DsGrid columns={12} gutter={16}>
    <DsGridItem colSpan={6}>
        <DsFormControl label="First name" required>
            <DsFormControl.TextInput />
        </DsFormControl>
    </DsGridItem>
    <DsGridItem colSpan={6}>
        <DsFormControl label="Last name" required>
            <DsFormControl.TextInput />
        </DsFormControl>
    </DsGridItem>

    <DsGridItem colSpan="full">
        <DsFormControl label="Email" required>
            <DsFormControl.TextInput type="email" />
        </DsFormControl>
    </DsGridItem>

</DsGrid>`}
/>

### Grouped sections with DsCard

Longer forms are easier to scan when related fields live inside a `DsCard`. Put the
section title in `DsCard.Header`, the fields in `DsCard.Body` (nested

`DsStack`), and actions in `DsCard.Footer`.

<Source
	dark
	language="tsx"
	code={`<DsCard.Root>
    <DsCard.Header>
        <DsTypography variant="heading4">Account</DsTypography>
    </DsCard.Header>
    <DsCard.Body>
        <DsStack direction="column" gap="16px">
            <DsFormControl label="Email" required>
                <DsFormControl.TextInput type="email" />
            </DsFormControl>
            <DsFormControl label="Password" required>
                <DsFormControl.TextInput type="password" />
            </DsFormControl>
        </DsStack>
    </DsCard.Body>
    <DsCard.Footer>
        <DsStack direction="row" justifyContent="flex-end" gap="8px">
            <DsButton schema="secondary" variant="ghost">Cancel</DsButton>
            <DsButton type="submit">Save</DsButton>
        </DsStack>
    </DsCard.Footer>
</DsCard.Root>`}
/>

For multi-section forms, stack several cards with `DsStack direction="column"
gap="24px"`, or separate in-card sub-sections with a horizontal `DsDivider`.

### Do / don't

| Do                                                                     | Don't                                            |
| ---------------------------------------------------------------------- | ------------------------------------------------ |
| Wrap the form body in `DsStack direction="column"`                     | Use a raw `div` with inline flex styles          |
| Use `DsFormControl` for every field (labels, required state, messages) | Render bare inputs with a manual `label` element |
| Let `DsGrid` handle column math on a 12-col track                      | Hand-roll percentage widths                      |
| Align submit buttons with `DsStack justifyContent="flex-end"`          | Rely on `text-align: right` on a wrapper         |

---

## Responsiveness

The design system targets two breakpoints, defined in

`src/utils/responsive.ts`:

| Breakpoint | Matches                                        |
| ---------- | ---------------------------------------------- |
| `md`       | narrow viewports (below `--breakpoint-lg`)     |
| `lg`       | wide viewports (at or above `--breakpoint-lg`) |

Any layout prop declared as `ResponsiveValue<T>` accepts either a plain value or a
`Partial<Record<'lg' | 'md', T>>`. The wrapper resolves the right value for the current
breakpoint; the underlying component never sees the responsive object.

`DsStack` and `DsGrid` / `DsGridItem` are both wrapped with
`withResponsiveProps`, so their layout props are already responsive-aware.

### Example: stack that becomes a row on wide screens

<Source
	dark
	language="tsx"
	code={`<DsStack
    direction={{ md: 'column', lg: 'row' }}
    gap={{ md: 8, lg: 24 }}
    alignItems="center"
>
    <DsCard.Root>{/* ... */}</DsCard.Root>
    <DsCard.Root>{/* ... */}</DsCard.Root>
</DsStack>`}
/>

### Example: two-column form that collapses on narrow screens

<Source
	dark
	language="tsx"
	code={`<DsGrid columns={12} gutter={{ md: 8, lg: 16 }}>
    <DsGridItem colSpan={{ md: 'full', lg: 6 }}>
        <DsFormControl label="First name">
            <DsFormControl.TextInput />
        </DsFormControl>
    </DsGridItem>
    <DsGridItem colSpan={{ md: 'full', lg: 6 }}>
        <DsFormControl label="Last name">
            <DsFormControl.TextInput />
        </DsFormControl>
    </DsGridItem>
</DsGrid>`}
/>

### When props aren't enough

For conditional rendering or logic that depends on the breakpoint, use the hooks exported from
`responsive.ts`:

<Source
	dark
	language="tsx"
	code={`import { useBreakpoint, useResponsiveValue } from '@drivenets/design-system';

const Toolbar = () => {
const bp = useBreakpoint(); // 'lg' | 'md'
const maxItems = useResponsiveValue({ md: 3, lg: 8 });

    return bp === 'lg' ? <WideToolbar max={maxItems} /> : <CompactToolbar max={maxItems} />;

};`}
/>

To give a custom component responsive props without rewriting it, wrap it with
`withResponsiveProps`:

<Source
	dark
	language="tsx"
	code={`
import type { ComponentProps } from 'react';
import { withResponsiveProps } from '@drivenets/design-system';

interface MyComponentProps {
size: 'large' | 'medium' | 'small' | 'tiny';
label: string;
}

const MyComponent = ({ size, label }: MyComponentProps) => (

<div className={\`my-component my-component--\${size}\`}>{label}</div>
);

export const MyResponsiveComponent = withResponsiveProps(MyComponent, ['size']);

// No need to redefine the props — withResponsiveProps rewrites the listed keys
// to accept responsive values, and the type is inferred automatically:
type MyResponsiveComponentProps = ComponentProps<typeof MyResponsiveComponent>;

// Consumers can now pass:

<MyResponsiveComponent label="Hello" size={{ md: 'small', lg: 'large' }} />`}
/>

Guidelines/Forms

guidelines-forms--docs · ./src/stories/patterns-forms.mdx
import { Meta, Source } from '@storybook/addon-docs/blocks';

<Meta title="Guidelines/Forms" />

# Forms

Forms in the design system are built with [`react-hook-form`](https://react-hook-form.com/) and
[`zod`](https://zod.dev/), and every input is wrapped in
[`DsFormControl`](?path=/docs/design-system-formcontrol-text--docs) so labels, required indicators, and
validation messages render consistently. This page describes the canonical pattern; for a runnable
reference see the [`Examples/SampleForm`](?path=/story/examples-sampleform--default) story.

## Related components

- [`DsFormControl`](?path=/docs/design-system-formcontrol-text--docs)
  ([Textarea](?path=/docs/design-system-formcontrol-textarea--docs),
  [Number](?path=/docs/design-system-formcontrol-number--docs),
  [Password](?path=/docs/design-system-formcontrol-password--docs),
  [Select](?path=/docs/design-system-formcontrol-select--docs),
  [DatePicker](?path=/docs/design-system-formcontrol-datepicker--docs),
  [TimePicker](?path=/docs/design-system-formcontrol-timepicker--docs))
- [`DsTextInput`](?path=/docs/design-system-textinput--docs)
- [`DsSelect`](?path=/docs/design-system-select--docs)
- [`DsDatePicker`](?path=/docs/design-system-datepicker--docs) /
  [`DsDateRangePicker`](?path=/docs/design-system-daterangepicker--docs) /
  [`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)
- [`DsTypography`](?path=/docs/design-system-typography--docs)

## Stack

| Dependency                | Why                                                                           |
| ------------------------- | ----------------------------------------------------------------------------- |
| `react-hook-form`         | Uncontrolled-by-default form state with minimal re-renders                    |
| `zod`                     | Schema-first validation that also produces the form's TypeScript types        |
| `@hookform/resolvers/zod` | Bridges a Zod schema into `react-hook-form`'s validation lifecycle            |
| `DsFormControl`           | Unifies label, required marker, status, and message rendering for every field |

## Set up the form

Call `useForm` with the Zod resolver, a complete `defaultValues` object, and `mode: 'onChange'`.

Wrap the tree in `FormProvider` so nested components can read
context when needed.

<Source dark language="tsx" code={`
import { FormProvider, useForm, type SubmitHandler } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { sampleFormSchema, type SampleFormValues } from './sample-form-schema';

const defaultValues: Partial<SampleFormValues> = { name: '', email: ''};

const SampleForm = () => {

    const form = useForm<SampleFormValues>({
        resolver: zodResolver(sampleFormSchema),
        defaultValues,
        mode: 'onChange',
    });

    const { handleSubmit, control } = form;

    const onSubmit = (data: SampleFormValues) => {
        // ...submit side effect
    };

    return (
        <FormProvider {...form}>
            <form onSubmit={handleSubmit(onSubmit)}>{/* fields */}</form>
        </FormProvider>
    );

};`} />

## Define the schema

Co-locate the schema in a `<form-name>-schema.ts` file next to the component. The
schema doubles as the form's type (`z.infer`) and as the source of every error message,
which keeps copy aligned across validation and UI.

<Source dark language="typescript" code={`
import { z } from 'zod';

const subscriptionTypes = ['basic', 'pro', 'enterprise'] as const;

export const sampleFormSchema = z.object({
	name: z.string().min(1, 'Name is required'),
	email: z.string().email('Invalid email address'),
	description: z.string().min(20, 'Short description is required (min. 20 chars)'),
	quantity: z
		.number('Quantity is required')
		.min(1, 'Quantity must be at least 1')
		.max(100, 'Quantity cannot exceed 100'),
	birthDate: z.date('Birth date is required'),
	eventStartDate: z.date('Event start date is required'),
	eventEndDate: z.date('Event end date is required'),
	acceptTerms: z.boolean().refine((v) => v, 'You must accept the terms and conditions'),
	subscription: z.enum(subscriptionTypes, {
		error: () => 'Please select a subscription plan',
	}),
	contactMethod: z.string().nonempty('Please select a contact method'),
});

export type SampleFormValues = z.infer<typeof sampleFormSchema>;`} />

## Bind every control with `Controller`

Prefer `Controller` over `register`. A single pattern keeps every
field consistent: `Controller` is the outer element, `DsFormControl` lives
inside the `render` callback reading status and message from `fieldState`,
and the input itself is driven by `field`.
Do **not** gate the error message on `fieldState.isTouched`.

<Source dark language="tsx" code={`<Controller
    name="name"
    control={control}
    render={({ field, fieldState }) => (
        <DsFormControl
            label="Name"
            required
            status="error"
            messageIcon="cancel"
            message={fieldState.error?.message}
        >
            <DsFormControl.TextInput placeholder="Enter your name" {...field} />
        </DsFormControl>
    )}
/>

<Controller
    name="description"
    control={control}
    render={({ field, fieldState }) => (
        <DsFormControl
            label="Description"
            required
            status="error"
            messageIcon="cancel"
            message={fieldState.error?.message}
        >
            <DsFormControl.Textarea placeholder="Enter your description" {...field} />
        </DsFormControl>
    )}
/>`} />

## Let `field` drive value, dirty, and touched state

With `mode: 'onChange'`, `field.onChange(value)` already updates the value,
marks the field dirty, and triggers validation; `field.onBlur()` marks the field
touched. Skip custom `setValue` wrappers — and whenever the control accepts standard
input semantics (`value` + `onChange(event)` + `onBlur`), spread `{...field}` into it.
A custom mapper is only needed when the control expects a different callback shape (`onValueChange`, `onCheckedChange`, a`Date` object, etc.).

## Date fields

Store dates as Date objects. Single-date pickers follow the same Controller-outer pattern
as any other field. [`DsDateRangePicker`](?path=/docs/design-system-daterangepicker--docs) is the
exception: it drives two form fields
simultaneously, so bind each field with `useController` and forward

`field.value` / `field.onChange` / `field.onBlur` through the range picker's
props and slots. Error state flows through
`slotProps.startDateFormControl` / `slotProps.endDateFormControl`;`onBlur` is
forwarded via `slotProps.startDatePicker` / `slotProps.endDatePicker`(
`DsFormControl` does not accept `onBlur`).

<Source dark language="tsx" code={`
<Controller
	name="birthDate"
	control={control}
	render={({ field, fieldState }) => (
		<DsFormControl
			label="Birth Date"
			required
			status="error"
			messageIcon="cancel"
			message={fieldState.error?.message}
		>
			<DsFormControl.DatePicker {...field} />
		</DsFormControl>
	)}
/>

const { field: eventStartField } = useController({ name: 'eventStartDate', control });
const { field: eventEndField } = useController({ name: 'eventEndDate', control });

<DsDateRangePicker
    value={[eventStartField.value, eventEndField.value]}
    onChange={([start, end]) => {
        eventStartField.onChange(start);
        eventEndField.onChange(end);
    }}
    slotProps={{
        startDatePicker: { onBlur: eventStartField.onBlur },
        endDatePicker:   { onBlur: eventEndField.onBlur },
        startDateFormControl: {
            required: true,
            status: 'error',
            messageIcon: 'cancel',
            message: errors.eventStartDate?.message,
        },
        endDateFormControl: {
            required: true,
            status: 'error',
            messageIcon: 'cancel',
            message: errors.eventEndDate?.message,
        },
    }}
/>`} />

## Grouped controls (radio, checkbox)

[`DsRadioGroup`](?path=/docs/design-system-radiogroup--docs) and
[`DsCheckbox`](?path=/docs/design-system-checkbox--docs) render their own labels, so they sit alongside —
not inside — `DsFormControl`. Wrap them in `Controller` just like any other field so RHF
owns value, dirty, and touched state. When you need to render an inline error message (for example below a
radio group), use [`DsTypography`](?path=/docs/design-system-typography--docs) and the
`--background-error` token — never inline styles or raw HTML elements.

<Source dark language="tsx" code={`
<Controller
    name="subscription"
    control={control}
    render={({ field, fieldState }) => (
        <>
            <DsRadioGroup.Root value={field.value ?? ''} onValueChange={field.onChange}>
                <DsRadioGroup.Item value="basic" label="Basic" />
                <DsRadioGroup.Item value="pro" label="Pro" />
                <DsRadioGroup.Item value="enterprise" label="Enterprise" />
            </DsRadioGroup.Root>
            {fieldState.isTouched && fieldState.error && (
                <DsTypography variant="body-xs-reg" style={{ color: 'var(--background-error)' }}>
                    {fieldState.error.message}
                </DsTypography>
            )}
        </>
    )}
/>

<Controller
    name="acceptTerms"
    control={control}
    render={({ field }) => (
        <DsCheckbox
            label="I accept the terms and conditions"
            checked={!!field.value}
            onCheckedChange={(checked) => field.onChange(checked === true)}
            onBlur={field.onBlur}
        />
    )}
/>`} />

## Submit gating and reset

Avoid disabling submit purely based on validation state. Users should be able to attempt submission and receive clear feedback about what needs fixing.

- 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
  spinner in place of the label
- Do not gate submit on isValid

For edit/update forms, disable until the user has made changes:

<Source
	dark
	language="tsx"
	code={`<DsButtonV3 type="submit" disabled={!isDirty || isSubmitting} loading={isSubmitting}>
    Submit
</DsButtonV3>`}
/>

For create forms, keep submit enabled and rely on validation on submit:

<Source
	dark
	language="tsx"
	code={`<DsButtonV3 type="submit" disabled={isSubmitting} loading={isSubmitting}>
    Submit
</DsButtonV3>`}
/>

## Checklist

- Schema lives in a sibling `<form-name>-schema.ts` and drives both types and copy.
- `useForm` uses `zodResolver`, `mode: 'onChange'`, and a complete
  `defaultValues` object.
- The form tree is wrapped in `FormProvider`.
- Every field is bound with `Controller` — never `register`. Reach for
  `useController` only when one input drives multiple fields (for example
  `DsDateRangePicker`).
- `Controller` is the outer element; `DsFormControl` lives inside
  `render` and reads status/message from `fieldState`.
- Inline text (for example grouped-control error messages) uses `DsTypography` and
  design tokens like `{`var(--background-error)`}` — no raw HTML or inline style colors.
- Inputs with standard `value`/`onChange`/`onBlur` semantics get
  `{...field}` spread straight into them. Only map callbacks explicitly when the control
  exposes a different shape (e.g. `onValueChange`, `onCheckedChange`, a`Date`
  object) — no custom `setValue` helper.
- When one input writes to multiple fields at once (for example `DsDateRangePicker`),
  create a `useController` per field and call each `field.onChange` in the
  shared handler — do not reach for `setValue`. Forward `field.onBlur`
  through the matching sub-component slot (e.g.
  `{`slotProps.startDatePicker.onBlur`}`), not the form-control slot.
- Submit is **not** gated on `isValid`.
  - Edit forms: `disabled={!isDirty || isSubmitting}`
  - Create forms: `disabled={isSubmitting}`

Guidelines/Token Migration

guidelines-token-migration--docs · ./src/stories/token-migration.mdx
import { Meta } from '@storybook/addon-docs/blocks';

<Meta title="Guidelines/Token Migration" />

# Token Migration

Shows the mapping of old tokens to new tokens.

> When replacing an old token, prefer the semantic token that expresses the role even if an alias or
> primitive has the same value. See the layer guidance on the
> [Colors](?path=/docs/design-system-colors--docs) page.

## Legend

| Status  | Meaning                                                                                                | Migration strategy                                                                                                                                     |
| ------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Renamed | Same resolved value; name changed (trivially shortened, semantically renamed, or numeric key remapped) | Find & replace; no testing is required                                                                                                                 |
| Changed | Resolved value differs                                                                                 | Find & replace; visual testing is required                                                                                                             |
| New     | No old equivalent                                                                                      |                                                                                                                                                        |
| Removed | Old token with no new equivalent                                                                       | The **New Name** column holds a literal CSS value that the script substitutes verbatim, unwrapping any `var(--token)` so the resulting CSS stays valid |

---

## 1. Primitive colors

### Blue

| Old Name           | New Name               | Status  |
| ------------------ | ---------------------- | ------- |
| `--color-blue-50`  | `--color-dap-blue-050` | Renamed |
| —                  | `--color-dap-blue-075` | New     |
| `--color-blue-100` | `--color-dap-blue-100` | Renamed |
| `--color-blue-200` | `--color-dap-blue-200` | Renamed |
| `--color-blue-300` | `--color-dap-blue-300` | Renamed |
| `--color-blue-400` | `--color-dap-blue-400` | Renamed |
| `--color-blue-500` | `--color-dap-blue-500` | Renamed |
| `--color-blue-600` | `--color-dap-blue-600` | Renamed |
| `--color-blue-700` | `--color-dap-blue-700` | Renamed |
| `--color-blue-800` | `--color-dap-blue-800` | Renamed |
| `--color-blue-900` | `--color-dap-blue-900` | Renamed |
| `--color-blue-950` | `--color-dap-blue-950` | Renamed |

### Brand

| Old Name            | New Name                | Status  |
| ------------------- | ----------------------- | ------- |
| `--color-brand-50`  | `--color-dap-brand-050` | Renamed |
| `--color-brand-100` | `--color-dap-brand-100` | Renamed |
| `--color-brand-200` | `--color-dap-brand-200` | Renamed |
| `--color-brand-300` | `--color-dap-brand-300` | Renamed |
| `--color-brand-400` | `--color-dap-brand-400` | Renamed |
| `--color-brand-500` | `--color-dap-brand-500` | Renamed |
| `--color-brand-600` | `--color-dap-brand-600` | Renamed |
| `--color-brand-700` | `--color-dap-brand-700` | Renamed |

### Dark Blue

| Old Name                | New Name                    | Status  |
| ----------------------- | --------------------------- | ------- |
| `--color-dark-blue-50`  | `--color-dap-dark-blue-050` | Renamed |
| `--color-dark-blue-100` | `--color-dap-dark-blue-100` | Renamed |
| —                       | `--color-dap-dark-blue-150` | New     |
| `--color-dark-blue-300` | `--color-dap-dark-blue-200` | Renamed |
| `--color-dark-blue-200` | `--color-dap-dark-blue-300` | Renamed |
| —                       | `--color-dap-dark-blue-400` | New     |
| —                       | `--color-dap-dark-blue-500` | New     |
| `--color-dark-blue-400` | `--color-dap-dark-blue-600` | Renamed |
| `--color-dark-blue-500` | `--color-dap-dark-blue-700` | Renamed |

### Dark Gray

| Old Name                | New Name                    | Status  |
| ----------------------- | --------------------------- | ------- |
| `--color-dark-gray-50`  | `--color-dap-dark-gray-050` | Renamed |
| `--color-dark-gray-100` | `--color-dap-dark-gray-100` | Renamed |
| `--color-dark-gray-200` | `--color-dap-dark-gray-200` | Renamed |

### Data

| Old Name                    | New Name                              | Status  |
| --------------------------- | ------------------------------------- | ------- |
| `--color-data-amber`        | `--color-dap-data-amber`              | Renamed |
| `--color-data-blue`         | `--color-dap-data-blue`               | Renamed |
| `--color-data-deep-fuchsia` | `--color-dap-data-deep-fuchsia`       | Renamed |
| `--color-data-deep-purple`  | `--color-dap-data-deep-purple`        | Renamed |
| `--color-data-error`        | `--color-dap-data-error`              | Renamed |
| `--color-data-fuchsia`      | `--color-dap-data-fuchsia`            | Renamed |
| `--color-data-green`        | `--color-dap-data-green`              | Renamed |
| —                           | `--color-dap-data-light-amber`        | New     |
| —                           | `--color-dap-data-light-blue`         | New     |
| —                           | `--color-dap-data-light-deep-fuchsia` | New     |
| —                           | `--color-dap-data-light-deep-purple`  | New     |
| —                           | `--color-dap-data-light-error`        | New     |
| —                           | `--color-dap-data-light-fuchsia`      | New     |
| —                           | `--color-dap-data-light-green`        | New     |
| —                           | `--color-dap-data-light-lime`         | New     |
| —                           | `--color-dap-data-light-orange`       | New     |
| —                           | `--color-dap-data-light-pink`         | New     |
| `--color-data-light-purple` | `--color-dap-data-light-purple`       | Renamed |
| —                           | `--color-dap-data-light-rose`         | New     |
| —                           | `--color-dap-data-light-teal`         | New     |
| —                           | `--color-dap-data-light-yellow`       | New     |
| `--color-data-lime`         | `--color-dap-data-lime`               | Renamed |
| `--color-data-orange`       | `--color-dap-data-orange`             | Renamed |
| `--color-data-pink`         | `--color-dap-data-pink`               | Renamed |
| `--color-data-purple`       | `--color-dap-data-purple`             | Renamed |
| `--color-data-rose`         | `--color-dap-data-rose`               | Renamed |
| `--color-data-teal`         | `--color-dap-data-teal`               | Renamed |
| `--color-data-yellow`       | `--color-dap-data-yellow`             | Renamed |

### Gray

| Old Name           | New Name               | Status  |
| ------------------ | ---------------------- | ------- |
| `--color-gray-50`  | `--color-dap-gray-050` | Renamed |
| `--color-gray-100` | `--color-dap-gray-100` | Renamed |
| `--color-gray-200` | `--color-dap-gray-200` | Renamed |
| `--color-gray-300` | `--color-dap-gray-300` | Renamed |
| `--color-gray-400` | `--color-dap-gray-400` | Renamed |
| `--color-gray-500` | `--color-dap-gray-500` | Renamed |
| `--color-gray-600` | `--color-dap-gray-600` | Renamed |
| `--color-gray-700` | `--color-dap-gray-700` | Renamed |
| `--color-gray-800` | `--color-dap-gray-800` | Renamed |
| `--color-gray-900` | `--color-dap-gray-900` | Renamed |

### Green

| Old Name            | New Name                | Status  |
| ------------------- | ----------------------- | ------- |
| `--color-green-50`  | `--color-dap-green-050` | Renamed |
| —                   | `--color-dap-green-075` | New     |
| `--color-green-100` | `--color-dap-green-100` | Renamed |
| `--color-green-200` | `--color-dap-green-200` | Renamed |
| `--color-green-300` | `--color-dap-green-300` | Renamed |
| `--color-green-400` | `--color-dap-green-400` | Renamed |
| `--color-green-500` | `--color-dap-green-500` | Renamed |
| `--color-green-600` | `--color-dap-green-600` | Renamed |
| `--color-green-700` | `--color-dap-green-700` | Renamed |
| `--color-green-800` | `--color-dap-green-800` | Renamed |
| `--color-green-900` | `--color-dap-green-900` | Renamed |

### Orange

| Old Name             | New Name                 | Status  |
| -------------------- | ------------------------ | ------- |
| `--color-orange-50`  | `--color-dap-orange-050` | Renamed |
| `--color-orange-100` | `--color-dap-orange-100` | Renamed |
| `--color-orange-200` | `--color-dap-orange-200` | Renamed |
| `--color-orange-300` | `--color-dap-orange-300` | Renamed |
| `--color-orange-400` | `--color-dap-orange-400` | Renamed |
| `--color-orange-500` | `--color-dap-orange-500` | Renamed |
| `--color-orange-600` | `--color-dap-orange-600` | Renamed |
| `--color-orange-700` | `--color-dap-orange-700` | Renamed |
| `--color-orange-800` | `--color-dap-orange-800` | Renamed |
| `--color-orange-900` | `--color-dap-orange-900` | Renamed |

### Purple

| Old Name             | New Name                 | Status  |
| -------------------- | ------------------------ | ------- |
| `--color-purple-50`  | `--color-dap-purple-050` | Renamed |
| `--color-purple-100` | `--color-dap-purple-100` | Renamed |
| `--color-purple-200` | `--color-dap-purple-200` | Renamed |
| `--color-purple-300` | `--color-dap-purple-300` | Renamed |
| `--color-purple-400` | `--color-dap-purple-400` | Renamed |
| `--color-purple-500` | `--color-dap-purple-500` | Renamed |
| `--color-purple-600` | `--color-dap-purple-600` | Renamed |
| `--color-purple-700` | `--color-dap-purple-700` | Renamed |
| `--color-purple-800` | `--color-dap-purple-800` | Renamed |
| `--color-purple-900` | `--color-dap-purple-900` | Renamed |
| `--color-purple-950` | `--color-dap-purple-950` | Renamed |

### Red

| Old Name          | New Name              | Status  |
| ----------------- | --------------------- | ------- |
| `--color-red-50`  | `--color-dap-red-050` | Renamed |
| `--color-red-100` | `--color-dap-red-100` | Changed |
| `--color-red-200` | `--color-dap-red-200` | Renamed |
| `--color-red-300` | `--color-dap-red-300` | Renamed |
| `--color-red-400` | `--color-dap-red-400` | Renamed |
| `--color-red-500` | `--color-dap-red-500` | Renamed |
| `--color-red-600` | `--color-dap-red-600` | Renamed |
| `--color-red-700` | `--color-dap-red-700` | Renamed |

---

## 2. Alias scales

### Brand

| Old Name                    | New Name              | Status  |
| --------------------------- | --------------------- | ------- |
| `--color-brand-050`         | `--brand-050`         | Renamed |
| —                           | `--brand-100`         | New     |
| —                           | `--brand-200`         | New     |
| —                           | `--brand-300`         | New     |
| —                           | `--brand-400`         | New     |
| `--color-brand-500-default` | `--brand-500-default` | Renamed |
| —                           | `--brand-600`         | New     |
| —                           | `--brand-700`         | New     |

### Darks

| Old Name            | New Name      | Status  |
| ------------------- | ------------- | ------- |
| `--color-darks-050` | `--darks-050` | Renamed |
| `--color-darks-100` | `--darks-100` | Renamed |
| `--color-darks-200` | `--darks-200` | Renamed |
| `--color-darks-300` | `--darks-300` | Renamed |
| `--color-darks-400` | `--darks-400` | Renamed |
| `--color-darks-500` | `--darks-500` | Renamed |
| `--color-darks-600` | `--darks-600` | Renamed |
| `--color-darks-700` | `--darks-700` | Renamed |

### Info

| Old Name                    | New Name              | Status  |
| --------------------------- | --------------------- | ------- |
| `--color-info-050`          | `--info-050`          | Renamed |
| `--color-info-100`          | `--info-100`          | Renamed |
| `--color-info-200`          | `--info-200`          | Renamed |
| `--color-info-300`          | `--info-300`          | Renamed |
| `--color-info-400`          | `--info-400`          | Renamed |
| `--color-info-500`          | `--info-500`          | Renamed |
| `--color-info-600-default`  | `--info-600-default`  | Renamed |
| `--color-info-700`          | `--info-700`          | Renamed |
| `--color-info-800-progress` | `--info-800-progress` | Renamed |
| `--color-info-900`          | `--info-900`          | Renamed |
| `--color-info-950`          | `--info-950`          | Renamed |

### Negative

| Old Name                       | New Name                 | Status  |
| ------------------------------ | ------------------------ | ------- |
| `--color-negative-050`         | `--negative-050`         | Renamed |
| `--color-negative-100`         | `--negative-100`         | Changed |
| `--color-negative-200`         | `--negative-200`         | Renamed |
| `--color-negative-300`         | `--negative-300`         | Renamed |
| `--color-negative-400-default` | `--negative-400-default` | Renamed |
| `--color-negative-500`         | `--negative-500`         | Renamed |
| `--color-negative-600`         | `--negative-600`         | Renamed |
| `--color-negative-700`         | `--negative-700`         | Renamed |

### Paused

| Old Name | New Name       | Status |
| -------- | -------------- | ------ |
| —        | `--paused-600` | New    |

### Positive

| Old Name                       | New Name                 | Status  |
| ------------------------------ | ------------------------ | ------- |
| `--color-positive-050`         | `--positive-050`         | Renamed |
| `--color-positive-100`         | `--positive-100`         | Renamed |
| `--color-positive-200`         | `--positive-200`         | Renamed |
| `--color-positive-300`         | `--positive-300`         | Renamed |
| `--color-positive-400`         | `--positive-400`         | Renamed |
| `--color-positive-500`         | `--positive-500`         | Renamed |
| `--color-positive-600`         | `--positive-600`         | Renamed |
| `--color-positive-700-default` | `--positive-700-default` | Renamed |
| `--color-positive-800`         | `--positive-800`         | Renamed |
| `--color-positive-900`         | `--positive-900`         | Renamed |

### Primary

| Old Name                      | New Name                | Status  |
| ----------------------------- | ----------------------- | ------- |
| `--color-primary-050`         | `--primary-050`         | Renamed |
| `--color-primary-100`         | `--primary-100`         | Renamed |
| `--color-primary-200`         | `--primary-200`         | Renamed |
| `--color-primary-300`         | `--primary-300`         | Renamed |
| `--color-primary-400`         | `--primary-400`         | Renamed |
| `--color-primary-500`         | `--primary-500`         | Renamed |
| `--color-primary-600`         | `--primary-600`         | Renamed |
| `--color-primary-700-default` | `--primary-700-default` | Renamed |
| `--color-primary-800`         | `--primary-800`         | Renamed |
| `--color-primary-900`         | `--primary-900`         | Renamed |
| `--color-primary-950`         | `--primary-950`         | Renamed |

### Secondary

| Old Name                      | New Name                | Status  |
| ----------------------------- | ----------------------- | ------- |
| `--color-secondary-white`     | `--secondary-050`       | Renamed |
| `--color-secondary-100`       | `--secondary-100`       | Renamed |
| `--color-secondary-200`       | `--secondary-200`       | Renamed |
| `--color-secondary-300`       | `--secondary-300`       | Renamed |
| `--color-secondary-400`       | `--secondary-400`       | Renamed |
| `--color-secondary-500-net-4` | `--secondary-500-net-4` | Renamed |
| `--color-secondary-600-net-2` | `--secondary-600-net-2` | Renamed |
| `--color-secondary-700-net-1` | `--secondary-700-net-1` | Renamed |
| `--color-secondary-800`       | `--secondary-800`       | Renamed |
| `--color-secondary-black`     | `--secondary-900`       | Renamed |

### Warning

| Old Name                      | New Name                | Status  |
| ----------------------------- | ----------------------- | ------- |
| `--color-warning-050`         | `--warning-050`         | Renamed |
| `--color-warning-100`         | `--warning-100`         | Renamed |
| `--color-warning-200`         | `--warning-200`         | Renamed |
| `--color-warning-300`         | `--warning-300`         | Renamed |
| `--color-warning-400`         | `--warning-400`         | Renamed |
| `--color-warning-500`         | `--warning-500`         | Renamed |
| `--color-warning-600-default` | `--warning-600-default` | Renamed |
| `--color-warning-700`         | `--warning-700`         | Renamed |
| `--color-warning-800`         | `--warning-800`         | Renamed |
| `--color-warning-900`         | `--warning-900`         | Renamed |

---

## 3. Semantic: Background

| Old Name                                     | New Name                                | Status  |
| -------------------------------------------- | --------------------------------------- | ------- |
| `--color-background`                         | `--background`                          | Renamed |
| `--color-background-action`                  | `--background-action`                   | Renamed |
| `--color-background-action-hover`            | `--background-action-hover`             | Renamed |
| `--color-background-action-hover-weak`       | `--background-action-hover-weak`        | Renamed |
| `--color-background-action-inverse`          | `--background-action-inverse`           | Renamed |
| `--color-background-action-secondary`        | `--background-action-secondary`         | Renamed |
| `--color-background-action-secondary-hover`  | `--background-action-secondary-hover`   | Renamed |
| `--color-background-action-tertiary`         | `--background-action-tertiary-hover`    | Renamed |
| —                                            | `--background-active-status`            | New     |
| `--color-background-active-moderate`         | `--background-active-moderate`          | Renamed |
| `--color-background-brand`                   | `--background-brand`                    | Renamed |
| `--color-background-deselected`              | `--background-deselected`               | Renamed |
| `--color-background-deselected-hover`        | `--background-deselected-hover`         | Renamed |
| `--color-background-disable`                 | `--background-disable`                  | Renamed |
| `--color-background-danger-strong`           | `--background-error`                    | Renamed |
| `--color-background-danger-strong-hover`     | `--background-error-hover`              | Renamed |
| `--color-background-danger-weak`             | `--background-error-secondary-hover`    | Renamed |
| —                                            | `--background-error-secondary-selected` | New     |
| —                                            | `--background-error-selected`           | New     |
| —                                            | `--background-execution`                | New     |
| —                                            | `--background-execution-strong`         | New     |
| `--color-background-info`                    | `--background-info`                     | Renamed |
| `--color-background-info-strong`             | `--background-info-strong`              | Renamed |
| —                                            | `--background-light`                    | New     |
| —                                            | `--background-light-disabled`           | New     |
| —                                            | `--background-light-primary`            | New     |
| —                                            | `--background-light-primary-hover`      | New     |
| —                                            | `--background-light-primary-selected`   | New     |
| —                                            | `--background-light-secondary-selected` | New     |
| —                                            | `--background-neutral`                  | New     |
| —                                            | `--background-overlay`                  | New     |
| —                                            | `--background-page`                     | New     |
| —                                            | `--background-pending`                  | New     |
| —                                            | `--background-pending-strong`           | New     |
| `--color-background-selected`                | `--background-primary`                  | Renamed |
| `--color-background-selected-hover`          | `--background-primary-hover`            | Renamed |
| —                                            | `--background-primary-selected`         | New     |
| `--color-background-secondary`               | `--background-secondary`                | Renamed |
| `--color-background-selected-weak`           | `--background-secondary-hover`          | Renamed |
| `--color-background-secondary-selected-weak` | `--background-secondary-selected-weak`  | Renamed |
| `--color-background-success`                 | `--background-success`                  | Changed |
| `--color-background-success-strong`          | `--background-success-strong`           | Renamed |
| `--color-background-success-stronger`        | `--background-success-strong-hover`     | Renamed |
| `--color-background-tertiary`                | `--background-tertiary`                 | Renamed |
| `--color-background-tertiary-selected-weak`  | `--background-tertiary-selected-weak`   | Renamed |
| —                                            | `--background-transparent-active`       | New     |
| —                                            | `--background-transparent-hover`        | New     |
| `--color-background-warning`                 | `--background-warning`                  | Renamed |
| `--color-background-warning-strong`          | `--background-warning-strong`           | Renamed |
| `--color-background-warning-weak`            | `--color-dap-red-050`                   | Changed |

---

## 4. Semantic: Border

| Old Name                                | New Name                          | Status  |
| --------------------------------------- | --------------------------------- | ------- |
| `--color-border`                        | `--border`                        | Renamed |
| `--color-border-action-primary`         | `--border-action-primary`         | Renamed |
| `--color-border-action-primary-hover`   | `--border-action-primary-hover`   | Renamed |
| `--color-border-action-secondary`       | `--border-action-secondary`       | Renamed |
| `--color-border-action-secondary-hover` | `--border-action-secondary-hover` | Renamed |
| `--color-border-contrast`               | `--border-contrast`               | Renamed |
| `--color-border-disabled`               | `--border-disabled`               | Changed |
| `--color-border-danger-strong`          | `--border-error`                  | Renamed |
| —                                       | `--border-error-hover`            | New     |
| `--color-border-danger-weak`            | `--border-error-weak`             | Changed |
| —                                       | `--border-field`                  | New     |
| —                                       | `--border-field-focus`            | New     |
| —                                       | `--border-field-hover`            | New     |
| `--color-border-inverse`                | `--border-inverse`                | Renamed |
| —                                       | `--border-light-disabled`         | New     |
| —                                       | `--border-light-secondary`        | New     |
| `--color-border-secondary`              | `--border-secondary`              | Renamed |
| —                                       | `--border-secondary-hover`        | New     |
| `--color-border-success`                | `--border-success`                | Renamed |
| `--color-border-tertiary`               | `--border-tertiary`               | Renamed |
| —                                       | `--border-warning-strong`         | New     |

---

## 5. Semantic: Font

| Old Name                              | New Name                        | Status  |
| ------------------------------------- | ------------------------------- | ------- |
| `--color-font-action`                 | `--font-action`                 | Renamed |
| `--color-font-action-hover`           | `--font-action-hover`           | Renamed |
| `--color-font-action-secondary`       | `--font-action-secondary`       | Renamed |
| `--color-font-action-secondary-hover` | `--font-action-secondary-hover` | Renamed |
| `--color-font-code`                   | `--font-code`                   | Renamed |
| `--color-font-disabled`               | `--font-disabled`               | Changed |
| `--color-font-danger`                 | `--font-error`                  | Renamed |
| `--color-font-highlight`              | `--font-highlight`              | Renamed |
| —                                     | `--font-light-disabled`         | New     |
| `--color-font-main`                   | `--font-main`                   | Renamed |
| `--color-font-on-action`              | `--font-on-action`              | Renamed |
| `--color-font-on-disabled`            | `--font-on-disabled`            | Renamed |
| —                                     | `--font-placeholder`            | New     |
| `--color-font-secondary`              | `--font-secondary`              | Renamed |
| `--color-font-success`                | `--font-success`                | Renamed |
| `--color-font-warning`                | `--font-warning`                | Renamed |

---

## 6. Semantic: Icon

| Old Name                             | New Name                       | Status  |
| ------------------------------------ | ------------------------------ | ------- |
| —                                    | `--icon-on-button`             | New     |
| `--color-icon-primary`               | `--icon-action`                | Renamed |
| —                                    | `--icon-action-hover`          | New     |
| `--color-icon-secondary`             | `--icon-action-secondary`      | Renamed |
| —                                    | `--icon-disabled`              | New     |
| `--color-icon-danger`                | `--icon-error`                 | Changed |
| —                                    | `--icon-execution`             | New     |
| `--color-icon-information-main`      | `--icon-information-main`      | Renamed |
| `--color-icon-information-secondary` | `--icon-information-secondary` | Renamed |
| `--color-icon-inverse`               | `--icon-inverse`               | Renamed |
| —                                    | `--icon-main`                  | New     |
| —                                    | `--icon-on-dark-disabled`      | New     |
| —                                    | `--icon-paused`                | New     |
| —                                    | `--icon-pending`               | New     |
| —                                    | `--icon-secondary`             | New     |
| `--color-icon-success`               | `--icon-success`               | Renamed |
| —                                    | `--icon-success-light`         | New     |
| `--color-icon-warning`               | `--icon-warning`               | Renamed |
| —                                    | `--icon-warning-light`         | New     |

---

## 7. Outline, Shadow, Status

### Outline

| Old Name                   | New Name             | Status  |
| -------------------------- | -------------------- | ------- |
| `--color-outline-extreme`  | `--outline-extreme`  | Renamed |
| `--color-outline-inverse`  | `--outline-inverse`  | Renamed |
| `--color-outline-moderate` | `--outline-moderate` | Renamed |
| `--color-outline-strong`   | `--outline-strong`   | Renamed |
| `--color-outline-weak`     | `--outline-weak`     | Renamed |

### Shadow

| Old Name                  | New Name            | Status  |
| ------------------------- | ------------------- | ------- |
| `--color-shadow-moderate` | `--shadow-moderate` | Renamed |
| `--color-shadow-strong`   | `--shadow-strong`   | Renamed |
| `--color-shadow-weak`     | `--shadow-weak`     | Renamed |

### Status

| Old Name                     | New Name                             | Status  |
| ---------------------------- | ------------------------------------ | ------- |
| `--color-status-bg-inactive` | `--status-bg-inactive`               | Renamed |
| `--color-status-bg-neutral`  | `--status-bg-neutral`                | Renamed |
| `--color-status-bg-pending`  | `--status-bg-pending`                | Renamed |
| `--color-status-bg-progress` | `--status-bg-progress`               | Renamed |
| `--color-status-bg-success`  | `--status-bg-success`                | Changed |
| `--color-status-bg-warning`  | `--status-bg-warning`                | Renamed |
| `--color-status-bg-failed`   | `--background-error-secondary-hover` | Changed |

---

## 8. Dark / Light buttons

Entire group renamed `dark-buttons` → `light-buttons`.

| Old Name                                                | New Name                                           | Status  |
| ------------------------------------------------------- | -------------------------------------------------- | ------- |
| `--color-dark-buttons-primary`                          | `--light-buttons-primary`                          | Renamed |
| —                                                       | `--light-buttons-primary-2`                        | New     |
| —                                                       | `--light-buttons-primary-3`                        | New     |
| —                                                       | `--light-buttons-primary-4`                        | New     |
| —                                                       | `--light-buttons-primary-5`                        | New     |
| —                                                       | `--light-buttons-primary-6`                        | New     |
| —                                                       | `--light-buttons-primary-7`                        | New     |
| `--color-dark-buttons-primary-hover`                    | `--light-buttons-primary-hover`                    | Renamed |
| —                                                       | `--light-buttons-primary-hover-2`                  | New     |
| —                                                       | `--light-buttons-primary-hover-3`                  | New     |
| —                                                       | `--light-buttons-primary-hover-4`                  | New     |
| —                                                       | `--light-buttons-primary-hover-5`                  | New     |
| —                                                       | `--light-buttons-primary-hover-6`                  | New     |
| —                                                       | `--light-buttons-primary-hover-7`                  | New     |
| `--color-dark-buttons-secondary-background`             | `--light-buttons-secondary-background`             | Renamed |
| `--color-dark-buttons-secondary-hover`                  | `--light-buttons-secondary-hover`                  | Renamed |
| `--color-dark-buttons-secondary-light`                  | `--light-buttons-secondary-light`                  | Renamed |
| `--color-dark-buttons-secondary-light-hover-background` | `--light-buttons-secondary-light-hover-background` | Renamed |
| `--color-dark-buttons-tertiary`                         | `--light-buttons-tertiary`                         | Renamed |
| `--color-dark-buttons-tertiary-hover-background`        | `--light-buttons-tertiary-hover-background`        | Renamed |

---

## 9. Schema (data aliases)

All new — flattened data color aliases without the `color-dap-data-` prefix.

| Old Name | New Name                      | Status |
| -------- | ----------------------------- | ------ |
| —        | `--schema-amber`              | New    |
| —        | `--schema-blue`               | New    |
| —        | `--schema-deep-fuchsia`       | New    |
| —        | `--schema-deep-purple`        | New    |
| —        | `--schema-error`              | New    |
| —        | `--schema-fuchsia`            | New    |
| —        | `--schema-green`              | New    |
| —        | `--schema-light-amber`        | New    |
| —        | `--schema-light-blue`         | New    |
| —        | `--schema-light-deep-fuchsia` | New    |
| —        | `--schema-light-deep-purple`  | New    |
| —        | `--schema-light-error`        | New    |
| —        | `--schema-light-fuchsia`      | New    |
| —        | `--schema-light-green`        | New    |
| —        | `--schema-light-lime`         | New    |
| —        | `--schema-light-orange`       | New    |
| —        | `--schema-light-pink`         | New    |
| —        | `--schema-light-purple`       | New    |
| —        | `--schema-light-rose`         | New    |
| —        | `--schema-light-teal`         | New    |
| —        | `--schema-light-yellow`       | New    |
| —        | `--schema-lime`               | New    |
| —        | `--schema-orange`             | New    |
| —        | `--schema-pink`               | New    |
| —        | `--schema-purple`             | New    |
| —        | `--schema-rose`               | New    |
| —        | `--schema-teal`               | New    |
| —        | `--schema-yellow`             | New    |

---

## 10. Brand vendor / misc

| Old Name | New Name                          | Status |
| -------- | --------------------------------- | ------ |
| —        | `--att-brand`                     | New    |
| —        | `--net-gen-1`                     | New    |
| —        | `--net-gen-2`                     | New    |
| —        | `--net-gen-hover-1`               | New    |
| —        | `--net-gen-hover-2`               | New    |
| —        | `--net-gen-logo-dark-grey`        | New    |
| —        | `--net-gen-logo-dodger-blue`      | New    |
| —        | `--net-gen-logo-light-grey`       | New    |
| —        | `--net-gen-logo-ultramarine-blue` | New    |
| —        | `--nokia-brand`                   | New    |
| —        | `--primary-green`                 | New    |
| —        | `--error`                         | New    |
| —        | `--text-on-button`                | New    |

---

## 11. Typography (legacy `_root.scss`)

### Font family

| Old Name             | New Name             | Status  |
| -------------------- | -------------------- | ------- |
| `--font-family-base` | `--font-family-base` | Changed |
| `--font-family-code` | `--font-family-code` | Changed |

### Font weight

| Old Name                   | New Name                   | Status  |
| -------------------------- | -------------------------- | ------- |
| `--font-weight-regular`    | `--font-weight-regular`    | Renamed |
| `--font-weight-medium`     | `--font-weight-medium`     | Renamed |
| `--font-weight-semi-bold`  | `--font-weight-semi-bold`  | Renamed |
| `--font-weight-bold`       | `--font-weight-bold`       | Renamed |
| `--font-weight-extra-bold` | `--font-weight-extra-bold` | Renamed |

### Font size

| Old Name            | New Name                    | Status  |
| ------------------- | --------------------------- | ------- |
| `--font-size-3xl`   | `--heading-font-size-3xl`   | Renamed |
| `--font-size-2xl`   | `--heading-font-size-2xl`   | Renamed |
| `--font-size-xl`    | `--heading-font-size-xl`    | Renamed |
| `--font-size-large` | `--heading-font-size-large` | Renamed |
| `--font-size-md`    | `--body-font-size-md`       | Renamed |
| `--font-size-sm`    | `--body-font-size-sm`       | Renamed |
| `--font-size-xs`    | `--body-font-size-xs`       | Renamed |

### Line height

| Old Name              | New Name                      | Status  |
| --------------------- | ----------------------------- | ------- |
| `--line-height-3xl`   | `--heading-line-height-3xl`   | Renamed |
| `--line-height-2xl`   | `--heading-line-height-2xl`   | Renamed |
| `--line-height-xl`    | `--heading-line-height-xl`    | Renamed |
| `--line-height-large` | `--heading-line-height-large` | Renamed |
| `--line-height-md`    | `--body-line-height-md`       | Renamed |
| `--line-height-sm`    | `--body-line-height-small`    | Renamed |
| `--line-height-xs`    | `--body-line-height-xs`       | Renamed |

### Paragraph spacing

Unit changed from unitless integers to `px` across the scale. See the breaking-changes summary.

| Old Name                    | New Name                          | Status  |
| --------------------------- | --------------------------------- | ------- |
| `--paragraph-spacing-3xl`   | `--heading-paragraph-space-3xl`   | Changed |
| `--paragraph-spacing-2xl`   | `--heading-paragraph-space-2xl`   | Changed |
| `--paragraph-spacing-xl`    | `--heading-paragraph-space-xl`    | Changed |
| `--paragraph-spacing-large` | `--heading-paragraph-space-large` | Changed |
| `--paragraph-spacing-md`    | `--body-paragraph-space-md`       | Changed |
| `--paragraph-spacing-sm`    | `--body-paragraph-space-small`    | Changed |
| `--paragraph-spacing-xs`    | `--body-paragraph-space-xs`       | Changed |

## 12. Gradients and Markers (legacy `_root.scss` — absent from new file)

| Old Name                              | New Name                                                                         | Status  |
| ------------------------------------- | -------------------------------------------------------------------------------- | ------- |
| `--color-gradient-violet-background`  | `linear-gradient(90deg, #001b48 -0.12%, #312e81 99.86%)`                         | Removed |
| `--color-gradient-violet-transparent` | `linear-gradient(180deg, rgba(30, 89, 215, 0.4) 0%, rgba(0, 27, 72, 0.16) 100%)` | Removed |
| `--color-gradient-blue-light`         | `linear-gradient(116deg, #e9eefc 22.81%, #d6e2ff 99.14%)`                        | Removed |
| `--color-gradient-linear-1`           | `linear-gradient(90deg, #014afb 0%, #152a7e 100%)`                               | Removed |
| `--color-marker-magenta-12`           | `rgba(255, 0, 183, 0.12)`                                                        | Removed |
| `--color-marker-magenta-60`           | `rgba(255, 0, 183, 0.6)`                                                         | Removed |
| `--color-marker-magenta-100`          | `rgba(255, 0, 183)`                                                              | Removed |
| `--color-marker-yellow-100`           | `--color-dap-data-yellow`                                                        | Changed |

---

## 13. Legacy colors (`_colors.scss`)

These tokens come from the older `$colors` Sass map. Where the hex matches a new primitive, we map it; otherwise they are Removed.

### Primary

| Old Name                  | New Name                | Status  |
| ------------------------- | ----------------------- | ------- |
| `--deep-blue` (`#001a47`) | `--color-dap-brand-500` | Renamed |
| `--white` (`#ffffff`)     | `--color-dap-gray-050`  | Renamed |
| `--black` (`#000000`)     | `--color-dap-gray-900`  | Renamed |

### Gradient (Specials)

| Old Name                        | New Name                                                                         | Status  |
| ------------------------------- | -------------------------------------------------------------------------------- | ------- |
| `--gradient-violet-background`  | `linear-gradient(90deg, #001b48 -0.12%, #312e81 99.86%)`                         | Removed |
| `--gradient-violet-transparent` | `linear-gradient(180deg, rgba(30, 89, 215, 0.4) 0%, rgba(0, 27, 72, 0.16) 100%)` | Removed |
| `--gradient-blue-light`         | `linear-gradient(116deg, #e9eefc 22.81%, #d6e2ff 99.14%)`                        | Removed |

### Neutral

| Old Name                   | New Name               | Status  |
| -------------------------- | ---------------------- | ------- |
| `--neutral-1` (`#2c3a4b`)  | `--color-dap-gray-700` | Renamed |
| `--neutral-2` (`#4c5f76`)  | `--color-dap-gray-600` | Renamed |
| `--neutral-3` (`#6c7b8e`)  | `--color-dap-gray-500` | Changed |
| `--neutral-4` (`#8b98a6`)  | `--color-dap-gray-500` | Renamed |
| `--neutral-5` (`#a0aab7`)  | `--color-dap-gray-400` | Changed |
| `--neutral-6` (`#b3bfd1`)  | `--color-dap-gray-400` | Changed |
| `--neutral-7` (`#c2cedf`)  | `--color-dap-gray-300` | Changed |
| `--neutral-8` (`#dee6f1`)  | `--color-dap-gray-200` | Changed |
| `--neutral-9` (`#eaecef`)  | `--color-dap-gray-200` | Changed |
| `--neutral-10` (`#f3f5fa`) | `--color-dap-gray-100` | Renamed |
| `--neutral-bg` (`#dae2f4`) | `--color-dap-gray-200` | Changed |

### Action

| Old Name                            | New Name                     | Status  |
| ----------------------------------- | ---------------------------- | ------- |
| `--action-cta1` (`#0064fa`)         | `--color-dap-blue-600`       | Changed |
| `--action-cta2` (`#579aff`)         | `--color-dap-blue-400`       | Changed |
| `--action-cta3` (`#6b7b8e`)         | `--color-dap-brand-300`      | Changed |
| `--action-active-light` (`#c7d8ff`) | `--color-dap-purple-200`     | Changed |
| `--action-hover-light` (`#e0eaff`)  | `--background-active-status` | Changed |
| `--action-disabled` (`#b0b4bf`)     | `--color-dap-gray-400`       | Changed |

### System Status

| Old Name                                  | New Name                        | Status  |
| ----------------------------------------- | ------------------------------- | ------- |
| `--system-status-ok` (`#067a00`)          | `--color-dap-data-green`        | Changed |
| `--system-status-info` (`#005dcf`)        | `--color-dap-blue-600`          | Changed |
| `--system-status-error` (`#d70a00`)       | `--background-error`            | Changed |
| `--system-status-alert` (`#d04d07`)       | `--color-dap-orange-600`        | Changed |
| `--system-status-in-progress` (`#1513da`) | `--color-dap-blue-800`          | Changed |
| `--system-status-pending` (`#8836d6`)     | `--color-dap-data-deep-fuchsia` | Changed |

### Utility

| Old Name                            | New Name                       | Status  |
| ----------------------------------- | ------------------------------ | ------- |
| `--utility-ok` (`#d6f5cf`)          | `--color-dap-data-light-green` | Changed |
| `--utility-info` (`#c4dcff`)        | `--color-dap-blue-200`         | Changed |
| `--utility-error` (`#ffd8d6`)       | `--color-dap-red-100`          | Changed |
| `--utility-alert` (`#ffeac0`)       | `--schema-light-amber`         | Changed |
| `--utility-in-progress` (`#d9d9ff`) | `--schema-light-blue`          | Changed |
| `--utility-pending` (`#ede2ff`)     | `--background-pending`         | Changed |
| `--utility-disabled` (`#e3e3e3`)    | `--color-dap-gray-200`         | Changed |

### Map / Scheme

| Old Name                                             | New Name                        | Status  |
| ---------------------------------------------------- | ------------------------------- | ------- |
| `--map-and-scheme-line-layer-0` (`#5452f6`)          | `--color-dap-purple-500`        | Changed |
| `--map-and-scheme-line-layer-01` (`#cc4c5f`)         | `--color-dap-data-rose`         | Changed |
| `--map-and-scheme-line-layer-02` (`#00894d`)         | `--color-dap-green-600`         | Changed |
| `--map-and-scheme-line-layer-1-optical` (`#3b8edb`)  | `--color-dap-blue-400`          | Changed |
| `--map-and-scheme-line-layer-2-ethernet` (`#00b8d9`) | `--at-t-brand`                  | Changed |
| `--map-and-scheme-line-layer-3-routing` (`#9a55e0`)  | `--color-dap-data-deep-fuchsia` | Changed |
| `--map-and-scheme-service-overlay` (`#c30ede`)       | `--color-dap-data-fuchsia`      | Changed |
| `--map-and-scheme-pops-core-zones` (`#a92a00`)       | `--color-dap-orange-700`        | Changed |
| `--map-and-scheme-line-optional-1` (`#da7b2d`)       | `--color-dap-orange-400`        | Changed |

---

## 14. Spacing

### Numeric scale (`_spacing.scss`) — remapped to t-shirt sizes

The whole `--spacing-*` numeric scale is gone; usages are remapped to the
named t-shirt scale below. Two values (`40px`, `1px`) have no t-shirt
equivalent and are inlined verbatim.

| Old Name                 | New Name     | Status  |
| ------------------------ | ------------ | ------- |
| `--base-unit`            | `--3xs`      | Renamed |
| `--spacing-1`            | `--3xs`      | Renamed |
| `--spacing-2`            | `--xs`       | Renamed |
| `--spacing-3`            | `--sm`       | Renamed |
| `--spacing-4`            | `--standard` | Renamed |
| `--spacing-5`            | `--md`       | Renamed |
| `--spacing-6`            | `--lg`       | Renamed |
| `--spacing-8`            | `--xl`       | Renamed |
| `--spacing-10`           | `40px`       | Removed |
| `--spacing-12`           | `--2xl`      | Renamed |
| `--spacing-16`           | `--3xl`      | Renamed |
| `--spacing-20`           | `--4xl`      | Changed |
| `--spacing-24`           | `--5xl`      | Renamed |
| `--spacing-base`         | `--standard` | Renamed |
| `--exo-spacing-standard` | `--standard` | Renamed |
| `--exo-spacing-small`    | `--sm`       | Renamed |
| `--exo-spacing-x-small`  | `--xs`       | Renamed |
| `--exo-spacing-2x-small` | `--3xs`      | Renamed |
| `--exo-spacing-4x-small` | `1px`        | Removed |

### Named scale (T-shirt sizes) — renamed (prefix dropped)

| Old Name             | New Name     | Status  |
| -------------------- | ------------ | ------- |
| `--spacing-none`     | `--none`     | Renamed |
| `--spacing-4xs`      | `--4xs`      | Renamed |
| `--spacing-3xs`      | `--3xs`      | Renamed |
| `--spacing-2xs`      | `--2xs`      | Renamed |
| `--spacing-xs`       | `--xs`       | Renamed |
| `--spacing-sm`       | `--sm`       | Renamed |
| `--spacing-standard` | `--standard` | Renamed |
| `--spacing-md`       | `--md`       | Renamed |
| `--spacing-lg`       | `--lg`       | Renamed |
| `--spacing-xl`       | `--xl`       | Renamed |
| `--spacing-2xl`      | `--2xl`      | Renamed |
| `--spacing-3xl`      | `--3xl`      | Renamed |
| `--spacing-4xl`      | `--4xl`      | Renamed |
| `--spacing-5xl`      | `--5xl`      | Renamed |

---

## Summary of value changes (breaking)

Every row tagged `Changed` in the tables above. Visual retesting is required wherever these tokens are consumed.

### Primitive / Alias colors

| Token (old → new)                         | Old value | New value |
| ----------------------------------------- | --------- | --------- |
| `--color-red-100` → `--color-dap-red-100` | `#f5c2bf` | `#ffd2cf` |
| `--color-negative-100` → `--negative-100` | `#f5c2bf` | `#ffd2cf` |

### Semantic: Background / Border / Font / Icon / Status

| Token (old → new)                                                 | Old value             | New value             |
| ----------------------------------------------------------------- | --------------------- | --------------------- |
| `--color-background-success` → `--background-success`             | `#ecfdf6` (green-50)  | `#d9f7e9` (green-075) |
| `--color-background-warning-weak` → `--color-dap-red-050`         | `#ffedf0` (red-50)    | `#ffedf0` (red-050)   |
| `--color-border-disabled` → `--border-disabled`                   | `#e5e8ed` (brand-050) | `#c7c9cc` (gray-300)  |
| `--color-border-danger-weak` → `--border-error-weak`              | `#f5c2bf` (red-100)   | `#ffd2cf` (red-100)   |
| `--color-font-disabled` → `--font-disabled`                       | `#8b98a6` (gray-500)  | `#c7c9cc` (gray-300)  |
| `--color-icon-danger` → `--icon-error`                            | `#d70a00` (red-400)   | `#a10800` (red-500)   |
| `--color-status-bg-success` → `--status-bg-success`               | `#ecfdf6` (green-50)  | `#d9f7e9` (green-075) |
| `--color-status-bg-failed` → `--background-error-secondary-hover` | `#ffedf0` (red-50)    | `#ffedf0` (red-050)   |

> `--color-background-warning-weak` and `--color-status-bg-failed` resolve to the same hex before and after, but their semantic role changed (no longer aliased through the deprecated `negative-050` / `background-danger-weak` chain). Listed here for completeness.

### Typography

| Token (old → new)                                 | Old value                | New value       |
| ------------------------------------------------- | ------------------------ | --------------- |
| `--font-family-base`                              | `'Roboto', sans-serif`   | `Roboto`        |
| `--font-family-code`                              | `'Fira Mono', monospace` | `Fira Mono`     |
| `--paragraph-spacing-*` → `--*-paragraph-space-*` | unitless (e.g. `8`)      | px (e.g. `8px`) |

### Markers (legacy `_root.scss`)

| Token (old → new)                                       | Old value | New value |
| ------------------------------------------------------- | --------- | --------- |
| `--color-marker-yellow-100` → `--color-dap-data-yellow` | `#fff500` | `#ffc107` |

### Legacy `_colors.scss` — Neutrals

| Token (old → new)                       | Old value | New value |
| --------------------------------------- | --------- | --------- |
| `--neutral-3` → `--color-dap-gray-500`  | `#6c7b8e` | `#8b98a6` |
| `--neutral-5` → `--color-dap-gray-400`  | `#a0aab7` | `#a5afba` |
| `--neutral-6` → `--color-dap-gray-400`  | `#b3bfd1` | `#a5afba` |
| `--neutral-7` → `--color-dap-gray-300`  | `#c2cedf` | `#c7c9cc` |
| `--neutral-8` → `--color-dap-gray-200`  | `#dee6f1` | `#e5e8ed` |
| `--neutral-9` → `--color-dap-gray-200`  | `#eaecef` | `#e5e8ed` |
| `--neutral-bg` → `--color-dap-gray-200` | `#dae2f4` | `#e5e8ed` |

### Legacy `_colors.scss` — Action

| Token (old → new)                                     | Old value | New value            |
| ----------------------------------------------------- | --------- | -------------------- |
| `--action-cta1` → `--color-dap-blue-600`              | `#0064fa` | `#0661ff`            |
| `--action-cta2` → `--color-dap-blue-400`              | `#579aff` | `#48aaff`            |
| `--action-cta3` → `--color-dap-brand-300`             | `#6b7b8e` | `#667691`            |
| `--action-active-light` → `--color-dap-purple-200`    | `#c7d8ff` | `#c5d0f8`            |
| `--action-hover-light` → `--background-active-status` | `#e0eaff` | `#e1efff` (blue-075) |
| `--action-disabled` → `--color-dap-gray-400`          | `#b0b4bf` | `#a5afba`            |

### Legacy `_colors.scss` — System Status

| Token (old → new)                                           | Old value | New value           |
| ----------------------------------------------------------- | --------- | ------------------- |
| `--system-status-ok` → `--color-dap-data-green`             | `#067a00` | `#43a047`           |
| `--system-status-info` → `--color-dap-blue-600`             | `#005dcf` | `#0661ff`           |
| `--system-status-error` → `--background-error`              | `#d70a00` | `#d70a00` (red-400) |
| `--system-status-alert` → `--color-dap-orange-600`          | `#d04d07` | `#c9510c`           |
| `--system-status-in-progress` → `--color-dap-blue-800`      | `#1513da` | `#083bc5`           |
| `--system-status-pending` → `--color-dap-data-deep-fuchsia` | `#8836d6` | `#9c27b0`           |

> `--system-status-error` resolves to the same hex before and after; flagged `Changed` because the role moved from a primitive to the semantic `--background-error`.

### Legacy `_colors.scss` — Utility

| Token (old → new)                               | Old value | New value |
| ----------------------------------------------- | --------- | --------- |
| `--utility-ok` → `--color-dap-data-light-green` | `#d6f5cf` | `#d9ecda` |
| `--utility-info` → `--color-dap-blue-200`       | `#c4dcff` | `#b5ddff` |
| `--utility-error` → `--color-dap-red-100`       | `#ffd8d6` | `#ffd2cf` |
| `--utility-alert` → `--schema-light-amber`      | `#ffeac0` | `#ffeacc` |
| `--utility-in-progress` → `--schema-light-blue` | `#d9d9ff` | `#d7daf7` |
| `--utility-pending` → `--background-pending`    | `#ede2ff` | `#e6e9fb` |
| `--utility-disabled` → `--color-dap-gray-200`   | `#e3e3e3` | `#e5e8ed` |

### Legacy `_colors.scss` — Map / Scheme

| Token (old → new)                                                         | Old value | New value |
| ------------------------------------------------------------------------- | --------- | --------- |
| `--map-and-scheme-line-layer-0` → `--color-dap-purple-500`                | `#5452f6` | `#4a61e3` |
| `--map-and-scheme-line-layer-01` → `--color-dap-data-rose`                | `#cc4c5f` | `#d81b60` |
| `--map-and-scheme-line-layer-02` → `--color-dap-green-600`                | `#00894d` | `#028764` |
| `--map-and-scheme-line-layer-1-optical` → `--color-dap-blue-400`          | `#3b8edb` | `#48aaff` |
| `--map-and-scheme-line-layer-2-ethernet` → `--at-t-brand`                 | `#00b8d9` | `#00a8e0` |
| `--map-and-scheme-line-layer-3-routing` → `--color-dap-data-deep-fuchsia` | `#9a55e0` | `#9c27b0` |
| `--map-and-scheme-service-overlay` → `--color-dap-data-fuchsia`           | `#c30ede` | `#d602ee` |
| `--map-and-scheme-pops-core-zones` → `--color-dap-orange-700`             | `#a92a00` | `#9a3412` |
| `--map-and-scheme-line-optional-1` → `--color-dap-orange-400`             | `#da7b2d` | `#fc9947` |

### Spacing (numeric → t-shirt)

| Token (old → new)        | Old value | New value | Δ        |
| ------------------------ | --------- | --------- | -------- |
| `--spacing-20` → `--4xl` | `80px`    | `72px`    | **−8px** |