diff --git a/pkgs/clan-app/ui/src/components/v2/Form/Checkbox.stories.tsx b/pkgs/clan-app/ui/src/components/v2/Form/Checkbox.stories.tsx
index 4685a5e29..191c44e0c 100644
--- a/pkgs/clan-app/ui/src/components/v2/Form/Checkbox.stories.tsx
+++ b/pkgs/clan-app/ui/src/components/v2/Form/Checkbox.stories.tsx
@@ -2,7 +2,7 @@ import meta, { Story } from "./TextField.stories";
const checkboxMeta = {
...meta,
- title: "Components/Form/Checkbox",
+ title: "Components/Form/Fields/Checkbox",
};
export default checkboxMeta;
diff --git a/pkgs/clan-app/ui/src/components/v2/Form/Field.tsx b/pkgs/clan-app/ui/src/components/v2/Form/Field.tsx
index 2e762ee23..cebee474c 100644
--- a/pkgs/clan-app/ui/src/components/v2/Form/Field.tsx
+++ b/pkgs/clan-app/ui/src/components/v2/Form/Field.tsx
@@ -17,7 +17,7 @@ import { Tooltip as KTooltip } from "@kobalte/core/tooltip";
import "./Field.css";
type Size = "default" | "s";
-type Orientation = "horizontal" | "vertical";
+export type Orientation = "horizontal" | "vertical";
type FieldType = "text" | "textarea" | "checkbox";
export interface TextFieldProps {
diff --git a/pkgs/clan-app/ui/src/components/v2/Form/Fieldset.css b/pkgs/clan-app/ui/src/components/v2/Form/Fieldset.css
new file mode 100644
index 000000000..f0a3cd5a7
--- /dev/null
+++ b/pkgs/clan-app/ui/src/components/v2/Form/Fieldset.css
@@ -0,0 +1,22 @@
+fieldset {
+ @apply flex flex-col w-full;
+
+ legend {
+ @apply mb-2.5 w-full;
+ }
+
+ div.fields {
+ @apply flex flex-col gap-4 w-full rounded-md;
+ @apply px-4 py-5 bg-def-2;
+ }
+
+ div.error {
+ @apply w-full;
+ }
+
+ &.inverted {
+ div.fields {
+ @apply bg-inv-2;
+ }
+ }
+}
diff --git a/pkgs/clan-app/ui/src/components/v2/Form/Fieldset.stories.tsx b/pkgs/clan-app/ui/src/components/v2/Form/Fieldset.stories.tsx
new file mode 100644
index 000000000..632361e1d
--- /dev/null
+++ b/pkgs/clan-app/ui/src/components/v2/Form/Fieldset.stories.tsx
@@ -0,0 +1,119 @@
+import type { Meta, StoryContext, StoryObj } from "@kachurun/storybook-solid";
+import { Fieldset, FieldsetProps } from "@/src/components/v2/Form/Fieldset";
+import cx from "classnames";
+
+const FieldsetExamples = (props: FieldsetProps) => (
+
+
+
+
+);
+
+const meta = {
+ title: "Components/Form/Fieldset",
+ component: FieldsetExamples,
+ decorators: [
+ (Story: StoryObj, context: StoryContext) => {
+ return (
+
+
+
+ );
+ },
+ ],
+} satisfies Meta;
+
+export default meta;
+
+export type Story = StoryObj;
+
+export const Default: Story = {
+ args: {
+ legend: "Signup",
+ fields: [
+ {
+ type: "text",
+ label: "First Name",
+ required: true,
+ control: { placeholder: "Ron" },
+ },
+ {
+ type: "text",
+ label: "Last Name",
+ required: true,
+ control: { placeholder: "Burgundy" },
+ },
+ {
+ type: "textarea",
+ label: "Bio",
+ control: { placeholder: "Tell us a bit about yourself", rows: 8 },
+ },
+ {
+ type: "checkbox",
+ label: "Accept Terms & Conditions",
+ required: true,
+ },
+ ],
+ },
+};
+
+export const Horizontal: Story = {
+ args: {
+ ...Default.args,
+ orientation: "horizontal",
+ },
+};
+
+export const Vertical: Story = {
+ args: {
+ ...Default.args,
+ orientation: "vertical",
+ },
+};
+
+export const Disabled: Story = {
+ args: {
+ ...Default.args,
+ disabled: true,
+ },
+};
+
+export const Error: Story = {
+ args: {
+ legend: "Signup",
+ error: "You must enter a First Name",
+ fields: [
+ {
+ type: "text",
+ label: "First Name",
+ required: true,
+ invalid: true,
+ control: { placeholder: "Ron" },
+ },
+ {
+ type: "text",
+ label: "Last Name",
+ required: true,
+ invalid: true,
+ control: { placeholder: "Burgundy" },
+ },
+ {
+ type: "textarea",
+ label: "Bio",
+ control: { placeholder: "Tell us a bit about yourself", rows: 8 },
+ },
+ {
+ type: "checkbox",
+ label: "Accept Terms & Conditions",
+ invalid: true,
+ required: true,
+ },
+ ],
+ },
+};
diff --git a/pkgs/clan-app/ui/src/components/v2/Form/Fieldset.tsx b/pkgs/clan-app/ui/src/components/v2/Form/Fieldset.tsx
new file mode 100644
index 000000000..632f5b6ac
--- /dev/null
+++ b/pkgs/clan-app/ui/src/components/v2/Form/Fieldset.tsx
@@ -0,0 +1,67 @@
+import "./Fieldset.css";
+import { Field, FieldProps, Orientation } from "./Field";
+import { For } from "solid-js";
+import cx from "classnames";
+import { Typography } from "@/src/components/v2/Typography/Typography";
+
+export interface FieldsetProps {
+ legend: string;
+ fields: FieldProps[];
+ inverted?: boolean;
+ disabled?: boolean;
+ orientation?: Orientation;
+ error?: string;
+}
+
+export const Fieldset = (props: FieldsetProps) => {
+ const orientation = () => props.orientation || "vertical";
+
+ return (
+
+ );
+};
diff --git a/pkgs/clan-app/ui/src/components/v2/Form/TextArea.stories.tsx b/pkgs/clan-app/ui/src/components/v2/Form/TextArea.stories.tsx
index cb87a9586..f7c9ed997 100644
--- a/pkgs/clan-app/ui/src/components/v2/Form/TextArea.stories.tsx
+++ b/pkgs/clan-app/ui/src/components/v2/Form/TextArea.stories.tsx
@@ -1,8 +1,8 @@
-import meta, { Icon, Story } from "./TextField.stories";
+import meta, { Story } from "./TextField.stories";
const textAreaMeta = {
...meta,
- title: "Components/Form/TextArea",
+ title: "Components/Form/Fields/TextArea",
};
export default textAreaMeta;
diff --git a/pkgs/clan-app/ui/src/components/v2/Form/TextField.stories.tsx b/pkgs/clan-app/ui/src/components/v2/Form/TextField.stories.tsx
index f4ca52ed6..bb2924e97 100644
--- a/pkgs/clan-app/ui/src/components/v2/Form/TextField.stories.tsx
+++ b/pkgs/clan-app/ui/src/components/v2/Form/TextField.stories.tsx
@@ -24,7 +24,7 @@ const FieldExamples = (props: FieldProps) => (
);
const meta = {
- title: "Components/Form/TextField",
+ title: "Components/Form/Fields/TextField",
component: FieldExamples,
decorators: [
(Story: StoryObj, context: StoryContext) => {
diff --git a/pkgs/clan-app/ui/src/components/v2/Typography/Typography.tsx b/pkgs/clan-app/ui/src/components/v2/Typography/Typography.tsx
index 6026b2c7b..dfb95f572 100644
--- a/pkgs/clan-app/ui/src/components/v2/Typography/Typography.tsx
+++ b/pkgs/clan-app/ui/src/components/v2/Typography/Typography.tsx
@@ -8,6 +8,7 @@ export type Tag = "span" | "p" | "h1" | "h2" | "h3" | "h4" | "div";
export type Hierarchy = "body" | "title" | "headline" | "label" | "teaser";
export type Weight = "normal" | "medium" | "bold";
export type Family = "regular" | "condensed" | "mono";
+export type Transform = "uppercase" | "lowercase" | "capitalize";
// type Size = "default" | "xs" | "s" | "m" | "l";
interface SizeForHierarchy {
@@ -94,6 +95,7 @@ interface _TypographyProps {
inverted?: boolean;
tag?: Tag;
class?: string;
+ transform?: Transform;
}
export const Typography = (props: _TypographyProps) => {
@@ -113,6 +115,7 @@ export const Typography = (props: _TypographyProps) => {
weight(),
size(),
color(),
+ props.transform,
props.class,
)}
component={props.tag || "span"}