Skip to content

Commit 4e06a3e

Browse files
✨(ats-presentation) add drawer component
1 parent b3c44cd commit 4e06a3e

2 files changed

Lines changed: 572 additions & 0 deletions

File tree

Lines changed: 320 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,320 @@
1+
import type { ComponentPropsAndSlots, StoryObj } from '@storybook/vue3'
2+
import { ref, watch } from 'vue'
3+
import CspButton from '@/components/base/CspButton/CspButton.vue'
4+
import CspDrawer from '@/components/base/CspDrawer/CspDrawer.vue'
5+
6+
type CspDrawerProps = ComponentPropsAndSlots<typeof CspDrawer>
7+
8+
const meta = {
9+
title: 'Éléments/Génériques/CspDrawer',
10+
component: CspDrawer,
11+
tags: ['autodocs'],
12+
parameters: {
13+
controls: {
14+
include: ['open', 'defaultOpen', 'modal', 'side', 'size', 'title', 'description', 'ariaLabel', 'showClose', 'closeLabel'],
15+
},
16+
docs: {
17+
description: {
18+
component: 'Tiroir générique (panneau latéral)',
19+
},
20+
},
21+
},
22+
argTypes: {
23+
open: {
24+
control: { type: 'boolean' },
25+
description: 'État d\'ouverture contrôlé. Liez avec `v-model:open`.',
26+
table: {
27+
type: {
28+
summary: 'boolean',
29+
},
30+
},
31+
},
32+
defaultOpen: {
33+
control: { type: 'boolean' },
34+
description: 'État d\'ouverture initial non contrôlé (utilisez quand `open` n\'est pas contrôlé).',
35+
table: {
36+
type: {
37+
summary: 'boolean',
38+
},
39+
defaultValue: {
40+
summary: 'false',
41+
},
42+
},
43+
},
44+
modal: {
45+
control: { type: 'boolean' },
46+
description: 'Si vrai, capture le focus et désactive les interactions extérieures.',
47+
table: {
48+
type: {
49+
summary: 'boolean',
50+
},
51+
defaultValue: {
52+
summary: 'true',
53+
},
54+
},
55+
},
56+
side: {
57+
control: { type: 'radio' },
58+
options: ['left', 'right'] satisfies NonNullable<CspDrawerProps['side']>[],
59+
description: 'Côté auquel le tiroir est attaché.',
60+
table: {
61+
type: {
62+
summary: 'left | right',
63+
},
64+
defaultValue: {
65+
summary: 'right',
66+
},
67+
},
68+
},
69+
size: {
70+
control: { type: 'radio' },
71+
options: ['xs', 'sm', 'md', 'lg', 'xl', 'full'] satisfies NonNullable<CspDrawerProps['size']>[],
72+
description: 'Preset de largeur du tiroir.',
73+
table: {
74+
type: {
75+
summary: 'xs | sm | md | lg | xl | full',
76+
},
77+
defaultValue: {
78+
summary: 'md',
79+
},
80+
},
81+
},
82+
title: {
83+
control: { type: 'text' },
84+
description: 'Texte du titre (ou utilisez le slot `title`). Recommandé pour l\'accessibilité.',
85+
table: {
86+
type: {
87+
summary: 'string | null',
88+
},
89+
},
90+
},
91+
description: {
92+
control: { type: 'text' },
93+
description: 'Texte de description (ou utilisez le slot `description`).',
94+
table: {
95+
type: {
96+
summary: 'string | null',
97+
},
98+
},
99+
},
100+
ariaLabel: {
101+
control: { type: 'text' },
102+
description: 'Libellé accessible utilisé lorsqu\'aucun titre n\'est fourni.',
103+
table: {
104+
type: {
105+
summary: 'string',
106+
},
107+
},
108+
},
109+
showClose: {
110+
control: { type: 'boolean' },
111+
description: 'Indique s\'il faut afficher un bouton de fermeture dans l\'en-tête.',
112+
table: {
113+
type: {
114+
summary: 'boolean',
115+
},
116+
defaultValue: {
117+
summary: 'true',
118+
},
119+
},
120+
},
121+
closeLabel: {
122+
control: { type: 'text' },
123+
description: 'Libellé accessible du bouton de fermeture.',
124+
table: {
125+
type: {
126+
summary: 'string',
127+
},
128+
defaultValue: {
129+
summary: 'Fermer',
130+
},
131+
},
132+
},
133+
trigger: {
134+
control: false,
135+
table: {
136+
disable: true,
137+
},
138+
},
139+
footer: {
140+
control: false,
141+
table: {
142+
disable: true,
143+
},
144+
},
145+
default: {
146+
control: false,
147+
table: {
148+
disable: true,
149+
},
150+
},
151+
class: {
152+
control: false,
153+
table: {
154+
disable: true,
155+
},
156+
},
157+
style: {
158+
control: false,
159+
table: {
160+
disable: true,
161+
},
162+
},
163+
key: {
164+
control: false,
165+
table: {
166+
disable: true,
167+
},
168+
},
169+
ref: {
170+
control: false,
171+
table: {
172+
disable: true,
173+
},
174+
},
175+
ref_for: {
176+
control: false,
177+
table: {
178+
disable: true,
179+
},
180+
},
181+
ref_key: {
182+
control: false,
183+
table: {
184+
disable: true,
185+
},
186+
},
187+
},
188+
args: {
189+
defaultOpen: false,
190+
modal: true,
191+
side: 'right',
192+
size: 'md',
193+
title: 'Titre du tiroir',
194+
description: 'Informations complémentaires sur ce panneau.',
195+
showClose: true,
196+
closeLabel: 'Fermer',
197+
},
198+
render: (args: CspDrawerProps) => ({
199+
components: { CspButton, CspDrawer },
200+
setup() {
201+
const open = ref(Boolean(args.open))
202+
203+
watch(
204+
() => args.open,
205+
(value) => {
206+
if (value === undefined) {
207+
return
208+
}
209+
210+
open.value = value
211+
},
212+
)
213+
214+
function handleUpdateOpen(value: boolean) {
215+
open.value = value
216+
}
217+
218+
return { args, open, handleUpdateOpen }
219+
},
220+
template: `
221+
<CspDrawer
222+
v-bind="args"
223+
:open="args.open === undefined ? undefined : open"
224+
@update:open="handleUpdateOpen"
225+
>
226+
<template #trigger>
227+
<CspButton
228+
label="Ouvrir le tiroir"
229+
variant="primary"
230+
/>
231+
</template>
232+
233+
<p class="text-sm">
234+
Contenu principal du tiroir, placé dans le slot par défaut
235+
</p>
236+
237+
<div class="h-48" />
238+
239+
<template #footer>
240+
<div class="flex gap-3">
241+
<CspButton
242+
label="Close"
243+
variant="secondary"
244+
@click="handleUpdateOpen(false)"
245+
/>
246+
</div>
247+
</template>
248+
</CspDrawer>
249+
`,
250+
}),
251+
}
252+
253+
export default meta
254+
type Story = StoryObj<CspDrawerProps>
255+
256+
export const Default: Story = {}
257+
258+
export const Controlled: Story = {
259+
args: {
260+
open: false,
261+
},
262+
}
263+
264+
export const Sides: Story = {
265+
render: args => ({
266+
components: { CspDrawer, CspButton },
267+
setup() {
268+
const sides = ['left', 'right'] as const
269+
return { args, sides }
270+
},
271+
template: `
272+
<div class="flex gap-6 flex-wrap">
273+
<CspDrawer
274+
v-for="s in sides"
275+
:key="s"
276+
v-bind="args"
277+
:side="s"
278+
>
279+
<template #trigger>
280+
<CspButton
281+
:label="'Ouvrir (' + s + ')'"
282+
variant="secondary"
283+
/>
284+
</template>
285+
286+
<p class="text-sm">Side: <strong>{{ s }}</strong></p>
287+
</CspDrawer>
288+
</div>
289+
`,
290+
}),
291+
}
292+
293+
export const Sizes: Story = {
294+
render: args => ({
295+
components: { CspDrawer, CspButton },
296+
setup() {
297+
const sizes = ['xs', 'sm', 'md', 'lg', 'xl', 'full'] as const
298+
return { args, sizes }
299+
},
300+
template: `
301+
<div class="flex gap-6 flex-wrap">
302+
<CspDrawer
303+
v-for="s in sizes"
304+
:key="s"
305+
v-bind="args"
306+
:size="s"
307+
>
308+
<template #trigger>
309+
<CspButton
310+
:label="'Ouvrir (' + s + ')'"
311+
variant="secondary"
312+
/>
313+
</template>
314+
315+
<p class="text-sm">Size: <strong>{{ s }}</strong></p>
316+
</CspDrawer>
317+
</div>
318+
`,
319+
}),
320+
}

0 commit comments

Comments
 (0)