@czi-sds/mcp 0.16.0 → 0.18.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 (167) 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/ProteinStructureViewer.md +1044 -0
  44. package/data/component-docs/SegmentedControl.md +396 -0
  45. package/data/component-docs/StackedBarChart.md +475 -0
  46. package/data/component-docs/Table.md +2000 -0
  47. package/data/component-docs/TableHeader.md +53 -0
  48. package/data/component-docs/TableRow.md +74 -0
  49. package/data/component-docs/Tabs.md +455 -0
  50. package/data/component-docs/Tag.md +647 -0
  51. package/data/component-docs/TagFilter.md +131 -0
  52. package/data/component-docs/Tooltip.md +561 -0
  53. package/data/component-docs/TooltipCondensed.md +203 -0
  54. package/data/component-docs/TooltipTable.md +145 -0
  55. package/data/component-docs/index.json +267 -0
  56. package/data/component-list.json +2 -5
  57. package/data/component-props/Accordion.json +31 -11
  58. package/data/component-props/Alert.json +3 -3
  59. package/data/component-props/Autocomplete.json +150 -15
  60. package/data/component-props/Banner.json +31 -17
  61. package/data/component-props/Button.json +45 -20
  62. package/data/component-props/ButtonDropdown.json +36 -17
  63. package/data/component-props/ButtonDropdownLegacy.json +11 -11
  64. package/data/component-props/ButtonGroup.json +32 -11
  65. package/data/component-props/ButtonIcon.json +10 -10
  66. package/data/component-props/ButtonLegacy.json +19 -19
  67. package/data/component-props/ButtonToggle.json +32 -19
  68. package/data/component-props/ButtonToggleLegacy.json +11 -11
  69. package/data/component-props/Callout.json +58 -17
  70. package/data/component-props/CellBasic.json +94 -41
  71. package/data/component-props/CellComponent.json +23 -7
  72. package/data/component-props/CellHeader.json +62 -19
  73. package/data/component-props/ComplexFilter.json +52 -21
  74. package/data/component-props/ContentCard.json +70 -47
  75. package/data/component-props/Dialog.json +47 -14
  76. package/data/component-props/Dropdown.json +68 -31
  77. package/data/component-props/DropdownMenu.json +128 -23
  78. package/data/component-props/HeatmapChart.json +88 -12
  79. package/data/component-props/Hero.json +80 -39
  80. package/data/component-props/Icon.json +20 -9
  81. package/data/component-props/InputCheckbox.json +36 -14
  82. package/data/component-props/InputDropdown.json +75 -27
  83. package/data/component-props/InputRadio.json +31 -13
  84. package/data/component-props/InputSearch.json +41 -18
  85. package/data/component-props/InputSlider.json +46 -19
  86. package/data/component-props/InputText.json +26 -19
  87. package/data/component-props/InputToggle.json +26 -9
  88. package/data/component-props/IntentMessage.json +38 -9
  89. package/data/component-props/Link.json +13 -7
  90. package/data/component-props/LoadingIndicator.json +11 -3
  91. package/data/component-props/MenuItem.json +38 -19
  92. package/data/component-props/MenuSelect.json +8 -8
  93. package/data/component-props/NavigationFooter.json +49 -19
  94. package/data/component-props/NavigationHeader.json +150 -37
  95. package/data/component-props/NavigationJumpTo.json +33 -7
  96. package/data/component-props/Notification.json +39 -21
  97. package/data/component-props/Pagination.json +39 -11
  98. package/data/component-props/Panel.json +62 -9
  99. package/data/component-props/Popover.json +80 -7
  100. package/data/component-props/PreComposedTable.json +99 -27
  101. package/data/component-props/ProteinStructureViewer.json +117 -0
  102. package/data/component-props/SegmentedControl.json +42 -3
  103. package/data/component-props/StackedBarChart.json +116 -42
  104. package/data/component-props/TableRow.json +38 -19
  105. package/data/component-props/Tabs.json +66 -9
  106. package/data/component-props/Tag.json +34 -17
  107. package/data/component-props/TagFilter.json +10 -3
  108. package/data/component-props/Tooltip.json +49 -18
  109. package/data/component-props/TooltipCondensed.json +18 -9
  110. package/data/component-props/TooltipTable.json +17 -7
  111. package/data/resources/{zeroheight-rules.md → documentation-rules.md} +43 -30
  112. package/data/resources/figma-rules.md +2 -2
  113. package/data/tailwind.json +20 -14
  114. package/dist/stdio.js +112 -208
  115. package/package.json +9 -5
  116. package/data/component-props/ButtonDropdownV2.json +0 -34
  117. package/data/component-props/ButtonNew.json +0 -47
  118. package/data/component-props/ButtonToggleV2.json +0 -44
  119. package/data/component-props/ButtonV2.json +0 -43
  120. package/data/zeroheight-cache.json +0 -10259
  121. package/data/zeroheight-exports/Accordion.md +0 -774
  122. package/data/zeroheight-exports/Banner.md +0 -691
  123. package/data/zeroheight-exports/Borders.md +0 -117
  124. package/data/zeroheight-exports/Buttons.md +0 -1854
  125. package/data/zeroheight-exports/Callout.md +0 -931
  126. package/data/zeroheight-exports/Colors.md +0 -1755
  127. package/data/zeroheight-exports/Content-Card.md +0 -309
  128. package/data/zeroheight-exports/Contributing-to-SDS.md +0 -98
  129. package/data/zeroheight-exports/Control-Inputs.md +0 -2818
  130. package/data/zeroheight-exports/Corners.md +0 -104
  131. package/data/zeroheight-exports/Cover-page.md +0 -3
  132. package/data/zeroheight-exports/Dialog.md +0 -1367
  133. package/data/zeroheight-exports/Downloads.md +0 -236
  134. package/data/zeroheight-exports/Drop-Shadows.md +0 -79
  135. package/data/zeroheight-exports/Dropdown-Input.md +0 -1776
  136. package/data/zeroheight-exports/Dropdown-Menus.md +0 -913
  137. package/data/zeroheight-exports/Element-Status-Tracker.md +0 -267
  138. package/data/zeroheight-exports/Field-Inputs.md +0 -1405
  139. package/data/zeroheight-exports/Filters.md +0 -1111
  140. package/data/zeroheight-exports/Getting-Started.md +0 -1157
  141. package/data/zeroheight-exports/Hero.md +0 -414
  142. package/data/zeroheight-exports/Icons.md +0 -2067
  143. package/data/zeroheight-exports/Intent.md +0 -86
  144. package/data/zeroheight-exports/Introduction-to-Bases.md +0 -35
  145. package/data/zeroheight-exports/Introduction-to-Cells.md +0 -35
  146. package/data/zeroheight-exports/Introduction-to-DNA.md +0 -35
  147. package/data/zeroheight-exports/Introduction-to-Genes.md +0 -35
  148. package/data/zeroheight-exports/Introduction.md +0 -115
  149. package/data/zeroheight-exports/Link.md +0 -432
  150. package/data/zeroheight-exports/Lists.md +0 -986
  151. package/data/zeroheight-exports/Loading-Indicator.md +0 -41
  152. package/data/zeroheight-exports/Navigation.md +0 -1190
  153. package/data/zeroheight-exports/Notification.md +0 -1038
  154. package/data/zeroheight-exports/Panel.md +0 -1030
  155. package/data/zeroheight-exports/README.md +0 -122
  156. package/data/zeroheight-exports/Responsive-Design.md +0 -330
  157. package/data/zeroheight-exports/Search-Input.md +0 -91
  158. package/data/zeroheight-exports/Search.md +0 -822
  159. package/data/zeroheight-exports/Segmented-Control.md +0 -609
  160. package/data/zeroheight-exports/Spacing.md +0 -297
  161. package/data/zeroheight-exports/Table.md +0 -5345
  162. package/data/zeroheight-exports/Tabs.md +0 -1324
  163. package/data/zeroheight-exports/Tags.md +0 -1545
  164. package/data/zeroheight-exports/Text-Link.md +0 -268
  165. package/data/zeroheight-exports/Theming.md +0 -362
  166. package/data/zeroheight-exports/Tooltips.md +0 -2015
  167. package/data/zeroheight-exports/Typography.md +0 -800
