883 Commits

Author SHA1 Message Date
github-actions[bot]
d556e4bc58 Update versions to 2.34.0 2026-03-22 22:21:50 +00:00
Hugo Alliaume
f246c18919 [Autocomplete][Chartjs][Cropperjs][Dropzone][LazyImage][LiveComponent][Map][Notify][React][StimulusBundle][Svelte][Swup][TogglePassword][Translator][Turbo][Typed][Vue] Update package.json to 2.33.0
The job that release npm packages failed https://github.com/symfony/ux/actions/runs/23216748609/job/67479440402

it should be fixed by https://github.com/symfony/ux/pull/3400
v2.34.0
2026-03-21 23:29:11 +01:00
Hugo Alliaume
3f2f1b320f Update tsdown & use @tsdown/css
This update simplifies the tsdown configuration, we do not need our custom plugin to minify CSS anymore (replaced by `css.minify = true`), and same for our hooks that rename the built CSS (replaced by `css.fileName`) 😍
2026-03-20 09:00:50 +01:00
Hugo Alliaume
5091cca304 bug #3367 [LiveComponent] Allow empty values to bypass model validation modifiers (xDeSwa)
This PR was merged into the 2.x branch.

Discussion
----------

[LiveComponent] Allow empty values to bypass model validation modifiers

| Q              | A
| -------------- | ---
| Bug fix?       | yes
| New feature?   |no
| Deprecations?  | no
| Documentation? | no
| Issues         | Fix #3153
| License        | MIT

**The Problem:**
Currently, when using input validation modifiers like `min_length(3)` on a LiveComponent model (e.g., `<input type="search" data-model="min_length(3)|query" />`), the modifier acts as a strict gatekeeper. If a user types "abc", the model updates correctly. However, if the user clears the input (e.g., by clicking the native "X" clear button on a search input or deleting the text), the update is blocked because the length is `0`.

This creates a frustrating UX: the input appears empty on the screen, but the component fails to re-render or notify the server, leaving the page (and backend state) stuck with the previous search results.

**The Solution:**
This PR updates `LiveController` to bypass `min_length`, `max_length`, `min_value`, and `max_value` checks if the input value is completely empty (`''`, `null`, or `undefined`).

**Why this is the correct approach (The HTML5 Standard):**
This change aligns LiveComponent's frontend validation strictly with standard HTML5 form validation behavior. In HTML5, attributes like `minlength` and `min` do **not** apply to empty values. If a field is allowed to be empty, it passes validation. If a field *must not* be empty, developers are expected to use the `required` attribute.

By allowing empty strings to pass through these modifiers:
1. It restores the expected behavior for search/filter inputs, allowing the component to naturally reset to its initial/unfiltered state when cleared.
2. It solves complex state synchronization issues between parent/child components when passing models via `props:props`.
3. It prevents numeric inputs (`min_value`) from casting an empty string to `0` and incorrectly failing validation.

