Skip to content

Commit 25e92d1

Browse files
authored
Fix radio buttons (#104)
* fix radio buttons * add range input * types * correct types for controlled radio/checkboxes
1 parent 80cba97 commit 25e92d1

File tree

4 files changed

+96
-12
lines changed

4 files changed

+96
-12
lines changed

src/components/Radio.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,10 @@ const LabelWithTooltipWrapper = styled.div`
5454
type RadioProps = PropsWithChildren<
5555
Omit<InputHTMLAttributes<HTMLInputElement>, 'type'>>;
5656

57-
export const Radio = ({ children, disabled, ...props }: RadioProps & {tooltipText?: string}) => {
57+
export const Radio = ({ children, disabled, labelAs, ...props }: RadioProps & {
58+
tooltipText?: string;
59+
labelAs?: string;
60+
}) => {
5861

5962
const state = useTooltipTriggerState({delay: 0});
6063
const ref = React.useRef(null);
@@ -64,7 +67,7 @@ export const Radio = ({ children, disabled, ...props }: RadioProps & {tooltipTex
6467
return props.tooltipText
6568
? <div>
6669
<LabelWithTooltipWrapper>
67-
<StyledLabel ref={ref} isDisabled={disabled} aria-disabled={disabled} {...triggerProps}>
70+
<StyledLabel ref={ref} as={labelAs as any} isDisabled={disabled} aria-disabled={disabled} {...triggerProps}>
6871
<StyledInput type="radio" onFocus={() => state.open()} isDisabled={disabled} aria-disabled={disabled} {...props} />
6972
{children}
7073
{state.isOpen && (
@@ -73,7 +76,7 @@ export const Radio = ({ children, disabled, ...props }: RadioProps & {tooltipTex
7376
</StyledLabel>
7477
</LabelWithTooltipWrapper>
7578
</div>
76-
: <StyledLabel isDisabled={disabled} aria-disabled={disabled}>
79+
: <StyledLabel isDisabled={disabled} as={labelAs as any} aria-disabled={disabled}>
7780
<StyledInput type="radio" isDisabled={disabled} aria-disabled={disabled} {...props} />
7881
{children}
7982
</StyledLabel>;

src/components/forms/controlled/inputTypes.tsx

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ type MakeControlled<T extends React.ComponentType<any>> =
77
name: string;
88
emptyDisabledValue?: boolean;
99
};
10+
type MakeControlledCheckbox<T extends React.ComponentType<any>> =
11+
Omit<React.ComponentPropsWithoutRef<T>, 'checked'> & {
12+
name: string;
13+
emptyDisabledValue?: boolean;
14+
};
1015

1116
const useEmptyDisabledValue = (
1217
props: {disabled?: boolean; emptyDisabledValue?: boolean},
@@ -43,7 +48,7 @@ export const TextInput = (props: MakeControlled<typeof Uncontrolled.TextInput>)
4348
return <Uncontrolled.TextInput
4449
{...props}
4550
name={namespace + '.' + props.name}
46-
value={value || ''}
51+
value={(value ?? '').toString()}
4752
onChangeValue={onChangeValue}
4853
/>;
4954
};
@@ -67,10 +72,10 @@ export const TextArea = (props: MakeControlled<typeof Uncontrolled.TextArea>) =>
6772
/>;
6873
};
6974

70-
export const Radio = (props: MakeControlled<typeof Uncontrolled.Radio>) => {
75+
export const Radio = (props: MakeControlledCheckbox<typeof Uncontrolled.Radio>) => {
7176
const {data, namespace, setInput} = useFormHelpers();
7277

73-
const onChangeValue = (value: boolean | undefined) => {
78+
const onChangeValue = (value: string) => {
7479
props.onChangeValue?.(value);
7580
setInput.field(props.name)(value);
7681
};
@@ -85,7 +90,7 @@ export const Radio = (props: MakeControlled<typeof Uncontrolled.Radio>) => {
8590
/>;
8691
};
8792

88-
export const Checkbox = (props: MakeControlled<typeof Uncontrolled.Checkbox>) => {
93+
export const Checkbox = (props: MakeControlledCheckbox<typeof Uncontrolled.Checkbox>) => {
8994
const {data, namespace, setInput} = useFormHelpers();
9095

9196
const onChangeValue = (value: boolean | undefined) => {
@@ -171,3 +176,25 @@ export const File = (props: MakeControlled<typeof Uncontrolled.File>) => {
171176
onChangeValue={onChangeValue}
172177
/>;
173178
};
179+
180+
export const RangeInput = (props: MakeControlled<typeof Uncontrolled.RangeInput>) => {
181+
const {data, namespace, setInput} = useFormHelpers();
182+
183+
const onChangeValue = (value: number | undefined) => {
184+
props.onChangeValue?.(value);
185+
setInput.field(props.name)(value);
186+
};
187+
188+
const value = data[props.name];
189+
const numberValue = parseFloat((value ?? '').toString());
190+
const formValue = isNaN(numberValue) ? '' : numberValue;
191+
192+
useEmptyDisabledValue(props, formValue, onChangeValue);
193+
194+
return <Uncontrolled.RangeInput
195+
{...props}
196+
name={namespace + '.' + props.name}
197+
value={formValue}
198+
onChangeValue={onChangeValue}
199+
/>;
200+
}

src/components/forms/uncontrolled/inputType.stories.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import styled from "styled-components";
2-
import { Checkbox } from "./inputTypes";
2+
import { Checkbox, RangeInput } from "./inputTypes";
33

44

55
const CheckboxGroup = styled.div`
@@ -39,4 +39,11 @@ export const disabledCheckbox = () => <>
3939
{renderCheckboxes({error: [], disabled: true, label: 'Checkbox Label', variant: 'disabled', size: 1.6})}
4040
{renderCheckboxes({error: [], disabled: true, label: 'Checkbox Label', variant: 'disabled', size: 1.8})}
4141
{renderCheckboxes({error: [], disabled: true, label: 'Checkbox Label', variant: 'disabled', size: 2.0})}
42-
</>;
42+
</>;
43+
44+
export const slider = () => <>
45+
<RangeInput min={0} max={100} defaultValue={50} label="Label" name="name" help="Help text" />
46+
<RangeInput min={0} max={100} defaultValue={50} label="Label" name="name" help="Help text"
47+
labels={[{value: 0, label: '0%'}, {value: 50, label: '50%'}, {value: 100, label: '100%'}]}
48+
/>
49+
</>;

src/components/forms/uncontrolled/inputTypes.tsx

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ export const Select = ({
161161
* radio element
162162
*/
163163
type RadioProps = React.ComponentPropsWithoutRef<'input'> & InputProps & {
164-
onChangeValue?: (value: boolean | undefined) => void;
164+
onChangeValue?: (value: string) => void;
165165
wrapperProps?: React.ComponentPropsWithoutRef<'label'>;
166166
tooltipText?: string;
167167
};
@@ -182,8 +182,10 @@ export const Radio = ({
182182
}: RadioProps) => {
183183
return <FormInputWrapper {...wrapperProps}>
184184
<RadioLine>
185-
<StyledRadio {...props} onChange={e => {
186-
onChangeValue?.(!!e.target.checked);
185+
<StyledRadio {...props} labelAs="div" onChange={e => {
186+
if (e.target.checked) {
187+
onChangeValue?.(e.target.value);
188+
}
187189
props.onChange?.(e);
188190
}}>
189191
<RadioFormLabelText><RequiredIndicator show={props.required} />{label}</RadioFormLabelText>
@@ -271,3 +273,48 @@ export const File = ({label, help, wrapperProps, onChangeValue, uploader, value,
271273
<HelpText value={help} />
272274
</FormInputWrapper>;
273275
};
276+
277+
const RangeInputWrapper = styled(FormInputWrapper)`
278+
datalist {
279+
display: flex;
280+
justify-content: space-between;
281+
writing-mode:unset;
282+
flex-direction: row;
283+
padding: 0 1em;
284+
285+
option {
286+
width: 0;
287+
text-align: center;
288+
display: flex;
289+
justify-content: center;
290+
}
291+
}
292+
`;
293+
type RangeProps = React.ComponentPropsWithoutRef<'input'> & InputProps & {
294+
wrapperProps?: React.ComponentPropsWithoutRef<'label'>;
295+
onChangeValue?: (value: number | undefined) => void;
296+
labels?: {value: number; label: string}[];
297+
};
298+
export const RangeInput = ({label, help, wrapperProps, onChangeValue, labels, ...props}: RangeProps) => {
299+
const datalistId = React.useMemo(() => `datalist-${Math.random().toString(36).substring(2, 15)}`, []);
300+
301+
return <RangeInputWrapper {...wrapperProps}>
302+
<FormLabelText><RequiredIndicator show={props.required} />{label}:</FormLabelText>
303+
<input type="range" {...props}
304+
list={labels && labels.length > 0 ? datalistId : undefined}
305+
onChange={e => {
306+
const newValue = parseFloat(e.target.value);
307+
onChangeValue?.(isNaN(newValue) ? undefined : newValue);
308+
props.onChange?.(e);
309+
}}
310+
/>
311+
{labels && labels.length > 0 && (
312+
<datalist id={datalistId}>
313+
{labels.map(label => (
314+
<option key={label.value} value={label.value} label={label.label} />
315+
))}
316+
</datalist>
317+
)}
318+
<HelpText value={help} />
319+
</RangeInputWrapper>;
320+
}

0 commit comments

Comments
 (0)