@spaced-out/ui-design-system
Version:
Sense UI components library
95 lines (85 loc) • 2.19 kB
Flow
// @flow strict
import * as React from 'react';
import type {GroupAlign} from '../../types/common';
import classify from '../../utils/classify';
import {BodySmall} from '../Text/Text';
import css from './RadioGroup.module.css';
/**
* Note:
* Do not wrap Radio in a label. For simplicity's sake, Radio uses an
* internal label tag to handle click delegation to the hidden input, and
* nesting labels is a bad idea.
*/
type ClassNames = $ReadOnly<{
wrapper?: string,
radioGroup?: string,
errorText?: string,
}>;
export type RadioGroupProps = {
align?: GroupAlign,
name: string,
value?: string,
onChange?: (value: string) => mixed,
children?: React.Node,
classNames?: ClassNames,
error?: boolean,
errorText?: string,
};
export const RadioGroup: React$AbstractComponent<
RadioGroupProps,
HTMLDivElement,
> = React.forwardRef<RadioGroupProps, HTMLDivElement>(
(
{
align = 'vertical',
name,
value,
onChange,
children,
classNames,
error = false,
errorText = '',
}: RadioGroupProps,
forwardRef,
): React.Node => {
const onChangeHandler = (value) => {
if (onChange) {
onChange(value);
}
};
const childrenWithProps = React.Children.map(children, (child) => {
if (React.isValidElement(child)) {
return React.cloneElement(child, {
name,
selectedValue: value,
onChange: onChangeHandler,
align,
error,
});
}
return child;
});
return (
<div className={classify(css.radioGroupContainer, classNames?.wrapper)}>
<div
className={classify(
{
[css.vertical]: align === 'vertical',
[css.horizontal]: align === 'horizontal-fixed',
[css.horizontalFluid]: align === 'horizontal-fluid',
},
classNames?.radioGroup,
)}
ref={forwardRef}
>
{childrenWithProps}
</div>
{error && (
<BodySmall color="danger" className={classNames?.errorText}>
{errorText}
</BodySmall>
)}
</div>
);
},
);