### **Demo**
![firefox_uET9s3ozhL](https://github.com/user-attachments/assets/faefd7ab-5c0a-4cfa-a708-e00cf08e4792)

Commits
-------

79205ce63de fix-input-validation-modifiers
2026-03-19 22:40:01 +01:00
xDeSwa
42188885a3 fix-input-validation-modifiers 2026-03-17 02:30:22 +03:00
Hugo Alliaume
f025e483f4 bug #3372 [LiveComponent][TwigComponent] Fix reflection issues for private properties from trait and parent class (kachnitel)
This PR was squashed before being merged into the 2.x branch.

Discussion
----------

[LiveComponent][TwigComponent] Fix reflection issues for private properties from trait and parent class

| Q              | A
| -------------- | ---
| Bug fix?       | yes
| New feature?   | no
| Deprecations?  | no
| Documentation? | no
| Issues         | <!-- none yet -->
| License        | MIT

## Problem

When a component class *extends* another class that uses a trait with `#[ExposeInTemplate]` or `#[LiveProp]` attributes on **private** properties, those attributes are invisible to the metadata scanners.

PHP's `ReflectionClass::getProperties()` called on a child class does not return private properties from traits used in *ancestor* classes — they are only returned when `getProperties()` is called on the exact class that declares `use TraitName`. This is standard PHP reflection behaviour.

This affects any inheritance pattern where a base component uses `ComponentWithFormTrait` (or any custom trait with private `#[ExposeInTemplate]` properties) and app-level components extend it without re-declaring the trait.

**Symptom in `ux-twig-component`:** `Variable "form" does not exist` in the child component's Twig template.

**Symptom in `ux-live-component`:** `#[LiveProp(fieldName: 'callable()')]` declared in a trait used by a parent class is not registered with the correct `fieldName` on the child component, causing frontend model key mismatches.

## Fix

### `ux-twig-component` — `ComponentProperties::loadClassMetadata()`

Replace the single `$refClass->getProperties()` call with a loop that walks up `getParentClass()`, calling `getProperties()` at each level. Results are deduplicated by property name (child-class declaration takes priority), giving a complete view of all `#[ExposeInTemplate]` properties across the full hierarchy.

### `ux-live-component` — `LiveComponentMetadataFactory::createPropMetadatas()`

Pass `$property->getDeclaringClass()->getName()` instead of `$class->getName()` to `createLivePropMetadata()`. When a property is declared in a trait used by a parent class, the type extractor must be given the declaring class — not the leaf child class — to resolve the type correctly.

`#[LiveAction]` and `#[LiveListener]` on parent class methods are **not** affected: `getMethods()` already walks the full inheritance chain, so those are discovered correctly on child components.

## Tests

Added ``@group` trait-inheritance` tests in both packages using a minimal fixture: a trait with a private `#[ExposeInTemplate]` property / `#[LiveProp(fieldName: callable)]`, a parent component that uses the trait, and a child component that extends the parent without re-declaring the trait.

Commits
-------

799736ff76c [LiveComponent][TwigComponent] Fix reflection issues for private properties from trait and parent class
v2.33.0
2026-03-15 19:48:53 +01:00
Ondrej Vana
0227b4d5f5 [LiveComponent][TwigComponent] Fix reflection issues for private properties from trait and parent class 2026-03-15 19:48:50 +01:00
Hugo Alliaume
ffb5b7a324 Update Vitest to ^4.1.0 2026-03-15 08:57:29 +01:00
Mickaël BULIARD
8d1df497b6 [LiveComponent] Add debug:live-component command 2026-03-13 21:08:11 +01:00
Leevi Graham
1d06dbc4d6 Fix email input class binding for validation errors 2026-03-13 09:32:09 +11:00
Hugo Alliaume
eb7778281e feature #3361 Allow components use dynamically templates (xDeSwa)
This PR was squashed before being merged into the 2.x branch.

Discussion
----------

 Allow components use dynamically templates

| Q              | A
| -------------- | ---
| Bug fix?      | no
| New feature?   | yes
| Deprecations?  | no
| Documentation? | yes
| Issues         | -
| License        | MIT

### Description

This PR introduces the ability to dynamically resolve component templates via a dedicated method in the component class. This is particularly useful for building responsive components or multi-theme applications where the same logic (PHP class) needs to render different templates based on its internal state or props.

Instead of relying on a "magic" method, this implementation uses a new `FromMethod` attribute passed to the `template` parameter. This approach ensures explicit intent and maintains compatibility with Symfony's service tag requirements.

**How it works**

- Define a public method in your component that returns the template path as a string.
- `Pass a new FromMethod('yourMethodName')` to the `#[AsTwigComponent]` or `#[AsLiveComponent]` attribute.

**TwigComponent Example**
```php
#[AsTwigComponent('user_profile', template: new FromMethod('getTemplateForRole'))]
class UserProfile
{
    public function getTemplateForRole(): string
    {
        if ($this->isGranted('ROLE_ADMIN')) {
            return 'components/user_profile/admin_dashboard.html.twig';
        }

        return 'components/user_profile/public_card.html.twig';
    }
}
```

**LiveComponent Example**
```php
#[AsLiveComponent('checkout_wizard', template: new FromMethod('getStepTemplate'))]
class CheckoutWizard
{
    #[LiveProp]
    public int $currentStep = 1;

    #[LiveAction]
    public function nextStep(): void
    {
        $this->currentStep++;
    }

    public function getStepTemplate(): string
    {
        return match ($this->currentStep) {
            1 => 'components/checkout/step_1_address.html.twig',
            2 => 'components/checkout/step_2_shipping.html.twig',
            3 => 'components/checkout/step_3_payment.html.twig',
            default => 'components/checkout/summary.html.twig',
        };
    }
}
```

<details>
<summary>First PR</summary>
### Description

When building responsive or multi-theme applications, developers frequently need to reuse the exact same component logic (PHP class) with entirely different Twig templates depending on the context (e.g., a Desktop Search Dropdown vs. a Mobile Search Offcanvas).

Currently, the only ways to achieve this are:
1. **Duplicating the component class** just to change the `#[AsLiveComponent(template: '...')]` attribute.
2. **Creating a global `PreRenderEvent` listener**, which breaks component encapsulation and litters the codebase with boilerplate listeners.

This PR solves this by introducing a magic `getTemplate(): string` method. If a component defines this public method, the `ComponentRenderer` will use its return value as the template, overriding the default attribute metadata. This aligns perfectly with existing UX magic methods like `mount()` and `hydrate()`.

**Additional Note:** P.S. As this relies on the underlying ComponentRenderer, the getTemplate() method also works out-of-the-box for LiveComponents. It might be worth mentioning this in the LiveComponent documentation as well.

### Example Usage

Instead of duplicating components, developers can now encapsulate the logic gracefully:

```php
#[AsLiveComponent('SearchForm')]
class SearchForm extends AbstractController
{
    #[LiveProp]
    public string $theme = 'desktop';

    // The component dynamically decides its own template
    public function getTemplate(): string
    {
        return $this->theme === 'mobile'
            ? 'components/MobileSearch.html.twig'
            : 'components/DesktopSearch.html.twig';
    }
}

{# Renders the mobile template #}
{{ component('SearchForm', { theme: 'mobile' }) }}
</details>

Commits
-------

e161632b30d  Allow components use dynamically templates
2026-03-09 22:00:04 +01:00
xDeSwa
c53c058399 Allow components use dynamically templates 2026-03-09 21:59:58 +01:00
xDeSwa
62d09787a7 fix properly apply inline styles with important priority 2026-03-04 12:37:38 +03:00
Hugo Alliaume
151537cadd Migrate from tsup (deprecated) to tsdown
tsup is deprecated in favor of tsdown.

Follow https://github.com/symfony/ux/pull/2935, https://github.com/symfony/ux/pull/2944, and many (local) tries were I was not really happy with the files generated by tsdown/rolldown, we are finally having something extra good!

We have the benefits from https://github.com/symfony/ux/pull/2935, https://github.com/symfony/ux/pull/2944, but without their drawbacks. The code correctly follow the `es2022` target and does not do anything weird anymore with static properties (needed by controllers).

I (Claude) added a plugin to remove the region and JSDoc comments (except if they contain `@deprecated``), since I think we want to keep the code non-minified.
2026-02-28 09:33:37 +01:00
Hugo Alliaume
e3da1c80b7 Remove tsx dependency and rely on Node.js 22.18.0 native TypeScript runner 2026-02-27 20:17:39 +01:00
Steven Renaux
56ef59eac4 Fix PHPDoc types of TestLiveComponent::call() 2026-02-24 13:09:02 +01:00
Hugo Alliaume
574ae35e5e feature #3336 [LiveComponent] Add option fetchCredentials (Kocal)
This PR was merged into the 2.x branch.

