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>
);
};
|