aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/molecules/modals/tooltip/tooltip.tsx
blob: 525900d6b848af32c62edf4e4d723505382b5171 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import { type FC, type MouseEventHandler, useRef } from 'react';
import { useIntl } from 'react-intl';
import { useOnClickOutside } from '../../../../utils/hooks';
import { Heading, Icon } from '../../../atoms';
import { HelpButton } from '../../buttons';
import { Modal, type ModalProps } from '../modal';
import styles from './tooltip.module.scss';

export type TooltipProps = Omit<ModalProps, 'heading'> & {
  /**
   * The tooltip direction when opening.
   *
   * @default "downwards"
   */
  direction?: 'downwards' | 'upwards';
  /**
   * The tooltip heading.
   */
  heading: string;
  /**
   * Should the tooltip be opened?
   *
   * @default false
   */
  isOpen?: boolean;
  /**
   * A callback function to trigger when clicking outside the modal.
   */
  onClickOutside?: () => void;
  /**
   * An event handler when clicking on the help button.
   */
  onToggle?: MouseEventHandler<HTMLButtonElement>;
};

/**
 * Tooltip component
 *
 * Render a button and a modal. Note: you should add a CSS rule
 * `position: relative;` on the consumer.
 */
export const Tooltip: FC<TooltipProps> = ({
  children,
  className = '',
  direction = 'downwards',
  heading,
  isOpen,
  onClickOutside,
  onToggle,
  ...props
}) => {
  const intl = useIntl();
  const helpLabel = intl.formatMessage({
    defaultMessage: 'Show help',
    description: 'Tooltip: show help label',
    id: '1Xgg7+',
  });
  const directionModifier =
    direction === 'upwards' ? 'tooltip--up' : 'tooltip--down';
  const visibilityModifier = isOpen ? 'tooltip--visible' : 'tooltip--hidden';
  const tooltipClass = `${styles.tooltip} ${styles[directionModifier]} ${styles[visibilityModifier]} ${className}`;
  const btnRef = useRef<HTMLButtonElement>(null);

  const closeModal = ({ target }: MouseEvent | FocusEvent) => {
    if (
      onClickOutside &&
      btnRef.current &&
      !btnRef.current.contains(target as Node)
    ) {
      onClickOutside();
    }
  };

  const modalRef = useOnClickOutside<HTMLDivElement>(closeModal);

  return (
    <>
      <Modal
        {...props}
        className={tooltipClass}
        heading={
          <Heading className={styles.heading} isFake level={6}>
            {heading}
          </Heading>
        }
        icon={<Icon aria-hidden shape="help" size="sm" />}
        kind="secondary"
        ref={modalRef}
      >
        {children}
      </Modal>
      <HelpButton
        className={styles.btn}
        isPressed={isOpen}
        label={helpLabel}
        onClick={onToggle}
        ref={btnRef}
      />
    </>
  );
};