Vaadin Flow Components V25.3.0-alpha1
Pre-releaseVaadin Flow Components 25.2.0-beta2
This is a release of the Java integration for Vaadin Components to be used from the Java server side with Vaadin Flow.
Changes in Flow Components from 25.2.0-beta1
Changes in All Components
- Chore:
Changes in vaadin-ai-components-flow
-
Breaking Changes:
-
⧉ Switch form filler value options to items-based resolution. PR:9564
## Description In form filler DX test sessions, the return type of value options APIs was a pain point. Even after updating examples and clarifying it in the documentation, it still was not intuitive: the developer had to think in labels (strings) when the rest of their codebase thought in domain objects, and had to supply a
toValueconverter that re-encoded the label-to-value mapping the field'sItemLabelGeneratoralready expressed. This PR replaces that shape with an items-based resolution path: - Registrations now carry the domain objects, not pre-rendered labels. Aligned with how the rest of the codebase models selection options. - Labels for the LLM are derived through oneItemLabelGenerator. By default, the controller reflects the field's ownsetItemLabelGenerator(...)so common cases need no extra wiring; an explicit one can be supplied onValueOptionsfor custom components or cases where the LLM should see a different label than the UI. - When the LLM picks a label, the controller walks the registration's items, applies the same generator per item, and writes the first matching item to the field. The bijection lives in one place — the label generator — and thetoValueconverter is no longer needed and has been removed. Changed inValueOptions: -options(Collection<String>)→options(Collection<V>)-options(BiFunction<String, Integer, List<String>>)→options(BiFunction<String, Integer, List<V>>)New inValueOptions: -itemLabelGenerator(ItemLabelGenerator<V>)— optional; falls back to the field's own labeler, then toString.valueOf(item). Changed inFormAIController: -fieldValueOptions(ValueOptions<String>)→fieldValueOptions(ValueOptions<V>)-fieldValueOptions(ValueOptions<V>, Function<String, V> toValue)removed. Other behavior: - Items with duplicate labels resolve to the first in registration order; a fixed-options registration logs a warning at registration so the developer notices. - A query-modefill_formthat arrives before anyquery_field_optionscall is rejected with a reason namingquery_field_optionsso the LLM can recover on the next turn. > [!WARNING] > The two-argumentfieldValueOptions(ValueOptions, Function<String, V>)overload and theString-only generics on the single-argument variant are gone. Based on DX test findings. No related issue. ## Type of change - [ ] Bugfix - [x] Feature ## Checklist - [x] I have read the contribution guide: https://vaadin.com/docs/latest/contributing/overview - [x] I have added a description following the guideline. - [ ] The issue is created in the corresponding repository and I have referenced it. - [x] I have added tests to ensure my change is effective and works as intended. - [x] New and existing tests are passing locally with my change. - [x] I have performed self-review and corrected misspellings. - [ ] Enhancement / new feature was discussed in a corresponding GitHub issue and Acceptance Criteria were created. -
⧉ Rename FormAIController API to include field. PR:9544
## Description This PR renames
FormAIControllerAPIs to include "field" in order to make it clearer for the developer. | Old | New | | --- | --- | | ignore | ignoreField | | describe | describeField | | valueOptions | fieldValueOptions | | setValuesHidden | setFieldValuesHidden | | isValuesHidden | isFieldValuesHidden | | showHighlight | showFieldHighlight | | hideHighlight | hideFieldHighlight | Based on DX test session findings. No related issue. ## Type of change - [ ] Bugfix - [ ] Feature - [x] Refactor ## Checklist - [x] I have read the contribution guide: https://vaadin.com/docs/latest/contributing/overview - [x] I have added a description following the guideline. - [ ] The issue is created in the corresponding repository and I have referenced it. - [ ] I have added tests to ensure my change is effective and works as intended. - [x] New and existing tests are passing locally with my change. - [x] I have performed self-review and corrected misspellings. -
⧉ Use config class for form ai controller value options. PR:9372
## Description This PR adds a
ValueOptionsbuilder for configuring per-field options onFormAIController. A registration carries the field together with the label set the LLM may pick from; the controller pairs it with a label-to-value converter (implicit forString-valued fields, required for any other value type). ###ValueOptionsAPI -ValueOptions.forField(HasValue<?, V> field)— single-value field. -ValueOptions.forField(MultiSelect<C, T> field)— multi-select field; picked by the compiler whenever the reference is statically typed asMultiSelect. -options(Collection<String>)— fixed label list. -options(BiFunction<String, Integer, List<String>>)— queryable label callback (filter + limit → labels).options(...)overloads are mutually exclusive; calling one clears the other so the last setter wins. ###FormAIController.valueOptionsoverloads -valueOptions(ValueOptions<String> config)— forString-valued fields. The converter is implicitlyFunction.identity(): the chosen label is the value. -<V> valueOptions(ValueOptions<V> config, Function<String, V> toValue)— for any other value type. The compiler requires the converter — a non-Stringregistration without one does not compile. For multi-select fields the controller aggregates the resolved per-label elements into aLinkedHashSetbeforeHasValue#setValue. ### Sample usesStringsingle-value field (no converter — identity implicit):java controller.valueOptions( ValueOptions.forField(currencyCombo) .options(List.of("EUR", "USD", "GBP")));Non-Stringsingle-value field (explicit converter):java controller.valueOptions( ValueOptions.forField(projectSelect) .options((filter, limit) -> projectService.search(filter, limit)), label -> projectService.findByName(label));Stringmulti-select field (no converter):java controller.valueOptions( ValueOptions.forField(tagsCheckboxGroup) .options(List.of("AI", "Cloud", "Security")));Non-Stringmulti-select field (per-element converter):java controller.valueOptions( ValueOptions.forField(projectsMultiSelect) .options(List.of("Apollo", "Vega")), label -> projectService.findByName(label));### Multi-value support contract Multi-value components are only supported if they implement theMultiSelectinterface. The controller's runtime checks enforce this at registration time: - AMultiSelectfield registered through the single-valueforFieldoverload (upcast reference) is rejected with a message that steers the developer to declare the reference asMultiSelect. - A field whose value type is aCollectionbut does not implementMultiSelectis rejected — Collection-valued fields must implementMultiSelectto be registered viavalueOptions(...). Fixes https://github.com/orgs/vaadin/projects/103/views/1?filterQuery=&pane=issue&itemId=190875414 ## Type of change - [ ] Bugfix - [ ] Feature - [x] Refactor ## Checklist - [x] I have read the contribution guide: https://vaadin.com/docs/latest/contributing/overview - [x] I have added a description following the guideline. - [x] The issue is created in the corresponding repository and I have referenced it. - [x] I have added tests to ensure my change is effective and works as intended. - [x] New and existing tests are passing locally with my change. - [x] I have performed self-review and corrected misspellings. ---------
-
-
New Features:
-
⧉ Hide all form field values from the LLM. PR:9527
## Summary - Let a form expose its fields to the AI for filling while keeping existing values private, for cases where the form may already hold personal data the user entered. 🤖 Generated with Claude Code ---------
-
⧉ Add field highlighting for ai updated fields. PR:9413
## Description This PR: - Adds
withFieldValuesChanged(Consumer<Map<HasValue, FieldValueChange>>), which fires once per successful turn. Ignored fields are excluded. Skipped when the turn ended in error or when no field changed. - AddsshowHighlight(HasValue)andhideHighlight(HasValue)to control the highlighter. Each controller carries a per-instance UUID id (vaadin-ai-<uuid>) and usesaddUser/removeUser, so the AI highlight coexists with any other field-highlighter user the application keeps on the same field. The highlight is not retained across detach/re-attach. Fixes https://github.com/orgs/vaadin/projects/103/views/1?filterQuery=&pane=issue&itemId=193540699 ## Type of change - [ ] Bugfix - [x] Feature ## Checklist - [x] I have read the contribution guide: https://vaadin.com/docs/latest/contributing/overview - [x] I have added a description following the guideline. - [x] The issue is created in the corresponding repository and I have referenced it. - [x] I have added tests to ensure my change is effective and works as intended. - [x] New and existing tests are passing locally with my change. - [x] I have performed self-review and corrected misspellings. - [x] Enhancement / new feature was discussed in a corresponding GitHub issue and Acceptance Criteria were created. -
⧉ Expose per-turn session metadata to the LLM. PR:9399
## Summary - The LLM needs ambient session context — current date/time, and optionally tenant, locale, or view state — to interpret relative references like "sales from the past two months" instead of guessing. Putting that in the system prompt would invalidate its cache every turn, so it rides on a built-in get_session_context tool whose description carries the content, keeping the (potentially large) system prompt cacheable. - The orchestrator installs a default supplier that provides the current server date and time; withMetadata lets apps replace or extend it with their own context, or pass null to disable the tool entirely. Closes https://github.com/orgs/vaadin/projects/103/views/3?pane=issue&itemId=193058102 🤖 Generated with Claude Code ---------
-
⧉ Expose form workflow via get_form_instructions tool. PR:9371
## Summary - Applications no longer have to repeat the form-fill workflow in their own system prompts — the controller carries its own contract, so adopters only supply domain context. - Mirrors the existing
get_chart_instructions/get_grid_instructionspattern so behaviour is consistent across AI-component controllers. Fixes https://github.com/orgs/vaadin/projects/103/views/3?pane=issue&itemId=189954324 🤖 Generated with Claude Code --------- -
⧉ Surface validation in fill_form response. PR:9341
## Summary - Wires
Binder.Binding/HasValidatorrejections into thefill_formtool result so the LLM can self-correct in the same turn instead of silently leaving the form in an invalid state. Validation runs atfill_formexecution time only, per the RFC's validation-feedback flow. - Per RFC,fill_formnow mirrorsget_form_state's shape — returning the full post-writefieldsblock plusrejected— so value-change listener cascades and other downstream shifts are visible without an extra round-trip. (Breaking: the previoussuccess/writtenkeys are gone; derive success fromrejected.isEmpty().) - The schema is otherwise intentionally kept shape-only (type / format / value / enum / queryable / array / items); JSR-303 and component-side constraints reach the LLM through the validator feedback path onfill_form. Surfacing those constraints proactively inget_form_state(e.g. viajakarta.validation.Validatorintrospection, paired withBeanValidationBinder) is deferred until later. Closes https://github.com/orgs/vaadin/projects/103/views/3?pane=issue&itemId=186557546 🤖 Generated with Claude Code --------- -
⧉ Add prompt overload accepting attachments. PR:9339
## Description This PR adds
prompt(String, List<AIAttachment>)overload toAIOrchestrator. The overload uses only the supplied list and does not drain a configuredAIFileReceiver; pending uploads stay in the receiver for the next call toprompt(String)or the user's next submit. An empty list is still "explicit" — it does not fall back to the receiver. Fixes https://github.com/orgs/vaadin/projects/103/views/1?filterQuery=&pane=issue&itemId=189945711 ## Type of change - [ ] Bugfix - [x] Feature ## Checklist - [x] I have read the contribution guide: https://vaadin.com/docs/latest/contributing/overview - [x] I have added a description following the guideline. - [x] The issue is created in the corresponding repository and I have referenced it. - [x] I have added tests to ensure my change is effective and works as intended. - [x] New and existing tests are passing locally with my change. - [x] I have performed self-review and corrected misspellings. - [x] Enhancement / new feature was discussed in a corresponding GitHub issue and Acceptance Criteria were created.
-
-
Fixes:
-
⧉ Surface binder cross-field rejections in fill_form. PR:9406
## Summary - Bean-level cross-field rules registered with
binder.withValidator((bean, ctx) -> ...)never ran duringfill_form, so a value combination that violated such a rule came back as success and the model had no way to correct it. - These failures now appear as rejections keyed on a__form__sentinel id, since a cross-field rule is not tied to a single field. -fill_formnow writes every value before it validates, so each field's verdict is based on the fully-written form and no longer depends on the order the values arrived in. - Validation only reads each verdict; it does not change the UI. Fields the current fill did not write are not marked invalid, so required fields the user has not reached yet stay clean. 🤖 Generated with Claude Code --------- -
⧉ Respect visibility, enabled, and read-only state in the form AI. PR:9398
## Description The form AI controller discovered every
HasValuein the form and offered all of them to the model throughget_form_state, no matter their state. A field the application had hidden, disabled, or set read-only was still readable — and writable throughfill_form. So the AI could read values off fields the user cannot see and overwrite fields the user cannot edit. For example, an expense form shows a read-only, computed "Total" and a "Cost center" field enabled only for business trips. Before this change, a prompt like "set the total to 0 and the cost center to X" was carried out even though the user can neither edit the total nor reach the cost center. Changes: - Hidden fields are excluded fromget_form_stateandfill_form. - Disabled and read-only fields stay inget_form_stateas read-only context, each carrying a"disabled"or"readOnly"flag alongside its description and current value.fill_formrejects writes to them with a reason. This keeps context the model may need to fill other fields, while preventing edits. - During a fill turn the controller sets every writable field read-only so the user cannot race the AI. That turn lock is excluded from the"readOnly"flag, so a field the controller locked is never reported as read-only to the model — only application-set read-only is. - The model instructions explain the flags and tell the model to set a controlling field first to unlock a disabled or read-only field. --- 🤖 Generated with Claude Code --------- -
⧉ Resolve eager-items selects without explicit valueOptions. PR:9357
## Summary - An eager
setItems(...)ComboBox/Select/RadioButtonGroup/CheckboxGroup/MultiSelectComboBox was unwritable fromfill_formunless the app also calledFormAIController.valueOptions(...), even though the schema already surfaces those items asenum. - The converter now resolves an LLM-supplied label against the field's in-memory items viarenderItemfor both single- and multi-select, so both halves of the tool protocol agree on the label set without forcing a second registration. 🤖 Generated with Claude Code ---------
-
Changes in vaadin-badge-flow
- New Features:
Changes in vaadin-breadcrumbs-flow
-
New Features:
-
⧉ Add Lumo and Aura theme variants to Breadcrumbs. PR:9543
The web component ships
primary(Lumo) andaccent(Aura) link-color theme variants, butBreadcrumbsVariantonly exposedSLASH. Added missingLUMO_PRIMARY(primary) andAURA_ACCENT(accent) variants. Related to vaadin/web-components#11868 --- 🤖 Generated with Claude Code -
⧉ Add Breadcrumbs TestBench element classes. PR:9530
## Description Task 8 of the Breadcrumbs SDD. Added TestBench element classes. ## Type of change - Feature ## Note The first commit is intentionally fully generated by Claude without any modifications on my part. It contains questionable APIs like
getOverflowOverlay(). I will fix any suggestions in follow up commits. --------- -
⧉ Add Breadcrumbs Mode.ROUTER navigation listener and trail builder. PR:9510
## Description Task 7 of the Breadcrumbs SDD. Added
Mode.ROUTERbuilding logic and navigation listener. ## Type of change - Feature --------- -
⧉ Add Mode.ROUTER guard on Breadcrumbs child management methods. PR:9485. Ticket:9482
## Description Depends on #9483 Task 6 of the Breadcrumbs SDD - based on tasks reorder in vaadin/web-components#11912. Added overrides to disallow manual children modifications when using
Mode.ROUTER. ## Type of change - Feature --------- -
⧉ Validate navigation-related URL schemes. PR:9447
## Motivation Mirror the Flow URL-scheme validation (vaadin/flow#24539) for the navigation, action and link sinks in flow-components. A
javascript:(or other non-allow-listed) scheme on a value that the browser later treats as a link, form action or script source is a familiar XSS shape; rejecting it at the setter closes that path while leaving an explicit escape hatch for trusted, hard-coded URLs. ## Changes Validating setters added across the affected components, each with a matchingsetUnsafe*variant that bypasses validation: -SideNavItem.setPath/BreadcrumbsItem.setPath— navigation link. -Credits.setHref— clickable Highcharts credits link. -AbstractLogin.setAction— the formactionattribute the browser POSTs to on submit. -Exporting.setUrl— Highcharts posts SVG to this URL as a form action for export fallback. -Exporting.setLibURL— concatenated into<script src="…">to load offline-export dependencies at runtime.SideNavItemandBreadcrumbsItemconstructors that take aString pathvalidate transitively viasetPath; their Javadoc now documents@throws IllegalArgumentExceptionand explicit constructor tests cover the behaviour. Safe schemes are configured through thecom.vaadin.safeUrlSchemesproperty and default tohttp,https,mailto,tel,ftp. Image, icon, SVG, Avatar, map tile and chart-resource URLs are intentionally not validated:javascript:cannot execute in<img src>/ SVG<image>elements, anddata:URLs are a legitimate, common use there.LoginFormTestandLoginOverlayTestalready mockLoggerFactoryto capture the existing "action attribute together with login listeners" warning. After merging Flow main, those tests started NPE-ing: Flow'sUrlUtil.isSafeUrldebug-logs a fallback notice, going throughLoggerFactory.getLogger(UrlUtil.class)which returnednullinside the broad mock. The mock now usesMockito.CALLS_REAL_METHODSso unrelated lookups fall through to the real factory; the explicitLoginForm/Overlay.classstubs still take precedence. Part of vaadin/flow#24539 --------- -
⧉ Add Breadcrumbs Mode enum, constructors and mode switching logic. PR:9483. Ticket:9481
## Description Task 5 of the Breadcrumbs SDD - based on tasks reorder in vaadin/web-components#11912. Added
Modeenum withROUTERandMANUALand logic related to mode switching. ## Type of change - Feature --------- -
⧉ Add BreadcrumbsI18n and setI18n API to Breadcrumbs. PR:9473
## Description Task 4 of the Breadcrumbs SDD - based on tasks reorder in vaadin/web-components#11912. Added
BreadcrumbsI18nandsetI18n()API for setting it reflecting the web componenti18nproperty. ## Type of change - Feature -
⧉ Add BreadcrumbsVariant enum and HasThemeVariant to Breadcrumbs. PR:9470
## Description Task 3 of the Breadcrumbs SDD - based on tasks reorder in vaadin/web-components#11912. Added
BreadcrumbsVariantenum with"slash"variant andHasThemeVariant<BreadcrumbsVariant>. ## Type of change - Feature -
⧉ Implement BreadcrumbsItem constructors, text, path, and prefix. PR:9380
Implements
BreadcrumbsItem: the seven constructors mirroringSideNavItem's overload set, thepathaccessors (getPath(),setPath(String),setPath(Class<? extends Component>),setPath(Class, RouteParameters)— the class overloads resolve viaRouteConfigurationexactly likeSideNavItem), and the inheritedHasText/HasEnabled/HasPrefixsurface. Stacked on #9379 (scaffolding) — base branch isbreadcrumbs-flow-scaffolding, so review/merge that first. Kept as a draft until the base PR lands and this can retargetmain. Closes #9378 🤖 Generated with Claude Code -
⧉ Initialize vaadin-breadcrumbs Flow component module. PR:9379
Scaffolds the experimental
vaadin-breadcrumbsFlow component module: thevaadin-breadcrumbs-flow-parentparent with its-flow,-flow-integration-tests, and-testbenchsub-modules, emptyBreadcrumbs/BreadcrumbsItemclass shells, and the experimental feature-flag plumbing (BreadcrumbsFeatureFlagProvider,ExperimentalFeatureException, ServiceLoader registration,onAttachcheck). The component is gated behind thecom.vaadin.experimental.breadcrumbsComponentfeature flag and wraps the@vaadin/breadcrumbsweb component. Smoke tests cover instantiation, Java serialization round-trip, and the feature-flag enforcement on attach. Subsequent tasks (item API,Modeenum, child-management guard, i18n, TestBench query methods, integration tests) land in follow-up PRs. Closes #9377 🤖 Generated with Claude Code
-
-
Fixes:
-
⧉ Keep BreadcrumbsItem prefix component when setting text. PR:9534. Ticket:9533
BreadcrumbsItem.setTextused the defaultHasText#setText, which replaces all of the item's content — so calling it aftersetPrefixComponentremoved the prefix (e.g. an icon).java BreadcrumbsItem item = new BreadcrumbsItem("Inbox (2 items)"); item.setPrefixComponent(new Icon(VaadinIcon.ENVELOPE)); item.setText("Inbox (3 items)"); // icon was lostThe text is now held in a dedicated text node managed throughSignalPropertySupport, the same approachButtonandBadgeuse.setText/bindTextonly update that text node, so the prefix (and any other slotted content) is preserved. --- 🤖 Generated with Claude Code -
⧉ Resolve Breadcrumbs router titles via Router.resolvePageTitle. PR:9531
Flow moved page-title resolution from the internal
RouteUtilto the publicRouter.resolvePageTitle(vaadin/flow#24647), removing theRouteUtil.resolvePageTitle(Instantiator, ...)overload thatBreadcrumbsrelied on. This brokevaadin-breadcrumbs-flowcompilation onmain:Breadcrumbs.java:[436,17] cannot find symbol symbol: method resolvePageTitle(Instantiator,Class<? extends Component>,RouteParameters,QueryParameters)Mode.ROUTERitem titles are now resolved through the publicRouter.resolvePageTitle(target, routeParameters, queryParameters), obtained viaComponentUtil.getRouter(this). This drops the manualVaadinService.getCurrent().getInstantiator()lookup, since the new API resolves the instantiator itself. The unit test's mockedRouteris replaced with a spy over a realRouter, so the realresolvePageTitleruns (titles are asserted) whilegetRegistry()stays stubbed to return the application registry directly. Related to vaadin/flow#24647 --- 🤖 Generated with Claude Code
-
Changes in vaadin-charts-flow
- New Features:
-
⧉ Validate navigation-related URL schemes. PR:9447
## Motivation Mirror the Flow URL-scheme validation (vaadin/flow#24539) for the navigation, action and link sinks in flow-components. A
javascript:(or other non-allow-listed) scheme on a value that the browser later treats as a link, form action or script source is a familiar XSS shape; rejecting it at the setter closes that path while leaving an explicit escape hatch for trusted, hard-coded URLs. ## Changes Validating setters added across the affected components, each with a matchingsetUnsafe*variant that bypasses validation: -SideNavItem.setPath/BreadcrumbsItem.setPath— navigation link. -Credits.setHref— clickable Highcharts credits link. -AbstractLogin.setAction— the formactionattribute the browser POSTs to on submit. -Exporting.setUrl— Highcharts posts SVG to this URL as a form action for export fallback. -Exporting.setLibURL— concatenated into<script src="…">to load offline-export dependencies at runtime.SideNavItemandBreadcrumbsItemconstructors that take aString pathvalidate transitively viasetPath; their Javadoc now documents@throws IllegalArgumentExceptionand explicit constructor tests cover the behaviour. Safe schemes are configured through thecom.vaadin.safeUrlSchemesproperty and default tohttp,https,mailto,tel,ftp. Image, icon, SVG, Avatar, map tile and chart-resource URLs are intentionally not validated:javascript:cannot execute in<img src>/ SVG<image>elements, anddata:URLs are a legitimate, common use there.LoginFormTestandLoginOverlayTestalready mockLoggerFactoryto capture the existing "action attribute together with login listeners" warning. After merging Flow main, those tests started NPE-ing: Flow'sUrlUtil.isSafeUrldebug-logs a fallback notice, going throughLoggerFactory.getLogger(UrlUtil.class)which returnednullinside the broad mock. The mock now usesMockito.CALLS_REAL_METHODSso unrelated lookups fall through to the real factory; the explicitLoginForm/Overlay.classstubs still take precedence. Part of vaadin/flow#24539 ---------
-
Changes in vaadin-combo-box-flow
- Fixes:
-
⧉ Assign data provider after $connector function are defined. PR:9625. Ticket:9622
The connector assigned comboBox.dataProvider near the start of initLazy, before $connector.requestPage and the other $connector functions were defined. Since Vaadin 25.2 the web component runs the dataProvider property observer synchronously, so the assignment immediately triggered a first-page load that called back into $connector.requestPage while it was still undefined. The resulting error aborted connector initialization, leaving the dropdown stuck on the loading spinner with empty items. Assign the data provider last, after all $connector functions exist, so the synchronous first-page load can safely call back into the connector. ---------
-
⧉ Focus selected item with wrapped in-memory data providers. PR:9396
A
ComboBoxwithsetFocusSelectedItem(true)throws aClassCastExceptionwhen opening the dropdown if its items come from aListDataProviderwrapped withwithConvertedFilter(...).scrollToSelectedItem()resolved the selected item's index through the list data view, which casts the provider toListDataProvider. The wrapper is in-memory but not aListDataProvider, so the cast fails.java ComboBox<String> comboBox = new ComboBox<>(); ListDataProvider<String> dataProvider = DataProvider .ofCollection(List.of("A", "B", "C", "D")); comboBox.setItems(dataProvider.withConvertedFilter( filterText -> item -> item.contains(filterText))); comboBox.setFocusSelectedItem(true); comboBox.setValue("C"); comboBox.setOpened(true); // throws ClassCastExceptionscrollToSelectedItem()now resolves the index through the generic data view (ComboBoxDataView) for in-memory providers instead of the list data view. The generic data view reads items from the data communicator and works for any in-memory provider, wrapped or not, so the list data view is no longer used for providers that aren't aListDataProvider. 🤖 Generated with Claude Code
-
Changes in vaadin-crud-flow
- Fixes:
Changes in vaadin-dialog-flow
- New Features:
Changes in vaadin-grid-flow
- Fixes:
-
⧉ Apply aria-multiselectable on grid attach. PR:9504
## Summary - The
isAttachedobserver that updatesaria-multiselectableon attach has not fired since the grid's Polymer to Lit migration: the Lit_createPropertyObservershim only accepts a method name, while the connector passed a function, so the observer was silently never registered. - As a result, a grid whose selection mode was set before the first render kept the template defaultaria-multiselectable="true"regardless of the actual selection mode. - The added WTR test initializes the connector on a detached grid like Flow does and checks the attribute after attach; it fails without the fix. 🤖 Generated with Claude Code -
⧉ Do not request page past end when scrolled to bottom. PR:9490
When the grid is scrolled to the bottom, the connector may request one page more than the grid has. For example, with 200 rows and a page size of 50, it requests rows 150–249 even though the last row is 199. The root cause is that the range end in
getFetchRangeis a row index, but it was clamped togrid.sizeinstead of the last row index,grid.size - 1. That off-by-one could push the end of the request onto the next page, past the end of the data. The item count estimate feature is unaffected:DataCommunicatorgrows the estimate whenever the requested range ends within one page of it, so requesting only the last existing page still triggers the increase. 🤖 Generated with Claude Code -
⧉ Do not include flow-server packages from OSGI manifest. PR:9414. **Tickets:**the,
vaadin-board, OSGI, manifest, to, not, export, 9392## Description
com.vaadin.flow.component.clipboardfromflow-server: - Change the exported package pattern to not use a wildcard before the component name - Introduce aosgi.export.package.additionalvariable, so thatvaadin-gridcan still exportcom.vaadin.flow.component.treegrid, which is otherwise not exported anymore with the new pattern - Remove the-noimport:=truedirective to simplify the implementation, which doesn't seem to make any difference as the same manifests are generated without it - Remove some demo related instructions, which should not be relevant since demo modules have been removed ## Type of change - Bugfix
-
Changes in vaadin-grid-pro-flow
- Fixes:
-
⧉ Sync editor value to server when same value is typed again. PR:9568. Ticket:9437
## Description Since a recent change, Firefox treats input fields that have the same value on blur as they had on focus as unchanged, even if the value was changed programmatically in between. This is problematic for custom editors in GridPro, where the value is set programmatically from the server after the client-side component has already focused the editor. If, after starting editing, a user types exactly the same value that the input had before the server updated the editor value, no change event is fired and the editor value is never synced to the server where it would be used to update the item data: - First edit: - User edits cell, types "foo", commits - "foo" is different from the value the editor had before focus (empty value), property is synced to the server -
item-property-changefires, server detects change in the editor's value, item is updated on server - Second edit (same column, different cell) - User starts editing, web component focuses editor component immediately - Initially the client-side value is still "foo", the server only updates the editor's value once it receives thecell-edit-startedevent, which eventually results in a client-side update of the editor value, let's say to "bar" - User types "foo" again, commits - "foo" is not different from the value the editor had before focusing it, no change event is fired for the editor, it's value is not synced back to the server -item-property-changefires, server does not detect a change in the editor's value, no item update This change addresses the issue by always firing a synthetic change event whenever an input element loses focus. The fix is scoped only Firefox, and only to custom editors. For built-in editors no such fix is needed as the edited value is applied by the web component before focusing. The change is not covered by tests, as the issue only manifests in Firefox whereas tests only run in Chrome. ## Type of change - Bugfix
-
Changes in vaadin-login-flow
- New Features:
-
⧉ Validate navigation-related URL schemes. PR:9447
## Motivation Mirror the Flow URL-scheme validation (vaadin/flow#24539) for the navigation, action and link sinks in flow-components. A
javascript:(or other non-allow-listed) scheme on a value that the browser later treats as a link, form action or script source is a familiar XSS shape; rejecting it at the setter closes that path while leaving an explicit escape hatch for trusted, hard-coded URLs. ## Changes Validating setters added across the affected components, each with a matchingsetUnsafe*variant that bypasses validation: -SideNavItem.setPath/BreadcrumbsItem.setPath— navigation link. -Credits.setHref— clickable Highcharts credits link. -AbstractLogin.setAction— the formactionattribute the browser POSTs to on submit. -Exporting.setUrl— Highcharts posts SVG to this URL as a form action for export fallback. -Exporting.setLibURL— concatenated into<script src="…">to load offline-export dependencies at runtime.SideNavItemandBreadcrumbsItemconstructors that take aString pathvalidate transitively viasetPath; their Javadoc now documents@throws IllegalArgumentExceptionand explicit constructor tests cover the behaviour. Safe schemes are configured through thecom.vaadin.safeUrlSchemesproperty and default tohttp,https,mailto,tel,ftp. Image, icon, SVG, Avatar, map tile and chart-resource URLs are intentionally not validated:javascript:cannot execute in<img src>/ SVG<image>elements, anddata:URLs are a legitimate, common use there.LoginFormTestandLoginOverlayTestalready mockLoggerFactoryto capture the existing "action attribute together with login listeners" warning. After merging Flow main, those tests started NPE-ing: Flow'sUrlUtil.isSafeUrldebug-logs a fallback notice, going throughLoggerFactory.getLogger(UrlUtil.class)which returnednullinside the broad mock. The mock now usesMockito.CALLS_REAL_METHODSso unrelated lookups fall through to the real factory; the explicitLoginForm/Overlay.classstubs still take precedence. Part of vaadin/flow#24539 ---------
-
Changes in vaadin-map-flow
- New Features:
Changes in vaadin-markdown-flow
- New Features:
-
⧉ Add Markdown.bindContent(Signal). PR:9391. Ticket:9388
Allow driving Markdown content from a Signal through a new bindContent method and matching signal constructor. The existing setContent and appendContent throw BindingActiveException while a binding is active to keep component state consistent with the signal. ---------
-
Changes in vaadin-popover-flow
- New Features:
Changes in vaadin-progress-bar-flow
- Fixes:
Changes in vaadin-side-nav-flow
- New Features:
-
⧉ Validate navigation-related URL schemes. PR:9447
## Motivation Mirror the Flow URL-scheme validation (vaadin/flow#24539) for the navigation, action and link sinks in flow-components. A
javascript:(or other non-allow-listed) scheme on a value that the browser later treats as a link, form action or script source is a familiar XSS shape; rejecting it at the setter closes that path while leaving an explicit escape hatch for trusted, hard-coded URLs. ## Changes Validating setters added across the affected components, each with a matchingsetUnsafe*variant that bypasses validation: -SideNavItem.setPath/BreadcrumbsItem.setPath— navigation link. -Credits.setHref— clickable Highcharts credits link. -AbstractLogin.setAction— the formactionattribute the browser POSTs to on submit. -Exporting.setUrl— Highcharts posts SVG to this URL as a form action for export fallback. -Exporting.setLibURL— concatenated into<script src="…">to load offline-export dependencies at runtime.SideNavItemandBreadcrumbsItemconstructors that take aString pathvalidate transitively viasetPath; their Javadoc now documents@throws IllegalArgumentExceptionand explicit constructor tests cover the behaviour. Safe schemes are configured through thecom.vaadin.safeUrlSchemesproperty and default tohttp,https,mailto,tel,ftp. Image, icon, SVG, Avatar, map tile and chart-resource URLs are intentionally not validated:javascript:cannot execute in<img src>/ SVG<image>elements, anddata:URLs are a legitimate, common use there.LoginFormTestandLoginOverlayTestalready mockLoggerFactoryto capture the existing "action attribute together with login listeners" warning. After merging Flow main, those tests started NPE-ing: Flow'sUrlUtil.isSafeUrldebug-logs a fallback notice, going throughLoggerFactory.getLogger(UrlUtil.class)which returnednullinside the broad mock. The mock now usesMockito.CALLS_REAL_METHODSso unrelated lookups fall through to the real factory; the explicitLoginForm/Overlay.classstubs still take precedence. Part of vaadin/flow#24539 ---------
-
Changes in vaadin-spreadsheet-flow
- Fixes:
-
⧉ Allow filtering out all values in spreadsheet filter. PR:9574. Ticket:9549
In the spreadsheet filter popup, unchecking "Select All" had no effect, and when unchecking values one by one, the last one didn't do anything either. The selection looked empty, but no filtering happened, and after closing the popup everything was selected again, which was confusing. Unchecking now hides the rows and the deselected state persists. The PR also hides the "Select All" checkbox entirely when all of a column's values are already filtered out by other columns. 🤖 Generated with Claude Code
-
⧉ Preserve custom editors and scope the display callback. PR:9493
When a
SpreadsheetComponentFactoryreturns a new editor instance fromgetCustomEditorForCell(the common pattern, e.g.return new ComboBox<>()), that editor was re-created on every re-render that kept the same cells visible — scrolling, changing the selection, or resizing a row or column. This wiped any value the user had typed but not yet written into the cell, left orphaned editor instances registered on the client, and re-firedonCustomEditorDisplayedeven when the selected cell never changed. To reproduce: a factory whosegetCustomEditorForCellreturns a newComboBoxfor a cell. Select the cell, pick or type a value, then scroll, select another cell or range, or resize a row or column. The value is cleared, andonCustomEditorDisplayedruns again for the same selection. ## Changes - Keep one server-side map from cell key to the editor instance already shown there. WhenloadCellsre-renders the same visible cells (scroll, selection, row/column resize), reuse that instance instead of asking the factory for a new one, so editor state (such as an uncommitted value) survives. The factory is only called the first time a cell needs an editor. - Recreate editors from the factory only when the application forces it:reloadVisibleCellContents(), a factory change, a sheet change, or a detach/attach reload. These paths drop the cache so the factory is asked for fresh instances, keeping the existing public-API behavior unchanged. - FireonCustomEditorDisplayedonly when the selected cell or its editor actually changes, not on every re-render of the same selection. - Derive the clientcellKeysToEditorIdMapproperty (cell key to editor node id) from the single editor map each time it is sent, instead of storing a second map, so the two can no longer drift apart. - Manage the editor map through onesetCellKeyToEditorMapsetter: a non-null map replaces the server map and pushes the derived node ids to the client; anullclears the server map, resets the callback tracker, and sends anullproperty (which has different client semantics than an empty map). Part of #9180 --- 🤖 Generated with Claude Code --------- -
⧉ Make spreadsheet overlays native popover, per instance. PR:9303
The spreadsheet attaches a
<div id="spreadsheet-overlays">to host its overlay widgets (context menu, tooltips, comment overlays, popup buttons). It has historically lived in<body>to escape clipping by ancestoroverflow/transformrules and as a singleton shared by all spreadsheet instances on the page. Switching the overlays to native popover changes the picture: every overlay enters the top layer regardless of DOM position, so neither the body-attachment nor the singleton are load-bearing anymore. This PR includes the native popover changes proposed in #9270 and adds the structural changes that the popover approach enables: - the container moves into the<vaadin-spreadsheet>light DOM, projected into the shadow root via a newoverlaysslot, with one container per instance; - the reference is threaded from JS into the GWT widget chain instead of being looked up by id; -SpreadsheetConnector— notApplicationConnection— now owns theSpreadsheetContextMenu. Tooltips and the cell-comment overlay are created inSheetWidget's constructor (before the host is known), so the container is wired in via a setter rather than a constructor argument. A side benefit: spreadsheet overlays inside a modalDialogare interactive again. The modal setsbody { pointer-events: none }to disable everything outside the dialog overlay; with the container now inside the dialog's slotted content, the overlays inheritpointer-events: autofrom the dialog's overlay part instead ofpointer-events: nonefrom<body>. Intended to replace #9270. Related to #9270 --- 🤖 Generated with Claude Code --------- -
⧉ Defer Spreadsheet.selectCellRange until sheet widget layout is ready. PR:9381
Follow-up to #9333. When a Spreadsheet inside a closed Dialog is opened, Flow flushes the property writes and the
setSelectedCellAndRangeRPC in the same task. The api receives the call before the deferred init scheduled inSheetWidget.resetFromModelhas run — soSheetWidget.getRowHeightreads an uninitializeddefinedRowHeightsand throwsUncaught TypeError: Cannot read properties of undefined (reading 'length')That throw drops the selection and trips the*_noErrors/*_noConsoleErrorsconsole-log assertions. #9333's connector-side guard masked this by probingdefinedRowHeightsthrough the api's private GWT fields — names that only survive-Dgwt.style=pretty(local/dev). CI compiles the GWT client with-Dgwt.style=OBF, where those private names are renamed andapi.spreadsheetWidgetisundefined. The guard itself then threw, producing the same SEVERE log and the same ~20 spreadsheet IT failures on the cherry-pick PRs. Move the layout-readiness handling into the widget that owns the operation: -SheetWidget.isLoaded()(package-private) — exposes the already-existingloadedflag that flips at the end ofresetFromModel's deferred command. -SpreadsheetWidget.selectCellRange— if!sheetWidget.isLoaded(), store the latest args andScheduler.scheduleDeferred(this::applyPendingSelectCellRange). The deferred re-checks, applies, or re-schedules. Latest-wins (rapid successive calls collapse to the final selection). Bounded at 100 hops as a safety net; in the typical case the layout init has already been scheduled before us, so GWT's FIFO scheduler resolves us in 0–1 hops. -vaadin-spreadsheet.js—setSelectedCellAndRangegoes back to a one-liner (this._flush(); this.api.setSelectedCellAndRange(...args)). No try/catch, no pending state, no constants. OnlyselectCellRangeis deferred — it's the only RPC that touches row/column metrics. Other RPCs (cellsUpdated, popup ops, etc.) don't need this and didn't fail in the original CI runs. Reproduce locally by compiling the client with-Dgwt.style=OBF(matching CI) and running the merged ITs in production mode:mvn clean install -DskipTests -Dgwt.style=OBF \ -pl vaadin-spreadsheet-flow-parent/vaadin-spreadsheet-flow-client,vaadin-spreadsheet-flow-parent/vaadin-spreadsheet-flow node scripts/mergeITs.js spreadsheet mvn verify -Drun-it -pl integration-tests -Dvaadin.productionMode=true \ -Dit.test='NavigationIT,SpreadsheetDialogIT' -DskipUnitTests- With this PR → NavigationIT 26/0, SpreadsheetDialogIT 1/0. - Reverting only the GWT-side change → SpreadsheetDialogIT fails with the underlyingCannot read properties of undefined (reading 'length')inFlowClient-…js— the actualdefinedRowHeightsNPE. Follow-up to #9333. --- 🤖 Generated with Claude Code --------- -
⧉ Hide already-filtered values from other spreadsheet column filters. PR:9386. Ticket:9329
## Description In a
SpreadsheetFilterTablewith filters on multiple columns, opening any column's filter dropdown showed values from rows already hidden by other columns as unchecked, suggesting they were filtered by this column. Re-checking them did nothing because another column still hid the row. Applying a second filter then implicitly added those already-hidden rows to the second column'sfilteredRows, so clearing the first filter did not bring the rows back — the second column now also filtered them. Root cause:ItemFilterreadspreadsheet.isRowHidden(r)for both building option lists and deciding which rows belong in its ownfilteredRows. That property reflects the union of all column filters, so each filter could not tell its own work from a sibling's. EachItemFilternow operates only on its ownfilteredRows. The dropdown option list omits values whose rows are all hidden by other columns via a newSpreadsheetFilterTable.getRowsHiddenByOtherFilters(self)helper.clearAllFiltersdoes a second pass to refresh option lists so cleared siblings are reflected immediately. ## Reproductionjava @Route("spreadsheet-filter-cross-column") public class SpreadsheetFilterCrossColumnPage extends VerticalLayout { public SpreadsheetFilterCrossColumnPage() { Spreadsheet spreadsheet = new Spreadsheet(); spreadsheet.setTheme(SpreadsheetTheme.LUMO); spreadsheet.setHeight("400px"); spreadsheet.createCell(0, 0, "Column A"); spreadsheet.createCell(0, 1, "Column B"); spreadsheet.createCell(0, 2, "Column C"); spreadsheet.createCell(1, 0, "Alpha"); spreadsheet.createCell(1, 1, "Foo"); spreadsheet.createCell(1, 2, "Alice"); spreadsheet.createCell(2, 0, "Beta"); spreadsheet.createCell(2, 1, "Bar"); spreadsheet.createCell(2, 2, "Bob"); spreadsheet.createCell(3, 0, "Gamma"); spreadsheet.createCell(3, 1, "Baz"); spreadsheet.createCell(3, 2, "Carol"); CellRangeAddress range = new CellRangeAddress(0, 3, 0, 2); SpreadsheetFilterTable table = new SpreadsheetFilterTable(spreadsheet, range); spreadsheet.registerTable(table); spreadsheet.refreshAllCellValues(); add(spreadsheet); } }| Column A | Column B | Column C | | -------- | -------- | -------- | | Alpha | Foo | Alice | | Beta | Bar | Bob | | Gamma | Baz | Carol | Steps to reproduce: - FilterBetain column A. - Open the filter dropdown in column B →Barshows up unchecked (before the fix), suggesting column B filtered it. - FilterFooin column B. - Re-checkBetain column A → the row does not reappear (before the fix), because column B now also filters it. 🤖 Generated with Claude Code --------- -
⧉ Defer setSelectedCellAndRange until sheet layout is ready. PR:9373
Follow-up to #9333. The connector's
setSelectedCellAndRangesilently dropped calls when the sheet widget'sdefinedRowHeightswasn't yet populated, on the assumption that Flow would reissue the call. That assumption only holds for the reload cycle path (initialSheetSelectionset on the server, thenonSheetScrollfrom the client triggeringreloadCurrentSelection). For any other path — most notably the selection RPC sent when the user types a range into the address field — there is no reissue, so a call that loses the race against GWT's deferred initial relayout is lost permanently. This caused the cherry-pick PRs (#9365, #9366, #9367) to consistently fail 20 selection-related tests (NavigationIT,NamedRangeIT,ChartsIT,SheetTabSheetIT,PreserveOnRefreshIT, and the newSpreadsheetDialogIT). The original PR happened to pass on main's CI agent timing, but the failure is deterministic on the cherry-pick branches' GWT 2.9 / older-Flow-SNAPSHOT environment. Queue the latest call as_pendingSelectionand polldefinedRowHeightsevery 10 ms until it's set or the element is disconnected. Rapid successive calls collapse to the final selection (the array is overwritten, only one retry loop runs at a time). Follow-up to #9333. --- 🤖 Generated with Claude Code
-
Changes in vaadin-upload-flow
- Fixes:
-
⧉ Avoid serializing full File instance for determining uploading state. PR:9444. Ticket:9434
## Description The Flow Upload component currently listens to several web component events to determine whether the component is still processing any upload or not. To facilitate that it sends fully serialized instances of all JS
Fileobjects to the server in order to access theuploadingproperty of each. Serializing the full File object can lead to circular serialization issues as reported in #9434. This changes the respective event listeners to determine the uploading state on the client via a JS expression and then only sends a single boolean with the event, rather than the full File object. This avoids serializing File objects completely and also reduces the payload of the event. ## Type of change - Bugfix
-
Compatibility
- This release use Web Components listed in Vaadin Platform 25.2.0-beta2
- Tested with Vaadin Flow version 25.3-SNAPSHOT

