@czi-sds/mcp 0.16.0 → 0.17.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 (164) hide show
  1. package/README.md +2 -1
  2. package/data/component-docs/Accordion.md +291 -0
  3. package/data/component-docs/Autocomplete.md +525 -0
  4. package/data/component-docs/Banner.md +231 -0
  5. package/data/component-docs/Button.md +224 -0
  6. package/data/component-docs/ButtonDropdown.md +256 -0
  7. package/data/component-docs/ButtonGroup.md +320 -0
  8. package/data/component-docs/ButtonToggle.md +397 -0
  9. package/data/component-docs/Callout.md +268 -0
  10. package/data/component-docs/CellBasic.md +122 -0
  11. package/data/component-docs/CellComponent.md +63 -0
  12. package/data/component-docs/CellHeader.md +128 -0
  13. package/data/component-docs/ComplexFilter.md +294 -0
  14. package/data/component-docs/ContentCard.md +406 -0
  15. package/data/component-docs/Dialog.md +651 -0
  16. package/data/component-docs/Dropdown.md +155 -0
  17. package/data/component-docs/DropdownMenu.md +949 -0
  18. package/data/component-docs/HeatmapChart.md +782 -0
  19. package/data/component-docs/Hero.md +363 -0
  20. package/data/component-docs/Icon.md +283 -0
  21. package/data/component-docs/InputCheckbox.md +205 -0
  22. package/data/component-docs/InputDropdown.md +482 -0
  23. package/data/component-docs/InputRadio.md +173 -0
  24. package/data/component-docs/InputSearch.md +81 -0
  25. package/data/component-docs/InputSlider.md +189 -0
  26. package/data/component-docs/InputText.md +110 -0
  27. package/data/component-docs/InputToggle.md +141 -0
  28. package/data/component-docs/IntentMessage.md +293 -0
  29. package/data/component-docs/Legend.md +442 -0
  30. package/data/component-docs/Link.md +23 -0
  31. package/data/component-docs/List.md +300 -0
  32. package/data/component-docs/LoadingIndicator.md +198 -0
  33. package/data/component-docs/Menu.md +298 -0
  34. package/data/component-docs/MenuItem.md +100 -0
  35. package/data/component-docs/NavigationFooter.md +165 -0
  36. package/data/component-docs/NavigationHeader.md +500 -0
  37. package/data/component-docs/NavigationJumpTo.md +414 -0
  38. package/data/component-docs/Notification.md +288 -0
  39. package/data/component-docs/Pagination.md +67 -0
  40. package/data/component-docs/Panel.md +636 -0
  41. package/data/component-docs/Popover.md +448 -0
  42. package/data/component-docs/PreComposedTable.md +2947 -0
  43. package/data/component-docs/SegmentedControl.md +396 -0
  44. package/data/component-docs/StackedBarChart.md +475 -0
  45. package/data/component-docs/Table.md +2000 -0
  46. package/data/component-docs/TableHeader.md +53 -0
  47. package/data/component-docs/TableRow.md +74 -0
  48. package/data/component-docs/Tabs.md +455 -0
  49. package/data/component-docs/Tag.md +647 -0
  50. package/data/component-docs/TagFilter.md +131 -0
  51. package/data/component-docs/Tooltip.md +561 -0
  52. package/data/component-docs/TooltipCondensed.md +203 -0
  53. package/data/component-docs/TooltipTable.md +145 -0
  54. package/data/component-docs/index.json +262 -0
  55. package/data/component-list.json +2 -5
  56. package/data/component-props/Accordion.json +31 -11
  57. package/data/component-props/Alert.json +3 -3
  58. package/data/component-props/Autocomplete.json +150 -15
  59. package/data/component-props/Banner.json +31 -17
  60. package/data/component-props/Button.json +45 -20
  61. package/data/component-props/ButtonDropdown.json +36 -17
  62. package/data/component-props/ButtonDropdownLegacy.json +11 -11
  63. package/data/component-props/ButtonGroup.json +32 -11
  64. package/data/component-props/ButtonIcon.json +10 -10
  65. package/data/component-props/ButtonLegacy.json +19 -19
  66. package/data/component-props/ButtonToggle.json +32 -19
  67. package/data/component-props/ButtonToggleLegacy.json +11 -11
  68. package/data/component-props/Callout.json +58 -17
  69. package/data/component-props/CellBasic.json +94 -41
  70. package/data/component-props/CellComponent.json +23 -7
  71. package/data/component-props/CellHeader.json +62 -19
  72. package/data/component-props/ComplexFilter.json +52 -21
  73. package/data/component-props/ContentCard.json +70 -47
  74. package/data/component-props/Dialog.json +47 -14
  75. package/data/component-props/Dropdown.json +68 -31
  76. package/data/component-props/DropdownMenu.json +128 -23
  77. package/data/component-props/HeatmapChart.json +88 -12
  78. package/data/component-props/Hero.json +80 -39
  79. package/data/component-props/Icon.json +20 -9
  80. package/data/component-props/InputCheckbox.json +36 -14
  81. package/data/component-props/InputDropdown.json +75 -27
  82. package/data/component-props/InputRadio.json +31 -13
  83. package/data/component-props/InputSearch.json +41 -18
  84. package/data/component-props/InputSlider.json +46 -19
  85. package/data/component-props/InputText.json +26 -19
  86. package/data/component-props/InputToggle.json +26 -9
  87. package/data/component-props/IntentMessage.json +38 -9
  88. package/data/component-props/Link.json +13 -7
  89. package/data/component-props/LoadingIndicator.json +11 -3
  90. package/data/component-props/MenuItem.json +38 -19
  91. package/data/component-props/MenuSelect.json +8 -8
  92. package/data/component-props/NavigationFooter.json +49 -19
  93. package/data/component-props/NavigationHeader.json +150 -37
  94. package/data/component-props/NavigationJumpTo.json +33 -7
  95. package/data/component-props/Notification.json +39 -21
  96. package/data/component-props/Pagination.json +39 -11
  97. package/data/component-props/Panel.json +62 -9
  98. package/data/component-props/Popover.json +80 -7
  99. package/data/component-props/PreComposedTable.json +99 -27
  100. package/data/component-props/SegmentedControl.json +42 -3
  101. package/data/component-props/StackedBarChart.json +116 -42
  102. package/data/component-props/TableRow.json +38 -19
  103. package/data/component-props/Tabs.json +66 -9
  104. package/data/component-props/Tag.json +34 -17
  105. package/data/component-props/TagFilter.json +10 -3
  106. package/data/component-props/Tooltip.json +49 -18
  107. package/data/component-props/TooltipCondensed.json +18 -9
  108. package/data/component-props/TooltipTable.json +17 -7
  109. package/data/resources/{zeroheight-rules.md → documentation-rules.md} +43 -30
  110. package/data/resources/figma-rules.md +2 -2
  111. package/dist/stdio.js +112 -208
  112. package/package.json +9 -5
  113. package/data/component-props/ButtonDropdownV2.json +0 -34
  114. package/data/component-props/ButtonNew.json +0 -47
  115. package/data/component-props/ButtonToggleV2.json +0 -44
  116. package/data/component-props/ButtonV2.json +0 -43
  117. package/data/zeroheight-cache.json +0 -10259
  118. package/data/zeroheight-exports/Accordion.md +0 -774
  119. package/data/zeroheight-exports/Banner.md +0 -691
  120. package/data/zeroheight-exports/Borders.md +0 -117
  121. package/data/zeroheight-exports/Buttons.md +0 -1854
  122. package/data/zeroheight-exports/Callout.md +0 -931
  123. package/data/zeroheight-exports/Colors.md +0 -1755
  124. package/data/zeroheight-exports/Content-Card.md +0 -309
  125. package/data/zeroheight-exports/Contributing-to-SDS.md +0 -98
  126. package/data/zeroheight-exports/Control-Inputs.md +0 -2818
  127. package/data/zeroheight-exports/Corners.md +0 -104
  128. package/data/zeroheight-exports/Cover-page.md +0 -3
  129. package/data/zeroheight-exports/Dialog.md +0 -1367
  130. package/data/zeroheight-exports/Downloads.md +0 -236
  131. package/data/zeroheight-exports/Drop-Shadows.md +0 -79
  132. package/data/zeroheight-exports/Dropdown-Input.md +0 -1776
  133. package/data/zeroheight-exports/Dropdown-Menus.md +0 -913
  134. package/data/zeroheight-exports/Element-Status-Tracker.md +0 -267
  135. package/data/zeroheight-exports/Field-Inputs.md +0 -1405
  136. package/data/zeroheight-exports/Filters.md +0 -1111
  137. package/data/zeroheight-exports/Getting-Started.md +0 -1157
  138. package/data/zeroheight-exports/Hero.md +0 -414
  139. package/data/zeroheight-exports/Icons.md +0 -2067
  140. package/data/zeroheight-exports/Intent.md +0 -86
  141. package/data/zeroheight-exports/Introduction-to-Bases.md +0 -35
  142. package/data/zeroheight-exports/Introduction-to-Cells.md +0 -35
  143. package/data/zeroheight-exports/Introduction-to-DNA.md +0 -35
  144. package/data/zeroheight-exports/Introduction-to-Genes.md +0 -35
  145. package/data/zeroheight-exports/Introduction.md +0 -115
  146. package/data/zeroheight-exports/Link.md +0 -432
  147. package/data/zeroheight-exports/Lists.md +0 -986
  148. package/data/zeroheight-exports/Loading-Indicator.md +0 -41
  149. package/data/zeroheight-exports/Navigation.md +0 -1190
  150. package/data/zeroheight-exports/Notification.md +0 -1038
  151. package/data/zeroheight-exports/Panel.md +0 -1030
  152. package/data/zeroheight-exports/README.md +0 -122
  153. package/data/zeroheight-exports/Responsive-Design.md +0 -330
  154. package/data/zeroheight-exports/Search-Input.md +0 -91
  155. package/data/zeroheight-exports/Search.md +0 -822
  156. package/data/zeroheight-exports/Segmented-Control.md +0 -609
  157. package/data/zeroheight-exports/Spacing.md +0 -297
  158. package/data/zeroheight-exports/Table.md +0 -5345
  159. package/data/zeroheight-exports/Tabs.md +0 -1324
  160. package/data/zeroheight-exports/Tags.md +0 -1545
  161. package/data/zeroheight-exports/Text-Link.md +0 -268
  162. package/data/zeroheight-exports/Theming.md +0 -362
  163. package/data/zeroheight-exports/Tooltips.md +0 -2015
  164. package/data/zeroheight-exports/Typography.md +0 -800
