@atlaskit/tooltip
Version:
A tooltip briefly describes an interactive element on mouse hover or keyboard focus.
227 lines (226 loc) • 11.1 kB
TypeScript
import { type ComponentType, type ReactNode } from 'react';
import type UIAnalyticsEvent from '@atlaskit/analytics-next/UIAnalyticsEvent';
import type { Placement } from '@atlaskit/popper/main';
import { type TooltipPrimitiveProps } from './tooltip-primitive';
export type PositionMouseOptions = 'mouse' | 'mouse-x' | 'mouse-y';
export type PositionTypeBase = Placement;
export type PositionType = PositionTypeBase | PositionMouseOptions;
export interface TriggerProps {
onMouseOver: (event: React.MouseEvent<HTMLElement>) => void;
onMouseOut: (event: React.MouseEvent<HTMLElement>) => void;
onMouseMove: ((event: React.MouseEvent<HTMLElement>) => void) | undefined;
/**
* Drives the deprecated `hideTooltipOnMouseDown`. With
* `platform-dst-top-layer-tooltip` it also records the press, which is what
* keeps the tooltip hidden after native light dismiss. Must be spread onto the
* trigger, or a pointer move inside the trigger re-shows a tooltip the browser
* just dismissed.
*/
onMouseDown: (event: React.MouseEvent<HTMLElement>) => void;
/**
* Drives the deprecated `hideTooltipOnClick`. With
* `platform-dst-top-layer-tooltip`, a pointer press has already hidden the
* tooltip (native light dismiss) by the time `click` fires. It still hides the
* tooltip on a click with no pointer press, for example Enter or Space.
*/
onClick: (event: React.MouseEvent<HTMLElement>) => void;
onFocus: (event: React.FocusEvent<HTMLElement>) => void;
onBlur: (event: React.FocusEvent<HTMLElement>) => void;
ref: (node: HTMLElement | null) => void;
'aria-describedby'?: string | undefined;
/**
* Set when `Tooltip` is given `testId`. Spread this onto your trigger element so
* `data-testid` reaches the rendered DOM. Required because `@atlaskit/button/new`
* (and other `Pressable`-backed primitives) overwrite `data-testid` from spread,
* so the legacy `(triggerProps as any)['data-testid']` workaround is silently
* absorbed by those consumers. A typed `testId` field flows through their own
* `testId` destructure instead.
*/
testId?: string;
}
export interface TooltipProps {
/**
* The content of the tooltip. It can be either a:
* 1. `ReactNode`
* 2. Function which returns a `ReactNode`
* The benefit of the second approach is that it allows you to consume the `update` render prop.
* This `update` function can be called to manually recalculate the position of the tooltip.
*
* This content will be rendered into two places:
* 1. Into the tooltip
* 2. Into a hidden element for screen readers (unless `isScreenReaderAnnouncementDisabled` is set to `true`)
*
*/
content: ReactNode | (({ update }: {
update?: () => void;
}) => ReactNode);
/**
* Extend `TooltipPrimitive` to create your own tooltip and pass it as component.
*
* Build it on `TooltipPrimitive` and forward the `ref`. With `platform-dst-top-layer-tooltip`
* on, the tooltip's top-layer host carries `role="tooltip"` and `TooltipPrimitive` defaults to
* `role="presentation"`, so:
* - don't set `role="tooltip"` on your own element, or the tooltip gets two tooltip roles
* - don't hide the content (for example `display: none`), or the host has a tooltip role with
* no visible name. To hide the tooltip, don't pass `content`.
*/
component?: ComponentType<TooltipPrimitiveProps> | React.ForwardRefExoticComponent<React.PropsWithoutRef<TooltipPrimitiveProps> & React.RefAttributes<HTMLDivElement>>;
/**
* Time in milliseconds to wait before showing and hiding the tooltip. Defaults to 300.
*/
delay?: number;
/**
* Adds `pointer-events: none` to the tooltip itself. Setting this to true will also prevent the tooltip from persisting when hovered.
*/
ignoreTooltipPointerEvents?: boolean;
/**
* Hide the tooltip when the click event is triggered. Use this when the tooltip should be hidden if `onClick` react synthetic event
* is triggered, which happens after `onMouseDown` event.
*
* @deprecated {@link https://hello.atlassian.net/browse/ENGHEALTH-TODO Internal documentation for deprecation (no external access)}
* TODO: replace ENGHEALTH-TODO with the real ticket.
* With `platform-dst-top-layer-tooltip`, a pointer press on the
* trigger closes the tooltip by default. Native `popover="hint"` light dismiss
* closes it on pointerup, before `click` fires. This prop then only has an
* effect on a click with no pointer press, for example Enter or Space on a
* focused trigger. Without the gate it still works as before.
*
* To keep the tooltip open on a press, use `hasNewContentOnTriggerClick`
* instead. Do not set both: this prop turns `hasNewContentOnTriggerClick`
* off. This prop will be removed in a future release.
*/
hideTooltipOnClick?: boolean;
/**
* Hide the tooltip when the mousedown event is triggered. This should be
* used when tooltip should be hidden if `onMouseDown` react synthetic event
* is triggered, which happens before `onClick` event.
*
* @deprecated {@link https://hello.atlassian.net/browse/ENGHEALTH-TODO Internal documentation for deprecation (no external access)}
* TODO: replace ENGHEALTH-TODO with the real ticket.
* With `platform-dst-top-layer-tooltip`, a press on the trigger
* closes the tooltip by default. Native `popover="hint"` light dismiss closes
* it on pointerup. This prop still works with the gate, but it only changes
* the timing: the tooltip closes on mousedown, so it does not show while the
* press is held. Either way the tooltip stays hidden until the trigger is
* re-entered or blurred. Without the gate it still works as before.
*
* To keep the tooltip open on a press, use `hasNewContentOnTriggerClick`
* instead. Do not set both: this prop turns `hasNewContentOnTriggerClick`
* off. This prop will be removed in a future release.
*/
hideTooltipOnMouseDown?: boolean;
/**
* Set this when a press on the trigger changes `content`, for example "Copy"
* to "Copied!". The tooltip then stays open when the trigger is pressed, so
* the new content is visible.
*
* Only has an effect with `platform-dst-top-layer-tooltip`. Without the gate
* the tooltip already stays open on a press. With the gate, a press normally
* closes the tooltip until the pointer re-enters the trigger.
*
* The tooltip still closes on pointer leave, blur, scroll and Escape.
*
* Do not use it with the deprecated `hideTooltipOnClick` or
* `hideTooltipOnMouseDown`. If you set one of them, it wins and this prop has
* no effect.
*/
hasNewContentOnTriggerClick?: boolean;
/**
* Where the tooltip should appear relative to the mouse pointer.
* Only use this when the `position` prop is set to `"mouse"`, `"mouse-y"`, or `"mouse-x"`.
* When interacting with the target element using a keyboard, it will use this position against the target element instead.
*/
mousePosition?: PositionTypeBase;
/**
* Whether or not the tooltip can be displayed. Once a tooltip
* is scheduled to be displayed, or is already displayed, it will
* continue to be shown.
*
* @description
*
* `canAppear()` is called in response to user events, and
* not during the rendering of components.
*
*/
canAppear?: () => boolean;
/**
* By default tooltip content will be duplicated into a hidden element so
* it can be read out by a screen reader. Sometimes this is not ideal as
* it can result in the same content be announced twice. For those situations,
* you can leverage this prop to disable the duplicate hidden text.
*/
isScreenReaderAnnouncementDisabled?: boolean;
/**
* Function to be called when the tooltip will be shown. It's called when the
* tooltip begins to animate in.
*/
onShow?: (analyticsEvent: UIAnalyticsEvent) => void;
/**
* Function to be called when the tooltip will be hidden. It's called after the
* delay, when the tooltip begins to animate out.
*/
onHide?: (analyticsEvent: UIAnalyticsEvent) => void;
/**
* Where the tooltip should appear relative to its target.
* If set to `"mouse"`, the tooltip will display next to the mouse pointer instead.
* If set to `"mouse-y"`, the tooltip will use the mouse Y coordinate but the target X coordinate.
* If set to `"mouse-x"`, the tooltip will use the mouse X coordinate but the target Y coordinate.
* Make sure to utilize the `mousePosition` if you want to customize where the tooltip will show in relation to the mouse.
*/
position?: PositionType;
/**
* Replace the wrapping element. This accepts the name of a html tag which will
* be used to wrap the element.
* If you provide a component, it needs to support a ref prop which is used by popper for positioning.
*/
tag?: keyof JSX.IntrinsicElements | React.ComponentType<React.AllHTMLAttributes<HTMLElement> & {
ref: React.Ref<HTMLElement>;
}> | React.ForwardRefExoticComponent<React.PropsWithoutRef<React.AllHTMLAttributes<HTMLElement>> & React.RefAttributes<HTMLElement>>;
/**
* Use this to show only one line of text, and truncate the text when it's too long.
*
* We no longer support truncating text in the tooltip as it's inaccessible, and this prop will be removed in a future release.
*
* @deprecated
*/
truncate?: boolean;
/**
* Elements to be wrapped by the tooltip.
* It can be either a:
* 1. `ReactNode`
* 2. Function which returns a `ReactNode`
*/
children: ReactNode | ((props: TriggerProps) => ReactNode);
/**
* A `testId` prop is provided for specified elements, which is a unique
* string that appears as a data attribute `data-testid` in the rendered code,
* serving as a hook for automated tests.
*/
testId?: string;
/**
* Analytics context metadata.
*/
analyticsContext?: Record<string, any>;
/**
* Use this to define the strategy of popper.
*/
strategy?: 'absolute' | 'fixed' | undefined;
/**
* Display a keyboard shortcut in the tooltip.
*
* Keys will be displayed as individual keyboard key segments after the tooltip content.
*/
shortcut?: string[];
/**
* When set to true, the tooltip will always use the fade-in animation
* and never use the show-immediate behavior, even when another tooltip
* is already visible.
*/
shouldAlwaysFadeIn?: boolean;
/**
* When enabled, the tooltip will be rendered as a sibling to the trigger element instead of being portaled to the document body.
*
* This is an experimental prop and may be removed at any time. It is not yet intended for public use.
*/
shouldRenderToParent?: boolean;
}