@@ -0,0 +1,448 @@
1
+ # Popover
2
+
3
+ ## Source Code
4
+
5
+ The component's source code in the SDS codebase can be found [here](https://github.com/chanzuckerberg/sci-components/blob/main/packages/components/src/core/Popover/index.tsx).
6
+
7
+ ## Import
8
+
9
+ **React TypeScript**
10
+
11
+ ```tsx
12
+ import { Popover } from "@czi-sds/components";
13
+ ```
14
+
15
+ ## Code examples
16
+
17
+ ### **Default Popover**
18
+
19
+ The least a popover needs: an anchor kept in state, an `open` derived from it, and an `onClose` that clears it. It opens below the trigger, with the 8px gap the SDS defaults provide.
20
+
21
+ **Example: DefaultPopover**
22
+
23
+ ```tsx
24
+ // The least a popover needs: the trigger records itself as the anchor, `open` is
25
+ // derived from whether there is one, and `onClose` clears it. The popover owns no
26
+ // open state of its own, so nothing closes it unless you do.
27
+ //
28
+ // anchorEl has to be state rather than a ref, because the popover measures the
29
+ // anchor as it renders and so needs a render to happen once the element is known.
30
+ //
31
+ // The paper pads itself by 6px and 12px, which is why the text below is written
32
+ // straight onto it. onClose fires for a backdrop click and for Escape; the second
33
+ // argument says which, and is ignored here because both should close.
34
+
35
+ import { useState } from "react";
36
+ import { Button, Popover } from "@czi-sds/components";
37
+
38
+ function App() {
39
+ const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
40
+ const open = Boolean(anchorEl);
41
+
42
+ return (
43
+ <div className="app">
44
+ <Button
45
+ aria-describedby={open ? "default-popover" : undefined}
46
+ onClick={(event) => setAnchorEl(event.currentTarget)}
47
+ sdsStyle="outline"
48
+ sdsType="primary"
49
+ >
50
+ Open Popover
51
+ </Button>
52
+
53
+ <Popover
54
+ anchorEl={anchorEl}
55
+ id="default-popover"
56
+ onClose={() => setAnchorEl(null)}
57
+ open={open}
58
+ >
59
+ Sequencing finished at 14:02.
60
+ </Popover>
61
+ </div>
62
+ );
63
+ }
64
+
65
+ export default App;
66
+ ```
67
+
68
+ ### **Positioning**
69
+
70
+ `anchorOrigin` picks the point on the trigger and `transformOrigin` the point on the popover that meets it. Because SDS's defaults are replaced rather than merged, each of these pairs writes the gap back in as a negative number.
71
+
72
+ **Example: PopoverPositioning**
73
+
74
+ ```tsx
75
+ // anchorOrigin names the point on the trigger, transformOrigin the point on the
76
+ // popover that meets it. A string names a corner or an edge; a number is measured
77
+ // in pixels and subtracted from the anchor point, which is how a gap is made.
78
+ //
79
+ // SDS already sets both to open the popover 8px below the left of its trigger. The
80
+ // defaults are spread before your props, so each one is replaced whole rather than
81
+ // merged: every pair below has to write its own gap back in as a negative number.
82
+
83
+ import { useState } from "react";
84
+ import {
85
+ Button,
86
+ Popover,
87
+ getSpaces,
88
+ type CommonThemeProps,
89
+ type PopoverProps,
90
+ } from "@czi-sds/components";
91
+ import styled from "@emotion/styled";
92
+
93
+ const GAP = 8;
94
+
95
+ interface Placement {
96
+ anchorOrigin: PopoverProps["anchorOrigin"];
97
+ description: string;
98
+ label: string;
99
+ transformOrigin: PopoverProps["transformOrigin"];
100
+ }
101
+
102
+ const PLACEMENTS: Placement[] = [
103
+ {
104
+ anchorOrigin: { horizontal: "left", vertical: "bottom" },
105
+ description: "Below, left aligned",
106
+ label: "Default",
107
+ transformOrigin: { horizontal: 0, vertical: -GAP },
108
+ },
109
+ {
110
+ anchorOrigin: { horizontal: "right", vertical: "bottom" },
111
+ description: "Below, right aligned",
112
+ label: "Right aligned",
113
+ transformOrigin: { horizontal: "right", vertical: -GAP },
114
+ },
115
+ {
116
+ anchorOrigin: { horizontal: "right", vertical: "top" },
117
+ description: "Beside the trigger",
118
+ label: "To the side",
119
+ transformOrigin: { horizontal: -GAP, vertical: "top" },
120
+ },
121
+ ];
122
+
123
+ const Stage = styled.div<CommonThemeProps>`
124
+ ${(props) => {
125
+ const spaces = getSpaces(props);
126
+
127
+ return `
128
+ display: flex;
129
+ flex-wrap: wrap;
130
+ gap: ${spaces?.l}px;
131
+ `;
132
+ }}
133
+ `;
134
+
135
+ function App() {
136
+ const [openIndex, setOpenIndex] = useState<number | null>(null);
137
+ const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
138
+
139
+ const close = () => {
140
+ setOpenIndex(null);
141
+ setAnchorEl(null);
142
+ };
143
+
144
+ return (
145
+ <div className="app">
146
+ <Stage>
147
+ {PLACEMENTS.map((placement, index) => (
148
+ <div key={placement.label}>
149
+ <Button
150
+ onClick={(event) => {
151
+ setAnchorEl(event.currentTarget);
152
+ setOpenIndex(index);
153
+ }}
154
+ sdsStyle="outline"
155
+ sdsType="primary"
156
+ >
157
+ {placement.label}
158
+ </Button>
159
+
160
+ <Popover
161
+ anchorEl={anchorEl}
162
+ anchorOrigin={placement.anchorOrigin}
163
+ onClose={close}
164
+ open={openIndex === index}
165
+ transformOrigin={placement.transformOrigin}
166
+ >
167
+ {placement.description}
168
+ </Popover>
169
+ </div>
170
+ ))}
171
+ </Stage>
172
+ </div>
173
+ );
174
+ }
175
+
176
+ export default App;
177
+ ```
178
+
179
+ ### **Popover with rich content**
180
+
181
+ Anything React can render goes on the paper. A popover holding more than a sentence wants a width, which is set through `slotProps.paper`, and a little more room than the paper's 6px and 12px, which the content adds itself.
182
+
183
+ **Example: PopoverWithRichContent**
184
+
185
+ ```tsx
186
+ // A popover is only a surface, so anything React can render can go on it: a
187
+ // heading, body copy, and the buttons that act on what it describes.
188
+ //
189
+ // Two things have to be decided for content this size. The width, because a
190
+ // popover is otherwise as wide as its content: slotProps.paper is the way in, and
191
+ // sx works there because SDS sets no width of its own. And the room around it,
192
+ // which the content adds: the paper's 6px and 12px come from a selector on the
193
+ // popover's root that outranks an sx on the paper, so they are there to build on
194
+ // rather than to replace.
195
+ //
196
+ // Focus is trapped while the popover is open, so the two buttons are reachable by
197
+ // keyboard and Escape closes the whole thing.
198
+
199
+ import { useState } from "react";
200
+ import {
201
+ Button,
202
+ Popover,
203
+ fontBodyS,
204
+ fontHeaderS,
205
+ getSemanticColors,
206
+ getSpaces,
207
+ type CommonThemeProps,
208
+ } from "@czi-sds/components";
209
+ import styled from "@emotion/styled";
210
+
211
+ const Content = styled.div<CommonThemeProps>`
212
+ ${(props) => {
213
+ const spaces = getSpaces(props);
214
+
215
+ return `
216
+ display: flex;
217
+ flex-direction: column;
218
+ gap: ${spaces?.s}px;
219
+ padding: ${spaces?.xs}px 0;
220
+ `;
221
+ }}
222
+ `;
223
+
224
+ const Title = styled.p`
225
+ ${fontHeaderS}
226
+ margin: 0;
227
+ `;
228
+
229
+ const Body = styled.p<CommonThemeProps>`
230
+ ${fontBodyS}
231
+
232
+ ${(props) => `
233
+ color: ${getSemanticColors(props)?.base?.textSecondary};
234
+ margin: 0;
235
+ `}
236
+ `;
237
+
238
+ const Actions = styled.div<CommonThemeProps>`
239
+ ${(props) => `
240
+ display: flex;
241
+ gap: ${getSpaces(props)?.s}px;
242
+ `}
243
+ `;
244
+
245
+ function App() {
246
+ const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
247
+ const open = Boolean(anchorEl);
248
+ const close = () => setAnchorEl(null);
249
+
250
+ return (
251
+ <div className="app">
252
+ <Button
253
+ aria-describedby={open ? "rich-popover" : undefined}
254
+ onClick={(event) => setAnchorEl(event.currentTarget)}
255
+ sdsStyle="outline"
256
+ sdsType="primary"
257
+ >
258
+ Discard run
259
+ </Button>
260
+
261
+ <Popover
262
+ anchorEl={anchorEl}
263
+ id="rich-popover"
264
+ onClose={close}
265
+ open={open}
266
+ slotProps={{ paper: { sx: { maxWidth: 320 } } }}
267
+ >
268
+ <Content>
269
+ <Title>Discard this run?</Title>
270
+ <Body>
271
+ The 240 samples queued behind it will be released, and the results
272
+ already written will be kept.
273
+ </Body>
274
+ <Actions>
275
+ <Button onClick={close} sdsStyle="solid" sdsType="destructive">
276
+ Discard
277
+ </Button>
278
+ <Button onClick={close} sdsStyle="minimal" sdsType="secondary">
279
+ Cancel
280
+ </Button>
281
+ </Actions>
282
+ </Content>
283
+ </Popover>
284
+ </div>
285
+ );
286
+ }
287
+
288
+ export default App;
289
+ ```
290
+
291
+ ### **Opening at the cursor**
292
+
293
+ With `anchorReference` set to `"anchorPosition"` the popover is placed at a point rather than against an element, which is what a right-click menu needs. The vertical offset in the SDS `transformOrigin` applies here too, so the popover sits 8px below the cursor.
294
+
295
+ **Example: PopoverAtCursorPosition**
296
+
297
+ ```tsx
298
+ // anchorReference="anchorPosition" positions the popover against a point instead
299
+ // of an element, which is what a right-click menu needs: there is no anchor to
300
+ // measure, only where the pointer was.
301
+ //
302
+ // anchorPosition is read in viewport coordinates, so clientX and clientY go
303
+ // straight in: no scroll offset, and no bounding rect. Everything else works as
304
+ // before, including the 8px the SDS transformOrigin subtracts, which is why the
305
+ // popover opens just below the cursor rather than under it.
306
+ //
307
+ // The browser's own context menu has to be prevented, and the surface is given a
308
+ // tabIndex and a keyboard route of its own, since a right-click is not something
309
+ // every person can perform.
310
+
311
+ import { useState, type MouseEvent } from "react";
312
+ import {
313
+ Popover,
314
+ fontBodyS,
315
+ getSemanticColors,
316
+ getSpaces,
317
+ type CommonThemeProps,
318
+ } from "@czi-sds/components";
319
+ import styled from "@emotion/styled";
320
+
321
+ interface Point {
322
+ left: number;
323
+ top: number;
324
+ }
325
+
326
+ const Surface = styled.div<CommonThemeProps>`
327
+ ${fontBodyS}
328
+
329
+ ${(props) => {
330
+ const semanticColors = getSemanticColors(props);
331
+ const spaces = getSpaces(props);
332
+
333
+ return `
334
+ align-items: center;
335
+ background-color: ${semanticColors?.base?.backgroundSecondary};
336
+ border: 1px dashed ${semanticColors?.base?.divider};
337
+ border-radius: 6px;
338
+ color: ${semanticColors?.base?.textSecondary};
339
+ display: flex;
340
+ justify-content: center;
341
+ min-height: 120px;
342
+ padding: ${spaces?.l}px;
343
+ text-align: center;
344
+ `;
345
+ }}
346
+ `;
347
+
348
+ function App() {
349
+ const [point, setPoint] = useState<Point | null>(null);
350
+
351
+ const openAt = (event: MouseEvent<HTMLDivElement>) => {
352
+ event.preventDefault();
353
+ setPoint({ left: event.clientX, top: event.clientY });
354
+ };
355
+
356
+ return (
357
+ <div className="app">
358
+ <Surface
359
+ onContextMenu={openAt}
360
+ onKeyDown={(event) => {
361
+ if (event.key === "Enter" || event.key === " ") {
362
+ event.preventDefault();
363
+ const box = event.currentTarget.getBoundingClientRect();
364
+ setPoint({ left: box.left + box.width / 2, top: box.top });
365
+ }
366
+ }}
367
+ role="button"
368
+ tabIndex={0}
369
+ >
370
+ Right-click anywhere in here, or focus it and press Enter
371
+ </Surface>
372
+
373
+ <Popover
374
+ anchorPosition={point ?? undefined}
375
+ anchorReference="anchorPosition"
376
+ onClose={() => setPoint(null)}
377
+ open={point !== null}
378
+ >
379
+ Opened at the cursor
380
+ </Popover>
381
+ </div>
382
+ );
383
+ }
384
+
385
+ export default App;
386
+ ```
387
+
388
+ ## SDS vs MUI
389
+
390
+ SDS Popover is a thin wrapper around MUI's Popover. `PopoverProps` is MUI's own type re-exported unchanged, so there are no sds-prefixed props and everything in the MUI documentation applies. What SDS contributes is two positioning defaults and the styling of the paper the content sits on:
391
+
392
+ - `anchorOrigin`: set to `{ vertical: "bottom", horizontal: "left" }`, where MUI's default is `"top"`/`"left"`. An SDS popover therefore opens below its anchor and aligned to its left edge, rather than on top of it.
393
+
394
+ - `transformOrigin`: set to `{ vertical: -8, horizontal: 0 }`, taken from the `s` space token. A number is subtracted from the anchor point rather than naming a corner, so a negative eight reads as an 8px gap between the anchor and the popover.
395
+
396
+ - **Both defaults are spread before your props,** which means each one is replaced whole rather than merged. Passing a `transformOrigin` of your own drops the 8px gap unless you write it back in, and the same is true of `anchorOrigin`.
397
+
398
+ - **The paper is restyled:** the surface colour comes from the theme's `surfacePrimary`, corners are the `l` radius (6px), the shadow is `m`, and there is a hairline in `borderSecondary` at 15% opacity. SDS also sets `background-image: none`, which removes the tint MUI's Paper paints over surfaces in dark mode.
399
+
400
+ - **The hairline is an outline, not a border.** It is drawn outside the paper's box, so it never adds to the popover's size or shifts the content inside it.
401
+
402
+ - **elevation has no visible effect.** SDS sets the shadow on the paper with enough specificity to win over MUI's elevation styles, so every popover carries the same one.
403
+
404
+ - **The paper already has padding** of 6px vertically and 12px horizontally, and it is best treated as fixed: SDS sets it through a descendant selector on the popover's root, which outranks an `sx` on the paper slot. Content that needs more room should add its own padding rather than try to replace what is there.
405
+
406
+ ## MUI Documentation
407
+
408
+ Documentation for the underlying MUI component can be found [here](https://mui.com/material-ui/react-popover/), and its full API [here](https://mui.com/material-ui/api/popover/).
409
+
410
+ ## Behavior and accessibility
411
+
412
+ - A Popover is built on MUI's Modal. It renders in a portal at the end of the document, traps focus while it is open, hides the rest of the page from assistive technology, and locks the page's scroll. It is a modal surface that happens to be anchored, not a floating layer over a page that carries on as normal.
413
+
414
+ - The backdrop is invisible but present, so a click anywhere outside the popover is caught by it rather than by whatever is underneath. `onClose` is called with the event and a reason: `"backdropClick"` or `"escapeKeyDown"`. Closing is left to you, since the open state is yours.
415
+
416
+ - **anchorEl has to be state, not a ref.** The popover measures the anchor while rendering, so it needs a render to happen once the element is known. Storing the trigger in a ref leaves the popover with nothing to measure on the first open.
417
+
418
+ - Nothing at all is rendered while `open` is `false`, so the content is mounted fresh on each open and its state starts over. Pass `keepMounted` to keep it in the document instead, worth it when the content is expensive to build or has to stay findable by an in-page search.
419
+
420
+ - The position is worked out when the popover opens and again on window resize; it does not follow its anchor. Page scroll is locked while it is open, so this is usually enough, but an anchor inside a box that scrolls on its own can be scrolled away from its popover.
421
+
422
+ - A popover that would fall off the screen is pulled back inside, leaving `marginThreshold` (16px) between it and the edge. Unlike Tooltip, it does not flip to the other side of its anchor.
423
+
424
+ - Give the popover an `id` and point the trigger at it with `aria-describedby` while it is open, so the popover is announced as the description of the control that opened it.
425
+
426
+ - Popover is the plain anchored surface the richer overlays are built from, and it is often not the component to reach for directly. Use Tooltip for a short hint on hover, DropdownMenu or Menu for a list of choices, Dialog for a task that should interrupt the page, and Popover for anchored content that none of those describe.
427
+
428
+ ## Props
429
+
430
+ Popover takes MUI's props and adds none of its own. The ones needed to get a popover working, and those whose SDS defaults differ from MUI's, are listed below. See the MUI documentation for the rest.
431
+
432
+ | Name | Type | Default | Description |
433
+ | -------------------- | -------------------------------------------------------------------------------------------------------------- | -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
434
+ | `open` | `boolean` | - | Required. Whether the popover is shown. Usually derived from whether an anchor has been recorded. |
435
+ | `anchorEl` | `HTMLElement \| (() => HTMLElement) \| null` | - | The element the popover is positioned against. Keep it in state so that setting it causes the render the popover needs in order to measure it. |
436
+ | `onClose` | `(event, reason) => void` | - | Called on backdrop click and on Escape, with `"backdropClick"` or `"escapeKeyDown"` as the reason. Closing the popover is up to you. |
437
+ | `children` | `ReactNode` | - | The content, placed straight onto the paper, which has already padded itself by 6px and 12px. |
438
+ | `anchorOrigin` | `{ vertical: "top" \| "center" \| "bottom" \| number,` `horizontal: "left" \| "center" \| "right" \| number }` | `{ vertical: "bottom", horizontal: "left" }` | The point on the anchor the popover attaches to. SDS changes MUI's vertical default from `"top"` to `"bottom"`. |
439
+ | `transformOrigin` | `{ vertical: "top" \| "center" \| "bottom" \| number,` `horizontal: "left" \| "center" \| "right" \| number }` | `{ vertical: -8, horizontal: 0 }` | The point on the popover that meets the anchor's. The negative eight is what produces the gap; passing your own value replaces it. |
440
+ | `anchorReference` | `"anchorEl" \| "anchorPosition" \| "none"` | `"anchorEl"` | Whether to position against an element, a point on the page, or nothing at all. `"anchorPosition"` is how a popover opens at the cursor. |
441
+ | `anchorPosition` | `{ top: number, left: number }` | - | The point to open at, in viewport coordinates. Read only when `anchorReference` is `"anchorPosition"`. |
442
+ | `slotProps` | `{ paper, backdrop, root, transition }` | - | Props for the parts the popover is made of. `slotProps.paper` is where a width or a max height belongs; the padding there is SDS's and outranks an `sx` on the slot. |
443
+ | `marginThreshold` | `number` | `16` | How much room is kept between the popover and the edge of the window when it has to be pulled back on screen. |
444
+ | `keepMounted` | `boolean` | `false` | Keeps the content in the document while closed, preserving its state at the cost of rendering it up front. |
445
+ | `disableScrollLock` | `boolean` | `false` | Lets the page scroll while the popover is open. The popover stays where it was placed, so the anchor scrolls out from under it. |
446
+ | `disablePortal` | `boolean` | `false` | Renders the popover where it is written rather than at the end of the document, which puts it back within reach of an ancestor's overflow. |
447
+ | `elevation` | `number` | `8` | MUI's prop. It has no visible effect, since SDS sets the paper's shadow itself. |
448
+ | `transitionDuration` | `number \| { appear, enter, exit } \| "auto"` | `"auto"` | How long the grow transition runs. `"auto"` scales it to the size of the popover. |