Discussion
----------

[LiveComponent] Add option `fetchCredentials`

| Q              | A                                                                                                                        |
| -------------- | ------------------------------------------------------------------------------------------------------------------------ |
| Bug fix?       | no                                                                                                                   |
| New feature?   | yes <!-- please update src/**/CHANGELOG.md files -->                                                                  |
| Deprecations?  | no <!-- if yes, also update UPGRADE-*.md and src/**/CHANGELOG.md -->                                                 |
| Documentation? | yes <!-- required for new features, or documentation updates -->                                                      |
| Issues         | Fix #3322 <!-- 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).
-->

Commits
-------

0a09f45e3a9 [LiveComponent] Add option `fetchCredentials`
2026-02-16 21:26:11 +01:00
Jérôme Tamarelle
461b767f83 Add version constraint for symfony/config and zenstruck/foundry
Support for Enum cases in the configuration was added in symfony/config:6.3
1655d1763b

Error: Object of class Zenstruck\Foundry\ORM\ResetDatabase\ResetDatabaseMode could not be converted to string

/home/runner/work/ux/ux/src/LiveComponent/vendor/symfony/config/Definition/Builder/EnumNodeDefinition.php:30
/home/runner/work/ux/ux/src/LiveComponent/vendor/zenstruck/foundry/src/ZenstruckFoundryBundle.php:152
2026-02-07 09:54:38 +01:00
Jérôme Tamarelle
32b5d7864a zenstruck/foundry bundle requires symfony/http-kernel 6.1
zenstruck/foundry removed the requirement for symfony/framework-bundle in version 5.0.

