Skip to content

Commit c5f2e07

Browse files
committed
Merge branch 'main' of github.com:drivenets/design-system into drivenets/michal/AR-53409-remove-ds-system-status
2 parents 5028ada + ef1db6b commit c5f2e07

20 files changed

Lines changed: 541 additions & 237 deletions

.changeset/lazy-trees-attend.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@drivenets/design-system': patch
3+
---
4+
5+
Add `width` (responsive), `style` props to DsPanel

.changeset/strong-toys-repair.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@drivenets/eslint-plugin-design-system': patch
3+
---
4+
5+
Refine the JSX reporting range to reduce noise

.changeset/two-pots-jog.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@drivenets/design-system': patch
3+
---
4+
5+
Make `columns` prop in `DsDrawer` responsive
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { useState } from 'react';
2+
import { describe, expect, it } from 'vitest';
3+
import { page } from 'vitest/browser';
4+
import DsDrawer from '../ds-drawer';
5+
import type { DsDrawerProps } from '../ds-drawer.types';
6+
7+
const ControlledDrawer = (props: Partial<DsDrawerProps>) => {
8+
const [open, setOpen] = useState(false);
9+
10+
return (
11+
<>
12+
<button type="button" onClick={() => setOpen(true)}>
13+
Open Drawer
14+
</button>
15+
<DsDrawer open={open} onOpenChange={setOpen} {...props}>
16+
<DsDrawer.Header>
17+
<DsDrawer.Title>Test Drawer</DsDrawer.Title>
18+
<DsDrawer.CloseTrigger />
19+
</DsDrawer.Header>
20+
<DsDrawer.Body>{props.children ?? <p>Body content</p>}</DsDrawer.Body>
21+
</DsDrawer>
22+
</>
23+
);
24+
};
25+
26+
describe('DsDrawer', () => {
27+
it('should open and close', async () => {
28+
await page.render(<ControlledDrawer />);
29+
30+
await page.getByRole('button', { name: /open drawer/i }).click();
31+
32+
const drawer = page.getByRole('dialog');
33+
await expect.element(drawer).toHaveAttribute('data-state', 'open');
34+
35+
await page.getByRole('button', { name: /close/i }).click();
36+
await expect.element(drawer).toHaveAttribute('data-state', 'closed');
37+
});
38+
39+
it('should render backdrop when enabled', async () => {
40+
await page.render(<ControlledDrawer backdrop />);
41+
42+
await page.getByRole('button', { name: /open drawer/i }).click();
43+
44+
const drawer = page.getByRole('dialog');
45+
await expect.element(drawer).toHaveAttribute('data-state', 'open');
46+
47+
const backdrop = document.querySelector<HTMLElement>('[data-part="backdrop"]');
48+
await expect.element(backdrop).toBeInTheDocument();
49+
await expect.element(backdrop).toHaveAttribute('data-state', 'open');
50+
});
51+
});

packages/design-system/src/components/ds-drawer/ds-drawer.stories.module.scss

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,13 @@
8585
padding: 2rem;
8686
}
8787

88+
.responsiveButtons {
89+
display: flex;
90+
flex-wrap: wrap;
91+
gap: var(--spacing-xs);
92+
margin-top: var(--spacing-sm);
93+
}
94+
8895
.contentSection {
8996
padding: 1rem;
9097
}

packages/design-system/src/components/ds-drawer/ds-drawer.stories.tsx

Lines changed: 67 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import type { Meta, StoryObj } from '@storybook/react-vite';
22
import { useState } from 'react';
33
import classNames from 'classnames';
4-
import { expect, userEvent, within } from 'storybook/test';
54
import DsDrawer from './ds-drawer';
65
import { DsButton } from '../ds-button';
76
import { DsTextInput } from '../ds-text-input';
87
import { DsIcon } from '../ds-icon';
98
import { DsStatusBadge } from '../ds-status-badge';
109
import styles from './ds-drawer.stories.module.scss';
1110
import { DsTypography } from '../ds-typography';
12-
import type { DsDrawerProps } from './ds-drawer.types';
11+
import type { DsDrawerColumns, DsDrawerProps } from './ds-drawer.types';
12+
import type { ResponsiveValue } from '../../utils/responsive';
1313

1414
const meta: Meta<typeof DsDrawer> = {
1515
title: 'Design System/Drawer',
@@ -136,20 +136,6 @@ export const Default: Story = {
136136
</>
137137
),
138138
},
139-
play: async ({ canvasElement }) => {
140-
const canvas = within(canvasElement);
141-
142-
const openButton = canvas.getByRole('button', { name: /open drawer/i });
143-
await userEvent.click(openButton);
144-
145-
const drawer = await canvas.findByRole('dialog');
146-
await expect(drawer).toHaveAttribute('data-state', 'open');
147-
148-
const closeButton = canvas.getByRole('button', { name: /close/i });
149-
await userEvent.click(closeButton);
150-
151-
await expect(drawer).toHaveAttribute('data-state', 'closed');
152-
},
153139
};
154140

155141
const Tabs = ({ total = 4 }: { total?: number }) => {
@@ -253,19 +239,6 @@ export const WithBackdropAndScroll: Story = {
253239
</>
254240
),
255241
},
256-
play: async ({ canvasElement }) => {
257-
const canvas = within(canvasElement);
258-
259-
const openButton = canvas.getByRole('button', { name: /open drawer/i });
260-
await userEvent.click(openButton);
261-
262-
const drawer = await canvas.findByRole('dialog');
263-
await expect(drawer).toHaveAttribute('data-state', 'open');
264-
265-
const backdrop = canvasElement.querySelector('[data-part="backdrop"]');
266-
await expect(backdrop).toBeInTheDocument();
267-
await expect(backdrop).toHaveAttribute('data-state', 'open');
268-
},
269242
};
270243

