aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/molecules/forms/fieldset.tsx
blob: 9f462478a89bdb0b557bc77cbf71315916f97b64 (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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import useClickOutside from '@utils/hooks/use-click-outside';
import {
  cloneElement,
  FC,
  ReactComponentElement,
  ReactNode,
  useRef,
  useState,
} from 'react';
import HelpButton from '../buttons/help-button';
import Tooltip from '../modals/tooltip';
import styles from './fieldset.module.scss';

export type FieldsetProps = {
  /**
   * Set additional classnames to the body wrapper.
   */
  bodyClassName?: string;
  /**
   * The fieldset body.
   */
  children: ReactNode | ReactNode[];
  /**
   * Set additional classnames to the fieldset wrapper.
   */
  className?: string;
  /**
   * The fieldset legend.
   */
  legend: string;
  /**
   * Set additional classnames to the legend.
   */
  legendClassName?: string;
  /**
   * The legend position. Default: stacked.
   */
  legendPosition?: 'inline' | 'stacked';
  /**
   * An accessible role. Default: group.
   */
  role?: 'group' | 'radiogroup' | 'presentation' | 'none';
  /**
   * An optional tooltip component.
   */
  Tooltip?: ReactComponentElement<typeof Tooltip>;
};

/**
 * Fieldset component
 *
 * Render a fieldset with a legend.
 */
const Fieldset: FC<FieldsetProps> = ({
  bodyClassName = '',
  children,
  className = '',
  legend,
  legendClassName = '',
  legendPosition = 'stacked',
  Tooltip: TooltipComponent,
  ...props
}) => {
  const [isTooltipOpened, setIsTooltipOpened] = useState<boolean>(false);
  const buttonRef = useRef<HTMLButtonElement>(null);
  const tooltipRef = useRef<HTMLDivElement>(null);
  const wrapperModifier = `wrapper--${legendPosition}`;
  const buttonModifier = isTooltipOpened ? styles['btn--activated'] : '';
  const legendModifier =
    TooltipComponent === undefined ? '' : 'legend--has-tooltip';
  const tooltipModifier = isTooltipOpened
    ? 'tooltip--visible'
    : 'tooltip--hidden';

  /**
   * Close the tooltip if the event target is outside.
   *
   * @param {EventTarget} target - The event target.
   */
  const closeTooltip = (target: EventTarget) => {
    if (buttonRef.current && !buttonRef.current.contains(target as Node))
      setIsTooltipOpened(false);
  };

  useClickOutside(
    tooltipRef,
    (target) => isTooltipOpened && closeTooltip(target)
  );

  return (
    <fieldset
      className={`${styles.wrapper} ${styles[wrapperModifier]} ${className}`}
      {...props}
    >
      <legend
        className={`${styles.legend} ${styles[legendModifier]} ${legendClassName}`}
      >
        {legend}
      </legend>
      {TooltipComponent && (
        <>
          <HelpButton
            className={`${styles.btn} ${buttonModifier}`}
            onClick={() => setIsTooltipOpened(!isTooltipOpened)}
            ref={buttonRef}
          />
          {cloneElement(TooltipComponent, {
            cloneClassName: `${styles.tooltip} ${styles[tooltipModifier]}`,
            ref: tooltipRef,
          })}
        </>
      )}
      <div className={`${styles.body} ${bodyClassName}`}>{children}</div>
    </fieldset>
  );
};

export default Fieldset;