@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.
- package/components/componentLibrary/Accordion/AccordionContent/ContentFields.tsx +5 -3
- package/components/componentLibrary/Accordion/AccordionItem/StyleFields.tsx +5 -3
- package/components/componentLibrary/Accordion/AccordionItem/index.module.scss +2 -2
- package/components/componentLibrary/Accordion/AccordionItem/index.tsx +3 -3
- package/components/componentLibrary/Accordion/AccordionTitle/ContentFields.tsx +5 -3
- package/components/componentLibrary/Accordion/AccordionTitle/index.module.scss +2 -2
- package/components/componentLibrary/Accordion/stories/Accordion.stories.tsx +80 -1
- package/components/componentLibrary/Accordion/stories/AccordionDecorator.tsx +14 -14
- package/components/componentLibrary/Button/ContentFields.tsx +5 -3
- package/components/componentLibrary/Button/StyleFields.tsx +5 -3
- package/components/componentLibrary/Button/index.module.scss +22 -14
- package/components/componentLibrary/Button/index.tsx +6 -6
- package/components/componentLibrary/Button/stories/Button.AsButton.stories.tsx +30 -1
- package/components/componentLibrary/Button/stories/Button.AsLink.stories.tsx +38 -1
- package/components/componentLibrary/Button/stories/ButtonDecorator.tsx +1 -1
- package/components/componentLibrary/Card/StyleFields.tsx +5 -3
- package/components/componentLibrary/Card/stories/Card.stories.tsx +46 -1
- package/components/componentLibrary/Card/stories/CardDecorator.tsx +1 -1
- package/components/componentLibrary/Divider/ContentFields.tsx +5 -3
- package/components/componentLibrary/Divider/StyleFields.tsx +5 -3
- package/components/componentLibrary/Divider/index.module.scss +6 -6
- package/components/componentLibrary/Divider/index.tsx +7 -3
- package/components/componentLibrary/Divider/stories/Divider.stories.tsx +44 -50
- package/components/componentLibrary/Divider/stories/{DividerDecorator.module.css → DividerDecorator.module.scss} +5 -4
- package/components/componentLibrary/Divider/stories/DividerDecorator.tsx +1 -1
- package/components/componentLibrary/Divider/types.ts +3 -1
- package/components/componentLibrary/Drawer/hooks/index.tsx +13 -0
- package/components/componentLibrary/Drawer/index.module.scss +94 -0
- package/components/componentLibrary/Drawer/index.tsx +131 -0
- package/components/componentLibrary/Drawer/llm.txt +416 -0
- package/components/componentLibrary/Drawer/stories/Drawer.stories.tsx +512 -0
- package/components/componentLibrary/Drawer/stories/DrawerDecorator.module.scss +8 -0
- package/components/componentLibrary/Drawer/stories/DrawerDecorator.tsx +18 -0
- package/components/componentLibrary/Drawer/types.ts +25 -0
- package/components/componentLibrary/Flex/stories/FlexDecorator.tsx +1 -1
- package/components/componentLibrary/Flex/types.ts +3 -1
- package/components/componentLibrary/Grid/stories/Grid.stories.tsx +454 -152
- package/components/componentLibrary/Grid/stories/GridDecorator.tsx +2 -2
- package/components/componentLibrary/Heading/ContentFields.tsx +5 -3
- package/components/componentLibrary/Heading/StyleFields.tsx +11 -9
- package/components/componentLibrary/Heading/index.tsx +3 -3
- package/components/componentLibrary/Heading/llm.txt +8 -8
- package/components/componentLibrary/Heading/stories/Heading.stories.tsx +3 -3
- package/components/componentLibrary/Heading/stories/HeadingDecorator.tsx +1 -1
- package/components/componentLibrary/Heading/types.ts +4 -4
- package/components/componentLibrary/Icon/ContentFields.tsx +5 -3
- package/components/componentLibrary/Icon/stories/Icon.stories.tsx +1 -1
- package/components/componentLibrary/Icon/stories/IconDecorator.tsx +1 -1
- package/components/componentLibrary/Image/ContentFields.tsx +5 -3
- package/components/componentLibrary/Image/index.tsx +4 -4
- package/components/componentLibrary/Image/llm.txt +17 -17
- package/components/componentLibrary/Image/stories/Image.stories.tsx +61 -18
- package/components/componentLibrary/Image/stories/ImageDecorator.tsx +1 -1
- package/components/componentLibrary/Image/types.ts +2 -2
- package/components/componentLibrary/LanguageSwitcher/ContentFields.tsx +18 -0
- package/components/componentLibrary/LanguageSwitcher/LanguageOptions.module.scss +37 -0
- package/components/componentLibrary/LanguageSwitcher/LanguageOptions.tsx +65 -0
- package/components/componentLibrary/LanguageSwitcher/StyleFields.tsx +48 -0
- package/components/componentLibrary/LanguageSwitcher/_dummyData.tsx +247 -0
- package/components/componentLibrary/LanguageSwitcher/assets/Globe.tsx +16 -0
- package/components/componentLibrary/LanguageSwitcher/index.module.scss +58 -0
- package/components/componentLibrary/LanguageSwitcher/index.tsx +125 -0
- package/components/componentLibrary/LanguageSwitcher/llm.txt +380 -0
- package/components/componentLibrary/LanguageSwitcher/stories/LanguageSwitcher.stories.tsx +349 -0
- package/components/componentLibrary/LanguageSwitcher/stories/LanguageSwitcherDecorator.module.scss +5 -0
- package/components/componentLibrary/LanguageSwitcher/stories/LanguageSwitcherDecorator.tsx +8 -0
- package/components/componentLibrary/LanguageSwitcher/types.ts +48 -0
- package/components/componentLibrary/LanguageSwitcher/utils.tsx +38 -0
- package/components/componentLibrary/Link/ContentFields.tsx +5 -3
- package/components/componentLibrary/Link/StyleFields.tsx +5 -3
- package/components/componentLibrary/Link/index.module.scss +10 -0
- package/components/componentLibrary/Link/index.tsx +24 -14
- package/components/componentLibrary/Link/stories/Link.stories.tsx +35 -5
- package/components/componentLibrary/Link/stories/LinkDecorator.tsx +11 -1
- package/components/componentLibrary/Link/types.ts +22 -13
- package/components/componentLibrary/List/ContentFields.tsx +5 -3
- package/components/componentLibrary/List/ListItem/ContentFields.tsx +6 -17
- package/components/componentLibrary/List/ListItem/index.module.scss +1 -13
- package/components/componentLibrary/List/ListItem/index.tsx +3 -30
- package/components/componentLibrary/List/ListItem/types.ts +1 -16
- package/components/componentLibrary/List/StyleFields.tsx +15 -18
- package/components/componentLibrary/List/index.module.scss +3 -0
- package/components/componentLibrary/List/index.tsx +5 -2
- package/components/componentLibrary/List/llm.txt +73 -103
- package/components/componentLibrary/List/stories/List.stories.tsx +56 -80
- package/components/componentLibrary/List/stories/ListDecorator.tsx +3 -6
- package/components/componentLibrary/List/types.ts +1 -3
- package/components/componentLibrary/Logo/_dummyLogoData.ts +12 -0
- package/components/componentLibrary/Logo/assets/hubspot-logo.png +0 -0
- package/components/componentLibrary/Logo/index.module.scss +22 -0
- package/components/componentLibrary/Logo/index.tsx +73 -0
- package/components/componentLibrary/Logo/llm.txt +262 -0
- package/components/componentLibrary/Logo/stories/Logo.stories.tsx +88 -0
- package/components/componentLibrary/Logo/stories/LogoDecorator.module.scss +10 -0
- package/components/componentLibrary/Logo/stories/LogoDecorator.tsx +8 -0
- package/components/componentLibrary/Logo/types.tsx +16 -0
- package/components/componentLibrary/Menu/ContentFields.tsx +16 -0
- package/components/componentLibrary/Menu/MenuItem/Chevron/index.module.scss +6 -0
- package/components/componentLibrary/Menu/MenuItem/Chevron/index.tsx +17 -0
- package/components/componentLibrary/Menu/MenuItem/index.module.scss +7 -0
- package/components/componentLibrary/Menu/MenuItem/index.tsx +266 -0
- package/components/componentLibrary/Menu/MenuItem/types.ts +17 -0
- package/components/componentLibrary/Menu/NavigationMenu/ContentFields.tsx +20 -0
- package/components/componentLibrary/Menu/NavigationMenu/index.tsx +18 -0
- package/components/componentLibrary/Menu/NavigationMenu/islands/NavigationMenuIsland.tsx +95 -0
- package/components/componentLibrary/Menu/NavigationMenu/islands/index.module.scss +100 -0
- package/components/componentLibrary/Menu/NavigationMenu/islands/types.ts +19 -0
- package/components/componentLibrary/Menu/NavigationMenu/llm.txt +197 -0
- package/components/componentLibrary/Menu/NavigationMenu/stories/NavigationMenu.stories.tsx +286 -0
- package/components/componentLibrary/Menu/NavigationMenu/stories/NavigationMenuDecorator.module.scss +15 -0
- package/components/componentLibrary/Menu/NavigationMenu/stories/NavigationMenuDecorator.tsx +12 -0
- package/components/componentLibrary/Menu/NavigationMenu/types.ts +3 -0
- package/components/componentLibrary/Menu/VerticalMenu/ContentFields.tsx +20 -0
- package/components/componentLibrary/Menu/VerticalMenu/index.tsx +18 -0
- package/components/componentLibrary/Menu/VerticalMenu/islands/index.module.scss +53 -0
- package/components/componentLibrary/Menu/VerticalMenu/islands/verticalMenuIsland.tsx +78 -0
- package/components/componentLibrary/Menu/VerticalMenu/llm.txt +177 -0
- package/components/componentLibrary/Menu/VerticalMenu/stories/VerticalMenu.stories.tsx +242 -0
- package/components/componentLibrary/Menu/VerticalMenu/stories/VerticalMenuDecorator.module.scss +19 -0
- package/components/componentLibrary/Menu/VerticalMenu/stories/VerticalMenuDecorator.tsx +12 -0
- package/components/componentLibrary/Menu/VerticalMenu/types.ts +21 -0
- package/components/componentLibrary/Menu/_dummyMenuData.js +1346 -0
- package/components/componentLibrary/Menu/types.ts +56 -0
- package/components/componentLibrary/Menu/utils/transformMenuData.ts +11 -0
- package/components/componentLibrary/_patterns/README.md +15 -17
- package/components/componentLibrary/_patterns/checklist-and-examples.md +17 -17
- package/components/componentLibrary/_patterns/component-structure.md +21 -23
- package/components/componentLibrary/_patterns/css-patterns.md +170 -18
- package/components/componentLibrary/_patterns/field-patterns.md +97 -27
- package/components/componentLibrary/_patterns/function-declaration-patterns.md +281 -0
- package/components/componentLibrary/_patterns/llm-txt.template.md +4 -2
- package/components/componentLibrary/_patterns/prop-naming-patterns.md +208 -0
- package/components/componentLibrary/_patterns/storybook-patterns.md +25 -8
- package/components/componentLibrary/_patterns/typescript-patterns.md +6 -3
- package/package.json +4 -2
- /package/components/componentLibrary/Button/stories/{ButtonDecorator.module.css → ButtonDecorator.module.scss} +0 -0
- /package/components/componentLibrary/Card/stories/{CardDecorator.module.css → CardDecorator.module.scss} +0 -0
- /package/components/componentLibrary/Flex/stories/{FlexDecorator.module.css → FlexDecorator.module.scss} +0 -0
- /package/components/componentLibrary/Grid/stories/{GridDecorator.module.css → GridDecorator.module.scss} +0 -0
- /package/components/componentLibrary/Heading/stories/{HeadingDecorator.module.css → HeadingDecorator.module.scss} +0 -0
- /package/components/componentLibrary/Icon/stories/{IconDecorator.module.css → IconDecorator.module.scss} +0 -0
- /package/components/componentLibrary/Image/stories/{ImageDecorator.module.css → ImageDecorator.module.scss} +0 -0
- /package/components/componentLibrary/Image/stories/assets/{catSmile.jpg → cat-smile.jpg} +0 -0
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
# Drawer Component
|
|
2
|
+
|
|
3
|
+
A slide-out panel component that overlays content from the side (left/right), top, or bottom of the viewport. Provides a modal-like experience for displaying supplementary content, navigation menus, or forms without navigating away from the current page.
|
|
4
|
+
|
|
5
|
+
## Import path
|
|
6
|
+
```tsx
|
|
7
|
+
import Drawer, { useDrawer } from '@hubspot/cms-component-library/Drawer';
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Purpose
|
|
11
|
+
|
|
12
|
+
The Drawer component provides a consistent way to create slide-out panels. It renders a fixed-position container that slides in from any edge of the viewport with smooth animations. The component automatically manages body scroll locking, keyboard interactions (ESC to close), and accessibility attributes. Use this component when you need to display additional content without losing the current page context, such as navigation menus, filters, settings panels, or detailed forms.
|
|
13
|
+
|
|
14
|
+
## Component Structure
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
Drawer/
|
|
18
|
+
├── index.tsx # Main component with render logic
|
|
19
|
+
├── types.ts # TypeScript type definitions
|
|
20
|
+
├── hooks/
|
|
21
|
+
│ └── index.tsx # useDrawer hook for state management
|
|
22
|
+
├── index.module.scss # CSS module with animations
|
|
23
|
+
└── stories/
|
|
24
|
+
├── Drawer.stories.tsx # Component usage examples
|
|
25
|
+
├── DrawerDecorator.tsx # Storybook decorator
|
|
26
|
+
└── DrawerDecorator.module.css # Decorator styles
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Components
|
|
30
|
+
|
|
31
|
+
### Drawer (Main Component)
|
|
32
|
+
|
|
33
|
+
**Purpose:** Renders a fixed-position panel that slides in from any viewport edge with overlay support and body scroll locking.
|
|
34
|
+
|
|
35
|
+
**Props:**
|
|
36
|
+
```tsx
|
|
37
|
+
{
|
|
38
|
+
open: boolean; // Controls drawer visibility (required)
|
|
39
|
+
direction: 'left' | 'right' | 'top' | 'bottom'; // Edge from which drawer slides (required)
|
|
40
|
+
variant?: 'primary' | 'secondary' | 'tertiary'; // Visual style variant (default: 'primary')
|
|
41
|
+
className?: string; // Additional CSS classes
|
|
42
|
+
style?: React.CSSProperties; // Inline styles
|
|
43
|
+
children?: React.ReactNode; // Drawer content
|
|
44
|
+
showOverlay?: boolean; // Show dark backdrop overlay (default: true)
|
|
45
|
+
onOverlayClick?: () => void; // Callback when overlay is clicked
|
|
46
|
+
fullScreen?: boolean; // Make drawer fill entire viewport (default: false)
|
|
47
|
+
size?: string; // Drawer width/height in CSS units (default: '300px')
|
|
48
|
+
'aria-label'?: string; // Accessible label for the drawer
|
|
49
|
+
'aria-labelledby'?: string; // ID of element that labels the drawer
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### useDrawer Hook
|
|
54
|
+
|
|
55
|
+
**Purpose:** Provides drawer state management with open, close, and toggle functions.
|
|
56
|
+
|
|
57
|
+
**Return Type:**
|
|
58
|
+
```tsx
|
|
59
|
+
{
|
|
60
|
+
isOpen: boolean; // Current open/closed state
|
|
61
|
+
open: () => void; // Opens the drawer
|
|
62
|
+
close: () => void; // Closes the drawer
|
|
63
|
+
toggle: () => void; // Toggles drawer state
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Usage Examples
|
|
68
|
+
|
|
69
|
+
### Island Usage
|
|
70
|
+
|
|
71
|
+
The Drawer component requires JavaScript for interactive state management and must be used within an Island component.
|
|
72
|
+
|
|
73
|
+
#### Island Component
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
// islands/DrawerIsland.tsx
|
|
77
|
+
import Drawer, { useDrawer } from '@hubspot/cms-component-library/Drawer';
|
|
78
|
+
|
|
79
|
+
export default function DrawerIsland({ menuItems, direction = 'right', size = '320px' }) {
|
|
80
|
+
const { isOpen, open, close } = useDrawer();
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<>
|
|
84
|
+
<button onClick={open}>Open Menu</button>
|
|
85
|
+
<Drawer
|
|
86
|
+
open={isOpen}
|
|
87
|
+
direction={direction}
|
|
88
|
+
size={size}
|
|
89
|
+
onOverlayClick={close}
|
|
90
|
+
aria-label="Navigation menu"
|
|
91
|
+
>
|
|
92
|
+
<nav style={{ padding: '24px' }}>
|
|
93
|
+
<h2>Menu</h2>
|
|
94
|
+
<ul>
|
|
95
|
+
{menuItems?.map((item, index) => (
|
|
96
|
+
<li key={index}>
|
|
97
|
+
<a href={item.url}>{item.label}</a>
|
|
98
|
+
</li>
|
|
99
|
+
))}
|
|
100
|
+
</ul>
|
|
101
|
+
<button onClick={close}>Close</button>
|
|
102
|
+
</nav>
|
|
103
|
+
</Drawer>
|
|
104
|
+
</>
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
#### Module File
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
import { Island } from '@hubspot/cms-components';
|
|
113
|
+
import DrawerIsland from './islands/DrawerIsland?island';
|
|
114
|
+
|
|
115
|
+
export const Component = ({ fieldValues }) => {
|
|
116
|
+
return (
|
|
117
|
+
<Island
|
|
118
|
+
module={DrawerIsland}
|
|
119
|
+
menuItems={fieldValues.menuItems}
|
|
120
|
+
direction={fieldValues.direction}
|
|
121
|
+
size={fieldValues.size}
|
|
122
|
+
/>
|
|
123
|
+
);
|
|
124
|
+
};
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Basic Drawer (Right Side)
|
|
128
|
+
|
|
129
|
+
```tsx
|
|
130
|
+
import Drawer, { useDrawer } from '@hubspot/cms-component-library/Drawer';
|
|
131
|
+
|
|
132
|
+
export default function MyComponent() {
|
|
133
|
+
const { isOpen, open, close } = useDrawer();
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<>
|
|
137
|
+
<button onClick={open}>Open Menu</button>
|
|
138
|
+
<Drawer
|
|
139
|
+
open={isOpen}
|
|
140
|
+
direction="right"
|
|
141
|
+
onOverlayClick={close}
|
|
142
|
+
aria-label="Navigation menu"
|
|
143
|
+
>
|
|
144
|
+
<div style={{ padding: '24px' }}>
|
|
145
|
+
<h2>Menu</h2>
|
|
146
|
+
<nav>
|
|
147
|
+
{/* Navigation content */}
|
|
148
|
+
</nav>
|
|
149
|
+
<button onClick={close}>Close</button>
|
|
150
|
+
</div>
|
|
151
|
+
</Drawer>
|
|
152
|
+
</>
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Left Side Drawer
|
|
158
|
+
|
|
159
|
+
```tsx
|
|
160
|
+
<Drawer
|
|
161
|
+
open={isOpen}
|
|
162
|
+
direction="left"
|
|
163
|
+
onOverlayClick={close}
|
|
164
|
+
aria-label="Side navigation"
|
|
165
|
+
>
|
|
166
|
+
<div style={{ padding: '24px' }}>
|
|
167
|
+
<h2>Navigation</h2>
|
|
168
|
+
{/* Drawer content */}
|
|
169
|
+
</div>
|
|
170
|
+
</Drawer>
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Top Drawer
|
|
174
|
+
|
|
175
|
+
```tsx
|
|
176
|
+
<Drawer
|
|
177
|
+
open={isOpen}
|
|
178
|
+
direction="top"
|
|
179
|
+
size="400px"
|
|
180
|
+
onOverlayClick={close}
|
|
181
|
+
aria-label="Details panel"
|
|
182
|
+
>
|
|
183
|
+
<div style={{ padding: '24px' }}>
|
|
184
|
+
<h2>Details</h2>
|
|
185
|
+
{/* Content */}
|
|
186
|
+
</div>
|
|
187
|
+
</Drawer>
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Bottom Drawer
|
|
191
|
+
|
|
192
|
+
```tsx
|
|
193
|
+
<Drawer
|
|
194
|
+
open={isOpen}
|
|
195
|
+
direction="bottom"
|
|
196
|
+
size="50vh"
|
|
197
|
+
onOverlayClick={close}
|
|
198
|
+
aria-label="Details panel"
|
|
199
|
+
>
|
|
200
|
+
<div style={{ padding: '24px' }}>
|
|
201
|
+
<h2>Details</h2>
|
|
202
|
+
{/* Content */}
|
|
203
|
+
</div>
|
|
204
|
+
</Drawer>
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Custom Size Drawer
|
|
208
|
+
|
|
209
|
+
```tsx
|
|
210
|
+
<Drawer
|
|
211
|
+
open={isOpen}
|
|
212
|
+
direction="right"
|
|
213
|
+
size="500px"
|
|
214
|
+
onOverlayClick={close}
|
|
215
|
+
aria-label="Details panel"
|
|
216
|
+
>
|
|
217
|
+
<div style={{ padding: '24px' }}>
|
|
218
|
+
<h2>Details</h2>
|
|
219
|
+
{/* Content */}
|
|
220
|
+
</div>
|
|
221
|
+
</Drawer>
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Full Screen Drawer
|
|
225
|
+
|
|
226
|
+
```tsx
|
|
227
|
+
<Drawer
|
|
228
|
+
open={isOpen}
|
|
229
|
+
direction="right"
|
|
230
|
+
fullScreen
|
|
231
|
+
onOverlayClick={close}
|
|
232
|
+
aria-label="Full screen content"
|
|
233
|
+
>
|
|
234
|
+
<div style={{ padding: '48px' }}>
|
|
235
|
+
<h1>Full Screen Content</h1>
|
|
236
|
+
<p>This drawer takes up the entire viewport.</p>
|
|
237
|
+
<button onClick={close}>Close</button>
|
|
238
|
+
</div>
|
|
239
|
+
</Drawer>
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Without Overlay
|
|
243
|
+
|
|
244
|
+
```tsx
|
|
245
|
+
<Drawer
|
|
246
|
+
open={isOpen}
|
|
247
|
+
direction="right"
|
|
248
|
+
showOverlay={false}
|
|
249
|
+
aria-label="Side panel"
|
|
250
|
+
>
|
|
251
|
+
<div style={{ padding: '24px' }}>
|
|
252
|
+
<h2>Panel</h2>
|
|
253
|
+
<p>Background remains visible and interactive.</p>
|
|
254
|
+
<button onClick={close}>Close</button>
|
|
255
|
+
</div>
|
|
256
|
+
</Drawer>
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### With Toggle Control
|
|
260
|
+
|
|
261
|
+
```tsx
|
|
262
|
+
const { isOpen, toggle } = useDrawer();
|
|
263
|
+
|
|
264
|
+
<>
|
|
265
|
+
<button onClick={toggle}>
|
|
266
|
+
{isOpen ? 'Close' : 'Open'} Drawer
|
|
267
|
+
</button>
|
|
268
|
+
<Drawer
|
|
269
|
+
open={isOpen}
|
|
270
|
+
direction="right"
|
|
271
|
+
showOverlay={false}
|
|
272
|
+
aria-label="Toggleable drawer"
|
|
273
|
+
>
|
|
274
|
+
<div style={{ padding: '24px' }}>
|
|
275
|
+
{/* Content */}
|
|
276
|
+
</div>
|
|
277
|
+
</Drawer>
|
|
278
|
+
</>
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## HubSpot CMS Integration
|
|
282
|
+
|
|
283
|
+
### Field Definitions
|
|
284
|
+
|
|
285
|
+
The Drawer component is primarily interactive and does not provide pre-built field definitions. Since drawers are typically controlled programmatically (open/close state, callbacks), the component focuses on behavior rather than static content configuration. You can define your own fields based on the content you want to display inside the drawer (such as menu items, settings options, or form data).
|
|
286
|
+
|
|
287
|
+
### Module Usage Example
|
|
288
|
+
|
|
289
|
+
When integrating Drawer into a HubSpot module, create an Island component that manages drawer state and pass any necessary data from field values.
|
|
290
|
+
|
|
291
|
+
```tsx
|
|
292
|
+
import { Island } from '@hubspot/cms-components';
|
|
293
|
+
import DrawerIsland from './islands/DrawerIsland?island';
|
|
294
|
+
|
|
295
|
+
export default function NavigationModule({ fieldValues }) {
|
|
296
|
+
return (
|
|
297
|
+
<Island
|
|
298
|
+
module={DrawerIsland}
|
|
299
|
+
menuItems={fieldValues.menuItems}
|
|
300
|
+
direction={fieldValues.direction || 'left'}
|
|
301
|
+
size={fieldValues.size || '320px'}
|
|
302
|
+
/>
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
## Styling
|
|
308
|
+
|
|
309
|
+
### CSS Variables
|
|
310
|
+
|
|
311
|
+
The Drawer component uses CSS variables for theming and customization:
|
|
312
|
+
|
|
313
|
+
**Base Styles:**
|
|
314
|
+
- `--hscl-drawer-backgroundColor`: Drawer background color (default: white)
|
|
315
|
+
- `--hscl-drawer-boxShadow`: Shadow effect for depth (default: 0 2px 8px rgba(0, 0, 0, 0.15))
|
|
316
|
+
- `--hscl-drawer-zIndex`: Drawer z-index (default: 1000)
|
|
317
|
+
- `--hscl-drawer-size`: Drawer width (for left/right) or height (for top/bottom)
|
|
318
|
+
|
|
319
|
+
**Overlay Styles:**
|
|
320
|
+
- `--hscl-drawer-overlay-backgroundColor`: Overlay background (default: rgba(0, 0, 0, 0.5))
|
|
321
|
+
- `--hscl-drawer-overlay-backdropFilter`: Backdrop blur effect (default: blur(3px))
|
|
322
|
+
- `--hscl-drawer-overlay-zIndex`: Overlay z-index (default: 999)
|
|
323
|
+
|
|
324
|
+
### Custom Styling Example
|
|
325
|
+
|
|
326
|
+
```tsx
|
|
327
|
+
<Drawer
|
|
328
|
+
open={isOpen}
|
|
329
|
+
direction="right"
|
|
330
|
+
onOverlayClick={close}
|
|
331
|
+
style={{
|
|
332
|
+
'--hscl-drawer-backgroundColor': '#f5f5f5',
|
|
333
|
+
'--hscl-drawer-boxShadow': '0 4px 16px rgba(0, 0, 0, 0.2)',
|
|
334
|
+
} as React.CSSProperties}
|
|
335
|
+
>
|
|
336
|
+
{/* Content */}
|
|
337
|
+
</Drawer>
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## Accessibility
|
|
341
|
+
|
|
342
|
+
The Drawer component follows accessibility best practices:
|
|
343
|
+
|
|
344
|
+
- **Semantic HTML**: Uses `role="dialog"` and `aria-modal="true"` for proper semantics
|
|
345
|
+
- **Keyboard Navigation**:
|
|
346
|
+
- `ESC` key closes the drawer
|
|
347
|
+
- Focus management is handled automatically
|
|
348
|
+
- **Screen Reader Support**:
|
|
349
|
+
- `aria-label` or `aria-labelledby` should be provided to describe drawer purpose
|
|
350
|
+
- `aria-hidden` attribute toggles based on open state
|
|
351
|
+
- Proper ARIA roles announce drawer as a modal dialog
|
|
352
|
+
- **Body Scroll Locking**: Background content scroll is disabled when drawer is open
|
|
353
|
+
- **Focus Trap**: Overlay click closes drawer, preventing accidental interactions with background
|
|
354
|
+
- **Visual Indicators**: Overlay provides clear visual feedback that drawer is modal
|
|
355
|
+
- **Reduced Motion**: Respects `prefers-reduced-motion` for users with motion sensitivity
|
|
356
|
+
|
|
357
|
+
## Best Practices
|
|
358
|
+
|
|
359
|
+
- **Use Islands for interactivity**: The Drawer component is interactive and requires JavaScript, so it must be used within an Island component in modules
|
|
360
|
+
- **Provide aria-label**: Always include `aria-label` or `aria-labelledby` to describe the drawer's purpose
|
|
361
|
+
- **Choose appropriate direction**:
|
|
362
|
+
- `left` or `right`: Best for navigation menus, settings panels, or supplementary content
|
|
363
|
+
- `top`: Good for notification panels or collapsible headers
|
|
364
|
+
- `bottom`: Ideal for mobile-style action sheets or details panels
|
|
365
|
+
- **Size considerations**:
|
|
366
|
+
- Consider `fullScreen` for complex workflows or extensive forms
|
|
367
|
+
- **Handle close actions**: Always provide a way to close the drawer (overlay click, close button)
|
|
368
|
+
- **Overlay usage**: Keep `showOverlay={true}` (default) for modal behavior, use `false` only for persistent sidebars
|
|
369
|
+
- **Body scroll locking**: The component automatically prevents background scrolling when open
|
|
370
|
+
- **ESC key support**: The drawer closes automatically when ESC is pressed (built-in)
|
|
371
|
+
- **Content structure**: Include a clear heading and close button within drawer content for usability
|
|
372
|
+
- **Avoid nested drawers**: Don't open multiple non-full-screen drawers simultaneously as it creates confusing UX
|
|
373
|
+
- **State management**: Use the `useDrawer` hook for consistent state management
|
|
374
|
+
- **Loading states**: Consider showing loading indicators within drawer content during async operations
|
|
375
|
+
|
|
376
|
+
## Common Patterns
|
|
377
|
+
|
|
378
|
+
### Navigation Menu Drawer
|
|
379
|
+
|
|
380
|
+
```tsx
|
|
381
|
+
import Drawer, { useDrawer } from '@hubspot/cms-component-library/Drawer';
|
|
382
|
+
|
|
383
|
+
export default function NavigationDrawer({ menuItems }) {
|
|
384
|
+
const { isOpen, open, close } = useDrawer();
|
|
385
|
+
|
|
386
|
+
return (
|
|
387
|
+
<>
|
|
388
|
+
<button onClick={open}>Menu</button>
|
|
389
|
+
<Drawer
|
|
390
|
+
open={isOpen}
|
|
391
|
+
direction="left"
|
|
392
|
+
size="320px"
|
|
393
|
+
onOverlayClick={close}
|
|
394
|
+
aria-label="Navigation menu"
|
|
395
|
+
>
|
|
396
|
+
<nav style={{ padding: '24px' }}>
|
|
397
|
+
<h2>Navigation</h2>
|
|
398
|
+
<ul>
|
|
399
|
+
{menuItems.map((item, i) => (
|
|
400
|
+
<li key={i}>
|
|
401
|
+
<a href={item.url}>{item.label}</a>
|
|
402
|
+
</li>
|
|
403
|
+
))}
|
|
404
|
+
</ul>
|
|
405
|
+
<button onClick={close}>Close</button>
|
|
406
|
+
</nav>
|
|
407
|
+
</Drawer>
|
|
408
|
+
</>
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
## Related Components
|
|
415
|
+
|
|
416
|
+
- **useDrawer**: Hook for managing drawer open/close state. Always use this hook rather than managing state manually.
|