Skip to content

Commit 441aede

Browse files
committed
feature #3073 [Toolkit][Shadcn] Add AlertDialog recipe (Kocal)
This PR was merged into the 2.x branch. Discussion ---------- [Toolkit][Shadcn] Add AlertDialog recipe | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes <!-- please update src/**/CHANGELOG.md files --> | Docs? | yes <!-- required for new features --> | Issues | Fix #... <!-- prefix each issue number with "Fix #", no need to create an issue if none exist, explain below instead --> | License | MIT <!-- Replace this notice by a description of your feature/bugfix. This will help reviewers and should be a good start for the documentation. Additionally (see https://symfony.com/releases): - Always add tests and ensure they pass. - For new features, provide some code snippets to help understand usage. - Features and deprecations must be submitted against branch main. - Update/add documentation as required (we can help!) - Changelog entry should follow https://symfony.com/doc/current/contributing/code/conventions.html#writing-a-changelog-entry - Never break backward compatibility (see https://symfony.com/bc). --> With this new AlertDialog recipe, the following code: ```twig <twig:AlertDialog> <twig:AlertDialog:Trigger> <twig:Button {{ ...trigger_attrs }}>Open</twig:Button> </twig:AlertDialog:Trigger> <twig:AlertDialog:Content> <twig:AlertDialog:Header> <twig:AlertDialog:Title>Are you absolutely sure?</twig:AlertDialog:Title> <twig:AlertDialog:Description> This action cannot be undone. This will permanently delete your account and remove your data from our servers. </twig:AlertDialog:Description> </twig:AlertDialog:Header> <twig:AlertDialog:Footer> <twig:AlertDialog:Cancel>Cancel</twig:AlertDialog:Cancel> <twig:AlertDialog:Action>Continue</twig:AlertDialog:Action> </twig:AlertDialog:Footer> </twig:AlertDialog:Content> </twig:AlertDialog> ``` renders: https://github.com/user-attachments/assets/4c1c836f-9051-489a-a2ec-83e09a75853f --- This pull request introduces the new `AlertDialog` component to the Shadcn UI kit in the Symfony UX Toolkit, including its templates, JavaScript controller, documentation, and manifest. It also updates the toolkit infrastructure to support the new component, refines asset mapping, and improves component context handling. Additionally, some minor documentation and test updates are included. **New AlertDialog Component:** - Added the complete `AlertDialog` component, including Twig templates for all subcomponents (`Content`, `Header`, `Footer`, `Title`, `Description`, `Trigger`, `Action`, `Cancel`), a Stimulus controller for open/close behavior with transitions, and detailed usage examples in `EXAMPLES.md`. [[1]](diffhunk://#diff-1e8ce2e329975545f086aa3b8750d3b44da63cdf032646af0d99e7b9b2de4c5cR1-R47) [[2]](diffhunk://#diff-f14c3832bf831f5267b41d784094cf24a84edc5f79abc0637b1dac6920e14df1R1-R27) [[3]](diffhunk://#diff-91557baac0c727764c3898ee86de6ec62deb6b33ce82bc86d621f2c3b9ba7233R1-R13) [[4]](diffhunk://#diff-a2c365dd11321098d3f26effc35341c9279cded71da56b1797f861e1cd0c723aR1-R37) [[5]](diffhunk://#diff-2b90716a494d08147e3707a62675fcaaf6befdc041085d7d44e06cb3af32b485R1-R6) [[6]](diffhunk://#diff-40dc30f4cd5b6a18cfd9da93bb067cf7854e200c2348431e045cdcbebed4eb96R1-R6) [[7]](diffhunk://#diff-206b916d71f01ccad6e8b3950731ec0f3cd8dab058b2e038804439ba9b2d9c44R1-R7) [[8]](diffhunk://#diff-0369bb96b47c3d4c0acfe5dfdea8642690e8f8f6681bc926fae0b6264ef34219R1-R7) [[9]](diffhunk://#diff-7eda7755d4c14d6ce00a3e8199913b3b9f7a0f1e1ced0deeb8fba9a0e3379e96R1-R7) [[10]](diffhunk://#diff-05905531ae89a23629be33318174aaead05963b525710179b98e8e248c0d6ea4R1-R4) [[11]](diffhunk://#diff-bb139b689add718b84387dddd493a83d71911eb6e6faed46e41d68461b78d9b4R1-R7) - Added a manifest for the `AlertDialog` component specifying dependencies and recipe details. **Toolkit Infrastructure and Asset Handling:** - Refined the `KitContextRunner` to better separate Twig and component factory contextualization, improving reliability and maintainability when running code for specific kits. [[1]](diffhunk://#diff-cfffacbba1a1731189cefcdcfbf0096947ac2b4f2d41d83656b8814f072ce535R27-R31) [[2]](diffhunk://#diff-cfffacbba1a1731189cefcdcfbf0096947ac2b4f2d41d83656b8814f072ce535L42-R99) - Updated asset mapping to expose kit assets under a namespaced path and to exclude kit templates, documentation, and manifests from asset mapping. [[1]](diffhunk://#diff-f414b69a2be9ec873133c4230f66df08251e9efc1885d4dc9246a7d28c428472L5-R6) [[2]](diffhunk://#diff-f414b69a2be9ec873133c4230f66df08251e9efc1885d4dc9246a7d28c428472R15-R17) - Registered the `alert-dialog` Stimulus controller in the app's JavaScript entrypoint and importmap, and added the `el-transition` dependency. [[1]](diffhunk://#diff-778967d6b92db56e30a61c00288340db8cf087bbb76626da93bdd2abd8399a18R2-R6) [[2]](diffhunk://#diff-a2b46fc4576c4f4edafcd41c9d18f6288c855a95ac32139e6e721be80d2e2052R207-R212) **Documentation and Test Updates:** - Updated kit and component documentation to reference the new homepage URL and include `AlertDialog` in suggestions and tests. [[1]](diffhunk://#diff-aa11a152e722d16f5bd4601cdbaa7a977b62370267ca536c1dc6677cb5d48cd3L6-R6) [[2]](diffhunk://#diff-bd0225f055b73556a1d02abc993c0cc75d00ec79077eebb0746a831d749c680cL79-R79) [[3]](diffhunk://#diff-13a35e56dbb84c2b7166f8ba00ee56fbbd9f79719a2e04f93a1171691961b22fL31-R31) - Minor fix to use `self::generateTabs` in the toolkit service for consistency. Commits ------- bc9accc [Toolkit][Shadcn] Add AlertDialog recipe
2 parents 67993da + bc9accc commit 441aede

