@czi-sds/mcp 0.15.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.
- 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/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 +262 -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 +67 -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/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/dist/stdio.js +483 -780
- package/package.json +12 -8
- 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,131 @@
|
|
|
1
|
+
# TagFilter
|
|
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/TagFilter/index.tsx).
|
|
6
|
+
|
|
7
|
+
## Import
|
|
8
|
+
|
|
9
|
+
**React TypeScript**
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
import { TagFilter } from "@czi-sds/components";
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Code examples
|
|
16
|
+
|
|
17
|
+
### Default TagFilter
|
|
18
|
+
|
|
19
|
+
A row of applied filters, each removing itself from the page's state through `onDelete`. The row is a named group, which gives the identical "Delete Tag" buttons some context.
|
|
20
|
+
|
|
21
|
+
**Example: DefaultTagFilter**
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
// TagFilter is the tag that comes off: onDelete is required, and removing the tag
|
|
25
|
+
// from your own state is what makes it disappear. Everything about how it looks is
|
|
26
|
+
// fixed (the accent fill, the square shape, and the small X), so there are no
|
|
27
|
+
// style props to pass.
|
|
28
|
+
//
|
|
29
|
+
// Both the tag and its X are focusable, so each filter is two tab stops: Backspace
|
|
30
|
+
// or Delete on the tag removes it, and so does Enter on the X.
|
|
31
|
+
|
|
32
|
+
import {
|
|
33
|
+
TagFilter,
|
|
34
|
+
fontBodyXs,
|
|
35
|
+
getSemanticColors,
|
|
36
|
+
getSpaces,
|
|
37
|
+
type CommonThemeProps,
|
|
38
|
+
} from "@czi-sds/components";
|
|
39
|
+
import styled from "@emotion/styled";
|
|
40
|
+
import { useState } from "react";
|
|
41
|
+
|
|
42
|
+
const FILTERS = ["Homo sapiens", "Lung", "10x 3' v3", "Healthy"];
|
|
43
|
+
|
|
44
|
+
const Row = styled.div<CommonThemeProps>`
|
|
45
|
+
${(props) => {
|
|
46
|
+
const spaces = getSpaces(props);
|
|
47
|
+
|
|
48
|
+
return `
|
|
49
|
+
display: flex;
|
|
50
|
+
flex-wrap: wrap;
|
|
51
|
+
gap: ${spaces?.s}px;
|
|
52
|
+
margin-bottom: ${spaces?.m}px;
|
|
53
|
+
min-height: 28px;
|
|
54
|
+
`;
|
|
55
|
+
}}
|
|
56
|
+
`;
|
|
57
|
+
|
|
58
|
+
const Readout = styled.p<CommonThemeProps>`
|
|
59
|
+
${fontBodyXs}
|
|
60
|
+
|
|
61
|
+
${(props) => {
|
|
62
|
+
const semanticColors = getSemanticColors(props);
|
|
63
|
+
|
|
64
|
+
return `
|
|
65
|
+
color: ${semanticColors?.base?.textSecondary};
|
|
66
|
+
margin: 0;
|
|
67
|
+
`;
|
|
68
|
+
}}
|
|
69
|
+
`;
|
|
70
|
+
|
|
71
|
+
function App() {
|
|
72
|
+
const [filters, setFilters] = useState(FILTERS);
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<div className="app">
|
|
76
|
+
<Row aria-label="Applied filters" role="group">
|
|
77
|
+
{filters.map((filter) => (
|
|
78
|
+
<TagFilter
|
|
79
|
+
key={filter}
|
|
80
|
+
label={filter}
|
|
81
|
+
onDelete={() =>
|
|
82
|
+
setFilters((current) => current.filter((item) => item !== filter))
|
|
83
|
+
}
|
|
84
|
+
/>
|
|
85
|
+
))}
|
|
86
|
+
</Row>
|
|
87
|
+
|
|
88
|
+
<Readout>
|
|
89
|
+
{filters.length > 0
|
|
90
|
+
? `${filters.length} of ${FILTERS.length} filters applied`
|
|
91
|
+
: "No filters applied"}
|
|
92
|
+
</Readout>
|
|
93
|
+
</div>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export default App;
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## SDS vs MUI
|
|
101
|
+
|
|
102
|
+
TagFilter is built on the SDS Tag, and so on MUI's Chip, but it exists for one job: showing which Filters are applied and letting them be taken off again. It has one appearance and no style props:
|
|
103
|
+
|
|
104
|
+
- **onDelete is required.** MUI treats it as the switch that decides whether a chip can be deleted; here the delete control is always there, so the handler has to be. Removing the tag from your own state is what makes it disappear.
|
|
105
|
+
|
|
106
|
+
- **The look is fixed:** the accent fill, the square shape, the small semibold label and the 12px X. Tag's `sdsStyle`, `sdsType`, `sdsSize` and `color` are not part of the type, and MUI's `variant` is not either.
|
|
107
|
+
|
|
108
|
+
- **The X cannot be swapped:** SDS applies its own `deleteIcon` (an SDS Button holding the XMark icon) after your props, so a `deleteIcon` of your own is ignored without a warning.
|
|
109
|
+
|
|
110
|
+
## MUI Documentation
|
|
111
|
+
|
|
112
|
+
Documentation for the underlying MUI component can be found [here](https://mui.com/material-ui/react-chip/).
|
|
113
|
+
|
|
114
|
+
## Behavior and accessibility
|
|
115
|
+
|
|
116
|
+
- There are two ways to remove a filter, and both are reachable from the keyboard: the X is a button that answers Enter, and the tag itself answers Backspace and Delete. That also means each filter tag is two tab stops.
|
|
117
|
+
|
|
118
|
+
- The X button is named "Delete Tag" on every filter tag, and the name cannot be changed. The label beside it is read as plain text rather than as part of the button, so a row of filters is worth wrapping in a group with a name of its own, to say what the buttons are removing. The example below uses `role="group"` with an `aria-label`.
|
|
119
|
+
|
|
120
|
+
- The tag carries `role="none presentation"`, so it contributes no semantics of its own. Adding an `onClick` does not change that: the tag becomes clickable to the pointer while still announcing nothing, so leave the clicking to the X.
|
|
121
|
+
|
|
122
|
+
- Like a Tag, a filter tag does not wrap: a long filter value is cut off with an ellipsis. Keep the values short, or shorten them yourself so the part that survives is the part that matters.
|
|
123
|
+
|
|
124
|
+
## Props
|
|
125
|
+
|
|
126
|
+
Any custom SDS props and MUI props required for implementation are found on the table below. See the MUI documentation for additional optional props.
|
|
127
|
+
|
|
128
|
+
| Name | Type | Default | Description |
|
|
129
|
+
| ---------- | ---------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
130
|
+
| `label` | `string` | - (required) | The filter's text. Cut off with an ellipsis when there is not room for it. |
|
|
131
|
+
| `onDelete` | `function` | - (required) | Runs when the X is clicked or activated, and when Backspace or Delete is pressed on the tag. Drop the filter from your state here; the component does not remove itself. **Signature:** `function(event: SyntheticEvent) => void` |
|
|
@@ -0,0 +1,561 @@
|
|
|
1
|
+
# Tooltip
|
|
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/Tooltip/index.tsx).
|
|
6
|
+
|
|
7
|
+
## Import
|
|
8
|
+
|
|
9
|
+
**React TypeScript**
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
import { Tooltip } from "@czi-sds/components";
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Code examples
|
|
16
|
+
|
|
17
|
+
### **Default Tooltip**
|
|
18
|
+
|
|
19
|
+
A title and a trigger, which is all a tooltip needs. The surface, the arrow, and the placement below the trigger are what the component gives you without being asked.
|
|
20
|
+
|
|
21
|
+
**Example: DefaultTooltip**
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
// The least a tooltip needs: a title, and something to hang it on. The surface,
|
|
25
|
+
// the arrow and the placement below the trigger all come as standard, so hover or
|
|
26
|
+
// focus the button to see what the component does unasked.
|
|
27
|
+
//
|
|
28
|
+
// The trigger still says what it is. A tooltip adds to a label; it is not one.
|
|
29
|
+
|
|
30
|
+
import { Button, Tooltip } from "@czi-sds/components";
|
|
31
|
+
import styled from "@emotion/styled";
|
|
32
|
+
|
|
33
|
+
const Stage = styled.div`
|
|
34
|
+
align-items: center;
|
|
35
|
+
display: flex;
|
|
36
|
+
justify-content: center;
|
|
37
|
+
min-height: 120px;
|
|
38
|
+
`;
|
|
39
|
+
|
|
40
|
+
function App() {
|
|
41
|
+
return (
|
|
42
|
+
<div className="app">
|
|
43
|
+
<Stage>
|
|
44
|
+
<Tooltip title="Recalculated whenever the filters change.">
|
|
45
|
+
<Button sdsStyle="minimal" sdsType="secondary">
|
|
46
|
+
Sequencing depth
|
|
47
|
+
</Button>
|
|
48
|
+
</Tooltip>
|
|
49
|
+
</Stage>
|
|
50
|
+
</div>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export default App;
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### **Tooltip with a subtitle**
|
|
58
|
+
|
|
59
|
+
A second line under the title, in smaller and dimmer text, for a detail that would crowd the title itself.
|
|
60
|
+
|
|
61
|
+
**Example: TooltipWithSubtitle**
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
// subtitle sits under title in smaller, dimmer text, for a detail like how to use
|
|
65
|
+
// the thing being pointed at. Both are optional, but a tooltip with neither a
|
|
66
|
+
// title, a subtitle, nor a componentSlot renders nothing at all.
|
|
67
|
+
//
|
|
68
|
+
// The trigger needs its own accessible name. An icon-only button gets one from
|
|
69
|
+
// aria-label, and it needs one because the tooltip text is not a substitute.
|
|
70
|
+
|
|
71
|
+
import { Button, Icon, Tooltip } from "@czi-sds/components";
|
|
72
|
+
import styled from "@emotion/styled";
|
|
73
|
+
|
|
74
|
+
const Stage = styled.div`
|
|
75
|
+
align-items: center;
|
|
76
|
+
display: flex;
|
|
77
|
+
justify-content: center;
|
|
78
|
+
min-height: 120px;
|
|
79
|
+
`;
|
|
80
|
+
|
|
81
|
+
function App() {
|
|
82
|
+
return (
|
|
83
|
+
<div className="app">
|
|
84
|
+
<Stage>
|
|
85
|
+
<Tooltip
|
|
86
|
+
placement="top"
|
|
87
|
+
subtitle="Values are recalculated whenever the filters change."
|
|
88
|
+
title="Sequencing depth"
|
|
89
|
+
>
|
|
90
|
+
<Button
|
|
91
|
+
aria-label="About sequencing depth"
|
|
92
|
+
sdsStyle="minimal"
|
|
93
|
+
sdsType="secondary"
|
|
94
|
+
>
|
|
95
|
+
<Icon sdsIcon="InfoCircle" sdsSize="s" />
|
|
96
|
+
</Button>
|
|
97
|
+
</Tooltip>
|
|
98
|
+
</Stage>
|
|
99
|
+
</div>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export default App;
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### **Tooltip widths**
|
|
107
|
+
|
|
108
|
+
Both widths side by side. The default caps the tooltip at 250px; the wide one holds 550px of left-aligned text.
|
|
109
|
+
|
|
110
|
+
**Example: TooltipWidths**
|
|
111
|
+
|
|
112
|
+
```tsx
|
|
113
|
+
// width caps how far a tooltip can stretch before its text wraps. The default stops
|
|
114
|
+
// at 250px and centres the text; "wide" lifts the cap to 550px and switches the text
|
|
115
|
+
// to the left, which is what longer explanations need.
|
|
116
|
+
|
|
117
|
+
import {
|
|
118
|
+
Button,
|
|
119
|
+
Icon,
|
|
120
|
+
Tooltip,
|
|
121
|
+
fontBodyXs,
|
|
122
|
+
getSemanticColors,
|
|
123
|
+
getSpaces,
|
|
124
|
+
type CommonThemeProps,
|
|
125
|
+
} from "@czi-sds/components";
|
|
126
|
+
import styled from "@emotion/styled";
|
|
127
|
+
|
|
128
|
+
const LONG_TEXT =
|
|
129
|
+
"Reads are aligned to the reference genome with minimap2, then filtered to remove duplicates and anything below the mapping quality threshold set for the project.";
|
|
130
|
+
|
|
131
|
+
const Stage = styled.div<CommonThemeProps>`
|
|
132
|
+
${(props) => {
|
|
133
|
+
const spaces = getSpaces(props);
|
|
134
|
+
|
|
135
|
+
return `
|
|
136
|
+
align-items: center;
|
|
137
|
+
display: flex;
|
|
138
|
+
gap: ${spaces?.xxl}px;
|
|
139
|
+
justify-content: center;
|
|
140
|
+
min-height: 140px;
|
|
141
|
+
`;
|
|
142
|
+
}}
|
|
143
|
+
`;
|
|
144
|
+
|
|
145
|
+
const Labelled = styled.div<CommonThemeProps>`
|
|
146
|
+
${fontBodyXs}
|
|
147
|
+
|
|
148
|
+
${(props) => {
|
|
149
|
+
const semanticColors = getSemanticColors(props);
|
|
150
|
+
const spaces = getSpaces(props);
|
|
151
|
+
|
|
152
|
+
return `
|
|
153
|
+
align-items: center;
|
|
154
|
+
color: ${semanticColors?.base?.textSecondary};
|
|
155
|
+
display: flex;
|
|
156
|
+
flex-direction: column;
|
|
157
|
+
gap: ${spaces?.xs}px;
|
|
158
|
+
`;
|
|
159
|
+
}}
|
|
160
|
+
`;
|
|
161
|
+
|
|
162
|
+
function App() {
|
|
163
|
+
return (
|
|
164
|
+
<div className="app">
|
|
165
|
+
<Stage>
|
|
166
|
+
<Labelled>
|
|
167
|
+
<Tooltip placement="top" title="Aligned with minimap2">
|
|
168
|
+
<Button
|
|
169
|
+
aria-label="About alignment"
|
|
170
|
+
sdsStyle="minimal"
|
|
171
|
+
sdsType="secondary"
|
|
172
|
+
>
|
|
173
|
+
<Icon sdsIcon="InfoCircle" sdsSize="s" />
|
|
174
|
+
</Button>
|
|
175
|
+
</Tooltip>
|
|
176
|
+
Default width
|
|
177
|
+
</Labelled>
|
|
178
|
+
|
|
179
|
+
<Labelled>
|
|
180
|
+
<Tooltip placement="top" title={LONG_TEXT} width="wide">
|
|
181
|
+
<Button
|
|
182
|
+
aria-label="About the alignment pipeline"
|
|
183
|
+
sdsStyle="minimal"
|
|
184
|
+
sdsType="secondary"
|
|
185
|
+
>
|
|
186
|
+
<Icon sdsIcon="InfoCircle" sdsSize="s" />
|
|
187
|
+
</Button>
|
|
188
|
+
</Tooltip>
|
|
189
|
+
Wide
|
|
190
|
+
</Labelled>
|
|
191
|
+
</Stage>
|
|
192
|
+
</div>
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export default App;
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### **Placement and the arrow**
|
|
200
|
+
|
|
201
|
+
Four of the twelve placements, and what `arrowOffset` does to the arrow: it sets a position rather than nudging one.
|
|
202
|
+
|
|
203
|
+
**Example: TooltipPlacement**
|
|
204
|
+
|
|
205
|
+
```tsx
|
|
206
|
+
// placement is MUI's, and all twelve values work: a side, optionally with -start or
|
|
207
|
+
// -end to pin the tooltip to one end of the trigger. A tooltip that would fall off
|
|
208
|
+
// the screen flips to the opposite side on its own.
|
|
209
|
+
//
|
|
210
|
+
// arrowOffset is SDS's, and it is not an offset: the number becomes the arrow's left
|
|
211
|
+
// position inside the tooltip, in pixels from its left edge. Small values move the
|
|
212
|
+
// arrow near the left corner and values past the tooltip's width push it out of
|
|
213
|
+
// sight, so it is only worth reaching for when the arrow has to line up with
|
|
214
|
+
// something narrower than the trigger.
|
|
215
|
+
|
|
216
|
+
import {
|
|
217
|
+
Button,
|
|
218
|
+
Icon,
|
|
219
|
+
Tooltip,
|
|
220
|
+
fontBodyXs,
|
|
221
|
+
getSemanticColors,
|
|
222
|
+
getSpaces,
|
|
223
|
+
type CommonThemeProps,
|
|
224
|
+
type TooltipProps,
|
|
225
|
+
} from "@czi-sds/components";
|
|
226
|
+
import styled from "@emotion/styled";
|
|
227
|
+
|
|
228
|
+
const PLACEMENTS: TooltipProps["placement"][] = [
|
|
229
|
+
"top",
|
|
230
|
+
"right",
|
|
231
|
+
"bottom-start",
|
|
232
|
+
"left-end",
|
|
233
|
+
];
|
|
234
|
+
|
|
235
|
+
const Stage = styled.div<CommonThemeProps>`
|
|
236
|
+
${(props) => {
|
|
237
|
+
const spaces = getSpaces(props);
|
|
238
|
+
|
|
239
|
+
return `
|
|
240
|
+
display: flex;
|
|
241
|
+
flex-wrap: wrap;
|
|
242
|
+
gap: ${spaces?.xxl}px;
|
|
243
|
+
justify-content: center;
|
|
244
|
+
padding: ${spaces?.xxl}px 0;
|
|
245
|
+
`;
|
|
246
|
+
}}
|
|
247
|
+
`;
|
|
248
|
+
|
|
249
|
+
const Labelled = styled.div<CommonThemeProps>`
|
|
250
|
+
${fontBodyXs}
|
|
251
|
+
|
|
252
|
+
${(props) => {
|
|
253
|
+
const semanticColors = getSemanticColors(props);
|
|
254
|
+
const spaces = getSpaces(props);
|
|
255
|
+
|
|
256
|
+
return `
|
|
257
|
+
align-items: center;
|
|
258
|
+
color: ${semanticColors?.base?.textSecondary};
|
|
259
|
+
display: flex;
|
|
260
|
+
flex-direction: column;
|
|
261
|
+
gap: ${spaces?.xs}px;
|
|
262
|
+
`;
|
|
263
|
+
}}
|
|
264
|
+
`;
|
|
265
|
+
|
|
266
|
+
function App() {
|
|
267
|
+
return (
|
|
268
|
+
<div className="app">
|
|
269
|
+
<Stage>
|
|
270
|
+
{PLACEMENTS.map((placement) => (
|
|
271
|
+
<Labelled key={placement}>
|
|
272
|
+
<Tooltip placement={placement} title={`Placed ${placement}`}>
|
|
273
|
+
<Button
|
|
274
|
+
aria-label={`Tooltip placed ${placement}`}
|
|
275
|
+
sdsStyle="minimal"
|
|
276
|
+
sdsType="secondary"
|
|
277
|
+
>
|
|
278
|
+
<Icon sdsIcon="InfoCircle" sdsSize="s" />
|
|
279
|
+
</Button>
|
|
280
|
+
</Tooltip>
|
|
281
|
+
{placement}
|
|
282
|
+
</Labelled>
|
|
283
|
+
))}
|
|
284
|
+
|
|
285
|
+
<Labelled>
|
|
286
|
+
<Tooltip
|
|
287
|
+
arrowOffset={12}
|
|
288
|
+
placement="top"
|
|
289
|
+
title="Arrow pinned 12px from the left edge"
|
|
290
|
+
>
|
|
291
|
+
<Button
|
|
292
|
+
aria-label="Tooltip with an offset arrow"
|
|
293
|
+
sdsStyle="minimal"
|
|
294
|
+
sdsType="secondary"
|
|
295
|
+
>
|
|
296
|
+
<Icon sdsIcon="InfoCircle" sdsSize="s" />
|
|
297
|
+
</Button>
|
|
298
|
+
</Tooltip>
|
|
299
|
+
arrowOffset
|
|
300
|
+
</Labelled>
|
|
301
|
+
</Stage>
|
|
302
|
+
</div>
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export default App;
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### **Tooltip with a component slot**
|
|
310
|
+
|
|
311
|
+
Content that is not a string goes in `componentSlot`, under the title. The pointer can travel into the tooltip, which is what makes the link inside it usable.
|
|
312
|
+
|
|
313
|
+
**Example: TooltipWithComponentSlot**
|
|
314
|
+
|
|
315
|
+
```tsx
|
|
316
|
+
// componentSlot takes anything React can render and puts it under the title and
|
|
317
|
+
// subtitle, with 12px between them. It is the way to get something other than text
|
|
318
|
+
// into a tooltip (a legend, a thumbnail, a link) because title and subtitle are
|
|
319
|
+
// each wrapped in a paragraph, so a table or a div passed to title produces invalid
|
|
320
|
+
// HTML.
|
|
321
|
+
//
|
|
322
|
+
// A tooltip is interactive: the pointer can travel from the trigger, across the gap,
|
|
323
|
+
// and onto the tooltip without closing it, which is what makes the link below
|
|
324
|
+
// reachable. Content that has to be clicked only works because of that, and it stops
|
|
325
|
+
// working the moment disableInteractive is set.
|
|
326
|
+
|
|
327
|
+
import {
|
|
328
|
+
Button,
|
|
329
|
+
Icon,
|
|
330
|
+
Tooltip,
|
|
331
|
+
focusVisibleA11yStyle,
|
|
332
|
+
fontBodyXs,
|
|
333
|
+
fontLinkXs,
|
|
334
|
+
getSemanticColors,
|
|
335
|
+
getSpaces,
|
|
336
|
+
type CommonThemeProps,
|
|
337
|
+
} from "@czi-sds/components";
|
|
338
|
+
import styled from "@emotion/styled";
|
|
339
|
+
|
|
340
|
+
const Stage = styled.div`
|
|
341
|
+
align-items: center;
|
|
342
|
+
display: flex;
|
|
343
|
+
justify-content: center;
|
|
344
|
+
min-height: 140px;
|
|
345
|
+
`;
|
|
346
|
+
|
|
347
|
+
const Legend = styled.div<CommonThemeProps>`
|
|
348
|
+
${fontBodyXs}
|
|
349
|
+
|
|
350
|
+
${(props) => {
|
|
351
|
+
const spaces = getSpaces(props);
|
|
352
|
+
|
|
353
|
+
return `
|
|
354
|
+
display: flex;
|
|
355
|
+
flex-direction: column;
|
|
356
|
+
gap: ${spaces?.xxs}px;
|
|
357
|
+
text-align: left;
|
|
358
|
+
`;
|
|
359
|
+
}}
|
|
360
|
+
`;
|
|
361
|
+
|
|
362
|
+
const Swatch = styled.span<CommonThemeProps>`
|
|
363
|
+
${(props) => {
|
|
364
|
+
const spaces = getSpaces(props);
|
|
365
|
+
|
|
366
|
+
return `
|
|
367
|
+
align-items: center;
|
|
368
|
+
display: flex;
|
|
369
|
+
gap: ${spaces?.s}px;
|
|
370
|
+
|
|
371
|
+
&::before {
|
|
372
|
+
border-radius: 2px;
|
|
373
|
+
content: "";
|
|
374
|
+
height: 10px;
|
|
375
|
+
width: 10px;
|
|
376
|
+
}
|
|
377
|
+
`;
|
|
378
|
+
}}
|
|
379
|
+
`;
|
|
380
|
+
|
|
381
|
+
const PassingSwatch = styled(Swatch)<CommonThemeProps>`
|
|
382
|
+
${(props) => `
|
|
383
|
+
&::before {
|
|
384
|
+
background-color: ${getSemanticColors(props)?.positive?.fillPrimary};
|
|
385
|
+
}
|
|
386
|
+
`}
|
|
387
|
+
`;
|
|
388
|
+
|
|
389
|
+
const FailingSwatch = styled(Swatch)<CommonThemeProps>`
|
|
390
|
+
${(props) => `
|
|
391
|
+
&::before {
|
|
392
|
+
background-color: ${getSemanticColors(props)?.negative?.fillPrimary};
|
|
393
|
+
}
|
|
394
|
+
`}
|
|
395
|
+
`;
|
|
396
|
+
|
|
397
|
+
// The tooltip renders in a portal outside the page's styles, so a link inside one
|
|
398
|
+
// carries the SDS link type style itself.
|
|
399
|
+
const SlotLink = styled.a<CommonThemeProps>`
|
|
400
|
+
${fontLinkXs}
|
|
401
|
+
${focusVisibleA11yStyle}
|
|
402
|
+
|
|
403
|
+
${(props) => {
|
|
404
|
+
const semanticColors = getSemanticColors(props);
|
|
405
|
+
|
|
406
|
+
return `
|
|
407
|
+
color: ${semanticColors?.accent?.textAction};
|
|
408
|
+
text-underline-offset: 2.5px;
|
|
409
|
+
|
|
410
|
+
&:hover {
|
|
411
|
+
color: ${semanticColors?.accent?.textActionHover};
|
|
412
|
+
}
|
|
413
|
+
`;
|
|
414
|
+
}}
|
|
415
|
+
`;
|
|
416
|
+
|
|
417
|
+
function App() {
|
|
418
|
+
return (
|
|
419
|
+
<div className="app">
|
|
420
|
+
<Stage>
|
|
421
|
+
<Tooltip
|
|
422
|
+
componentSlot={
|
|
423
|
+
<Legend>
|
|
424
|
+
<PassingSwatch>Above 30x coverage</PassingSwatch>
|
|
425
|
+
<FailingSwatch>Below the threshold</FailingSwatch>
|
|
426
|
+
<SlotLink href="https://sds.czi.design/009eaf17b">
|
|
427
|
+
Read the full method
|
|
428
|
+
</SlotLink>
|
|
429
|
+
</Legend>
|
|
430
|
+
}
|
|
431
|
+
placement="right"
|
|
432
|
+
textAlign="left"
|
|
433
|
+
title="Coverage"
|
|
434
|
+
>
|
|
435
|
+
<Button
|
|
436
|
+
aria-label="Coverage legend"
|
|
437
|
+
sdsStyle="minimal"
|
|
438
|
+
sdsType="secondary"
|
|
439
|
+
>
|
|
440
|
+
<Icon sdsIcon="InfoCircle" sdsSize="s" />
|
|
441
|
+
</Button>
|
|
442
|
+
</Tooltip>
|
|
443
|
+
</Stage>
|
|
444
|
+
</div>
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
export default App;
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
### **Tooltip on a disabled element**
|
|
452
|
+
|
|
453
|
+
A disabled control reports no hovers, so the tooltip goes on a span around it.
|
|
454
|
+
|
|
455
|
+
> **Note for Tooltips on Disabled elements**
|
|
456
|
+
>
|
|
457
|
+
> If the tooltip wraps a disabled component, please make sure to wrap the children in a `<span>` tag. SDS puts `tabIndex={0}` on whatever the tooltip wraps, so that span also becomes a tab stop and the tooltip opens on focus.
|
|
458
|
+
>
|
|
459
|
+
> [https://mui.com/components/tooltips/#disabled-elements](https://mui.com/components/tooltips/#disabled-elements)
|
|
460
|
+
|
|
461
|
+
**Example: TooltipOnDisabledElement**
|
|
462
|
+
|
|
463
|
+
```tsx
|
|
464
|
+
// A disabled button fires no pointer events, so a tooltip attached straight to one
|
|
465
|
+
// never opens. Wrapping it in a span gives the tooltip an element that still reports
|
|
466
|
+
// hovers.
|
|
467
|
+
//
|
|
468
|
+
// The span works for the keyboard too, without any extra work: SDS puts tabIndex={0}
|
|
469
|
+
// on whatever a tooltip wraps, so the span becomes a tab stop of its own and the
|
|
470
|
+
// tooltip opens when it takes focus. That is the one route left, since the disabled
|
|
471
|
+
// button itself cannot be focused.
|
|
472
|
+
|
|
473
|
+
import { Button, Tooltip } from "@czi-sds/components";
|
|
474
|
+
import styled from "@emotion/styled";
|
|
475
|
+
|
|
476
|
+
const Stage = styled.div`
|
|
477
|
+
align-items: center;
|
|
478
|
+
display: flex;
|
|
479
|
+
justify-content: center;
|
|
480
|
+
min-height: 120px;
|
|
481
|
+
`;
|
|
482
|
+
|
|
483
|
+
function App() {
|
|
484
|
+
return (
|
|
485
|
+
<div className="app">
|
|
486
|
+
<Stage>
|
|
487
|
+
<Tooltip
|
|
488
|
+
placement="top"
|
|
489
|
+
title="Pick at least one sample before exporting"
|
|
490
|
+
>
|
|
491
|
+
<span>
|
|
492
|
+
<Button disabled sdsStyle="solid" sdsType="primary">
|
|
493
|
+
Export
|
|
494
|
+
</Button>
|
|
495
|
+
</span>
|
|
496
|
+
</Tooltip>
|
|
497
|
+
</Stage>
|
|
498
|
+
</div>
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
export default App;
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
## SDS vs MUI
|
|
506
|
+
|
|
507
|
+
SDS Tooltip wraps MUI's Tooltip, composes the content out of its own props rather than taking it whole, and pins the parts of MUI's API the design has an opinion about. What it adds and changes:
|
|
508
|
+
|
|
509
|
+
- **One surface, which follows the theme:** a tooltip is light in the light theme and dark in the dark theme, and there is nothing to set. MUI leaves the surface to the theme as well, but SDS pins the shape of it: the radius, the shadow, the hairline outline and the type scale.
|
|
510
|
+
|
|
511
|
+
- `title` and `subtitle`: SDS builds the content itself, wrapping `title` and then `subtitle` in paragraphs of its own, so `title` is not the free-form node MUI's is. `subtitle` is the smaller, dimmer second line. A tooltip with no `title`, `subtitle` or `componentSlot` renders nothing and leaves its child alone.
|
|
512
|
+
|
|
513
|
+
- `componentSlot`: anything React can render, placed under the text with 12px above it when there is text to sit under. This is where non-text content goes, since a table or a div handed to `title` lands inside a paragraph and produces invalid HTML.
|
|
514
|
+
|
|
515
|
+
- `width`: `"default"` caps the tooltip at 250px, `"wide"` at 550px and switches the text to the left. Compare MUI, which has no `width` prop of its own.
|
|
516
|
+
|
|
517
|
+
- `textAlign`: overrides the alignment each width picks for itself: centred at the default width, left when wide.
|
|
518
|
+
|
|
519
|
+
- `arrowOffset`: not an offset but a position: the number becomes the arrow's `left` in pixels, measured from the tooltip's left edge, replacing the one the positioning engine worked out. Values past the tooltip's own width put the arrow out of sight.
|
|
520
|
+
|
|
521
|
+
- **arrow is on:** MUI defaults to no arrow; SDS turns it on for every tooltip. It can still be turned back off with `arrow={false}`, though the design always draws one.
|
|
522
|
+
|
|
523
|
+
- **hasInvertedStyle, inverted and sdsStyle are deprecated and now do nothing.** All three chose between a light and a dark tooltip, back when the choice was the author's to make. Passing any of them still type-checks, so nothing breaks, but the tooltip takes its colours from the theme regardless and a deprecation warning is logged once. They will be removed in a future release: delete them.
|
|
524
|
+
|
|
525
|
+
- **The popper is SDS's:** it carries the arrow's shape and its position for all twelve placements, and it arrives through `PopperComponent`, which SDS keeps as a prop name of its own after MUI moved to `slots.popper`. SDS fills in `slots` last, so a `slots` object of your own is dropped; pass `PopperComponent` instead.
|
|
526
|
+
|
|
527
|
+
## MUI Documentation
|
|
528
|
+
|
|
529
|
+
Documentation for the underlying MUI component can be found [here](https://mui.com/material-ui/react-tooltip/).
|
|
530
|
+
|
|
531
|
+
## Behavior and accessibility
|
|
532
|
+
|
|
533
|
+
- A tooltip opens on hover and on keyboard focus, and closes on Escape. SDS gives the trigger `tabIndex={0}` so that focus can reach it even when the element wrapped is not naturally focusable.
|
|
534
|
+
|
|
535
|
+
- While a tooltip is open, MUI points the trigger's `aria-labelledby` at it, so the tooltip text becomes the trigger's name for that moment rather than an extra description. The trigger still needs a name of its own for when the tooltip is closed: an icon-only button needs an `aria-label`, and text in a tooltip is never a substitute for it.
|
|
536
|
+
|
|
537
|
+
- Tooltips are interactive: the pointer can travel from the trigger, across the 14px gap, and onto the tooltip without it closing, which is what makes a link inside one reachable. MUI's `disableInteractive` turns that off, and with it any chance of clicking what the tooltip holds.
|
|
538
|
+
|
|
539
|
+
- A disabled element fires no pointer events, so a tooltip attached to one never opens. Wrap the child in a `<span>` to give the tooltip something that reports hovers. The span inherits the `tabIndex={0}` SDS adds, so it becomes a tab stop and the tooltip still opens on focus. That is the only route left, given that the disabled control cannot take focus itself.
|
|
540
|
+
|
|
541
|
+
- The tooltip renders in a portal at the end of the document, so it is not clipped by whatever it sits inside and does not need room reserved for it.
|
|
542
|
+
|
|
543
|
+
- A tooltip is not a place for anything a person has to have. It cannot be reached on a touch screen without a tap that also does something else, and it is gone the moment the pointer moves.
|
|
544
|
+
|
|
545
|
+
## Props
|
|
546
|
+
|
|
547
|
+
Any custom SDS props and MUI props required for implementation are found on the table below. See the MUI documentation for additional optional props.
|
|
548
|
+
|
|
549
|
+
| Name | Type | Default | Description |
|
|
550
|
+
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
551
|
+
| `title` | `ReactNode` | - | The tooltip's first line, wrapped in a paragraph by SDS. A tooltip with no `title`, `subtitle` or `componentSlot` does not render. |
|
|
552
|
+
| `subtitle` | `ReactNode` | - | A smaller, dimmer second line under the title, for a detail such as how to use what the tooltip points at. |
|
|
553
|
+
| `componentSlot` | `ReactNode` | - | Content below the text, with 12px above it when there is text. The place for anything that is not a string, including TooltipTable. |
|
|
554
|
+
| `width` | `"default" \| "wide"` | `"default"` | Caps the tooltip at 250px or 550px. Wide also left-aligns the text. |
|
|
555
|
+
| `textAlign` | `"left" \| "center" \| "right"` | `"center"`, or `"left"` when wide | Overrides the alignment the width would pick. |
|
|
556
|
+
| `arrow` | `boolean` | `true` | MUI's prop, which SDS turns on for every tooltip. It can be set back to `false`, though the design always draws an arrow. |
|
|
557
|
+
| `arrowOffset` | `number` | - | Sets the arrow's left position, in pixels from the tooltip's left edge, instead of letting it be calculated. Values past the tooltip's width move the arrow out of sight. |
|
|
558
|
+
| `PopperComponent` | `ElementType` | SDS `StyledPopper` | The popper the tooltip renders into, carrying the arrow's shape and placement. SDS maps it to MUI's `slots.popper`, and a `slots` object passed directly is ignored. |
|
|
559
|
+
| `placement` | `"bottom-start"` \| `"bottom"` \| `"bottom-end"` \| `"left-start"` \| `"left"` \| `"left-end"` \| `"right-start"` \| `"right"` \| `"right-end"` \| `"top-start"` \| `"top"` \| `"top-end"` | `"bottom"` | MUI's prop. All twelve values work, and a tooltip that would fall off the screen flips to the opposite side on its own. |
|
|
560
|
+
| `disableInteractive` | `boolean` | `false` | MUI's prop. Stops the tooltip from staying open while the pointer moves onto it, which also puts anything clickable inside it out of reach. |
|
|
561
|
+
| `followCursor` | `boolean` | `false` | MUI's prop, which keeps the tooltip under the pointer as it moves. SDS also reads it, and gives a tooltip that follows the cursor the roomier padding it uses for tables. |
|