@fluentui/react-tooltip 9.0.0-rc.1 → 9.0.0-rc.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. package/CHANGELOG.json +467 -1
  2. package/CHANGELOG.md +239 -101
  3. package/MIGRATION.md +51 -43
  4. package/Spec.md +201 -337
  5. package/dist/{react-tooltip.d.ts → index.d.ts} +42 -35
  6. package/{lib → dist}/tsdoc-metadata.json +0 -0
  7. package/lib/Tooltip.js.map +1 -1
  8. package/lib/components/Tooltip/Tooltip.js.map +1 -1
  9. package/lib/components/Tooltip/Tooltip.types.js.map +1 -1
  10. package/lib/components/Tooltip/index.js.map +1 -1
  11. package/lib/components/Tooltip/private/constants.js.map +1 -1
  12. package/lib/components/Tooltip/renderTooltip.js +3 -1
  13. package/lib/components/Tooltip/renderTooltip.js.map +1 -1
  14. package/lib/components/Tooltip/useTooltip.js +32 -39
  15. package/lib/components/Tooltip/useTooltip.js.map +1 -1
  16. package/lib/components/Tooltip/useTooltipStyles.js +4 -2
  17. package/lib/components/Tooltip/useTooltipStyles.js.map +1 -1
  18. package/lib/index.js +1 -1
  19. package/lib/index.js.map +1 -1
  20. package/lib-commonjs/Tooltip.js.map +1 -1
  21. package/lib-commonjs/components/Tooltip/Tooltip.js.map +1 -1
  22. package/lib-commonjs/components/Tooltip/Tooltip.types.js.map +1 -1
  23. package/lib-commonjs/components/Tooltip/index.js.map +1 -1
  24. package/lib-commonjs/components/Tooltip/private/constants.js.map +1 -1
  25. package/lib-commonjs/components/Tooltip/renderTooltip.js +3 -1
  26. package/lib-commonjs/components/Tooltip/renderTooltip.js.map +1 -1
  27. package/lib-commonjs/components/Tooltip/useTooltip.js +31 -38
  28. package/lib-commonjs/components/Tooltip/useTooltip.js.map +1 -1
  29. package/lib-commonjs/components/Tooltip/useTooltipStyles.js +5 -3
  30. package/lib-commonjs/components/Tooltip/useTooltipStyles.js.map +1 -1
  31. package/lib-commonjs/index.js +32 -2
  32. package/lib-commonjs/index.js.map +1 -1
  33. package/package.json +19 -22
  34. package/lib/Tooltip.d.ts +0 -1
  35. package/lib/components/Tooltip/Tooltip.d.ts +0 -7
  36. package/lib/components/Tooltip/Tooltip.types.d.ts +0 -108
  37. package/lib/components/Tooltip/index.d.ts +0 -5
  38. package/lib/components/Tooltip/private/constants.d.ts +0 -12
  39. package/lib/components/Tooltip/renderTooltip.d.ts +0 -5
  40. package/lib/components/Tooltip/useTooltip.d.ts +0 -10
  41. package/lib/components/Tooltip/useTooltipStyles.d.ts +0 -6
  42. package/lib/index.d.ts +0 -1
  43. package/lib-commonjs/Tooltip.d.ts +0 -1
  44. package/lib-commonjs/components/Tooltip/Tooltip.d.ts +0 -7
  45. package/lib-commonjs/components/Tooltip/Tooltip.types.d.ts +0 -108
  46. package/lib-commonjs/components/Tooltip/index.d.ts +0 -5
  47. package/lib-commonjs/components/Tooltip/private/constants.d.ts +0 -12
  48. package/lib-commonjs/components/Tooltip/renderTooltip.d.ts +0 -5
  49. package/lib-commonjs/components/Tooltip/useTooltip.d.ts +0 -10
  50. package/lib-commonjs/components/Tooltip/useTooltipStyles.d.ts +0 -6
  51. package/lib-commonjs/index.d.ts +0 -1
package/Spec.md CHANGED
@@ -44,31 +44,42 @@ v0 tooltips use a `trigger` property to render the tooltip's target component. H
44
44
 
45
45
  # Sample Code
46
46
 
