@hubspot/cms-component-library 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/components/componentLibrary/Accordion/AccordionContent/ContentFields.tsx +5 -3
  2. package/components/componentLibrary/Accordion/AccordionItem/StyleFields.tsx +5 -3
  3. package/components/componentLibrary/Accordion/AccordionItem/index.module.scss +2 -2
  4. package/components/componentLibrary/Accordion/AccordionItem/index.tsx +3 -3
  5. package/components/componentLibrary/Accordion/AccordionTitle/ContentFields.tsx +5 -3
  6. package/components/componentLibrary/Accordion/AccordionTitle/index.module.scss +2 -2
  7. package/components/componentLibrary/Accordion/stories/Accordion.stories.tsx +80 -1
  8. package/components/componentLibrary/Accordion/stories/AccordionDecorator.tsx +14 -14
  9. package/components/componentLibrary/Button/ContentFields.tsx +5 -3
  10. package/components/componentLibrary/Button/StyleFields.tsx +5 -3
  11. package/components/componentLibrary/Button/index.module.scss +22 -14
  12. package/components/componentLibrary/Button/index.tsx +6 -6
  13. package/components/componentLibrary/Button/stories/Button.AsButton.stories.tsx +30 -1
  14. package/components/componentLibrary/Button/stories/Button.AsLink.stories.tsx +38 -1
  15. package/components/componentLibrary/Button/stories/ButtonDecorator.tsx +1 -1
  16. package/components/componentLibrary/Card/StyleFields.tsx +5 -3
  17. package/components/componentLibrary/Card/stories/Card.stories.tsx +46 -1
  18. package/components/componentLibrary/Card/stories/CardDecorator.tsx +1 -1
  19. package/components/componentLibrary/Divider/ContentFields.tsx +5 -3
  20. package/components/componentLibrary/Divider/StyleFields.tsx +5 -3
  21. package/components/componentLibrary/Divider/index.module.scss +6 -6
  22. package/components/componentLibrary/Divider/index.tsx +7 -3
  23. package/components/componentLibrary/Divider/stories/Divider.stories.tsx +44 -50
  24. package/components/componentLibrary/Divider/stories/{DividerDecorator.module.css → DividerDecorator.module.scss} +5 -4
  25. package/components/componentLibrary/Divider/stories/DividerDecorator.tsx +1 -1
  26. package/components/componentLibrary/Divider/types.ts +3 -1
  27. package/components/componentLibrary/Drawer/hooks/index.tsx +13 -0
  28. package/components/componentLibrary/Drawer/index.module.scss +94 -0
  29. package/components/componentLibrary/Drawer/index.tsx +131 -0
  30. package/components/componentLibrary/Drawer/llm.txt +416 -0
  31. package/components/componentLibrary/Drawer/stories/Drawer.stories.tsx +512 -0
  32. package/components/componentLibrary/Drawer/stories/DrawerDecorator.module.scss +8 -0
  33. package/components/componentLibrary/Drawer/stories/DrawerDecorator.tsx +18 -0
  34. package/components/componentLibrary/Drawer/types.ts +25 -0
  35. package/components/componentLibrary/Flex/stories/FlexDecorator.tsx +1 -1
  36. package/components/componentLibrary/Flex/types.ts +3 -1
  37. package/components/componentLibrary/Grid/stories/Grid.stories.tsx +454 -152
  38. package/components/componentLibrary/Grid/stories/GridDecorator.tsx +2 -2
  39. package/components/componentLibrary/Heading/ContentFields.tsx +5 -3
  40. package/components/componentLibrary/Heading/StyleFields.tsx +11 -9
  41. package/components/componentLibrary/Heading/index.tsx +3 -3
  42. package/components/componentLibrary/Heading/llm.txt +8 -8
  43. package/components/componentLibrary/Heading/stories/Heading.stories.tsx +3 -3
  44. package/components/componentLibrary/Heading/stories/HeadingDecorator.tsx +1 -1
  45. package/components/componentLibrary/Heading/types.ts +4 -4
  46. package/components/componentLibrary/Icon/ContentFields.tsx +5 -3
  47. package/components/componentLibrary/Icon/stories/Icon.stories.tsx +1 -1
  48. package/components/componentLibrary/Icon/stories/IconDecorator.tsx +1 -1
  49. package/components/componentLibrary/Image/ContentFields.tsx +5 -3
  50. package/components/componentLibrary/Image/index.tsx +4 -4
  51. package/components/componentLibrary/Image/llm.txt +17 -17
  52. package/components/componentLibrary/Image/stories/Image.stories.tsx +61 -18
  53. package/components/componentLibrary/Image/stories/ImageDecorator.tsx +1 -1
  54. package/components/componentLibrary/Image/types.ts +2 -2
  55. package/components/componentLibrary/LanguageSwitcher/ContentFields.tsx +18 -0
  56. package/components/componentLibrary/LanguageSwitcher/LanguageOptions.module.scss +37 -0
  57. package/components/componentLibrary/LanguageSwitcher/LanguageOptions.tsx +65 -0
  58. package/components/componentLibrary/LanguageSwitcher/StyleFields.tsx +48 -0
  59. package/components/componentLibrary/LanguageSwitcher/_dummyData.tsx +247 -0
  60. package/components/componentLibrary/LanguageSwitcher/assets/Globe.tsx +16 -0
  61. package/components/componentLibrary/LanguageSwitcher/index.module.scss +58 -0
  62. package/components/componentLibrary/LanguageSwitcher/index.tsx +125 -0
  63. package/components/componentLibrary/LanguageSwitcher/llm.txt +380 -0
  64. package/components/componentLibrary/LanguageSwitcher/stories/LanguageSwitcher.stories.tsx +349 -0
  65. package/components/componentLibrary/LanguageSwitcher/stories/LanguageSwitcherDecorator.module.scss +5 -0
  66. package/components/componentLibrary/LanguageSwitcher/stories/LanguageSwitcherDecorator.tsx +8 -0
  67. package/components/componentLibrary/LanguageSwitcher/types.ts +48 -0
  68. package/components/componentLibrary/LanguageSwitcher/utils.tsx +38 -0
  69. package/components/componentLibrary/Link/ContentFields.tsx +5 -3
  70. package/components/componentLibrary/Link/StyleFields.tsx +5 -3
  71. package/components/componentLibrary/Link/index.module.scss +10 -0
  72. package/components/componentLibrary/Link/index.tsx +24 -14
  73. package/components/componentLibrary/Link/stories/Link.stories.tsx +35 -5
  74. package/components/componentLibrary/Link/stories/LinkDecorator.tsx +11 -1
  75. package/components/componentLibrary/Link/types.ts +22 -13
  76. package/components/componentLibrary/List/ContentFields.tsx +5 -3
  77. package/components/componentLibrary/List/ListItem/ContentFields.tsx +6 -17
  78. package/components/componentLibrary/List/ListItem/index.module.scss +1 -13
  79. package/components/componentLibrary/List/ListItem/index.tsx +3 -30
  80. package/components/componentLibrary/List/ListItem/types.ts +1 -16
  81. package/components/componentLibrary/List/StyleFields.tsx +15 -18
  82. package/components/componentLibrary/List/index.module.scss +3 -0
  83. package/components/componentLibrary/List/index.tsx +5 -2
  84. package/components/componentLibrary/List/llm.txt +73 -103
  85. package/components/componentLibrary/List/stories/List.stories.tsx +56 -80
  86. package/components/componentLibrary/List/stories/ListDecorator.tsx +3 -6
  87. package/components/componentLibrary/List/types.ts +1 -3
  88. package/components/componentLibrary/Logo/_dummyLogoData.ts +12 -0
  89. package/components/componentLibrary/Logo/assets/hubspot-logo.png +0 -0
  90. package/components/componentLibrary/Logo/index.module.scss +22 -0
  91. package/components/componentLibrary/Logo/index.tsx +73 -0
  92. package/components/componentLibrary/Logo/llm.txt +262 -0
  93. package/components/componentLibrary/Logo/stories/Logo.stories.tsx +88 -0
  94. package/components/componentLibrary/Logo/stories/LogoDecorator.module.scss +10 -0
  95. package/components/componentLibrary/Logo/stories/LogoDecorator.tsx +8 -0
  96. package/components/componentLibrary/Logo/types.tsx +16 -0
  97. package/components/componentLibrary/Menu/ContentFields.tsx +16 -0
  98. package/components/componentLibrary/Menu/MenuItem/Chevron/index.module.scss +6 -0
  99. package/components/componentLibrary/Menu/MenuItem/Chevron/index.tsx +17 -0
  100. package/components/componentLibrary/Menu/MenuItem/index.module.scss +7 -0
  101. package/components/componentLibrary/Menu/MenuItem/index.tsx +266 -0
  102. package/components/componentLibrary/Menu/MenuItem/types.ts +17 -0
  103. package/components/componentLibrary/Menu/NavigationMenu/ContentFields.tsx +20 -0
  104. package/components/componentLibrary/Menu/NavigationMenu/index.tsx +18 -0
  105. package/components/componentLibrary/Menu/NavigationMenu/islands/NavigationMenuIsland.tsx +95 -0
  106. package/components/componentLibrary/Menu/NavigationMenu/islands/index.module.scss +100 -0
  107. package/components/componentLibrary/Menu/NavigationMenu/islands/types.ts +19 -0
  108. package/components/componentLibrary/Menu/NavigationMenu/llm.txt +197 -0
  109. package/components/componentLibrary/Menu/NavigationMenu/stories/NavigationMenu.stories.tsx +286 -0
  110. package/components/componentLibrary/Menu/NavigationMenu/stories/NavigationMenuDecorator.module.scss +15 -0
  111. package/components/componentLibrary/Menu/NavigationMenu/stories/NavigationMenuDecorator.tsx +12 -0
  112. package/components/componentLibrary/Menu/NavigationMenu/types.ts +3 -0
  113. package/components/componentLibrary/Menu/VerticalMenu/ContentFields.tsx +20 -0
  114. package/components/componentLibrary/Menu/VerticalMenu/index.tsx +18 -0
  115. package/components/componentLibrary/Menu/VerticalMenu/islands/index.module.scss +53 -0
  116. package/components/componentLibrary/Menu/VerticalMenu/islands/verticalMenuIsland.tsx +78 -0
  117. package/components/componentLibrary/Menu/VerticalMenu/llm.txt +177 -0
  118. package/components/componentLibrary/Menu/VerticalMenu/stories/VerticalMenu.stories.tsx +242 -0
  119. package/components/componentLibrary/Menu/VerticalMenu/stories/VerticalMenuDecorator.module.scss +19 -0
  120. package/components/componentLibrary/Menu/VerticalMenu/stories/VerticalMenuDecorator.tsx +12 -0
  121. package/components/componentLibrary/Menu/VerticalMenu/types.ts +21 -0
  122. package/components/componentLibrary/Menu/_dummyMenuData.js +1346 -0
  123. package/components/componentLibrary/Menu/types.ts +56 -0
  124. package/components/componentLibrary/Menu/utils/transformMenuData.ts +11 -0
  125. package/components/componentLibrary/_patterns/README.md +15 -17
  126. package/components/componentLibrary/_patterns/checklist-and-examples.md +17 -17
  127. package/components/componentLibrary/_patterns/component-structure.md +21 -23
  128. package/components/componentLibrary/_patterns/css-patterns.md +170 -18
  129. package/components/componentLibrary/_patterns/field-patterns.md +97 -27
  130. package/components/componentLibrary/_patterns/function-declaration-patterns.md +281 -0
  131. package/components/componentLibrary/_patterns/llm-txt.template.md +4 -2
  132. package/components/componentLibrary/_patterns/prop-naming-patterns.md +208 -0
  133. package/components/componentLibrary/_patterns/storybook-patterns.md +25 -8
  134. package/components/componentLibrary/_patterns/typescript-patterns.md +6 -3
  135. package/package.json +4 -2
  136. /package/components/componentLibrary/Button/stories/{ButtonDecorator.module.css → ButtonDecorator.module.scss} +0 -0
  137. /package/components/componentLibrary/Card/stories/{CardDecorator.module.css → CardDecorator.module.scss} +0 -0
  138. /package/components/componentLibrary/Flex/stories/{FlexDecorator.module.css → FlexDecorator.module.scss} +0 -0
  139. /package/components/componentLibrary/Grid/stories/{GridDecorator.module.css → GridDecorator.module.scss} +0 -0
  140. /package/components/componentLibrary/Heading/stories/{HeadingDecorator.module.css → HeadingDecorator.module.scss} +0 -0
  141. /package/components/componentLibrary/Icon/stories/{IconDecorator.module.css → IconDecorator.module.scss} +0 -0
  142. /package/components/componentLibrary/Image/stories/{ImageDecorator.module.css → ImageDecorator.module.scss} +0 -0
  143. /package/components/componentLibrary/Image/stories/assets/{catSmile.jpg → cat-smile.jpg} +0 -0
