import {ExampleCodeBlock, Specifications, SymbolDoc} from '@workday/canvas-kit-docs';
import {InformationHighlight} from '@workday/canvas-kit-react/information-highlight';
import {StatusIndicator} from '@workday/canvas-kit-preview-react/status-indicator';
import CustomContent from './examples/CustomContent';
import Default from './examples/Default';
import DelayedTooltip from './examples/DelayedTooltip';
import DescribeType from './examples/DescribeType';
import DescriptionType from './examples/DescriptionType';
import Ellipsis from './examples/Ellipsis';
import LineClamp from './examples/LineClamp';
import Muted from './examples/Muted';
import Placements from './examples/Placements';
import UseTooltip from './examples/UseTooltip';


# Canvas Kit React Tooltips

A Tooltip component that renders information/text when the user hovers over an element. A tooltip is
used to label or describe an element. By default, a tooltip will label an element. This is useful
for buttons with icons. A tooltip can also be used to describe additional information about an
element

[Workday Design Reference](https://design.workday.com/components/popups/tooltips)

## Installation

```sh
yarn add @workday/canvas-kit-react
```

## Usage

This component follows the
[W3 Tooltip specification](https://www.w3.org/WAI/ARIA/apg/patterns/tooltip/). Tooltips are used to
label buttons with icons and provide additional context to elements.

### When to use tooltips

Use a tooltip when you want to display additional information for users to better understand the
purpose, context, or interaction.

### When not to use tooltips

When the visual text will be the exact same as what is visually displayed to the user without the
tooltip being visible

- Does this element need additional context or information?
  - No: Don't use a tooltip
  - Yes:
    - Is the tooltip text useful to screen reader users?
      - No: Use `type="muted"` which will not make the tooltip visible to screen reader users
      - Yes:
        - Is the tooltip text different from the visual text displayed to users?
          - No text: Use `type="label"` which will add `aria-label` like the icon example
          - Yes: Use `type="describe"` which will add `aria-describedby`
          - No: Don't use a tooltip

### Basic Example

Here is a basic example of a `TertiaryButton` that renders an icon using a tooltip to label the
icon. This labels the button for both sighted users and screen readers. A tooltip provides an
`aria-label` to child elements for the accessibility tree and a visual tooltip during mouse hover
and focus events.

<ExampleCodeBlock code={Default} />

### Describing an Element

<InformationHighlight variant={'caution'} className="sb-unstyled">
  <InformationHighlight.Icon />
  <InformationHighlight.Heading>
    {' '}
    Caution: Describe type has been deprecated{' '}
  </InformationHighlight.Heading>
  <InformationHighlight.Body>
    Assistive technology may ignore{' '}
    <StatusIndicator variant="gray">
      <StatusIndicator.Label cs={{textTransform: 'lowercase'}}>
        type="describe"
      </StatusIndicator.Label>
    </StatusIndicator>{' '}
    techniques based on verbosity settings. Please use{' '}
    <StatusIndicator cs={{textTransform: 'lowercase'}} variant="gray">
      <StatusIndicator.Label cs={{textTransform: 'lowercase'}}>
        type="description"
      </StatusIndicator.Label>
    </StatusIndicator>{' '}
    on Tooltips.
  </InformationHighlight.Body>
</InformationHighlight>

The default mode for a tooltip is to label content via `aria-label`. If a tooltip is meant to
provide ancillary information, the `type` can be set to `describe`. This will add `aria-describedby`
to the target element. This will allow screen reader users to hear the name of the control that is
being focused and the ancillary tooltip information.

<ExampleCodeBlock code={DescribeType} />

### Description of an Element

The default mode for a tooltip is to assign a name to the target element with an `aria-label`
string. If a tooltip is meant to provide ancillary information, the `type` can be set to `description`.
This will add `aria-description` strings to the target element instead. This variant is useful on
text buttons and other components that already have a label or name. Use this type instead of `describe` to ensure proper aria attributes are added to the dom regardless if the tooltip is visible.

> **Note:** If you use `description` type and want to pass `jsx`, it **must\* be inline and
> **not\*\* a component to ensure the inner text is properly read by voiceover.
>
> ```jsx
> // The text will be understood as: You must accept terms and conditions
> <Tooltip type="description" title={<span>You<i>must</i> accept terms and conditions</span>}/>
>
> // This will render a string including the html and will not be properly understood by voice over.
> const MyComponent = () => <span>You<i>must</i> accept terms and conditions</span>
> <Tool
> ```

<ExampleCodeBlock code={DescriptionType} />

### Muted Tooltips

If a tooltip does not need to be visible to screen reader users, or you handle accessibility of the
tooltip yourself, you can set the `type` to `muted`. This will not add any special ARIA attributes
to the target element.

<ExampleCodeBlock code={Muted} />

### Custom Content

A tooltip can contain HTML, but should not contain any focusable elements or semantically meaningful
formatting. The content will lose all semantic meaning when read by a screen reader. If complex
content or a focusable element is needed by your UI, a tooltip is not a good choice. Consider using
a dialog instead.

<ExampleCodeBlock code={CustomContent} />

### Delayed Tooltip

The default delay for showing and hiding a tooltip are 300ms and 100ms, respectively. You can
control the length of the delay by providing custom `showDelay` and `hideDelay` in ms.

<ExampleCodeBlock code={DelayedTooltip} />

### Placements

The tooltip allows for a `placement` configuration. The tooltip uses
[PopperJS](https://popper.js.org/) to position tooltips, so any valid PopperJS placement is valid
for tooltips.

<ExampleCodeBlock code={Placements} />

### Tooltips on overflowing content

The `OverflowTooltip` component can be applied to any element that has some type of overflow
applied, or has a child element that has overflow applied. The most common and widely supported type
of truncation is the ellipsis.

```css
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
```

**Note**: Text truncation should be avoided if possible. A user should not have to activate a
tooltip to access important content. If user-generated content is being truncated, the following
situation might occur which is a bad user experience. Consider the following list:

- Home Site A
- Home Site B
- Home Site C

If the list items get truncated via an ellipsis, this is what the user could see:

- Home Sit...
- Home Sit...
- Home Sit...

Here are suggestions to try to avoid truncation:

- Allow content to wrap instead
- Limit character count in admin interfaces if possible to avoid need for truncation
- Avoid fixed container sizes if possible to allow content to flow naturally

If truncation is required, here are a few guidelines to insure minimal impact on users:

- Only truncate text of elements that naturally receive focus.
  - Keyboard users can only activate tooltips with focus. Adding `tabindex=0` can give focus to
    non-focusable elements, but increase the amount of tab stops for keyboard users.
- Provide the full content elsewhere in the UI

Canvas Kit Buttons have this style applied to the text inside them. `OverflowTooltip` in combination
with a max-width can show a tooltip only when overflow is detected:

<ExampleCodeBlock code={Ellipsis} />

### Line Clamp

The `OverflowTooltip` can support various types of overflow. The component will first look for
`text-overflow: ellipsis` and `-webkit-line-clamp`, but will fall back to
`overflow: auto | scroll | clip | hidden`. These properties will be used to determine which
`element` is experiencing an overflow. Overflow detection is as follows where `element` is
determined by the above style properties:

```js
element.scrollWidth > element.clientWidth || element.scrollHeight > element.clientHeight;
```

Here's an example using the `-webkit-line-clamp` property (multi-line ellipsis which works in all
browsers):

<ExampleCodeBlock code={LineClamp} />

Other truncation techniques should be supported as well, even JavaScript ones as long as overflow is
triggered somehow and detectable differences in scroll size and client size.

### The UseTooltip Hook

The `Tooltip` component is a combination of the `TooltipContainer` (a styled element), `Popper`
(which uses PopperJS and the popup stack), the `useTooltip` hook and some behavior. If custom
behavior is required, these sub-components can be composed in a custom container element. This
example uses those parts directly while being functionally equivalent to the original basic example.

<ExampleCodeBlock code={UseTooltip} />

## Component API

<SymbolDoc name="Tooltip" fileName="/react/" />

## Specifications

<Specifications file="./cypress/component/Tooltip.spec.tsx" name="Tooltip" />