Skip to content

Commit 2cb1d63

Browse files
committed
feature #3804 [Toolkit][Shadcn] Add form recipe (Kocal)
This PR was merged into the 3.x branch. Discussion ---------- [Toolkit][Shadcn] Add form recipe | Q | A | -------------- | --- | Bug fix? | no | New feature? | yes | Deprecations? | no | Documentation? | no | Issues | Part of #3233 | License | MIT Adds the `form` recipe to the Shadcn kit: a set of accessible layout wrappers (`Form`, `Form:Item`, `Form:Label`, `Form:Description`, `Form:Message`) for building forms around Symfony form fields. It's pure Twig and CSS, no Stimulus controller involved. When a `Form:Item` contains a `Form:Message`, its `Form:Label` automatically switches to the destructive color. This is handled entirely in CSS via a `group-has` selector, no client-side state needed. Split out of #3473 (closed in favor of this PR and its two siblings for `sheet` and `drawer`), itself part of #3233. Commits ------- 86d1064 [Toolkit][Shadcn] Add form recipe
2 parents b224271 + 86d1064 commit 2cb1d63

10 files changed

Lines changed: 184 additions & 0 deletions

src/Toolkit/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
- [Shadcn] Add `slider` recipe
1515
- [Shadcn] Add `drawer` recipe
1616
- [Shadcn] Add `sheet` recipe
17+
- [Shadcn] Add `form` recipe
1718
- [Shadcn][Flowbite] Merge component classes with `attributes.defaults({ class: '...'|tailwind_classes })` instead of `tailwind_merge` (consumer classes now override component variants)
1819
- [Shadcn] Fix Field/Label, InputGroup and Pagination silently dropping their own base classes when forwarding to a child component (their checked/disabled/nested/dark styles now apply)
1920
- [Shadcn] Fix `dialog` opening on initial render when `open` is `false`
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Form
2+
3+
A set of layout wrappers to build accessible forms with label, description and error-message slots.
4+
5+
```twig {"preview":true}
6+
<twig:Form class="mx-auto w-full max-w-md">
7+
<twig:Form:Item>
8+
<twig:Form:Label for="form-email">Email</twig:Form:Label>
9+
<twig:Input id="form-email" name="email" type="email" placeholder="you@example.com" />
10+
<twig:Form:Description>We'll never share your email with anyone else.</twig:Form:Description>
11+
</twig:Form:Item>
12+
<twig:Form:Item>
13+
<twig:Form:Label for="form-bio">Bio</twig:Form:Label>
14+
<twig:Textarea id="form-bio" name="bio" placeholder="Tell us a bit about yourself" />
15+
<twig:Form:Description>You can @mention other users and organizations.</twig:Form:Description>
16+
</twig:Form:Item>
17+
<div class="flex justify-end gap-2">
18+
<twig:Button type="button" variant="outline">Cancel</twig:Button>
19+
<twig:Button type="submit">Save</twig:Button>
20+
</div>
21+
</twig:Form>
22+
```
23+
24+
## Installation
25+
26+
::: installation
27+
28+
## Usage
29+
30+
```twig
31+
<twig:Form>
32+
<twig:Form:Item>
33+
<twig:Form:Label for="form-username">Username</twig:Form:Label>
34+
<twig:Input id="form-username" name="username" placeholder="shadcn" />
35+
<twig:Form:Description>This is your public display name.</twig:Form:Description>
36+
</twig:Form:Item>
37+
<twig:Button type="submit">Submit</twig:Button>
38+
</twig:Form>
39+
```
40+
41+
## Examples
42+
43+
### Validation
44+
45+
When a `Form:Item` contains a `Form:Message`, its `Form:Label` switches to the destructive color.
46+
47+
```twig {"preview":true}
48+
<twig:Form class="mx-auto w-full max-w-md">
49+
<twig:Form:Item>
50+
<twig:Form:Label for="form-validation-email">Email</twig:Form:Label>
51+
<twig:Input id="form-validation-email" name="email" type="email" value="not-an-email" aria-invalid="true" />
52+
<twig:Form:Message>Please enter a valid email address.</twig:Form:Message>
53+
</twig:Form:Item>
54+
</twig:Form>
55+
```
56+
57+
## API Reference
58+
59+
::: api-reference
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"$schema": "../../../schema-kit-recipe-v1.json",
3+
"type": "component",
4+
"name": "Form",
5+
"version-added": "3.5",
6+
"copy-files": {
7+
"templates/": "templates/"
8+
},
9+
"dependencies": {
10+
"composer": [
11+
"twig/html-extra:^3.24.0",
12+
"symfony/ux-twig-component:^3.5",
13+
"tales-from-a-dev/twig-tailwind-extra:^1.3.0"
14+
]
15+
}
16+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{# @block content The form fields, typically a list of `Form:Item`. #}
2+
<form
3+
data-slot="form"
4+
{{ attributes.defaults({
5+
class: 'space-y-6'|tailwind_classes,
6+
}) }}
7+
>
8+
{%- block content %}{% endblock -%}
9+
</form>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{# @block content The help text. #}
2+
<p
3+
data-slot="form-description"
4+
{{ attributes.defaults({
5+
class: 'text-sm text-muted-foreground'|tailwind_classes,
6+
}) }}
7+
>
8+
{%- block content %}{% endblock -%}
9+
</p>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{# @block content A `Form:Label`, a form control, an optional `Form:Description` and an optional `Form:Message`. #}
2+
<div
3+
data-slot="form-item"
4+
{{ attributes.defaults({
5+
class: 'group/form-item grid gap-2'|tailwind_classes,
6+
}) }}
7+
>
8+
{%- block content %}{% endblock -%}
9+
</div>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{# @block content The label text. #}
2+
<label
3+
data-slot="form-label"
4+
{{ attributes.defaults({
5+
class: 'flex items-center gap-2 text-sm font-medium leading-none select-none group-has-[[data-slot=form-message]]/form-item:text-destructive'|tailwind_classes,
6+
}) }}
7+
>
8+
{%- block content %}{% endblock -%}
9+
</label>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{# @block content The validation error message. #}
2+
<p
3+
data-slot="form-message"
4+
{{ attributes.defaults({
5+
class: 'text-sm text-destructive'|tailwind_classes,
6+
}) }}
7+
>
8+
{%- block content %}{% endblock -%}
9+
</p>

src/Toolkit/tests/Functional/__snapshots__/ComponentsRenderingTest__testComponentRendering with data set Kit shadcn, component form, example 0__1.html

Lines changed: 41 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Toolkit/tests/Functional/__snapshots__/ComponentsRenderingTest__testComponentRendering with data set Kit shadcn, component form, example 1__1.html

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)