24 files changed

+354
-28
lines changed

.github/workflows/unit-tests.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ jobs:
5757

5858
env:
5959
SYMFONY_REQUIRE: ${{ matrix.symfony-version || '>=5.4' }} # TODO: To change to '>=6.4' in 3.x
60+
# https://github.com/spatie/phpunit-snapshot-assertions#usage-in-ci
61+
CREATE_SNAPSHOTS: false
6062
steps:
6163
- uses: actions/checkout@v4
6264

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Examples
2+
3+
## Default
4+
5+
```twig {"preview":true,"height":"500px"}
6+
<twig:AlertDialog id="delete_account">
7+
<twig:AlertDialog:Trigger>
8+
<twig:Button {{ ...trigger_attrs }}>Open</twig:Button>
9+
</twig:AlertDialog:Trigger>
10+
<twig:AlertDialog:Content>
11+
<twig:AlertDialog:Header>
12+
<twig:AlertDialog:Title>Are you absolutely sure?</twig:AlertDialog:Title>
13+
<twig:AlertDialog:Description>
14+
This action cannot be undone. This will permanently delete your account
15+
and remove your data from our servers.
16+
</twig:AlertDialog:Description>
17+
</twig:AlertDialog:Header>
18+
<twig:AlertDialog:Footer>
19+
<twig:AlertDialog:Cancel>Cancel</twig:AlertDialog:Cancel>
20+
<twig:AlertDialog:Action>Continue</twig:AlertDialog:Action>
21+
</twig:AlertDialog:Footer>
22+
</twig:AlertDialog:Content>
23+
</twig:AlertDialog>
24+
```
25+
26+
## Opened by default
27+
28+
```twig {"preview":true,"height":"500px"}
29+
<twig:AlertDialog id="delete_account" open>
30+
<twig:AlertDialog:Trigger>
31+
<twig:Button {{ ...trigger_attrs }}>Open</twig:Button>
32+
</twig:AlertDialog:Trigger>
33+
<twig:AlertDialog:Content>
34+
<twig:AlertDialog:Header>
35+
<twig:AlertDialog:Title>Are you absolutely sure?</twig:AlertDialog:Title>
36+
<twig:AlertDialog:Description>
37+
This action cannot be undone. This will permanently delete your account
38+
and remove your data from our servers.
39+
</twig:AlertDialog:Description>
40+
</twig:AlertDialog:Header>
41+
<twig:AlertDialog:Footer>
42+
<twig:AlertDialog:Cancel>Cancel</twig:AlertDialog:Cancel>
43+
<twig:AlertDialog:Action>Continue</twig:AlertDialog:Action>
44+
</twig:AlertDialog:Footer>
45+
</twig:AlertDialog:Content>
46+
</twig:AlertDialog>
47+
```
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Controller } from '@hotwired/stimulus';
2+
import { enter, leave } from 'el-transition';
3+
4+
export default class extends Controller {
5+
6+
static targets = ['trigger', 'overlay', 'dialog', 'content'];
7+
8+
async open() {
9+
this.dialogTarget.showModal();
10+
11+
await Promise.all([enter(this.overlayTarget), enter(this.contentTarget)]);
12+
13+
if (this.hasTriggerTarget) {
14+
this.triggerTarget.setAttribute('aria-expanded', 'true');
15+
}
16+
}
17+
18+
async close() {
19+
await Promise.all([leave(this.overlayTarget), leave(this.contentTarget)]);
20+
21+
this.dialogTarget.close();
22+
23+
if (this.hasTriggerTarget) {
24+
this.triggerTarget.setAttribute('aria-expanded', 'false');
25+
}
26+
}
27+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"$schema": "../../../schema-kit-recipe-v1.json",
3+
"type": "component",
4+
"name": "AlertDialog",
5+
"description": "A modal dialog that interrupts the user with important content and expects a response.",
6+
"copy-files": {
7+
"templates/": "templates/"
8+
},
9+
"dependencies": [
10+
{
11+
"type": "php",
12+
"name": "twig/extra-bundle"
13+
},
14+
{
15+
"type": "php",
16+
"name": "twig/html-extra",
17+
"version": "^3.12.0"
18+
},
19+
{
20+
"type": "php",
21+
"name": "tales-from-a-dev/twig-tailwind-extra"
22+
},
23+
{
24+
"type": "npm",
25+
"name": "el-transition"
26+
},
27+
{
28+
"type": "importmap",
29+
"package": "el-transition"
30+
},
31+
{
32+
"type": "recipe",
33+
"name": "Button"
34+
}
35+
]
36+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{%- props open = false, id -%}
2+
3+
{%- set _alert_dialog_open = open %}
4+
{%- set _alert_dialog_id = 'alert-dialog-' ~ id -%}
5+
{%- set _alert_dialog_title_id = _alert_dialog_id ~ '-title' -%}
6+
{%- set _alert_dialog_description_id = _alert_dialog_id ~ '-description' -%}
7+
<div {{ attributes.defaults({
8+
'data-controller': 'alert-dialog',
9+
'aria-labelledby': _alert_dialog_title_id,
10+
'aria-describedby': _alert_dialog_description_id,
11+
}) }}>
12+
{% block content %}{% endblock %}
13+
</div>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{% props variant = 'default' %}
2+
<twig:Button variant="{{ variant }}" {{ ...attributes }}>
3+
{{ block(outerBlocks.content) }}
4+
</twig:Button>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<twig:Button
2+
variant="outline"
3+
data-action="click->alert-dialog#close"
4+
{{ ...attributes }}
5+
>
6+
{{- block(outerBlocks.content) -}}
7+
</twig:Button>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<dialog
2+
id="{{ _alert_dialog_id }}"
3+
{{ _alert_dialog_open ? 'open' }}
4+
class="{{ 'fixed inset-0 size-auto max-h-none max-w-none overflow-y-auto bg-transparent backdrop:bg-transparent ' ~ attributes.render('class')|tailwind_merge }}"
5+
data-alert-dialog-target="dialog"
6+
data-action="keydown.esc->alert-dialog#close:prevent"
7+
{{ attributes.without('id') }}
8+
>
9+
<div
10+
data-alert-dialog-target="overlay"
11+
data-transition-enter="transition ease-out duration-100"
12+
data-transition-enter-start="transform opacity-0"
13+
data-transition-enter-end="transform opacity-100"
14+
data-transition-leave="transition ease-in duration-75"
15+
data-transition-leave-start="transform opacity-100"
16+
data-transition-leave-end="transform opacity-0"
17+
class="{{ _alert_dialog_open ? '' : 'hidden' }} fixed inset-0 z-50 bg-black/50"
18+
></div>
19+
20+
<section
21+
tabindex="0"
22+
class="flex min-h-full items-end justify-center p-4 text-center focus:outline-none sm:items-center sm:p-0"
23+
>
24+
<div
25+
data-transition-enter="transition ease-out duration-200"
26+
data-transition-enter-start="transform opacity-0 scale-95"
27+
data-transition-enter-end="transform opacity-100 scale-100"
28+
data-transition-leave="transition ease-in duration-75"
29+
data-transition-leave-start="transform opacity-100 scale-100"
30+
data-transition-leave-end="transform opacity-0 scale-95"
31+
class="{{ _alert_dialog_open ? '' : 'hidden' }} bg-background fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg sm:max-w-lg"
32+
data-alert-dialog-target="content"
33+
>
34+
{%- block content %}{% endblock -%}
35+
</div>
36+
</section>
37+
</dialog>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<p
2+
id="{{ _alert_dialog_description_id }}"
3+
class="{{ 'text-muted-foreground text-sm ' ~ attributes.render('class')|tailwind_merge }}"
4+
{{ attributes.without('id') }}
5+
>
6+
{%- block content %}{% endblock -%}
7+
</p>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<footer
2+
class="{{ 'flex flex-col-reverse gap-2 sm:flex-row sm:justify-end ' ~ attributes.render('class')|tailwind_merge }}"
3+
{{ attributes }}
4+
>
5+
{%- block content %}{% endblock -%}
6+
</footer>

0 commit comments

Comments
 (0)