Skip to content

Commit d4bf06f

Browse files
authored
Tooltip docs (#1098)
1 parent 5aa7539 commit d4bf06f

File tree

7 files changed

+421
-3
lines changed

7 files changed

+421
-3
lines changed
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
<!-- Copyright 2020 Adobe. All rights reserved.
2+
This file is licensed to you under the Apache License, Version 2.0 (the "License");
3+
you may not use this file except in compliance with the License. You may obtain a copy
4+
of the License at http://www.apache.org/licenses/LICENSE-2.0
5+
Unless required by applicable law or agreed to in writing, software distributed under
6+
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
7+
OF ANY KIND, either express or implied. See the License for the specific language
8+
governing permissions and limitations under the License. -->
9+
10+
import {Layout} from '@react-spectrum/docs';
11+
export default Layout;
12+
13+
import docs from 'docs:@react-aria/tooltip';
14+
import statelyDocs from 'docs:@react-stately/tooltip';
15+
import {HeaderInfo, FunctionAPI, TypeContext, InterfaceType, TypeLink} from '@react-spectrum/docs';
16+
import packageData from '@react-aria/tooltip/package.json';
17+
18+
```jsx import
19+
import {useTooltipTrigger} from '@react-aria/tooltip';
20+
import {useTooltip} from '@react-aria/tooltip';
21+
```
22+
23+
---
24+
category: Overlays
25+
keywords: [tooltip, aria]
26+
---
27+
28+
# useTooltipTrigger
29+
30+
<p>{docs.exports.useTooltipTrigger.description}</p>
31+
32+
<HeaderInfo
33+
packageData={packageData}
34+
componentNames={['useTooltipTrigger', 'useTooltip']}
35+
sourceData={[
36+
{type: 'W3C', url: 'https://www.w3.org/TR/wai-aria-1.2/#tooltip'}
37+
]} />
38+
39+
## API
40+
41+
<FunctionAPI function={docs.exports.useTooltipTrigger} links={docs.links} />
42+
<FunctionAPI function={docs.exports.useTooltip} links={docs.links} />
43+
44+
## Features
45+
46+
The HTML `title` attribute can be used to create a tooltip, but it cannot be styled. Custom styled
47+
tooltips can be hard to build in an accessible way. `useTooltipTrigger` and `useTooltip` help build
48+
fully accessible tooltips that can be styled as needed.
49+
50+
* Keyboard focus management and cross browser normalization
51+
* Hover management and cross browser normalization
52+
* Labeling support for screen readers (aria-describedby)
53+
* Exposed as a tooltip to assistive technology via ARIA
54+
* Matches native tooltip behavior with delay on hover of first tooltip and no delay on subsequent tooltips.
55+
56+
## Anatomy
57+
58+
A tooltip consists of two parts, the trigger element and the tooltip itself.
59+
Users may reveal the tooltip by hovering or focusing the trigger.
60+
61+
`useTooltipTrigger` returns props to be spread onto its target trigger and the tooltip:
62+
63+
<TypeContext.Provider value={docs.links}>
64+
<InterfaceType properties={docs.links[docs.exports.useTooltipTrigger.return.id].properties} />
65+
</TypeContext.Provider>
66+
67+
`useTooltip` returns props to be spread onto the tooltip:
68+
69+
<TypeContext.Provider value={docs.links}>
70+
<InterfaceType properties={docs.links[docs.exports.useTooltip.return.id].properties} />
71+
</TypeContext.Provider>
72+
73+
Tooltip state is managed by the <TypeLink links={statelyDocs.links} type={statelyDocs.exports.useTooltipTriggerState} />
74+
hook in `@react-stately/tooltip`. The state object should be passed as an option to `useTooltipTrigger`.
75+
76+
## Example
77+
78+
This example implements a Tooltip that renders in an absolute position next to its target. The <TypeLink links={docs.links} type={docs.exports.useTooltip} /> hook applies the correct ARIA attributes to the tooltip, and `useTooltipTrigger` associates it to the trigger element.
79+
80+
Two instances of the example are rendered to demonstrate the behavior of the delay on hover. If you hover over the first button, the tooltip will be shown after a delay. Hovering over the second button shows the tooltip immediately. If you wait for a delay before hovering another element, the delay restarts.
81+
82+
```tsx example
83+
import {useTooltipTriggerState} from '@react-stately/tooltip';
84+
import {mergeProps} from '@react-aria/utils';
85+
86+
function Tooltip(props) {
87+
let {tooltipProps} = useTooltip(props);
88+
89+
return (
90+
<span
91+
style={{
92+
position: 'absolute',
93+
left: '5px',
94+
top: '100%',
95+
marginTop: '10px',
96+
backgroundColor: 'white',
97+
color: 'black',
98+
padding: '5px'
99+
}}
100+
{...mergeProps(props, tooltipProps)} >
101+
{props.children}
102+
</span>
103+
);
104+
}
105+
106+
function Example(props) {
107+
let state = useTooltipTriggerState(props);
108+
let ref = React.useRef();
109+
110+
// Get props for the trigger and its tooltip
111+
let {triggerProps, tooltipProps} = useTooltipTrigger(props, state, ref);
112+
113+
return (
114+
<span style={{position: 'relative'}}>
115+
<button ref={ref} {...triggerProps}>I have a tooltip</button>
116+
{state.isOpen && (
117+
<Tooltip {...tooltipProps} >
118+
And the tooltip tells you more information.
119+
</Tooltip>
120+
)}
121+
</span>
122+
);
123+
}
124+
125+
<Example />
126+
<Example />
127+
```
128+
129+
## Internationalization
130+
131+
### RTL
132+
133+
In right-to-left languages, tooltips should be mirrored across trigger. Ensure that your CSS and overlay positioning (if any) accounts for this.

packages/@react-aria/tooltip/src/useTooltip.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,15 @@ import {filterDOMProps, mergeProps} from '@react-aria/utils';
1515
import {HTMLAttributes} from 'react';
1616

1717
interface TooltipAria {
18+
/**
19+
* Props for the tooltip element.
20+
*/
1821
tooltipProps: HTMLAttributes<HTMLElement>
1922
}
2023

24+
/**
25+
* Provides the accessibility implementation for a Tooltip component.
26+
*/
2127
export function useTooltip(props: AriaTooltipProps): TooltipAria {
2228
let domProps = filterDOMProps(props, {labelable: true});
2329

packages/@react-aria/tooltip/src/useTooltipTrigger.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,21 @@ import {useFocusable} from '@react-aria/focus';
2020
import {useHover} from '@react-aria/interactions';
2121

2222
interface TooltipTriggerAria {
23+
/**
24+
* Props for the trigger element.
25+
*/
2326
triggerProps: HTMLAttributes<HTMLElement> & PressProps & HoverProps & FocusEvents,
27+
28+
/**
29+
* Props for the overlay container element.
30+
*/
2431
tooltipProps: HTMLAttributes<HTMLElement>
2532
}
2633

34+
/**
35+
* Provides the behavior and accessibility implementation for a tooltip trigger, e.g. a button
36+
* that shows a description when focused or hovered.
37+
*/
2738
export function useTooltipTrigger(props: TooltipTriggerProps, state: TooltipTriggerState, ref: RefObject<HTMLElement>) : TooltipTriggerAria {
2839
let {
2940
isDisabled
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
<!-- Copyright 2020 Adobe. All rights reserved.
2+
This file is licensed to you under the Apache License, Version 2.0 (the "License");
3+
you may not use this file except in compliance with the License. You may obtain a copy
4+
of the License at http://www.apache.org/licenses/LICENSE-2.0
5+
Unless required by applicable law or agreed to in writing, software distributed under
6+
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
7+
OF ANY KIND, either express or implied. See the License for the specific language
8+
governing permissions and limitations under the License. -->
9+
10+
import {Layout} from '@react-spectrum/docs';
11+
export default Layout;
12+
13+
import docs from 'docs:@react-spectrum/tooltip';
14+
import {HeaderInfo, PropTable} from '@react-spectrum/docs';
15+
import packageData from '@react-spectrum/tooltip/package.json';
16+
17+
```jsx import
18+
import {ActionButton} from '@react-spectrum/button';
19+
import Delete from '@spectrum-icons/workflow/Delete';
20+
import Edit from '@spectrum-icons/workflow/Edit';
21+
import {Flex} from '@react-spectrum/layout';
22+
import Question from '@spectrum-icons/workflow/Question';
23+
import Resize from '@spectrum-icons/workflow/Resize';
24+
import Save from '@spectrum-icons/workflow/SaveTo';
25+
import {Text} from '@react-spectrum/text';
26+
import ThumbUp from '@spectrum-icons/workflow/ThumbUp';
27+
import { Tooltip, TooltipTrigger } from '@react-spectrum/tooltip';
28+
```
29+
30+
---
31+
category: Overlays
32+
keywords: [tooltip]
33+
---
34+
35+
# Tooltip
36+
37+
<p>{docs.exports.Tooltip.description}</p>
38+
39+
<HeaderInfo
40+
packageData={packageData}
41+
componentNames={['Tooltip']}
42+
sourceData={[
43+
{type: 'Spectrum', url: 'https://spectrum.adobe.com/page/tooltip/'}
44+
]} />
45+
46+
## Examples
47+
```tsx example
48+
<TooltipTrigger>
49+
<ActionButton aria-label="Edit Name"><Edit /></ActionButton>
50+
<Tooltip>Change Name</Tooltip>
51+
</TooltipTrigger>
52+
```
53+
54+
## Content
55+
56+
Tooltips accept content as children. All content should be internationalized.
57+
58+
### Accessibility
59+
60+
Tooltip triggers must be focusable and hoverable in order to ensure that all users can activate them. When displayed, TooltipTrigger automatically associates the tooltip with the trigger element so that it is described by the tooltip content.
61+
62+
## Tooltip Delay
63+
64+
Tooltips should appear after a short delay when hovering the trigger, or instantly when using keyboard focus. This delay can be adjusted for hover.
65+
[View guidelines](https://spectrum.corp.adobe.com/page/tooltip/#Immediate-or-delayed-appearance)
66+
67+
```tsx example
68+
<TooltipTrigger delay={0}>
69+
<ActionButton aria-label="Save"><Save /></ActionButton>
70+
<Tooltip>Saving applies your new settings right away.</Tooltip>
71+
</TooltipTrigger>
72+
```
73+
74+
### Warmup / Cooldown
75+
76+
Tooltips have a warm up and cool down period, [see guidelines](https://spectrum.corp.adobe.com/page/tooltip/#Warmup-and-cooldown).
77+
Only one tooltip can be open at a time.
78+
```tsx example
79+
<Flex gap="size-200">
80+
<TooltipTrigger>
81+
<ActionButton>Hover me</ActionButton>
82+
<Tooltip>I come up after a delay.</Tooltip>
83+
</TooltipTrigger>
84+
<TooltipTrigger>
85+
<ActionButton>Then hover me</ActionButton>
86+
<Tooltip>If you did it quickly, I appear immediately.</Tooltip>
87+
</TooltipTrigger>
88+
</Flex>
89+
```
90+
91+
## Tooltip placement
92+
93+
Tooltips support a variety of placement options.
94+
95+
### Placement
96+
97+
The Tooltip's placement with respect to its trigger element can be adjusted using the `placement`
98+
prop. See the props table [below](#tooltiptrigger-props) for a full list of available placement combinations.
99+
100+
```tsx example
101+
<TooltipTrigger placement="end">
102+
<ActionButton aria-label="Foo">Placement</ActionButton>
103+
<Tooltip>In left-to-right, this is on the right. In right-to-left, this is on the left.</Tooltip>
104+
</TooltipTrigger>
105+
```
106+
107+
### Offset and cross offset
108+
109+
The Tooltip's offset with respect to its trigger can be adjusted using the `offset` and
110+
`crossOffset` props. The `offset` prop controls the spacing applied along the main axis between the element and its
111+
trigger whereas the `crossOffset` prop handles the spacing applied along the cross axis.
112+
113+
Below is a tooltip offset by an additional 50px above the trigger.
114+
```tsx example
115+
<TooltipTrigger offset={50}>
116+
<ActionButton aria-label="Offset from trigger">Offset</ActionButton>
117+
<Tooltip>This will shift up.</Tooltip>
118+
</TooltipTrigger>
119+
```
120+
Below is a tooltip cross offset by an additional 100px to the right of the trigger.
121+
```tsx example
122+
<TooltipTrigger crossOffset={100} placement="bottom">
123+
<ActionButton aria-label="Cross Offset from trigger">Cross Offset</ActionButton>
124+
<Tooltip>This will shift over to the right.</Tooltip>
125+
</TooltipTrigger>
126+
```
127+
128+
## Events
129+
130+
TooltipTrigger accepts an `onOpenChange` handler which is triggered whenever the Tooltip is shown or hidden.
131+
132+
The example below uses `onOpenChange` to update a separate element with the current open state of the
133+
Dialog.
134+
135+
```tsx example
136+
function Example() {
137+
let [state, setState] = React.useState(false);
138+
139+
return (
140+
<Flex alignItems="center" gap="size-100">
141+
<TooltipTrigger onOpenChange={(isOpen) => setState(isOpen)}>
142+
<ActionButton aria-label="Resize"><Resize /></ActionButton>
143+
<Tooltip>Resize text.</Tooltip>
144+
</TooltipTrigger>
145+
<Text>Current open state: {state.toString()}</Text>
146+
</Flex>
147+
);
148+
}
149+
```
150+
151+
## Props
152+
153+
### TooltipTrigger Props
154+
<PropTable component={docs.exports.TooltipTrigger} links={docs.links} />
155+
156+
### Tooltip Props
157+
<PropTable component={docs.exports.Tooltip} links={docs.links} />
158+
159+
## Visual options
160+
[View guidelines](https://spectrum.adobe.com/page/tooltip/#Table-of-options)
161+
162+
**Positive**
163+
```tsx example
164+
<TooltipTrigger>
165+
<ActionButton aria-label="Approve"><ThumbUp /></ActionButton>
166+
<Tooltip variant="positive" showIcon>Approve workflow.</Tooltip>
167+
</TooltipTrigger>
168+
```
169+
170+
**Information**
171+
```tsx example
172+
<TooltipTrigger>
173+
<ActionButton aria-label="Information"><Question /></ActionButton>
174+
<Tooltip variant="info" showIcon>More information menu.</Tooltip>
175+
</TooltipTrigger>
176+
```
177+
178+
**Negative**
179+
```tsx example
180+
<TooltipTrigger>
181+
<ActionButton aria-label="Danger Will Robinson"><Delete /></ActionButton>
182+
<Tooltip variant="negative" showIcon>Dangerous action.</Tooltip>
183+
</TooltipTrigger>
184+
```
185+
186+
## Options
187+
A TooltipTrigger can be disabled without disabling the trigger it displays on.
188+
189+
**isDisabled**
190+
```tsx example
191+
<TooltipTrigger isDisabled>
192+
<ActionButton aria-label="Danger Will Robinson" onPress={() => alert('pressed trigger')}><Delete /></ActionButton>
193+
<Tooltip variant="negative" showIcon>Dangerous action.</Tooltip>
194+
</TooltipTrigger>
195+
```

0 commit comments

Comments
 (0)