0b09c20e79

Its bundle requires Symfony\Component\HttpKernel\Bundle\AbstractBundle that was introduced in symfony/http-kernel:6.1

7e8cf5df10

This commit fixes the build to lowest dependencies
2026-02-07 09:54:13 +01:00
Hugo Alliaume
7f5915193e [LiveComponent] Add option fetchCredentials 2026-02-07 09:02:34 +01:00
Hugo Alliaume
ea9a6b112c Drop Biome.js for oxfmt and oxlint 2026-02-03 23:14:09 +01:00
Hugo Alliaume
6d1a33f3b6 Run PHP-CS-Fixer (no_useless_else & static_lambda) 2026-02-03 22:36:24 +01:00
Hugo Alliaume
77a223fff3 Fix minimum requested 6.x version of FrameworkBundle for LiveComponent
Related to https://github.com/symfony/ux/actions/runs/21540501201/job/62074166912?pr=3318#step:9:1262
AbstractBundle has been introduced in 6.1: 7e8cf5df10
2026-02-01 08:43:19 +01:00
Hugo Alliaume
d64a1cf12e Fix npm releases due to repository issue 2026-01-16 23:36:00 +01:00
Hugo Alliaume
cfb4eaa929 Update versions to 2.32.0 2026-01-16 23:35:37 +01:00
Hugo Alliaume
b33c0ebd25 Update root JS dependencies 2026-01-11 00:13:28 +01:00
Ousmane NDIAYE
3299f6c1f5 Fix button syntax for file upload actions
Since self-closing the button tag is not recommended
2026-01-10 23:04:20 +01:00
Hugo Alliaume
c5aeccbf96 [CI] Remove usage of framework.annotations.enabled
Done in https://github.com/symfony/symfony/pull/61174
2026-01-09 00:52:01 +01:00
Ousmane NDIAYE
0beb730fa9 Update index.rst with MarkdownTextarea template info v2.32.0 2026-01-04 00:25:02 +01:00
Dariusz Ruminski
8441a53978 PHP CS Fixer: update usage 2026-01-03 23:36:42 +01:00
Hugo Alliaume
411a1092ee Pin phpdocumentor/reflection-docblock to non dev version 2025-12-25 08:58:38 +01:00
Hugo Alliaume
45cf663510 Add E2E tests for LiveComponent 2025-12-15 07:13:47 +01:00
Hugo Alliaume
6f72eac9ef Git-ignore config/reference.php 2025-12-02 08:12:06 +01:00
Hugo Alliaume
e62123879b Tests on Symfony 8 stable 2025-11-29 13:17:40 +01:00
Hugo Alliaume
d51d36b266 [CI] Tests over Symfony 7.4.0-beta1 and 8.0.0-beta1 2025-11-16 19:52:01 +01:00
Allan A. Nielsen
c7ccc51eb4 Add clarification about difference between component re-rendering and initial loading
Add a note that explains that the re-rendering loading states is a different concept from the components initial loading state regarding placeholders, with a link to the related "loading content" section in this doc
2025-11-11 13:24:05 +01:00
Hugo Alliaume
6df9015e7a Update versions to 2.31.0 2025-10-27 23:21:26 +01:00
Hugo Alliaume
10776be28e feature #3136 [LiveComponent] Add dispatch browser event assertion in InteractsWithLiveComponents (Develog)
This PR was merged into the 2.x branch.

