Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions packages/react/src/patterns/F0Form/F0Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ function F0FormPerSection<T extends F0PerSectionSchema>(
} = props

const showSectionsSidepanel = styling?.showSectionsSidepanel ?? false
const noPadding = styling?.noPadding ?? false

const sectionIds = useMemo(() => Object.keys(schema), [schema])

Expand Down Expand Up @@ -218,7 +219,11 @@ function F0FormPerSection<T extends F0PerSectionSchema>(
)
}

return <div className="flex justify-center p-4">{content}</div>
return (
<div className={cn("flex justify-center", !noPadding && "p-4")}>
{content}
</div>
)
}

/**
Expand Down Expand Up @@ -538,6 +543,7 @@ function F0FormSingleSchema<TSchema extends F0FormSchema>(

// Resolve styling configuration
const showSectionsSidepanel = styling?.showSectionsSidepanel ?? false
const noPadding = styling?.noPadding ?? false

// Resolve submit type from config
const isActionBar = submitConfig?.type === "action-bar"
Expand Down Expand Up @@ -1167,7 +1173,9 @@ function F0FormSingleSchema<TSchema extends F0FormSchema>(
</div>
</div>
) : (
<div className="flex justify-center p-4">{formContent}</div>
<div className={cn("flex justify-center", !noPadding && "p-4")}>
{formContent}
</div>
)}

{!hideActionBar && (
Expand Down
17 changes: 17 additions & 0 deletions packages/react/src/patterns/F0Form/__stories__/F0Form.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,23 @@ Use `sections` to split a long form into named groups with independent submit bu

<Canvas of={Stories.WithSectionsSidepanel} />

## Styling

The optional `styling` config controls the form layout:

- `showSectionsSidepanel` — render a sticky table-of-contents sidebar for the form's sections (see the example above).
- `noPadding` — remove the default `p-4` padding around the form content. Use it when embedding the form flush inside a host surface (e.g. an AI canvas panel) that already provides its own spacing. Defaults to `false`, so existing forms are unaffected.

```tsx
<F0Form
name="embedded"
schema={schema}
defaultValues={defaultValues}
onSubmit={onSubmit}
styling={{ noPadding: true }}
/>
```

## Conditional rendering

Use `renderIf` to show or hide fields based on the values of other fields.
Expand Down
66 changes: 66 additions & 0 deletions packages/react/src/patterns/F0Form/__tests__/F0Form.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3662,6 +3662,72 @@ describe("F0Form sections sidepanel scroll", () => {
expect(stickyElement).toBeInTheDocument()
expect(stickyElement).toHaveClass("top-0")
})

it("keeps the default content padding (p-4) when noPadding is not set", () => {
const formSchema = z.object({
name: f0FormField(z.string(), { label: "Name" }),
})

const { container } = render(
<F0Form
name="padding-default"
schema={formSchema}
defaultValues={{ name: "" }}
onSubmit={async () => ({ success: true })}
/>
)

expect(container.querySelector(".justify-center.p-4")).toBeInTheDocument()
})

it("removes the content padding when styling.noPadding is true (single schema)", () => {
const formSchema = z.object({
name: f0FormField(z.string(), { label: "Name" }),
})

const { container } = render(
<F0Form
name="padding-single-no-padding"
schema={formSchema}
defaultValues={{ name: "" }}
onSubmit={async () => ({ success: true })}
styling={{ noPadding: true }}
/>
)

// The centered wrapper is still there, just without the p-4 padding.
expect(
container.querySelector(".justify-center.p-4")
).not.toBeInTheDocument()
expect(container.querySelector(".justify-center")).toBeInTheDocument()
})

it("removes the content padding when styling.noPadding is true (per-section schema)", () => {
const formSchema = {
personal: z.object({
name: f0FormField(z.string(), { label: "Name" }),
}),
}

const sections: Record<string, F0SectionConfig> = {
personal: { title: "Personal" },
}

const { container } = render(
<F0Form
name="padding-per-section-no-padding"
schema={formSchema}
defaultValues={{ personal: { name: "" } }}
onSubmit={async () => ({ success: true })}
sections={sections}
styling={{ noPadding: true }}
/>
)

expect(
container.querySelector(".justify-center.p-4")
).not.toBeInTheDocument()
})
})

describe("F0Form renderIf hidden field layout", () => {
Expand Down
5 changes: 5 additions & 0 deletions packages/react/src/patterns/F0Form/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,11 @@ export interface F0FormStylingConfig {
* @default false
*/
showSectionsSidepanel?: boolean
/**
* Removes the default padding around the form content.
* @default false
*/
noPadding?: boolean
}

/**
Expand Down
Loading