diff --git a/articles/building-apps/views/pass-data/query-parameters.adoc b/articles/building-apps/views/pass-data/query-parameters.adoc index 30d715e436..0d87c53f5c 100644 --- a/articles/building-apps/views/pass-data/query-parameters.adoc +++ b/articles/building-apps/views/pass-data/query-parameters.adoc @@ -94,6 +94,15 @@ public class OrdersView extends Main implements BeforeEnterObserver { [IMPORTANT] You _cannot_ set query parameters to `null`. To clear a query parameter, exclude it from the [classname]`QueryParameters` object. +[since:com.vaadin:vaadin@V25.3]#When you navigate by location string, the query string can also be part of the string itself, which is handy for a link that's already assembled -- one that comes from a mail template or an external system, for example. The query string is parsed into the [classname]`QueryParameters` of the resulting location, so the view reads it the same way either way:# + +[source,java] +---- +UI.getCurrentOrThrow().navigate("orders?filter=late&sort=due"); +---- + +The string form and a separate [classname]`QueryParameters` object are alternatives, not a combination: [methodname]`navigate(String, QueryParameters)` throws an [classname]`IllegalArgumentException` if the location carries a query string or a fragment of its own while the parameters aren't empty, since the ones in the string would be lost. A location that consists only of a fragment, such as `"#totals"`, identifies no route and is passed on to the client router as it is. + == Binding a Field to a Query Parameter diff --git a/articles/flow/advanced/application-lifecycle.adoc b/articles/flow/advanced/application-lifecycle.adoc index 9bddd4260c..188457aff4 100644 --- a/articles/flow/advanced/application-lifecycle.adoc +++ b/articles/flow/advanced/application-lifecycle.adoc @@ -84,7 +84,8 @@ See <<{articles}/flow/advanced/session-and-ui-init-listener#, Session and UI Lis ((("UI", "loading"))) When a browser first accesses a URL mapped to the servlet of a particular UI class, the Vaadin servlet generates a loader page. -The page loads the client-side engine (widget set), which in turn loads the UI in a separate request to the Vaadin servlet. +The page loads the client-side engine, which in turn loads the UI in a separate request to the Vaadin servlet. +[since:com.vaadin:vaadin@V25.3]#The engine is written in TypeScript and is bundled with the rest of the frontend by Vite. Earlier versions loaded an engine compiled from Java with Google Web Toolkit (GWT) from `VAADIN/static/client/`.# A [classname]`UI` instance is created when the client-side engine makes its first request. @@ -99,7 +100,7 @@ To do this, you need to extend the [classname]`VaadinServlet` and add a [interfa You can then add the bootstrap listener to a user session with the [methodname]`addIndexHtmlRequestListener()` method when the session is initialized. -Loading the widget set is handled in the loader page with functions defined in a separate [filename]`BootstrapHandler.js` script, whose content is included inline in the page. +Loading the client-side engine is handled in the loader page with functions defined in a separate [filename]`BootstrapHandler.js` script, whose content is included inline in the page. [[application.lifecycle.ui-expiration]] == UI Expiration diff --git a/articles/flow/routing/navigation.adoc b/articles/flow/routing/navigation.adoc index b0838adbb4..dbaf0560dc 100644 --- a/articles/flow/routing/navigation.adoc +++ b/articles/flow/routing/navigation.adoc @@ -72,6 +72,8 @@ editButton.addClickListener(e -> In addition to handling <> as in the above example, the [methodname]`UI.navigate()` method has other overloads that allow you to pass <> or <> to the target view. +[since:com.vaadin:vaadin@V25.3]#A location string may carry its query string and fragment itself, as in [methodname]`ui.navigate("user/123?tab=orders#totals")`. The query string is parsed into the query parameters of the resulting location. Passing a [classname]`QueryParameters` object alongside such a location is an error -- the parameters in the string would be lost -- and throws an [classname]`IllegalArgumentException`.# + == Using the RouterLink Component [classname]`RouterLink` is a special component based on the tag to create links pointing to route targets in your application. diff --git a/articles/flow/security/advanced-topics/frequent-issues.adoc b/articles/flow/security/advanced-topics/frequent-issues.adoc index 5ab81522d8..200b079aab 100644 --- a/articles/flow/security/advanced-topics/frequent-issues.adoc +++ b/articles/flow/security/advanced-topics/frequent-issues.adoc @@ -14,7 +14,7 @@ From time to time, Vaadin users perform security tests on the framework and repo == Content-Security-Policy (CSP) Set to Unsafe Values -The settings `script-src 'unsafe-inline' 'unsafe-eval'` and `style-src 'unsafe-inline'` are required during Vaadin application start, that is, the bootstrap process. The bootstrap process that starts the application loads the widget set which is the client-side engine part of the application. This consists of precompiled JavaScript logic, for example, for the communication protocol, DOM control, Buttons, Layouts, etc., but not the application code. The widget set is a static resource. After it's loaded, the client-side engine needs to be started using [methodname]`JavaScript.eval()`. +The settings `script-src 'unsafe-inline' 'unsafe-eval'` and `style-src 'unsafe-inline'` are required during Vaadin application start, that is, the bootstrap process. The bootstrap process that starts the application loads the client-side engine, which is the part of the framework that runs in the browser. It consists of JavaScript logic for the communication protocol and DOM control, for example, but not of the application code. The engine is a static resource, and once it's loaded, it's started by a script that the bootstrap page includes inline. The engine then evaluates the JavaScript expressions that the server sends -- those of [methodname]`executeJs()` and of the event data expressions -- by constructing functions from them in the browser, which is what `unsafe-eval` permits. Hence, these settings are architectural limitations in Vaadin, so that the framework can start its client-side engine in the browser. @@ -76,7 +76,7 @@ This happens when Vaadin sends user events to the server and receives JSON data == Open Redirection – DOM-Based -This issue is reported because `vaadinBootstrap.js` indeed opens a new HTTP request. This is done to fetch the initial application state. On first request to an application URL, Vaadin replies with the bootstrap file, which, in turn loads the theme, widget set, and application state. +This issue is reported because `vaadinBootstrap.js` indeed opens a new HTTP request. This is done to fetch the initial application state. On first request to an application URL, Vaadin replies with the bootstrap file, which, in turn loads the theme, client-side engine, and application state. The way this request is done can't be used by an attacker to modify the application state. Hence, this is a false positive. diff --git a/articles/flow/security/enabling-security.adoc b/articles/flow/security/enabling-security.adoc index db4a390824..f97876d0c6 100644 --- a/articles/flow/security/enabling-security.adoc +++ b/articles/flow/security/enabling-security.adoc @@ -285,6 +285,10 @@ Configure form login with [methodname]`loginView()` rather than by calling Sprin A hand-written [methodname]`formLogin()` configuration does none of that. The internal request that the log-in view itself makes to the server is then treated as an unauthenticated request and redirected back to the log-in view, which produces an endless redirect loop -- typically visible as an `ERR_TOO_MANY_REDIRECTS` page or a "Connection lost" notification instead of a working log-in form. ==== +.Blocked Sub-Resources Answer With 401 +[NOTE] +[since:com.vaadin:vaadin@V25.3]#With a log-in view configured, an unauthorized request that the browser makes for a sub-resource -- a stylesheet, script, image, font, or web app manifest -- is answered with `401 Unauthorized` rather than redirected to the log-in view, which such a request can't render. The browser then reports the resource that was blocked, instead of failing in a redirect loop. Permit the path in the security configuration to serve the resource; see <<{articles}/upgrading#spring-security-and-stylesheet, Spring Security and StyleSheet>>.# + .Never Use Hard-Coded Credentials in Production [WARNING] The implementation of the [methodname]`userDetailsService()` method is just an in-memory implementation for the sake of brevity in this documentation. In a normal application, you can change the Spring Security configuration to use an authentication provider for Lightweight Directory Access Protocol (LDAP), JAAS, and other real-world sources. See https://dzone.com/articles/flow/spring-security-authentication[Spring Security authentication providers] to read more about them. diff --git a/articles/flow/testing/browserless/component-query.adoc b/articles/flow/testing/browserless/component-query.adoc index 7ad1d61b4b..4bd5d522e2 100644 --- a/articles/flow/testing/browserless/component-query.adoc +++ b/articles/flow/testing/browserless/component-query.adoc @@ -128,6 +128,11 @@ The following table lists all available filter methods, grouped by category: | [methodname]`withoutAttribute(String)` | Excludes components that have the given attribute. +2+h| Slots + +| [since:com.vaadin:vaadin@V25.3]#[methodname]`withinSlot(String)`# +| Matches components that sit in the given named slot of the component that hosts them. See <>. + 2+h| Value & Properties | [methodname]`withValue(V)` @@ -192,6 +197,26 @@ Button submitButton = find(Button.class).testId("submit-button"); To combine a test ID with other filter conditions, use the [methodname]`withTestId()` filter method instead and finish the chain with a regular terminal operator. Since test IDs are expected to be unique, both forms fail if more than one component matches. See <> for more on test IDs. +[[slots]] +[role="since:com.vaadin:vaadin@V25.3"] +=== Filtering by Slot + +Components such as [classname]`Card`, [classname]`Dialog`, and [classname]`SplitLayout` place content in named slots. An unfiltered query returns matches from all of them, and [methodname]`withinSlot()` narrows the result to one slot: + +[source,java] +---- +// Only the buttons the card put in its footer +List