271244
export const DockToStart: Story = {
@@ -345,6 +318,71 @@ export const WithGridContent: Story = {
345318
},
346319
};
347320

321+
/**
322+
* Right drawer responsiveness: the columns prop accepts a responsive value `{ lg, md }`.
323+
* On screens < 1440px the drawer automatically switches to the `md` column count.
324+
*
325+
* Recommended responsive mappings for end-positioned drawers:
326+
* - 3 cols → `{ lg: 3, md: 4 }`
327+
* - 4–5 cols → `{ lg: 4, md: 6 }` / `{ lg: 5, md: 6 }`
328+
* - 6+ cols → `{ lg: 6, md: 10 }` (up to 10)
329+
*/
330+
export const Responsive: Story = {
331+
render: function Render() {
332+
const [openDrawer, setOpenDrawer] = useState<string | null>(null);
333+
334+
const close = () => setOpenDrawer(null);
335+
336+
const variants = [
337+
{ label: '3 cols → 4 on md', columns: { lg: 3, md: 4 } },
338+
{ label: '4 cols → 6 on md', columns: { lg: 4, md: 6 } },
339+
{ label: '5 cols → 6 on md', columns: { lg: 5, md: 6 } },
340+
{ label: '6 cols → 10 on md', columns: { lg: 6, md: 10 } },
341+
{ label: '8 cols → 10 on md', columns: { lg: 8, md: 10 } },
342+
] satisfies Array<{ label: string; columns: ResponsiveValue<DsDrawerColumns> }>;
343+
344+
return (
345+
<div className={styles.storyWrapper}>
346+
<DsTypography variant="body-md-semi-bold">
347+
Resize the window below 1440 px to see the responsive column change.
348+
</DsTypography>
349+
350+
<div className={styles.responsiveButtons}>
351+
{variants.map(({ label }) => (
352+
<DsButton key={label} onClick={() => setOpenDrawer(label)}>
353+
{label}
354+
</DsButton>
355+
))}
356+
</div>
357+
358+
{variants.map(({ label, columns }) => (
359+
<DsDrawer
360+
key={label}
361+
open={openDrawer === label}
362+
onOpenChange={(open) => !open && close()}
363+
columns={columns}
364+
>
365+
<DsDrawer.Header>
366+
<DsDrawer.Title>{label}</DsDrawer.Title>
367+
<DsDrawer.CloseTrigger />
368+
</DsDrawer.Header>
369+
<DsDrawer.Body className={styles.body}>
370+
<div className={styles.section}>
371+
<DsTypography className={styles.sectionHeader} variant="body-md-semi-bold">
372+
lg: {columns.lg} columns · md: {columns.md} columns
373+
</DsTypography>
374+
<DsTypography variant="heading2" className={styles.sectionContent}>
375+
Drawer content
376+
</DsTypography>
377+
</div>
378+
</DsDrawer.Body>
379+
</DsDrawer>
380+
))}
381+
</div>
382+
);
383+
},
384+
};
385+
348386
export const ToggleFullSize: Story = {
349387
args: {
350388
columns: 4,

packages/design-system/src/components/ds-drawer/ds-drawer.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,8 @@ import type { DsDrawerProps } from './ds-drawer.types';
66
import styles from './ds-drawer.module.scss';
77
import { DsIcon } from '../ds-icon';
88
import { DsTypography } from '../ds-typography';
9+
import { useResponsiveValue } from '../../utils/responsive';
910

10-
/**
11-
* Composable drawer component.
12-
* Supports grid-based sizing, flexible positioning, and compound components.
13-
* Use columns prop to control drawer width (1-12 grid columns).
14-
*/
1511
const DsDrawer = ({
1612
open,
1713
onOpenChange,
@@ -25,9 +21,12 @@ const DsDrawer = ({
2521
className,
2622
children,
2723
}: DsDrawerProps) => {
24+
const resolvedColumns = useResponsiveValue(columns);
25+
2826
const handleOpenChange = (details: { open: boolean }) => {
2927
onOpenChange(details.open);
3028
};
29+
3130
const Wrapper = portal ? Portal : Fragment;
3231

3332
return (
@@ -48,9 +47,8 @@ const DsDrawer = ({
4847
styles.drawer,
4948
styles[`position-${position}`],
5049
className,
51-
5250
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
53-
styles[`cols-${columns}`],
51+
styles[`cols-${resolvedColumns}`],
5452
)}
5553
>
5654
<div className={styles.content}>{children}</div>

packages/design-system/src/components/ds-drawer/ds-drawer.types.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { CSSProperties, ReactNode } from 'react';
22
import type { UseDialogProps as DialogProps } from '@ark-ui/react/dialog';
3+
import type { ResponsiveValue } from '../../utils/responsive';
34

45
/**
56
* Available positions for the drawer
@@ -20,10 +21,11 @@ export interface DsDrawerProps extends Pick<DialogProps, 'closeOnEscape' | 'clos
2021
*/
2122
onOpenChange: (open: boolean) => void;
2223
/**
23-
* Number of grid columns the drawer should span (1-12)
24+
* Number of grid columns the drawer should span (1-12).
25+
* Accepts a responsive value, e.g. `{ lg: 4, md: 6 }`.
2426
* @default 4
2527
*/
26-
columns?: DsDrawerColumns;
28+
columns?: ResponsiveValue<DsDrawerColumns>;
2729
/**
2830
* Position of the drawer
2931
* @default 'end'

0 commit comments

Comments
 (0)