@@ -0,0 +1,636 @@
1
+ # Panel
2
+
3
+ ## Source Code
4
+
5
+ The component's source code in the SDS codebase can be found here.
6
+
7
+ ## Import
8
+
9
+ **React TypeScript**
10
+
11
+ ```tsx
12
+ import { Panel } from "@czi-sds/components";
13
+ ```
14
+
15
+ ## Code examples
16
+
17
+ > **Note:** A panel fixes itself to an edge of the viewport, which on this page means an edge of the docs rather than of the example. Each example below therefore holds its panel inside its own frame, with a wrapper that fixed positioning resolves against and, for the overlay panels, a container for the portal and another for the transition, so the panel slides the width of the frame rather than of the window. None of that belongs in an application.
18
+
19
+ ### Basic panel
20
+
21
+ A persistent panel on the left, with the page's content shifted by the same width so the two do not overlap.
22
+
23
+ **Example: BasicPanel**
24
+
25
+ ```tsx
26
+ // sdsType="basic" is a persistent drawer: it sits in the page rather than over
27
+ // it, and nothing closes it but your own control. It does not move the page's
28
+ // content, so shift that yourself by the same width you gave the panel.
29
+ //
30
+ // Panel fixes itself to the edge of the viewport, which on this page is the edge
31
+ // of the docs rather than of the example. Stage carries a transform, which makes
32
+ // it the box that fixed positioning resolves against, so the panel stays in the
33
+ // frame, flush against its edge as it would be against the window's. A real page
34
+ // wants the viewport and needs none of this, and the padding below stands in for
35
+ // whatever spacing the page's own content already has.
36
+
37
+ import {
38
+ Button,
39
+ Icon,
40
+ Panel,
41
+ fontBodyS,
42
+ getSemanticColors,
43
+ type CommonThemeProps,
44
+ } from "@czi-sds/components";
45
+ import styled from "@emotion/styled";
46
+ import { useState } from "react";
47
+
48
+ const PANEL_WIDTH = 280;
49
+
50
+ const Stage = styled.div`
51
+ height: 320px;
52
+ overflow: clip;
53
+ transform: translateZ(0);
54
+ `;
55
+
56
+ const Content = styled.div<CommonThemeProps & { shifted: boolean }>`
57
+ ${fontBodyS}
58
+
59
+ ${(props) => {
60
+ const semanticColors = getSemanticColors(props);
61
+
62
+ return `
63
+ color: ${semanticColors?.base?.textPrimary};
64
+ margin-left: ${props.shifted ? `${PANEL_WIDTH}px` : "0"};
65
+ padding: 50px;
66
+ transition: margin-left 225ms ease-out;
67
+ `;
68
+ }}
69
+ `;
70
+
71
+ function App() {
72
+ const [open, setOpen] = useState(true);
73
+
74
+ return (
75
+ <div className="app">
76
+ <Stage>
77
+ <Panel open={open} sdsType="basic" width={PANEL_WIDTH}>
78
+ Filters, dataset pickers, and anything else that changes what the page
79
+ shows.
80
+ </Panel>
81
+
82
+ <Content shifted={open}>
83
+ <Button
84
+ onClick={() => setOpen((prev) => !prev)}
85
+ sdsStyle="solid"
86
+ sdsType="primary"
87
+ startIcon={<Icon sdsIcon="Filter" sdsSize="s" />}
88
+ >
89
+ {open ? "Hide filters" : "Show filters"}
90
+ </Button>
91
+
92
+ <p>
93
+ The page's own content moves aside for a basic panel, so the two
94
+ never overlap.
95
+ </p>
96
+ </Content>
97
+ </Stage>
98
+ </div>
99
+ );
100
+ }
101
+
102
+ export default App;
103
+ ```
104
+
105
+ ### Overlay panel
106
+
107
+ An overlay panel on the right with a header, the default close button, and `isBackdropClickEnabled` so a click outside closes it through `onClose`.
108
+
109
+ **Example: OverlayPanel**
110
+
111
+ ```tsx
112
+ // sdsType="overlay" is a temporary drawer: it floats over the page and always
113
+ // draws a close button, so give it closeButtonOnClick. isBackdropClickEnabled
114
+ // adds a click-outside target, which reports through onClose rather than through
115
+ // closeButtonOnClick, so both have to set the state.
116
+ //
117
+ // Panel fixes itself to the edge of the viewport, which on this page is the edge
118
+ // of the docs rather than of the example. ModalProps.container renders it inside
119
+ // Stage instead, which is why the panel waits for that node to exist, and
120
+ // Stage's transform makes it the box that fixed positioning resolves against.
121
+ // The transition needs the same node, or it measures its slide against the
122
+ // window and spends most of the animation outside the frame. Stage clips with
123
+ // overflow: clip rather than hidden, because a hidden box is one the browser can
124
+ // scroll: it would scroll the frame to reveal the panel as the panel takes focus
125
+ // on its way in, dragging the example's own content sideways. A real page wants
126
+ // the viewport and needs none of this; the padding on Stage just stands in for
127
+ // the spacing a page's own content would have.
128
+
129
+ import {
130
+ Button,
131
+ Icon,
132
+ Panel,
133
+ fontBodyS,
134
+ fontHeaderM,
135
+ getSemanticColors,
136
+ type CommonThemeProps,
137
+ } from "@czi-sds/components";
138
+ import styled from "@emotion/styled";
139
+ import { useState } from "react";
140
+
141
+ const Stage = styled.div<CommonThemeProps>`
142
+ ${fontBodyS}
143
+
144
+ ${(props) => {
145
+ const semanticColors = getSemanticColors(props);
146
+
147
+ return `
148
+ color: ${semanticColors?.base?.textPrimary};
149
+ height: 340px;
150
+ overflow: clip;
151
+ padding: 50px;
152
+ transform: translateZ(0);
153
+ `;
154
+ }}
155
+ `;
156
+
157
+ const Header = styled.h3<CommonThemeProps>`
158
+ ${fontHeaderM}
159
+ margin: 0;
160
+ `;
161
+
162
+ function App() {
163
+ const [open, setOpen] = useState(false);
164
+ const [stage, setStage] = useState<HTMLDivElement | null>(null);
165
+
166
+ return (
167
+ <div className="app">
168
+ <Stage ref={setStage}>
169
+ <Button
170
+ onClick={() => setOpen(true)}
171
+ sdsStyle="solid"
172
+ sdsType="primary"
173
+ startIcon={<Icon sdsIcon="InfoCircle" sdsSize="s" />}
174
+ >
175
+ Show details
176
+ </Button>
177
+
178
+ <p>
179
+ An overlay panel reads a piece of the page rather than changing it, so
180
+ the content underneath stays where it is.
181
+ </p>
182
+
183
+ {stage && (
184
+ <Panel
185
+ closeButtonOnClick={() => setOpen(false)}
186
+ HeaderComponent={<Header>Sample details</Header>}
187
+ isBackdropClickEnabled
188
+ ModalProps={{ container: stage }}
189
+ onClose={() => setOpen(false)}
190
+ open={open}
191
+ position="right"
192
+ sdsType="overlay"
193
+ slotProps={{ transition: { container: stage } }}
194
+ >
195
+ Collection date, tissue, and the rest of the record for whichever
196
+ row the page has selected.
197
+ </Panel>
198
+ )}
199
+ </Stage>
200
+ </div>
201
+ );
202
+ }
203
+
204
+ export default App;
205
+ ```
206
+
207
+ ### Overlay panel along the bottom
208
+
209
+ The bottom position spans the full width, and the `width` prop becomes the panel's height. This one has no backdrop, so clicks outside it land on the page underneath.
210
+
211
+ **Example: OverlayPanelBottom**
212
+
213
+ ```tsx
214
+ // position="bottom" belongs to the overlay type only, and it turns the width
215
+ // prop into a height: the panel spans the full width and stands as tall as the
216
+ // value you pass. The paper also has a 320px floor in both directions, so a
217
+ // smaller number changes nothing.
218
+ //
219
+ // Panel fixes itself to the edge of the viewport, which on this page is the edge
220
+ // of the docs rather than of the example. ModalProps.container renders it inside
221
+ // Stage instead, which is why the panel waits for that node to exist, and
222
+ // Stage's transform makes it the box that fixed positioning resolves against.
223
+ // The transition needs the same node, or it measures its slide against the
224
+ // window and spends most of the animation outside the frame, and Stage clips with
225
+ // overflow: clip rather than hidden so the browser cannot scroll the frame to
226
+ // reveal the panel while it is still on its way in. A real page wants the
227
+ // viewport and needs none of this; the padding on Stage just stands in for the
228
+ // spacing a page's own content would have.
229
+
230
+ import {
231
+ Button,
232
+ Icon,
233
+ Panel,
234
+ fontBodyS,
235
+ fontHeaderM,
236
+ getSemanticColors,
237
+ type CommonThemeProps,
238
+ } from "@czi-sds/components";
239
+ import styled from "@emotion/styled";
240
+ import { useState } from "react";
241
+
242
+ const Stage = styled.div<CommonThemeProps>`
243
+ ${fontBodyS}
244
+
245
+ ${(props) => {
246
+ const semanticColors = getSemanticColors(props);
247
+
248
+ return `
249
+ color: ${semanticColors?.base?.textPrimary};
250
+ height: 520px;
251
+ overflow: clip;
252
+ padding: 50px;
253
+ transform: translateZ(0);
254
+ `;
255
+ }}
256
+ `;
257
+
258
+ const Header = styled.h3<CommonThemeProps>`
259
+ ${fontHeaderM}
260
+ margin: 0;
261
+ `;
262
+
263
+ const Table = styled.div<CommonThemeProps>`
264
+ ${(props) => {
265
+ const semanticColors = getSemanticColors(props);
266
+
267
+ return `
268
+ border: 1px dashed ${semanticColors?.base?.divider};
269
+ border-radius: 4px;
270
+ color: ${semanticColors?.base?.textSecondary};
271
+ padding: 24px;
272
+ `;
273
+ }}
274
+ `;
275
+
276
+ function App() {
277
+ const [open, setOpen] = useState(false);
278
+ const [stage, setStage] = useState<HTMLDivElement | null>(null);
279
+
280
+ return (
281
+ <div className="app">
282
+ <Stage ref={setStage}>
283
+ <Button
284
+ onClick={() => setOpen(true)}
285
+ sdsStyle="solid"
286
+ sdsType="primary"
287
+ startIcon={<Icon sdsIcon="Table" sdsSize="s" />}
288
+ >
289
+ Show table
290
+ </Button>
291
+
292
+ <p>
293
+ A bottom panel suits content that is wider than it is tall, such as a
294
+ table of the rows behind a chart.
295
+ </p>
296
+
297
+ {stage && (
298
+ <Panel
299
+ closeButtonOnClick={() => setOpen(false)}
300
+ HeaderComponent={<Header>Underlying rows</Header>}
301
+ ModalProps={{ container: stage }}
302
+ onClose={() => setOpen(false)}
303
+ open={open}
304
+ position="bottom"
305
+ sdsType="overlay"
306
+ slotProps={{ transition: { container: stage } }}
307
+ width={360}
308
+ >
309
+ <Table>[Table of rows, 360px of height to work with]</Table>
310
+ </Panel>
311
+ )}
312
+ </Stage>
313
+ </div>
314
+ );
315
+ }
316
+
317
+ export default App;
318
+ ```
319
+
320
+ ### Overlay panel with a custom close control
321
+
322
+ `CloseButtonComponent` replaces the X with anything you like, and the header holds more than a title.
323
+
324
+ **Example: OverlayPanelCustomClose**
325
+
326
+ ```tsx
327
+ // CloseButtonComponent replaces the default X. The click handler stays on
328
+ // closeButtonOnClick, which the panel attaches to the wrapper around whatever
329
+ // you pass, so the replacement needs no onClick of its own. The wrapper also
330
+ // forces any icon inside a button to 24px, so sdsSize on it has no effect.
331
+ //
332
+ // Stage, ModalProps.container and the transition's container are here only to
333
+ // keep the panel and its slide inside the example frame, as in the examples
334
+ // above.
335
+
336
+ import {
337
+ Button,
338
+ Icon,
339
+ Panel,
340
+ fontBodyS,
341
+ fontHeaderM,
342
+ getSemanticColors,
343
+ type CommonThemeProps,
344
+ } from "@czi-sds/components";
345
+ import styled from "@emotion/styled";
346
+ import { useState } from "react";
347
+
348
+ const Stage = styled.div<CommonThemeProps>`
349
+ ${fontBodyS}
350
+
351
+ ${(props) => {
352
+ const semanticColors = getSemanticColors(props);
353
+
354
+ return `
355
+ color: ${semanticColors?.base?.textPrimary};
356
+ height: 340px;
357
+ overflow: clip;
358
+ padding: 50px;
359
+ transform: translateZ(0);
360
+ `;
361
+ }}
362
+ `;
363
+
364
+ const Header = styled.div<CommonThemeProps>`
365
+ ${(props) => {
366
+ const semanticColors = getSemanticColors(props);
367
+
368
+ return `
369
+ h3 {
370
+ ${fontHeaderM(props)}
371
+ margin: 0;
372
+ }
373
+
374
+ p {
375
+ ${fontBodyS(props)}
376
+ color: ${semanticColors?.base?.textSecondary};
377
+ margin: 4px 0 0 0;
378
+ }
379
+ `;
380
+ }}
381
+ `;
382
+
383
+ function App() {
384
+ const [open, setOpen] = useState(false);
385
+ const [stage, setStage] = useState<HTMLDivElement | null>(null);
386
+
387
+ return (
388
+ <div className="app">
389
+ <Stage ref={setStage}>
390
+ <Button
391
+ onClick={() => setOpen(true)}
392
+ sdsStyle="solid"
393
+ sdsType="primary"
394
+ startIcon={<Icon sdsIcon="Gear" sdsSize="s" />}
395
+ >
396
+ Show settings
397
+ </Button>
398
+
399
+ <p>The header holds as much as you want to put in it.</p>
400
+
401
+ {stage && (
402
+ <Panel
403
+ closeButtonOnClick={() => setOpen(false)}
404
+ CloseButtonComponent={
405
+ <Button sdsStyle="minimal" sdsType="primary">
406
+ Done
407
+ </Button>
408
+ }
409
+ HeaderComponent={
410
+ <Header>
411
+ <h3>Display settings</h3>
412
+ <p>Applies to this session only</p>
413
+ </Header>
414
+ }
415
+ ModalProps={{ container: stage }}
416
+ onClose={() => setOpen(false)}
417
+ open={open}
418
+ position="right"
419
+ sdsType="overlay"
420
+ slotProps={{ transition: { container: stage } }}
421
+ width={360}
422
+ >
423
+ Colour scale, point size, and the other controls for the plot.
424
+ </Panel>
425
+ )}
426
+ </Stage>
427
+ </div>
428
+ );
429
+ }
430
+
431
+ export default App;
432
+ ```
433
+
434
+ ### Changing the animation speed
435
+
436
+ `transitionDuration` times the slide. Each button below reopens the same panel at a different setting, from the 225ms default to none at all, and the example holds the setting while the panel closes so the way out matches the way in.
437
+
438
+ **Example: OverlayPanelTransition**
439
+
440
+ ```tsx
441
+ // transitionDuration sets how long the panel takes to travel in and out, in
442
+ // milliseconds. A single number times both directions; { enter, exit } times them
443
+ // separately, and 0 removes the animation. The Panel defaults to
444
+ // { enter: 225, exit: 195 }, which is MUI's drawer timing rather than the SDS
445
+ // theme's much shorter one.
446
+ //
447
+ // The duration stays put while the panel closes so the exit runs at the speed it
448
+ // opened with. Swapping it back on close would exit at the new speed instead.
449
+ //
450
+ // Stage, ModalProps.container and the transition's container are here only to
451
+ // keep the panel and its slide inside the example frame, as in the examples
452
+ // above.
453
+
454
+ import {
455
+ Button,
456
+ Panel,
457
+ fontBodyS,
458
+ fontBodyXs,
459
+ fontHeaderM,
460
+ getSemanticColors,
461
+ getSpaces,
462
+ type CommonThemeProps,
463
+ type PanelProps,
464
+ } from "@czi-sds/components";
465
+ import styled from "@emotion/styled";
466
+ import { useState } from "react";
467
+
468
+ const SPEEDS: {
469
+ duration: PanelProps["transitionDuration"];
470
+ label: string;
471
+ }[] = [
472
+ { duration: { enter: 225, exit: 195 }, label: "Default" },
473
+ { duration: { enter: 700, exit: 500 }, label: "Slow" },
474
+ { duration: 120, label: "Quick, both ways" },
475
+ { duration: 0, label: "None" },
476
+ ];
477
+
478
+ const Stage = styled.div<CommonThemeProps>`
479
+ ${fontBodyS}
480
+
481
+ ${(props) => {
482
+ const semanticColors = getSemanticColors(props);
483
+
484
+ return `
485
+ color: ${semanticColors?.base?.textPrimary};
486
+ height: 340px;
487
+ overflow: clip;
488
+ padding: 50px;
489
+ transform: translateZ(0);
490
+ `;
491
+ }}
492
+ `;
493
+
494
+ const Controls = styled.div<CommonThemeProps>`
495
+ ${(props) => {
496
+ const spaces = getSpaces(props);
497
+
498
+ return `
499
+ display: flex;
500
+ flex-wrap: wrap;
501
+ gap: ${spaces?.s}px;
502
+ `;
503
+ }}
504
+ `;
505
+
506
+ const Readout = styled.p<CommonThemeProps>`
507
+ ${fontBodyXs}
508
+
509
+ ${(props) => {
510
+ const semanticColors = getSemanticColors(props);
511
+
512
+ return `
513
+ color: ${semanticColors?.base?.textSecondary};
514
+ `;
515
+ }}
516
+ `;
517
+
518
+ const Header = styled.h3<CommonThemeProps>`
519
+ ${fontHeaderM}
520
+ margin: 0;
521
+ `;
522
+
523
+ function App() {
524
+ const [open, setOpen] = useState(false);
525
+ const [speed, setSpeed] = useState(SPEEDS[0]);
526
+ const [stage, setStage] = useState<HTMLDivElement | null>(null);
527
+
528
+ return (
529
+ <div className="app">
530
+ <Stage ref={setStage}>
531
+ <Controls>
532
+ {SPEEDS.map((option) => (
533
+ <Button
534
+ key={option.label}
535
+ onClick={() => {
536
+ setSpeed(option);
537
+ setOpen(true);
538
+ }}
539
+ sdsStyle="outline"
540
+ sdsType="secondary"
541
+ >
542
+ {option.label}
543
+ </Button>
544
+ ))}
545
+ </Controls>
546
+
547
+ <Readout>
548
+ Opening with transitionDuration of {JSON.stringify(speed.duration)}
549
+ </Readout>
550
+
551
+ {stage && (
552
+ <Panel
553
+ closeButtonOnClick={() => setOpen(false)}
554
+ HeaderComponent={<Header>Sample details</Header>}
555
+ isBackdropClickEnabled
556
+ ModalProps={{ container: stage }}
557
+ onClose={() => setOpen(false)}
558
+ open={open}
559
+ position="right"
560
+ sdsType="overlay"
561
+ slotProps={{ transition: { container: stage } }}
562
+ transitionDuration={speed.duration}
563
+ >
564
+ Close this and pick another speed to compare them.
565
+ </Panel>
566
+ )}
567
+ </Stage>
568
+ </div>
569
+ );
570
+ }
571
+
572
+ export default App;
573
+ ```
574
+
575
+ ## SDS vs MUI
576
+
577
+ The SDS Panel wraps the MUI Drawer, with these differences:
578
+
579
+ - **sdsType instead of variant:** MUI picks the kind of drawer with `variant`; SDS picks it with `sdsType`, which takes two of the three MUI variants.
580
+
581
+ - `"basic"` is MUI's persistent drawer.
582
+
583
+ - `"overlay"` is MUI's temporary drawer.
584
+
585
+ - **position instead of anchor:** MUI's `anchor` takes four edges. SDS narrows it to `position`: `"left"` or `"right"` for a basic panel, plus `"bottom"` for an overlay. A basic panel asked for `"bottom"` is anchored left instead, and TypeScript rejects the combination up front.
586
+
587
+ - `width`: MUI leaves the drawer's size to your own styles. SDS takes a `width` and defaults it to 240px for a basic panel and 320px for an overlay. Along the bottom edge it sets the height instead, since the panel spans the screen. Either way the panel will not go below its type's default in either direction, so smaller values have no effect.
588
+
589
+ - **A header row on the overlay:** an overlay panel always draws a close button, and `HeaderComponent` fills the space beside it. Both live in a bar that sticks to the top of the panel while its content scrolls under a short gradient. A basic panel has no such bar and no close control of its own.
590
+
591
+ - **A backdrop you opt into:** MUI's temporary drawer always lays a visible backdrop over the page. SDS hides it, so the page behind an overlay panel stays legible and clickable. `isBackdropClickEnabled` brings back an invisible one, which gives you a click-outside target without dimming anything.
592
+
593
+ - **Motion the SDS theme does not shorten:** every MUI transition reads its length from the theme, and SDS sets those lengths short (20ms in, 10ms out) for the small elements that make up most of the system. A panel is far too large to cross the screen in a frame, so it keeps MUI's own drawer timings of 225ms in and 195ms out. `transitionDuration` overrides either direction.
594
+
595
+ - **Focus and scrolling stay with the page:** SDS forces `disableEnforceFocus` and `disableScrollLock` on the underlying Modal, so an open panel neither traps the keyboard nor freezes the page's scrollbar. Anything else you pass through `ModalProps` is kept. The paper also gets a default `aria-label` of `"Panel"`, which `slotProps.paper` can replace.
596
+
597
+ ## Behavior notes
598
+
599
+ - Nothing about a basic panel is automatic: it does not close itself, and it does not move the page's content. Put a control on the page to toggle `open`, and shift your content by the same width you gave the panel so the two do not overlap.
600
+
601
+ - An overlay panel closes through two separate props. `closeButtonOnClick` fires from the header's close button; `onClose` fires on Escape, and on a click outside the panel when `isBackdropClickEnabled` is set. Set both to the same handler, or the panel will only close one way.
602
+
603
+ - The close button is drawn whether or not you handle it, so an overlay panel without `closeButtonOnClick` offers a control that does nothing.
604
+
605
+ - `closeButtonOnClick` is attached to the wrapper around the close button rather than to the button itself, so a `CloseButtonComponent` needs no click handler of its own. That wrapper also sizes any icon inside a button to 24px, which overrides `sdsSize` on the icon.
606
+
607
+ - Two basic panels can be open at once, one on each side, because each is part of the page's layout. Overlay panels are not, so a second one opens over the first rather than beside it; the guidance above asks for one at a time.
608
+
609
+ - An overlay panel takes focus when it opens but does not keep it, so Escape only reaches the panel while focus is still inside. Once the reader clicks or tabs back into the page, the close button and the backdrop are the only ways out.
610
+
611
+ - Because an overlay panel deliberately does not trap focus, MUI's `aria-hidden-focus` rule reports a violation against it. SDS suppresses that rule for the Panel's stories while waiting on a fix upstream.
612
+
613
+ - The panel slides from the edge it is anchored to over 225ms, and back out over 195ms. `transitionDuration` changes both, or either one on its own with `{ enter, exit }`. These are MUI's own drawer timings rather than the SDS theme's, which are set short for smaller elements and leave a panel looking like it jumps into place. Note that SDS leaves MUI's `motion.reducedMotion` setting at `"never"`, so the animation plays regardless of the reader's reduced-motion preference; an application that wants to honour it can set that theme option to `"system"`.
614
+
615
+ ## MUI Documentation
616
+
617
+ Documentation for the underlying MUI component can be found [here](https://mui.com/material-ui/react-drawer/).
618
+
619
+ ## Props
620
+
621
+ Any custom SDS props and MUI props required for implementation are found on the table below. See the MUI documentation for additional optional props.
622
+
623
+ | Name | Type | Default | Description |
624
+ | ------------------------ | -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
625
+ | `sdsType` | `"basic" \| "overlay"` | - (required) | `"basic"` sits in the page and stays until you close it; `"overlay"` floats over the page with a header and close button. It also decides which of the props below apply, so TypeScript needs it spelled out even though the component falls back to `"basic"`. |
626
+ | `open` | `boolean` | `false` | Whether the panel is showing. The panel never changes this itself, so hold it in your own state. |
627
+ | `position` | `"left"` \| `"right"` \| `"bottom"` (overlay only) | `"left"` | Which edge the panel is attached to. A basic panel takes `"left"` or `"right"`; an overlay adds `"bottom"`. |
628
+ | `width` | `number \| string` | `240` (basic) `320` (overlay) | How wide the panel is, or how tall it is when positioned along the bottom. Numbers are read as pixels. Values below the default are ignored, since the panel keeps that as its minimum in both directions. |
629
+ | `HeaderComponent` | `React.ReactNode` | - | Overlay only. Fills the header bar beside the close button. Leave it out and the bar holds the close button alone. |
630
+ | `closeButtonOnClick` | `React.MouseEventHandler<HTMLDivElement>` | - | Overlay only. Runs when the header's close control is clicked. Without it, the control does nothing. |
631
+ | `CloseButtonComponent` | `React.ReactNode` | `<Button` `sdsStyle="minimal"` `sdsType="secondary"` `size="large"` `backgroundOnHover={false}` `aria-label="Panel Toggle">` `<Icon sdsIcon="XMark" sdsSize="l" />` `</Button>` | Overlay only. Replaces the close button. Clicks are handled by the wrapper around it, so it needs no handler of its own. |
632
+ | `isBackdropClickEnabled` | `boolean` | `false` | Overlay only in practice. Adds an invisible backdrop so a click outside the panel reaches `onClose`. Left off, clicks pass through to the page. |
633
+ | `onClose` | `(event, reason) => void` | - | MUI's Drawer callback, fired on a backdrop click and on Escape while focus is inside the panel. It does not fire for the header's close button, which reports through `closeButtonOnClick`. |
634
+ | `transitionDuration` | `number` \| `{ appear?: number,` `enter?: number,` `exit?: number }` | `{ enter: 225, exit: 195 }` | How long the panel takes to slide in and out, in milliseconds. The Panel keeps MUI's drawer timings instead of the much shorter ones in the SDS theme, which a surface this large arrives too quickly to read as motion. Pass `0` for no animation. |
635
+ | `ModalProps` | `ModalProps` | - | Passed to the Modal behind an overlay panel, except for `disableEnforceFocus` and `disableScrollLock`, which SDS pins to `true`. |
636
+ | `slotProps` | `DrawerProps["slotProps"]` | `{ backdrop: { invisible: true },` `paper: { aria-label: "Panel" } }` | Merged with the SDS defaults slot by slot, so a paper `aria-label` or `aria-labelledby` of your own replaces the default name while the rest of the defaults survive. |