Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: radio group #808

Closed
wants to merge 11 commits into from
2 changes: 1 addition & 1 deletion .storybook/main.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
var path = require("path");

module.exports = {
stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
stories: ["../src/**/*.stories.@(js|jsx|ts|tsx)"],

addons: [
"@storybook/addon-links",
Expand Down
53 changes: 53 additions & 0 deletions src/components/input-elements/RadioGroup/RadioGroup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"use client";
import { RadioGroup as HeadlessRadioGroup } from "@headlessui/react";
import { makeClassName, tremorTwMerge } from "lib";
import React from "react";

export interface RadioGroupProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange"> {
value?: string;
defaultValue?: string;
onValueChange?: (value: any) => void; //sev
disabled?: boolean;
name?: string;
}

const makeRadioGroupClassName = makeClassName("RadioGroup");

const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>((props, ref) => {
const {
defaultValue,
value,
onValueChange,
disabled = false,
name,
className,
children,
...other
} = props;

return (
<HeadlessRadioGroup
as="div"
ref={ref}
disabled={disabled}
name={name}
defaultValue={defaultValue}
value={value}
onChange={(e) => {
onValueChange?.(e);
}}
className={tremorTwMerge(
makeRadioGroupClassName("root"),
"disabled:opacity-50 flex flex-col gap-3",
className,
)}
{...other}
>
{children}
</HeadlessRadioGroup>
);
});

RadioGroup.displayName = "RadioGroup";

export default RadioGroup;
34 changes: 34 additions & 0 deletions src/components/input-elements/RadioGroup/RadioGroupLabel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { RadioGroup as HeadlessRadioGroup } from "@headlessui/react";
import { makeClassName, tremorTwMerge } from "lib";
import React from "react";

const makeRadioGroupLabelClassName = makeClassName("RadioGroupLabel");

const RadioGroupLabel = React.forwardRef<HTMLLabelElement, React.HTMLAttributes<HTMLLabelElement>>(
(props, ref) => {
const { children, className, ...other } = props;

return (
<HeadlessRadioGroup.Label
as="label"
ref={ref}
{...other}
className={tremorTwMerge(
makeRadioGroupLabelClassName("root"),
"cursor-pointer ui-disabled:cursor-not-allowed text-tremor-default",
// light
"text-tremor-content-emphasis",
// dark
"dark:text-dark-tremor-content-emphasis",
className,
)}
>
{children}
</HeadlessRadioGroup.Label>
);
},
);

RadioGroupLabel.displayName = "RadioGroupLabel";

export default RadioGroupLabel;
59 changes: 59 additions & 0 deletions src/components/input-elements/RadioGroup/RadioGroupOption.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"use client";
import { RadioGroup as HeadlessRadioGroup } from "@headlessui/react";
import { makeClassName, tremorTwMerge } from "lib";
import React from "react";

const makeRadioGroupOptionClassName = makeClassName("RadioGroupOption");

export interface RadioGroupOptionProps extends React.HTMLAttributes<HTMLDivElement> {
value?: string;
disabled?: boolean;
}

const RadioGroupOption = React.forwardRef<HTMLDivElement, RadioGroupOptionProps>((props, ref) => {
const { value, disabled, className, children, ...other } = props;

return (
<HeadlessRadioGroup.Option
as="div"
ref={ref}
value={value}
disabled={disabled}
className={tremorTwMerge(
makeRadioGroupOptionClassName("root"),
"flex flex-nowrap items-center gap-3 cursor-pointer",
"ui-disabled:opacity-50 ui-disabled:cursor-not-allowed",
className,
)}
{...other}
>
<div
className={tremorTwMerge(
"h-4 w-4 flex-shrink-0 rounded-tremor-full border flex items-center justify-center",
// light
"bg-tremor-background border-tremor-border",
"ui-checked:bg-tremor-brand ui-checked:border-transparent",
// dark
"dark:bg-dark-tremor-background dark:border-dark-tremor-border",
"dark:ui-checked:bg-tremor-brand dark:ui-checked:border-transparent",
)}
aria-hidden="true"
>
<span
className={tremorTwMerge(
"rounded-tremor-full w-1.5 h-1.5 ui-checked:scale-100 scale-0 transition-transform duration-150 ease-out",
//light
"bg-tremor-background",
// dark
"dark:bg-dark-tremor-background",
)}
/>
</div>
{children}
</HeadlessRadioGroup.Option>
);
});

RadioGroupOption.displayName = "RadioGroupOption";

export default RadioGroupOption;
5 changes: 5 additions & 0 deletions src/components/input-elements/RadioGroup/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export { default as RadioGroup } from "./RadioGroup";
export type { RadioGroupProps } from "./RadioGroup";
export { default as RadioGroupOption } from "./RadioGroupOption";
export type { RadioGroupOptionProps } from "./RadioGroupOption";
export { default as RadioGroupLabel } from "./RadioGroupLabel";
1 change: 1 addition & 0 deletions src/components/input-elements/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export * from "./Select";
export * from "./Tabs";
export * from "./TextInput";
export * from "./Switch";
export * from "./RadioGroup";
19 changes: 19 additions & 0 deletions src/stories/input-elements/RadioGroup.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { Meta, StoryObj } from "@storybook/react";
import { RadioGroup } from "components";
import { SimpleRadioGroup, SimpleRadioGroupControlled } from "./helpers/SimpleRadioGroup";

const meta: Meta<typeof RadioGroup> = {
title: "Components/Input/RadioGroup",
component: RadioGroup,
};

export default meta;
type Story = StoryObj<typeof RadioGroup>;

export const Default: Story = {
render: SimpleRadioGroup,
};

export const Controlled: Story = {
render: SimpleRadioGroupControlled,
};
32 changes: 32 additions & 0 deletions src/stories/input-elements/helpers/SimpleRadioGroup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { RadioGroup } from "@headlessui/react";
import { RadioGroupOption, RadioGroupLabel } from "components";
import React, { useState } from "react";

export function SimpleRadioGroup() {
return (
<RadioGroup defaultValue="startups">
<RadioGroupOption value="startups">
<RadioGroupLabel>Startups</RadioGroupLabel>
</RadioGroupOption>
<RadioGroupOption value="enterprise">
<RadioGroupLabel>Enterprise</RadioGroupLabel>
</RadioGroupOption>
</RadioGroup>
);
}

export function SimpleRadioGroupControlled() {
const [selected, setSelected] = useState("startup");
return (
<div className="space-y-6">
<RadioGroup value={selected} onValueChange={setSelected}>
<RadioGroupOption value="startups">
<RadioGroupLabel>Startups</RadioGroupLabel>
</RadioGroupOption>
<RadioGroupOption value="enterprise">
<RadioGroupLabel>Enterprise</RadioGroupLabel>
</RadioGroupOption>
</RadioGroup>
</div>
);
}
Loading