@@ -0,0 +1,512 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import Drawer, { useDrawer } from '../index.js';
3
+ import { DrawerProps } from '../types.js';
4
+ import { withDrawerStyles, buttonStyle } from './DrawerDecorator.js';
5
+ import { SBContainer } from '@sb-utils/SBContainer.js';
6
+ import type { CSSVariables } from '../../utils/types.js';
7
+
8
+ const meta: Meta<DrawerProps> = {
9
+ title: 'Component Library/Drawer',
10
+ component: Drawer,
11
+ parameters: {
12
+ layout: 'fullscreen',
13
+ docs: {
14
+ description: {
15
+ component: `The Drawer component provides a slide-out panel that overlays content from the side, top, or bottom of the viewport. It supports customizable directions, sizes, overlay control, and full-screen mode. Use the \`useDrawer\` hook for state management.`,
16
+ },
17
+ story: {
18
+ inline: false,
19
+ iframeHeight: 500,
20
+ },
21
+ },
22
+ },
23
+ decorators: [withDrawerStyles],
24
+ };
25
+
26
+ export default meta;
27
+ type Story = StoryObj<typeof meta>;
28
+
29
+ export const Default: Story = {
30
+ render: () => {
31
+ const { isOpen, open, close } = useDrawer();
32
+
33
+ return (
34
+ <SBContainer flex direction="column" gap="medium">
35
+ <button type="button" style={buttonStyle} onClick={open}>
36
+ Open Drawer
37
+ </button>
38
+ <Drawer
39
+ open={isOpen}
40
+ direction="right"
41
+ onOverlayClick={close}
42
+ aria-label="Default drawer"
43
+ >
44
+ <SBContainer padding="large">
45
+ <h2>Drawer Content</h2>
46
+ <p>This is the default drawer sliding in from the right.</p>
47
+ <button type="button" style={buttonStyle} onClick={close}>
48
+ Close
49
+ </button>
50
+ </SBContainer>
51
+ </Drawer>
52
+ </SBContainer>
53
+ );
54
+ },
55
+ };
56
+
57
+ export const Directions: Story = {
58
+ name: 'Direction Options',
59
+ render: () => {
60
+ const rightDrawer = useDrawer();
61
+ const leftDrawer = useDrawer();
62
+ const topDrawer = useDrawer();
63
+ const bottomDrawer = useDrawer();
64
+
65
+ return (
66
+ <SBContainer flex direction="column" gap="large">
67
+ <SBContainer addBackground flex direction="row" gap="medium">
68
+ <button type="button" style={buttonStyle} onClick={rightDrawer.open}>
69
+ Right Drawer
70
+ </button>
71
+ <button type="button" style={buttonStyle} onClick={leftDrawer.open}>
72
+ Left Drawer
73
+ </button>
74
+ <button type="button" style={buttonStyle} onClick={topDrawer.open}>
75
+ Top Drawer
76
+ </button>
77
+ <button type="button" style={buttonStyle} onClick={bottomDrawer.open}>
78
+ Bottom Drawer
79
+ </button>
80
+ </SBContainer>
81
+
82
+ <Drawer
83
+ open={rightDrawer.isOpen}
84
+ direction="right"
85
+ onOverlayClick={rightDrawer.close}
86
+ aria-label="Right drawer"
87
+ >
88
+ <SBContainer padding="large">
89
+ <h2>Right Drawer</h2>
90
+ <p>Slides in from the right side of the screen.</p>
91
+ <button
92
+ type="button"
93
+ style={buttonStyle}
94
+ onClick={rightDrawer.close}
95
+ >
96
+ Close
97
+ </button>
98
+ </SBContainer>
99
+ </Drawer>
100
+
101
+ <Drawer
102
+ open={leftDrawer.isOpen}
103
+ direction="left"
104
+ onOverlayClick={leftDrawer.close}
105
+ aria-label="Left drawer"
106
+ >
107
+ <SBContainer padding="large">
108
+ <h2>Left Drawer</h2>
109
+ <p>Slides in from the left side of the screen.</p>
110
+ <button
111
+ type="button"
112
+ style={buttonStyle}
113
+ onClick={leftDrawer.close}
114
+ >
115
+ Close
116
+ </button>
117
+ </SBContainer>
118
+ </Drawer>
119
+
120
+ <Drawer
121
+ open={topDrawer.isOpen}
122
+ direction="top"
123
+ onOverlayClick={topDrawer.close}
124
+ aria-label="Top drawer"
125
+ >
126
+ <SBContainer padding="large">
127
+ <h2>Top Drawer</h2>
128
+ <p>Slides in from the top of the screen.</p>
129
+ <button type="button" style={buttonStyle} onClick={topDrawer.close}>
130
+ Close
131
+ </button>
132
+ </SBContainer>
133
+ </Drawer>
134
+
135
+ <Drawer
136
+ open={bottomDrawer.isOpen}
137
+ direction="bottom"
138
+ onOverlayClick={bottomDrawer.close}
139
+ aria-label="Bottom drawer"
140
+ >
141
+ <SBContainer padding="large">
142
+ <h2>Bottom Drawer</h2>
143
+ <p>Slides in from the bottom of the screen.</p>
144
+ <button
145
+ type="button"
146
+ style={buttonStyle}
147
+ onClick={bottomDrawer.close}
148
+ >
149
+ Close
150
+ </button>
151
+ </SBContainer>
152
+ </Drawer>
153
+ </SBContainer>
154
+ );
155
+ },
156
+ };
157
+
158
+ export const Sizes: Story = {
159
+ name: 'Size Variations',
160
+ render: () => {
161
+ const smallDrawer = useDrawer();
162
+ const mediumDrawer = useDrawer();
163
+ const largeDrawer = useDrawer();
164
+ const fullScreenDrawer = useDrawer();
165
+
166
+ return (
167
+ <SBContainer flex direction="column" gap="large">
168
+ <SBContainer addBackground flex direction="row" gap="medium">
169
+ <button type="button" style={buttonStyle} onClick={smallDrawer.open}>
170
+ Small (250px)
171
+ </button>
172
+ <button type="button" style={buttonStyle} onClick={mediumDrawer.open}>
173
+ Medium (400px)
174
+ </button>
175
+ <button type="button" style={buttonStyle} onClick={largeDrawer.open}>
176
+ Large (600px)
177
+ </button>
178
+ <button
179
+ type="button"
180
+ style={buttonStyle}
181
+ onClick={fullScreenDrawer.open}
182
+ >
183
+ Full Screen
184
+ </button>
185
+ </SBContainer>
186
+
187
+ <Drawer
188
+ open={smallDrawer.isOpen}
189
+ direction="right"
190
+ size="250px"
191
+ onOverlayClick={smallDrawer.close}
192
+ aria-label="Small drawer"
193
+ >
194
+ <SBContainer padding="large">
195
+ <h3>Small Drawer</h3>
196
+ <p>Width: 250px</p>
197
+ <button
198
+ type="button"
199
+ style={buttonStyle}
200
+ onClick={smallDrawer.close}
201
+ >
202
+ Close
203
+ </button>
204
+ </SBContainer>
205
+ </Drawer>
206
+
207
+ <Drawer
208
+ open={mediumDrawer.isOpen}
209
+ direction="right"
210
+ size="400px"
211
+ onOverlayClick={mediumDrawer.close}
212
+ aria-label="Medium drawer"
213
+ >
214
+ <SBContainer padding="large">
215
+ <h3>Medium Drawer</h3>
216
+ <p>Width: 400px</p>
217
+ <button
218
+ type="button"
219
+ style={buttonStyle}
220
+ onClick={mediumDrawer.close}
221
+ >
222
+ Close
223
+ </button>
224
+ </SBContainer>
225
+ </Drawer>
226
+
227
+ <Drawer
228
+ open={largeDrawer.isOpen}
229
+ direction="right"
230
+ size="600px"
231
+ onOverlayClick={largeDrawer.close}
232
+ aria-label="Large drawer"
233
+ >
234
+ <SBContainer padding="large">
235
+ <h3>Large Drawer</h3>
236
+ <p>Width: 600px</p>
237
+ <button
238
+ type="button"
239
+ style={buttonStyle}
240
+ onClick={largeDrawer.close}
241
+ >
242
+ Close
243
+ </button>
244
+ </SBContainer>
245
+ </Drawer>
246
+
247
+ <Drawer
248
+ open={fullScreenDrawer.isOpen}
249
+ direction="right"
250
+ fullScreen
251
+ onOverlayClick={fullScreenDrawer.close}
252
+ aria-label="Full screen drawer"
253
+ >
254
+ <SBContainer padding="extralarge" flex direction="column" gap="large">
255
+ <h1>Full Screen Drawer</h1>
256
+ <p>
257
+ This drawer takes up the entire screen, useful for forms, detailed
258
+ content, or focused tasks.
259
+ </p>
260
+ <p>
261
+ The fullScreen prop overrides the size prop and makes the drawer
262
+ fill the viewport.
263
+ </p>
264
+ <button
265
+ type="button"
266
+ style={buttonStyle}
267
+ onClick={fullScreenDrawer.close}
268
+ >
269
+ Close
270
+ </button>
271
+ </SBContainer>
272
+ </Drawer>
273
+ </SBContainer>
274
+ );
275
+ },
276
+ };
277
+
278
+ export const WithoutOverlay: Story = {
279
+ name: 'Without Overlay',
280
+ render: () => {
281
+ const { isOpen, close, toggle } = useDrawer();
282
+
283
+ return (
284
+ <SBContainer flex direction="column" gap="medium">
285
+ <SBContainer addBackground>
286
+ <h4>Drawer Without Overlay</h4>
287
+ <p>
288
+ Set showOverlay to false to remove the dark backdrop. The drawer
289
+ will not close when clicking outside.
290
+ </p>
291
+ <button type="button" style={buttonStyle} onClick={toggle}>
292
+ {isOpen ? 'Close' : 'Open'} Drawer
293
+ </button>
294
+ </SBContainer>
295
+
296
+ <Drawer
297
+ open={isOpen}
298
+ direction="right"
299
+ showOverlay={false}
300
+ aria-label="Drawer without overlay"
301
+ >
302
+ <SBContainer padding="large">
303
+ <h3>No Overlay</h3>
304
+ <p>Background content remains visible and interactive.</p>
305
+ <button type="button" style={buttonStyle} onClick={close}>
306
+ Close
307
+ </button>
308
+ </SBContainer>
309
+ </Drawer>
310
+ </SBContainer>
311
+ );
312
+ },
313
+ };
314
+
315
+ export const KeyboardInteraction: Story = {
316
+ name: 'Keyboard Interaction',
317
+ render: () => {
318
+ const { isOpen, open, close } = useDrawer();
319
+
320
+ return (
321
+ <SBContainer flex direction="column" gap="medium">
322
+ <SBContainer addBackground>
323
+ <h4>Keyboard Support</h4>
324
+ <p>Press ESC to close the drawer when it is open</p>
325
+ <button type="button" style={buttonStyle} onClick={open}>
326
+ Open Drawer
327
+ </button>
328
+ </SBContainer>
329
+
330
+ <Drawer
331
+ open={isOpen}
332
+ direction="right"
333
+ onOverlayClick={close}
334
+ aria-label="Keyboard interactive drawer"
335
+ >
336
+ <SBContainer padding="large">
337
+ <h3>Keyboard Accessible</h3>
338
+ <p>
339
+ This drawer can be closed by pressing the <strong>ESC</strong> key
340
+ or clicking the overlay.
341
+ </p>
342
+ <p style={{ marginTop: '16px' }}>
343
+ Try pressing ESC on your keyboard to close this drawer.
344
+ </p>
345
+ <button type="button" style={buttonStyle} onClick={close}>
346
+ Or Click to Close
347
+ </button>
348
+ </SBContainer>
349
+ </Drawer>
350
+ </SBContainer>
351
+ );
352
+ },
353
+ };
354
+
355
+ export const BodyScrollLock: Story = {
356
+ name: 'Body Scroll Lock',
357
+ render: () => {
358
+ const { isOpen, open, close } = useDrawer();
359
+
360
+ return (
361
+ <SBContainer padding="large" style={{ minHeight: '200vh' }}>
362
+ <SBContainer addBackground>
363
+ <h4>Background Scroll Prevention</h4>
364
+ <p>
365
+ When the drawer is open, the background page scroll is locked to
366
+ prevent scrolling behind the overlay.
367
+ </p>
368
+ <p style={{ marginTop: '16px' }}>
369
+ This page has a long scrollable body. Scroll down and open the
370
+ drawer from any section - the background scroll will be locked.
371
+ </p>
372
+ </SBContainer>
373
+
374
+ {Array.from({ length: 20 }, (_, i) => (
375
+ <SBContainer
376
+ key={i}
377
+ padding="large"
378
+ addBackground
379
+ style={{
380
+ margin: '16px 0',
381
+ }}
382
+ >
383
+ <h3>Background Content Section {i + 1}</h3>
384
+ <p>
385
+ This is scrollable content on the main page. When the drawer is
386
+ open, you should not be able to scroll this content.
387
+ </p>
388
+ <button
389
+ type="button"
390
+ style={{ ...buttonStyle, marginTop: '12px' }}
391
+ onClick={open}
392
+ >
393
+ Open Drawer
394
+ </button>
395
+ </SBContainer>
396
+ ))}
397
+
398
+ <Drawer
399
+ open={isOpen}
400
+ direction="right"
401
+ onOverlayClick={close}
402
+ aria-label="Drawer with scroll lock"
403
+ >
404
+ <SBContainer padding="large">
405
+ <h3>Drawer Content</h3>
406
+ <p>The background scroll is now locked.</p>
407
+ <p style={{ marginTop: '16px' }}>
408
+ Try scrolling the page - it should be prevented. The body element
409
+ has <code>overflow: hidden</code> applied when the drawer is open.
410
+ </p>
411
+ <button type="button" style={buttonStyle} onClick={close}>
412
+ Close Drawer
413
+ </button>
414
+ </SBContainer>
415
+ </Drawer>
416
+ </SBContainer>
417
+ );
418
+ },
419
+ };
420
+
421
+ export const NestedDrawers: Story = {
422
+ name: 'Nested Drawers',
423
+ render: () => {
424
+ const parentDrawer = useDrawer();
425
+ const childDrawer = useDrawer();
426
+
427
+ const childDrawerStyle: CSSVariables = {
428
+ '--hscl-drawer-zIndex': '1001',
429
+ '--hscl-drawer-overlay-zIndex': '1000',
430
+ };
431
+
432
+ return (
433
+ <SBContainer flex direction="column" gap="medium">
434
+ <SBContainer addBackground>
435
+ <h4>Nested Drawer Example</h4>
436
+ <p>
437
+ This example demonstrates opening a second drawer from within the
438
+ first drawer. Use this pattern carefully - only when necessary for
439
+ complex workflows.
440
+ </p>
441
+ <button
442
+ type="button"
443
+ style={{ ...buttonStyle, marginTop: '16px' }}
444
+ onClick={parentDrawer.open}
445
+ >
446
+ Open Parent Drawer
447
+ </button>
448
+ </SBContainer>
449
+
450
+ <Drawer
451
+ open={parentDrawer.isOpen}
452
+ direction="right"
453
+ size="400px"
454
+ onOverlayClick={parentDrawer.close}
455
+ aria-label="Parent drawer"
456
+ >
457
+ <SBContainer padding="large">
458
+ <h2>Parent Drawer</h2>
459
+ <p>This is the first level drawer.</p>
460
+ <p style={{ marginTop: '16px' }}>
461
+ Click the button below to open a second drawer on top of this one:
462
+ </p>
463
+ <button
464
+ type="button"
465
+ style={{ ...buttonStyle, marginTop: '16px' }}
466
+ onClick={childDrawer.open}
467
+ >
468
+ Open Child Drawer
469
+ </button>
470
+ <button
471
+ type="button"
472
+ style={{
473
+ ...buttonStyle,
474
+ marginTop: '16px',
475
+ marginLeft: '12px',
476
+ backgroundColor: '#666',
477
+ }}
478
+ onClick={parentDrawer.close}
479
+ >
480
+ Close Parent
481
+ </button>
482
+ </SBContainer>
483
+ </Drawer>
484
+
485
+ <Drawer
486
+ open={childDrawer.isOpen}
487
+ direction="bottom"
488
+ fullScreen
489
+ onOverlayClick={childDrawer.close}
490
+ aria-label="Child drawer"
491
+ style={childDrawerStyle}
492
+ >
493
+ <SBContainer padding="large">
494
+ <h2>Child Drawer</h2>
495
+ <p>This drawer opened from within the parent drawer.</p>
496
+ <p style={{ marginTop: '16px' }}>
497
+ Notice how it appears on top with a higher z-index. Close this
498
+ drawer to return to the parent.
499
+ </p>
500
+ <button
501
+ type="button"
502
+ style={{ ...buttonStyle, marginTop: '16px' }}
503
+ onClick={childDrawer.close}
504
+ >
505
+ Close Child
506
+ </button>
507
+ </SBContainer>
508
+ </Drawer>
509
+ </SBContainer>
510
+ );
511
+ },
512
+ };
@@ -0,0 +1,8 @@
1
+ .decoratorContainer {
2
+ display: flex;
3
+ align-items: center;
4
+ justify-content: center;
5
+ padding: 20px;
6
+ width: 100%;
7
+ min-height: 400px;
8
+ }
@@ -0,0 +1,18 @@
1
+ import type { Decorator } from '@storybook/react';
2
+ import styles from './DrawerDecorator.module.scss';
3
+
4
+ export const buttonStyle = {
5
+ padding: '8px 16px',
6
+ backgroundColor: '#0066cc',
7
+ color: 'white',
8
+ border: 'none',
9
+ borderRadius: '4px',
10
+ cursor: 'pointer',
11
+ fontSize: '14px',
12
+ };
13
+
14
+ export const withDrawerStyles: Decorator = Story => (
15
+ <div className={styles.decoratorContainer}>
16
+ <Story />
17
+ </div>
18
+ );
@@ -0,0 +1,25 @@
1
+ import type { CSSVariables } from '../utils/types.js';
2
+
3
+ export type DrawerDirection = 'left' | 'right' | 'top' | 'bottom';
4
+
5
+ export type DrawerProps = {
6
+ open: boolean;
7
+ direction: DrawerDirection;
8
+ variant?: 'primary' | 'secondary' | 'tertiary';
9
+ className?: string;
10
+ style?: CSSVariables;
11
+ children?: React.ReactNode;
12
+ showOverlay?: boolean;
13
+ onOverlayClick?: () => void;
14
+ fullScreen?: boolean;
15
+ size?: string;
16
+ 'aria-label'?: string;
17
+ 'aria-labelledby'?: string;
18
+ };
19
+
20
+ export type UseDrawerReturn = {
21
+ isOpen: boolean;
22
+ open: () => void;
23
+ close: () => void;
24
+ toggle: () => void;
25
+ };
@@ -1,5 +1,5 @@
1
1
  import { Decorator } from '@storybook/react';
2
- import styles from './FlexDecorator.module.css';
2
+ import styles from './FlexDecorator.module.scss';
3
3
 
4
4
  export const withFlexStyles: Decorator = Story => {
5
5
  return (
@@ -1,3 +1,5 @@
1
+ import { CSSVariables } from '../utils/types.js';
2
+
1
3
  export type FlexDirection = 'row' | 'row-reverse' | 'column' | 'column-reverse';
2
4
 
3
5
  export type FlexWrap = 'nowrap' | 'wrap' | 'wrap-reverse';
@@ -58,6 +60,6 @@ export type FlexProps = {
58
60
  maxWidth?: string;
59
61
  maxHeight?: string;
60
62
  className?: string;
61
- style?: React.CSSProperties;
63
+ style?: CSSVariables;
62
64
  children?: React.ReactNode;
63
65
  };