@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.
- package/README.md +2 -1
- package/data/component-docs/Accordion.md +291 -0
- package/data/component-docs/Autocomplete.md +525 -0
- package/data/component-docs/Banner.md +231 -0
- package/data/component-docs/Button.md +224 -0
- package/data/component-docs/ButtonDropdown.md +256 -0
- package/data/component-docs/ButtonGroup.md +320 -0
- package/data/component-docs/ButtonToggle.md +397 -0
- package/data/component-docs/Callout.md +268 -0
- package/data/component-docs/CellBasic.md +122 -0
- package/data/component-docs/CellComponent.md +63 -0
- package/data/component-docs/CellHeader.md +128 -0
- package/data/component-docs/ComplexFilter.md +294 -0
- package/data/component-docs/ContentCard.md +406 -0
- package/data/component-docs/Dialog.md +651 -0
- package/data/component-docs/Dropdown.md +155 -0
- package/data/component-docs/DropdownMenu.md +949 -0
- package/data/component-docs/HeatmapChart.md +782 -0
- package/data/component-docs/Hero.md +363 -0
- package/data/component-docs/Icon.md +283 -0
- package/data/component-docs/InputCheckbox.md +205 -0
- package/data/component-docs/InputDropdown.md +482 -0
- package/data/component-docs/InputRadio.md +173 -0
- package/data/component-docs/InputSearch.md +81 -0
- package/data/component-docs/InputSlider.md +189 -0
- package/data/component-docs/InputText.md +110 -0
- package/data/component-docs/InputToggle.md +141 -0
- package/data/component-docs/IntentMessage.md +293 -0
- package/data/component-docs/Legend.md +442 -0
- package/data/component-docs/Link.md +23 -0
- package/data/component-docs/List.md +300 -0
- package/data/component-docs/LoadingIndicator.md +198 -0
- package/data/component-docs/Menu.md +298 -0
- package/data/component-docs/MenuItem.md +100 -0
- package/data/component-docs/NavigationFooter.md +165 -0
- package/data/component-docs/NavigationHeader.md +500 -0
- package/data/component-docs/NavigationJumpTo.md +414 -0
- package/data/component-docs/Notification.md +288 -0
- package/data/component-docs/Pagination.md +67 -0
- package/data/component-docs/Panel.md +636 -0
- package/data/component-docs/Popover.md +448 -0
- package/data/component-docs/PreComposedTable.md +2947 -0
- package/data/component-docs/ProteinStructureViewer.md +1044 -0
- package/data/component-docs/SegmentedControl.md +396 -0
- package/data/component-docs/StackedBarChart.md +475 -0
- package/data/component-docs/Table.md +2000 -0
- package/data/component-docs/TableHeader.md +53 -0
- package/data/component-docs/TableRow.md +74 -0
- package/data/component-docs/Tabs.md +455 -0
- package/data/component-docs/Tag.md +647 -0
- package/data/component-docs/TagFilter.md +131 -0
- package/data/component-docs/Tooltip.md +561 -0
- package/data/component-docs/TooltipCondensed.md +203 -0
- package/data/component-docs/TooltipTable.md +145 -0
- package/data/component-docs/index.json +267 -0
- package/data/component-list.json +2 -5
- package/data/component-props/Accordion.json +31 -11
- package/data/component-props/Alert.json +3 -3
- package/data/component-props/Autocomplete.json +150 -15
- package/data/component-props/Banner.json +31 -17
- package/data/component-props/Button.json +45 -20
- package/data/component-props/ButtonDropdown.json +36 -17
- package/data/component-props/ButtonDropdownLegacy.json +11 -11
- package/data/component-props/ButtonGroup.json +32 -11
- package/data/component-props/ButtonIcon.json +10 -10
- package/data/component-props/ButtonLegacy.json +19 -19
- package/data/component-props/ButtonToggle.json +32 -19
- package/data/component-props/ButtonToggleLegacy.json +11 -11
- package/data/component-props/Callout.json +58 -17
- package/data/component-props/CellBasic.json +94 -41
- package/data/component-props/CellComponent.json +23 -7
- package/data/component-props/CellHeader.json +62 -19
- package/data/component-props/ComplexFilter.json +52 -21
- package/data/component-props/ContentCard.json +70 -47
- package/data/component-props/Dialog.json +47 -14
- package/data/component-props/Dropdown.json +68 -31
- package/data/component-props/DropdownMenu.json +128 -23
- package/data/component-props/HeatmapChart.json +88 -12
- package/data/component-props/Hero.json +80 -39
- package/data/component-props/Icon.json +20 -9
- package/data/component-props/InputCheckbox.json +36 -14
- package/data/component-props/InputDropdown.json +75 -27
- package/data/component-props/InputRadio.json +31 -13
- package/data/component-props/InputSearch.json +41 -18
- package/data/component-props/InputSlider.json +46 -19
- package/data/component-props/InputText.json +26 -19
- package/data/component-props/InputToggle.json +26 -9
- package/data/component-props/IntentMessage.json +38 -9
- package/data/component-props/Link.json +13 -7
- package/data/component-props/LoadingIndicator.json +11 -3
- package/data/component-props/MenuItem.json +38 -19
- package/data/component-props/MenuSelect.json +8 -8
- package/data/component-props/NavigationFooter.json +49 -19
- package/data/component-props/NavigationHeader.json +150 -37
- package/data/component-props/NavigationJumpTo.json +33 -7
- package/data/component-props/Notification.json +39 -21
- package/data/component-props/Pagination.json +39 -11
- package/data/component-props/Panel.json +62 -9
- package/data/component-props/Popover.json +80 -7
- package/data/component-props/PreComposedTable.json +99 -27
- package/data/component-props/ProteinStructureViewer.json +117 -0
- package/data/component-props/SegmentedControl.json +42 -3
- package/data/component-props/StackedBarChart.json +116 -42
- package/data/component-props/TableRow.json +38 -19
- package/data/component-props/Tabs.json +66 -9
- package/data/component-props/Tag.json +34 -17
- package/data/component-props/TagFilter.json +10 -3
- package/data/component-props/Tooltip.json +49 -18
- package/data/component-props/TooltipCondensed.json +18 -9
- package/data/component-props/TooltipTable.json +17 -7
- package/data/resources/{zeroheight-rules.md → documentation-rules.md} +43 -30
- package/data/resources/figma-rules.md +2 -2
- package/data/tailwind.json +20 -14
- package/dist/stdio.js +112 -208
- package/package.json +9 -5
- package/data/component-props/ButtonDropdownV2.json +0 -34
- package/data/component-props/ButtonNew.json +0 -47
- package/data/component-props/ButtonToggleV2.json +0 -44
- package/data/component-props/ButtonV2.json +0 -43
- package/data/zeroheight-cache.json +0 -10259
- package/data/zeroheight-exports/Accordion.md +0 -774
- package/data/zeroheight-exports/Banner.md +0 -691
- package/data/zeroheight-exports/Borders.md +0 -117
- package/data/zeroheight-exports/Buttons.md +0 -1854
- package/data/zeroheight-exports/Callout.md +0 -931
- package/data/zeroheight-exports/Colors.md +0 -1755
- package/data/zeroheight-exports/Content-Card.md +0 -309
- package/data/zeroheight-exports/Contributing-to-SDS.md +0 -98
- package/data/zeroheight-exports/Control-Inputs.md +0 -2818
- package/data/zeroheight-exports/Corners.md +0 -104
- package/data/zeroheight-exports/Cover-page.md +0 -3
- package/data/zeroheight-exports/Dialog.md +0 -1367
- package/data/zeroheight-exports/Downloads.md +0 -236
- package/data/zeroheight-exports/Drop-Shadows.md +0 -79
- package/data/zeroheight-exports/Dropdown-Input.md +0 -1776
- package/data/zeroheight-exports/Dropdown-Menus.md +0 -913
- package/data/zeroheight-exports/Element-Status-Tracker.md +0 -267
- package/data/zeroheight-exports/Field-Inputs.md +0 -1405
- package/data/zeroheight-exports/Filters.md +0 -1111
- package/data/zeroheight-exports/Getting-Started.md +0 -1157
- package/data/zeroheight-exports/Hero.md +0 -414
- package/data/zeroheight-exports/Icons.md +0 -2067
- package/data/zeroheight-exports/Intent.md +0 -86
- package/data/zeroheight-exports/Introduction-to-Bases.md +0 -35
- package/data/zeroheight-exports/Introduction-to-Cells.md +0 -35
- package/data/zeroheight-exports/Introduction-to-DNA.md +0 -35
- package/data/zeroheight-exports/Introduction-to-Genes.md +0 -35
- package/data/zeroheight-exports/Introduction.md +0 -115
- package/data/zeroheight-exports/Link.md +0 -432
- package/data/zeroheight-exports/Lists.md +0 -986
- package/data/zeroheight-exports/Loading-Indicator.md +0 -41
- package/data/zeroheight-exports/Navigation.md +0 -1190
- package/data/zeroheight-exports/Notification.md +0 -1038
- package/data/zeroheight-exports/Panel.md +0 -1030
- package/data/zeroheight-exports/README.md +0 -122
- package/data/zeroheight-exports/Responsive-Design.md +0 -330
- package/data/zeroheight-exports/Search-Input.md +0 -91
- package/data/zeroheight-exports/Search.md +0 -822
- package/data/zeroheight-exports/Segmented-Control.md +0 -609
- package/data/zeroheight-exports/Spacing.md +0 -297
- package/data/zeroheight-exports/Table.md +0 -5345
- package/data/zeroheight-exports/Tabs.md +0 -1324
- package/data/zeroheight-exports/Tags.md +0 -1545
- package/data/zeroheight-exports/Text-Link.md +0 -268
- package/data/zeroheight-exports/Theming.md +0 -362
- package/data/zeroheight-exports/Tooltips.md +0 -2015
- package/data/zeroheight-exports/Typography.md +0 -800
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# TableHeader
|
|
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/TableHeader/index.tsx).
|
|
6
|
+
|
|
7
|
+
## Import
|
|
8
|
+
|
|
9
|
+
**React TypeScript**
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
import { TableHeader } from "@czi-sds/components";
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Code examples
|
|
16
|
+
|
|
17
|
+
### **Default TableHeader**
|
|
18
|
+
|
|
19
|
+
This example demonstrates the minimum required props for rendering a TableHeader component.
|
|
20
|
+
|
|
21
|
+
**Example: DefaultTableHeader**
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
// TableHeader renders the thead and the header row itself, so its children are
|
|
25
|
+
// header cells rather than a tr.
|
|
26
|
+
|
|
27
|
+
import { CellHeader, Table, TableHeader } from "@czi-sds/components";
|
|
28
|
+
|
|
29
|
+
function App() {
|
|
30
|
+
return (
|
|
31
|
+
<div className="app">
|
|
32
|
+
<Table>
|
|
33
|
+
<TableHeader>
|
|
34
|
+
<CellHeader>Header</CellHeader>
|
|
35
|
+
<CellHeader>Header</CellHeader>
|
|
36
|
+
<CellHeader>Header</CellHeader>
|
|
37
|
+
</TableHeader>
|
|
38
|
+
</Table>
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default App;
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Props
|
|
47
|
+
|
|
48
|
+
TableHeader renders the `thead` and the header row inside it, so its children are header cells rather than a row. It has two props of its own.
|
|
49
|
+
|
|
50
|
+
| Name | Type | Default | Description |
|
|
51
|
+
| ----------- | ----------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
52
|
+
| `children` | `React.ReactNode` | - | Required. The header cells, normally CellHeaders. They are wrapped in a TableRow with hover and tooltips switched off, so do not pass a `tr` yourself. |
|
|
53
|
+
| `filterRow` | `React.ReactNode` | - | A second row rendered below the header row, inside the same `thead`, for per-column filter controls. Pass a `tr` of cells. Supplying it also changes the border on the header row above. PreComposedTable uses this for its column filtering. |
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# TableRow
|
|
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/TableRow/index.tsx).
|
|
6
|
+
|
|
7
|
+
## Import
|
|
8
|
+
|
|
9
|
+
**React TypeScript**
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
import { TableRow } from "@czi-sds/components";
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Code examples
|
|
16
|
+
|
|
17
|
+
### **Default TableRow**
|
|
18
|
+
|
|
19
|
+
This example demonstrates the minimum required props for rendering a TableRow component.
|
|
20
|
+
|
|
21
|
+
**Example: DefaultTableRow**
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
import { TableRow, CellBasic } from "@czi-sds/components";
|
|
25
|
+
|
|
26
|
+
function App() {
|
|
27
|
+
return (
|
|
28
|
+
<div className="app">
|
|
29
|
+
<table>
|
|
30
|
+
<tbody>
|
|
31
|
+
<TableRow>
|
|
32
|
+
<CellBasic
|
|
33
|
+
primaryText="Primary Text"
|
|
34
|
+
secondaryText="Secondary Text"
|
|
35
|
+
shouldShowTooltipOnHover={false}
|
|
36
|
+
/>
|
|
37
|
+
<CellBasic
|
|
38
|
+
primaryText="Primary Text"
|
|
39
|
+
secondaryText="Secondary Text"
|
|
40
|
+
shouldShowTooltipOnHover={false}
|
|
41
|
+
/>
|
|
42
|
+
<CellBasic
|
|
43
|
+
primaryText="Primary Text"
|
|
44
|
+
secondaryText="Secondary Text"
|
|
45
|
+
shouldShowTooltipOnHover={false}
|
|
46
|
+
/>
|
|
47
|
+
</TableRow>
|
|
48
|
+
</tbody>
|
|
49
|
+
</table>
|
|
50
|
+
</div>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export default App;
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Props
|
|
58
|
+
|
|
59
|
+
Any custom SDS props and MUI props required for implementation are found on the table below. See the MUI documentation for additional optional props.
|
|
60
|
+
|
|
61
|
+
| Name | Type | Default | Description |
|
|
62
|
+
| -------------------------- | ----------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
63
|
+
| `children` | `React.ReactNode` | - | Required. The row's cells. |
|
|
64
|
+
| `disabled` | `boolean` | `false` | Grays the row out and stops pointer events inside its CellComponents, so controls in the row cannot be used. Tags and icons in the row are dimmed to match. |
|
|
65
|
+
| `hover` | `boolean` | `true` | Gives the row a hover background, and is also required for its tooltip to appear. TableHeader sets it to `false` for the header row. |
|
|
66
|
+
| `rowHeight` | `number` | - | Caps the row's height, in pixels. |
|
|
67
|
+
| `selected` | `boolean` | `false` | Gives the row's cells the accent surface used for selection. It is styling only; it sets no ARIA state. |
|
|
68
|
+
| `shouldShowTooltipOnHover` | `boolean` | `true` | Wraps the row in a Tooltip, but only while `hover` is also `true`. On its own it shows nothing: a row tooltip needs `tooltipText` as well. |
|
|
69
|
+
| `tooltipText` | `string` | - | The tooltip's title. |
|
|
70
|
+
| `tooltipSubtitle` | `string` | - | A second line below the tooltip's title. |
|
|
71
|
+
| `tooltipProps` | `Partial<TooltipProps>` | - | Passed to the Tooltip, and overrides the defaults the row sets (arrow, dark style). |
|
|
72
|
+
| `useDivider` | `boolean` | `true` | Draws the 1px divider along the bottom of the row. |
|
|
73
|
+
|
|
74
|
+
Anything else you pass, such as `onClick`, `className` or a data attribute, lands on the rendered `tr`. Note that a row tooltip and a cell tooltip will both fire, so turn off `shouldShowTooltipOnHover` on the cells of a row that has its own.
|
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
# Tabs
|
|
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/Tabs/index.tsx).
|
|
6
|
+
|
|
7
|
+
## Import
|
|
8
|
+
|
|
9
|
+
**React TypeScript**
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
import { Tab, Tabs } from "@czi-sds/components";
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Code examples
|
|
16
|
+
|
|
17
|
+
### Tabs – Large
|
|
18
|
+
|
|
19
|
+
The default size, for the top level of a screen. The selection is held in the page's state and written back from `onChange`, which is what a Tabs group needs to respond to a click.
|
|
20
|
+
|
|
21
|
+
**Example: TabsLarge**
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
// Tabs is a controlled component: it draws the tab whose value matches the value
|
|
25
|
+
// prop, so the selection has to live in your state and onChange has to write it
|
|
26
|
+
// back. Without that, clicking a tab changes nothing.
|
|
27
|
+
//
|
|
28
|
+
// The large size is the default and belongs at the top level of a screen. The
|
|
29
|
+
// component brings its own spacing, 16px above and 24px below, so the group does
|
|
30
|
+
// not need margins of its own.
|
|
31
|
+
|
|
32
|
+
import { Tab, Tabs } from "@czi-sds/components";
|
|
33
|
+
import { SyntheticEvent, useState } from "react";
|
|
34
|
+
|
|
35
|
+
function App() {
|
|
36
|
+
const [value, setValue] = useState(0);
|
|
37
|
+
|
|
38
|
+
const handleChange = (_event: SyntheticEvent, newValue: unknown) => {
|
|
39
|
+
setValue(newValue as number);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<div className="app">
|
|
44
|
+
<Tabs aria-label="Sample views" onChange={handleChange} value={value}>
|
|
45
|
+
<Tab label="Overview" />
|
|
46
|
+
<Tab label="Samples" />
|
|
47
|
+
<Tab label="Sequencing runs" />
|
|
48
|
+
</Tabs>
|
|
49
|
+
</div>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export default App;
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Tabs – Small
|
|
57
|
+
|
|
58
|
+
A group at the small size, which drops the label text to 13px. It is meant for a second layer of tabbing rather than on its own.
|
|
59
|
+
|
|
60
|
+
**Example: TabsSmall**
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
// The small size drops the label text from 14px to 13px and tightens the spacing
|
|
64
|
+
// around the group to 12px. It is meant for a second layer of tabbing underneath a
|
|
65
|
+
// large group, not on its own. See the two levels example further down.
|
|
66
|
+
//
|
|
67
|
+
// sdsSize reaches every Tab through context, so it is set once on the group. A
|
|
68
|
+
// Tab cannot ask for a different size than the group it sits in.
|
|
69
|
+
|
|
70
|
+
import { Tab, Tabs } from "@czi-sds/components";
|
|
71
|
+
import { SyntheticEvent, useState } from "react";
|
|
72
|
+
|
|
73
|
+
function App() {
|
|
74
|
+
const [value, setValue] = useState(0);
|
|
75
|
+
|
|
76
|
+
const handleChange = (_event: SyntheticEvent, newValue: unknown) => {
|
|
77
|
+
setValue(newValue as number);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<div className="app">
|
|
82
|
+
<Tabs
|
|
83
|
+
aria-label="Sample details"
|
|
84
|
+
onChange={handleChange}
|
|
85
|
+
sdsSize="small"
|
|
86
|
+
value={value}
|
|
87
|
+
>
|
|
88
|
+
<Tab label="Metadata" />
|
|
89
|
+
<Tab label="Quality control" />
|
|
90
|
+
<Tab label="Files" />
|
|
91
|
+
</Tabs>
|
|
92
|
+
</div>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export default App;
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Tabs with count, text, and component
|
|
100
|
+
|
|
101
|
+
`count` takes any node: a number, a count of `0` on an empty tab, and a number paired with a Tag on the last tab. The group is underlined, which separates it from the content below.
|
|
102
|
+
|
|
103
|
+
**Example: TabsWithCountTextAndComponent**
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
// count sits after a tab's label and says how many items wait on that tab's
|
|
107
|
+
// screen. It takes any node, not just a number, so it can carry text or a whole
|
|
108
|
+
// component. The last tab below pairs a number with a Tag.
|
|
109
|
+
//
|
|
110
|
+
// A count of 0 is drawn rather than dropped, which is what makes an empty tab
|
|
111
|
+
// read as empty rather than as unknown. Whatever count renders becomes part of
|
|
112
|
+
// the tab's accessible name, so it is announced as "Contributors 0".
|
|
113
|
+
//
|
|
114
|
+
// underlined runs a divider under the full width of the group, separating the
|
|
115
|
+
// tabs from the content below them.
|
|
116
|
+
|
|
117
|
+
import {
|
|
118
|
+
Tab,
|
|
119
|
+
Tabs,
|
|
120
|
+
Tag,
|
|
121
|
+
getSpaces,
|
|
122
|
+
type CommonThemeProps,
|
|
123
|
+
} from "@czi-sds/components";
|
|
124
|
+
import styled from "@emotion/styled";
|
|
125
|
+
import { SyntheticEvent, useState } from "react";
|
|
126
|
+
|
|
127
|
+
const CountWithTag = styled.span<CommonThemeProps>`
|
|
128
|
+
${(props) => {
|
|
129
|
+
const spaces = getSpaces(props);
|
|
130
|
+
|
|
131
|
+
return `
|
|
132
|
+
align-items: center;
|
|
133
|
+
display: inline-flex;
|
|
134
|
+
gap: ${spaces?.s}px;
|
|
135
|
+
`;
|
|
136
|
+
}}
|
|
137
|
+
`;
|
|
138
|
+
|
|
139
|
+
function App() {
|
|
140
|
+
const [value, setValue] = useState(0);
|
|
141
|
+
|
|
142
|
+
const handleChange = (_event: SyntheticEvent, newValue: unknown) => {
|
|
143
|
+
setValue(newValue as number);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
return (
|
|
147
|
+
<div className="app">
|
|
148
|
+
<Tabs
|
|
149
|
+
aria-label="Project contents"
|
|
150
|
+
onChange={handleChange}
|
|
151
|
+
underlined
|
|
152
|
+
value={value}
|
|
153
|
+
>
|
|
154
|
+
<Tab count={4} label="Samples" />
|
|
155
|
+
<Tab count={0} label="Contributors" />
|
|
156
|
+
<Tab
|
|
157
|
+
count={
|
|
158
|
+
<CountWithTag>
|
|
159
|
+
14
|
|
160
|
+
<Tag
|
|
161
|
+
color="beta"
|
|
162
|
+
label="BETA"
|
|
163
|
+
sdsStyle="rounded"
|
|
164
|
+
sdsType="secondary"
|
|
165
|
+
/>
|
|
166
|
+
</CountWithTag>
|
|
167
|
+
}
|
|
168
|
+
label="Analyses"
|
|
169
|
+
/>
|
|
170
|
+
</Tabs>
|
|
171
|
+
</div>
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export default App;
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Tabs with panels
|
|
179
|
+
|
|
180
|
+
The wiring between a tab and the content it reveals, which the component leaves to you: matching ids on each Tab and its panel, `role="tabpanel"` on the panel, and only the selected panel rendered.
|
|
181
|
+
|
|
182
|
+
**Example: TabsWithPanels**
|
|
183
|
+
|
|
184
|
+
```tsx
|
|
185
|
+
// Tabs draws the tab strip; the panel below it is yours to render and to connect.
|
|
186
|
+
// The component sets role="tablist" and role="tab" and moves the tab stop between
|
|
187
|
+
// the tabs, but it does not know about your content, so nothing links a tab to
|
|
188
|
+
// the panel it controls until you pass the ids yourself:
|
|
189
|
+
//
|
|
190
|
+
// Tab: id="…-tab", aria-controls="…-panel"
|
|
191
|
+
// Panel: id="…-panel", role="tabpanel", aria-labelledby="…-tab"
|
|
192
|
+
//
|
|
193
|
+
// Only the selected panel is rendered here, so the tabs are the only way to reach
|
|
194
|
+
// the other content. tabIndex={0} on the panel gives it a tab stop, which lets a
|
|
195
|
+
// keyboard user move from the selected tab straight into what it revealed.
|
|
196
|
+
|
|
197
|
+
import {
|
|
198
|
+
Tab,
|
|
199
|
+
Tabs,
|
|
200
|
+
fontBodyS,
|
|
201
|
+
getSemanticColors,
|
|
202
|
+
getSpaces,
|
|
203
|
+
type CommonThemeProps,
|
|
204
|
+
} from "@czi-sds/components";
|
|
205
|
+
import styled from "@emotion/styled";
|
|
206
|
+
import { SyntheticEvent, useState } from "react";
|
|
207
|
+
|
|
208
|
+
const VIEWS = [
|
|
209
|
+
{
|
|
210
|
+
body: "12 samples collected between March and June, 3 of them flagged for re-sequencing.",
|
|
211
|
+
id: "samples",
|
|
212
|
+
label: "Samples",
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
body: "Two runs on the NovaSeq X, both passing the coverage threshold of 30x.",
|
|
216
|
+
id: "runs",
|
|
217
|
+
label: "Sequencing runs",
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
body: "A differential expression analysis finished last week and is ready to download.",
|
|
221
|
+
id: "analyses",
|
|
222
|
+
label: "Analyses",
|
|
223
|
+
},
|
|
224
|
+
];
|
|
225
|
+
|
|
226
|
+
const Panel = styled.div<CommonThemeProps>`
|
|
227
|
+
${fontBodyS}
|
|
228
|
+
|
|
229
|
+
${(props) => {
|
|
230
|
+
const semanticColors = getSemanticColors(props);
|
|
231
|
+
const spaces = getSpaces(props);
|
|
232
|
+
|
|
233
|
+
return `
|
|
234
|
+
border: 1px solid ${semanticColors?.base?.divider};
|
|
235
|
+
border-radius: 4px;
|
|
236
|
+
color: ${semanticColors?.base?.textPrimary};
|
|
237
|
+
padding: ${spaces?.l}px;
|
|
238
|
+
`;
|
|
239
|
+
}}
|
|
240
|
+
`;
|
|
241
|
+
|
|
242
|
+
function App() {
|
|
243
|
+
const [value, setValue] = useState(0);
|
|
244
|
+
|
|
245
|
+
const handleChange = (_event: SyntheticEvent, newValue: unknown) => {
|
|
246
|
+
setValue(newValue as number);
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
const view = VIEWS[value];
|
|
250
|
+
|
|
251
|
+
return (
|
|
252
|
+
<div className="app">
|
|
253
|
+
<Tabs
|
|
254
|
+
aria-label="Project sections"
|
|
255
|
+
onChange={handleChange}
|
|
256
|
+
underlined
|
|
257
|
+
value={value}
|
|
258
|
+
>
|
|
259
|
+
{VIEWS.map((item) => (
|
|
260
|
+
<Tab
|
|
261
|
+
aria-controls={`${item.id}-panel`}
|
|
262
|
+
id={`${item.id}-tab`}
|
|
263
|
+
key={item.id}
|
|
264
|
+
label={item.label}
|
|
265
|
+
/>
|
|
266
|
+
))}
|
|
267
|
+
</Tabs>
|
|
268
|
+
|
|
269
|
+
<Panel
|
|
270
|
+
aria-labelledby={`${view.id}-tab`}
|
|
271
|
+
id={`${view.id}-panel`}
|
|
272
|
+
role="tabpanel"
|
|
273
|
+
tabIndex={0}
|
|
274
|
+
>
|
|
275
|
+
{view.body}
|
|
276
|
+
</Panel>
|
|
277
|
+
</div>
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export default App;
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### A disabled tab
|
|
285
|
+
|
|
286
|
+
A tab whose content is not ready yet. It stays in the strip, greyed out, and both the pointer and the arrow keys pass over it.
|
|
287
|
+
|
|
288
|
+
**Example: TabsWithDisabledTab**
|
|
289
|
+
|
|
290
|
+
```tsx
|
|
291
|
+
// disabled comes from MUI and greys a tab out. The tab stays in the strip, so it
|
|
292
|
+
// still says that the section exists, but it cannot be clicked and the arrow keys
|
|
293
|
+
// skip past it.
|
|
294
|
+
//
|
|
295
|
+
// A tab is worth disabling when its content is not ready yet rather than not
|
|
296
|
+
// available at all. Otherwise leave it out of the group.
|
|
297
|
+
|
|
298
|
+
import { Tab, Tabs } from "@czi-sds/components";
|
|
299
|
+
import { SyntheticEvent, useState } from "react";
|
|
300
|
+
|
|
301
|
+
function App() {
|
|
302
|
+
const [value, setValue] = useState(0);
|
|
303
|
+
|
|
304
|
+
const handleChange = (_event: SyntheticEvent, newValue: unknown) => {
|
|
305
|
+
setValue(newValue as number);
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
return (
|
|
309
|
+
<div className="app">
|
|
310
|
+
<Tabs aria-label="Analysis steps" onChange={handleChange} value={value}>
|
|
311
|
+
<Tab count={12} label="Samples" />
|
|
312
|
+
<Tab disabled label="Alignment" />
|
|
313
|
+
<Tab count={3} label="Variant calls" />
|
|
314
|
+
</Tabs>
|
|
315
|
+
</div>
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
export default App;
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### Two levels of tabs
|
|
323
|
+
|
|
324
|
+
A large group over a small one, which is the pairing the small size exists for. Each level keeps its own state, and the second level resets when the first one changes.
|
|
325
|
+
|
|
326
|
+
**Example: NestedTabs**
|
|
327
|
+
|
|
328
|
+
```tsx
|
|
329
|
+
// The small size exists for a second layer of tabbing under a large group, which
|
|
330
|
+
// is the only place the design guidance puts it. Each level is its own Tabs group
|
|
331
|
+
// with its own state, and the second level resets when the first one changes, so a
|
|
332
|
+
// section never opens on a sub-tab that was chosen somewhere else.
|
|
333
|
+
//
|
|
334
|
+
// Each group brings its own spacing (24px under the large one, 12px over the
|
|
335
|
+
// small one), and those margins collapse into a single 24px gap between the two
|
|
336
|
+
// strips, so neither level needs margins added or taken away here.
|
|
337
|
+
|
|
338
|
+
import { Tab, Tabs } from "@czi-sds/components";
|
|
339
|
+
import { SyntheticEvent, useState } from "react";
|
|
340
|
+
|
|
341
|
+
const SECTIONS = [
|
|
342
|
+
{ label: "Samples", subTabs: ["All", "Flagged", "Archived"] },
|
|
343
|
+
{ label: "Sequencing runs", subTabs: ["Completed", "In progress"] },
|
|
344
|
+
];
|
|
345
|
+
|
|
346
|
+
function App() {
|
|
347
|
+
const [section, setSection] = useState(0);
|
|
348
|
+
const [subTab, setSubTab] = useState(0);
|
|
349
|
+
|
|
350
|
+
const handleSectionChange = (_event: SyntheticEvent, newValue: unknown) => {
|
|
351
|
+
setSection(newValue as number);
|
|
352
|
+
setSubTab(0);
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
const handleSubTabChange = (_event: SyntheticEvent, newValue: unknown) => {
|
|
356
|
+
setSubTab(newValue as number);
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
return (
|
|
360
|
+
<div className="app">
|
|
361
|
+
<Tabs
|
|
362
|
+
aria-label="Project sections"
|
|
363
|
+
onChange={handleSectionChange}
|
|
364
|
+
underlined
|
|
365
|
+
value={section}
|
|
366
|
+
>
|
|
367
|
+
{SECTIONS.map((item) => (
|
|
368
|
+
<Tab key={item.label} label={item.label} />
|
|
369
|
+
))}
|
|
370
|
+
</Tabs>
|
|
371
|
+
|
|
372
|
+
<Tabs
|
|
373
|
+
aria-label={`${SECTIONS[section].label} views`}
|
|
374
|
+
onChange={handleSubTabChange}
|
|
375
|
+
sdsSize="small"
|
|
376
|
+
value={subTab}
|
|
377
|
+
>
|
|
378
|
+
{SECTIONS[section].subTabs.map((label) => (
|
|
379
|
+
<Tab key={label} label={label} />
|
|
380
|
+
))}
|
|
381
|
+
</Tabs>
|
|
382
|
+
</div>
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export default App;
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
## SDS vs MUI
|
|
390
|
+
|
|
391
|
+
SDS Tabs wraps MUI's Tabs, and SDS Tab wraps MUI's Tab. Both are exported from the library and are used together: a Tab has to be a direct child of a Tabs group. The differences from MUI are these:
|
|
392
|
+
|
|
393
|
+
- `sdsSize`: takes `"large"` (default) or `"small"`, and sets both the size of the label text on every tab in the group (14px for large, 13px for small) and the spacing around the group: 16px above and 24px below at the large size, 12px on both sides at the small size. It reaches the tabs through context, so it belongs on the group; a Tab cannot ask for a size of its own.
|
|
394
|
+
|
|
395
|
+
- `underlined`: runs a divider under the full width of the group when `true`, separating the tabs from the content below them. Without it only the selected tab is underlined, in the accent color.
|
|
396
|
+
|
|
397
|
+
- **count on a Tab:** a value drawn after the label, saying how many items wait on that tab's screen. It is a prop on each Tab, not on the group, and it takes any node: a number, a string, or a component such as a Tag. A count of `0` is drawn rather than dropped.
|
|
398
|
+
|
|
399
|
+
- **The label is rendered twice:** each tab carries a second, hidden copy of its label set in semibold, which holds the width the label will need once the tab is selected and its text turns semibold. It is why selecting a tab does not shift the strip. The copy is hidden from assistive technology, but it is in the DOM, so a test looking for a tab's text by content finds two nodes.
|
|
400
|
+
|
|
401
|
+
- **The indicator is fixed:** SDS styles the selected tab's underline itself (2px, in the accent color), so MUI's `indicatorColor` and `textColor` have no visible effect. That styling arrives through `slotProps.indicator`, so passing your own `slotProps` replaces it and leaves the indicator with MUI's default appearance.
|
|
402
|
+
|
|
403
|
+
- **Only a horizontal, full-width strip is supported:** SDS turns off the clipping that MUI's scroller relies on, so `variant="scrollable"` neither clips nor scrolls: too many tabs spill out of a narrow container rather than scrolling inside it. Setting `orientation="vertical"` stacks the tabs but leaves the indicator drawn as a horizontal underline. Keep the group horizontal and keep the number of tabs to what fits.
|
|
404
|
+
|
|
405
|
+
- **Icons are not part of the design:** MUI's `icon` and `iconPosition` props still render, but SDS does not style them, so an SDS tab is a label and an optional count.
|
|
406
|
+
|
|
407
|
+
## MUI Documentation
|
|
408
|
+
|
|
409
|
+
Documentation for the underlying MUI component can be found [here](https://mui.com/material-ui/react-tabs/).
|
|
410
|
+
|
|
411
|
+
## Behavior and accessibility
|
|
412
|
+
|
|
413
|
+
- The group is controlled. It draws the tab whose value matches `value`, so the selection has to live in your state and `onChange` has to write it back; without that, clicking a tab changes nothing. A value that matches no tab leaves the group with nothing selected and logs a warning listing the values it accepts. Passing `value={false}` selects nothing on purpose, without the warning.
|
|
414
|
+
|
|
415
|
+
- Tabs are numbered by position unless a Tab is given a `value` of its own, which is worth doing when the strip can change, so the selection does not follow whichever tab happens to sit in that position.
|
|
416
|
+
|
|
417
|
+
- A Tab has to be a direct child of a Tabs group: rendered on its own it throws, and wrapped in an element it renders but never selects, since the group passes its state to its immediate children. Mapping over an array is fine.
|
|
418
|
+
|
|
419
|
+
- The group renders `role="tablist"` with a `role="tab"` button per tab, and keeps a single tab stop: Tab enters the strip on the selected tab, the arrow keys move between tabs and wrap around, Home and End jump to the ends, and Space or Enter selects the focused tab. Selecting on focus alone is available through MUI's `selectionFollowsFocus`, which suits a strip whose panels are already loaded.
|
|
420
|
+
|
|
421
|
+
- Nothing connects a tab to the content it reveals until you do it. Give each Tab an `id` and an `aria-controls` pointing at its panel, and give the panel `role="tabpanel"` and an `aria-labelledby` pointing back at the tab. The panels example below shows the wiring.
|
|
422
|
+
|
|
423
|
+
- `aria-label` on the group lands on the tablist and names the set of tabs. Worth setting, since a screen reader otherwise announces an unnamed tab list.
|
|
424
|
+
|
|
425
|
+
- A tab's accessible name is its label followed by whatever `count` renders, so a count of `0` is announced as part of the name: "Contributors 0". Keep a count short enough to read that way.
|
|
426
|
+
|
|
427
|
+
- A `disabled` tab keeps its place in the strip but cannot be clicked, and the arrow keys skip past it. It says that a section exists but is not ready; a section that does not apply at all is better left out of the group.
|
|
428
|
+
|
|
429
|
+
## Props
|
|
430
|
+
|
|
431
|
+
Any custom SDS props and MUI props required for implementation are found on the table below. See the MUI documentation for additional optional props.
|
|
432
|
+
|
|
433
|
+
| Name | Type | Default | Description |
|
|
434
|
+
| ----------------------- | ----------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------ |
|
|
435
|
+
| `children` | `React.ReactNode` | - (required) | The tabs in the group, as direct Tab children in the order they appear. |
|
|
436
|
+
| `value` | `any` | - (required) | The selected tab's value: its position in the group, or the value set on the Tab itself. Pass `false` to select nothing. |
|
|
437
|
+
| `onChange` | `(event, value: any) => void` | - | Runs when a tab is selected, with that tab's value. Write it back to `value`, or the group will keep showing the same tab. |
|
|
438
|
+
| `sdsSize` | `"small"` \| `"large"` | `"large"` | The size of the label text on every tab in the group, and the spacing around the group. |
|
|
439
|
+
| `underlined` | `boolean` | `false` | Draws a divider along the full width of the group, below the tabs; the selected tab's indicator sits on top of it. |
|
|
440
|
+
| `aria-label` | `string` | - | Names the tablist. Say what the tabs switch between, and use `aria-labelledby` instead when a visible heading already says it. |
|
|
441
|
+
| `selectionFollowsFocus` | `boolean` | `false` | MUI's prop for selecting a tab as soon as the arrow keys reach it, instead of waiting for Space or Enter. |
|
|
442
|
+
|
|
443
|
+
### Tab
|
|
444
|
+
|
|
445
|
+
One tab in the group. Beyond the props below it takes MUI's Tab props and the attributes of the button it renders.
|
|
446
|
+
|
|
447
|
+
| Name | Type | Default | Description |
|
|
448
|
+
| --------------- | ----------------- | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
449
|
+
| `label` | `React.ReactNode` | - | The tab's text, and its accessible name. Rendered twice, once visibly and once hidden in semibold to hold the width the selected state needs. |
|
|
450
|
+
| `count` | `React.ReactNode` | - | A value drawn after the label: a number, a string, or a component. `0` is drawn rather than dropped, and whatever renders joins the tab's accessible name. |
|
|
451
|
+
| `value` | `any` | position in the group | Identifies the tab in the group's `value` and `onChange`. Worth setting when the set of tabs can change. |
|
|
452
|
+
| `disabled` | `boolean` | `false` | Greys the tab out and takes it out of reach of the pointer and the arrow keys, while leaving it in the strip. |
|
|
453
|
+
| `id` | `string` | - | The tab's id, for a panel's `aria-labelledby` to point at. Not generated for you. |
|
|
454
|
+
| `aria-controls` | `string` | - | The id of the panel this tab reveals. |
|
|
455
|
+
| `selected` | `boolean` | - | Set by the group from its value, and used to draw the label in semibold. Not something to pass yourself. |
|