aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/molecules/forms/fieldset.tsx
blob: 7564d14b922f692f5cd5b5230a2b1777007670b3 (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 {
  cloneElement,
  FC,
  ReactComponentElement,
  ReactNode,
  useRef,
  useState,
} from 'react';
import { useOnClickOutside } from '../../../utils/hooks';
import { HelpButton } from '../buttons';
import { Tooltip } from '../modals';
import styles from './fieldset.module.scss';

export type FieldsetProps = {
  /**
   * Set additional classnames to the body wrapper.
   */
  bodyClassName?: string;
  /**
   * Set additional classnames to the help button.
   */
  buttonClassName?: 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.
 */
export const Fieldset: FC<FieldsetProps> = ({
  bodyClassName = '',
  buttonClassName = '',
  children,
  className = '',
  legend,
  legendClassName = '',
  legendPosition = 'stacked',
  Tooltip: TooltipComponent,
  ...props
}) => {
  const [isTooltipOpened, setIsTooltipOpened] = useState<boolean>(false);
  const buttonRef = useRef<HTMLButtonElement>(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 target is not the button.
   *
   * @param {Node} target - The event target.
   */
  const closeTooltip = (target: Node) => {
    if (buttonRef.current && !buttonRef.current.contains(target)) {
      setIsTooltipOpened(false);
    }
  };

  const tooltipRef = useOnClickOutside<HTMLDivElement>(closeTooltip);
  const fieldsetClass = `${styles.wrapper} ${styles[wrapperModifier]} ${className}`;
  const legendClass = `${styles.legend} ${styles[legendModifier]} ${legendClassName}`;

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