47
- To attach a tooltip to an element, wrap it with a `TooltipTrigger`. There is a `tooltip` shorthand slot for the content of the tooltip itself. It doesn't create any DOM nodes of its own (it does _not_ wrap the element with a `<div>` for example). Instead, it attaches listeners to the child by cloning the JSX object and adding `onPointerDown`, etc. events.
47
+ Label tooltip for an icon-only button:
48
48
 
49
- TooltipTrigger only supports a single child element, which can be either:
49
+ ```tsx
50
+ <Tooltip content="Copy" relationship="label">
51
+ <Button icon={<CopyRegular />} />
52
+ </Tooltip>
53
+ ```
50
54
 
51
- - A native element or component that supports DOM attributes (the child can't be a string, for example).
52
- - A render function that takes the extra props to be added to the trigger element.
55
+ Description tooltip for a link:
53
56
 
54
57
  ```tsx
55
- <TooltipTrigger tooltip="Example Tooltip">
56
- <a href="http://example.com">
57
- A link with a tooltip
58
- </a>
59
- </TooltipTrigger>
60
-
61
- <TooltipTrigger tooltip="Label for an icon button" type="label">
62
- <button href="http://example.com">
63
- <SomeIcon />
64
- </button>
65
- </TooltipTrigger>
58
+ <Tooltip content="This is an example" relationship="description">
59
+ <a href="http://example.com">A link</a>
60
+ </Tooltip>
61
+ ```
66
62
 
67
- <TooltipTrigger tooltip={{<>This supports <b>third party</b> components</>}}>
68
- <ThirdPartyComponent />
69
- </TooltipTrigger>
63
+ Tooltip with custom JSX content:
70
64
 
71
- <TooltipTrigger tooltip="The child can be a render function" placement="before" align="top" subtle showDelay={200}>
65
+ ```tsx
66
+ <Tooltip content={<b>The content can be JSX</b>} relationship="label">
67
+ <Button />
68
+ </Tooltip>
69
+ ```
70
+
71
+ Custom component as a trigger:
72
+
73
+ ```tsx
74
+ <Tooltip content="Supports any component that accepts HTML attributes" relationship="label">
75
+ <FancyButton />
76
+ </Tooltip>
77
+ ```
78
+
79
+ Render function for the trigger:
80
+
81
+ ```tsx
82
+ <Tooltip content="The child can be a render function" relationship="description">
72
83
  {triggerProps => (
73
84
  <>
74
85
  <div>
@@ -76,34 +87,37 @@ TooltipTrigger only supports a single child element, which can be either:
76
87
  </div>
77
88
  </>
78
89
  )}
79
- </TooltipTrigger>
90
+ </Tooltip>
91
+ ```
80
92
 
81
- <TooltipTrigger tooltip="It can target an element other than its trigger" targetRef={targetRef}>
93
+ ```tsx
94
+ <Tooltip
95
+ content="It can target an element other than its trigger"
96
+ relationship="description"
97
+ positioning={{ target: targetElement }}
98
+ >
82
99
  <button>
83
- Custom target:{' '}
84
- <div ref={targetRef} style={{ display: 'inline-block', width: '8px', height: '8px', background: 'red' }} />
100
+ Custom target: <div ref={setTargetElement} />
85
101
  </button>
86
- </TooltipTrigger>
102
+ </Tooltip>
87
103
  ```
88
104
 
89
105
  # Variants
90
106
 
91
- - The tooltip can have a `subtle` style variant with a different background and text color.
92
- - The tooltip can have render without an arrow pointing to the target element by using `noArrow`.
107
+ - The tooltip supports higher contrast colors with `appearance="inverted"`.
108
+ - The tooltip supports rendering an arrow pointing to the target element, using `withArrow`.
93
109
 
94
110
  # API
95
111
 
96
- The Tooltip API is split among several components and hooks, in two packages. Having two packages allows the lightweight `react-tooltip-trigger` package to be referenced by any component, while the bulk of the code for tooltip positioning and management lives in the larger `react-tooltip` package.
112
+ To attach a tooltip to an element, wrap it with a `Tooltip`. There is a `content` slot for the text of the tooltip itself.
113
+
114
+ Unlike most components, Tooltip doesn't have a root slot and doesn't allow native DOM props on the Tooltip itself. This is because it doesn't render any nodes inline around its trigger (it does _not_ wrap the element with a `<div>` for example). Instead, it attaches listeners to the child by cloning the JSX object and adding `onPointerEnter`, etc. listeners.
97
115
 
98
- - [**@fluentui/react-tooltip-trigger**](#fluentuireact-tooltip-trigger)
99
- - [`TooltipProps`](#tooltipprops)
100
- - [`TooltipImperativeHandle`](#tooltipimperativehandle)
101
- - [`TooltipManager`](#tooltipmanager)
102
- - [`useTooltipContext`](#usetooltipcontext)
103
- - [`TooltipTrigger`](#tooltiptrigger)
104
- - [**@fluentui/react-tooltip**](#fluentuireact-tooltip)
105
- - [`Tooltip`](#tooltip)
106
- - [`TooltipProvider`](#tooltipprovider)
116
+ Tooltip only supports a single child element, which can be either:
117
+
118
+ - A native element or component that supports DOM attributes (the child can't be a string, for example).
119
+ - A render function that takes the extra props to be added to the trigger element.
120
+ - It is allowed to have a tooltip without a child (trigger) element, in which case it _must_ have a target set via the `positioning` prop, and its visibility must be controlled with the `visible` prop.
107
121
 
108
122
  _A note about the terminology used for the elements that the tooltip is attached to:_
109
123
 
@@ -111,400 +125,250 @@ _A note about the terminology used for the elements that the tooltip is attached
111
125
  - _The **target** is the element that the tooltip is anchored to (and the arrow points to)._
112
126
  - _Almost always, these will both be the same element, but it is possible to specify them separately, so the tooltip can show up adjacent to a different element than the one that triggered it._
113
127
 
114
- ## @fluentui/react-tooltip-trigger
115
-
116
- `react-tooltip-trigger` is a lightweight package that allows any component to add a tooltip without taking a dependency on the full `react-tooltip` package. It provides the basic functionality to render the tooltip; but it will only work if the `TooltipProvider` context is present.
128
+ ## Types
117
129
 
118
- ### TooltipProps
130
+ ### `Tooltip`
119
131
 
120
- The `TooltipProps` interface is defined in `react-tooltip-trigger` so that components can specify the details of the tooltip without needing the full `react-tooltip` package.
132
+ From [Tooltip.types.tsx](https://github.com/microsoft/fluentui/blob/master/packages/react-tooltip/src/components/Tooltip/Tooltip.types.tsx) in `@fluentui/react-tooltip`:
121
133
 
122
134
  ```ts
123
- export type TooltipProps = ComponentProps &
124
- React.HTMLAttributes<HTMLElement> & {
125
- /**
126
- * How to position the tooltip relative to the target element. This is a "best effort" placement,
127
- * but the tooltip may be flipped to the other side if there is not enough room.
128
- *
129
- * @defaultvalue above
130
- */
131
- position?: 'above' | 'below' | 'before' | 'after';
132
-
133
- /**
134
- * How to align the tooltip along the edge of the target element.
135
- *
136
- * @defaultvalue center
137
- */
138
- align?: 'top' | 'bottom' | 'start' | 'end' | 'center';
139
-
140
- /**
141
- * Color variant with a subtle look
142
- */
143
- subtle?: boolean;
144
-
145
- /**
146
- * Do not render an arrow pointing to the target element
147
- */
148
- noArrow?: boolean;
149
-
150
- /**
151
- * Distance between the tooltip and the target element, in pixels
152
- *
153
- * @defaultvalue 4
154
- */
155
- offset?: number;
156
-
157
- /**
158
- * The arrow that points to the target element. This will be rendered by default unless `noArrow` is specified.
159
- */
160
- arrow?: ShorthandProps<React.HTMLAttributes<HTMLElement> & React.RefAttributes<HTMLElement>>;
161
-
162
- /**
163
- * Imperative handle to show and hide the tooltip
164
- */
165
- componentRef?: React.Ref<TooltipImperativeHandle>;
166
- };
167
- ```
168
-
169
- ### TooltipImperativeHandle
170
-
171
- The `TooltipImperativeHandle` is the imperative API used by `TooltipManager` to show and hide the tooltip.
172
-
173
- ```ts
174
- export interface TooltipImperativeHandle {
135
+ /**
136
+ * Slot properties for Tooltip
137
+ */
138
+ export type TooltipSlots = {
175
139
  /**
176
- * Show the tooltip, pointing to the target element
140
+ * The text or JSX content of the tooltip.
177
141
  */
178
- show: (target: HTMLElement) => void;
142
+ content: NonNullable<Slot<'div'>>;
143
+ };
179
144
 
145
+ /**
146
+ * Properties for Tooltip
147
+ */
148
+ export type TooltipProps = ComponentProps<TooltipSlots> & {
180
149
  /**
181
- * Hide the tooltip
150
+ * (Required) Specifies whether this tooltip is acting as the description or label of its trigger element.
151
+ *
152
+ * * `label` - The tooltip sets the trigger's aria-label or aria-labelledby attribute. This is useful for buttons
153
+ * displaying only an icon, for example.
154
+ * * `description` - The tooltip sets the trigger's aria-description or aria-describedby attribute.
155
+ * * `inaccessible` - No aria attributes are set on the trigger. This makes the tooltip's content inaccessible to
156
+ * screen readers, and should only be used if the tooltip's text is available by some other means.
182
157
  */
183
- hide: () => void;
158
+ relationship: 'label' | 'description' | 'inaccessible';
184
159
 
185
160
  /**
186
- * Get the root element of the tooltip
161
+ * The tooltip can have a single JSX child, or a render function that accepts TooltipTriggerProps.
162
+ *
163
+ * If no child is provided, the tooltip's target must be set with the `positioning` prop, and its
164
+ * visibility must be controlled with the `visible` prop.
187
165
  */
188
- getRoot: () => HTMLElement;
189
- }
190
- ```
191
-
192
- ### TooltipManager
193
-
194
- The `TooltipManager` is responsible for managing the visibiltiy of the tooltips, including ensuring that only one tooltip is visible at once, and handling the delay to show or hide a tooltip.
195
-
196
- This imperative interface is implemented by `TooltipProvider`, and used by `TooltipTrigger` to show and hide its tooltip based on events on the trigger element.
197
-
198
- ```ts
199
- /**
200
- * The tooltip manager is responsible for managing the visibiltiy of the tooltips,
201
- * including ensuring that only one tooltip is visible at once, and handling the
202
- * delay to show or hide a tooltip.
203
- *
204
- * This imperative interface is used by TooltipTrigger to show and hide its tooltip
205
- * based on events on the trigger element.
206
- */
207
- export interface TooltipManager {
208
- showTooltip: (args: ShowTooltipArgs, reason: TooltipTriggerReason) => void;
209
- hideTooltip: (trigger: HTMLElement, reason: TooltipTriggerReason) => void;
210
-
211
- hideAll: () => void;
212
-
213
- onPointerEnterTooltip: (tooltipRoot: HTMLElement) => void;
214
- onPointerLeaveTooltip: (tooltipRoot: HTMLElement) => void;
215
- }
216
-
217
- /**
218
- * The arguments to TooltipManager.showTooltip
219
- */
220
- export type ShowTooltipArgs = {
221
- tooltip: TooltipImperativeHandle;
222
- trigger: HTMLElement;
223
- target: HTMLElement;
224
- showDelay: number;
225
- hideDelay: number;
226
- onlyIfTruncated?: boolean;
227
- };
228
-
229
- /**
230
- * The source of the event that caused the tooltip to be shown or hidden
231
- */
232
- export type TooltipTriggerReason = 'focus' | 'pointer';
233
- ```
234
-
235
- ### useTooltipContext
236
-
237
- The tooltip hooks get the actual implementation of the TooltipManagerApi and Tooltip renderer via React context. By default this is `undefined`, and requires a `TooltipProvider` to provide the actual implementations.
238
-
239
- ```ts
240
- export type TooltipContext = {
241
- Tooltip: React.FC<TooltipProps & React.RefAttributes<HTMLElement>>;
242
- manager: TooltipManager | undefined;
243
- portalRoot: HTMLElement;
244
- };
166
+ children?:
167
+ | (React.ReactElement & { ref?: React.Ref<unknown> })
168
+ | ((props: TooltipTriggerProps) => React.ReactElement | null)
169
+ | null;
245
170
 
246
- export const internal__TooltipContext = React.createContext<TooltipContext>({
247
- // These default values are replaced by TooltipProvider
248
- Tooltip: () => null,
249
- manager: undefined,
250
- portalRoot: document.body,
251
- });
252
-
253
- export const useTooltipContext = () => React.useContext(internal__TooltipContext);
254
- ```
255
-
256
- ### TooltipTrigger
257
-
258
- `TooltipTrigger` allows tooltips to be added to any focusable component that doesn't have its own `tooltip` prop. It supports a single JSX child, and works by cloning the JSX element in order to add the same `onPointerDown`, etc. listeners that are added by `useTooltipSlot`.
259
-
260
- ```ts
261
- export type TooltipTriggerProps
262
- = Pick<TooltipProps, 'position' | 'align' | 'subtle' | 'noArrow' | 'offset'> & {
263
171
  /**
264
- * The child of TooltipTrigger is the element that triggers the tooltip. It will
265
- * have additional properties added, including events and aria properties.
266
- * Alternatively, children can be a render function that takes the props and adds
267
- * them to the appropriate elements.
172
+ * The tooltip's visual appearance.
173
+ * * `normal` - Uses the theme's background and text colors.
174
+ * * `inverted` - Higher contrast variant that uses the theme's inverted colors.
175
+ *
176
+ * @defaultvalue normal
268
177
  */
269
- children:
270
- | React.ReactElement<React.HTMLAttributes<HTMLElement>>
271
- | ((props: TooltipTriggerChildProps) => React.ReactNode);
178
+ appearance?: 'normal' | 'inverted';
272
179
 
273
180
  /**
274
- * The content of the tooltip.
181
+ * Render an arrow pointing to the target element
182
+ *
183
+ * @defaultvalue false
275
184
  */
276
- tooltip: ShorthandProps<TooltipProps>;
185
+ withArrow?: boolean;
277
186
 
278
187
  /**
279
- * Determines whether the tooltip is being used as the trigger's label or description.
280
- * This determines whether to set aria-describedby or aria-labelledby on the trigger element.
188
+ * Configure the positioning of the tooltip
281
189
  *
282
- * @defaultvalue description
190
+ * @defaultvalue above
283
191
  */
284
- type?: 'description' | 'label';
192
+ positioning?: PositioningShorthand;
285
193
 
286
194
  /**
287
- * Delay before the tooltip is shown, in milliseconds
195
+ * Control the tooltip's visibility programatically.
288
196
  *
289
- * @defaultvalue 250
197
+ * This can be used in conjunction with onVisibleChange to modify the tooltip's show and hide behavior.
198
+ *
199
+ * If not provided, the visibility will be controlled by the tooltip itself, based on hover and focus events on the
200
+ * trigger (child) element.
290
201
  */
291
- showDelay?: number;
202
+ visible?: boolean;
292
203
 
293
204
  /**
294
- * Delay before the tooltip is hidden, in milliseconds
295
- *
296
- * @defaultvalue 250
205
+ * Notification when the visibility of the tooltip is changing
297
206
  */
298
- hideDelay?: number;
207
+ onVisibleChange?: (
208
+ event: React.PointerEvent<HTMLElement> | React.FocusEvent<HTMLElement> | undefined,
209
+ data: OnVisibleChangeData,
210
+ ) => void;
299
211
 
300
212
  /**
301
- * Only show the tooltip if the target element's children are truncated (overflowing).
213
+ * Delay before the tooltip is shown, in milliseconds.
214
+ *
215
+ * @defaultvalue 250
302
216
  */
303
- onlyIfTruncated?: boolean;
217
+ showDelay?: number;
304
218
 
305
219
  /**
306
- * A ref to an element that the tooltip should be anchored to.
220
+ * Delay before the tooltip is hidden, in milliseconds.
307
221
  *
308
- * If not specified, the tooltip will point to the same element that triggered it, which is the common use case.
222
+ * @defaultvalue 250
309
223
  */
310
- targetRef?: React.RefObject<HTMLElement>;
311
- }
224
+ hideDelay?: number;
225
+ };
312
226
 
313
227
  /**
314
- * The props that are added to the child of the TooltipTrigger
228
+ * The properties that are added to the trigger of the Tooltip
315
229
  */
316
- export type TooltipTriggerChildProps = Pick<
230
+ export type TooltipTriggerProps = {
231
+ ref?: React.Ref<never>;
232
+ } & Pick<
317
233
  React.HTMLAttributes<HTMLElement>,
318
- 'onPointerEnter' | 'onPointerLeave' | 'onFocus' | 'onBlur' | 'aria-describedby' | 'aria-labelledby'
234
+ 'onPointerEnter' | 'onPointerLeave' | 'onFocus' | 'onBlur' | 'aria-describedby' | 'aria-labelledby' | 'aria-label'
319
235
  >;
320
236
 
321
237
  /**
322
- * Names of the shorthand properties in TooltipTriggerProps
323
- */
324
- export type TooltipTriggerShorthandProps = typeof tooltipTriggerShorthandProps[number];
325
-
326
- /**
327
- * Names of TooltipTriggerProps that have a default value in useTooltipTrigger
238
+ * Data for the Tooltip's onVisibleChange event.
328
239
  */
329
- export type TooltipTriggerDefaultedProps = 'showDelay' | 'hideDelay';
330
-
331
- export type TooltipTriggerState = RequiredProps<
332
- ResolvedShorthandProps<
333
- TooltipTriggerProps & {
334
- manager: TooltipManager | undefined;
335
- portalRoot: HTMLElement;
336
- tooltipRef: React.MutableRefObject<TooltipImperativeHandle | null>;
337
- },
338
- TooltipTriggerShorthandProps
339
- >,
340
- TooltipTriggerDefaultedProps
240
+ export type OnVisibleChangeData = {
241
+ visible: boolean;
242
+ };
341
243
  ```
342
244
 
343
- ## @fluentui/react-tooltip
344
-
345
- The `react-tooltip` package contains the bulk of the implementation of tooltips, including rendering, styling, positioning, lifetime management, etc.
245
+ ### `TooltipContext`
346
246
 
347
- ### Tooltip
247
+ The context is included at the app root on `FluentProvider` and is used by `Tooltip` to ensure that only one is visible at once.
348
248
 
349
- `Tooltip` renders the tooltip content itself, and the arrow that points to the target element. The tooltip renders in a React portal to avoid clipping.
350
-
351
- The `TooltipProps` interface is defined in the `react-tooltip-trigger` package.
249
+ From [TooltipContext.ts](https://github.com/microsoft/fluentui/blob/master/packages/react-shared-contexts/src/TooltipContext/TooltipContext.ts) in `@fluentui/react-shared-contexts`:
352
250
 
353
251
  ```ts
354
- import { TooltipProps } from '@fluentui/react-tooltip-trigger';
355
-
356
- export { TooltipProps };
357
-
358
252
  /**
359
- * Names of the shorthand properties in TooltipProps
253
+ * The context provided by TooltipProvider
360
254
  */
361
- export type TooltipShorthandProps = 'arrow';
255
+ export type TooltipContextType = {
256
+ /**
257
+ * When a tooltip is shown, it sets itself as the visibleTooltip.
258
+ * The next tooltip to become visible can use it to hide the previous tooltip immediately.
259
+ */
260
+ visibleTooltip?: {
261
+ hide: () => void;
262
+ };
263
+ };
362
264
 
363
265
  /**
364
- * Names of TooltipProps that have a default value in useTooltip
266
+ * Context shared by all of the tooltips in the app
365
267
  */
366
- export type TooltipDefaultedProps = 'position' | 'align' | 'offset';
367
-
368
- export type TooltipState = ComponentState<
369
- React.Ref<HTMLElement>,
370
- TooltipProps & {
371
- visible: boolean;
372
- },
373
- TooltipShorthandProps,
374
- TooltipDefaultedProps
375
- >;
376
- ```
377
-
378
- ### TooltipProvider
379
-
380
- `TooltipProvider` is responsible for providing the actual implementation of `TooltipManager` and `Tooltip`. It uses a React context to that contains those when it is included in the tree. This context will also be included into `FluentContext`.
381
-
382
- ```ts
383
- export type TooltipProviderProps = ComponentProps &
384
- React.HTMLAttributes<HTMLElement> & {
385
- // TooltipProvider has no additional props
386
- };
387
-
388
- export type TooltipProviderState = ComponentState<
389
- React.RefObject<HTMLElement>,
390
- TooltipProviderProps & {
391
- manager: TooltipManager;
392
- portalRoot: HTMLElement;
393
- }
394
- >;
268
+ export const TooltipContext = React.createContext<TooltipContextType>({});
395
269
  ```
396
270
 
397
271
  # Structure
398
272
 
399
- ## Public
273
+ ## Tooltip as a label
274
+
275
+ ### JSX tree
400
276
 
401
277
  ```tsx
402
- <TooltipProvider>
403
- <TooltipTrigger tooltip="Example tooltip">
404
- <a href="http://example.com">...</a>
405
- </TooltipTrigger>
406
- <TooltipTrigger tooltip="Button with a tooltip for a label" type="label">
407
- <button>...</button>
408
- </TooltipTrigger>
409
- </TooltipProvider>
278
+ <Tooltip content="Example" relationship="label">
279
+ <button>
280
+ <svg>...</svg>
281
+ </button>
282
+ </Tooltip>
410
283
  ```
411
284
 
412
- ## DOM
285
+ ### DOM
413
286
 
414
- In this example, the mouse is hovering over the first tooltip in the example above
287
+ Tooltip with `relationship="label"` is not rendered when it is not visible. Its content is used as the `aria-label` of the control. The Tooltip will be rendered once it is visible; see the next example for what the DOM structure looks like in that case.
415
288
 
416
- ```tsx
289
+ ```html
417
290
  <body>
418
- <div {/* TooltipProvider */}>
419
- <a href="http://example.com" aria-describedby="tooltip-1" onPointerEnter={...} onPointerLeave={...} onFocus={...} onBlur={...}>...</a>
420
- <button aria-labelledby="tooltip-2" onPointerEnter={...} onPointerLeave={...} onFocus={...} onBlur={...}>...</button>
421
-
422
- <div {/* Tooltip portal */}>
423
- <div role="tooltip" id="tooltip-1" {/* Tooltip */}>
424
- <div {/* Arrow */} />
425
- Example tooltip
426
- </div>
427
- <div role="tooltip" id="tooltip-2" class="... (display: none) ..." {/* Tooltip */}>
428
- <div {/* Arrow */} />
429
- Button with a tooltip for a label
430
- </div>
431
- </div>
291
+ <!-- App root -->
292
+ <div>
293
+ <button aria-label="Example" onPointerEnter="{...}" onPointerLeave="{...}" onFocus="{...}" onBlur="{...}">
294
+ <svg>...</svg>
295
+ </button>
432
296
  </div>
433
297
  </body>
434
298
  ```
435
299
 
436
- ## Internal
300
+ ## Tooltip as a description
437
301
 
438
- `TooltipProvider`:
302
+ ### JSX tree
439
303
 
440
304
  ```tsx
441
- <internal__TooltipContext.Provider
442
- value={{
443
- manager: state.manager,
444
- portalRoot: state.portalRoot,
445
- Tooltip,
446
- }}
447
- >
448
- {children}
449
- <slots.root {...rootProps} />
450
- </internal__TooltipContext.Provider>
305
+ <Tooltip content="Example description of the button" relationship="description" withArrow>
306
+ <button>The Button</button>
307
+ </Tooltip>
451
308
  ```
452
309
 
453
- `Tooltip`:
454
-
455
- ```tsx
456
- <slots.root {...slotProps.root}>
457
- <slots.arrow {...slotProps.arrow} />
458
- {state.children}
459
- </slots.root>
460
- ```
310
+ ### DOM
461
311
 
462
- `TooltipTrigger`:
312
+ Tooltip with `relationship="description"` is always rendered because it's used as the `aria-describedby` of the control, which always has to point to a valid DOM element even if it's not visible.
463
313
 
464
- Transparently renders children, without any wrapper element. Also renders the `Tooltip` itself in a portal provided by the `TooltipProvider`
314
+ ```html
315
+ <body>
316
+ <!-- App root -->
317
+ <div>
318
+ <button aria-describedby="tooltip-2" onPointerEnter="{...}" onPointerLeave="{...}" onFocus="{...}" onBlur="{...}">
319
+ The Button
320
+ </button>
321
+ </div>
465
322
 
466
- ```tsx
467
- <>
468
- {state.children}
469
- {ReactDOM.createPortal(<slots.tooltip {...slotProps.tooltip} />, state.portalRoot)}
470
- </>
323
+ <!-- Portal for Tooltip -->
324
+ <div>
325
+ <div role="tooltip" id="tooltip-2" class="{tooltip}">
326
+ <div class="{arrow}"></div>
327
+ Example description of the button
328
+ </div>
329
+ </div>
330
+ </body>
471
331
  ```
472
332
 
473
333
  # Migration
474
334
 
475
- See MIGRATION.md.
335
+ See [MIGRATION.md](./MIGRATION.md).
476
336
 
477
337
  # Behaviors
478
338
 
479
- - Visibility
480
-
481
- - There is only ever one tooltip visible at once.
482
- - The tooltip shows 250ms (by default) after the trigger element receives either mouse/pointer hover or keyboard focus, with the following exceptions:
483
- - If another tooltip is currently visible, the new tooltip will immediately replace the old one without any delay.
484
- - If `onlyIfTruncated` is true, then the tooltip will only show if the target element's content is overflowing.
485
- - The tooltip hides 250ms (by default) after the trigger element loses focus, with the following exceptions:
486
- - The tooltip will not hide if the pointer is hovering on the tooltip itself
487
- - If the tooltip was triggered by getting focus, then moving the mouse out of the element won't hide it (it'll hide when the element loses focus, or another tooltip is triggered).
488
- - The tooltip hides immediately when the user presses Esc.
339
+ ## Visibility
489
340
 
490
- - Placement
341
+ - The tooltip shows:
342
+ - After `showDelay` (250ms default) from when the mouse/pointer enters the trigger.
343
+ - After `showDelay` (250ms default) from when the trigger gets keyboard focus.
344
+ - _Immediately_ (ignoring `showDelay`) if it is triggered while another tooltip is currently visible.
345
+ - The tooltip hides:
346
+ - After `hideDelay` (250ms default) from when the mouse/pointer leaves BOTH the trigger AND the tooltip itself.
347
+ - _Immediately_ when the trigger loses keyboard focus.
348
+ - _Immediately_ when the ESC key is pressed.
349
+ - _Immediately_ when another tooltip is shown.
491
350
 
492
- - The tooltip is placed relative to its target element, based on the `position` and `align` properties.
493
- - The placement is handled by the react-positioning package, which uses PopperJS.
351
+ There is only ever one tooltip visible at once (coordinated using `TooltipContext`). If another tooltip is triggered while there's one currently visible, the previous one hides and the new one shows immediately, without delay.
494
352
 
495
- - Focus
353
+ ### Placement
496
354
 
497
- - Content within the tooltip is not focusable, and can't be interacted with directly by keyboard or mouse.
355
+ The tooltip is placed relative to its target element based on the `positioning` prop. The placement is handled by the `@fluentui/react-positioning` package, which uses PopperJS.
498
356
 
499
- - Screen readers
357
+ ### Focus
500
358
 
501
- - The tooltip is connected to the trigger element using `aria-describedby`, which should result in the screen reader reading the tooltip when shown.
359
+ Content within the tooltip is not focusable, and can't be interacted with directly by keyboard or mouse.
502
360
 
503
361
  # Accessibility
504
362
 
505
- - ARIA design pattern: [Tooltip Widget](https://www.w3.org/TR/wai-aria-practices-1.2/#tooltip)
506
- - The Tooltip root element will have `role="tooltip"`
507
- - Every tooltip will always be rendered in the DOM; though they will be hidden (including `aria-hidden="true"`) except for the one currently being shown, if any.
508
- - The trigger element (child of the `TooltipTrigger`) will have `aria-describedby` or `aria-labelledby` set to the tooltip element's ID. The ID will be generated automatically if not supplied.
363
+ - ARIA design pattern: https://www.w3.org/TR/wai-aria-practices-1.2/#tooltip.
364
+ - The Tooltip content element has `role="tooltip"`.
365
+ - If Tooltip has `relationship="label"` with simple string content:
366
+ - The content is set as the trigger's `aria-label`.
367
+ - The tooltip is only rendered while it is visible.
368
+ - If Tooltip has `relationship="label"` with JSX content, OR `relationship="description"` (string or JSX):
369
+ - The tooltip's ID is set as the trigger's `aria-labelledby` or `aria-describedby` (an ID is generated if not provided).
370
+ - The tooltip is always rendered, even while hidden.
371
+ - While hidden, the tooltip itself is styled with `display: none`.
509
372
  - The Tooltip itself can never receive focus.
510
373
  - The Tooltip should never contain focusable elements.
374
+ - The trigger for the Tooltip should be focusable.