mirror of
https://github.com/detleph/components.git
synced 2026-09-07 07:56:14 +02:00
Add radio button groups
This commit is contained in:
@@ -1,23 +1,38 @@
|
|||||||
import styles from "./RadioButton.module.scss";
|
import styles from "./RadioButton.module.scss";
|
||||||
import { useState } from "react";
|
import { useContext, useState } from "react";
|
||||||
|
import { RadioButtonGroupContext } from "../group/RadioButtonGroup";
|
||||||
|
|
||||||
interface RadioButtonProps {
|
interface RadioButtonProps {
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
value?: string;
|
value: string;
|
||||||
onClick: React.HTMLAttributes<HTMLInputElement>["onChange"];
|
onClick?: React.HTMLAttributes<HTMLInputElement>["onChange"];
|
||||||
label: string;
|
label: string;
|
||||||
checked?: boolean;
|
checked?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const RadioButton: React.FC<RadioButtonProps> = ({ onClick, checked, disabled, label, value }) => {
|
const RadioButton: React.FC<RadioButtonProps> = ({ onClick, checked, disabled, label, value }) => {
|
||||||
return (
|
const parentGroupContext = useContext(RadioButtonGroupContext);
|
||||||
<>
|
|
||||||
<label className={styles.containerLabel}>
|
const localOnClick: React.FormEventHandler<HTMLInputElement> = (event) => {
|
||||||
<input className={styles.radioButton} type="radio" value={value} checked={checked} disabled={disabled} onClick={onClick} />
|
onClick?.(event);
|
||||||
{label}
|
parentGroupContext?.[2](value);
|
||||||
</label>
|
}
|
||||||
</>
|
|
||||||
)
|
return (
|
||||||
}
|
<>
|
||||||
|
<label className={styles.containerLabel}>
|
||||||
|
<input
|
||||||
|
className={styles.radioButton}
|
||||||
|
type="radio"
|
||||||
|
value={value}
|
||||||
|
checked={parentGroupContext ? parentGroupContext[0] === value : checked}
|
||||||
|
disabled={parentGroupContext?.[1] ?? disabled}
|
||||||
|
onClick={localOnClick}
|
||||||
|
/>
|
||||||
|
{label}
|
||||||
|
</label>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default RadioButton;
|
export default RadioButton;
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
|
||||||
|
type RadioButtonGroupContextType = [value: string | undefined, disabled: boolean, dispatch: (value: string) => unknown];
|
||||||
|
|
||||||
|
export const RadioButtonGroupContext = React.createContext<RadioButtonGroupContextType>(null!);
|
||||||
|
|
||||||
|
interface RadioButtonGroupProps {
|
||||||
|
disabled?: boolean;
|
||||||
|
value?: string; // For controlled groups
|
||||||
|
defaultValue?: string; // For uncontrolled group
|
||||||
|
onSelection?: (value: string) => unknown;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RadioButtonGroup: React.FC<RadioButtonGroupProps> = ({
|
||||||
|
defaultValue,
|
||||||
|
disabled,
|
||||||
|
onSelection,
|
||||||
|
value,
|
||||||
|
children,
|
||||||
|
}) => {
|
||||||
|
const [controlledValue, setControlledValue] = useState(defaultValue);
|
||||||
|
|
||||||
|
const dispatcher = (value: string) => {
|
||||||
|
setControlledValue(value);
|
||||||
|
onSelection?.(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<RadioButtonGroupContext.Provider value={[value ?? controlledValue, disabled ?? false, dispatcher]}>
|
||||||
|
{children}
|
||||||
|
</RadioButtonGroupContext.Provider>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RadioButtonGroup;
|
||||||
@@ -4,7 +4,11 @@ import { ComponentStory, ComponentMeta } from "@storybook/react";
|
|||||||
|
|
||||||
import RadioButton from "../components/radio/button/RadioButton";
|
import RadioButton from "../components/radio/button/RadioButton";
|
||||||
|
|
||||||
export default { title: "Radio button", component: RadioButton } as ComponentMeta<typeof RadioButton>;
|
export default {
|
||||||
|
title: "Radio button",
|
||||||
|
component: RadioButton,
|
||||||
|
argTypes: { onClick: { action: "click" } },
|
||||||
|
} as ComponentMeta<typeof RadioButton>;
|
||||||
|
|
||||||
const Template: ComponentStory<typeof RadioButton> = (args) => <RadioButton {...args} />;
|
const Template: ComponentStory<typeof RadioButton> = (args) => <RadioButton {...args} />;
|
||||||
|
|
||||||
@@ -14,5 +18,5 @@ DefaultInput.args = {
|
|||||||
value: "test",
|
value: "test",
|
||||||
label: "test",
|
label: "test",
|
||||||
checked: false,
|
checked: false,
|
||||||
disabled: false
|
disabled: false,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import { ComponentStory, ComponentMeta } from "@storybook/react";
|
||||||
|
|
||||||
|
import RadioButtonGroup from "../components/radio/group/RadioButtonGroup";
|
||||||
|
import RadioButton from "../components/radio/button/RadioButton";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Radio button group",
|
||||||
|
component: RadioButtonGroup,
|
||||||
|
argTypes: { onSelection: { action: "select" }, value: {type: "string"} },
|
||||||
|
} as ComponentMeta<typeof RadioButtonGroup>;
|
||||||
|
|
||||||
|
const Template: ComponentStory<typeof RadioButtonGroup> = (args) => <RadioButtonGroup {...args}>
|
||||||
|
<RadioButton value="one" label="First option" />
|
||||||
|
<RadioButton value="two" label="Second option" />
|
||||||
|
</RadioButtonGroup>;
|
||||||
|
|
||||||
|
export const DefaultInput = Template.bind({});
|
||||||
|
|
||||||
|
DefaultInput.args = {
|
||||||
|
value: undefined,
|
||||||
|
defaultValue: "two",
|
||||||
|
disabled: false,
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user