Skip to content

Commit

Permalink
Merge pull request #2072 from cprussin/form-components
Browse files Browse the repository at this point in the history
feat(component-library): add first round of form components
  • Loading branch information
cprussin authored Oct 28, 2024
2 parents a43818d + 2ac2b4f commit b60a7cd
Show file tree
Hide file tree
Showing 11 changed files with 416 additions and 4 deletions.
8 changes: 4 additions & 4 deletions packages/component-library/.storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ export const decorators: Decorator[] = [
withRootClasses("font-sans antialiased", sans.variable),
withThemeByClassName({
themes: {
white: "light bg-white",
light: "light bg-beige-50",
dark: "dark bg-steel-800",
darker: "dark bg-steel-900",
white: "light bg-white text-steel-900",
light: "light bg-beige-100 text-steel-900",
dark: "dark bg-steel-800 text-steel-50",
darker: "dark bg-steel-900 text-steel-50",
},
defaultTheme: "light",
}),
Expand Down
37 changes: 37 additions & 0 deletions packages/component-library/src/Checkbox/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type { Meta, StoryObj } from "@storybook/react";

import { Checkbox as CheckboxComponent } from "./index.js";

const meta = {
component: CheckboxComponent,
argTypes: {
children: {
control: "text",
table: {
category: "Contents",
},
},
isDisabled: {
control: "boolean",
table: {
category: "State",
},
},
},
decorators: [
(Story) => (
<div className="max-w-sm">
<Story />
</div>
),
],
} satisfies Meta<typeof CheckboxComponent>;
export default meta;

export const Checkbox = {
args: {
children:
"By clicking here you agree that this is a checkbox and it's super duper checkboxy",
isDisabled: false,
},
} satisfies StoryObj<typeof CheckboxComponent>;
42 changes: 42 additions & 0 deletions packages/component-library/src/Checkbox/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import clsx from "clsx";
import type { ComponentProps } from "react";
import { Checkbox as BaseCheckbox } from "react-aria-components";

export const Checkbox = ({
children,
className,
...props
}: ComponentProps<typeof BaseCheckbox>) => (
<BaseCheckbox
className={clsx(
"group/checkbox inline-flex cursor-pointer flex-row gap-2 py-1 text-sm data-[disabled]:cursor-not-allowed",
className,
)}
{...props}
>
{(args) => (
<>
<div className="relative top-[0.0625rem] mx-1 size-4 flex-none">
<div className="size-full rounded border border-stone-300 bg-white outline-4 outline-violet-500/40 transition duration-100 group-data-[hovered]/checkbox:border-2 group-data-[disabled]/checkbox:border-none group-data-[hovered]/checkbox:border-stone-400 group-data-[pressed]/checkbox:border-stone-500 group-data-[disabled]/checkbox:bg-stone-200 group-data-[focus-visible]/checkbox:outline dark:border-steel-700 dark:bg-steel-800 dark:group-data-[hovered]/checkbox:border-steel-600 dark:group-data-[pressed]/checkbox:border-steel-500 dark:group-data-[disabled]/checkbox:bg-steel-600" />
<div className="absolute inset-0 grid place-content-center rounded bg-violet-500 stroke-white p-1 opacity-0 transition duration-100 group-data-[disabled]/checkbox:bg-transparent group-data-[disabled]/checkbox:stroke-stone-400 group-data-[selected]/checkbox:opacity-100 dark:bg-violet-600 dark:stroke-steel-950 dark:group-data-[disabled]/checkbox:stroke-steel-400">
<svg
className="w-full"
viewBox="0 0 8 6"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M1 3L2.76471 5L7 1"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</div>
<div className="pointer-events-none absolute -inset-1.5 -z-10 rounded-full bg-black/20 opacity-0 transition duration-100 group-data-[focus-visible]/checkbox:opacity-0 group-data-[hovered]/checkbox:opacity-50 group-data-[pressed]/checkbox:opacity-100 dark:bg-white/20" />
</div>
{typeof children === "function" ? children(args) : children}
</>
)}
</BaseCheckbox>
);
66 changes: 66 additions & 0 deletions packages/component-library/src/CheckboxGroup/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import type { Meta, StoryObj } from "@storybook/react";

import {
ORIENTATIONS,
CheckboxGroup as CheckboxGroupComponent,
} from "./index.js";
import { Checkbox } from "../Checkbox/index.js";

const meta = {
component: CheckboxGroupComponent,
argTypes: {
label: {
control: "text",
table: {
category: "Contents",
},
},
description: {
control: "text",
table: {
category: "Contents",
},
},
isDisabled: {
control: "boolean",
table: {
category: "State",
},
},
orientation: {
control: "inline-radio",
options: ORIENTATIONS,
table: {
category: "Layout",
},
},
},
decorators: [
(Story) => (
<div className="max-w-sm">
<Story />
</div>
),
],
render: (args) => (
<CheckboxGroupComponent {...args}>
<Checkbox value="one">
{
"By clicking here you agree that this is a checkbox and it's super duper checkboxy"
}
</Checkbox>
<Checkbox value="two">Second</Checkbox>
<Checkbox value="three">Third</Checkbox>
</CheckboxGroupComponent>
),
} satisfies Meta<typeof CheckboxGroupComponent>;
export default meta;

export const CheckboxGroup = {
args: {
label: "This is a checkbox group!",
description: "",
isDisabled: false,
orientation: "vertical",
},
} satisfies StoryObj<typeof CheckboxGroupComponent>;
44 changes: 44 additions & 0 deletions packages/component-library/src/CheckboxGroup/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import clsx from "clsx";
import type { ComponentProps } from "react";
import {
CheckboxGroup as BaseCheckboxGroup,
Label,
Text,
} from "react-aria-components";

export const ORIENTATIONS = ["vertical", "horizontal"] as const;

type CheckboxGroupProps = ComponentProps<typeof BaseCheckboxGroup> & {
label: ComponentProps<typeof Label>["children"];
description?: ComponentProps<typeof Text>["children"] | undefined;
orientation?: (typeof ORIENTATIONS)[number] | undefined;
};

export const CheckboxGroup = ({
children,
className,
label,
description,
orientation = "vertical",
...props
}: CheckboxGroupProps) => (
<BaseCheckboxGroup
data-orientation={orientation}
className={clsx("group/checkbox-group", className)}
{...props}
>
{(args) => (
<>
<Label className="mb-1 text-sm font-medium">{label}</Label>
<div className="flex group-data-[orientation=horizontal]/checkbox-group:flex-row group-data-[orientation=vertical]/checkbox-group:flex-col group-data-[orientation=horizontal]/checkbox-group:gap-6">
{typeof children === "function" ? children(args) : children}
</div>
{description && description !== "" && (
<Text slot="description" className="text-xs font-light">
{description}
</Text>
)}
</>
)}
</BaseCheckboxGroup>
);
43 changes: 43 additions & 0 deletions packages/component-library/src/Link/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type { Meta, StoryObj } from "@storybook/react";

import { Link as LinkComponent } from "./index.js";

const meta = {
component: LinkComponent,
argTypes: {
children: {
control: "text",
table: {
category: "Contents",
},
},
href: {
control: "text",
table: {
category: "Link",
},
},
target: {
control: "text",
table: {
category: "Link",
},
},
isDisabled: {
control: "boolean",
table: {
category: "State",
},
},
},
} satisfies Meta<typeof LinkComponent>;
export default meta;

export const Link = {
args: {
children: "Link",
href: "https://www.pyth.network",
target: "_blank",
isDisabled: false,
},
} satisfies StoryObj<typeof LinkComponent>;
16 changes: 16 additions & 0 deletions packages/component-library/src/Link/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import clsx from "clsx";
import type { ComponentProps } from "react";
import { Link as BaseLink } from "react-aria-components";

export const Link = ({
className,
...props
}: ComponentProps<typeof BaseLink>) => (
<BaseLink
className={clsx(
"underline outline-0 outline-offset-4 outline-inherit data-[disabled]:cursor-not-allowed data-[disabled]:text-stone-400 data-[disabled]:no-underline data-[focus-visible]:outline-2 hover:no-underline dark:data-[disabled]:text-steel-400",
className,
)}
{...props}
/>
);
38 changes: 38 additions & 0 deletions packages/component-library/src/Radio/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type { Meta, StoryObj } from "@storybook/react";
import { RadioGroup } from "react-aria-components";

import { Radio as RadioComponent } from "./index.js";

const meta = {
component: RadioComponent,
argTypes: {
children: {
control: "text",
table: {
category: "Contents",
},
},
isDisabled: {
control: "boolean",
table: {
category: "State",
},
},
},
decorators: [
(Story) => (
<RadioGroup className="max-w-sm">
<Story />
</RadioGroup>
),
],
} satisfies Meta<typeof RadioComponent>;
export default meta;

export const Radio = {
args: {
children:
"This is a radio button, check out how radioish it is and how it handles multiline labels",
isDisabled: false,
},
} satisfies StoryObj<typeof RadioComponent>;
28 changes: 28 additions & 0 deletions packages/component-library/src/Radio/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import clsx from "clsx";
import type { ComponentProps } from "react";
import { Radio as BaseRadio } from "react-aria-components";

export const Radio = ({
children,
className,
...props
}: ComponentProps<typeof BaseRadio>) => (
<BaseRadio
className={clsx(
"group/radio inline-flex cursor-pointer flex-row gap-2 py-1 text-sm data-[disabled]:cursor-not-allowed data-[selected]:cursor-default",
className,
)}
{...props}
>
{(args) => (
<>
<div className="relative top-[0.0625rem] mx-1 size-4 flex-none">
<div className="size-full rounded-full border border-stone-300 bg-white outline-4 outline-violet-500/40 transition duration-100 group-data-[hovered]/radio:border-2 group-data-[disabled]/radio:border-none group-data-[hovered]/radio:border-stone-400 group-data-[pressed]/radio:border-stone-500 group-data-[disabled]/radio:bg-stone-200 group-data-[focus-visible]/radio:outline dark:border-steel-700 dark:bg-steel-800 dark:group-data-[hovered]/radio:border-steel-600 dark:group-data-[pressed]/radio:border-steel-500 dark:group-data-[disabled]/radio:bg-steel-600" />
<div className="absolute inset-0 rounded-full border-[0.3rem] border-violet-500 bg-white opacity-0 transition duration-100 group-data-[disabled]/radio:border-transparent group-data-[disabled]/radio:bg-stone-400 group-data-[selected]/radio:opacity-100 dark:border-violet-600 dark:bg-steel-950 dark:group-data-[disabled]/radio:bg-steel-400" />
<div className="pointer-events-none absolute -inset-1.5 -z-10 rounded-full bg-black/20 opacity-0 transition duration-100 group-data-[focus-visible]/radio:opacity-0 group-data-[hovered]/radio:opacity-50 group-data-[pressed]/radio:opacity-100 group-data-[selected]/radio:opacity-0 dark:bg-white/20" />
</div>
{typeof children === "function" ? children(args) : children}
</>
)}
</BaseRadio>
);
62 changes: 62 additions & 0 deletions packages/component-library/src/RadioGroup/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import type { Meta, StoryObj } from "@storybook/react";

import { RadioGroup as RadioGroupComponent } from "./index.js";
import { Radio } from "../Radio/index.js";

const meta = {
component: RadioGroupComponent,
argTypes: {
label: {
control: "text",
table: {
category: "Contents",
},
},
description: {
control: "text",
table: {
category: "Contents",
},
},
isDisabled: {
control: "boolean",
table: {
category: "State",
},
},
orientation: {
control: "inline-radio",
options: ["vertical", "horizontal"],
table: {
category: "Layout",
},
},
},
decorators: [
(Story) => (
<div className="max-w-sm">
<Story />
</div>
),
],
render: (args) => (
<RadioGroupComponent {...args}>
<Radio value="one">
This is a radio button, check out how radioish it is and how it handles
multiline labels
</Radio>
<Radio value="two">Second</Radio>
<Radio value="three">Third</Radio>
</RadioGroupComponent>
),
} satisfies Meta<typeof RadioGroupComponent>;
export default meta;

export const RadioGroup = {
args: {
label: "This is a radio group!",
description: "",
isDisabled: false,
orientation: "vertical",
},
} satisfies StoryObj<typeof RadioGroupComponent>;
Loading

0 comments on commit b60a7cd

Please sign in to comment.