@commercetools/nimbus-mcp 3.2.0 → 3.3.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 +9 -1
- package/data/docs/route-manifest.json +806 -41
- package/data/docs/routes/components-buttons-button.json +25 -2
- package/data/docs/routes/components-chat-chat-message-list.json +448 -0
- package/data/docs/routes/components-chat-chat-message.json +580 -0
- package/data/docs/routes/components-content-markdown.json +548 -0
- package/data/docs/routes/components-feedback-activity-indicator.json +396 -0
- package/data/docs/routes/components-feedback-tooltip.json +23 -1
- package/data/docs/routes/components-inputs-drop-zone.json +562 -0
- package/data/docs/routes/components-inputs-file-trigger.json +421 -0
- package/data/docs/routes/components-layout-splitter.json +3 -3
- package/data/docs/routes/components-media-avatar.json +16 -5
- package/data/docs/routes/components-navigation-menu.json +1 -1
- package/data/docs/routes/components-navigation-tabnav.json +80 -14
- package/data/docs/routes/components-navigation-tabs.json +44 -11
- package/data/docs/routes/components-navigation-tree.json +445 -0
- package/data/docs/routes/home-getting-started-mcp-server-overview.json +2 -2
- package/data/docs/routes/home-getting-started-mcp-server-setup.json +2 -2
- package/data/docs/routes/patterns-buttons-floating-action-button.json +392 -0
- package/data/docs/routes/patterns-buttons.json +58 -0
- package/data/docs/search-index.json +1 -1
- package/data/docs/types/AccordionContent.json +24 -24
- package/data/docs/types/AccordionHeader.json +88 -88
- package/data/docs/types/AccordionItem.json +20 -20
- package/data/docs/types/AccordionRoot.json +14 -14
- package/data/docs/types/ActivityIndicator.json +202 -0
- package/data/docs/types/AlertDescription.json +8 -8
- package/data/docs/types/AlertDismissButton.json +98 -81
- package/data/docs/types/AlertTitle.json +8 -8
- package/data/docs/types/Avatar.json +19 -2
- package/data/docs/types/Badge.json +2 -2
- package/data/docs/types/Body.json +6 -6
- package/data/docs/types/Box.json +6 -6
- package/data/docs/types/Button.json +106 -89
- package/data/docs/types/Calendar.json +56 -56
- package/data/docs/types/Caption.json +6 -6
- package/data/docs/types/CardRoot.json +2 -2
- package/data/docs/types/Cell.json +6 -6
- package/data/docs/types/ChatMessage.json +12 -0
- package/data/docs/types/ChatMessageActions.json +42 -0
- package/data/docs/types/ChatMessageAvatar.json +140 -0
- package/data/docs/types/ChatMessageBody.json +42 -0
- package/data/docs/types/ChatMessageList.json +12 -0
- package/data/docs/types/ChatMessageListItem.json +42 -0
- package/data/docs/types/ChatMessageListRoot.json +78 -0
- package/data/docs/types/ChatMessageMeta.json +42 -0
- package/data/docs/types/ChatMessageRoot.json +119 -0
- package/data/docs/types/ChatMessageTyping.json +42 -0
- package/data/docs/types/Checkbox.json +86 -86
- package/data/docs/types/Code.json +11 -11
- package/data/docs/types/Column.json +6 -6
- package/data/docs/types/ColumnGroup.json +6 -6
- package/data/docs/types/ColumnHeader.json +6 -6
- package/data/docs/types/ComboBoxListBox.json +70 -70
- package/data/docs/types/ComboBoxOption.json +64 -64
- package/data/docs/types/ComboBoxPopover.json +91 -70
- package/data/docs/types/ComboBoxRoot.json +8 -8
- package/data/docs/types/ComboBoxSection.json +24 -24
- package/data/docs/types/ComboBoxTrigger.json +6 -6
- package/data/docs/types/DataTable.json +2 -2
- package/data/docs/types/DataTableBody.json +20 -20
- package/data/docs/types/DataTableHeader.json +25 -25
- package/data/docs/types/DataTableRoot.json +2 -2
- package/data/docs/types/DataTableTable.json +6 -6
- package/data/docs/types/DateInput.json +73 -73
- package/data/docs/types/DatePicker.json +58 -58
- package/data/docs/types/DateRangePicker.json +86 -86
- package/data/docs/types/DateRangePickerField.json +86 -86
- package/data/docs/types/DefaultPageRoot.json +2 -2
- package/data/docs/types/DialogCloseTrigger.json +96 -79
- package/data/docs/types/DraggableListField.json +88 -60
- package/data/docs/types/DraggableListItem.json +52 -52
- package/data/docs/types/DraggableListRoot.json +88 -60
- package/data/docs/types/DrawerCloseTrigger.json +96 -79
- package/data/docs/types/DropZone.json +161 -0
- package/data/docs/types/FieldErrors.json +2 -2
- package/data/docs/types/FileTrigger.json +130 -0
- package/data/docs/types/Flex.json +22 -22
- package/data/docs/types/FloatingActionButton.json +1036 -0
- package/data/docs/types/Footer.json +6 -6
- package/data/docs/types/FormActionBar.json +1 -1
- package/data/docs/types/FormFieldRoot.json +2 -2
- package/data/docs/types/Grid.json +24 -24
- package/data/docs/types/Group.json +12 -12
- package/data/docs/types/Header.json +6 -6
- package/data/docs/types/Heading.json +8 -8
- package/data/docs/types/Icon.json +4 -4
- package/data/docs/types/IconButton.json +106 -89
- package/data/docs/types/IconToggleButton.json +71 -71
- package/data/docs/types/Image.json +43 -43
- package/data/docs/types/Indicator.json +6 -6
- package/data/docs/types/InlineSvg.json +2 -2
- package/data/docs/types/Item.json +6 -6
- package/data/docs/types/Kbd.json +8 -8
- package/data/docs/types/Link.json +14 -14
- package/data/docs/types/ListIndicator.json +6 -6
- package/data/docs/types/ListItem.json +6 -6
- package/data/docs/types/ListRoot.json +11 -11
- package/data/docs/types/LoadingSpinner.json +2 -2
- package/data/docs/types/MakeElementFocusable.json +18 -18
- package/data/docs/types/Markdown.json +183 -0
- package/data/docs/types/MenuItem.json +62 -62
- package/data/docs/types/MenuRoot.json +55 -55
- package/data/docs/types/MenuSection.json +30 -30
- package/data/docs/types/MenuSubmenuTrigger.json +4 -4
- package/data/docs/types/MenuTrigger.json +88 -88
- package/data/docs/types/MultilineTextInput.json +104 -104
- package/data/docs/types/MultilineTextInputField.json +102 -102
- package/data/docs/types/NumberInput.json +86 -86
- package/data/docs/types/NumberInputField.json +82 -82
- package/data/docs/types/PageContentColumn.json +6 -6
- package/data/docs/types/PageContentRoot.json +6 -6
- package/data/docs/types/PasswordInput.json +100 -100
- package/data/docs/types/PasswordInputField.json +100 -100
- package/data/docs/types/ProgressBar.json +13 -13
- package/data/docs/types/RadioInputOption.json +56 -56
- package/data/docs/types/RadioInputRoot.json +48 -48
- package/data/docs/types/RangeCalendar.json +58 -58
- package/data/docs/types/Region.json +6 -6
- package/data/docs/types/RegionTarget.json +6 -6
- package/data/docs/types/RichTextInput.json +2 -2
- package/data/docs/types/Root.json +11 -11
- package/data/docs/types/Row.json +6 -6
- package/data/docs/types/ScrollArea.json +2 -2
- package/data/docs/types/SearchInput.json +106 -106
- package/data/docs/types/SearchInputField.json +102 -102
- package/data/docs/types/SelectOption.json +54 -54
- package/data/docs/types/SelectOptionGroup.json +18 -18
- package/data/docs/types/SelectOptions.json +64 -64
- package/data/docs/types/SelectRoot.json +92 -92
- package/data/docs/types/Separator.json +4 -4
- package/data/docs/types/SimpleGrid.json +28 -28
- package/data/docs/types/SlidingIndicatorGeometry.json +9 -0
- package/data/docs/types/SlidingIndicatorRects.json +9 -0
- package/data/docs/types/SplitButton.json +16 -20
- package/data/docs/types/Stack.json +2 -2
- package/data/docs/types/StepsRoot.json +2 -2
- package/data/docs/types/Switch.json +34 -34
- package/data/docs/types/TabNavItem.json +5 -5
- package/data/docs/types/TabNavRoot.json +5 -5
- package/data/docs/types/TableBody.json +6 -6
- package/data/docs/types/TableCaption.json +6 -6
- package/data/docs/types/TableCell.json +6 -6
- package/data/docs/types/TableColumn.json +6 -6
- package/data/docs/types/TableColumnGroup.json +6 -6
- package/data/docs/types/TableColumnHeader.json +6 -6
- package/data/docs/types/TableFooter.json +6 -6
- package/data/docs/types/TableHeader.json +6 -6
- package/data/docs/types/TableRoot.json +19 -19
- package/data/docs/types/TableRow.json +6 -6
- package/data/docs/types/TableScrollArea.json +6 -6
- package/data/docs/types/TabsList.json +2 -2
- package/data/docs/types/TabsPanel.json +2 -2
- package/data/docs/types/TabsPanels.json +2 -2
- package/data/docs/types/TabsRoot.json +3 -3
- package/data/docs/types/TabsTab.json +2 -2
- package/data/docs/types/TagGroupRoot.json +24 -24
- package/data/docs/types/TagGroupTag.json +56 -56
- package/data/docs/types/TagGroupTagList.json +14 -14
- package/data/docs/types/Text.json +8 -8
- package/data/docs/types/TextInput.json +102 -102
- package/data/docs/types/TextInputField.json +100 -100
- package/data/docs/types/TimeInput.json +68 -68
- package/data/docs/types/ToggleButton.json +73 -73
- package/data/docs/types/ToggleButtonGroupButton.json +30 -30
- package/data/docs/types/ToggleButtonGroupRoot.json +20 -20
- package/data/docs/types/Toolbar.json +9 -9
- package/data/docs/types/TooltipContent.json +28 -28
- package/data/docs/types/TooltipRoot.json +16 -16
- package/data/docs/types/Tree.json +12 -0
- package/data/docs/types/TreeIndicator.json +828 -0
- package/data/docs/types/TreeItem.json +559 -0
- package/data/docs/types/TreeItemContent.json +27 -0
- package/data/docs/types/TreeRoot.json +636 -0
- package/data/docs/types/TreeSubTree.json +40 -0
- package/data/docs/types/VisuallyHidden.json +6 -6
- package/data/docs/types/createItemsFromCsvDrop.json +4 -4
- package/data/docs/types/createItemsFromDirectoryDrop.json +4 -4
- package/data/docs/types/createItemsFromFileDrop.json +4 -4
- package/data/docs/types/createItemsFromImageDrop.json +4 -4
- package/data/docs/types/createItemsFromJsonDrop.json +4 -4
- package/data/docs/types/manifest.json +25 -0
- package/data/docs/types/useRegion.json +2 -2
- package/data/docs/types/useSlidingIndicator.json +145 -0
- package/data/docs/types/useTree.json +267 -0
- package/data/tokens.json +21 -0
- package/data/uikit-tokens.json +894 -0
- package/dist/index.js +3298 -109
- package/package.json +8 -6
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
{
|
|
2
|
+
"meta": {
|
|
3
|
+
"id": "Components-ActivityIndicator",
|
|
4
|
+
"title": "Activity indicator",
|
|
5
|
+
"exportName": "ActivityIndicator",
|
|
6
|
+
"description": "An animated three-dot indicator that signals ongoing agent or system activity, such as \"thinking\", \"processing\", or \"typing\" in chat and AI surfaces.",
|
|
7
|
+
"lifecycleState": "Experimental",
|
|
8
|
+
"order": 999,
|
|
9
|
+
"repoPath": "packages/nimbus/src/components/activity-indicator/activity-indicator.mdx",
|
|
10
|
+
"menu": [
|
|
11
|
+
"Components",
|
|
12
|
+
"Feedback",
|
|
13
|
+
"Activity indicator"
|
|
14
|
+
],
|
|
15
|
+
"route": "components/feedback/activity-indicator",
|
|
16
|
+
"tags": [
|
|
17
|
+
"component",
|
|
18
|
+
"activity",
|
|
19
|
+
"typing",
|
|
20
|
+
"dots",
|
|
21
|
+
"chat"
|
|
22
|
+
],
|
|
23
|
+
"toc": [
|
|
24
|
+
{
|
|
25
|
+
"value": "Overview",
|
|
26
|
+
"href": "#overview",
|
|
27
|
+
"depth": 2,
|
|
28
|
+
"numbering": [
|
|
29
|
+
1,
|
|
30
|
+
1
|
|
31
|
+
],
|
|
32
|
+
"parent": "root"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"value": "Resources",
|
|
36
|
+
"href": "#resources",
|
|
37
|
+
"depth": 3,
|
|
38
|
+
"numbering": [
|
|
39
|
+
1,
|
|
40
|
+
1,
|
|
41
|
+
1
|
|
42
|
+
],
|
|
43
|
+
"parent": "root"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"value": "Variables",
|
|
47
|
+
"href": "#variables",
|
|
48
|
+
"depth": 2,
|
|
49
|
+
"numbering": [
|
|
50
|
+
1,
|
|
51
|
+
2
|
|
52
|
+
],
|
|
53
|
+
"parent": "root"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"value": "Visuals",
|
|
57
|
+
"href": "#visuals",
|
|
58
|
+
"depth": 3,
|
|
59
|
+
"numbering": [
|
|
60
|
+
1,
|
|
61
|
+
2,
|
|
62
|
+
1
|
|
63
|
+
],
|
|
64
|
+
"parent": "root"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"value": "Inline with text",
|
|
68
|
+
"href": "#inline-with-text",
|
|
69
|
+
"depth": 4,
|
|
70
|
+
"numbering": [
|
|
71
|
+
1,
|
|
72
|
+
2,
|
|
73
|
+
1,
|
|
74
|
+
1
|
|
75
|
+
],
|
|
76
|
+
"parent": "root"
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"value": "On a solid colored surface",
|
|
80
|
+
"href": "#on-a-solid-colored-surface",
|
|
81
|
+
"depth": 4,
|
|
82
|
+
"numbering": [
|
|
83
|
+
1,
|
|
84
|
+
2,
|
|
85
|
+
1,
|
|
86
|
+
2
|
|
87
|
+
],
|
|
88
|
+
"parent": "root"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"value": "Size",
|
|
92
|
+
"href": "#size",
|
|
93
|
+
"depth": 3,
|
|
94
|
+
"numbering": [
|
|
95
|
+
1,
|
|
96
|
+
2,
|
|
97
|
+
2
|
|
98
|
+
],
|
|
99
|
+
"parent": "root"
|
|
100
|
+
}
|
|
101
|
+
],
|
|
102
|
+
"figmaLink": "https://www.figma.com/design/AvtPX6g7OGGCRvNlatGOIY/NIMBUS-design-system?node-id=10603-16574&m=dev",
|
|
103
|
+
"layout": "app-frame",
|
|
104
|
+
"tabs": [
|
|
105
|
+
{
|
|
106
|
+
"key": "overview",
|
|
107
|
+
"title": "Overview",
|
|
108
|
+
"order": 0
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"key": "guidelines",
|
|
112
|
+
"title": "Guidelines",
|
|
113
|
+
"order": 2
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"key": "dev",
|
|
117
|
+
"title": "Implementation",
|
|
118
|
+
"order": 3
|
|
119
|
+
}
|
|
120
|
+
]
|
|
121
|
+
},
|
|
122
|
+
"mdx": "\n## Overview\n\nThe activity indicator communicates that an agent or system is actively working\n— for example composing a reply, thinking, or processing a request. It is most\ncommonly placed inline next to a short status message (\"Thinking…\", \"Processing\nrequest\") in chat and AI surfaces.\n\nUnlike the [Loading spinner](/components/feedback/loadingspinner), which\nrepresents indeterminate progress and signals \"please wait\", the activity\nindicator is a lightweight, presentational hint. Reach for the loading spinner\nwhen something is loading and for the activity indicator when an agent is\nactive.\n\n### Resources\n\nDeep dive into implementation details and access the Nimbus design library.\n\n[Figma library](https://www.figma.com/design/AvtPX6g7OGGCRvNlatGOIY/NIMBUS-design-system?node-id=10603-16574&m=dev)\n\n## Variables\n\nGet familiar with the features.\n\n### Visuals\n\n#### Inline with text\n\nBy default the indicator scales with the surrounding `font-size`, so it sits\nnaturally beside a status label.\n\n```jsx live\nconst App = () => (\n <Text fontSize=\"md\">\n Thinking <ActivityIndicator />\n </Text>\n);\n```\n\n#### On a solid colored surface\n\nWhen the indicator sits on a solid colored fill, use `variant=\"contrast\"`. The\ndots take the palette's `contrast` step (black or white, chosen automatically per\npalette) so they stay legible on the surface.\n\n```jsx live\nconst App = () => (\n <Stack\n colorPalette=\"primary\"\n backgroundColor=\"colorPalette.9\"\n padding=\"400\"\n >\n <Text color=\"colorPalette.contrast\">\n Thinking{\" \"}\n <ActivityIndicator colorPalette=\"primary\" variant=\"contrast\" />\n </Text>\n </Stack>\n);\n```\n\n### Size\n\nThe default `inherit` size tracks the surrounding text. Fixed sizes (`2xs`–`lg`)\nreserve a square icon-box footprint so the indicator can sit in input icon slots\nalongside other icons.\n\n```jsx live\nconst App = () => (\n <Stack direction=\"row\" gap=\"400\" alignItems=\"center\">\n <ActivityIndicator size=\"lg\" />\n <ActivityIndicator size=\"md\" />\n <ActivityIndicator size=\"sm\" />\n <ActivityIndicator size=\"xs\" />\n <ActivityIndicator size=\"2xs\" />\n </Stack>\n);\n```\n",
|
|
123
|
+
"views": {
|
|
124
|
+
"overview": {
|
|
125
|
+
"mdx": "\n## Overview\n\nThe activity indicator communicates that an agent or system is actively working\n— for example composing a reply, thinking, or processing a request. It is most\ncommonly placed inline next to a short status message (\"Thinking…\", \"Processing\nrequest\") in chat and AI surfaces.\n\nUnlike the [Loading spinner](/components/feedback/loadingspinner), which\nrepresents indeterminate progress and signals \"please wait\", the activity\nindicator is a lightweight, presentational hint. Reach for the loading spinner\nwhen something is loading and for the activity indicator when an agent is\nactive.\n\n### Resources\n\nDeep dive into implementation details and access the Nimbus design library.\n\n[Figma library](https://www.figma.com/design/AvtPX6g7OGGCRvNlatGOIY/NIMBUS-design-system?node-id=10603-16574&m=dev)\n\n## Variables\n\nGet familiar with the features.\n\n### Visuals\n\n#### Inline with text\n\nBy default the indicator scales with the surrounding `font-size`, so it sits\nnaturally beside a status label.\n\n```jsx live\nconst App = () => (\n <Text fontSize=\"md\">\n Thinking <ActivityIndicator />\n </Text>\n);\n```\n\n#### On a solid colored surface\n\nWhen the indicator sits on a solid colored fill, use `variant=\"contrast\"`. The\ndots take the palette's `contrast` step (black or white, chosen automatically per\npalette) so they stay legible on the surface.\n\n```jsx live\nconst App = () => (\n <Stack\n colorPalette=\"primary\"\n backgroundColor=\"colorPalette.9\"\n padding=\"400\"\n >\n <Text color=\"colorPalette.contrast\">\n Thinking{\" \"}\n <ActivityIndicator colorPalette=\"primary\" variant=\"contrast\" />\n </Text>\n </Stack>\n);\n```\n\n### Size\n\nThe default `inherit` size tracks the surrounding text. Fixed sizes (`2xs`–`lg`)\nreserve a square icon-box footprint so the indicator can sit in input icon slots\nalongside other icons.\n\n```jsx live\nconst App = () => (\n <Stack direction=\"row\" gap=\"400\" alignItems=\"center\">\n <ActivityIndicator size=\"lg\" />\n <ActivityIndicator size=\"md\" />\n <ActivityIndicator size=\"sm\" />\n <ActivityIndicator size=\"xs\" />\n <ActivityIndicator size=\"2xs\" />\n </Stack>\n);\n```\n",
|
|
126
|
+
"toc": [
|
|
127
|
+
{
|
|
128
|
+
"value": "Overview",
|
|
129
|
+
"href": "#overview",
|
|
130
|
+
"depth": 2,
|
|
131
|
+
"numbering": [
|
|
132
|
+
1,
|
|
133
|
+
1
|
|
134
|
+
],
|
|
135
|
+
"parent": "root"
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"value": "Resources",
|
|
139
|
+
"href": "#resources",
|
|
140
|
+
"depth": 3,
|
|
141
|
+
"numbering": [
|
|
142
|
+
1,
|
|
143
|
+
1,
|
|
144
|
+
1
|
|
145
|
+
],
|
|
146
|
+
"parent": "root"
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"value": "Variables",
|
|
150
|
+
"href": "#variables",
|
|
151
|
+
"depth": 2,
|
|
152
|
+
"numbering": [
|
|
153
|
+
1,
|
|
154
|
+
2
|
|
155
|
+
],
|
|
156
|
+
"parent": "root"
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"value": "Visuals",
|
|
160
|
+
"href": "#visuals",
|
|
161
|
+
"depth": 3,
|
|
162
|
+
"numbering": [
|
|
163
|
+
1,
|
|
164
|
+
2,
|
|
165
|
+
1
|
|
166
|
+
],
|
|
167
|
+
"parent": "root"
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"value": "Inline with text",
|
|
171
|
+
"href": "#inline-with-text",
|
|
172
|
+
"depth": 4,
|
|
173
|
+
"numbering": [
|
|
174
|
+
1,
|
|
175
|
+
2,
|
|
176
|
+
1,
|
|
177
|
+
1
|
|
178
|
+
],
|
|
179
|
+
"parent": "root"
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"value": "On a solid colored surface",
|
|
183
|
+
"href": "#on-a-solid-colored-surface",
|
|
184
|
+
"depth": 4,
|
|
185
|
+
"numbering": [
|
|
186
|
+
1,
|
|
187
|
+
2,
|
|
188
|
+
1,
|
|
189
|
+
2
|
|
190
|
+
],
|
|
191
|
+
"parent": "root"
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
"value": "Size",
|
|
195
|
+
"href": "#size",
|
|
196
|
+
"depth": 3,
|
|
197
|
+
"numbering": [
|
|
198
|
+
1,
|
|
199
|
+
2,
|
|
200
|
+
2
|
|
201
|
+
],
|
|
202
|
+
"parent": "root"
|
|
203
|
+
}
|
|
204
|
+
]
|
|
205
|
+
},
|
|
206
|
+
"dev": {
|
|
207
|
+
"mdx": "\n## Getting started\n\n### Import\n\n```tsx\nimport {\n ActivityIndicator,\n type ActivityIndicatorProps,\n} from \"@commercetools/nimbus\";\n```\n\n### Basic usage\n\nThe ActivityIndicator signals that an agent or system is actively working. By\ndefault it is decorative and scales with the surrounding `font-size`, so it can\nsit inline next to a visible status label.\n\n```jsx live-dev\nconst App = () => (\n <Text>\n Thinking <ActivityIndicator />\n </Text>\n);\n```\n\n## Usage examples\n\n### Size options\n\nThe `size` prop defaults to `inherit`, which ties the indicator's box to the\nsurrounding `font-size` (`1em`) so it scales with the text. The fixed sizes\n(`2xs`, `xs`, `sm`, `md`, `lg`) reserve a square icon-box footprint — the same\nscale points `LoadingSpinner` uses — so the indicator drops into input start/end\nicon slots interchangeably with a spinner.\n\n```jsx live-dev\nconst App = () => (\n <Stack direction=\"row\" gap=\"400\" alignItems=\"center\">\n <ActivityIndicator size=\"2xs\" />\n <ActivityIndicator size=\"xs\" />\n <ActivityIndicator size=\"sm\" />\n <ActivityIndicator size=\"md\" />\n <ActivityIndicator size=\"lg\" />\n </Stack>\n);\n```\n\n### Color palette\n\nThe `colorPalette` prop accepts any Nimbus color palette (default `primary`). The\ndots are filled from the palette's `11` shade.\n\n```jsx live-dev\nconst App = () => (\n <Stack direction=\"row\" gap=\"400\">\n <ActivityIndicator colorPalette=\"primary\" />\n <ActivityIndicator colorPalette=\"positive\" />\n <ActivityIndicator colorPalette=\"info\" />\n </Stack>\n);\n```\n\n### On a solid colored surface\n\nWhen the dots sit on a solid colored fill (e.g. an agent bubble), use\n`variant=\"contrast\"`. This fills the dots from the palette's `contrast` step,\nwhich resolves to black or white automatically per palette so the dots stay\nlegible on the surface.\n\n```jsx live-dev\nconst App = () => (\n <Stack direction=\"row\" gap=\"400\">\n <Box colorPalette=\"primary\" bg=\"colorPalette.9\" padding=\"400\" borderRadius=\"200\">\n <ActivityIndicator colorPalette=\"primary\" variant=\"contrast\" />\n </Box>\n <Box colorPalette=\"ctyellow\" bg=\"colorPalette.9\" padding=\"400\" borderRadius=\"200\">\n <ActivityIndicator colorPalette=\"ctyellow\" variant=\"contrast\" />\n </Box>\n </Stack>\n);\n```\n\n## Accessibility\n\nThe ActivityIndicator is **purely decorative** — it is always marked\n`aria-hidden`, so assistive technology never announces the animation itself.\nAlways pair it with visible text that conveys the state (for example\n\"Thinking…\"), so screen-reader users get the same information sighted users do.\n\n```tsx\n// Visible text conveys the state; the indicator is decorative.\n<Text>\n Thinking <ActivityIndicator />\n</Text>\n```\n\n**Announcing the activity to assistive technology is the consumer's\nresponsibility**, not the component's. The indicator does not own a live region,\nbecause it is typically rendered only while activity is in progress — and a live\nregion mounted together with its content is not announced reliably across screen\nreaders (the region must already exist in the DOM before its content changes).\nIf you need to announce the activity without adjacent visible text, drive a\n**persistent** live region owned by the surrounding chat/turn container (for\nexample via React Aria's [`@react-aria/live-announcer`](https://react-spectrum.adobe.com/react-aria/announce.html)),\nand keep the indicator decorative.\n\nThis component is **not** a progress indicator and does not use\n`role=\"progressbar\"`. For indeterminate progress (\"please wait\"), use\n[LoadingSpinner](/components/feedback/loadingspinner) instead.\n\n## Reduced motion\n\nWhen the user has `prefers-reduced-motion: reduce` enabled, the vertical bounce\nis replaced by a gentle opacity pulse, so the \"working\" state remains\nperceivable without large motion.\n\n## API reference\n\n<PropsTable id=\"ActivityIndicator\" />\n\n## Testing your implementation\n\nThese examples demonstrate how to test your implementation when using\nActivityIndicator within your application. As the component's internal\nfunctionality is already tested by Nimbus, these patterns help you verify your\nintegration and application-specific logic.\n\n### Basic Rendering Tests\n\nVerify the indicator renders and exposes the expected accessibility semantics\n\n```tsx\nimport { describe, it, expect } from \"vitest\";\nimport { render, screen } from \"@testing-library/react\";\nimport { ActivityIndicator, NimbusProvider } from \"@commercetools/nimbus\";\n\ndescribe(\"ActivityIndicator - Basic rendering\", () => {\n it(\"is decorative by default (aria-hidden, no role)\", () => {\n render(\n <NimbusProvider>\n <ActivityIndicator data-testid=\"indicator\" />\n </NimbusProvider>\n );\n\n const indicator = screen.getByTestId(\"indicator\");\n expect(indicator).toBeInTheDocument();\n expect(indicator).toHaveAttribute(\"aria-hidden\", \"true\");\n expect(indicator).not.toHaveAttribute(\"role\");\n });\n\n it(\"renders three dot elements\", () => {\n render(\n <NimbusProvider>\n <ActivityIndicator data-testid=\"indicator\" />\n </NimbusProvider>\n );\n\n const dots = screen.getByTestId(\"indicator\").querySelectorAll(\"[data-dot]\");\n expect(dots).toHaveLength(3);\n });\n});\n```\n\n\n## Resources\n\n- [Storybook](https://nimbus-storybook.vercel.app/?path=/docs/components-activityindicator--docs)\n",
|
|
208
|
+
"toc": [
|
|
209
|
+
{
|
|
210
|
+
"value": "Getting started",
|
|
211
|
+
"href": "#getting-started",
|
|
212
|
+
"depth": 2,
|
|
213
|
+
"numbering": [
|
|
214
|
+
1,
|
|
215
|
+
1
|
|
216
|
+
],
|
|
217
|
+
"parent": "root"
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
"value": "Import",
|
|
221
|
+
"href": "#import",
|
|
222
|
+
"depth": 3,
|
|
223
|
+
"numbering": [
|
|
224
|
+
1,
|
|
225
|
+
1,
|
|
226
|
+
1
|
|
227
|
+
],
|
|
228
|
+
"parent": "root"
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
"value": "Basic usage",
|
|
232
|
+
"href": "#basic-usage",
|
|
233
|
+
"depth": 3,
|
|
234
|
+
"numbering": [
|
|
235
|
+
1,
|
|
236
|
+
1,
|
|
237
|
+
2
|
|
238
|
+
],
|
|
239
|
+
"parent": "root"
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
"value": "Usage examples",
|
|
243
|
+
"href": "#usage-examples",
|
|
244
|
+
"depth": 2,
|
|
245
|
+
"numbering": [
|
|
246
|
+
1,
|
|
247
|
+
2
|
|
248
|
+
],
|
|
249
|
+
"parent": "root"
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
"value": "Size options",
|
|
253
|
+
"href": "#size-options",
|
|
254
|
+
"depth": 3,
|
|
255
|
+
"numbering": [
|
|
256
|
+
1,
|
|
257
|
+
2,
|
|
258
|
+
1
|
|
259
|
+
],
|
|
260
|
+
"parent": "root"
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
"value": "Color palette",
|
|
264
|
+
"href": "#color-palette",
|
|
265
|
+
"depth": 3,
|
|
266
|
+
"numbering": [
|
|
267
|
+
1,
|
|
268
|
+
2,
|
|
269
|
+
2
|
|
270
|
+
],
|
|
271
|
+
"parent": "root"
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
"value": "On a solid colored surface",
|
|
275
|
+
"href": "#on-a-solid-colored-surface",
|
|
276
|
+
"depth": 3,
|
|
277
|
+
"numbering": [
|
|
278
|
+
1,
|
|
279
|
+
2,
|
|
280
|
+
3
|
|
281
|
+
],
|
|
282
|
+
"parent": "root"
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
"value": "Accessibility",
|
|
286
|
+
"href": "#accessibility",
|
|
287
|
+
"depth": 2,
|
|
288
|
+
"numbering": [
|
|
289
|
+
1,
|
|
290
|
+
3
|
|
291
|
+
],
|
|
292
|
+
"parent": "root"
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
"value": "Reduced motion",
|
|
296
|
+
"href": "#reduced-motion",
|
|
297
|
+
"depth": 2,
|
|
298
|
+
"numbering": [
|
|
299
|
+
1,
|
|
300
|
+
4
|
|
301
|
+
],
|
|
302
|
+
"parent": "root"
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
"value": "API reference",
|
|
306
|
+
"href": "#api-reference",
|
|
307
|
+
"depth": 2,
|
|
308
|
+
"numbering": [
|
|
309
|
+
1,
|
|
310
|
+
5
|
|
311
|
+
],
|
|
312
|
+
"parent": "root"
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
"value": "Testing your implementation",
|
|
316
|
+
"href": "#testing-your-implementation",
|
|
317
|
+
"depth": 2,
|
|
318
|
+
"numbering": [
|
|
319
|
+
1,
|
|
320
|
+
6
|
|
321
|
+
],
|
|
322
|
+
"parent": "root"
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
"value": "Basic Rendering Tests",
|
|
326
|
+
"href": "#basic-rendering-tests",
|
|
327
|
+
"depth": 3,
|
|
328
|
+
"numbering": [
|
|
329
|
+
1,
|
|
330
|
+
6,
|
|
331
|
+
1
|
|
332
|
+
],
|
|
333
|
+
"parent": "root"
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
"value": "Resources",
|
|
337
|
+
"href": "#resources",
|
|
338
|
+
"depth": 2,
|
|
339
|
+
"numbering": [
|
|
340
|
+
1,
|
|
341
|
+
7
|
|
342
|
+
],
|
|
343
|
+
"parent": "root"
|
|
344
|
+
}
|
|
345
|
+
]
|
|
346
|
+
},
|
|
347
|
+
"guidelines": {
|
|
348
|
+
"mdx": "\n## Guidelines\n\nActivity indicator guidelines aim to communicate that an agent or system is\nactively working — composing, thinking, or processing — in a way that feels\nlightweight and conversational, without implying that the user must wait for a\nblocking operation to finish.\n\n### Best practices\n\n- **Pair with a status label:** Place the indicator inline next to a short,\n human-readable message (\"Thinking…\", \"Processing request\"). The default size\n scales with the surrounding text, so it aligns naturally.\n- **Use the right tool for the job:** Use the activity indicator for agent or\n conversational activity. For indeterminate loading where the user must wait,\n use the [Loading spinner](/components/feedback/loadingspinner) instead.\n- **Match the surface:** On neutral or page backgrounds, keep the default\n (`plain`) treatment. When the dots sit on a solid colored fill — such as an\n agent bubble — use `variant=\"contrast\"` so they take the palette's contrast\n color (black or white) and stay legible.\n- **Always pair it with visible text:** The indicator is purely decorative\n (`aria-hidden`), so screen readers rely on the adjacent text (\"Thinking…\") to\n convey the state. Never use it as the sole indication of activity.\n- **Announce from the container, not the indicator:** If you need to announce\n activity without adjacent visible text, drive a persistent live region owned\n by the surrounding chat/turn container (e.g. `@react-aria/live-announcer`).\n The indicator itself does not own a live region — it is usually mounted only\n while active, and a live region mounted with its content does not announce\n reliably.\n- **Respect motion preferences:** The indicator automatically swaps its bounce\n for a gentle pulse under `prefers-reduced-motion`. Do not reintroduce large\n motion on top of it.\n\n### Usage\n\n> [!TIP]\\\n> When to use\n\n- **Agent is composing or thinking:** A chat or AI surface is generating a\n response.\n- **Short, non-blocking activity:** A brief background step where the user can\n keep reading or interacting.\n- **Inline with a status message:** Alongside text such as \"Thinking…\" or\n \"Processing request\".\n\n> [!CAUTION]\\\n> When not to use\n\n- **Indeterminate loading the user must wait for:** Use the\n [Loading spinner](/components/feedback/loadingspinner).\n- **Determinate progress:** Use a progress bar so the user can see how far along\n the operation is.\n- **Persistent, page-level busy states:** Prefer a clearly labeled loading state\n with context.\n\n### Accessibility\n\n- The indicator is purely decorative (`aria-hidden`) and relies on adjacent\n visible text to convey meaning to assistive technology.\n- It does not own a live region. Announce activity from a persistent live region\n in the surrounding container (e.g. `@react-aria/live-announcer`) when no\n adjacent visible text is present.\n- It is not a progress indicator and never uses `role=\"progressbar\"`.\n",
|
|
349
|
+
"toc": [
|
|
350
|
+
{
|
|
351
|
+
"value": "Guidelines",
|
|
352
|
+
"href": "#guidelines",
|
|
353
|
+
"depth": 2,
|
|
354
|
+
"numbering": [
|
|
355
|
+
1,
|
|
356
|
+
1
|
|
357
|
+
],
|
|
358
|
+
"parent": "root"
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
"value": "Best practices",
|
|
362
|
+
"href": "#best-practices",
|
|
363
|
+
"depth": 3,
|
|
364
|
+
"numbering": [
|
|
365
|
+
1,
|
|
366
|
+
1,
|
|
367
|
+
1
|
|
368
|
+
],
|
|
369
|
+
"parent": "root"
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
"value": "Usage",
|
|
373
|
+
"href": "#usage",
|
|
374
|
+
"depth": 3,
|
|
375
|
+
"numbering": [
|
|
376
|
+
1,
|
|
377
|
+
1,
|
|
378
|
+
2
|
|
379
|
+
],
|
|
380
|
+
"parent": "root"
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
"value": "Accessibility",
|
|
384
|
+
"href": "#accessibility",
|
|
385
|
+
"depth": 3,
|
|
386
|
+
"numbering": [
|
|
387
|
+
1,
|
|
388
|
+
1,
|
|
389
|
+
3
|
|
390
|
+
],
|
|
391
|
+
"parent": "root"
|
|
392
|
+
}
|
|
393
|
+
]
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
@@ -165,7 +165,7 @@
|
|
|
165
165
|
]
|
|
166
166
|
},
|
|
167
167
|
"dev": {
|
|
168
|
-
"mdx": "\n## Getting started\n\n### Import\n\n```tsx\nimport { Tooltip, MakeElementFocusable } from '@commercetools/nimbus';\n```\n\n### Basic usage\n\nThe simplest implementation wraps a trigger element and tooltip content:\n\n```jsx live-dev\nconst App = () => (\n <Tooltip.Root>\n <Button>Hover me</Button>\n <Tooltip.Content>\n Helpful information appears here\n </Tooltip.Content>\n </Tooltip.Root>\n)\n```\n\n## Usage examples\n\n### Placement options\n\nThe `placement` prop controls where the tooltip appears relative to the trigger element. The default placement is `\"bottom\"`:\n\n```jsx live-dev\nconst App = () => (\n <Stack direction=\"row\" gap=\"400\" wrap=\"wrap\">\n <Tooltip.Root>\n <Button>Top</Button>\n <Tooltip.Content placement=\"top\">\n Top placement\n </Tooltip.Content>\n </Tooltip.Root>\n\n <Tooltip.Root>\n <Button>Bottom</Button>\n <Tooltip.Content placement=\"bottom\">\n Bottom placement\n </Tooltip.Content>\n </Tooltip.Root>\n\n <Tooltip.Root>\n <Button>Left</Button>\n <Tooltip.Content placement=\"left\">\n Left placement\n </Tooltip.Content>\n </Tooltip.Root>\n\n <Tooltip.Root>\n <Button>Right</Button>\n <Tooltip.Content placement=\"right\">\n Right placement\n </Tooltip.Content>\n </Tooltip.Root>\n </Stack>\n)\n```\n\n**Available placements:**\n\nThe component supports 26 placement options:\n- **Basic**: `top`, `bottom`, `left`, `right`, `start`, `end`\n- **With alignment**: `top left`, `top right`, `top start`, `top end`, `bottom left`, `bottom right`, `bottom start`, `bottom end`, `left top`, `left bottom`, `right top`, `right bottom`, `start top`, `start bottom`, `end top`, `end bottom`\n\n### Delay configuration\n\nControl when tooltips appear and disappear using `delay` and `closeDelay` props on `Tooltip.Root`:\n\n```jsx live-dev\nconst App = () => (\n <Stack direction=\"row\" gap=\"400\" wrap=\"wrap\">\n <Tooltip.Root delay={0} closeDelay={0}>\n <Button>Instant</Button>\n <Tooltip.Content>\n Shows immediately\n </Tooltip.Content>\n </Tooltip.Root>\n\n <Tooltip.Root delay={300} closeDelay={200}>\n <Button>Default timing</Button>\n <Tooltip.Content>\n 300ms delay to show, 200ms to hide\n </Tooltip.Content>\n </Tooltip.Root>\n\n <Tooltip.Root delay={1000}>\n <Button>Slow</Button>\n <Tooltip.Content>\n 1000ms delay to show\n </Tooltip.Content>\n </Tooltip.Root>\n </Stack>\n)\n```\n\n**Timing considerations:**\n- `delay`: Time in milliseconds before the tooltip appears (default: `300`)\n- `closeDelay`: Time in milliseconds before the tooltip disappears (default: `200`)\n- Shorter delays improve responsiveness but may feel too aggressive\n- Longer delays reduce accidental triggers but may feel sluggish\n\n### Complex content\n\nTooltip content can include multiple elements using layout components:\n\n```jsx live-dev\nconst App = () => (\n <Tooltip.Root>\n <Button>Feature info</Button>\n <Tooltip.Content>\n <Stack direction=\"column\" gap=\"100\">\n <Text fontWeight=\"600\" fontSize=\"sm\">Premium Feature</Text>\n <Text fontSize=\"xs\">\n Available on Pro and Enterprise plans\n </Text>\n </Stack>\n </Tooltip.Content>\n </Tooltip.Root>\n)\n```\n\n### With icon buttons\n\nA common pattern is providing context for icon-only buttons:\n\n```jsx live-dev\nconst App = () => (\n <Stack direction=\"row\" gap=\"300\">\n <Tooltip.Root>\n <IconButton aria-label=\"Edit\">\n <Icons.Edit />\n </IconButton>\n <Tooltip.Content placement=\"top\">\n Edit item\n </Tooltip.Content>\n </Tooltip.Root>\n\n <Tooltip.Root>\n <IconButton aria-label=\"Delete\">\n <Icons.Delete />\n </IconButton>\n <Tooltip.Content placement=\"top\">\n Delete item\n </Tooltip.Content>\n </Tooltip.Root>\n\n <Tooltip.Root>\n <IconButton aria-label=\"Share\">\n <Icons.Share />\n </IconButton>\n <Tooltip.Content placement=\"top\">\n Share with team\n </Tooltip.Content>\n </Tooltip.Root>\n </Stack>\n)\n```\n\n### With focusable elements\n\nTooltips work with any focusable element. For non-interactive elements like Text, use `MakeElementFocusable`:\n\n```jsx live-dev\nconst App = () => (\n <Stack direction=\"column\" gap=\"400\">\n <Tooltip.Root>\n <MakeElementFocusable>\n <Text\n as=\"span\"\n textDecoration=\"underline\"\n cursor=\"help\"\n >\n Hover or focus me\n </Text>\n </MakeElementFocusable>\n <Tooltip.Content>\n This text element is focusable and shows a tooltip\n </Tooltip.Content>\n </Tooltip.Root>\n\n <Tooltip.Root>\n <Button variant=\"ghost\">\n <Icons.Info />\n Learn more\n </Button>\n <Tooltip.Content placement=\"right\">\n Opens detailed documentation\n </Tooltip.Content>\n </Tooltip.Root>\n </Stack>\n)\n```\n\n## Component requirements\n\n## Accessibility\n\nThe Tooltip component is built with React Aria Components and provides accessible tooltip behavior by default. The component:\n\n- Automatically associates tooltip content with the trigger using `aria-describedby`\n- Assigns the `tooltip` ARIA role to the content\n- Shows on both hover and keyboard focus\n- Hides when focus leaves or `Escape` is pressed\n\n**Important:** Ensure the trigger element has an accessible label. For icon-only buttons, always provide an `aria-label`:\n\n```tsx\n<Tooltip.Root>\n <IconButton aria-label=\"Delete item\">\n <Icons.Delete />\n </IconButton>\n <Tooltip.Content>Delete this item</Tooltip.Content>\n</Tooltip.Root>\n```\n\nIf your use case requires tracking and analytics for this component, it is good practice to add a **persistent**, **unique** id to the trigger element:\n\n```tsx\nconst PERSISTENT_ID = \"delete-button-tooltip\";\n\nexport const Example = () => (\n <Tooltip.Root>\n <IconButton id={PERSISTENT_ID} aria-label=\"Delete\">\n <Icons.Delete />\n </IconButton>\n <Tooltip.Content>Delete item</Tooltip.Content>\n </Tooltip.Root>\n);\n```\n\n#### Keyboard navigation\n\nThe component supports full keyboard interaction:\n\n- `Tab` / `Shift+Tab`: Move focus to/from the trigger element\n- Tooltip appears automatically when trigger receives focus\n- `Escape`: Dismisses the tooltip (if shown)\n- Tooltip disappears automatically when focus leaves the trigger\n\n### Trigger element requirements\n\nThe first child of `Tooltip.Root` must be a focusable element:\n\n- Use buttons, links, or other interactive elements (recommended)\n- For non-interactive elements like Text or div, wrap with `MakeElementFocusable`\n- Avoid wrapping non-focusable elements without proper handling\n\n```tsx\n// ✅ Good - button is natively focusable\n<Tooltip.Root>\n <Button>Trigger</Button>\n <Tooltip.Content>Content</Tooltip.Content>\n</Tooltip.Root>\n\n// ✅ Good - non-interactive element with MakeElementFocusable\n<Tooltip.Root>\n <MakeElementFocusable>\n <Text as=\"span\">Trigger</Text>\n </MakeElementFocusable>\n <Tooltip.Content>Content</Tooltip.Content>\n</Tooltip.Root>\n\n// ❌ Bad - Text with tabIndex won't work properly\n<Tooltip.Root>\n <Text as=\"span\" tabIndex={0}>Trigger</Text>\n <Tooltip.Content>Content</Tooltip.Content>\n</Tooltip.Root>\n```\n\n## API reference\n\n<PropsTable id=\"Tooltip\" />\n\n## Common patterns\n\n### Info tooltips for form fields\n\nProvide additional context for form fields without cluttering the interface:\n\n```jsx live-dev\nconst App = () => {\n const [value, setValue] = useState('');\n\n return (\n <Stack direction=\"column\" gap=\"400\">\n <Stack direction=\"row\" gap=\"200\" alignItems=\"center\">\n <Text as=\"label\" htmlFor=\"username-input\" fontWeight=\"500\">\n Username\n </Text>\n <Tooltip.Root>\n <IconButton\n aria-label=\"Username requirements\"\n size=\"sm\"\n variant=\"ghost\"\n >\n <Icons.Info />\n </IconButton>\n <Tooltip.Content placement=\"right\">\n <Stack direction=\"column\" gap=\"100\">\n <Text fontSize=\"xs\" fontWeight=\"600\">Requirements:</Text>\n <Text fontSize=\"xs\">• 3-20 characters</Text>\n <Text fontSize=\"xs\">• Letters and numbers only</Text>\n <Text fontSize=\"xs\">• No spaces</Text>\n </Stack>\n </Tooltip.Content>\n </Tooltip.Root>\n </Stack>\n <TextInput\n id=\"username-input\"\n value={value}\n onChange={(e) => setValue(e.target.value)}\n placeholder=\"Enter username\"\n />\n </Stack>\n );\n}\n```\n\n### Action descriptions for icon buttons\n\nClarify what icon buttons do before users click them:\n\n```jsx live-dev\nconst App = () => {\n const [copied, setCopied] = useState(false);\n\n const handleCopy = () => {\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n };\n\n return (\n <Stack direction=\"column\" gap=\"400\">\n <Text fontSize=\"sm\">\n Code snippet:\n </Text>\n <Stack\n direction=\"row\"\n gap=\"300\"\n padding=\"300\"\n backgroundColor=\"neutral.3\"\n borderRadius=\"200\"\n alignItems=\"center\"\n >\n <Text fontFamily=\"mono\" fontSize=\"sm\" flex=\"1\">\n npm install @commercetools/nimbus\n </Text>\n\n <Tooltip.Root>\n <IconButton\n aria-label=\"Copy to clipboard\"\n size=\"sm\"\n variant=\"ghost\"\n onClick={handleCopy}\n >\n {copied ? <Icons.Check /> : <Icons.ContentCopy />}\n </IconButton>\n <Tooltip.Content placement=\"top\">\n {copied ? 'Copied!' : 'Copy to clipboard'}\n </Tooltip.Content>\n </Tooltip.Root>\n </Stack>\n </Stack>\n );\n}\n```\n\n### Contextual help in data tables\n\nProvide explanations for table headers or data values:\n\n```jsx live-dev\nconst App = () => (\n <Stack direction=\"column\" gap=\"300\">\n <Stack direction=\"row\" gap=\"400\" paddingX=\"300\" paddingY=\"200\">\n <Stack direction=\"row\" gap=\"100\" alignItems=\"center\" flex=\"1\">\n <Text fontWeight=\"600\" fontSize=\"sm\">Product Name</Text>\n </Stack>\n\n <Stack direction=\"row\" gap=\"100\" alignItems=\"center\" width=\"150px\">\n <Text fontWeight=\"600\" fontSize=\"sm\">Conversion Rate</Text>\n <Tooltip.Root>\n <IconButton\n aria-label=\"About conversion rate\"\n size=\"sm\"\n variant=\"ghost\"\n >\n <Icons.HelpOutline />\n </IconButton>\n <Tooltip.Content placement=\"top\">\n <Text fontSize=\"xs\">\n Percentage of visitors who make a purchase\n </Text>\n </Tooltip.Content>\n </Tooltip.Root>\n </Stack>\n\n <Stack direction=\"row\" gap=\"100\" alignItems=\"center\" width=\"100px\">\n <Text fontWeight=\"600\" fontSize=\"sm\">Status</Text>\n </Stack>\n </Stack>\n\n <Stack direction=\"row\" gap=\"400\" paddingX=\"300\" paddingY=\"200\" backgroundColor=\"neutral.2\">\n <Text fontSize=\"sm\" flex=\"1\">Premium Widget</Text>\n <Text fontSize=\"sm\" width=\"150px\">24.5%</Text>\n <Text fontSize=\"sm\" width=\"100px\">Active</Text>\n </Stack>\n </Stack>\n)\n```\n\n## Testing your implementation\n\nThese examples demonstrate how to test your implementation when using Tooltip within your application. As the component's internal functionality is already tested by Nimbus, these patterns help you verify your integration and application-specific logic.\n\n### Basic Rendering Tests\n\nVerify the tooltip renders and displays content on interaction\n\n```tsx\nimport { describe, it, expect } from \"vitest\";\nimport { render, screen, waitFor } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { Tooltip, Button, NimbusProvider } from \"@commercetools/nimbus\";\n\ndescribe(\"Tooltip - Basic rendering\", () => {\n it(\"renders trigger element\", () => {\n render(\n <NimbusProvider>\n <Tooltip.Root>\n <Button>Hover me</Button>\n <Tooltip.Content>Tooltip text</Tooltip.Content>\n </Tooltip.Root>\n </NimbusProvider>\n );\n\n expect(\n screen.getByRole(\"button\", { name: /hover me/i })\n ).toBeInTheDocument();\n });\n\n it(\"shows tooltip content on focus\", async () => {\n const user = userEvent.setup();\n render(\n <NimbusProvider>\n <Tooltip.Root>\n <Button>Hover me</Button>\n <Tooltip.Content>Tooltip text</Tooltip.Content>\n </Tooltip.Root>\n </NimbusProvider>\n );\n\n // Focus the button using tab\n await user.tab();\n\n await waitFor(() => {\n expect(screen.getByRole(\"tooltip\")).toBeInTheDocument();\n expect(screen.getByText(\"Tooltip text\")).toBeInTheDocument();\n });\n });\n});\n```\n\n### Interaction Tests\n\nTest focus interactions (hover tests require browser environment)\n\n```tsx\nimport { describe, it, expect } from \"vitest\";\nimport { render, screen, waitFor } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { Tooltip, Button, NimbusProvider } from \"@commercetools/nimbus\";\n\ndescribe(\"Tooltip - Interactions\", () => {\n it(\"shows tooltip on focus\", async () => {\n const user = userEvent.setup();\n render(\n <NimbusProvider>\n <Tooltip.Root>\n <Button>Action Button</Button>\n <Tooltip.Content>Action tooltip</Tooltip.Content>\n </Tooltip.Root>\n </NimbusProvider>\n );\n\n // Use userEvent.tab() instead of element.focus() to avoid act() warnings\n await user.tab();\n\n await waitFor(() => {\n expect(screen.getByRole(\"tooltip\")).toBeInTheDocument();\n expect(screen.getByText(\"Action tooltip\")).toBeInTheDocument();\n });\n });\n\n it(\"hides tooltip when focus leaves\", async () => {\n const user = userEvent.setup();\n render(\n <NimbusProvider>\n <Tooltip.Root>\n <Button>First</Button>\n <Tooltip.Content>First tooltip</Tooltip.Content>\n </Tooltip.Root>\n <Button>Second</Button>\n </NimbusProvider>\n );\n\n // Focus first button\n await user.tab();\n await waitFor(() => {\n expect(screen.getByRole(\"tooltip\")).toBeInTheDocument();\n });\n\n // Focus second button\n await user.tab();\n await waitFor(() => {\n expect(screen.queryByRole(\"tooltip\")).not.toBeInTheDocument();\n });\n });\n});\n```\n\n### Testing Placement\n\nVerify tooltip positioning\n\n```tsx\nimport { describe, it, expect } from \"vitest\";\nimport { render, screen, waitFor } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { Tooltip, Button, NimbusProvider } from \"@commercetools/nimbus\";\n\ndescribe(\"Tooltip - Placement\", () => {\n it(\"renders with top placement\", async () => {\n const user = userEvent.setup();\n render(\n <NimbusProvider>\n <Tooltip.Root>\n <Button>Trigger</Button>\n <Tooltip.Content placement=\"top\">Top tooltip</Tooltip.Content>\n </Tooltip.Root>\n </NimbusProvider>\n );\n\n // Tab to focus the button\n await user.tab();\n\n await waitFor(() => {\n const tooltip = screen.getByRole(\"tooltip\");\n expect(tooltip).toBeInTheDocument();\n expect(tooltip).toHaveTextContent(\"Top tooltip\");\n });\n });\n});\n```\n\n### Testing with Portal Content\n\nTooltips render in a portal, so query from document when needed\n\n```tsx\nimport { describe, it, expect } from \"vitest\";\nimport { render, screen, waitFor } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { Tooltip, Button, NimbusProvider } from \"@commercetools/nimbus\";\n\ndescribe(\"Tooltip - Portal rendering\", () => {\n it(\"renders tooltip in portal\", async () => {\n const user = userEvent.setup();\n render(\n <NimbusProvider>\n <Tooltip.Root>\n <Button>Show tooltip</Button>\n <Tooltip.Content>Portal content</Tooltip.Content>\n </Tooltip.Root>\n </NimbusProvider>\n );\n\n // Use userEvent.tab() instead of element.focus() to avoid act() warnings\n await user.tab();\n\n // Wait for portal content to appear\n await waitFor(() => {\n const tooltip = screen.getByRole(\"tooltip\");\n expect(tooltip).toBeInTheDocument();\n });\n });\n});\n```\n\n### Testing Custom Delay Timing\n\nVerify delay configuration works as expected\n\n```tsx\nimport { describe, it, expect } from \"vitest\";\nimport { render, screen, waitFor } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { Tooltip, Button, NimbusProvider } from \"@commercetools/nimbus\";\n\ndescribe(\"Tooltip - Timing\", () => {\n it(\"respects custom delay\", async () => {\n const user = userEvent.setup();\n render(\n <NimbusProvider>\n <Tooltip.Root delay={0} closeDelay={0}>\n <Button>Instant</Button>\n <Tooltip.Content>Shows immediately</Tooltip.Content>\n </Tooltip.Root>\n </NimbusProvider>\n );\n\n // Use userEvent.tab() instead of element.focus() to avoid act() warnings\n await user.tab();\n\n // With delay={0}, tooltip should appear immediately\n await waitFor(\n () => {\n expect(screen.getByRole(\"tooltip\")).toBeInTheDocument();\n },\n { timeout: 100 }\n );\n });\n});\n```\n\n\n## Resources\n\n- [Storybook](https://nimbus-storybook.vercel.app/?path=/docs/components-tooltip-tooltip--docs)\n- [React Aria Tooltip](https://react-spectrum.adobe.com/react-aria/Tooltip.html)\n- [ARIA Tooltip Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/tooltip/)\n",
|
|
168
|
+
"mdx": "\n## Getting started\n\n### Import\n\n```tsx\nimport { Tooltip, MakeElementFocusable } from '@commercetools/nimbus';\n```\n\n### Basic usage\n\nThe simplest implementation wraps a trigger element and tooltip content:\n\n```jsx live-dev\nconst App = () => (\n <Tooltip.Root>\n <Button>Hover me</Button>\n <Tooltip.Content>\n Helpful information appears here\n </Tooltip.Content>\n </Tooltip.Root>\n)\n```\n\n## Usage examples\n\n### Placement options\n\nThe `placement` prop controls where the tooltip appears relative to the trigger element. The default placement is `\"bottom\"`:\n\n```jsx live-dev\nconst App = () => (\n <Stack direction=\"row\" gap=\"400\" wrap=\"wrap\">\n <Tooltip.Root>\n <Button>Top</Button>\n <Tooltip.Content placement=\"top\">\n Top placement\n </Tooltip.Content>\n </Tooltip.Root>\n\n <Tooltip.Root>\n <Button>Bottom</Button>\n <Tooltip.Content placement=\"bottom\">\n Bottom placement\n </Tooltip.Content>\n </Tooltip.Root>\n\n <Tooltip.Root>\n <Button>Left</Button>\n <Tooltip.Content placement=\"left\">\n Left placement\n </Tooltip.Content>\n </Tooltip.Root>\n\n <Tooltip.Root>\n <Button>Right</Button>\n <Tooltip.Content placement=\"right\">\n Right placement\n </Tooltip.Content>\n </Tooltip.Root>\n </Stack>\n)\n```\n\n**Available placements:**\n\nThe component supports 26 placement options:\n- **Basic**: `top`, `bottom`, `left`, `right`, `start`, `end`\n- **With alignment**: `top left`, `top right`, `top start`, `top end`, `bottom left`, `bottom right`, `bottom start`, `bottom end`, `left top`, `left bottom`, `right top`, `right bottom`, `start top`, `start bottom`, `end top`, `end bottom`\n\n### Delay configuration\n\nControl when tooltips appear and disappear using `delay` and `closeDelay` props on `Tooltip.Root`:\n\n```jsx live-dev\nconst App = () => (\n <Stack direction=\"row\" gap=\"400\" wrap=\"wrap\">\n <Tooltip.Root delay={0} closeDelay={0}>\n <Button>Instant</Button>\n <Tooltip.Content>\n Shows immediately\n </Tooltip.Content>\n </Tooltip.Root>\n\n <Tooltip.Root delay={300} closeDelay={200}>\n <Button>Default timing</Button>\n <Tooltip.Content>\n 300ms delay to show, 200ms to hide\n </Tooltip.Content>\n </Tooltip.Root>\n\n <Tooltip.Root delay={1000}>\n <Button>Slow</Button>\n <Tooltip.Content>\n 1000ms delay to show\n </Tooltip.Content>\n </Tooltip.Root>\n </Stack>\n)\n```\n\n**Timing considerations:**\n- `delay`: Time in milliseconds before the tooltip appears (default: `300`)\n- `closeDelay`: Time in milliseconds before the tooltip disappears (default: `200`)\n- Shorter delays improve responsiveness but may feel too aggressive\n- Longer delays reduce accidental triggers but may feel sluggish\n\n### Complex content\n\nTooltip content can include multiple elements using layout components:\n\n```jsx live-dev\nconst App = () => (\n <Tooltip.Root>\n <Button>Feature info</Button>\n <Tooltip.Content>\n <Stack direction=\"column\" gap=\"100\">\n <Text fontWeight=\"600\" fontSize=\"sm\">Premium Feature</Text>\n <Text fontSize=\"xs\">\n Available on Pro and Enterprise plans\n </Text>\n </Stack>\n </Tooltip.Content>\n </Tooltip.Root>\n)\n```\n\n### With icon buttons\n\nA common pattern is providing context for icon-only buttons:\n\n```jsx live-dev\nconst App = () => (\n <Stack direction=\"row\" gap=\"300\">\n <Tooltip.Root>\n <IconButton aria-label=\"Edit\">\n <Icons.Edit />\n </IconButton>\n <Tooltip.Content placement=\"top\">\n Edit item\n </Tooltip.Content>\n </Tooltip.Root>\n\n <Tooltip.Root>\n <IconButton aria-label=\"Delete\">\n <Icons.Delete />\n </IconButton>\n <Tooltip.Content placement=\"top\">\n Delete item\n </Tooltip.Content>\n </Tooltip.Root>\n\n <Tooltip.Root>\n <IconButton aria-label=\"Share\">\n <Icons.Share />\n </IconButton>\n <Tooltip.Content placement=\"top\">\n Share with team\n </Tooltip.Content>\n </Tooltip.Root>\n </Stack>\n)\n```\n\n### With focusable elements\n\nTooltips work with any focusable element. For non-interactive elements like Text, use `MakeElementFocusable`:\n\n```jsx live-dev\nconst App = () => (\n <Stack direction=\"column\" gap=\"400\">\n <Tooltip.Root>\n <MakeElementFocusable>\n <Text\n as=\"span\"\n textDecoration=\"underline\"\n cursor=\"help\"\n >\n Hover or focus me\n </Text>\n </MakeElementFocusable>\n <Tooltip.Content>\n This text element is focusable and shows a tooltip\n </Tooltip.Content>\n </Tooltip.Root>\n\n <Tooltip.Root>\n <Button variant=\"ghost\">\n <Icons.Info />\n Learn more\n </Button>\n <Tooltip.Content placement=\"right\">\n Opens detailed documentation\n </Tooltip.Content>\n </Tooltip.Root>\n </Stack>\n)\n```\n\n### Disabling the tooltip\n\nUse the `isDisabled` prop on `Tooltip.Root` to suppress the tooltip without\nremoving the trigger from the tree. When `isDisabled` is `true`, hover and focus\nlisteners are inactive, the overlay never opens, and no `aria-describedby` is\nadded to the trigger. The trigger element renders unchanged with the same DOM\nand focus order.\n\nThis is useful when tooltip text is conditionally irrelevant (e.g. the trigger\nlabel already conveys the information) or when a parent feature flag should\nsuppress all tooltips in a region.\n\n```jsx live-dev\nconst App = () => {\n const [isDisabled, setIsDisabled] = useState(true);\n return (\n <Stack direction=\"row\" alignItems=\"center\" gap=\"400\">\n <Switch isSelected={isDisabled} onChange={setIsDisabled}>\n Tooltip disabled\n </Switch>\n <Tooltip.Root isDisabled={isDisabled}>\n <Button>Hover me</Button>\n <Tooltip.Content>\n This tooltip can be toggled\n </Tooltip.Content>\n </Tooltip.Root>\n </Stack>\n );\n}\n```\n\n> **Note:** `isDisabled` on `Tooltip.Root` disables the *tooltip overlay*. To\n> keep a tooltip visible on a *disabled button*, see\n> [With disabled buttons](#with-disabled-buttons) below.\n\n### With disabled buttons\n\nA natively disabled button cannot host a tooltip: the `disabled` attribute\nremoves it from the tab order and stops it firing hover/focus events, so the\ntooltip has nothing to open from. This is unfortunate precisely when a tooltip\nis most useful — explaining _why_ an action is unavailable.\n\nPass `allowFocusWhenDisabled` together with `isDisabled` on the `Button`. The\nbutton stays focusable and hoverable and is announced as disabled via\n`aria-disabled` (instead of the native `disabled` attribute), while its action\nstays fully suppressed. The tooltip then opens on hover and keyboard focus like\nany other trigger.\n\n```jsx live-dev\nconst App = () => (\n <Tooltip.Root>\n <Button isDisabled allowFocusWhenDisabled>\n Publish\n </Button>\n <Tooltip.Content>\n Complete all required fields before publishing.\n </Tooltip.Content>\n </Tooltip.Root>\n)\n```\n\nPrefer this to wrapping a disabled button in `MakeElementFocusable`: the\n`allowFocusWhenDisabled` path keeps the disabled semantics on the button itself\n(`aria-disabled`) and suppresses activation, whereas a focusable wrapper only\nadds focus and describes the wrong element.\n\n## Component requirements\n\n## Accessibility\n\nThe Tooltip component is built with React Aria Components and provides accessible tooltip behavior by default. The component:\n\n- Automatically associates tooltip content with the trigger using `aria-describedby`\n- Assigns the `tooltip` ARIA role to the content\n- Shows on both hover and keyboard focus\n- Hides when focus leaves or `Escape` is pressed\n\n**Important:** Ensure the trigger element has an accessible label. For icon-only buttons, always provide an `aria-label`:\n\n```tsx\n<Tooltip.Root>\n <IconButton aria-label=\"Delete item\">\n <Icons.Delete />\n </IconButton>\n <Tooltip.Content>Delete this item</Tooltip.Content>\n</Tooltip.Root>\n```\n\nIf your use case requires tracking and analytics for this component, it is good practice to add a **persistent**, **unique** id to the trigger element:\n\n```tsx\nconst PERSISTENT_ID = \"delete-button-tooltip\";\n\nexport const Example = () => (\n <Tooltip.Root>\n <IconButton id={PERSISTENT_ID} aria-label=\"Delete\">\n <Icons.Delete />\n </IconButton>\n <Tooltip.Content>Delete item</Tooltip.Content>\n </Tooltip.Root>\n);\n```\n\n#### Keyboard navigation\n\nThe component supports full keyboard interaction:\n\n- `Tab` / `Shift+Tab`: Move focus to/from the trigger element\n- Tooltip appears automatically when trigger receives focus\n- `Escape`: Dismisses the tooltip (if shown)\n- Tooltip disappears automatically when focus leaves the trigger\n\n### Trigger element requirements\n\nThe first child of `Tooltip.Root` must be a focusable element:\n\n- Use buttons, links, or other interactive elements (recommended)\n- For non-interactive elements like Text or div, wrap with `MakeElementFocusable`\n- Avoid wrapping non-focusable elements without proper handling\n\n```tsx\n// ✅ Good - button is natively focusable\n<Tooltip.Root>\n <Button>Trigger</Button>\n <Tooltip.Content>Content</Tooltip.Content>\n</Tooltip.Root>\n\n// ✅ Good - non-interactive element with MakeElementFocusable\n<Tooltip.Root>\n <MakeElementFocusable>\n <Text as=\"span\">Trigger</Text>\n </MakeElementFocusable>\n <Tooltip.Content>Content</Tooltip.Content>\n</Tooltip.Root>\n\n// ❌ Bad - Text with tabIndex won't work properly\n<Tooltip.Root>\n <Text as=\"span\" tabIndex={0}>Trigger</Text>\n <Tooltip.Content>Content</Tooltip.Content>\n</Tooltip.Root>\n```\n\n## API reference\n\n<PropsTable id=\"Tooltip\" />\n\n## Common patterns\n\n### Info tooltips for form fields\n\nProvide additional context for form fields without cluttering the interface:\n\n```jsx live-dev\nconst App = () => {\n const [value, setValue] = useState('');\n\n return (\n <Stack direction=\"column\" gap=\"400\">\n <Stack direction=\"row\" gap=\"200\" alignItems=\"center\">\n <Text as=\"label\" htmlFor=\"username-input\" fontWeight=\"500\">\n Username\n </Text>\n <Tooltip.Root>\n <IconButton\n aria-label=\"Username requirements\"\n size=\"sm\"\n variant=\"ghost\"\n >\n <Icons.Info />\n </IconButton>\n <Tooltip.Content placement=\"right\">\n <Stack direction=\"column\" gap=\"100\">\n <Text fontSize=\"xs\" fontWeight=\"600\">Requirements:</Text>\n <Text fontSize=\"xs\">• 3-20 characters</Text>\n <Text fontSize=\"xs\">• Letters and numbers only</Text>\n <Text fontSize=\"xs\">• No spaces</Text>\n </Stack>\n </Tooltip.Content>\n </Tooltip.Root>\n </Stack>\n <TextInput\n id=\"username-input\"\n value={value}\n onChange={(e) => setValue(e.target.value)}\n placeholder=\"Enter username\"\n />\n </Stack>\n );\n}\n```\n\n### Action descriptions for icon buttons\n\nClarify what icon buttons do before users click them:\n\n```jsx live-dev\nconst App = () => {\n const [copied, setCopied] = useState(false);\n\n const handleCopy = () => {\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n };\n\n return (\n <Stack direction=\"column\" gap=\"400\">\n <Text fontSize=\"sm\">\n Code snippet:\n </Text>\n <Stack\n direction=\"row\"\n gap=\"300\"\n padding=\"300\"\n backgroundColor=\"neutral.3\"\n borderRadius=\"200\"\n alignItems=\"center\"\n >\n <Text fontFamily=\"mono\" fontSize=\"sm\" flex=\"1\">\n npm install @commercetools/nimbus\n </Text>\n\n <Tooltip.Root>\n <IconButton\n aria-label=\"Copy to clipboard\"\n size=\"sm\"\n variant=\"ghost\"\n onClick={handleCopy}\n >\n {copied ? <Icons.Check /> : <Icons.ContentCopy />}\n </IconButton>\n <Tooltip.Content placement=\"top\">\n {copied ? 'Copied!' : 'Copy to clipboard'}\n </Tooltip.Content>\n </Tooltip.Root>\n </Stack>\n </Stack>\n );\n}\n```\n\n### Contextual help in data tables\n\nProvide explanations for table headers or data values:\n\n```jsx live-dev\nconst App = () => (\n <Stack direction=\"column\" gap=\"300\">\n <Stack direction=\"row\" gap=\"400\" paddingX=\"300\" paddingY=\"200\">\n <Stack direction=\"row\" gap=\"100\" alignItems=\"center\" flex=\"1\">\n <Text fontWeight=\"600\" fontSize=\"sm\">Product Name</Text>\n </Stack>\n\n <Stack direction=\"row\" gap=\"100\" alignItems=\"center\" width=\"150px\">\n <Text fontWeight=\"600\" fontSize=\"sm\">Conversion Rate</Text>\n <Tooltip.Root>\n <IconButton\n aria-label=\"About conversion rate\"\n size=\"sm\"\n variant=\"ghost\"\n >\n <Icons.HelpOutline />\n </IconButton>\n <Tooltip.Content placement=\"top\">\n <Text fontSize=\"xs\">\n Percentage of visitors who make a purchase\n </Text>\n </Tooltip.Content>\n </Tooltip.Root>\n </Stack>\n\n <Stack direction=\"row\" gap=\"100\" alignItems=\"center\" width=\"100px\">\n <Text fontWeight=\"600\" fontSize=\"sm\">Status</Text>\n </Stack>\n </Stack>\n\n <Stack direction=\"row\" gap=\"400\" paddingX=\"300\" paddingY=\"200\" backgroundColor=\"neutral.2\">\n <Text fontSize=\"sm\" flex=\"1\">Premium Widget</Text>\n <Text fontSize=\"sm\" width=\"150px\">24.5%</Text>\n <Text fontSize=\"sm\" width=\"100px\">Active</Text>\n </Stack>\n </Stack>\n)\n```\n\n## Testing your implementation\n\nThese examples demonstrate how to test your implementation when using Tooltip within your application. As the component's internal functionality is already tested by Nimbus, these patterns help you verify your integration and application-specific logic.\n\n### Basic Rendering Tests\n\nVerify the tooltip renders and displays content on interaction\n\n```tsx\nimport { describe, it, expect } from \"vitest\";\nimport { render, screen, waitFor } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { Tooltip, Button, NimbusProvider } from \"@commercetools/nimbus\";\n\ndescribe(\"Tooltip - Basic rendering\", () => {\n it(\"renders trigger element\", () => {\n render(\n <NimbusProvider>\n <Tooltip.Root>\n <Button>Hover me</Button>\n <Tooltip.Content>Tooltip text</Tooltip.Content>\n </Tooltip.Root>\n </NimbusProvider>\n );\n\n expect(\n screen.getByRole(\"button\", { name: /hover me/i })\n ).toBeInTheDocument();\n });\n\n it(\"shows tooltip content on focus\", async () => {\n const user = userEvent.setup();\n render(\n <NimbusProvider>\n <Tooltip.Root>\n <Button>Hover me</Button>\n <Tooltip.Content>Tooltip text</Tooltip.Content>\n </Tooltip.Root>\n </NimbusProvider>\n );\n\n // Focus the button using tab\n await user.tab();\n\n await waitFor(() => {\n expect(screen.getByRole(\"tooltip\")).toBeInTheDocument();\n expect(screen.getByText(\"Tooltip text\")).toBeInTheDocument();\n });\n });\n});\n```\n\n### Interaction Tests\n\nTest focus interactions (hover tests require browser environment)\n\n```tsx\nimport { describe, it, expect } from \"vitest\";\nimport { render, screen, waitFor } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { Tooltip, Button, NimbusProvider } from \"@commercetools/nimbus\";\n\ndescribe(\"Tooltip - Interactions\", () => {\n it(\"shows tooltip on focus\", async () => {\n const user = userEvent.setup();\n render(\n <NimbusProvider>\n <Tooltip.Root>\n <Button>Action Button</Button>\n <Tooltip.Content>Action tooltip</Tooltip.Content>\n </Tooltip.Root>\n </NimbusProvider>\n );\n\n // Use userEvent.tab() instead of element.focus() to avoid act() warnings\n await user.tab();\n\n await waitFor(() => {\n expect(screen.getByRole(\"tooltip\")).toBeInTheDocument();\n expect(screen.getByText(\"Action tooltip\")).toBeInTheDocument();\n });\n });\n\n it(\"hides tooltip when focus leaves\", async () => {\n const user = userEvent.setup();\n render(\n <NimbusProvider>\n <Tooltip.Root>\n <Button>First</Button>\n <Tooltip.Content>First tooltip</Tooltip.Content>\n </Tooltip.Root>\n <Button>Second</Button>\n </NimbusProvider>\n );\n\n // Focus first button\n await user.tab();\n await waitFor(() => {\n expect(screen.getByRole(\"tooltip\")).toBeInTheDocument();\n });\n\n // Focus second button\n await user.tab();\n await waitFor(() => {\n expect(screen.queryByRole(\"tooltip\")).not.toBeInTheDocument();\n });\n });\n});\n```\n\n### Testing Placement\n\nVerify tooltip positioning\n\n```tsx\nimport { describe, it, expect } from \"vitest\";\nimport { render, screen, waitFor } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { Tooltip, Button, NimbusProvider } from \"@commercetools/nimbus\";\n\ndescribe(\"Tooltip - Placement\", () => {\n it(\"renders with top placement\", async () => {\n const user = userEvent.setup();\n render(\n <NimbusProvider>\n <Tooltip.Root>\n <Button>Trigger</Button>\n <Tooltip.Content placement=\"top\">Top tooltip</Tooltip.Content>\n </Tooltip.Root>\n </NimbusProvider>\n );\n\n // Tab to focus the button\n await user.tab();\n\n await waitFor(() => {\n const tooltip = screen.getByRole(\"tooltip\");\n expect(tooltip).toBeInTheDocument();\n expect(tooltip).toHaveTextContent(\"Top tooltip\");\n });\n });\n});\n```\n\n### Testing with Portal Content\n\nTooltips render in a portal, so query from document when needed\n\n```tsx\nimport { describe, it, expect } from \"vitest\";\nimport { render, screen, waitFor } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { Tooltip, Button, NimbusProvider } from \"@commercetools/nimbus\";\n\ndescribe(\"Tooltip - Portal rendering\", () => {\n it(\"renders tooltip in portal\", async () => {\n const user = userEvent.setup();\n render(\n <NimbusProvider>\n <Tooltip.Root>\n <Button>Show tooltip</Button>\n <Tooltip.Content>Portal content</Tooltip.Content>\n </Tooltip.Root>\n </NimbusProvider>\n );\n\n // Use userEvent.tab() instead of element.focus() to avoid act() warnings\n await user.tab();\n\n // Wait for portal content to appear\n await waitFor(() => {\n const tooltip = screen.getByRole(\"tooltip\");\n expect(tooltip).toBeInTheDocument();\n });\n });\n});\n```\n\n### Testing Custom Delay Timing\n\nVerify delay configuration works as expected\n\n```tsx\nimport { describe, it, expect } from \"vitest\";\nimport { render, screen, waitFor } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { Tooltip, Button, NimbusProvider } from \"@commercetools/nimbus\";\n\ndescribe(\"Tooltip - Timing\", () => {\n it(\"respects custom delay\", async () => {\n const user = userEvent.setup();\n render(\n <NimbusProvider>\n <Tooltip.Root delay={0} closeDelay={0}>\n <Button>Instant</Button>\n <Tooltip.Content>Shows immediately</Tooltip.Content>\n </Tooltip.Root>\n </NimbusProvider>\n );\n\n // Use userEvent.tab() instead of element.focus() to avoid act() warnings\n await user.tab();\n\n // With delay={0}, tooltip should appear immediately\n await waitFor(\n () => {\n expect(screen.getByRole(\"tooltip\")).toBeInTheDocument();\n },\n { timeout: 100 }\n );\n });\n});\n```\n\n\n## Resources\n\n- [Storybook](https://nimbus-storybook.vercel.app/?path=/docs/components-tooltip-tooltip--docs)\n- [React Aria Tooltip](https://react-spectrum.adobe.com/react-aria/Tooltip.html)\n- [ARIA Tooltip Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/tooltip/)\n",
|
|
169
169
|
"toc": [
|
|
170
170
|
{
|
|
171
171
|
"value": "Getting started",
|
|
@@ -264,6 +264,28 @@
|
|
|
264
264
|
],
|
|
265
265
|
"parent": "root"
|
|
266
266
|
},
|
|
267
|
+
{
|
|
268
|
+
"value": "Disabling the tooltip",
|
|
269
|
+
"href": "#disabling-the-tooltip",
|
|
270
|
+
"depth": 3,
|
|
271
|
+
"numbering": [
|
|
272
|
+
1,
|
|
273
|
+
2,
|
|
274
|
+
6
|
|
275
|
+
],
|
|
276
|
+
"parent": "root"
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
"value": "With disabled buttons",
|
|
280
|
+
"href": "#with-disabled-buttons",
|
|
281
|
+
"depth": 3,
|
|
282
|
+
"numbering": [
|
|
283
|
+
1,
|
|
284
|
+
2,
|
|
285
|
+
7
|
|
286
|
+
],
|
|
287
|
+
"parent": "root"
|
|
288
|
+
},
|
|
267
289
|
{
|
|
268
290
|
"value": "Component requirements",
|
|
269
291
|
"href": "#component-requirements",
|