Discussion
----------

[LiveComponent] Add dispatch browser event assertion in InteractsWithLiveComponents

| Q              | A
| -------------- | ---
| Bug fix?       | no
| New feature?   | yes <!-- please update src/**/CHANGELOG.md files -->
| Deprecations?  | no <!-- if yes, also update UPGRADE-*.md and src/**/CHANGELOG.md -->
| Documentation? | yes <!-- required for new features, or documentation updates -->
| Issues         | no <!-- prefix each issue number with "Fix #", no need to create an issue if none exist, explain below instead -->
| License        | MIT

Next to this PR https://github.com/symfony/ux/pull/2712 I wanted to add test assertion on dispatchBrowserEvent and not only on emited event.
I reuse same logic as the `ComponentWithEmit.php`.

```
$this->assertComponentDispatchEvent($testComponent->render(), 'browserEvent')
    ->withPayload(['amount' => 2, 'unit' => 'kg'])
    ->withPayloadSubset(['amount' => 2]) ;
```

`$this->assertComponentNotDispatchEvent($testComponent->render(), 'otherBrowserEvent');`

Commits
-------

5943c89f4ea [LiveComponent] Add dispatch browser event test
v2.31.0
2025-10-22 04:51:40 +02:00
Develog
d4841320f1 [LiveComponent] Add dispatch browser event test 2025-10-21 22:20:39 +02:00
Hugo Alliaume
5663d29bb7 Add --json flag to composer config command 2025-10-16 09:24:06 +02:00
Randy Geraads
3e03e47140 added missing import to code snippet 2025-10-08 16:58:43 +02:00
Sait KURT
3a0725e126 [LiveComponent] Replace the browser's URL before triggering render:finished hook 2025-10-01 21:43:04 +02:00
Thibault G
b3607791b7 [Docs] Fix composer config to avoid modifying package.json automatically 2025-09-21 21:04:19 +02:00
Hugo Alliaume
d041f6c874 Refactor "test_package.sh" to its original purpose, add multiples checks for packages definition 2025-09-20 13:50:04 +02:00
Hugo Alliaume
6b3ba2070d minor #3086 Add doc for E2E steps + minor modifications (raphael-geffroy)
This PR was merged into the 2.x branch.

Discussion
----------

 Add doc for E2E steps + minor modifications

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | no
| Docs?         | yes
| Issues        | Fix #3039
| License       | MIT

Commits
-------

ceefa1d8501 [Docs] Add doc for E2E steps + minor modifications
2025-09-19 15:08:37 +02:00
Raphaël Geffroy
69a35c1ad8 [Docs] Add doc for E2E steps + minor modifications 2025-09-19 15:06:31 +02:00
Hugo Alliaume
4b8fb2a994 [Autocomplete][LiveComponent] Fix deprecations with zenstruck/foundry:^2.7 2025-09-19 08:10:27 +02:00
Danny van Wijk
83fd5ad8a7 Improve performance for LiveUrlSubscriber 2025-09-11 14:15:26 +02:00
William Pinaud
6deed10aac Update index.rst 2025-09-09 12:53:23 +02:00
Hugo Alliaume
4c906a882b Configure .gitattributes to ignore Vitest and Playwright config files from export 2025-09-01 22:55:23 +02:00