@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,414 @@
1
+ # NavigationJumpTo
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/NavigationJumpTo/index.tsx).
6
+
7
+ ## Import
8
+
9
+ **React TypeScript**
10
+
11
+ ```tsx
12
+ import { NavigationJumpTo } from "@czi-sds/components";
13
+ ```
14
+
15
+ ## Code examples
16
+
17
+ ### Default NavigationJumpTo
18
+
19
+ Three items, with the two sub-sections of Methods indented under it. The highlight starts on the first item and moves with a click. No refs are attached to sections here, so nothing scrolls: this is the list's default state and nothing more.
20
+
21
+ **Example: DefaultNavigationJumpTo**
22
+
23
+ ```tsx
24
+ // The list on its own: items in the order they are given, sub-items indented
25
+ // one level under the item they belong to, and the highlight starting on the
26
+ // first item.
27
+ //
28
+ // The refs are held but never handed to an element, which keeps this to the
29
+ // shape of the component. Nothing scrolls, and the highlight moves only when an
30
+ // item is clicked. The examples below point the refs at sections, which is what
31
+ // puts the highlight under the reader's own scrolling.
32
+
33
+ import { NavigationJumpTo } from "@czi-sds/components";
34
+ import { useRef } from "react";
35
+
36
+ function App() {
37
+ const overviewRef = useRef<HTMLElement | null>(null);
38
+ const methodsRef = useRef<HTMLElement | null>(null);
39
+ const samplePrepRef = useRef<HTMLElement | null>(null);
40
+ const sequencingRef = useRef<HTMLElement | null>(null);
41
+ const resultsRef = useRef<HTMLElement | null>(null);
42
+
43
+ return (
44
+ <div className="app">
45
+ <NavigationJumpTo
46
+ items={[
47
+ { elementRef: overviewRef, title: "Overview" },
48
+ {
49
+ elementRef: methodsRef,
50
+ subItems: [
51
+ { elementRef: samplePrepRef, title: "Sample prep" },
52
+ { elementRef: sequencingRef, title: "Sequencing" },
53
+ ],
54
+ title: "Methods",
55
+ },
56
+ { elementRef: resultsRef, title: "Results" },
57
+ ]}
58
+ width="200px"
59
+ />
60
+ </div>
61
+ );
62
+ }
63
+
64
+ export default App;
65
+ ```
66
+
67
+ ### Sections in a scrolling panel
68
+
69
+ Three sections with refs wired to the items. Clicking scrolls the panel, and scrolling the panel moves the highlight. Bringing a section into view scrolls every scroll container around it, so this example holds the docs page still while the panel moves; a page that scrolls its own sections wants the opposite.
70
+
71
+ **Example: JumpToSections**
72
+
73
+ ```tsx
74
+ // Each item points at a section through a ref, and the component keeps the
75
+ // highlight on whichever section is showing. Clicking an item scrolls its
76
+ // section into view, which here means scrolling the panel on the right.
77
+ //
78
+ // Give the sections ids: the tabs point their aria-controls at them, and
79
+ // without ids they reference elements that do not exist.
80
+ //
81
+ // "Into view" means every scroll container around the section, so this page
82
+ // scrolls along with the panel. holdPageStill pins it for the length of the
83
+ // animation, which a page that scrolls its own sections would not want.
84
+
85
+ import {
86
+ NavigationJumpTo,
87
+ fontBodyS,
88
+ fontHeaderM,
89
+ getSemanticColors,
90
+ type CommonThemeProps,
91
+ } from "@czi-sds/components";
92
+ import styled from "@emotion/styled";
93
+ import { useRef } from "react";
94
+
95
+ const Layout = styled.div`
96
+ display: flex;
97
+ gap: 16px;
98
+ `;
99
+
100
+ const ScrollArea = styled.div<CommonThemeProps>`
101
+ ${(props) => {
102
+ const semanticColors = getSemanticColors(props);
103
+
104
+ return `
105
+ border: 1px solid ${semanticColors?.base?.divider};
106
+ border-radius: 4px;
107
+ flex: 1;
108
+ height: 280px;
109
+ overflow-y: auto;
110
+ padding: 16px;
111
+ `;
112
+ }}
113
+ `;
114
+
115
+ const Section = styled.section<CommonThemeProps>`
116
+ ${fontBodyS}
117
+
118
+ ${(props) => {
119
+ const semanticColors = getSemanticColors(props);
120
+
121
+ return `
122
+ color: ${semanticColors?.base?.textPrimary};
123
+ min-height: 320px;
124
+
125
+ h3 {
126
+ ${fontHeaderM(props)}
127
+ margin: 0 0 8px 0;
128
+ }
129
+ `;
130
+ }}
131
+ `;
132
+
133
+ function holdPageStill() {
134
+ const { scrollX, scrollY } = window;
135
+ const until = performance.now() + 500;
136
+
137
+ const pin = () => {
138
+ window.scrollTo({ behavior: "instant", left: scrollX, top: scrollY });
139
+ if (performance.now() < until) requestAnimationFrame(pin);
140
+ };
141
+
142
+ requestAnimationFrame(pin);
143
+ }
144
+
145
+ function App() {
146
+ const overviewRef = useRef<HTMLElement | null>(null);
147
+ const methodsRef = useRef<HTMLElement | null>(null);
148
+ const resultsRef = useRef<HTMLElement | null>(null);
149
+
150
+ return (
151
+ <div className="app">
152
+ <Layout>
153
+ <NavigationJumpTo
154
+ items={[
155
+ { elementRef: overviewRef, title: "Overview" },
156
+ { elementRef: methodsRef, title: "Methods" },
157
+ { elementRef: resultsRef, title: "Results" },
158
+ ]}
159
+ onChange={(_value, _event, type) => {
160
+ if (type === "click") holdPageStill();
161
+ }}
162
+ width="160px"
163
+ />
164
+
165
+ <ScrollArea>
166
+ <Section id="overview" ref={overviewRef}>
167
+ <h3>Overview</h3>
168
+ <p>What the study set out to measure and why.</p>
169
+ </Section>
170
+ <Section id="methods" ref={methodsRef}>
171
+ <h3>Methods</h3>
172
+ <p>How the samples were collected and processed.</p>
173
+ </Section>
174
+ <Section id="results" ref={resultsRef}>
175
+ <h3>Results</h3>
176
+ <p>What came out of the analysis.</p>
177
+ </Section>
178
+ </ScrollArea>
179
+ </Layout>
180
+ </div>
181
+ );
182
+ }
183
+
184
+ export default App;
185
+ ```
186
+
187
+ ### Sub-items and onChange
188
+
189
+ Sub-items render indented under their parent, and the readout shows the index that `onChange` reports along with what triggered it. Click an item, then scroll the panel by hand to see the other kind of change arrive. The sub-sections are siblings of Methods rather than children, because a parent section wrapping them would keep the highlight for itself.
190
+
191
+ **Example: JumpToSubItems**
192
+
193
+ ```tsx
194
+ // subItems nest one level and render indented under their parent. onChange
195
+ // reports the index of the highlighted item, counting sub-items in the order
196
+ // they render, along with whether a click or a scroll caused the change: click
197
+ // an item, then scroll the panel by hand to see the other kind arrive.
198
+ //
199
+ // The sub-sections are siblings of Methods rather than children of it. Nested
200
+ // inside, Methods would be in view whenever they were, and since the highlight
201
+ // goes to the first item in view, the sub-items would never take it on scroll.
202
+ //
203
+ // Bringing a section into view scrolls every container around it, this page
204
+ // included, so holdPageStill pins the page while the panel moves. A page that
205
+ // scrolls its own sections wants none of that.
206
+
207
+ import {
208
+ NavigationJumpTo,
209
+ fontBodyS,
210
+ fontBodyXs,
211
+ fontHeaderM,
212
+ fontHeaderS,
213
+ getSemanticColors,
214
+ getSpaces,
215
+ type CommonThemeProps,
216
+ } from "@czi-sds/components";
217
+ import styled from "@emotion/styled";
218
+ import { useRef, useState } from "react";
219
+
220
+ const Layout = styled.div`
221
+ display: flex;
222
+ gap: 16px;
223
+ `;
224
+
225
+ const ScrollArea = styled.div<CommonThemeProps>`
226
+ ${(props) => {
227
+ const semanticColors = getSemanticColors(props);
228
+
229
+ return `
230
+ border: 1px solid ${semanticColors?.base?.divider};
231
+ border-radius: 4px;
232
+ flex: 1;
233
+ height: 280px;
234
+ overflow-y: auto;
235
+ padding: 16px;
236
+ `;
237
+ }}
238
+ `;
239
+
240
+ const Section = styled.section<CommonThemeProps>`
241
+ ${fontBodyS}
242
+
243
+ ${(props) => {
244
+ const semanticColors = getSemanticColors(props);
245
+
246
+ return `
247
+ color: ${semanticColors?.base?.textPrimary};
248
+ min-height: 320px;
249
+
250
+ h3 {
251
+ ${fontHeaderM(props)}
252
+ margin: 0 0 8px 0;
253
+ }
254
+
255
+ h4 {
256
+ ${fontHeaderS(props)}
257
+ margin: 0 0 8px 0;
258
+ }
259
+ `;
260
+ }}
261
+ `;
262
+
263
+ const Sidebar = styled.p<CommonThemeProps>`
264
+ ${(props) => {
265
+ const spaces = getSpaces(props);
266
+
267
+ return `
268
+ display: flex;
269
+ flex-direction: column;
270
+ gap: ${spaces?.xl}px;
271
+ `;
272
+ }}
273
+ `;
274
+
275
+ const Readout = styled.p<CommonThemeProps>`
276
+ ${fontBodyXs}
277
+
278
+ ${(props) => {
279
+ const semanticColors = getSemanticColors(props);
280
+
281
+ return `
282
+ color: ${semanticColors?.base?.textSecondary};
283
+ `;
284
+ }}
285
+ `;
286
+
287
+ function holdPageStill() {
288
+ const { scrollX, scrollY } = window;
289
+ const until = performance.now() + 500;
290
+
291
+ const pin = () => {
292
+ window.scrollTo({ behavior: "instant", left: scrollX, top: scrollY });
293
+ if (performance.now() < until) requestAnimationFrame(pin);
294
+ };
295
+
296
+ requestAnimationFrame(pin);
297
+ }
298
+
299
+ function App() {
300
+ const [lastChange, setLastChange] = useState("none yet");
301
+ const methodsRef = useRef<HTMLElement | null>(null);
302
+ const samplePrepRef = useRef<HTMLElement | null>(null);
303
+ const sequencingRef = useRef<HTMLElement | null>(null);
304
+ const resultsRef = useRef<HTMLElement | null>(null);
305
+ const discussionRef = useRef<HTMLElement | null>(null);
306
+
307
+ return (
308
+ <div className="app">
309
+ <Layout>
310
+ <Sidebar>
311
+ <NavigationJumpTo
312
+ items={[
313
+ {
314
+ elementRef: methodsRef,
315
+ subItems: [
316
+ { elementRef: samplePrepRef, title: "Sample prep" },
317
+ { elementRef: sequencingRef, title: "Sequencing" },
318
+ ],
319
+ title: "Methods",
320
+ },
321
+ { elementRef: resultsRef, title: "Results" },
322
+ { elementRef: discussionRef, title: "Discussion" },
323
+ ]}
324
+ onChange={(value, _event, type) => {
325
+ setLastChange(`index ${value}, from ${type}`);
326
+ if (type === "click") holdPageStill();
327
+ }}
328
+ width="160px"
329
+ />
330
+
331
+ <Readout>
332
+ Last change: <br />
333
+ {lastChange}
334
+ </Readout>
335
+ </Sidebar>
336
+
337
+ <ScrollArea>
338
+ <Section id="methods" ref={methodsRef}>
339
+ <h3>Methods</h3>
340
+ <p>How the samples were collected and processed.</p>
341
+ </Section>
342
+ <Section id="sample-prep" ref={samplePrepRef}>
343
+ <h4>Sample prep</h4>
344
+ <p>Dissociation, staining, and quality gates.</p>
345
+ </Section>
346
+ <Section id="sequencing" ref={sequencingRef}>
347
+ <h4>Sequencing</h4>
348
+ <p>Library construction and read depth per sample.</p>
349
+ </Section>
350
+ <Section id="results" ref={resultsRef}>
351
+ <h3>Results</h3>
352
+ <p>What came out of the analysis.</p>
353
+ </Section>
354
+ <Section id="discussion" ref={discussionRef}>
355
+ <h3>Discussion</h3>
356
+ <p>What the numbers do and do not support.</p>
357
+ </Section>
358
+ </ScrollArea>
359
+ </Layout>
360
+ </div>
361
+ );
362
+ }
363
+
364
+ export default App;
365
+ ```
366
+
367
+ ## SDS vs MUI
368
+
369
+ NavigationJumpTo is built on MUI's [Tabs](https://mui.com/material-ui/react-tabs/), but it is not a tab strip: the tabs are page sections, and which one is highlighted follows the scroll position rather than a value you hold. It differs from Tabs in these ways:
370
+
371
+ - `value`: the component owns it. An IntersectionObserver watches the sections and highlights the first one in view, so there is no `value` prop to set.
372
+
373
+ - `onChange`: MUI's signature is replaced. It reports the index that became active, the event, and whether a click or a scroll caused it, and it fires once per change rather than on every scroll frame.
374
+
375
+ - `orientation`: `"vertical"` is set for you, and the design only covers vertical. Passing `"horizontal"` is possible, since your props are applied last, but nothing is styled for it.
376
+
377
+ - **Colors are fixed.** The indicator is a 2px rule in the accent active color and the label colors come from the theme. There is no `indicatorColor` or `textColor` to set.
378
+
379
+ - **No icons.** Items are a title and a ref; there is nowhere to put one.
380
+
381
+ - **Layout is baked in.** The component is sticky 24px from the top of its scroll container, draws a 1px rule down its left edge, and reserves 16px below and 12px to its right. Width is the only dimension exposed.
382
+
383
+ ## Behavior notes
384
+
385
+ - Each item carries a ref to its section, and the highlight follows the first section in view, in the order the items are listed. An earlier section still showing by a few pixels therefore keeps the highlight, which is the problem `offsetTop` solves. While a click's smooth scroll is running the scroll-driven update is suspended, so the indicator does not race through the sections on the way.
386
+
387
+ - Clicking an item scrolls its section into view. With `offsetTop` left at `0` that is a plain `scrollIntoView`, which works inside a scrolling container as well as on the page. Note that it scrolls every container around the section, so a section in a panel moves the panel and the page holding it.
388
+
389
+ - Setting `offsetTop` changes the mechanism: the component scrolls the window and finds the section by id. Sections therefore need `id` attributes, and a section without one simply will not scroll. It also assumes the page itself scrolls, so pair it with page-level scrolling rather than a scrolling panel.
390
+
391
+ - Sub-items nest exactly one level through `subItems` and render indented under their parent. They are ordinary items as far as indexing goes: a parent with two sub-items occupies indices 0, 1, and 2.
392
+
393
+ - Each tab points `aria-controls` at its section's id, falling back to a generated `navigation-panel-N` when the section has no id. Give your sections ids so the reference resolves, or the tabs point at elements that do not exist.
394
+
395
+ - The strip labels itself `navigation-jump-to`. Pass an `aria-label` of your own when a page has more than one.
396
+
397
+ ## Props
398
+
399
+ Everything below is SDS's own. Other [Tabs props](https://mui.com/material-ui/api/tabs/) pass through to the underlying element, and because they are applied after the component's own, they can override them.
400
+
401
+ | Name | Type | Default | Description |
402
+ | ----------- | ------------------------ | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
403
+ | `items` | `Item[]` | - (required) | The sections to list. See the shape below. |
404
+ | `offsetTop` | `number` | `0` | How many pixels above a section the highlight should switch to it, which is what you want when a sticky header covers the top of the page. Any non-zero value also switches clicks to window scrolling by element id. |
405
+ | `onChange` | `function` | - | `(value: number, event?: React.SyntheticEvent, type?: "click" \| "scroll") => void`. Called when the highlighted item changes, with its index and what caused the change. For a scroll the event is synthesized, not a real DOM event. |
406
+ | `width` | `CSSProperties["width"]` | `"100%"` | The width of the strip and of every item in it. Long titles wrap rather than truncate. |
407
+
408
+ ## Item
409
+
410
+ | Name | Type | Default | Description |
411
+ | ------------ | --------------------------------------- | ------- | ---------------------------------------------------------------------------------------------------- |
412
+ | `title` | `string` | - | The item's label. It is also kebab-cased into the tab's id, so keep titles distinct. |
413
+ | `elementRef` | `MutableRefObject<HTMLElement \| null>` | - | A ref on the section this item points at. The component observes and scrolls whatever the ref holds. |
414
+ | `subItems` | `{ title, elementRef }[]` | - | Indented children, one level only. Sub-items cannot nest sub-items of their own. |