From 2282aaf683505aab7b23ad4ec921d7ab2b05c5ca Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Wed, 14 Jan 2026 16:42:05 +0400 Subject: [PATCH 01/36] feat: add vaadin-slider-flow-parent module Add Slider Flow component wrapping web component. The Slider extends AbstractSinglePropertyField and provides min, max, step, and value properties. Co-Authored-By: Claude Opus 4.5 --- flow-components-bom/pom.xml | 10 ++ pom.xml | 1 + vaadin-slider-flow-parent/pom.xml | 31 ++++ .../pom.xml | 156 ++++++++++++++++++ .../vaadin-slider-flow/pom.xml | 93 +++++++++++ .../vaadin/flow/component/slider/Slider.java | 130 +++++++++++++++ .../vaadin-slider-testbench/pom.xml | 45 +++++ .../slider/testbench/SliderElement.java | 36 ++++ 8 files changed, 502 insertions(+) create mode 100644 vaadin-slider-flow-parent/pom.xml create mode 100644 vaadin-slider-flow-parent/vaadin-slider-flow-integration-tests/pom.xml create mode 100644 vaadin-slider-flow-parent/vaadin-slider-flow/pom.xml create mode 100644 vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java create mode 100644 vaadin-slider-flow-parent/vaadin-slider-testbench/pom.xml create mode 100644 vaadin-slider-flow-parent/vaadin-slider-testbench/src/main/java/com/vaadin/flow/component/slider/testbench/SliderElement.java diff --git a/flow-components-bom/pom.xml b/flow-components-bom/pom.xml index 76be01ecbdf..00d90a2d8b7 100644 --- a/flow-components-bom/pom.xml +++ b/flow-components-bom/pom.xml @@ -417,6 +417,16 @@ vaadin-side-nav-testbench ${project.version} + + com.vaadin + vaadin-slider-flow + ${project.version} + + + com.vaadin + vaadin-slider-testbench + ${project.version} + com.vaadin vaadin-split-layout-flow diff --git a/pom.xml b/pom.xml index 161dbfed307..ab8b084d619 100644 --- a/pom.xml +++ b/pom.xml @@ -61,6 +61,7 @@ vaadin-spreadsheet-flow-parent vaadin-field-highlighter-flow-parent vaadin-side-nav-flow-parent + vaadin-slider-flow-parent vaadin-master-detail-layout-flow-parent vaadin-ai-components-flow-parent diff --git a/vaadin-slider-flow-parent/pom.xml b/vaadin-slider-flow-parent/pom.xml new file mode 100644 index 00000000000..9aa460c95ad --- /dev/null +++ b/vaadin-slider-flow-parent/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + + com.vaadin + vaadin-flow-components + 25.1-SNAPSHOT + + vaadin-slider-flow-parent + pom + Vaadin Slider Parent + Vaadin Slider Parent + + vaadin-slider-flow + vaadin-slider-testbench + + + + + default + + + !release + + + + vaadin-slider-flow-integration-tests + + + + diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow-integration-tests/pom.xml b/vaadin-slider-flow-parent/vaadin-slider-flow-integration-tests/pom.xml new file mode 100644 index 00000000000..269c17821b2 --- /dev/null +++ b/vaadin-slider-flow-parent/vaadin-slider-flow-integration-tests/pom.xml @@ -0,0 +1,156 @@ + + + 4.0.0 + + com.vaadin + vaadin-slider-flow-parent + 25.1-SNAPSHOT + + vaadin-slider-integration-tests + war + Vaadin Slider Integration Tests + Vaadin Slider Integration Tests + + + com.vaadin + flow-client + ${flow.version} + + + com.vaadin + flow-html-components + ${flow.version} + + + com.vaadin + flow-test-generic + test + + + com.vaadin + flow-test-util + test + + + com.vaadin + vaadin-dev-server + + + com.vaadin + vaadin-flow-components-test-util + ${project.version} + test + + + com.vaadin + vaadin-slider-flow + ${project.version} + + + com.vaadin + vaadin-slider-testbench + ${project.version} + + + com.vaadin + vaadin-testbench-core + test + + + org.slf4j + slf4j-simple + + + + + + maven-clean-plugin + + + + ${project.basedir} + + package*.json + pnpm* + vite.generated.ts + types.d.ts + tsconfig.json + frontend/routes.tsx + frontend/App.tsx + + + + ${project.basedir}/node_modules + + + ${project.basedir}/frontend/generated + + + + + + maven-failsafe-plugin + + + maven-resources-plugin + + + org.sonatype.plugins + nexus-staging-maven-plugin + + true + + + + org.codehaus.mojo + properties-maven-plugin + + + maven-install-plugin + + true + + + + + + + build-frontend + + + !skipFrontend + + + + + + com.vaadin + flow-maven-plugin + + ./frontend + + + + + + + run-jetty + + + !skipJetty + + + + + + org.eclipse.jetty.ee10 + jetty-ee10-maven-plugin + + 5 + + + + + + + diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/pom.xml b/vaadin-slider-flow-parent/vaadin-slider-flow/pom.xml new file mode 100644 index 00000000000..691671efcf1 --- /dev/null +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/pom.xml @@ -0,0 +1,93 @@ + + + 4.0.0 + + com.vaadin + vaadin-slider-flow-parent + 25.1-SNAPSHOT + + vaadin-slider-flow + jar + Vaadin Slider + Vaadin Slider + + + com.vaadin + flow-server + provided + + + com.vaadin + flow-test-generic + test + + + com.vaadin + vaadin-flow-components-base + ${project.version} + + + com.vaadin + vaadin-flow-components-test-util + ${project.version} + test + + + jakarta.platform + jakarta.jakartaee-web-api + test + + + jakarta.servlet + jakarta.servlet-api + + + org.slf4j + slf4j-simple + test + + + + + + biz.aQute.bnd + bnd-maven-plugin + + + org.apache.maven.plugins + maven-jar-plugin + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + + + + attach-docs + + + with-docs + + + + + + org.apache.maven.plugins + maven-source-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + + org.codehaus.mojo + build-helper-maven-plugin + + + + + + diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java new file mode 100644 index 00000000000..7d1196d2d64 --- /dev/null +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java @@ -0,0 +1,130 @@ +/* + * Copyright 2000-2026 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.flow.component.slider; + +import com.vaadin.flow.component.AbstractSinglePropertyField; +import com.vaadin.flow.component.HasSize; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.component.dependency.JsModule; +import com.vaadin.flow.component.dependency.NpmPackage; + +/** + * Slider is an input component that allows the user to select a numeric value + * within a range by dragging a handle along a track. + * + * @author Vaadin Ltd. + */ +@Tag("vaadin-slider") +@NpmPackage(value = "@vaadin/slider", version = "25.1.0-alpha1") +@JsModule("@vaadin/slider/src/vaadin-slider.js") +public class Slider extends AbstractSinglePropertyField + implements HasSize { + + /** + * Constructs a new Slider with default values (min=0, max=100, value=0). + */ + public Slider() { + this(0, 100); + } + + /** + * Constructs a new Slider with the given min and max values. The initial + * value is set to min. + * + * @param min + * the minimum value + * @param max + * the maximum value + */ + public Slider(double min, double max) { + this(min, max, min); + } + + /** + * Constructs a new Slider with the given min, max, and initial value. + * + * @param min + * the minimum value + * @param max + * the maximum value + * @param value + * the initial value + */ + public Slider(double min, double max, double value) { + super("value", 0.0, false); + setMin(min); + setMax(max); + setValue(value); + } + + /** + * Sets the minimum value of the slider. + * + * @param min + * the minimum value + */ + public void setMin(double min) { + getElement().setProperty("min", min); + } + + /** + * Gets the minimum value of the slider. + * + * @return the minimum value + */ + public double getMin() { + return getElement().getProperty("min", 0.0); + } + + /** + * Sets the maximum value of the slider. + * + * @param max + * the maximum value + */ + public void setMax(double max) { + getElement().setProperty("max", max); + } + + /** + * Gets the maximum value of the slider. + * + * @return the maximum value + */ + public double getMax() { + return getElement().getProperty("max", 100.0); + } + + /** + * Sets the step value of the slider. The step is the amount the value + * changes when the user moves the handle. + * + * @param step + * the step value + */ + public void setStep(double step) { + getElement().setProperty("step", step); + } + + /** + * Gets the step value of the slider. + * + * @return the step value + */ + public double getStep() { + return getElement().getProperty("step", 1.0); + } +} diff --git a/vaadin-slider-flow-parent/vaadin-slider-testbench/pom.xml b/vaadin-slider-flow-parent/vaadin-slider-testbench/pom.xml new file mode 100644 index 00000000000..03f35c68820 --- /dev/null +++ b/vaadin-slider-flow-parent/vaadin-slider-testbench/pom.xml @@ -0,0 +1,45 @@ + + + 4.0.0 + + com.vaadin + vaadin-slider-flow-parent + 25.1-SNAPSHOT + + vaadin-slider-testbench + jar + Vaadin Slider Testbench API + Vaadin Slider Testbench API + + + com.vaadin + vaadin-testbench-shared + provided + + + + + + + + attach-docs + + + with-docs + + + + + + org.apache.maven.plugins + maven-source-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + + + diff --git a/vaadin-slider-flow-parent/vaadin-slider-testbench/src/main/java/com/vaadin/flow/component/slider/testbench/SliderElement.java b/vaadin-slider-flow-parent/vaadin-slider-testbench/src/main/java/com/vaadin/flow/component/slider/testbench/SliderElement.java new file mode 100644 index 00000000000..1a03e839f4f --- /dev/null +++ b/vaadin-slider-flow-parent/vaadin-slider-testbench/src/main/java/com/vaadin/flow/component/slider/testbench/SliderElement.java @@ -0,0 +1,36 @@ +/* + * Copyright 2000-2026 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.flow.component.slider.testbench; + +import com.vaadin.testbench.TestBenchElement; +import com.vaadin.testbench.elementsbase.Element; + +/** + * A TestBench element representing a <vaadin-slider> + * element. + */ +@Element("vaadin-slider") +public class SliderElement extends TestBenchElement { + + /** + * Gets the current value of the slider. + * + * @return the current value + */ + public double getValue() { + return getPropertyDouble("value"); + } +} From cd98555aec47818ffe64b8d8878002bfe8b946d7 Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Thu, 15 Jan 2026 11:19:23 +0400 Subject: [PATCH 02/36] fix: address PR review comments - Remove explicit flow dependency versions from integration-tests pom.xml - Add README.md and LICENSE files - Add vite.config.ts for integration tests Co-Authored-By: Claude Opus 4.5 --- vaadin-slider-flow-parent/LICENSE | 202 ++++++++++++++++++ vaadin-slider-flow-parent/README.md | 20 ++ .../pom.xml | 2 - .../vite.config.ts | 16 ++ 4 files changed, 238 insertions(+), 2 deletions(-) create mode 100644 vaadin-slider-flow-parent/LICENSE create mode 100644 vaadin-slider-flow-parent/README.md create mode 100644 vaadin-slider-flow-parent/vaadin-slider-flow-integration-tests/vite.config.ts diff --git a/vaadin-slider-flow-parent/LICENSE b/vaadin-slider-flow-parent/LICENSE new file mode 100644 index 00000000000..a8cdb96073f --- /dev/null +++ b/vaadin-slider-flow-parent/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2000-2026 Vaadin Ltd. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vaadin-slider-flow-parent/README.md b/vaadin-slider-flow-parent/README.md new file mode 100644 index 00000000000..4ca53b4383c --- /dev/null +++ b/vaadin-slider-flow-parent/README.md @@ -0,0 +1,20 @@ +# Slider component for Vaadin Flow + +This project is the Component wrapper implementation of [``](https://github.com/vaadin/web-components/tree/main/packages/slider) +element for use from the server side with [Vaadin Flow](https://github.com/vaadin/flow). + +## Using the component in a Flow application + +To use the component in an application using maven, +add the following dependency to your `pom.xml`: +``` + + com.vaadin + vaadin-slider-flow + ${component.version} + +``` + +## License + +Apache License 2.0 diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow-integration-tests/pom.xml b/vaadin-slider-flow-parent/vaadin-slider-flow-integration-tests/pom.xml index 269c17821b2..0ab323d8c2e 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow-integration-tests/pom.xml +++ b/vaadin-slider-flow-parent/vaadin-slider-flow-integration-tests/pom.xml @@ -14,12 +14,10 @@ com.vaadin flow-client - ${flow.version} com.vaadin flow-html-components - ${flow.version} com.vaadin diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow-integration-tests/vite.config.ts b/vaadin-slider-flow-parent/vaadin-slider-flow-integration-tests/vite.config.ts new file mode 100644 index 00000000000..bd0ce4ce0ae --- /dev/null +++ b/vaadin-slider-flow-parent/vaadin-slider-flow-integration-tests/vite.config.ts @@ -0,0 +1,16 @@ +// @ts-ignore can not be resolved until NPM packages are installed +import { defineConfig, UserConfigFn } from 'vite'; +// @ts-ignore can not be resolved until Flow generates base Vite config +import { vaadinConfig } from './vite.generated'; +import { sharedConfig, mergeConfigs } from '../../shared/shared-vite-config'; + +const customConfig: UserConfigFn = (env) => ({ + // Here you can add custom Vite parameters + // https://vitejs.dev/config/ +}); + +export default defineConfig((env) => mergeConfigs( + vaadinConfig(env), + sharedConfig(env), + customConfig(env) +)); From 69da7a779bb2794d700ea5afd27ccf7afea1a2f0 Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Thu, 15 Jan 2026 11:46:04 +0400 Subject: [PATCH 03/36] test: add unit tests for Slider component Add tests for constructors, min/max/step properties, and validation that throws IllegalArgumentException for invalid values. Co-Authored-By: Claude Opus 4.5 --- .../vaadin/flow/component/slider/Slider.java | 20 ++++ .../component/slider/tests/SliderTest.java | 110 ++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java index 7d1196d2d64..15b60c73add 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java @@ -70,6 +70,16 @@ public Slider(double min, double max, double value) { setValue(value); } + @Override + public void setValue(Double value) { + if (value < getMin() || value > getMax()) { + throw new IllegalArgumentException( + "The value must be between min and max"); + } + + super.setValue(value); + } + /** * Sets the minimum value of the slider. * @@ -77,6 +87,11 @@ public Slider(double min, double max, double value) { * the minimum value */ public void setMin(double min) { + if (min > getMax()) { + throw new IllegalArgumentException( + "The min value cannot be greater than the max value"); + } + getElement().setProperty("min", min); } @@ -96,6 +111,11 @@ public double getMin() { * the maximum value */ public void setMax(double max) { + if (max < getMin()) { + throw new IllegalArgumentException( + "The max value cannot be less than the min value"); + } + getElement().setProperty("max", max); } diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java new file mode 100644 index 00000000000..e2534cc11c0 --- /dev/null +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java @@ -0,0 +1,110 @@ +/* + * Copyright 2000-2026 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.flow.component.slider.tests; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.flow.component.slider.Slider; + +public class SliderTest { + @Test + public void defaultConstructor() { + Slider slider = new Slider(); + Assert.assertEquals(0.0, slider.getMin(), 0.0); + Assert.assertEquals(100.0, slider.getMax(), 0.0); + Assert.assertEquals(0.0, slider.getValue(), 0.0); + } + + @Test + public void minMaxConstructor() { + Slider slider = new Slider(10.0, 50.0); + Assert.assertEquals(10.0, slider.getMin(), 0.0); + Assert.assertEquals(50.0, slider.getMax(), 0.0); + Assert.assertEquals(10.0, slider.getValue(), 0.0); + } + + @Test + public void minMaxValueConstructor() { + Slider slider = new Slider(10.0, 50.0, 25.0); + Assert.assertEquals(10.0, slider.getMin(), 0.0); + Assert.assertEquals(50.0, slider.getMax(), 0.0); + Assert.assertEquals(25.0, slider.getValue(), 0.0); + } + + @Test + public void setMin_getMin() { + Slider slider = new Slider(); + Assert.assertEquals(0.0, slider.getMin(), 0.0); + Assert.assertEquals(0.0, slider.getElement().getProperty("min", 0.0), + 0.0); + + slider.setMin(10.0); + Assert.assertEquals(10.0, slider.getMin(), 0.0); + Assert.assertEquals(10.0, slider.getElement().getProperty("min", 0.0), + 0.0); + } + + @Test + public void setMax_getMax() { + Slider slider = new Slider(); + Assert.assertEquals(100.0, slider.getMax(), 0.0); + Assert.assertEquals(100.0, slider.getElement().getProperty("max", 0.0), + 0.0); + + slider.setMax(200.0); + Assert.assertEquals(200.0, slider.getMax(), 0.0); + Assert.assertEquals(200.0, slider.getElement().getProperty("max", 0.0), + 0.0); + } + + @Test + public void setStep_getStep() { + Slider slider = new Slider(); + Assert.assertEquals(1.0, slider.getStep(), 0.0); + Assert.assertEquals(1.0, slider.getElement().getProperty("step", 1.0), + 0.0); + + slider.setStep(5.0); + Assert.assertEquals(5.0, slider.getStep(), 0.0); + Assert.assertEquals(5.0, slider.getElement().getProperty("step", 1.0), + 0.0); + } + + @Test(expected = IllegalArgumentException.class) + public void setMin_greaterThanMax_throws() { + Slider slider = new Slider(0, 100); + slider.setMin(150.0); + } + + @Test(expected = IllegalArgumentException.class) + public void setMax_lessThanMin_throws() { + Slider slider = new Slider(50, 100); + slider.setMax(25.0); + } + + @Test(expected = IllegalArgumentException.class) + public void setValue_lessThanMin_throws() { + Slider slider = new Slider(10, 100); + slider.setValue(5.0); + } + + @Test(expected = IllegalArgumentException.class) + public void setValue_greaterThanMax_throws() { + Slider slider = new Slider(0, 100); + slider.setValue(150.0); + } +} From c00aa077e73d892d469c89f044c1aacff30f9d70 Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Thu, 15 Jan 2026 12:34:45 +0400 Subject: [PATCH 04/36] test: add unit test for setStep IllegalArgumentException Co-Authored-By: Claude Opus 4.5 --- .../com/vaadin/flow/component/slider/Slider.java | 15 +++++++++++++++ .../flow/component/slider/tests/SliderTest.java | 6 ++++++ 2 files changed, 21 insertions(+) diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java index 15b60c73add..0a1c559f8b5 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java @@ -70,6 +70,10 @@ public Slider(double min, double max, double value) { setValue(value); } + /** + * @throws IllegalArgumentException + * if the value is not between min and max + */ @Override public void setValue(Double value) { if (value < getMin() || value > getMax()) { @@ -85,6 +89,8 @@ public void setValue(Double value) { * * @param min * the minimum value + * @throws IllegalArgumentException + * if the min is greater than the max value */ public void setMin(double min) { if (min > getMax()) { @@ -109,6 +115,8 @@ public double getMin() { * * @param max * the maximum value + * @throws IllegalArgumentException + * if the max is less than the min value */ public void setMax(double max) { if (max < getMin()) { @@ -134,8 +142,15 @@ public double getMax() { * * @param step * the step value + * @throws IllegalArgumentException + * if the step is less than or equal to zero */ public void setStep(double step) { + if (step <= 0) { + throw new IllegalArgumentException( + "The step value must be a positive number"); + } + getElement().setProperty("step", step); } diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java index e2534cc11c0..2b582b1a84c 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java @@ -107,4 +107,10 @@ public void setValue_greaterThanMax_throws() { Slider slider = new Slider(0, 100); slider.setValue(150.0); } + + @Test(expected = IllegalArgumentException.class) + public void setStep_notPositive_throws() { + Slider slider = new Slider(); + slider.setStep(0); + } } From 2eb630e6788aee3c6700228b6fa969425340df65 Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Thu, 15 Jan 2026 16:25:46 +0400 Subject: [PATCH 05/36] add test for serializability --- .../slider/tests/SliderSerializableTest.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderSerializableTest.java diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderSerializableTest.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderSerializableTest.java new file mode 100644 index 00000000000..0e628b3d9a4 --- /dev/null +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderSerializableTest.java @@ -0,0 +1,21 @@ +/* + * Copyright 2000-2026 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.flow.component.slider.tests; + +import com.vaadin.flow.testutil.ClassesSerializableTest; + +public class SliderSerializableTest extends ClassesSerializableTest { +} From 35b5b9ddce0def578e8f3a500c5cbc0e97d44e00 Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Fri, 16 Jan 2026 11:52:18 +0400 Subject: [PATCH 06/36] feat: add experimental feature flag and basic integration test for Slider - Add ExperimentalFeatureException thrown when feature flag is disabled - Add feature flag check in Slider.onAttach - Add getMin, getMax, getStep methods to SliderElement - Add basic integration test verifying slider properties Co-Authored-By: Claude Opus 4.5 --- .../component/slider/tests/BasicPage.java | 30 +++++++++++++ .../resources/vaadin-featureflags.properties | 1 + .../flow/component/slider/tests/BasicIT.java | 44 +++++++++++++++++++ .../slider/ExperimentalFeatureException.java | 33 ++++++++++++++ .../vaadin/flow/component/slider/Slider.java | 24 +++++++++- .../slider/SliderFeatureFlagProvider.java | 19 ++++++++ ...om.vaadin.experimental.FeatureFlagProvider | 1 + .../slider/testbench/SliderElement.java | 27 ++++++++++++ 8 files changed, 177 insertions(+), 2 deletions(-) create mode 100644 vaadin-slider-flow-parent/vaadin-slider-flow-integration-tests/src/main/java/com/vaadin/flow/component/slider/tests/BasicPage.java create mode 100644 vaadin-slider-flow-parent/vaadin-slider-flow-integration-tests/src/main/resources/vaadin-featureflags.properties create mode 100644 vaadin-slider-flow-parent/vaadin-slider-flow-integration-tests/src/test/java/com/vaadin/flow/component/slider/tests/BasicIT.java create mode 100644 vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/ExperimentalFeatureException.java create mode 100644 vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderFeatureFlagProvider.java create mode 100644 vaadin-slider-flow-parent/vaadin-slider-flow/src/main/resources/META-INF/services/com.vaadin.experimental.FeatureFlagProvider diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow-integration-tests/src/main/java/com/vaadin/flow/component/slider/tests/BasicPage.java b/vaadin-slider-flow-parent/vaadin-slider-flow-integration-tests/src/main/java/com/vaadin/flow/component/slider/tests/BasicPage.java new file mode 100644 index 00000000000..7332b70b027 --- /dev/null +++ b/vaadin-slider-flow-parent/vaadin-slider-flow-integration-tests/src/main/java/com/vaadin/flow/component/slider/tests/BasicPage.java @@ -0,0 +1,30 @@ +/* + * Copyright 2000-2026 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.flow.component.slider.tests; + +import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.component.slider.Slider; +import com.vaadin.flow.router.Route; + +@Route("vaadin-slider/basic") +public class BasicPage extends Div { + + public BasicPage() { + Slider slider = new Slider(10, 200, 50); + slider.setStep(5); + add(slider); + } +} diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow-integration-tests/src/main/resources/vaadin-featureflags.properties b/vaadin-slider-flow-parent/vaadin-slider-flow-integration-tests/src/main/resources/vaadin-featureflags.properties new file mode 100644 index 00000000000..1cd3ee28642 --- /dev/null +++ b/vaadin-slider-flow-parent/vaadin-slider-flow-integration-tests/src/main/resources/vaadin-featureflags.properties @@ -0,0 +1 @@ +com.vaadin.experimental.sliderComponent=true diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow-integration-tests/src/test/java/com/vaadin/flow/component/slider/tests/BasicIT.java b/vaadin-slider-flow-parent/vaadin-slider-flow-integration-tests/src/test/java/com/vaadin/flow/component/slider/tests/BasicIT.java new file mode 100644 index 00000000000..2d507973d9e --- /dev/null +++ b/vaadin-slider-flow-parent/vaadin-slider-flow-integration-tests/src/test/java/com/vaadin/flow/component/slider/tests/BasicIT.java @@ -0,0 +1,44 @@ +/* + * Copyright 2000-2026 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.flow.component.slider.tests; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.flow.component.slider.testbench.SliderElement; +import com.vaadin.flow.testutil.TestPath; +import com.vaadin.tests.AbstractComponentIT; + +@TestPath("vaadin-slider/basic") +public class BasicIT extends AbstractComponentIT { + + private SliderElement slider; + + @Before + public void init() { + open(); + slider = $(SliderElement.class).first(); + } + + @Test + public void basicProperties() { + Assert.assertEquals(10, slider.getMin(), 0); + Assert.assertEquals(200, slider.getMax(), 0); + Assert.assertEquals(50, slider.getValue(), 0); + Assert.assertEquals(5, slider.getStep(), 0); + } +} diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/ExperimentalFeatureException.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/ExperimentalFeatureException.java new file mode 100644 index 00000000000..d05a2c9719a --- /dev/null +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/ExperimentalFeatureException.java @@ -0,0 +1,33 @@ +/* + * Copyright 2000-2026 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.flow.component.slider; + +/** + * An exception which is thrown when somebody attempts to use the + * {@link Slider} component without activating the associated + * feature flag first. + * + * @author Vaadin Ltd + */ +public class ExperimentalFeatureException extends RuntimeException { + public ExperimentalFeatureException() { + super("The Slider component is currently an experimental feature and needs to be " + + "explicitly enabled. The component can be enabled using Copilot, in the " + + "experimental features tab, or by adding a " + + "`src/main/resources/vaadin-featureflags.properties` file with the following content: " + + "`com.vaadin.experimental.sliderComponent=true`"); + } +} diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java index 0a1c559f8b5..c543af45cff 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java @@ -15,9 +15,12 @@ */ package com.vaadin.flow.component.slider; +import com.vaadin.experimental.FeatureFlags; import com.vaadin.flow.component.AbstractSinglePropertyField; +import com.vaadin.flow.component.AttachEvent; import com.vaadin.flow.component.HasSize; import com.vaadin.flow.component.Tag; +import com.vaadin.flow.component.UI; import com.vaadin.flow.component.dependency.JsModule; import com.vaadin.flow.component.dependency.NpmPackage; @@ -28,8 +31,8 @@ * @author Vaadin Ltd. */ @Tag("vaadin-slider") -@NpmPackage(value = "@vaadin/slider", version = "25.1.0-alpha1") -@JsModule("@vaadin/slider/src/vaadin-slider.js") +// @NpmPackage(value = "@vaadin/slider", version = "25.1.0-alpha1") +// @JsModule("@vaadin/slider/src/vaadin-slider.js") public class Slider extends AbstractSinglePropertyField implements HasSize { @@ -70,6 +73,23 @@ public Slider(double min, double max, double value) { setValue(value); } + @Override + protected void onAttach(AttachEvent attachEvent) { + super.onAttach(attachEvent); + checkFeatureFlag(attachEvent.getUI()); + } + + private void checkFeatureFlag(UI ui) { + FeatureFlags featureFlags = FeatureFlags + .get(ui.getSession().getService().getContext()); + boolean enabled = featureFlags + .isEnabled(SliderFeatureFlagProvider.SLIDER_COMPONENT); + + if (!enabled) { + throw new ExperimentalFeatureException(); + } + } + /** * @throws IllegalArgumentException * if the value is not between min and max diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderFeatureFlagProvider.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderFeatureFlagProvider.java new file mode 100644 index 00000000000..0d996a469b4 --- /dev/null +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderFeatureFlagProvider.java @@ -0,0 +1,19 @@ +package com.vaadin.flow.component.slider; + +import java.util.List; + +import com.vaadin.experimental.Feature; +import com.vaadin.experimental.FeatureFlagProvider; + +public class SliderFeatureFlagProvider implements FeatureFlagProvider { + + public static final Feature SLIDER_COMPONENT = new Feature( + "Slider component", "sliderComponent", + "https://github.com/vaadin/platform/issues/8397", true, + "com.vaadin.flow.component.slider.Slider"); + + @Override + public List getFeatures() { + return List.of(SLIDER_COMPONENT); + } +} diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/resources/META-INF/services/com.vaadin.experimental.FeatureFlagProvider b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/resources/META-INF/services/com.vaadin.experimental.FeatureFlagProvider new file mode 100644 index 00000000000..809a40f2baf --- /dev/null +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/resources/META-INF/services/com.vaadin.experimental.FeatureFlagProvider @@ -0,0 +1 @@ +com.vaadin.flow.component.slider.SliderFeatureFlagProvider diff --git a/vaadin-slider-flow-parent/vaadin-slider-testbench/src/main/java/com/vaadin/flow/component/slider/testbench/SliderElement.java b/vaadin-slider-flow-parent/vaadin-slider-testbench/src/main/java/com/vaadin/flow/component/slider/testbench/SliderElement.java index 1a03e839f4f..9b36c1111c8 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-testbench/src/main/java/com/vaadin/flow/component/slider/testbench/SliderElement.java +++ b/vaadin-slider-flow-parent/vaadin-slider-testbench/src/main/java/com/vaadin/flow/component/slider/testbench/SliderElement.java @@ -33,4 +33,31 @@ public class SliderElement extends TestBenchElement { public double getValue() { return getPropertyDouble("value"); } + + /** + * Gets the minimum value of the slider. + * + * @return the minimum value + */ + public double getMin() { + return getPropertyDouble("min"); + } + + /** + * Gets the maximum value of the slider. + * + * @return the maximum value + */ + public double getMax() { + return getPropertyDouble("max"); + } + + /** + * Gets the step value of the slider. + * + * @return the step value + */ + public double getStep() { + return getPropertyDouble("step"); + } } From c945833a24ea8514cf97f7327c3495a27f79737e Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Fri, 16 Jan 2026 12:43:04 +0400 Subject: [PATCH 07/36] run formatter --- .../slider/ExperimentalFeatureException.java | 5 ++--- .../com/vaadin/flow/component/slider/Slider.java | 2 -- .../slider/SliderFeatureFlagProvider.java | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/ExperimentalFeatureException.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/ExperimentalFeatureException.java index d05a2c9719a..ae2d76cf2b5 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/ExperimentalFeatureException.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/ExperimentalFeatureException.java @@ -16,9 +16,8 @@ package com.vaadin.flow.component.slider; /** - * An exception which is thrown when somebody attempts to use the - * {@link Slider} component without activating the associated - * feature flag first. + * An exception which is thrown when somebody attempts to use the {@link Slider} + * component without activating the associated feature flag first. * * @author Vaadin Ltd */ diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java index c543af45cff..78ada6e2a74 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java @@ -21,8 +21,6 @@ import com.vaadin.flow.component.HasSize; import com.vaadin.flow.component.Tag; import com.vaadin.flow.component.UI; -import com.vaadin.flow.component.dependency.JsModule; -import com.vaadin.flow.component.dependency.NpmPackage; /** * Slider is an input component that allows the user to select a numeric value diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderFeatureFlagProvider.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderFeatureFlagProvider.java index 0d996a469b4..16e922e0545 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderFeatureFlagProvider.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderFeatureFlagProvider.java @@ -1,3 +1,18 @@ +/* + * Copyright 2000-2026 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ package com.vaadin.flow.component.slider; import java.util.List; From b71fe9d840526410c780afe6db8a537923bf4f7e Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Fri, 16 Jan 2026 13:52:23 +0400 Subject: [PATCH 08/36] implement Focusable and KeyNotifier interfaces --- .../vaadin/flow/component/slider/Slider.java | 4 +++- .../component/slider/tests/SliderTest.java | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java index 78ada6e2a74..3e89f06ef79 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java @@ -18,7 +18,9 @@ import com.vaadin.experimental.FeatureFlags; import com.vaadin.flow.component.AbstractSinglePropertyField; import com.vaadin.flow.component.AttachEvent; +import com.vaadin.flow.component.Focusable; import com.vaadin.flow.component.HasSize; +import com.vaadin.flow.component.KeyNotifier; import com.vaadin.flow.component.Tag; import com.vaadin.flow.component.UI; @@ -32,7 +34,7 @@ // @NpmPackage(value = "@vaadin/slider", version = "25.1.0-alpha1") // @JsModule("@vaadin/slider/src/vaadin-slider.js") public class Slider extends AbstractSinglePropertyField - implements HasSize { + implements HasSize, Focusable, KeyNotifier { /** * Constructs a new Slider with default values (min=0, max=100, value=0). diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java index 2b582b1a84c..e27361c60d0 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java @@ -18,6 +18,9 @@ import org.junit.Assert; import org.junit.Test; +import com.vaadin.flow.component.Focusable; +import com.vaadin.flow.component.HasSize; +import com.vaadin.flow.component.KeyNotifier; import com.vaadin.flow.component.slider.Slider; public class SliderTest { @@ -113,4 +116,19 @@ public void setStep_notPositive_throws() { Slider slider = new Slider(); slider.setStep(0); } + + public void implementsHasSizeInterface() { + Slider slider = new Slider(); + Assert.assertTrue(slider instanceof HasSize); + } + + public void implementsFocusableInterface() { + Slider slider = new Slider(); + Assert.assertTrue(slider instanceof Focusable); + } + + public void implementsKeyNotifierInterface() { + Slider slider = new Slider(); + Assert.assertTrue(slider instanceof KeyNotifier); + } } From 1ae8222a4d7cd4610ab9f061ad5f3196484f7b57 Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Fri, 16 Jan 2026 15:57:48 +0400 Subject: [PATCH 09/36] refactor: extract SliderBase abstract class with min, max, step properties - Create SliderBase abstract class extending AbstractSinglePropertyField - Move min, max, step getters/setters with validation to SliderBase - Update Slider to extend SliderBase Co-Authored-By: Claude Opus 4.5 --- .../vaadin/flow/component/slider/Slider.java | 82 +-------- .../flow/component/slider/SliderBase.java | 172 ++++++++++++++++++ 2 files changed, 173 insertions(+), 81 deletions(-) create mode 100644 vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java index 3e89f06ef79..a8b66670630 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java @@ -16,7 +16,6 @@ package com.vaadin.flow.component.slider; import com.vaadin.experimental.FeatureFlags; -import com.vaadin.flow.component.AbstractSinglePropertyField; import com.vaadin.flow.component.AttachEvent; import com.vaadin.flow.component.Focusable; import com.vaadin.flow.component.HasSize; @@ -33,7 +32,7 @@ @Tag("vaadin-slider") // @NpmPackage(value = "@vaadin/slider", version = "25.1.0-alpha1") // @JsModule("@vaadin/slider/src/vaadin-slider.js") -public class Slider extends AbstractSinglePropertyField +public class Slider extends SliderBase implements HasSize, Focusable, KeyNotifier { /** @@ -103,83 +102,4 @@ public void setValue(Double value) { super.setValue(value); } - - /** - * Sets the minimum value of the slider. - * - * @param min - * the minimum value - * @throws IllegalArgumentException - * if the min is greater than the max value - */ - public void setMin(double min) { - if (min > getMax()) { - throw new IllegalArgumentException( - "The min value cannot be greater than the max value"); - } - - getElement().setProperty("min", min); - } - - /** - * Gets the minimum value of the slider. - * - * @return the minimum value - */ - public double getMin() { - return getElement().getProperty("min", 0.0); - } - - /** - * Sets the maximum value of the slider. - * - * @param max - * the maximum value - * @throws IllegalArgumentException - * if the max is less than the min value - */ - public void setMax(double max) { - if (max < getMin()) { - throw new IllegalArgumentException( - "The max value cannot be less than the min value"); - } - - getElement().setProperty("max", max); - } - - /** - * Gets the maximum value of the slider. - * - * @return the maximum value - */ - public double getMax() { - return getElement().getProperty("max", 100.0); - } - - /** - * Sets the step value of the slider. The step is the amount the value - * changes when the user moves the handle. - * - * @param step - * the step value - * @throws IllegalArgumentException - * if the step is less than or equal to zero - */ - public void setStep(double step) { - if (step <= 0) { - throw new IllegalArgumentException( - "The step value must be a positive number"); - } - - getElement().setProperty("step", step); - } - - /** - * Gets the step value of the slider. - * - * @return the step value - */ - public double getStep() { - return getElement().getProperty("step", 1.0); - } } diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java new file mode 100644 index 00000000000..6b0faabdd25 --- /dev/null +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java @@ -0,0 +1,172 @@ +/* + * Copyright 2000-2026 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.flow.component.slider; + +import com.vaadin.flow.component.AbstractSinglePropertyField; +import com.vaadin.flow.function.SerializableBiFunction; +import com.vaadin.flow.function.SerializableFunction; + +/** + * Abstract base class for slider components. + * + * @param + * the component type + * @param + * the value type + * + * @author Vaadin Ltd + */ +public abstract class SliderBase, TValue> + extends AbstractSinglePropertyField { + + /** + * Creates a new SliderBase instance. + * + * @param propertyName + * the name of the element property to bind the value to + * @param defaultValue + * the default value + * @param elementPropertyType + * the type of the element property + * @param presentationToModel + * a function to convert element property value to model value + * @param modelToPresentation + * a function to convert model value to element property value + */ + public

SliderBase(String propertyName, TValue defaultValue, + Class

elementPropertyType, + SerializableFunction presentationToModel, + SerializableFunction modelToPresentation) { + super(propertyName, defaultValue, elementPropertyType, + presentationToModel, modelToPresentation); + } + + /** + * Creates a new SliderBase instance. + * + * @param propertyName + * the name of the element property to bind the value to + * @param defaultValue + * the default value + * @param acceptNullValues + * whether null values are accepted + */ + public SliderBase(String propertyName, TValue defaultValue, + boolean acceptNullValues) { + super(propertyName, defaultValue, acceptNullValues); + } + + /** + * Creates a new SliderBase instance. + * + * @param propertyName + * the name of the element property to bind the value to + * @param defaultValue + * the default value + * @param elementPropertyType + * the type of the element property + * @param presentationToModel + * a function to convert element property value to model value + * @param modelToPresentation + * a function to convert model value to element property value + */ + public

SliderBase(String propertyName, TValue defaultValue, + Class

elementPropertyType, + SerializableBiFunction presentationToModel, + SerializableBiFunction modelToPresentation) { + super(propertyName, defaultValue, elementPropertyType, + presentationToModel, modelToPresentation); + } + + /** + * Sets the minimum value of the slider. + * + * @param min + * the minimum value + * @throws IllegalArgumentException + * if the min is greater than the max value + */ + public void setMin(double min) { + if (min > getMax()) { + throw new IllegalArgumentException( + "The min value cannot be greater than the max value"); + } + + getElement().setProperty("min", min); + } + + /** + * Gets the minimum value of the slider. + * + * @return the minimum value + */ + public double getMin() { + return getElement().getProperty("min", 0.0); + } + + /** + * Sets the maximum value of the slider. + * + * @param max + * the maximum value + * @throws IllegalArgumentException + * if the max is less than the min value + */ + public void setMax(double max) { + if (max < getMin()) { + throw new IllegalArgumentException( + "The max value cannot be less than the min value"); + } + + getElement().setProperty("max", max); + } + + /** + * Gets the maximum value of the slider. + * + * @return the maximum value + */ + public double getMax() { + return getElement().getProperty("max", 100.0); + } + + /** + * Sets the step value of the slider. The step is the amount the value + * changes when the user moves the handle. + * + * @param step + * the step value + * @throws IllegalArgumentException + * if the step is less than or equal to zero + */ + public void setStep(double step) { + if (step <= 0) { + throw new IllegalArgumentException( + "The step value must be a positive number"); + } + + getElement().setProperty("step", step); + } + + /** + * Gets the step value of the slider. + * + * @return the step value + */ + public double getStep() { + return getElement().getProperty("step", 1.0); + } +} From 5116efc61ba496d387a7365a8855660f0f29a0f3 Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Fri, 16 Jan 2026 21:45:07 +0400 Subject: [PATCH 10/36] docs: improve JavaDoc descriptions for Slider constructors Co-Authored-By: Claude Opus 4.5 --- .../vaadin/flow/component/slider/Slider.java | 99 ++++++++++++++++--- .../flow/component/slider/SliderBase.java | 98 +++++++++--------- 2 files changed, 137 insertions(+), 60 deletions(-) diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java index a8b66670630..a9dec9bd5b6 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java @@ -35,16 +35,57 @@ public class Slider extends SliderBase implements HasSize, Focusable, KeyNotifier { + private final static double DEFAULT_MIN = 0.0; + private final static double DEFAULT_MAX = 100.0; + /** - * Constructs a new Slider with default values (min=0, max=100, value=0). + * Constructs a slider with a default range of 0 to 100 and an initial value + * of 0. */ public Slider() { - this(0, 100); + this((String) null); } /** - * Constructs a new Slider with the given min and max values. The initial - * value is set to min. + * Constructs a slider with a value change listener, a default range of 0 to + * 100, and an initial value of 0. + * + * @param listener + * the value change listener + */ + public Slider( + ValueChangeListener> listener) { + this((String) null, listener); + } + + /** + * Constructs a slider with the given label, a default range of 0 to 100, + * and an initial value of 0. + * + * @param label + * the text to set as the label + */ + public Slider(String label) { + this(label, DEFAULT_MIN, DEFAULT_MAX); + } + + /** + * Constructs a slider with the given label, value change listener, a + * default range of 0 to 100, and an initial value of 0. + * + * @param label + * the text to set as the label + * @param listener + * the value change listener + */ + public Slider(String label, + ValueChangeListener> listener) { + this(label, DEFAULT_MIN, DEFAULT_MAX, listener); + } + + /** + * Constructs a slider with the given min and max values. The initial value + * is set to the minimum. * * @param min * the minimum value @@ -52,24 +93,56 @@ public Slider() { * the maximum value */ public Slider(double min, double max) { - this(min, max, min); + this((String) null, min, max); + } + + /** + * Constructs a slider with the given min and max values, and a value change + * listener. The initial value is set to the minimum. + * + * @param min + * the minimum value + * @param max + * the maximum value + * @param listener + * the value change listener + */ + public Slider(double min, double max, + ValueChangeListener> listener) { + this((String) null, min, max, listener); + } + + /** + * Constructs a slider with the given label, min, and max values. The + * initial value is set to the minimum. + * + * @param label + * the text to set as the label + * @param min + * the minimum value + * @param max + * the maximum value + */ + public Slider(String label, double min, double max) { + super(label, min, max, min); } /** - * Constructs a new Slider with the given min, max, and initial value. + * Constructs a slider with the given label, min, and max values, and a + * value change listener. The initial value is set to the minimum. * + * @param label + * the text to set as the label * @param min * the minimum value * @param max * the maximum value - * @param value - * the initial value + * @param listener + * the value change listener */ - public Slider(double min, double max, double value) { - super("value", 0.0, false); - setMin(min); - setMax(max); - setValue(value); + public Slider(String label, double min, double max, + ValueChangeListener> listener) { + super(label, min, max, min, listener); } @Override diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java index 6b0faabdd25..94bb828d64b 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java @@ -16,8 +16,9 @@ package com.vaadin.flow.component.slider; import com.vaadin.flow.component.AbstractSinglePropertyField; -import com.vaadin.flow.function.SerializableBiFunction; -import com.vaadin.flow.function.SerializableFunction; +import com.vaadin.flow.component.HasHelper; +import com.vaadin.flow.component.HasLabel; +import com.vaadin.flow.component.shared.HasValidationProperties; /** * Abstract base class for slider components. @@ -30,65 +31,68 @@ * @author Vaadin Ltd */ public abstract class SliderBase, TValue> - extends AbstractSinglePropertyField { + extends AbstractSinglePropertyField + implements HasLabel, HasHelper, HasValidationProperties { /** - * Creates a new SliderBase instance. + * Constructs a slider with the given min, max, and initial value. * - * @param propertyName - * the name of the element property to bind the value to - * @param defaultValue - * the default value - * @param elementPropertyType - * the type of the element property - * @param presentationToModel - * a function to convert element property value to model value - * @param modelToPresentation - * a function to convert model value to element property value + * @param min + * the minimum value + * @param max + * the maximum value + * @param value + * the initial value */ - public

SliderBase(String propertyName, TValue defaultValue, - Class

elementPropertyType, - SerializableFunction presentationToModel, - SerializableFunction modelToPresentation) { - super(propertyName, defaultValue, elementPropertyType, - presentationToModel, modelToPresentation); + public SliderBase(double min, double max, TValue value) { + super("value", null, false); + + getElement().setProperty("manualValidation", true); + + setMin(min); + setMax(max); + setValue(value); + + // workaround for // https://github.com/vaadin/flow/issues/3496 + setInvalid(false); } /** - * Creates a new SliderBase instance. + * Constructs a slider with the given label, min, max, and initial value. * - * @param propertyName - * the name of the element property to bind the value to - * @param defaultValue - * the default value - * @param acceptNullValues - * whether null values are accepted + * @param label + * the text to set as the label + * @param min + * the minimum value + * @param max + * the maximum value + * @param value + * the initial value */ - public SliderBase(String propertyName, TValue defaultValue, - boolean acceptNullValues) { - super(propertyName, defaultValue, acceptNullValues); + public SliderBase(String label, double min, double max, TValue value) { + this(min, max, value); + setLabel(label); } /** - * Creates a new SliderBase instance. + * Constructs a slider with the given label, min, max, initial value, and a + * value change listener. * - * @param propertyName - * the name of the element property to bind the value to - * @param defaultValue - * the default value - * @param elementPropertyType - * the type of the element property - * @param presentationToModel - * a function to convert element property value to model value - * @param modelToPresentation - * a function to convert model value to element property value + * @param label + * the text to set as the label + * @param min + * the minimum value + * @param max + * the maximum value + * @param value + * the initial value + * @param listener + * the value change listener */ - public

SliderBase(String propertyName, TValue defaultValue, - Class

elementPropertyType, - SerializableBiFunction presentationToModel, - SerializableBiFunction modelToPresentation) { - super(propertyName, defaultValue, elementPropertyType, - presentationToModel, modelToPresentation); + public SliderBase(String label, double min, double max, TValue value, + ValueChangeListener> listener) { + this(label, min, max, value); + addValueChangeListener(listener); } /** From 0a666020336120d95421a1eb65b2b0b5489e6f38 Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Fri, 16 Jan 2026 21:45:46 +0400 Subject: [PATCH 11/36] test: add unit tests for Slider constructors and basic validation Co-Authored-By: Claude Opus 4.5 --- .../component/slider/tests/SliderTest.java | 27 ++++++++++++++++++ .../validation/SliderBasicValidationTest.java | 28 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/validation/SliderBasicValidationTest.java diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java index e27361c60d0..fdf420961b1 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java @@ -48,6 +48,33 @@ public void minMaxValueConstructor() { Assert.assertEquals(25.0, slider.getValue(), 0.0); } + @Test + public void labelConstructor() { + Slider slider = new Slider("Label"); + Assert.assertEquals("Label", slider.getLabel()); + Assert.assertEquals(0.0, slider.getMin(), 0.0); + Assert.assertEquals(100.0, slider.getMax(), 0.0); + Assert.assertEquals(0.0, slider.getValue(), 0.0); + } + + @Test + public void labelMinMaxConstructor() { + Slider slider = new Slider("Label", 10.0, 50.0); + Assert.assertEquals("Label", slider.getLabel()); + Assert.assertEquals(10.0, slider.getMin(), 0.0); + Assert.assertEquals(50.0, slider.getMax(), 0.0); + Assert.assertEquals(10.0, slider.getValue(), 0.0); + } + + @Test + public void labelMinMaxValueConstructor() { + Slider slider = new Slider("Label", 10.0, 50.0, 25.0); + Assert.assertEquals("Label", slider.getLabel()); + Assert.assertEquals(10.0, slider.getMin(), 0.0); + Assert.assertEquals(50.0, slider.getMax(), 0.0); + Assert.assertEquals(25.0, slider.getValue(), 0.0); + } + @Test public void setMin_getMin() { Slider slider = new Slider(); diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/validation/SliderBasicValidationTest.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/validation/SliderBasicValidationTest.java new file mode 100644 index 00000000000..8d507cfa22a --- /dev/null +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/validation/SliderBasicValidationTest.java @@ -0,0 +1,28 @@ +/* + * Copyright 2000-2026 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.flow.component.slider.validation; + +import com.vaadin.flow.component.slider.Slider; +import com.vaadin.tests.validation.AbstractBasicValidationTest; + +public class SliderBasicValidationTest + extends AbstractBasicValidationTest { + + @Override + protected Slider createTestField() { + return new Slider(); + } +} From 105ae3127ff3dd973ebf7338a0da9edf00d67c54 Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Fri, 16 Jan 2026 21:51:26 +0400 Subject: [PATCH 12/36] test: add SliderBase tests and refactor Slider tests Move min, max, step tests to SliderBaseTest. Add tests for SliderBase constructors and value change listener constructors. Co-Authored-By: Claude Opus 4.5 --- .../slider/tests/SliderBaseTest.java | 131 ++++++++++++++++++ .../component/slider/tests/SliderTest.java | 95 +++++-------- 2 files changed, 168 insertions(+), 58 deletions(-) create mode 100644 vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderBaseTest.java diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderBaseTest.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderBaseTest.java new file mode 100644 index 00000000000..1ea7313f700 --- /dev/null +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderBaseTest.java @@ -0,0 +1,131 @@ +/* + * Copyright 2000-2026 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.flow.component.slider.tests; + +import java.util.concurrent.atomic.AtomicBoolean; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.component.slider.SliderBase; + +public class SliderBaseTest { + + @Tag("test-slider") + private static class TestSlider extends SliderBase { + public TestSlider(double min, double max, Double value) { + super(min, max, value); + } + + public TestSlider(String label, double min, double max, Double value) { + super(label, min, max, value); + } + + public TestSlider(String label, double min, double max, Double value, + ValueChangeListener> listener) { + super(label, min, max, value, listener); + } + } + + @Test + public void minMaxValueConstructor() { + TestSlider slider = new TestSlider(10.0, 50.0, 25.0); + Assert.assertEquals(10.0, slider.getMin(), 0.0); + Assert.assertEquals(50.0, slider.getMax(), 0.0); + Assert.assertEquals(25.0, slider.getValue(), 0.0); + } + + @Test + public void labelMinMaxValueConstructor() { + TestSlider slider = new TestSlider("Label", 10.0, 50.0, 25.0); + Assert.assertEquals("Label", slider.getLabel()); + Assert.assertEquals(10.0, slider.getMin(), 0.0); + Assert.assertEquals(50.0, slider.getMax(), 0.0); + Assert.assertEquals(25.0, slider.getValue(), 0.0); + } + + @Test + public void labelMinMaxValueListenerConstructor() { + AtomicBoolean listenerInvoked = new AtomicBoolean(false); + TestSlider slider = new TestSlider("Label", 10.0, 50.0, 25.0, + e -> listenerInvoked.set(true)); + Assert.assertEquals("Label", slider.getLabel()); + Assert.assertEquals(10.0, slider.getMin(), 0.0); + Assert.assertEquals(50.0, slider.getMax(), 0.0); + Assert.assertEquals(25.0, slider.getValue(), 0.0); + + slider.setValue(30.0); + Assert.assertTrue(listenerInvoked.get()); + } + + @Test + public void setMin_getMin() { + TestSlider slider = new TestSlider(0.0, 100.0, 0.0); + Assert.assertEquals(0.0, slider.getMin(), 0.0); + Assert.assertEquals(0.0, slider.getElement().getProperty("min", 0.0), + 0.0); + + slider.setMin(10.0); + Assert.assertEquals(10.0, slider.getMin(), 0.0); + Assert.assertEquals(10.0, slider.getElement().getProperty("min", 0.0), + 0.0); + } + + @Test + public void setMax_getMax() { + TestSlider slider = new TestSlider(0.0, 100.0, 0.0); + Assert.assertEquals(100.0, slider.getMax(), 0.0); + Assert.assertEquals(100.0, slider.getElement().getProperty("max", 0.0), + 0.0); + + slider.setMax(200.0); + Assert.assertEquals(200.0, slider.getMax(), 0.0); + Assert.assertEquals(200.0, slider.getElement().getProperty("max", 0.0), + 0.0); + } + + @Test + public void setStep_getStep() { + TestSlider slider = new TestSlider(0.0, 100.0, 0.0); + Assert.assertEquals(1.0, slider.getStep(), 0.0); + Assert.assertEquals(1.0, slider.getElement().getProperty("step", 1.0), + 0.0); + + slider.setStep(5.0); + Assert.assertEquals(5.0, slider.getStep(), 0.0); + Assert.assertEquals(5.0, slider.getElement().getProperty("step", 1.0), + 0.0); + } + + @Test(expected = IllegalArgumentException.class) + public void setMin_greaterThanMax_throws() { + TestSlider slider = new TestSlider(0.0, 100.0, 0.0); + slider.setMin(150.0); + } + + @Test(expected = IllegalArgumentException.class) + public void setMax_lessThanMin_throws() { + TestSlider slider = new TestSlider(50.0, 100.0, 50.0); + slider.setMax(25.0); + } + + @Test(expected = IllegalArgumentException.class) + public void setStep_notPositive_throws() { + TestSlider slider = new TestSlider(0.0, 100.0, 0.0); + slider.setStep(0); + } +} diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java index fdf420961b1..8fa2e6996a6 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java @@ -15,6 +15,8 @@ */ package com.vaadin.flow.component.slider.tests; +import java.util.concurrent.atomic.AtomicBoolean; + import org.junit.Assert; import org.junit.Test; @@ -41,11 +43,27 @@ public void minMaxConstructor() { } @Test - public void minMaxValueConstructor() { - Slider slider = new Slider(10.0, 50.0, 25.0); + public void minMaxListenerConstructor() { + AtomicBoolean listenerInvoked = new AtomicBoolean(false); + Slider slider = new Slider(10.0, 50.0, e -> listenerInvoked.set(true)); Assert.assertEquals(10.0, slider.getMin(), 0.0); Assert.assertEquals(50.0, slider.getMax(), 0.0); - Assert.assertEquals(25.0, slider.getValue(), 0.0); + Assert.assertEquals(10.0, slider.getValue(), 0.0); + + slider.setValue(25.0); + Assert.assertTrue(listenerInvoked.get()); + } + + @Test + public void listenerConstructor() { + AtomicBoolean listenerInvoked = new AtomicBoolean(false); + Slider slider = new Slider(e -> listenerInvoked.set(true)); + Assert.assertEquals(0.0, slider.getMin(), 0.0); + Assert.assertEquals(100.0, slider.getMax(), 0.0); + Assert.assertEquals(0.0, slider.getValue(), 0.0); + + slider.setValue(50.0); + Assert.assertTrue(listenerInvoked.get()); } @Test @@ -67,63 +85,30 @@ public void labelMinMaxConstructor() { } @Test - public void labelMinMaxValueConstructor() { - Slider slider = new Slider("Label", 10.0, 50.0, 25.0); + public void labelListenerConstructor() { + AtomicBoolean listenerInvoked = new AtomicBoolean(false); + Slider slider = new Slider("Label", e -> listenerInvoked.set(true)); Assert.assertEquals("Label", slider.getLabel()); - Assert.assertEquals(10.0, slider.getMin(), 0.0); - Assert.assertEquals(50.0, slider.getMax(), 0.0); - Assert.assertEquals(25.0, slider.getValue(), 0.0); - } - - @Test - public void setMin_getMin() { - Slider slider = new Slider(); Assert.assertEquals(0.0, slider.getMin(), 0.0); - Assert.assertEquals(0.0, slider.getElement().getProperty("min", 0.0), - 0.0); - - slider.setMin(10.0); - Assert.assertEquals(10.0, slider.getMin(), 0.0); - Assert.assertEquals(10.0, slider.getElement().getProperty("min", 0.0), - 0.0); - } - - @Test - public void setMax_getMax() { - Slider slider = new Slider(); Assert.assertEquals(100.0, slider.getMax(), 0.0); - Assert.assertEquals(100.0, slider.getElement().getProperty("max", 0.0), - 0.0); + Assert.assertEquals(0.0, slider.getValue(), 0.0); - slider.setMax(200.0); - Assert.assertEquals(200.0, slider.getMax(), 0.0); - Assert.assertEquals(200.0, slider.getElement().getProperty("max", 0.0), - 0.0); + slider.setValue(50.0); + Assert.assertTrue(listenerInvoked.get()); } @Test - public void setStep_getStep() { - Slider slider = new Slider(); - Assert.assertEquals(1.0, slider.getStep(), 0.0); - Assert.assertEquals(1.0, slider.getElement().getProperty("step", 1.0), - 0.0); - - slider.setStep(5.0); - Assert.assertEquals(5.0, slider.getStep(), 0.0); - Assert.assertEquals(5.0, slider.getElement().getProperty("step", 1.0), - 0.0); - } - - @Test(expected = IllegalArgumentException.class) - public void setMin_greaterThanMax_throws() { - Slider slider = new Slider(0, 100); - slider.setMin(150.0); - } + public void labelMinMaxListenerConstructor() { + AtomicBoolean listenerInvoked = new AtomicBoolean(false); + Slider slider = new Slider("Label", 10.0, 50.0, + e -> listenerInvoked.set(true)); + Assert.assertEquals("Label", slider.getLabel()); + Assert.assertEquals(10.0, slider.getMin(), 0.0); + Assert.assertEquals(50.0, slider.getMax(), 0.0); + Assert.assertEquals(10.0, slider.getValue(), 0.0); - @Test(expected = IllegalArgumentException.class) - public void setMax_lessThanMin_throws() { - Slider slider = new Slider(50, 100); - slider.setMax(25.0); + slider.setValue(25.0); + Assert.assertTrue(listenerInvoked.get()); } @Test(expected = IllegalArgumentException.class) @@ -138,12 +123,6 @@ public void setValue_greaterThanMax_throws() { slider.setValue(150.0); } - @Test(expected = IllegalArgumentException.class) - public void setStep_notPositive_throws() { - Slider slider = new Slider(); - slider.setStep(0); - } - public void implementsHasSizeInterface() { Slider slider = new Slider(); Assert.assertTrue(slider instanceof HasSize); From ae501f7342e3a390dacb15b41d685a6034e45a2d Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Fri, 16 Jan 2026 22:34:22 +0400 Subject: [PATCH 13/36] docs: add JavaDoc for Slider constructors and unit tests Co-Authored-By: Claude Opus 4.5 --- .../vaadin/flow/component/slider/Slider.java | 91 +++++++++++-------- .../flow/component/slider/SliderBase.java | 38 -------- .../slider/tests/SliderBaseTest.java | 44 +-------- .../component/slider/tests/SliderTest.java | 69 +++++++------- 4 files changed, 87 insertions(+), 155 deletions(-) diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java index a9dec9bd5b6..9b5f006c10b 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java @@ -39,15 +39,15 @@ public class Slider extends SliderBase private final static double DEFAULT_MAX = 100.0; /** - * Constructs a slider with a default range of 0 to 100 and an initial value + * Constructs a {@code Slider} with a default range of 0 to 100 and an initial value * of 0. */ public Slider() { - this((String) null); + this(DEFAULT_MIN, DEFAULT_MAX, DEFAULT_MIN); } /** - * Constructs a slider with a value change listener, a default range of 0 to + * Constructs a {@code Slider} with a value change listener, a default range of 0 to * 100, and an initial value of 0. * * @param listener @@ -55,66 +55,71 @@ public Slider() { */ public Slider( ValueChangeListener> listener) { - this((String) null, listener); + this(DEFAULT_MIN, DEFAULT_MAX, DEFAULT_MIN, listener); } /** - * Constructs a slider with the given label, a default range of 0 to 100, - * and an initial value of 0. + * Constructs a {@code Slider} with the given min, max, and initial value. * - * @param label - * the text to set as the label + * @param min + * the minimum value + * @param max + * the maximum value + * @param value + * the initial value */ - public Slider(String label) { - this(label, DEFAULT_MIN, DEFAULT_MAX); + public Slider(double min, double max, double value) { + super(min, max, value); } /** - * Constructs a slider with the given label, value change listener, a - * default range of 0 to 100, and an initial value of 0. + * Constructs a {@code Slider} with the given min, max, initial value, and a value + * change listener. * - * @param label - * the text to set as the label + * @param min + * the minimum value + * @param max + * the maximum value + * @param value + * the initial value * @param listener * the value change listener */ - public Slider(String label, + public Slider(double min, double max, double value, ValueChangeListener> listener) { - this(label, DEFAULT_MIN, DEFAULT_MAX, listener); + this(min, max, value); + addValueChangeListener(listener); } /** - * Constructs a slider with the given min and max values. The initial value - * is set to the minimum. + * Constructs a {@code Slider} with the given label, a default range of 0 to 100, + * and an initial value of 0. * - * @param min - * the minimum value - * @param max - * the maximum value + * @param label + * the text to set as the label */ - public Slider(double min, double max) { - this((String) null, min, max); + public Slider(String label) { + this(); + setLabel(label); } /** - * Constructs a slider with the given min and max values, and a value change - * listener. The initial value is set to the minimum. + * Constructs a {@code Slider} with the given label and a value change listener, a + * default range of 0 to 100, and an initial value of 0. * - * @param min - * the minimum value - * @param max - * the maximum value + * @param label + * the text to set as the label * @param listener * the value change listener */ - public Slider(double min, double max, + public Slider(String label, ValueChangeListener> listener) { - this((String) null, min, max, listener); + this(listener); + setLabel(label); } /** - * Constructs a slider with the given label, min, and max values. The - * initial value is set to the minimum. + * Constructs a {@code Slider} with the given label, min, max, and initial value. * * @param label * the text to set as the label @@ -122,14 +127,17 @@ public Slider(double min, double max, * the minimum value * @param max * the maximum value + * @param value + * the initial value */ - public Slider(String label, double min, double max) { - super(label, min, max, min); + public Slider(String label, double min, double max, double value) { + this(min, max, value); + setLabel(label); } /** - * Constructs a slider with the given label, min, and max values, and a - * value change listener. The initial value is set to the minimum. + * Constructs a {@code Slider} with the given label, min, max, initial value, and a + * value change listener. * * @param label * the text to set as the label @@ -137,12 +145,15 @@ public Slider(String label, double min, double max) { * the minimum value * @param max * the maximum value + * @param value + * the initial value * @param listener * the value change listener */ - public Slider(String label, double min, double max, + public Slider(String label, double min, double max, double value, ValueChangeListener> listener) { - super(label, min, max, min, listener); + this(min, max, value, listener); + setLabel(label); } @Override diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java index 94bb828d64b..413fb8445dd 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java @@ -57,44 +57,6 @@ public SliderBase(double min, double max, TValue value) { setInvalid(false); } - /** - * Constructs a slider with the given label, min, max, and initial value. - * - * @param label - * the text to set as the label - * @param min - * the minimum value - * @param max - * the maximum value - * @param value - * the initial value - */ - public SliderBase(String label, double min, double max, TValue value) { - this(min, max, value); - setLabel(label); - } - - /** - * Constructs a slider with the given label, min, max, initial value, and a - * value change listener. - * - * @param label - * the text to set as the label - * @param min - * the minimum value - * @param max - * the maximum value - * @param value - * the initial value - * @param listener - * the value change listener - */ - public SliderBase(String label, double min, double max, TValue value, - ValueChangeListener> listener) { - this(label, min, max, value); - addValueChangeListener(listener); - } - /** * Sets the minimum value of the slider. * diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderBaseTest.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderBaseTest.java index 1ea7313f700..ea7b501acf5 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderBaseTest.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderBaseTest.java @@ -15,8 +15,6 @@ */ package com.vaadin.flow.component.slider.tests; -import java.util.concurrent.atomic.AtomicBoolean; - import org.junit.Assert; import org.junit.Test; @@ -27,49 +25,9 @@ public class SliderBaseTest { @Tag("test-slider") private static class TestSlider extends SliderBase { - public TestSlider(double min, double max, Double value) { + public TestSlider(double min, double max, double value) { super(min, max, value); } - - public TestSlider(String label, double min, double max, Double value) { - super(label, min, max, value); - } - - public TestSlider(String label, double min, double max, Double value, - ValueChangeListener> listener) { - super(label, min, max, value, listener); - } - } - - @Test - public void minMaxValueConstructor() { - TestSlider slider = new TestSlider(10.0, 50.0, 25.0); - Assert.assertEquals(10.0, slider.getMin(), 0.0); - Assert.assertEquals(50.0, slider.getMax(), 0.0); - Assert.assertEquals(25.0, slider.getValue(), 0.0); - } - - @Test - public void labelMinMaxValueConstructor() { - TestSlider slider = new TestSlider("Label", 10.0, 50.0, 25.0); - Assert.assertEquals("Label", slider.getLabel()); - Assert.assertEquals(10.0, slider.getMin(), 0.0); - Assert.assertEquals(50.0, slider.getMax(), 0.0); - Assert.assertEquals(25.0, slider.getValue(), 0.0); - } - - @Test - public void labelMinMaxValueListenerConstructor() { - AtomicBoolean listenerInvoked = new AtomicBoolean(false); - TestSlider slider = new TestSlider("Label", 10.0, 50.0, 25.0, - e -> listenerInvoked.set(true)); - Assert.assertEquals("Label", slider.getLabel()); - Assert.assertEquals(10.0, slider.getMin(), 0.0); - Assert.assertEquals(50.0, slider.getMax(), 0.0); - Assert.assertEquals(25.0, slider.getValue(), 0.0); - - slider.setValue(30.0); - Assert.assertTrue(listenerInvoked.get()); } @Test diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java index 8fa2e6996a6..1d2ccb9a384 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java @@ -35,34 +35,35 @@ public void defaultConstructor() { } @Test - public void minMaxConstructor() { - Slider slider = new Slider(10.0, 50.0); - Assert.assertEquals(10.0, slider.getMin(), 0.0); - Assert.assertEquals(50.0, slider.getMax(), 0.0); - Assert.assertEquals(10.0, slider.getValue(), 0.0); + public void listenerConstructor() { + AtomicBoolean listenerInvoked = new AtomicBoolean(false); + Slider slider = new Slider(e -> listenerInvoked.set(true)); + Assert.assertEquals(0.0, slider.getMin(), 0.0); + Assert.assertEquals(100.0, slider.getMax(), 0.0); + Assert.assertEquals(0.0, slider.getValue(), 0.0); + + slider.setValue(50.0); + Assert.assertTrue(listenerInvoked.get()); } @Test - public void minMaxListenerConstructor() { - AtomicBoolean listenerInvoked = new AtomicBoolean(false); - Slider slider = new Slider(10.0, 50.0, e -> listenerInvoked.set(true)); + public void minMaxValueConstructor() { + Slider slider = new Slider(10.0, 50.0, 25.0); Assert.assertEquals(10.0, slider.getMin(), 0.0); Assert.assertEquals(50.0, slider.getMax(), 0.0); - Assert.assertEquals(10.0, slider.getValue(), 0.0); - - slider.setValue(25.0); - Assert.assertTrue(listenerInvoked.get()); + Assert.assertEquals(25.0, slider.getValue(), 0.0); } @Test - public void listenerConstructor() { + public void minMaxValueListenerConstructor() { AtomicBoolean listenerInvoked = new AtomicBoolean(false); - Slider slider = new Slider(e -> listenerInvoked.set(true)); - Assert.assertEquals(0.0, slider.getMin(), 0.0); - Assert.assertEquals(100.0, slider.getMax(), 0.0); - Assert.assertEquals(0.0, slider.getValue(), 0.0); + Slider slider = new Slider(10.0, 50.0, 25.0, + e -> listenerInvoked.set(true)); + Assert.assertEquals(10.0, slider.getMin(), 0.0); + Assert.assertEquals(50.0, slider.getMax(), 0.0); + Assert.assertEquals(25.0, slider.getValue(), 0.0); - slider.setValue(50.0); + slider.setValue(30.0); Assert.assertTrue(listenerInvoked.get()); } @@ -75,15 +76,6 @@ public void labelConstructor() { Assert.assertEquals(0.0, slider.getValue(), 0.0); } - @Test - public void labelMinMaxConstructor() { - Slider slider = new Slider("Label", 10.0, 50.0); - Assert.assertEquals("Label", slider.getLabel()); - Assert.assertEquals(10.0, slider.getMin(), 0.0); - Assert.assertEquals(50.0, slider.getMax(), 0.0); - Assert.assertEquals(10.0, slider.getValue(), 0.0); - } - @Test public void labelListenerConstructor() { AtomicBoolean listenerInvoked = new AtomicBoolean(false); @@ -98,28 +90,37 @@ public void labelListenerConstructor() { } @Test - public void labelMinMaxListenerConstructor() { + public void labelMinMaxValueConstructor() { + Slider slider = new Slider("Label", 10.0, 50.0, 25.0); + Assert.assertEquals("Label", slider.getLabel()); + Assert.assertEquals(10.0, slider.getMin(), 0.0); + Assert.assertEquals(50.0, slider.getMax(), 0.0); + Assert.assertEquals(25.0, slider.getValue(), 0.0); + } + + @Test + public void labelMinMaxValueListenerConstructor() { AtomicBoolean listenerInvoked = new AtomicBoolean(false); - Slider slider = new Slider("Label", 10.0, 50.0, + Slider slider = new Slider("Label", 10.0, 50.0, 25.0, e -> listenerInvoked.set(true)); Assert.assertEquals("Label", slider.getLabel()); Assert.assertEquals(10.0, slider.getMin(), 0.0); Assert.assertEquals(50.0, slider.getMax(), 0.0); - Assert.assertEquals(10.0, slider.getValue(), 0.0); + Assert.assertEquals(25.0, slider.getValue(), 0.0); - slider.setValue(25.0); + slider.setValue(30.0); Assert.assertTrue(listenerInvoked.get()); } @Test(expected = IllegalArgumentException.class) public void setValue_lessThanMin_throws() { - Slider slider = new Slider(10, 100); - slider.setValue(5.0); + Slider slider = new Slider(0, 100, 0); + slider.setValue(-150.0); } @Test(expected = IllegalArgumentException.class) public void setValue_greaterThanMax_throws() { - Slider slider = new Slider(0, 100); + Slider slider = new Slider(0, 100, 0); slider.setValue(150.0); } From 50ad9488afd4593abf5580fa936d42973fed9bcd Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Fri, 16 Jan 2026 22:51:24 +0400 Subject: [PATCH 14/36] move interfaces to base class, format fixes --- .../vaadin/flow/component/slider/Slider.java | 38 +++++++++---------- .../flow/component/slider/SliderBase.java | 6 ++- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java index 9b5f006c10b..97e8d7ce3bb 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java @@ -17,38 +17,35 @@ import com.vaadin.experimental.FeatureFlags; import com.vaadin.flow.component.AttachEvent; -import com.vaadin.flow.component.Focusable; -import com.vaadin.flow.component.HasSize; -import com.vaadin.flow.component.KeyNotifier; import com.vaadin.flow.component.Tag; import com.vaadin.flow.component.UI; /** - * Slider is an input component that allows the user to select a numeric value - * within a range by dragging a handle along a track. + * Slider is an input field that allows the user to select a numeric value + * within a range by dragging a handle along a track or using arrow keys for + * precise input. * * @author Vaadin Ltd. */ @Tag("vaadin-slider") // @NpmPackage(value = "@vaadin/slider", version = "25.1.0-alpha1") // @JsModule("@vaadin/slider/src/vaadin-slider.js") -public class Slider extends SliderBase - implements HasSize, Focusable, KeyNotifier { +public class Slider extends SliderBase { private final static double DEFAULT_MIN = 0.0; private final static double DEFAULT_MAX = 100.0; /** - * Constructs a {@code Slider} with a default range of 0 to 100 and an initial value - * of 0. + * Constructs a {@code Slider} with a default range of 0 to 100 and an + * initial value of 0. */ public Slider() { this(DEFAULT_MIN, DEFAULT_MAX, DEFAULT_MIN); } /** - * Constructs a {@code Slider} with a value change listener, a default range of 0 to - * 100, and an initial value of 0. + * Constructs a {@code Slider} with a value change listener, a default range + * of 0 to 100, and an initial value of 0. * * @param listener * the value change listener @@ -73,8 +70,8 @@ public Slider(double min, double max, double value) { } /** - * Constructs a {@code Slider} with the given min, max, initial value, and a value - * change listener. + * Constructs a {@code Slider} with the given min, max, initial value, and a + * value change listener. * * @param min * the minimum value @@ -92,8 +89,8 @@ public Slider(double min, double max, double value, } /** - * Constructs a {@code Slider} with the given label, a default range of 0 to 100, - * and an initial value of 0. + * Constructs a {@code Slider} with the given label, a default range of 0 to + * 100, and an initial value of 0. * * @param label * the text to set as the label @@ -104,8 +101,8 @@ public Slider(String label) { } /** - * Constructs a {@code Slider} with the given label and a value change listener, a - * default range of 0 to 100, and an initial value of 0. + * Constructs a {@code Slider} with the given label and a value change + * listener, a default range of 0 to 100, and an initial value of 0. * * @param label * the text to set as the label @@ -119,7 +116,8 @@ public Slider(String label, } /** - * Constructs a {@code Slider} with the given label, min, max, and initial value. + * Constructs a {@code Slider} with the given label, min, max, and initial + * value. * * @param label * the text to set as the label @@ -136,8 +134,8 @@ public Slider(String label, double min, double max, double value) { } /** - * Constructs a {@code Slider} with the given label, min, max, initial value, and a - * value change listener. + * Constructs a {@code Slider} with the given label, min, max, initial + * value, and a value change listener. * * @param label * the text to set as the label diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java index 413fb8445dd..a042f67b438 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java @@ -16,8 +16,11 @@ package com.vaadin.flow.component.slider; import com.vaadin.flow.component.AbstractSinglePropertyField; +import com.vaadin.flow.component.Focusable; import com.vaadin.flow.component.HasHelper; import com.vaadin.flow.component.HasLabel; +import com.vaadin.flow.component.HasSize; +import com.vaadin.flow.component.KeyNotifier; import com.vaadin.flow.component.shared.HasValidationProperties; /** @@ -32,7 +35,8 @@ */ public abstract class SliderBase, TValue> extends AbstractSinglePropertyField - implements HasLabel, HasHelper, HasValidationProperties { + implements HasLabel, HasHelper, HasValidationProperties, HasSize, + Focusable, KeyNotifier { /** * Constructs a slider with the given min, max, and initial value. From 0bd5afbbeb255b91084621bdea94f174986f0af5 Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Mon, 19 Jan 2026 11:47:33 +0400 Subject: [PATCH 15/36] refactor SliderBase to support IntegerSlider in the future --- .../vaadin/flow/component/slider/Slider.java | 88 +++++++++++++----- .../flow/component/slider/SliderBase.java | 42 ++++++--- .../slider/tests/SliderBaseTest.java | 89 ------------------- .../component/slider/tests/SliderTest.java | 61 ++++++++++++- 4 files changed, 156 insertions(+), 124 deletions(-) delete mode 100644 vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderBaseTest.java diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java index 97e8d7ce3bb..2d94b8634d6 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java @@ -15,10 +15,7 @@ */ package com.vaadin.flow.component.slider; -import com.vaadin.experimental.FeatureFlags; -import com.vaadin.flow.component.AttachEvent; import com.vaadin.flow.component.Tag; -import com.vaadin.flow.component.UI; /** * Slider is an input field that allows the user to select a numeric value @@ -32,8 +29,8 @@ // @JsModule("@vaadin/slider/src/vaadin-slider.js") public class Slider extends SliderBase { - private final static double DEFAULT_MIN = 0.0; - private final static double DEFAULT_MAX = 100.0; + private final static double DEFAULT_MIN = 0; + private final static double DEFAULT_MAX = 100; /** * Constructs a {@code Slider} with a default range of 0 to 100 and an @@ -154,23 +151,6 @@ public Slider(String label, double min, double max, double value, setLabel(label); } - @Override - protected void onAttach(AttachEvent attachEvent) { - super.onAttach(attachEvent); - checkFeatureFlag(attachEvent.getUI()); - } - - private void checkFeatureFlag(UI ui) { - FeatureFlags featureFlags = FeatureFlags - .get(ui.getSession().getService().getContext()); - boolean enabled = featureFlags - .isEnabled(SliderFeatureFlagProvider.SLIDER_COMPONENT); - - if (!enabled) { - throw new ExperimentalFeatureException(); - } - } - /** * @throws IllegalArgumentException * if the value is not between min and max @@ -184,4 +164,68 @@ public void setValue(Double value) { super.setValue(value); } + + /** + * Sets the minimum value of the slider. + * + * @param min + * the minimum value + * @throws IllegalArgumentException + * if the min is greater than the max value + */ + public void setMin(double min) { + super.setMin(min); + } + + /** + * Gets the minimum value of the slider. + * + * @return the minimum value + */ + public double getMin() { + return getMinDouble(); + } + + /** + * Sets the maximum value of the slider. + * + * @param max + * the maximum value + * @throws IllegalArgumentException + * if the max is less than the min value + */ + public void setMax(double max) { + super.setMax(max); + } + + /** + * Gets the maximum value of the slider. + * + * @return the maximum value + */ + public double getMax() { + return getMaxDouble(); + } + + /** + * Sets the step value of the slider. The step is the amount the value + * changes when the user moves the handle. + * + * @param step + * the step value + * @throws IllegalArgumentException + * if the step is less than or equal to zero + */ + public void setStep(double step) { + super.setStep(step); + } + + /** + * Gets the step value of the slider. + * + * @return the step value + */ + public double getStep() { + return getStepDouble(); + } } diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java index a042f67b438..99136040d98 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java @@ -15,12 +15,15 @@ */ package com.vaadin.flow.component.slider; +import com.vaadin.experimental.FeatureFlags; import com.vaadin.flow.component.AbstractSinglePropertyField; +import com.vaadin.flow.component.AttachEvent; import com.vaadin.flow.component.Focusable; import com.vaadin.flow.component.HasHelper; import com.vaadin.flow.component.HasLabel; import com.vaadin.flow.component.HasSize; import com.vaadin.flow.component.KeyNotifier; +import com.vaadin.flow.component.UI; import com.vaadin.flow.component.shared.HasValidationProperties; /** @@ -61,6 +64,23 @@ public SliderBase(double min, double max, TValue value) { setInvalid(false); } + @Override + protected void onAttach(AttachEvent attachEvent) { + super.onAttach(attachEvent); + checkFeatureFlag(attachEvent.getUI()); + } + + private void checkFeatureFlag(UI ui) { + FeatureFlags featureFlags = FeatureFlags + .get(ui.getSession().getService().getContext()); + boolean enabled = featureFlags + .isEnabled(SliderFeatureFlagProvider.SLIDER_COMPONENT); + + if (!enabled) { + throw new ExperimentalFeatureException(); + } + } + /** * Sets the minimum value of the slider. * @@ -69,8 +89,8 @@ public SliderBase(double min, double max, TValue value) { * @throws IllegalArgumentException * if the min is greater than the max value */ - public void setMin(double min) { - if (min > getMax()) { + void setMin(double min) { + if (min > getMaxDouble()) { throw new IllegalArgumentException( "The min value cannot be greater than the max value"); } @@ -83,8 +103,8 @@ public void setMin(double min) { * * @return the minimum value */ - public double getMin() { - return getElement().getProperty("min", 0.0); + double getMinDouble() { + return getElement().getProperty("min", 0); } /** @@ -95,8 +115,8 @@ public double getMin() { * @throws IllegalArgumentException * if the max is less than the min value */ - public void setMax(double max) { - if (max < getMin()) { + void setMax(double max) { + if (max < getMinDouble()) { throw new IllegalArgumentException( "The max value cannot be less than the min value"); } @@ -109,8 +129,8 @@ public void setMax(double max) { * * @return the maximum value */ - public double getMax() { - return getElement().getProperty("max", 100.0); + double getMaxDouble() { + return getElement().getProperty("max", 100); } /** @@ -122,7 +142,7 @@ public double getMax() { * @throws IllegalArgumentException * if the step is less than or equal to zero */ - public void setStep(double step) { + void setStep(double step) { if (step <= 0) { throw new IllegalArgumentException( "The step value must be a positive number"); @@ -136,7 +156,7 @@ public void setStep(double step) { * * @return the step value */ - public double getStep() { - return getElement().getProperty("step", 1.0); + double getStepDouble() { + return getElement().getProperty("step", 1); } } diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderBaseTest.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderBaseTest.java deleted file mode 100644 index ea7b501acf5..00000000000 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderBaseTest.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2000-2026 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.vaadin.flow.component.slider.tests; - -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.flow.component.Tag; -import com.vaadin.flow.component.slider.SliderBase; - -public class SliderBaseTest { - - @Tag("test-slider") - private static class TestSlider extends SliderBase { - public TestSlider(double min, double max, double value) { - super(min, max, value); - } - } - - @Test - public void setMin_getMin() { - TestSlider slider = new TestSlider(0.0, 100.0, 0.0); - Assert.assertEquals(0.0, slider.getMin(), 0.0); - Assert.assertEquals(0.0, slider.getElement().getProperty("min", 0.0), - 0.0); - - slider.setMin(10.0); - Assert.assertEquals(10.0, slider.getMin(), 0.0); - Assert.assertEquals(10.0, slider.getElement().getProperty("min", 0.0), - 0.0); - } - - @Test - public void setMax_getMax() { - TestSlider slider = new TestSlider(0.0, 100.0, 0.0); - Assert.assertEquals(100.0, slider.getMax(), 0.0); - Assert.assertEquals(100.0, slider.getElement().getProperty("max", 0.0), - 0.0); - - slider.setMax(200.0); - Assert.assertEquals(200.0, slider.getMax(), 0.0); - Assert.assertEquals(200.0, slider.getElement().getProperty("max", 0.0), - 0.0); - } - - @Test - public void setStep_getStep() { - TestSlider slider = new TestSlider(0.0, 100.0, 0.0); - Assert.assertEquals(1.0, slider.getStep(), 0.0); - Assert.assertEquals(1.0, slider.getElement().getProperty("step", 1.0), - 0.0); - - slider.setStep(5.0); - Assert.assertEquals(5.0, slider.getStep(), 0.0); - Assert.assertEquals(5.0, slider.getElement().getProperty("step", 1.0), - 0.0); - } - - @Test(expected = IllegalArgumentException.class) - public void setMin_greaterThanMax_throws() { - TestSlider slider = new TestSlider(0.0, 100.0, 0.0); - slider.setMin(150.0); - } - - @Test(expected = IllegalArgumentException.class) - public void setMax_lessThanMin_throws() { - TestSlider slider = new TestSlider(50.0, 100.0, 50.0); - slider.setMax(25.0); - } - - @Test(expected = IllegalArgumentException.class) - public void setStep_notPositive_throws() { - TestSlider slider = new TestSlider(0.0, 100.0, 0.0); - slider.setStep(0); - } -} diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java index 1d2ccb9a384..c2cb6922f9f 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java @@ -114,13 +114,13 @@ public void labelMinMaxValueListenerConstructor() { @Test(expected = IllegalArgumentException.class) public void setValue_lessThanMin_throws() { - Slider slider = new Slider(0, 100, 0); + Slider slider = new Slider(0.0, 100.0, 0.0); slider.setValue(-150.0); } @Test(expected = IllegalArgumentException.class) public void setValue_greaterThanMax_throws() { - Slider slider = new Slider(0, 100, 0); + Slider slider = new Slider(0.0, 100.0, 0.0); slider.setValue(150.0); } @@ -138,4 +138,61 @@ public void implementsKeyNotifierInterface() { Slider slider = new Slider(); Assert.assertTrue(slider instanceof KeyNotifier); } + + @Test + public void setMin_getMin() { + Slider slider = new Slider(0.0, 100.0, 0.0); + Assert.assertEquals(0.0, slider.getMin(), 0.0); + Assert.assertEquals(0.0, slider.getElement().getProperty("min", 0.0), + 0.0); + + slider.setMin(10.0); + Assert.assertEquals(10.0, slider.getMin(), 0.0); + Assert.assertEquals(10.0, slider.getElement().getProperty("min", 0.0), + 0.0); + } + + @Test + public void setMax_getMax() { + Slider slider = new Slider(0.0, 100.0, 0.0); + Assert.assertEquals(100.0, slider.getMax(), 0.0); + Assert.assertEquals(100.0, slider.getElement().getProperty("max", 0.0), + 0.0); + + slider.setMax(200.0); + Assert.assertEquals(200.0, slider.getMax(), 0.0); + Assert.assertEquals(200.0, slider.getElement().getProperty("max", 0.0), + 0.0); + } + + @Test + public void setStep_getStep() { + Slider slider = new Slider(0.0, 100.0, 0.0); + Assert.assertEquals(1.0, slider.getStep(), 0.0); + Assert.assertEquals(1.0, slider.getElement().getProperty("step", 1.0), + 0.0); + + slider.setStep(5.0); + Assert.assertEquals(5.0, slider.getStep(), 0.0); + Assert.assertEquals(5.0, slider.getElement().getProperty("step", 1.0), + 0.0); + } + + @Test(expected = IllegalArgumentException.class) + public void setMin_greaterThanMax_throws() { + Slider slider = new Slider(0.0, 100.0, 0.0); + slider.setMin(150.0); + } + + @Test(expected = IllegalArgumentException.class) + public void setMax_lessThanMin_throws() { + Slider slider = new Slider(50.0, 100.0, 50.0); + slider.setMax(25.0); + } + + @Test(expected = IllegalArgumentException.class) + public void setStep_notPositive_throws() { + Slider slider = new Slider(0.0, 100.0, 0.0); + slider.setStep(0.0); + } } From c7a45e828a65d32d08d319c9e19b6c2eb2fb17ff Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Mon, 19 Jan 2026 16:21:09 +0400 Subject: [PATCH 16/36] feat: add scheduleBeforeClientResponse helper to SliderBase Add a utility method to schedule actions that run before the client response, with deduplication by key. This allows deferring state validity checks until all property changes are complete. Co-Authored-By: Claude Opus 4.5 --- .../flow/component/slider/SliderBase.java | 45 ++++++++++++++++--- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java index 99136040d98..25b85781f56 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java @@ -15,6 +15,9 @@ */ package com.vaadin.flow.component.slider; +import java.util.HashSet; +import java.util.Set; + import com.vaadin.experimental.FeatureFlags; import com.vaadin.flow.component.AbstractSinglePropertyField; import com.vaadin.flow.component.AttachEvent; @@ -25,6 +28,7 @@ import com.vaadin.flow.component.KeyNotifier; import com.vaadin.flow.component.UI; import com.vaadin.flow.component.shared.HasValidationProperties; +import com.vaadin.flow.function.SerializableRunnable; /** * Abstract base class for slider components. @@ -36,10 +40,11 @@ * * @author Vaadin Ltd */ -public abstract class SliderBase, TValue> +public abstract class SliderBase, TValue extends Number> extends AbstractSinglePropertyField implements HasLabel, HasHelper, HasValidationProperties, HasSize, Focusable, KeyNotifier { + private Set pendingBeforeClientResponseActions = new HashSet<>(); /** * Constructs a slider with the given min, max, and initial value. @@ -51,13 +56,14 @@ public abstract class SliderBase getMaxDouble()) { throw new IllegalArgumentException( "The min value cannot be greater than the max value"); @@ -115,7 +121,7 @@ void setMin(double min) { * @throws IllegalArgumentException * if the max is less than the min value */ - void setMax(double max) { + void setMaxDouble(double max) { if (max < getMinDouble()) { throw new IllegalArgumentException( "The max value cannot be less than the min value"); @@ -142,7 +148,7 @@ void setMax(double max) { * @throws IllegalArgumentException * if the step is less than or equal to zero */ - void setStep(double step) { + void setStepDouble(double step) { if (step <= 0) { throw new IllegalArgumentException( "The step value must be a positive number"); @@ -159,4 +165,29 @@ void setStep(double step) { double getStepDouble() { return getElement().getProperty("step", 1); } + + /** + * Schedules the given action to be executed before the client response, + * identified by the given key. If an action with the same key is already + * scheduled, it will not be added again. + * + * @param key + * the unique key identifying the action + * @param action + * the action to be executed + */ + void scheduleBeforeClientResponse(String key, SerializableRunnable action) { + if (pendingBeforeClientResponseActions.contains(key)) { + return; + } + + getElement().getNode().runWhenAttached(ui -> { + ui.beforeClientResponse(this, context -> { + pendingBeforeClientResponseActions.remove(key); + action.run(); + }); + }); + + pendingBeforeClientResponseActions.add(key); + } } From 18524be67a74ad5b29d37d5b45d37b949432229b Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Mon, 19 Jan 2026 16:21:18 +0400 Subject: [PATCH 17/36] feat: add state validity warnings for Slider Log warnings when setMin, setMax, or setStep is called and the current value becomes inconsistent with the new constraints. The warnings are deferred using beforeClientResponse to check the final state after all property changes in the current request are complete. Co-Authored-By: Claude Opus 4.5 --- .../vaadin/flow/component/slider/Slider.java | 63 +++++++++++++++++-- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java index 2d94b8634d6..aa11c4c821a 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java @@ -15,6 +15,10 @@ */ package com.vaadin.flow.component.slider; +import java.util.Objects; + +import org.slf4j.LoggerFactory; + import com.vaadin.flow.component.Tag; /** @@ -31,6 +35,9 @@ public class Slider extends SliderBase { private final static double DEFAULT_MIN = 0; private final static double DEFAULT_MAX = 100; + private final static double DEFAULT_STEP = 1; + + private boolean pendingStateValidityCheck = false; /** * Constructs a {@code Slider} with a default range of 0 to 100 and an @@ -63,7 +70,7 @@ public Slider( * the initial value */ public Slider(double min, double max, double value) { - super(min, max, value); + super(min, max, DEFAULT_STEP, value); } /** @@ -153,15 +160,23 @@ public Slider(String label, double min, double max, double value, /** * @throws IllegalArgumentException - * if the value is not between min and max + * if the value is not between min and max or not aligned with + * the step value */ @Override public void setValue(Double value) { + Objects.requireNonNull(value, "Value cannot be null"); + if (value < getMin() || value > getMax()) { throw new IllegalArgumentException( "The value must be between min and max"); } + if (value % getStep() != 0) { + throw new IllegalArgumentException( + "The value is not aligned with the step value"); + } + super.setValue(value); } @@ -174,7 +189,19 @@ public void setValue(Double value) { * if the min is greater than the max value */ public void setMin(double min) { - super.setMin(min); + super.setMinDouble(min); + + scheduleBeforeClientResponse("min", () -> { + if (getValue() < getMin()) { + LoggerFactory.getLogger(Slider.class).warn( + """ + Value {} is below the minimum of {}. \ + This may happen when the minimum was changed but the value was not updated. \ + Consider increasing the value or decreasing the minimum to avoid inconsistent behavior. + """, + getValue(), getMin()); + } + }); } /** @@ -195,7 +222,19 @@ public double getMin() { * if the max is less than the min value */ public void setMax(double max) { - super.setMax(max); + super.setMaxDouble(max); + + scheduleBeforeClientResponse("max", () -> { + if (getValue() > getMax()) { + LoggerFactory.getLogger(Slider.class).warn( + """ + Current value {} exceeds the maximum of {}. \ + This may happen when the maximum was changed but the value was not updated. \ + Consider reducing the value or increasing the maximum to avoid inconsistent behavior. + """, + getValue(), getMax()); + } + }); } /** @@ -213,11 +252,23 @@ public double getMax() { * * @param step * the step value - * @throws IllegalArgumentException + * @throws IllegalArgumentExceptionm * if the step is less than or equal to zero */ public void setStep(double step) { - super.setStep(step); + super.setStepDouble(step); + + scheduleBeforeClientResponse("step", () -> { + if (getValue() % getStep() != 0) { + LoggerFactory.getLogger(Slider.class).warn( + """ + Value {} is not aligned with the step {}. \ + This may happen when the step was changed but the value was not updated. \ + Consider adjusting the value to align with the step to avoid inconsistent behavior. + """, + getValue(), getStep()); + } + }); } /** From 8329dddfc966019fd2a5fa76de9376e32af3430f Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Mon, 19 Jan 2026 16:21:25 +0400 Subject: [PATCH 18/36] test: add unit tests for Slider state validity warnings Add tests verifying that warnings are logged when value doesn't align with min, max, or step constraints after property changes. Co-Authored-By: Claude Opus 4.5 --- .../slider/tests/SliderWarningsTest.java | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderWarningsTest.java diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderWarningsTest.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderWarningsTest.java new file mode 100644 index 00000000000..b523a33191d --- /dev/null +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderWarningsTest.java @@ -0,0 +1,142 @@ +/* + * Copyright 2000-2026 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.flow.component.slider.tests; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.mockito.MockedStatic; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.vaadin.experimental.FeatureFlags; +import com.vaadin.flow.component.UI; +import com.vaadin.flow.component.slider.Slider; +import com.vaadin.flow.component.slider.SliderFeatureFlagProvider; +import com.vaadin.flow.server.VaadinContext; +import com.vaadin.flow.server.VaadinService; +import com.vaadin.flow.server.VaadinSession; + +public class SliderWarningsTest { + + private UI ui; + private Slider slider; + private Logger logger; + private FeatureFlags featureFlags = Mockito.mock(FeatureFlags.class); + private MockedStatic featureFlagsStatic = Mockito + .mockStatic(FeatureFlags.class); + private MockedStatic loggerFactoryStatic = Mockito + .mockStatic(LoggerFactory.class); + + @Before + public void setup() { + ui = new UI(); + UI.setCurrent(ui); + + VaadinSession session = Mockito.mock(VaadinSession.class); + VaadinService service = Mockito.mock(VaadinService.class); + VaadinContext context = Mockito.mock(VaadinContext.class); + + Mockito.when(session.hasLock()).thenReturn(true); + Mockito.when(session.getService()).thenReturn(service); + Mockito.when(service.getContext()).thenReturn(context); + + featureFlagsStatic.when(() -> FeatureFlags.get(context)) + .thenReturn(featureFlags); + Mockito.when(featureFlags + .isEnabled(SliderFeatureFlagProvider.SLIDER_COMPONENT)) + .thenReturn(true); + + logger = Mockito.mock(Logger.class); + loggerFactoryStatic.when(() -> LoggerFactory.getLogger(Slider.class)) + .thenReturn(logger); + + ui.getInternals().setSession(session); + + slider = new Slider(0, 100, 50); + ui.add(slider); + fakeClientCommunication(); + } + + @After + public void tearDown() { + UI.setCurrent(null); + featureFlagsStatic.close(); + loggerFactoryStatic.close(); + } + + @Test + public void setMin_valueBelowMin_warningIsShown() { + slider.setMin(60); + fakeClientCommunication(); + + Mockito.verify(logger).warn(Mockito.contains("below the minimum"), + Mockito.any(), Mockito.any()); + } + + @Test + public void setMin_valueAboveMin_noWarning() { + slider.setMin(40); + fakeClientCommunication(); + + Mockito.verify(logger, Mockito.never()).warn(Mockito.anyString(), + Mockito.any(), Mockito.any()); + } + + @Test + public void setMax_valueAboveMax_warningIsShown() { + slider.setMax(40); + fakeClientCommunication(); + + Mockito.verify(logger).warn(Mockito.contains("exceeds the maximum"), + Mockito.any(), Mockito.any()); + } + + @Test + public void setMax_valueBelowMax_noWarning() { + slider.setMax(60); + fakeClientCommunication(); + + Mockito.verify(logger, Mockito.never()).warn(Mockito.anyString(), + Mockito.any(), Mockito.any()); + } + + @Test + public void setStep_valueNotAligned_warningIsShown() { + slider.setStep(7); + fakeClientCommunication(); + + Mockito.verify(logger).warn( + Mockito.contains("not aligned with the step"), Mockito.any(), + Mockito.any()); + } + + @Test + public void setStep_valueAligned_noWarning() { + slider.setStep(10); + fakeClientCommunication(); + + Mockito.verify(logger, Mockito.never()).warn(Mockito.anyString(), + Mockito.any(), Mockito.any()); + } + + private void fakeClientCommunication() { + ui.getInternals().getStateTree().runExecutionsBeforeClientResponse(); + ui.getInternals().getStateTree().collectChanges(ignore -> { + }); + } +} From df8d95b31a6361c888eb519ce6f7a14f1f4a77ee Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Mon, 19 Jan 2026 16:32:22 +0400 Subject: [PATCH 19/36] docs: update JavaDoc for Slider constructors to include step parameter Co-Authored-By: Claude Opus 4.5 --- .../vaadin/flow/component/slider/Slider.java | 56 +++++++++++-------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java index aa11c4c821a..d40361bc511 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java @@ -37,64 +37,67 @@ public class Slider extends SliderBase { private final static double DEFAULT_MAX = 100; private final static double DEFAULT_STEP = 1; - private boolean pendingStateValidityCheck = false; - /** - * Constructs a {@code Slider} with a default range of 0 to 100 and an - * initial value of 0. + * Constructs a {@code Slider} with a default range of 0 to 100, a step of + * 1, and an initial value of 0. */ public Slider() { - this(DEFAULT_MIN, DEFAULT_MAX, DEFAULT_MIN); + this(DEFAULT_MIN, DEFAULT_MAX, DEFAULT_STEP, DEFAULT_MIN); } /** * Constructs a {@code Slider} with a value change listener, a default range - * of 0 to 100, and an initial value of 0. + * of 0 to 100, a step of 1, and an initial value of 0. * * @param listener * the value change listener */ public Slider( ValueChangeListener> listener) { - this(DEFAULT_MIN, DEFAULT_MAX, DEFAULT_MIN, listener); + this(DEFAULT_MIN, DEFAULT_MAX, DEFAULT_STEP, DEFAULT_MIN, listener); } /** - * Constructs a {@code Slider} with the given min, max, and initial value. + * Constructs a {@code Slider} with the given min, max, step, and initial + * value. * * @param min * the minimum value * @param max * the maximum value + * @param step + * the step value * @param value * the initial value */ - public Slider(double min, double max, double value) { - super(min, max, DEFAULT_STEP, value); + public Slider(double min, double max, double step, double value) { + super(min, max, step, value); } /** - * Constructs a {@code Slider} with the given min, max, initial value, and a - * value change listener. + * Constructs a {@code Slider} with the given min, max, step, initial value, + * and a value change listener. * * @param min * the minimum value * @param max * the maximum value + * @param step + * the step value * @param value * the initial value * @param listener * the value change listener */ - public Slider(double min, double max, double value, + public Slider(double min, double max, double step, double value, ValueChangeListener> listener) { - this(min, max, value); + this(min, max, step, value); addValueChangeListener(listener); } /** * Constructs a {@code Slider} with the given label, a default range of 0 to - * 100, and an initial value of 0. + * 100, a step of 1, and an initial value of 0. * * @param label * the text to set as the label @@ -106,7 +109,8 @@ public Slider(String label) { /** * Constructs a {@code Slider} with the given label and a value change - * listener, a default range of 0 to 100, and an initial value of 0. + * listener, a default range of 0 to 100, a step of 1, and an initial value + * of 0. * * @param label * the text to set as the label @@ -120,8 +124,8 @@ public Slider(String label, } /** - * Constructs a {@code Slider} with the given label, min, max, and initial - * value. + * Constructs a {@code Slider} with the given label, min, max, step, and + * initial value. * * @param label * the text to set as the label @@ -129,16 +133,19 @@ public Slider(String label, * the minimum value * @param max * the maximum value + * @param step + * the step value * @param value * the initial value */ - public Slider(String label, double min, double max, double value) { - this(min, max, value); + public Slider(String label, double min, double max, double step, + double value) { + this(min, max, step, value); setLabel(label); } /** - * Constructs a {@code Slider} with the given label, min, max, initial + * Constructs a {@code Slider} with the given label, min, max, step, initial * value, and a value change listener. * * @param label @@ -147,14 +154,17 @@ public Slider(String label, double min, double max, double value) { * the minimum value * @param max * the maximum value + * @param step + * the step value * @param value * the initial value * @param listener * the value change listener */ - public Slider(String label, double min, double max, double value, + public Slider(String label, double min, double max, double step, + double value, ValueChangeListener> listener) { - this(min, max, value, listener); + this(min, max, step, value, listener); setLabel(label); } From ec114bb31bf39ced00107ff3579f0c18a67e4034 Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Mon, 19 Jan 2026 16:34:21 +0400 Subject: [PATCH 20/36] refactor: change SliderBase constructor visibility and update tests Change SliderBase constructor from protected to package-private. Update SliderTest to use the 4-parameter constructor with step value. Co-Authored-By: Claude Opus 4.5 --- .../flow/component/slider/SliderBase.java | 2 +- .../component/slider/tests/SliderTest.java | 36 ++++++++++--------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java index 25b85781f56..0073774aa2c 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java @@ -56,7 +56,7 @@ public abstract class SliderBase listenerInvoked.set(true)); Assert.assertEquals(10.0, slider.getMin(), 0.0); Assert.assertEquals(50.0, slider.getMax(), 0.0); + Assert.assertEquals(5.0, slider.getStep(), 0.0); Assert.assertEquals(25.0, slider.getValue(), 0.0); slider.setValue(30.0); @@ -90,22 +92,24 @@ public void labelListenerConstructor() { } @Test - public void labelMinMaxValueConstructor() { - Slider slider = new Slider("Label", 10.0, 50.0, 25.0); + public void labelMinMaxStepValueConstructor() { + Slider slider = new Slider("Label", 10.0, 50.0, 5.0, 25.0); Assert.assertEquals("Label", slider.getLabel()); Assert.assertEquals(10.0, slider.getMin(), 0.0); Assert.assertEquals(50.0, slider.getMax(), 0.0); + Assert.assertEquals(5.0, slider.getStep(), 0.0); Assert.assertEquals(25.0, slider.getValue(), 0.0); } @Test - public void labelMinMaxValueListenerConstructor() { + public void labelMinMaxStepValueListenerConstructor() { AtomicBoolean listenerInvoked = new AtomicBoolean(false); - Slider slider = new Slider("Label", 10.0, 50.0, 25.0, + Slider slider = new Slider("Label", 10.0, 50.0, 5.0, 25.0, e -> listenerInvoked.set(true)); Assert.assertEquals("Label", slider.getLabel()); Assert.assertEquals(10.0, slider.getMin(), 0.0); Assert.assertEquals(50.0, slider.getMax(), 0.0); + Assert.assertEquals(5.0, slider.getStep(), 0.0); Assert.assertEquals(25.0, slider.getValue(), 0.0); slider.setValue(30.0); @@ -114,13 +118,13 @@ public void labelMinMaxValueListenerConstructor() { @Test(expected = IllegalArgumentException.class) public void setValue_lessThanMin_throws() { - Slider slider = new Slider(0.0, 100.0, 0.0); + Slider slider = new Slider(0.0, 100.0, 1.0, 0.0); slider.setValue(-150.0); } @Test(expected = IllegalArgumentException.class) public void setValue_greaterThanMax_throws() { - Slider slider = new Slider(0.0, 100.0, 0.0); + Slider slider = new Slider(0.0, 100.0, 1.0, 0.0); slider.setValue(150.0); } @@ -141,7 +145,7 @@ public void implementsKeyNotifierInterface() { @Test public void setMin_getMin() { - Slider slider = new Slider(0.0, 100.0, 0.0); + Slider slider = new Slider(0.0, 100.0, 1.0, 0.0); Assert.assertEquals(0.0, slider.getMin(), 0.0); Assert.assertEquals(0.0, slider.getElement().getProperty("min", 0.0), 0.0); @@ -154,7 +158,7 @@ public void setMin_getMin() { @Test public void setMax_getMax() { - Slider slider = new Slider(0.0, 100.0, 0.0); + Slider slider = new Slider(0.0, 100.0, 1.0, 0.0); Assert.assertEquals(100.0, slider.getMax(), 0.0); Assert.assertEquals(100.0, slider.getElement().getProperty("max", 0.0), 0.0); @@ -167,7 +171,7 @@ public void setMax_getMax() { @Test public void setStep_getStep() { - Slider slider = new Slider(0.0, 100.0, 0.0); + Slider slider = new Slider(0.0, 100.0, 1.0, 0.0); Assert.assertEquals(1.0, slider.getStep(), 0.0); Assert.assertEquals(1.0, slider.getElement().getProperty("step", 1.0), 0.0); @@ -180,19 +184,19 @@ public void setStep_getStep() { @Test(expected = IllegalArgumentException.class) public void setMin_greaterThanMax_throws() { - Slider slider = new Slider(0.0, 100.0, 0.0); + Slider slider = new Slider(0.0, 100.0, 1.0, 0.0); slider.setMin(150.0); } @Test(expected = IllegalArgumentException.class) public void setMax_lessThanMin_throws() { - Slider slider = new Slider(50.0, 100.0, 50.0); + Slider slider = new Slider(50.0, 100.0, 10.0, 50.0); slider.setMax(25.0); } @Test(expected = IllegalArgumentException.class) public void setStep_notPositive_throws() { - Slider slider = new Slider(0.0, 100.0, 0.0); + Slider slider = new Slider(0.0, 100.0, 1.0, 0.0); slider.setStep(0.0); } } From 9696324fbfacd38778232d93086a3367d0a0d950 Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Mon, 19 Jan 2026 16:36:57 +0400 Subject: [PATCH 21/36] minor corrections --- .../java/com/vaadin/flow/component/slider/SliderBase.java | 4 +++- .../flow/component/slider/tests/SliderWarningsTest.java | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java index 0073774aa2c..889ecc7180d 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java @@ -47,12 +47,14 @@ public abstract class SliderBase pendingBeforeClientResponseActions = new HashSet<>(); /** - * Constructs a slider with the given min, max, and initial value. + * Constructs a slider with the given min, max, step, and initial value. * * @param min * the minimum value * @param max * the maximum value + * @param step + * the step value * @param value * the initial value */ diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderWarningsTest.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderWarningsTest.java index b523a33191d..69730661eb5 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderWarningsTest.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderWarningsTest.java @@ -18,8 +18,8 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.mockito.Mockito; import org.mockito.MockedStatic; +import org.mockito.Mockito; import org.slf4j.Logger; import org.slf4j.LoggerFactory; From 2a8eb8963d44a4ab07eb528af6c574cd930be509 Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Mon, 19 Jan 2026 16:54:54 +0400 Subject: [PATCH 22/36] feat: add Slider constructors with default step and update tests Add constructors that use default step value of 1: - Slider(min, max, value) - Slider(min, max, value, listener) - Slider(label, min, max, value) - Slider(label, min, max, value, listener) Add corresponding unit tests verifying step defaults to 1. Co-Authored-By: Claude Opus 4.5 --- .../vaadin/flow/component/slider/Slider.java | 109 +++++++++++++++--- .../component/slider/tests/SliderTest.java | 48 ++++++++ 2 files changed, 144 insertions(+), 13 deletions(-) diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java index d40361bc511..1b675dcfb2d 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java @@ -38,16 +38,19 @@ public class Slider extends SliderBase { private final static double DEFAULT_STEP = 1; /** - * Constructs a {@code Slider} with a default range of 0 to 100, a step of - * 1, and an initial value of 0. + * Constructs a {@code Slider} with range 0-100 and initial value 0. + *

+ * The step defaults to 1. */ public Slider() { this(DEFAULT_MIN, DEFAULT_MAX, DEFAULT_STEP, DEFAULT_MIN); } /** - * Constructs a {@code Slider} with a value change listener, a default range - * of 0 to 100, a step of 1, and an initial value of 0. + * Constructs a {@code Slider} with range 0-100, initial value 0, and a + * value change listener. + *

+ * The step defaults to 1. * * @param listener * the value change listener @@ -58,7 +61,43 @@ public Slider( } /** - * Constructs a {@code Slider} with the given min, max, step, and initial + * Constructs a {@code Slider} with the given range and initial value. + *

+ * The step defaults to 1. + * + * @param min + * the minimum value + * @param max + * the maximum value + * @param value + * the initial value + */ + public Slider(double min, double max, double value) { + this(min, max, DEFAULT_STEP, value); + } + + /** + * Constructs a {@code Slider} with the given range, initial value, and a + * value change listener. + *

+ * The step defaults to 1. + * + * @param min + * the minimum value + * @param max + * the maximum value + * @param value + * the initial value + * @param listener + * the value change listener + */ + public Slider(double min, double max, double value, + ValueChangeListener> listener) { + this(min, max, DEFAULT_STEP, value, listener); + } + + /** + * Constructs a {@code Slider} with the given range, step, and initial * value. * * @param min @@ -75,7 +114,7 @@ public Slider(double min, double max, double step, double value) { } /** - * Constructs a {@code Slider} with the given min, max, step, initial value, + * Constructs a {@code Slider} with the given range, step, initial value, * and a value change listener. * * @param min @@ -96,8 +135,10 @@ public Slider(double min, double max, double step, double value, } /** - * Constructs a {@code Slider} with the given label, a default range of 0 to - * 100, a step of 1, and an initial value of 0. + * Constructs a {@code Slider} with the given label, range 0-100, and + * initial value 0. + *

+ * The step defaults to 1. * * @param label * the text to set as the label @@ -108,9 +149,10 @@ public Slider(String label) { } /** - * Constructs a {@code Slider} with the given label and a value change - * listener, a default range of 0 to 100, a step of 1, and an initial value - * of 0. + * Constructs a {@code Slider} with the given label, range 0-100, initial + * value 0, and a value change listener. + *

+ * The step defaults to 1. * * @param label * the text to set as the label @@ -124,7 +166,48 @@ public Slider(String label, } /** - * Constructs a {@code Slider} with the given label, min, max, step, and + * Constructs a {@code Slider} with the given label, range, and initial + * value. + *

+ * The step defaults to 1. + * + * @param label + * the text to set as the label + * @param min + * the minimum value + * @param max + * the maximum value + * @param value + * the initial value + */ + public Slider(String label, double min, double max, double value) { + this(min, max, value); + setLabel(label); + } + + /** + * Constructs a {@code Slider} with the given label, range, initial value, + * and a value change listener. + * + * @param label + * the text to set as the label + * @param min + * the minimum value + * @param max + * the maximum value + * @param value + * the initial value + * @param listener + * the value change listener + */ + public Slider(String label, double min, double max, double value, + ValueChangeListener> listener) { + this(min, max, value, listener); + setLabel(label); + } + + /** + * Constructs a {@code Slider} with the given label, range, step, and * initial value. * * @param label @@ -145,7 +228,7 @@ public Slider(String label, double min, double max, double step, } /** - * Constructs a {@code Slider} with the given label, min, max, step, initial + * Constructs a {@code Slider} with the given label, range, step, initial * value, and a value change listener. * * @param label diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java index 82e5fa348fb..b8149635a53 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java @@ -46,6 +46,29 @@ public void listenerConstructor() { Assert.assertTrue(listenerInvoked.get()); } + @Test + public void minMaxValueConstructor() { + Slider slider = new Slider(10.0, 50.0, 25.0); + Assert.assertEquals(10.0, slider.getMin(), 0.0); + Assert.assertEquals(50.0, slider.getMax(), 0.0); + Assert.assertEquals(1.0, slider.getStep(), 0.0); + Assert.assertEquals(25.0, slider.getValue(), 0.0); + } + + @Test + public void minMaxValueListenerConstructor() { + AtomicBoolean listenerInvoked = new AtomicBoolean(false); + Slider slider = new Slider(10.0, 50.0, 25.0, + e -> listenerInvoked.set(true)); + Assert.assertEquals(10.0, slider.getMin(), 0.0); + Assert.assertEquals(50.0, slider.getMax(), 0.0); + Assert.assertEquals(1.0, slider.getStep(), 0.0); + Assert.assertEquals(25.0, slider.getValue(), 0.0); + + slider.setValue(30.0); + Assert.assertTrue(listenerInvoked.get()); + } + @Test public void minMaxStepValueConstructor() { Slider slider = new Slider(10.0, 50.0, 5.0, 25.0); @@ -91,6 +114,31 @@ public void labelListenerConstructor() { Assert.assertTrue(listenerInvoked.get()); } + @Test + public void labelMinMaxValueConstructor() { + Slider slider = new Slider("Label", 10.0, 50.0, 25.0); + Assert.assertEquals("Label", slider.getLabel()); + Assert.assertEquals(10.0, slider.getMin(), 0.0); + Assert.assertEquals(50.0, slider.getMax(), 0.0); + Assert.assertEquals(1.0, slider.getStep(), 0.0); + Assert.assertEquals(25.0, slider.getValue(), 0.0); + } + + @Test + public void labelMinMaxValueListenerConstructor() { + AtomicBoolean listenerInvoked = new AtomicBoolean(false); + Slider slider = new Slider("Label", 10.0, 50.0, 25.0, + e -> listenerInvoked.set(true)); + Assert.assertEquals("Label", slider.getLabel()); + Assert.assertEquals(10.0, slider.getMin(), 0.0); + Assert.assertEquals(50.0, slider.getMax(), 0.0); + Assert.assertEquals(1.0, slider.getStep(), 0.0); + Assert.assertEquals(25.0, slider.getValue(), 0.0); + + slider.setValue(30.0); + Assert.assertTrue(listenerInvoked.get()); + } + @Test public void labelMinMaxStepValueConstructor() { Slider slider = new Slider("Label", 10.0, 50.0, 5.0, 25.0); From 0cc794a0dfdf6784962fb7e99e496fb0d0df19ce Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Mon, 19 Jan 2026 19:02:52 +0400 Subject: [PATCH 23/36] feat: add RangeSlider component with start/end value support Add RangeSlider component that allows selecting a numeric range with two handles. Includes RangeSliderValue record for the start/end value pair and updates SliderBase to support custom value converters. Co-Authored-By: Claude Opus 4.5 --- .../flow/component/slider/RangeSlider.java | 211 ++++++++++++++++++ .../component/slider/RangeSliderValue.java | 33 +++ .../flow/component/slider/SliderBase.java | 38 +++- 3 files changed, 280 insertions(+), 2 deletions(-) create mode 100644 vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/RangeSlider.java create mode 100644 vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/RangeSliderValue.java diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/RangeSlider.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/RangeSlider.java new file mode 100644 index 00000000000..c9da1d3ce34 --- /dev/null +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/RangeSlider.java @@ -0,0 +1,211 @@ +/* + * Copyright 2000-2026 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.flow.component.slider; + +import java.util.Objects; + +import org.slf4j.LoggerFactory; + +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.function.SerializableFunction; +import com.vaadin.flow.internal.JacksonUtils; + +import tools.jackson.databind.node.ArrayNode; + +/** + * RangeSlider is an input field that allows the user to select a numeric range + * within bounds by dragging two handles along a track or using arrow keys for + * precise input. + * + * @author Vaadin Ltd. + */ +@Tag("vaadin-range-slider") +// @NpmPackage(value = "@vaadin/slider", version = "25.1.0-alpha1") +// @JsModule("@vaadin/slider/src/vaadin-range-slider.js") +public class RangeSlider extends SliderBase { + + private static final SerializableFunction PARSER = arrayNode -> new RangeSliderValue( + arrayNode.get(0).asDouble(), arrayNode.get(1).asDouble()); + + private static final SerializableFunction FORMATTER = value -> { + ArrayNode arrayNode = JacksonUtils.createArrayNode(); + arrayNode.add(value.start()); + arrayNode.add(value.end()); + return arrayNode; + }; + + private final static double DEFAULT_MIN = 0; + private final static double DEFAULT_MAX = 100; + private final static double DEFAULT_STEP = 1; + + /** + * Constructs a {@code RangeSlider} with range 0-100 and initial value + * 0-100. + *

+ * The step defaults to 1. + */ + public RangeSlider() { + this(DEFAULT_MIN, DEFAULT_MAX, DEFAULT_STEP, + new RangeSliderValue(DEFAULT_MIN, DEFAULT_MAX)); + } + + /** + * Constructs a {@code RangeSlider} with the given range, step, and initial + * value. + * + * @param min + * the minimum value + * @param max + * the maximum value + * @param step + * the step value + * @param value + * the initial value + */ + public RangeSlider(double min, double max, double step, + RangeSliderValue value) { + super(min, max, step, value, ArrayNode.class, PARSER, FORMATTER); + } + + /** + * @throws IllegalArgumentException + * if the value is not between min and max, not aligned with the + * step value, or if start is greater than end + */ + @Override + public void setValue(RangeSliderValue value) { + Objects.requireNonNull(value, "Value cannot be null"); + + if (value.start() > value.end()) { + throw new IllegalArgumentException( + "Start value cannot be greater than end value"); + } + + if (value.start() < getMin() || value.end() > getMax()) { + throw new IllegalArgumentException( + "The value must be between min and max"); + } + + if (value.start() % getStep() != 0 || value.end() % getStep() != 0) { + throw new IllegalArgumentException( + "The value is not aligned with the step value"); + } + + super.setValue(value); + } + + /** + * Sets the minimum value of the slider. + * + * @param min + * the minimum value + * @throws IllegalArgumentException + * if the min is greater than the max value + */ + public void setMin(double min) { + super.setMinDouble(min); + + scheduleBeforeClientResponse("min", () -> { + if (getValue().start() < getMin()) { + LoggerFactory.getLogger(RangeSlider.class).warn( + """ + Start value {} is below the minimum of {}. \ + This may happen when the minimum was changed but the value was not updated. \ + Consider increasing the value or decreasing the minimum to avoid inconsistent behavior. + """, + getValue().start(), getMin()); + } + }); + } + + /** + * Gets the minimum value of the slider. + * + * @return the minimum value + */ + public double getMin() { + return getMinDouble(); + } + + /** + * Sets the maximum value of the slider. + * + * @param max + * the maximum value + * @throws IllegalArgumentException + * if the max is less than the min value + */ + public void setMax(double max) { + super.setMaxDouble(max); + + scheduleBeforeClientResponse("max", () -> { + if (getValue().end() > getMax()) { + LoggerFactory.getLogger(RangeSlider.class).warn( + """ + End value {} exceeds the maximum of {}. \ + This may happen when the maximum was changed but the value was not updated. \ + Consider reducing the value or increasing the maximum to avoid inconsistent behavior. + """, + getValue().end(), getMax()); + } + }); + } + + /** + * Gets the maximum value of the slider. + * + * @return the maximum value + */ + public double getMax() { + return getMaxDouble(); + } + + /** + * Sets the step value of the slider. The step is the amount the value + * changes when the user moves a handle. + * + * @param step + * the step value + * @throws IllegalArgumentException + * if the step is less than or equal to zero + */ + public void setStep(double step) { + super.setStepDouble(step); + + scheduleBeforeClientResponse("step", () -> { + RangeSliderValue value = getValue(); + if (value.start() % getStep() != 0 + || value.end() % getStep() != 0) { + LoggerFactory.getLogger(RangeSlider.class).warn( + """ + Value [{}, {}] is not aligned with the step {}. \ + This may happen when the step was changed but the value was not updated. \ + Consider adjusting the value to align with the step to avoid inconsistent behavior. + """, + value.start(), value.end(), getStep()); + } + }); + } + + /** + * Gets the step value of the slider. + * + * @return the step value + */ + public double getStep() { + return getStepDouble(); + } +} diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/RangeSliderValue.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/RangeSliderValue.java new file mode 100644 index 00000000000..4e1ef9c94f4 --- /dev/null +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/RangeSliderValue.java @@ -0,0 +1,33 @@ +/* + * Copyright 2000-2026 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.flow.component.slider; + +import java.io.Serializable; + +/** + * Represents the value of a {@link RangeSlider}, consisting of a start and end + * value. + * + * @param start + * the start value of the range + * @param end + * the end value of the range + * + * @author Vaadin Ltd + */ +public record RangeSliderValue(double start, + double end) implements Serializable { +} diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java index 889ecc7180d..64eea3ed1fa 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java @@ -28,6 +28,7 @@ import com.vaadin.flow.component.KeyNotifier; import com.vaadin.flow.component.UI; import com.vaadin.flow.component.shared.HasValidationProperties; +import com.vaadin.flow.function.SerializableFunction; import com.vaadin.flow.function.SerializableRunnable; /** @@ -40,10 +41,10 @@ * * @author Vaadin Ltd */ -public abstract class SliderBase, TValue extends Number> +public abstract class SliderBase, TValue> extends AbstractSinglePropertyField implements HasLabel, HasHelper, HasValidationProperties, HasSize, - Focusable, KeyNotifier { + Focusable, KeyNotifier { private Set pendingBeforeClientResponseActions = new HashSet<>(); /** @@ -60,7 +61,40 @@ public abstract class SliderBase + * the presentation type used by the element property + * @param min + * the minimum value + * @param max + * the maximum value + * @param step + * the step value + * @param value + * the initial value + * @param presentationType + * the class of the presentation type + * @param presentationToModel + * a function to convert from presentation to model + * @param modelToPresentation + * a function to convert from model to presentation + */ + SliderBase(double min, double max, double step, + TValue value, Class presentationType, + SerializableFunction presentationToModel, + SerializableFunction modelToPresentation) { + super("value", null, presentationType, presentationToModel, + modelToPresentation); + init(min, max, step, value); + } + private void init(double min, double max, double step, TValue value) { getElement().setProperty("manualValidation", true); setMinDouble(min); From 113dd688a3e275e1e2b2b3d177279de9fd5834ec Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Mon, 26 Jan 2026 12:01:33 +0400 Subject: [PATCH 24/36] refactor: move start/end validation to RangeSliderValue record Move the "start value cannot be greater than end value" validation from RangeSlider#setValue to the RangeSliderValue compact constructor, ensuring the invariant is enforced at construction time. Co-Authored-By: Claude Opus 4.5 --- .../flow/component/slider/RangeSlider.java | 9 ++------- .../flow/component/slider/RangeSliderValue.java | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/RangeSlider.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/RangeSlider.java index c9da1d3ce34..43b835a5c34 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/RangeSlider.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/RangeSlider.java @@ -82,18 +82,13 @@ public RangeSlider(double min, double max, double step, /** * @throws IllegalArgumentException - * if the value is not between min and max, not aligned with the - * step value, or if start is greater than end + * if the value is not between min and max or not aligned with + * the step value */ @Override public void setValue(RangeSliderValue value) { Objects.requireNonNull(value, "Value cannot be null"); - if (value.start() > value.end()) { - throw new IllegalArgumentException( - "Start value cannot be greater than end value"); - } - if (value.start() < getMin() || value.end() > getMax()) { throw new IllegalArgumentException( "The value must be between min and max"); diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/RangeSliderValue.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/RangeSliderValue.java index 4e1ef9c94f4..76a9d7e3300 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/RangeSliderValue.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/RangeSliderValue.java @@ -30,4 +30,21 @@ */ public record RangeSliderValue(double start, double end) implements Serializable { + + /** + * Creates a new RangeSliderValue with the given start and end values. + * + * @param start + * the start value of the range + * @param end + * the end value of the range + * @throws IllegalArgumentException + * if start is greater than end + */ + public RangeSliderValue { + if (start > end) { + throw new IllegalArgumentException( + "Start value cannot be greater than end value"); + } + } } From 35b103d4084e5e7b60229019c4cc0b14937e7c51 Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Mon, 26 Jan 2026 12:44:05 +0400 Subject: [PATCH 25/36] code polish --- .../flow/component/slider/RangeSlider.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/RangeSlider.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/RangeSlider.java index 43b835a5c34..2fd30a4f2ce 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/RangeSlider.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/RangeSlider.java @@ -15,6 +15,7 @@ */ package com.vaadin.flow.component.slider; +import java.util.Arrays; import java.util.Objects; import org.slf4j.LoggerFactory; @@ -37,14 +38,14 @@ // @JsModule("@vaadin/slider/src/vaadin-range-slider.js") public class RangeSlider extends SliderBase { - private static final SerializableFunction PARSER = arrayNode -> new RangeSliderValue( - arrayNode.get(0).asDouble(), arrayNode.get(1).asDouble()); + private static final SerializableFunction PARSER = arrayNode -> { + return new RangeSliderValue(arrayNode.get(0).asDouble(), + arrayNode.get(1).asDouble()); + }; private static final SerializableFunction FORMATTER = value -> { - ArrayNode arrayNode = JacksonUtils.createArrayNode(); - arrayNode.add(value.start()); - arrayNode.add(value.end()); - return arrayNode; + return JacksonUtils + .listToJson(Arrays.asList(value.start(), value.end())); }; private final static double DEFAULT_MIN = 0; @@ -119,7 +120,7 @@ public void setMin(double min) { """ Start value {} is below the minimum of {}. \ This may happen when the minimum was changed but the value was not updated. \ - Consider increasing the value or decreasing the minimum to avoid inconsistent behavior. + Increase the start value or decrease the minimum to avoid inconsistent UI behavior. """, getValue().start(), getMin()); } @@ -150,9 +151,9 @@ public void setMax(double max) { if (getValue().end() > getMax()) { LoggerFactory.getLogger(RangeSlider.class).warn( """ - End value {} exceeds the maximum of {}. \ + End value {} exceeds the maximum of {}. This may happen when the maximum was changed but the value was not updated. \ - Consider reducing the value or increasing the maximum to avoid inconsistent behavior. + Decrease the end value or increase the maximum to avoid inconsistent UI behavior. """, getValue().end(), getMax()); } @@ -188,7 +189,7 @@ public void setStep(double step) { """ Value [{}, {}] is not aligned with the step {}. \ This may happen when the step was changed but the value was not updated. \ - Consider adjusting the value to align with the step to avoid inconsistent behavior. + Update the value so that it aligns with the step to avoid inconsistent UI behavior. """, value.start(), value.end(), getStep()); } From 33a9c5b9647c46267aee44321ea3391f35c0fd8b Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Mon, 26 Jan 2026 12:44:14 +0400 Subject: [PATCH 26/36] code polish --- .../main/java/com/vaadin/flow/component/slider/Slider.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java index 1b675dcfb2d..236bca0466b 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java @@ -290,7 +290,7 @@ public void setMin(double min) { """ Value {} is below the minimum of {}. \ This may happen when the minimum was changed but the value was not updated. \ - Consider increasing the value or decreasing the minimum to avoid inconsistent behavior. + Increase the value or decrease the minimum to avoid inconsistent UI behavior. """, getValue(), getMin()); } @@ -323,7 +323,7 @@ public void setMax(double max) { """ Current value {} exceeds the maximum of {}. \ This may happen when the maximum was changed but the value was not updated. \ - Consider reducing the value or increasing the maximum to avoid inconsistent behavior. + Decrease the value or increase the maximum to avoid inconsistent UI behavior. """, getValue(), getMax()); } @@ -357,7 +357,7 @@ public void setStep(double step) { """ Value {} is not aligned with the step {}. \ This may happen when the step was changed but the value was not updated. \ - Consider adjusting the value to align with the step to avoid inconsistent behavior. + Update the value so that it aligns with the step to avoid inconsistent UI behavior. """, getValue(), getStep()); } From 6c572fd1b212ef3596f277dd4551ed09215570cb Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Mon, 26 Jan 2026 12:45:30 +0400 Subject: [PATCH 27/36] attach Mockito as agent in Surefire test runs --- vaadin-slider-flow-parent/vaadin-slider-flow/pom.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/pom.xml b/vaadin-slider-flow-parent/vaadin-slider-flow/pom.xml index 691671efcf1..566208d8ebd 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/pom.xml +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/pom.xml @@ -10,6 +10,9 @@ jar Vaadin Slider Vaadin Slider + + -javaagent:${org.mockito:mockito-core:jar} + com.vaadin From 7e02f904b438125faf96ca98443f01c09ea3dd4f Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Mon, 26 Jan 2026 13:58:40 +0400 Subject: [PATCH 28/36] add RangeSliderWarningsTest --- .../slider/tests/RangeSliderWarningsTest.java | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderWarningsTest.java diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderWarningsTest.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderWarningsTest.java new file mode 100644 index 00000000000..6d5c8767e52 --- /dev/null +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderWarningsTest.java @@ -0,0 +1,129 @@ +/* + * Copyright 2000-2026 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.flow.component.slider.tests; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.vaadin.experimental.FeatureFlags; +import com.vaadin.flow.component.slider.RangeSlider; +import com.vaadin.flow.component.slider.RangeSliderValue; +import com.vaadin.flow.component.slider.SliderFeatureFlagProvider; +import com.vaadin.tests.MockUI; + +public class RangeSliderWarningsTest { + + private MockUI ui = new MockUI(); + private RangeSlider slider; + private Logger logger; + private FeatureFlags featureFlags = Mockito.mock(FeatureFlags.class); + private MockedStatic featureFlagsStatic = Mockito + .mockStatic(FeatureFlags.class); + private MockedStatic loggerFactoryStatic = Mockito + .mockStatic(LoggerFactory.class); + + @Before + public void setup() { + featureFlagsStatic + .when(() -> FeatureFlags + .get(ui.getSession().getService().getContext())) + .thenReturn(featureFlags); + Mockito.when(featureFlags + .isEnabled(SliderFeatureFlagProvider.SLIDER_COMPONENT)) + .thenReturn(true); + + logger = Mockito.mock(Logger.class); + loggerFactoryStatic + .when(() -> LoggerFactory.getLogger(RangeSlider.class)) + .thenReturn(logger); + + slider = new RangeSlider(0, 100, 1, new RangeSliderValue(25, 75)); + ui.add(slider); + fakeClientCommunication(); + } + + @After + public void tearDown() { + featureFlagsStatic.close(); + loggerFactoryStatic.close(); + } + + @Test + public void setMin_startValueBelowMin_warningIsShown() { + slider.setMin(30); + fakeClientCommunication(); + + Mockito.verify(logger).warn(Mockito.contains("below the minimum"), + Mockito.any(), Mockito.any()); + } + + @Test + public void setMin_startValueAboveMin_noWarning() { + slider.setMin(20); + fakeClientCommunication(); + + Mockito.verify(logger, Mockito.never()).warn(Mockito.anyString(), + Mockito.any(), Mockito.any()); + } + + @Test + public void setMax_endValueAboveMax_warningIsShown() { + slider.setMax(70); + fakeClientCommunication(); + + Mockito.verify(logger).warn(Mockito.contains("exceeds the maximum"), + Mockito.any(), Mockito.any()); + } + + @Test + public void setMax_endValueBelowMax_noWarning() { + slider.setMax(80); + fakeClientCommunication(); + + Mockito.verify(logger, Mockito.never()).warn(Mockito.anyString(), + Mockito.any(), Mockito.any()); + } + + @Test + public void setStep_valueNotAligned_warningIsShown() { + slider.setStep(7); + fakeClientCommunication(); + + Mockito.verify(logger).warn( + Mockito.contains("not aligned with the step"), Mockito.any(), + Mockito.any(), Mockito.any()); + } + + @Test + public void setStep_valueAligned_noWarning() { + slider.setStep(5); + fakeClientCommunication(); + + Mockito.verify(logger, Mockito.never()).warn(Mockito.anyString(), + Mockito.any(), Mockito.any(), Mockito.any()); + } + + private void fakeClientCommunication() { + ui.getInternals().getStateTree().runExecutionsBeforeClientResponse(); + ui.getInternals().getStateTree().collectChanges(ignore -> { + }); + } +} From ef69bbcfe6016b3a553f5ad7de34ae30869daa09 Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Mon, 26 Jan 2026 14:04:05 +0400 Subject: [PATCH 29/36] update tests to use MockUI --- .../slider/tests/SliderWarningsTest.java | 30 +++++-------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderWarningsTest.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderWarningsTest.java index 69730661eb5..50655f3cf26 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderWarningsTest.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderWarningsTest.java @@ -24,18 +24,16 @@ import org.slf4j.LoggerFactory; import com.vaadin.experimental.FeatureFlags; -import com.vaadin.flow.component.UI; import com.vaadin.flow.component.slider.Slider; import com.vaadin.flow.component.slider.SliderFeatureFlagProvider; -import com.vaadin.flow.server.VaadinContext; -import com.vaadin.flow.server.VaadinService; -import com.vaadin.flow.server.VaadinSession; +import com.vaadin.tests.MockUI; public class SliderWarningsTest { - private UI ui; private Slider slider; - private Logger logger; + + private MockUI ui = new MockUI(); + private Logger logger = Mockito.mock(Logger.class); private FeatureFlags featureFlags = Mockito.mock(FeatureFlags.class); private MockedStatic featureFlagsStatic = Mockito .mockStatic(FeatureFlags.class); @@ -44,29 +42,16 @@ public class SliderWarningsTest { @Before public void setup() { - ui = new UI(); - UI.setCurrent(ui); - - VaadinSession session = Mockito.mock(VaadinSession.class); - VaadinService service = Mockito.mock(VaadinService.class); - VaadinContext context = Mockito.mock(VaadinContext.class); - - Mockito.when(session.hasLock()).thenReturn(true); - Mockito.when(session.getService()).thenReturn(service); - Mockito.when(service.getContext()).thenReturn(context); - - featureFlagsStatic.when(() -> FeatureFlags.get(context)) + featureFlagsStatic + .when(() -> FeatureFlags + .get(ui.getSession().getService().getContext())) .thenReturn(featureFlags); Mockito.when(featureFlags .isEnabled(SliderFeatureFlagProvider.SLIDER_COMPONENT)) .thenReturn(true); - - logger = Mockito.mock(Logger.class); loggerFactoryStatic.when(() -> LoggerFactory.getLogger(Slider.class)) .thenReturn(logger); - ui.getInternals().setSession(session); - slider = new Slider(0, 100, 50); ui.add(slider); fakeClientCommunication(); @@ -74,7 +59,6 @@ public void setup() { @After public void tearDown() { - UI.setCurrent(null); featureFlagsStatic.close(); loggerFactoryStatic.close(); } From bbb9ce2d4bb2f03ebae119086f417a07996deb6a Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Mon, 26 Jan 2026 14:16:29 +0400 Subject: [PATCH 30/36] code polish --- .../component/slider/tests/RangeSliderWarningsTest.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderWarningsTest.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderWarningsTest.java index 6d5c8767e52..10791720a27 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderWarningsTest.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderWarningsTest.java @@ -31,9 +31,10 @@ public class RangeSliderWarningsTest { - private MockUI ui = new MockUI(); private RangeSlider slider; - private Logger logger; + + private MockUI ui = new MockUI(); + private Logger logger = Mockito.mock(Logger.class); private FeatureFlags featureFlags = Mockito.mock(FeatureFlags.class); private MockedStatic featureFlagsStatic = Mockito .mockStatic(FeatureFlags.class); @@ -49,8 +50,6 @@ public void setup() { Mockito.when(featureFlags .isEnabled(SliderFeatureFlagProvider.SLIDER_COMPONENT)) .thenReturn(true); - - logger = Mockito.mock(Logger.class); loggerFactoryStatic .when(() -> LoggerFactory.getLogger(RangeSlider.class)) .thenReturn(logger); From dfc3cf4d9a567baeb7f78bd035d9c19b0b811d5f Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Mon, 26 Jan 2026 14:57:18 +0400 Subject: [PATCH 31/36] test: add unit tests for RangeSlider Add RangeSliderTest with tests for constructors, setValue validation, interface implementations, and property setters/getters. Add RangeSliderSerializableTest for serialization testing. Co-Authored-By: Claude Opus 4.5 --- .../tests/RangeSliderSerializableTest.java | 21 +++ .../slider/tests/RangeSliderTest.java | 153 ++++++++++++++++++ 2 files changed, 174 insertions(+) create mode 100644 vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderSerializableTest.java create mode 100644 vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderTest.java diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderSerializableTest.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderSerializableTest.java new file mode 100644 index 00000000000..303fd71c45b --- /dev/null +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderSerializableTest.java @@ -0,0 +1,21 @@ +/* + * Copyright 2000-2026 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.flow.component.slider.tests; + +import com.vaadin.flow.testutil.ClassesSerializableTest; + +public class RangeSliderSerializableTest extends ClassesSerializableTest { +} diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderTest.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderTest.java new file mode 100644 index 00000000000..fcabcb17502 --- /dev/null +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderTest.java @@ -0,0 +1,153 @@ +/* + * Copyright 2000-2026 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.flow.component.slider.tests; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.flow.component.Focusable; +import com.vaadin.flow.component.HasSize; +import com.vaadin.flow.component.KeyNotifier; +import com.vaadin.flow.component.slider.RangeSlider; +import com.vaadin.flow.component.slider.RangeSliderValue; + +public class RangeSliderTest { + + @Test + public void defaultConstructor() { + RangeSlider slider = new RangeSlider(); + Assert.assertEquals(0.0, slider.getMin(), 0.0); + Assert.assertEquals(100.0, slider.getMax(), 0.0); + Assert.assertEquals(1.0, slider.getStep(), 0.0); + Assert.assertEquals(new RangeSliderValue(0.0, 100.0), slider.getValue()); + } + + @Test + public void minMaxStepValueConstructor() { + RangeSlider slider = new RangeSlider(10.0, 50.0, 5.0, + new RangeSliderValue(15.0, 45.0)); + Assert.assertEquals(10.0, slider.getMin(), 0.0); + Assert.assertEquals(50.0, slider.getMax(), 0.0); + Assert.assertEquals(5.0, slider.getStep(), 0.0); + Assert.assertEquals(new RangeSliderValue(15.0, 45.0), slider.getValue()); + } + + @Test(expected = IllegalArgumentException.class) + public void setValue_startLessThanMin_throws() { + RangeSlider slider = new RangeSlider(0.0, 100.0, 1.0, + new RangeSliderValue(0.0, 100.0)); + slider.setValue(new RangeSliderValue(-10.0, 50.0)); + } + + @Test(expected = IllegalArgumentException.class) + public void setValue_endGreaterThanMax_throws() { + RangeSlider slider = new RangeSlider(0.0, 100.0, 1.0, + new RangeSliderValue(0.0, 100.0)); + slider.setValue(new RangeSliderValue(50.0, 150.0)); + } + + @Test(expected = IllegalArgumentException.class) + public void setValue_startGreaterThanEnd_throws() { + new RangeSliderValue(75.0, 25.0); + } + + @Test(expected = NullPointerException.class) + public void setValue_null_throws() { + RangeSlider slider = new RangeSlider(); + slider.setValue(null); + } + + @Test + public void implementsHasSizeInterface() { + RangeSlider slider = new RangeSlider(); + Assert.assertTrue(slider instanceof HasSize); + } + + @Test + public void implementsFocusableInterface() { + RangeSlider slider = new RangeSlider(); + Assert.assertTrue(slider instanceof Focusable); + } + + @Test + public void implementsKeyNotifierInterface() { + RangeSlider slider = new RangeSlider(); + Assert.assertTrue(slider instanceof KeyNotifier); + } + + @Test + public void setMin_getMin() { + RangeSlider slider = new RangeSlider(0.0, 100.0, 1.0, + new RangeSliderValue(0.0, 100.0)); + Assert.assertEquals(0.0, slider.getMin(), 0.0); + Assert.assertEquals(0.0, slider.getElement().getProperty("min", 0.0), + 0.0); + + slider.setMin(10.0); + Assert.assertEquals(10.0, slider.getMin(), 0.0); + Assert.assertEquals(10.0, slider.getElement().getProperty("min", 0.0), + 0.0); + } + + @Test + public void setMax_getMax() { + RangeSlider slider = new RangeSlider(0.0, 100.0, 1.0, + new RangeSliderValue(0.0, 100.0)); + Assert.assertEquals(100.0, slider.getMax(), 0.0); + Assert.assertEquals(100.0, slider.getElement().getProperty("max", 0.0), + 0.0); + + slider.setMax(200.0); + Assert.assertEquals(200.0, slider.getMax(), 0.0); + Assert.assertEquals(200.0, slider.getElement().getProperty("max", 0.0), + 0.0); + } + + @Test + public void setStep_getStep() { + RangeSlider slider = new RangeSlider(0.0, 100.0, 1.0, + new RangeSliderValue(0.0, 100.0)); + Assert.assertEquals(1.0, slider.getStep(), 0.0); + Assert.assertEquals(1.0, slider.getElement().getProperty("step", 1.0), + 0.0); + + slider.setStep(5.0); + Assert.assertEquals(5.0, slider.getStep(), 0.0); + Assert.assertEquals(5.0, slider.getElement().getProperty("step", 1.0), + 0.0); + } + + @Test(expected = IllegalArgumentException.class) + public void setMin_greaterThanMax_throws() { + RangeSlider slider = new RangeSlider(0.0, 100.0, 1.0, + new RangeSliderValue(0.0, 100.0)); + slider.setMin(150.0); + } + + @Test(expected = IllegalArgumentException.class) + public void setMax_lessThanMin_throws() { + RangeSlider slider = new RangeSlider(50.0, 100.0, 10.0, + new RangeSliderValue(50.0, 100.0)); + slider.setMax(25.0); + } + + @Test(expected = IllegalArgumentException.class) + public void setStep_notPositive_throws() { + RangeSlider slider = new RangeSlider(0.0, 100.0, 1.0, + new RangeSliderValue(0.0, 100.0)); + slider.setStep(0.0); + } +} From 8e2ac9e0b7f86b22375f5757e3bfdd8228fcd99b Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Mon, 26 Jan 2026 15:38:52 +0400 Subject: [PATCH 32/36] add convenience constructors to RangeSlider Add constructors with various parameter combinations including label, min/max range, step, initial value, and value change listener. Also add corresponding unit tests for all new constructors. Co-Authored-By: Claude Opus 4.5 --- .../flow/component/slider/RangeSlider.java | 194 ++++++++++++++++++ .../slider/tests/RangeSliderTest.java | 134 ++++++++++++ 2 files changed, 328 insertions(+) diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/RangeSlider.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/RangeSlider.java index 2fd30a4f2ce..daeead35d4d 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/RangeSlider.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/RangeSlider.java @@ -63,6 +63,57 @@ public RangeSlider() { new RangeSliderValue(DEFAULT_MIN, DEFAULT_MAX)); } + /** + * Constructs a {@code RangeSlider} with range 0-100, initial value 0-100, + * and a value change listener. + *

+ * The step defaults to 1. + * + * @param listener + * the value change listener + */ + public RangeSlider( + ValueChangeListener> listener) { + this(DEFAULT_MIN, DEFAULT_MAX, DEFAULT_STEP, + new RangeSliderValue(DEFAULT_MIN, DEFAULT_MAX), listener); + } + + /** + * Constructs a {@code RangeSlider} with the given range and initial value. + *

+ * The step defaults to 1. + * + * @param min + * the minimum value + * @param max + * the maximum value + * @param value + * the initial value + */ + public RangeSlider(double min, double max, RangeSliderValue value) { + this(min, max, DEFAULT_STEP, value); + } + + /** + * Constructs a {@code RangeSlider} with the given range, initial value, and + * a value change listener. + *

+ * The step defaults to 1. + * + * @param min + * the minimum value + * @param max + * the maximum value + * @param value + * the initial value + * @param listener + * the value change listener + */ + public RangeSlider(double min, double max, RangeSliderValue value, + ValueChangeListener> listener) { + this(min, max, DEFAULT_STEP, value, listener); + } + /** * Constructs a {@code RangeSlider} with the given range, step, and initial * value. @@ -81,6 +132,149 @@ public RangeSlider(double min, double max, double step, super(min, max, step, value, ArrayNode.class, PARSER, FORMATTER); } + /** + * Constructs a {@code RangeSlider} with the given range, step, initial + * value, and a value change listener. + * + * @param min + * the minimum value + * @param max + * the maximum value + * @param step + * the step value + * @param value + * the initial value + * @param listener + * the value change listener + */ + public RangeSlider(double min, double max, double step, + RangeSliderValue value, + ValueChangeListener> listener) { + this(min, max, step, value); + addValueChangeListener(listener); + } + + /** + * Constructs a {@code RangeSlider} with the given label, range 0-100, and + * initial value 0-100. + *

+ * The step defaults to 1. + * + * @param label + * the text to set as the label + */ + public RangeSlider(String label) { + this(); + setLabel(label); + } + + /** + * Constructs a {@code RangeSlider} with the given label, range 0-100, + * initial value 0-100, and a value change listener. + *

+ * The step defaults to 1. + * + * @param label + * the text to set as the label + * @param listener + * the value change listener + */ + public RangeSlider(String label, + ValueChangeListener> listener) { + this(listener); + setLabel(label); + } + + /** + * Constructs a {@code RangeSlider} with the given label, range, and initial + * value. + *

+ * The step defaults to 1. + * + * @param label + * the text to set as the label + * @param min + * the minimum value + * @param max + * the maximum value + * @param value + * the initial value + */ + public RangeSlider(String label, double min, double max, + RangeSliderValue value) { + this(min, max, value); + setLabel(label); + } + + /** + * Constructs a {@code RangeSlider} with the given label, range, initial + * value, and a value change listener. + *

+ * The step defaults to 1. + * + * @param label + * the text to set as the label + * @param min + * the minimum value + * @param max + * the maximum value + * @param value + * the initial value + * @param listener + * the value change listener + */ + public RangeSlider(String label, double min, double max, + RangeSliderValue value, + ValueChangeListener> listener) { + this(min, max, value, listener); + setLabel(label); + } + + /** + * Constructs a {@code RangeSlider} with the given label, range, step, and + * initial value. + * + * @param label + * the text to set as the label + * @param min + * the minimum value + * @param max + * the maximum value + * @param step + * the step value + * @param value + * the initial value + */ + public RangeSlider(String label, double min, double max, double step, + RangeSliderValue value) { + this(min, max, step, value); + setLabel(label); + } + + /** + * Constructs a {@code RangeSlider} with the given label, range, step, + * initial value, and a value change listener. + * + * @param label + * the text to set as the label + * @param min + * the minimum value + * @param max + * the maximum value + * @param step + * the step value + * @param value + * the initial value + * @param listener + * the value change listener + */ + public RangeSlider(String label, double min, double max, double step, + RangeSliderValue value, + ValueChangeListener> listener) { + this(min, max, step, value, listener); + setLabel(label); + } + /** * @throws IllegalArgumentException * if the value is not between min and max or not aligned with diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderTest.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderTest.java index fcabcb17502..a1496ed68a8 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderTest.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderTest.java @@ -15,6 +15,8 @@ */ package com.vaadin.flow.component.slider.tests; +import java.util.concurrent.atomic.AtomicBoolean; + import org.junit.Assert; import org.junit.Test; @@ -35,6 +37,44 @@ public void defaultConstructor() { Assert.assertEquals(new RangeSliderValue(0.0, 100.0), slider.getValue()); } + @Test + public void listenerConstructor() { + AtomicBoolean listenerInvoked = new AtomicBoolean(false); + RangeSlider slider = new RangeSlider(e -> listenerInvoked.set(true)); + Assert.assertEquals(0.0, slider.getMin(), 0.0); + Assert.assertEquals(100.0, slider.getMax(), 0.0); + Assert.assertEquals(1.0, slider.getStep(), 0.0); + Assert.assertEquals(new RangeSliderValue(0.0, 100.0), slider.getValue()); + + slider.setValue(new RangeSliderValue(25.0, 75.0)); + Assert.assertTrue(listenerInvoked.get()); + } + + @Test + public void minMaxValueConstructor() { + RangeSlider slider = new RangeSlider(10.0, 50.0, + new RangeSliderValue(15.0, 45.0)); + Assert.assertEquals(10.0, slider.getMin(), 0.0); + Assert.assertEquals(50.0, slider.getMax(), 0.0); + Assert.assertEquals(1.0, slider.getStep(), 0.0); + Assert.assertEquals(new RangeSliderValue(15.0, 45.0), slider.getValue()); + } + + @Test + public void minMaxValueListenerConstructor() { + AtomicBoolean listenerInvoked = new AtomicBoolean(false); + RangeSlider slider = new RangeSlider(10.0, 50.0, + new RangeSliderValue(15.0, 45.0), + e -> listenerInvoked.set(true)); + Assert.assertEquals(10.0, slider.getMin(), 0.0); + Assert.assertEquals(50.0, slider.getMax(), 0.0); + Assert.assertEquals(1.0, slider.getStep(), 0.0); + Assert.assertEquals(new RangeSliderValue(15.0, 45.0), slider.getValue()); + + slider.setValue(new RangeSliderValue(20.0, 40.0)); + Assert.assertTrue(listenerInvoked.get()); + } + @Test public void minMaxStepValueConstructor() { RangeSlider slider = new RangeSlider(10.0, 50.0, 5.0, @@ -45,6 +85,100 @@ public void minMaxStepValueConstructor() { Assert.assertEquals(new RangeSliderValue(15.0, 45.0), slider.getValue()); } + @Test + public void minMaxStepValueListenerConstructor() { + AtomicBoolean listenerInvoked = new AtomicBoolean(false); + RangeSlider slider = new RangeSlider(10.0, 50.0, 5.0, + new RangeSliderValue(15.0, 45.0), + e -> listenerInvoked.set(true)); + Assert.assertEquals(10.0, slider.getMin(), 0.0); + Assert.assertEquals(50.0, slider.getMax(), 0.0); + Assert.assertEquals(5.0, slider.getStep(), 0.0); + Assert.assertEquals(new RangeSliderValue(15.0, 45.0), slider.getValue()); + + slider.setValue(new RangeSliderValue(20.0, 40.0)); + Assert.assertTrue(listenerInvoked.get()); + } + + @Test + public void labelConstructor() { + RangeSlider slider = new RangeSlider("Label"); + Assert.assertEquals("Label", slider.getLabel()); + Assert.assertEquals(0.0, slider.getMin(), 0.0); + Assert.assertEquals(100.0, slider.getMax(), 0.0); + Assert.assertEquals(1.0, slider.getStep(), 0.0); + Assert.assertEquals(new RangeSliderValue(0.0, 100.0), slider.getValue()); + } + + @Test + public void labelListenerConstructor() { + AtomicBoolean listenerInvoked = new AtomicBoolean(false); + RangeSlider slider = new RangeSlider("Label", + e -> listenerInvoked.set(true)); + Assert.assertEquals("Label", slider.getLabel()); + Assert.assertEquals(0.0, slider.getMin(), 0.0); + Assert.assertEquals(100.0, slider.getMax(), 0.0); + Assert.assertEquals(1.0, slider.getStep(), 0.0); + Assert.assertEquals(new RangeSliderValue(0.0, 100.0), slider.getValue()); + + slider.setValue(new RangeSliderValue(25.0, 75.0)); + Assert.assertTrue(listenerInvoked.get()); + } + + @Test + public void labelMinMaxValueConstructor() { + RangeSlider slider = new RangeSlider("Label", 10.0, 50.0, + new RangeSliderValue(15.0, 45.0)); + Assert.assertEquals("Label", slider.getLabel()); + Assert.assertEquals(10.0, slider.getMin(), 0.0); + Assert.assertEquals(50.0, slider.getMax(), 0.0); + Assert.assertEquals(1.0, slider.getStep(), 0.0); + Assert.assertEquals(new RangeSliderValue(15.0, 45.0), slider.getValue()); + } + + @Test + public void labelMinMaxValueListenerConstructor() { + AtomicBoolean listenerInvoked = new AtomicBoolean(false); + RangeSlider slider = new RangeSlider("Label", 10.0, 50.0, + new RangeSliderValue(15.0, 45.0), + e -> listenerInvoked.set(true)); + Assert.assertEquals("Label", slider.getLabel()); + Assert.assertEquals(10.0, slider.getMin(), 0.0); + Assert.assertEquals(50.0, slider.getMax(), 0.0); + Assert.assertEquals(1.0, slider.getStep(), 0.0); + Assert.assertEquals(new RangeSliderValue(15.0, 45.0), slider.getValue()); + + slider.setValue(new RangeSliderValue(20.0, 40.0)); + Assert.assertTrue(listenerInvoked.get()); + } + + @Test + public void labelMinMaxStepValueConstructor() { + RangeSlider slider = new RangeSlider("Label", 10.0, 50.0, 5.0, + new RangeSliderValue(15.0, 45.0)); + Assert.assertEquals("Label", slider.getLabel()); + Assert.assertEquals(10.0, slider.getMin(), 0.0); + Assert.assertEquals(50.0, slider.getMax(), 0.0); + Assert.assertEquals(5.0, slider.getStep(), 0.0); + Assert.assertEquals(new RangeSliderValue(15.0, 45.0), slider.getValue()); + } + + @Test + public void labelMinMaxStepValueListenerConstructor() { + AtomicBoolean listenerInvoked = new AtomicBoolean(false); + RangeSlider slider = new RangeSlider("Label", 10.0, 50.0, 5.0, + new RangeSliderValue(15.0, 45.0), + e -> listenerInvoked.set(true)); + Assert.assertEquals("Label", slider.getLabel()); + Assert.assertEquals(10.0, slider.getMin(), 0.0); + Assert.assertEquals(50.0, slider.getMax(), 0.0); + Assert.assertEquals(5.0, slider.getStep(), 0.0); + Assert.assertEquals(new RangeSliderValue(15.0, 45.0), slider.getValue()); + + slider.setValue(new RangeSliderValue(20.0, 40.0)); + Assert.assertTrue(listenerInvoked.get()); + } + @Test(expected = IllegalArgumentException.class) public void setValue_startLessThanMin_throws() { RangeSlider slider = new RangeSlider(0.0, 100.0, 1.0, From 9d2ac43c18093df19fbdb9b4cc82e12fde7bc65c Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Mon, 26 Jan 2026 15:40:33 +0400 Subject: [PATCH 33/36] format code --- .../slider/tests/RangeSliderTest.java | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderTest.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderTest.java index a1496ed68a8..30c8ffaef48 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderTest.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderTest.java @@ -34,7 +34,8 @@ public void defaultConstructor() { Assert.assertEquals(0.0, slider.getMin(), 0.0); Assert.assertEquals(100.0, slider.getMax(), 0.0); Assert.assertEquals(1.0, slider.getStep(), 0.0); - Assert.assertEquals(new RangeSliderValue(0.0, 100.0), slider.getValue()); + Assert.assertEquals(new RangeSliderValue(0.0, 100.0), + slider.getValue()); } @Test @@ -44,7 +45,8 @@ public void listenerConstructor() { Assert.assertEquals(0.0, slider.getMin(), 0.0); Assert.assertEquals(100.0, slider.getMax(), 0.0); Assert.assertEquals(1.0, slider.getStep(), 0.0); - Assert.assertEquals(new RangeSliderValue(0.0, 100.0), slider.getValue()); + Assert.assertEquals(new RangeSliderValue(0.0, 100.0), + slider.getValue()); slider.setValue(new RangeSliderValue(25.0, 75.0)); Assert.assertTrue(listenerInvoked.get()); @@ -57,7 +59,8 @@ public void minMaxValueConstructor() { Assert.assertEquals(10.0, slider.getMin(), 0.0); Assert.assertEquals(50.0, slider.getMax(), 0.0); Assert.assertEquals(1.0, slider.getStep(), 0.0); - Assert.assertEquals(new RangeSliderValue(15.0, 45.0), slider.getValue()); + Assert.assertEquals(new RangeSliderValue(15.0, 45.0), + slider.getValue()); } @Test @@ -69,7 +72,8 @@ public void minMaxValueListenerConstructor() { Assert.assertEquals(10.0, slider.getMin(), 0.0); Assert.assertEquals(50.0, slider.getMax(), 0.0); Assert.assertEquals(1.0, slider.getStep(), 0.0); - Assert.assertEquals(new RangeSliderValue(15.0, 45.0), slider.getValue()); + Assert.assertEquals(new RangeSliderValue(15.0, 45.0), + slider.getValue()); slider.setValue(new RangeSliderValue(20.0, 40.0)); Assert.assertTrue(listenerInvoked.get()); @@ -82,7 +86,8 @@ public void minMaxStepValueConstructor() { Assert.assertEquals(10.0, slider.getMin(), 0.0); Assert.assertEquals(50.0, slider.getMax(), 0.0); Assert.assertEquals(5.0, slider.getStep(), 0.0); - Assert.assertEquals(new RangeSliderValue(15.0, 45.0), slider.getValue()); + Assert.assertEquals(new RangeSliderValue(15.0, 45.0), + slider.getValue()); } @Test @@ -94,7 +99,8 @@ public void minMaxStepValueListenerConstructor() { Assert.assertEquals(10.0, slider.getMin(), 0.0); Assert.assertEquals(50.0, slider.getMax(), 0.0); Assert.assertEquals(5.0, slider.getStep(), 0.0); - Assert.assertEquals(new RangeSliderValue(15.0, 45.0), slider.getValue()); + Assert.assertEquals(new RangeSliderValue(15.0, 45.0), + slider.getValue()); slider.setValue(new RangeSliderValue(20.0, 40.0)); Assert.assertTrue(listenerInvoked.get()); @@ -107,7 +113,8 @@ public void labelConstructor() { Assert.assertEquals(0.0, slider.getMin(), 0.0); Assert.assertEquals(100.0, slider.getMax(), 0.0); Assert.assertEquals(1.0, slider.getStep(), 0.0); - Assert.assertEquals(new RangeSliderValue(0.0, 100.0), slider.getValue()); + Assert.assertEquals(new RangeSliderValue(0.0, 100.0), + slider.getValue()); } @Test @@ -119,7 +126,8 @@ public void labelListenerConstructor() { Assert.assertEquals(0.0, slider.getMin(), 0.0); Assert.assertEquals(100.0, slider.getMax(), 0.0); Assert.assertEquals(1.0, slider.getStep(), 0.0); - Assert.assertEquals(new RangeSliderValue(0.0, 100.0), slider.getValue()); + Assert.assertEquals(new RangeSliderValue(0.0, 100.0), + slider.getValue()); slider.setValue(new RangeSliderValue(25.0, 75.0)); Assert.assertTrue(listenerInvoked.get()); @@ -133,7 +141,8 @@ public void labelMinMaxValueConstructor() { Assert.assertEquals(10.0, slider.getMin(), 0.0); Assert.assertEquals(50.0, slider.getMax(), 0.0); Assert.assertEquals(1.0, slider.getStep(), 0.0); - Assert.assertEquals(new RangeSliderValue(15.0, 45.0), slider.getValue()); + Assert.assertEquals(new RangeSliderValue(15.0, 45.0), + slider.getValue()); } @Test @@ -146,7 +155,8 @@ public void labelMinMaxValueListenerConstructor() { Assert.assertEquals(10.0, slider.getMin(), 0.0); Assert.assertEquals(50.0, slider.getMax(), 0.0); Assert.assertEquals(1.0, slider.getStep(), 0.0); - Assert.assertEquals(new RangeSliderValue(15.0, 45.0), slider.getValue()); + Assert.assertEquals(new RangeSliderValue(15.0, 45.0), + slider.getValue()); slider.setValue(new RangeSliderValue(20.0, 40.0)); Assert.assertTrue(listenerInvoked.get()); @@ -160,7 +170,8 @@ public void labelMinMaxStepValueConstructor() { Assert.assertEquals(10.0, slider.getMin(), 0.0); Assert.assertEquals(50.0, slider.getMax(), 0.0); Assert.assertEquals(5.0, slider.getStep(), 0.0); - Assert.assertEquals(new RangeSliderValue(15.0, 45.0), slider.getValue()); + Assert.assertEquals(new RangeSliderValue(15.0, 45.0), + slider.getValue()); } @Test @@ -173,7 +184,8 @@ public void labelMinMaxStepValueListenerConstructor() { Assert.assertEquals(10.0, slider.getMin(), 0.0); Assert.assertEquals(50.0, slider.getMax(), 0.0); Assert.assertEquals(5.0, slider.getStep(), 0.0); - Assert.assertEquals(new RangeSliderValue(15.0, 45.0), slider.getValue()); + Assert.assertEquals(new RangeSliderValue(15.0, 45.0), + slider.getValue()); slider.setValue(new RangeSliderValue(20.0, 40.0)); Assert.assertTrue(listenerInvoked.get()); From f82e8dd6d37bf8a42e595456f7a5a6abc0411f8a Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Mon, 26 Jan 2026 16:09:30 +0400 Subject: [PATCH 34/36] test: add basic integration test for RangeSlider Add RangeSliderElement testbench class and basic IT test that verifies min, max, step, and value properties are correctly set on the component. Co-Authored-By: Claude Opus 4.5 --- .../slider/tests/RangeSliderBasicPage.java | 31 ++++++++ .../slider/tests/RangeSliderBasicIT.java | 45 ++++++++++++ .../slider/testbench/RangeSliderElement.java | 72 +++++++++++++++++++ 3 files changed, 148 insertions(+) create mode 100644 vaadin-slider-flow-parent/vaadin-slider-flow-integration-tests/src/main/java/com/vaadin/flow/component/slider/tests/RangeSliderBasicPage.java create mode 100644 vaadin-slider-flow-parent/vaadin-slider-flow-integration-tests/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderBasicIT.java create mode 100644 vaadin-slider-flow-parent/vaadin-slider-testbench/src/main/java/com/vaadin/flow/component/slider/testbench/RangeSliderElement.java diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow-integration-tests/src/main/java/com/vaadin/flow/component/slider/tests/RangeSliderBasicPage.java b/vaadin-slider-flow-parent/vaadin-slider-flow-integration-tests/src/main/java/com/vaadin/flow/component/slider/tests/RangeSliderBasicPage.java new file mode 100644 index 00000000000..167d28bf100 --- /dev/null +++ b/vaadin-slider-flow-parent/vaadin-slider-flow-integration-tests/src/main/java/com/vaadin/flow/component/slider/tests/RangeSliderBasicPage.java @@ -0,0 +1,31 @@ +/* + * Copyright 2000-2026 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.flow.component.slider.tests; + +import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.component.slider.RangeSlider; +import com.vaadin.flow.component.slider.RangeSliderValue; +import com.vaadin.flow.router.Route; + +@Route("vaadin-range-slider/basic") +public class RangeSliderBasicPage extends Div { + + public RangeSliderBasicPage() { + RangeSlider rangeSlider = new RangeSlider(10, 200, 5, + new RangeSliderValue(25, 150)); + add(rangeSlider); + } +} diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow-integration-tests/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderBasicIT.java b/vaadin-slider-flow-parent/vaadin-slider-flow-integration-tests/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderBasicIT.java new file mode 100644 index 00000000000..913a11d32b3 --- /dev/null +++ b/vaadin-slider-flow-parent/vaadin-slider-flow-integration-tests/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderBasicIT.java @@ -0,0 +1,45 @@ +/* + * Copyright 2000-2026 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.flow.component.slider.tests; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.flow.component.slider.testbench.RangeSliderElement; +import com.vaadin.flow.testutil.TestPath; +import com.vaadin.tests.AbstractComponentIT; + +@TestPath("vaadin-range-slider/basic") +public class RangeSliderBasicIT extends AbstractComponentIT { + + private RangeSliderElement rangeSlider; + + @Before + public void init() { + open(); + rangeSlider = $(RangeSliderElement.class).first(); + } + + @Test + public void basicProperties() { + Assert.assertEquals(10, rangeSlider.getMin(), 0); + Assert.assertEquals(200, rangeSlider.getMax(), 0); + Assert.assertEquals(5, rangeSlider.getStep(), 0); + Assert.assertEquals(25, rangeSlider.getStartValue(), 0); + Assert.assertEquals(150, rangeSlider.getEndValue(), 0); + } +} diff --git a/vaadin-slider-flow-parent/vaadin-slider-testbench/src/main/java/com/vaadin/flow/component/slider/testbench/RangeSliderElement.java b/vaadin-slider-flow-parent/vaadin-slider-testbench/src/main/java/com/vaadin/flow/component/slider/testbench/RangeSliderElement.java new file mode 100644 index 00000000000..441eea2c1e2 --- /dev/null +++ b/vaadin-slider-flow-parent/vaadin-slider-testbench/src/main/java/com/vaadin/flow/component/slider/testbench/RangeSliderElement.java @@ -0,0 +1,72 @@ +/* + * Copyright 2000-2026 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.flow.component.slider.testbench; + +import com.vaadin.testbench.TestBenchElement; +import com.vaadin.testbench.elementsbase.Element; + +/** + * A TestBench element representing a <vaadin-range-slider> + * element. + */ +@Element("vaadin-range-slider") +public class RangeSliderElement extends TestBenchElement { + + /** + * Gets the start value of the range slider. + * + * @return the start value + */ + public double getStartValue() { + return getPropertyDouble("value", "0"); + } + + /** + * Gets the end value of the range slider. + * + * @return the end value + */ + public double getEndValue() { + return getPropertyDouble("value", "1"); + } + + /** + * Gets the minimum value of the range slider. + * + * @return the minimum value + */ + public double getMin() { + return getPropertyDouble("min"); + } + + /** + * Gets the maximum value of the range slider. + * + * @return the maximum value + */ + public double getMax() { + return getPropertyDouble("max"); + } + + /** + * Gets the step value of the range slider. + * + * @return the step value + */ + public double getStep() { + return getPropertyDouble("step"); + } +} From d2b7ec78c91f0280db56673462c4e32ce7cdfc44 Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Mon, 26 Jan 2026 17:34:01 +0400 Subject: [PATCH 35/36] refactor: use SerializableBiFunction converters in SliderBase Update SliderBase to use SerializableBiFunction for value converters, allowing subclasses to access component state during conversion. Remove unused constructor and inline init method. Update Slider to validate values during conversion and throw exceptions for invalid min/max/step changes. Remove SliderWarningsTest as warnings are replaced with exceptions. Co-Authored-By: Claude Opus 4.5 --- .../vaadin/flow/component/slider/Slider.java | 116 ++++++++-------- .../flow/component/slider/SliderBase.java | 24 +++- .../component/slider/tests/SliderTest.java | 6 +- .../slider/tests/SliderWarningsTest.java | 126 ------------------ 4 files changed, 74 insertions(+), 198 deletions(-) delete mode 100644 vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderWarningsTest.java diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java index 236bca0466b..a7301acb602 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/Slider.java @@ -17,9 +17,8 @@ import java.util.Objects; -import org.slf4j.LoggerFactory; - import com.vaadin.flow.component.Tag; +import com.vaadin.flow.function.SerializableBiFunction; /** * Slider is an input field that allows the user to select a numeric value @@ -32,10 +31,17 @@ // @NpmPackage(value = "@vaadin/slider", version = "25.1.0-alpha1") // @JsModule("@vaadin/slider/src/vaadin-slider.js") public class Slider extends SliderBase { + private static final double DEFAULT_MIN = 0; + private static final double DEFAULT_MAX = 100; + private static final double DEFAULT_STEP = 1; + + private static final SerializableBiFunction PARSER = (component, value) -> { + return component.requireValidValue(value); + }; - private final static double DEFAULT_MIN = 0; - private final static double DEFAULT_MAX = 100; - private final static double DEFAULT_STEP = 1; + private static final SerializableBiFunction FORMATTER = (component, value) -> { + return component.requireValidValue(value); + }; /** * Constructs a {@code Slider} with range 0-100 and initial value 0. @@ -110,7 +116,7 @@ public Slider(double min, double max, double value, * the initial value */ public Slider(double min, double max, double step, double value) { - super(min, max, step, value); + super(min, max, step, value, Double.class, PARSER, FORMATTER); } /** @@ -251,28 +257,6 @@ public Slider(String label, double min, double max, double step, setLabel(label); } - /** - * @throws IllegalArgumentException - * if the value is not between min and max or not aligned with - * the step value - */ - @Override - public void setValue(Double value) { - Objects.requireNonNull(value, "Value cannot be null"); - - if (value < getMin() || value > getMax()) { - throw new IllegalArgumentException( - "The value must be between min and max"); - } - - if (value % getStep() != 0) { - throw new IllegalArgumentException( - "The value is not aligned with the step value"); - } - - super.setValue(value); - } - /** * Sets the minimum value of the slider. * @@ -280,21 +264,17 @@ public void setValue(Double value) { * the minimum value * @throws IllegalArgumentException * if the min is greater than the max value + * @throws IllegalArgumentException + * if the current value is less than the new minimum value */ public void setMin(double min) { - super.setMinDouble(min); + if (getValue() < min) { + throw new IllegalArgumentException( + "The current value {} is less than the new minimum value {}".formatted( + getValue(), min)); + } - scheduleBeforeClientResponse("min", () -> { - if (getValue() < getMin()) { - LoggerFactory.getLogger(Slider.class).warn( - """ - Value {} is below the minimum of {}. \ - This may happen when the minimum was changed but the value was not updated. \ - Increase the value or decrease the minimum to avoid inconsistent UI behavior. - """, - getValue(), getMin()); - } - }); + super.setMinDouble(min); } /** @@ -313,21 +293,17 @@ public double getMin() { * the maximum value * @throws IllegalArgumentException * if the max is less than the min value + * @throws IllegalArgumentException + * if the current value is greater than the new maximum value */ public void setMax(double max) { - super.setMaxDouble(max); + if (getValue() > max) { + throw new IllegalArgumentException( + "The current value {} is greater than the new maximum value {}".formatted( + getValue(), max)); + } - scheduleBeforeClientResponse("max", () -> { - if (getValue() > getMax()) { - LoggerFactory.getLogger(Slider.class).warn( - """ - Current value {} exceeds the maximum of {}. \ - This may happen when the maximum was changed but the value was not updated. \ - Decrease the value or increase the maximum to avoid inconsistent UI behavior. - """, - getValue(), getMax()); - } - }); + super.setMaxDouble(max); } /** @@ -347,21 +323,17 @@ public double getMax() { * the step value * @throws IllegalArgumentExceptionm * if the step is less than or equal to zero + * @throws IllegalArgumentException + * if the current value is not aligned with the new step value */ public void setStep(double step) { - super.setStepDouble(step); + if (getValue() % step != 0) { + throw new IllegalArgumentException( + "The current value {} is not aligned with the new step value {}".formatted( + getValue(), step)); + } - scheduleBeforeClientResponse("step", () -> { - if (getValue() % getStep() != 0) { - LoggerFactory.getLogger(Slider.class).warn( - """ - Value {} is not aligned with the step {}. \ - This may happen when the step was changed but the value was not updated. \ - Update the value so that it aligns with the step to avoid inconsistent UI behavior. - """, - getValue(), getStep()); - } - }); + super.setStepDouble(step); } /** @@ -372,4 +344,20 @@ public void setStep(double step) { public double getStep() { return getStepDouble(); } + + private double requireValidValue(Double value) { + Objects.requireNonNull(value, "Value cannot be null"); + + if (value < getMin() || value > getMax()) { + throw new IllegalArgumentException( + "The value must be between min and max"); + } + + if (value % getStep() != 0) { + throw new IllegalArgumentException( + "The value is not aligned with the step value"); + } + + return value; + } } diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java index 889ecc7180d..c1f6892875e 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/SliderBase.java @@ -28,6 +28,7 @@ import com.vaadin.flow.component.KeyNotifier; import com.vaadin.flow.component.UI; import com.vaadin.flow.component.shared.HasValidationProperties; +import com.vaadin.flow.function.SerializableBiFunction; import com.vaadin.flow.function.SerializableRunnable; /** @@ -40,15 +41,18 @@ * * @author Vaadin Ltd */ -public abstract class SliderBase, TValue extends Number> +public abstract class SliderBase, TValue> extends AbstractSinglePropertyField implements HasLabel, HasHelper, HasValidationProperties, HasSize, - Focusable, KeyNotifier { + Focusable, KeyNotifier { private Set pendingBeforeClientResponseActions = new HashSet<>(); /** - * Constructs a slider with the given min, max, step, and initial value. + * Constructs a slider with the given min, max, step, initial value, and + * custom converters for the value property. * + * @param + * the presentation type used by the element property * @param min * the minimum value * @param max @@ -57,9 +61,19 @@ public abstract class SliderBase SliderBase(double min, double max, double step, + TValue value, Class presentationType, + SerializableBiFunction presentationToModel, + SerializableBiFunction modelToPresentation) { + super("value", null, presentationType, presentationToModel, + modelToPresentation); getElement().setProperty("manualValidation", true); diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java index b8149635a53..96d74c7cede 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderTest.java @@ -198,9 +198,9 @@ public void setMin_getMin() { Assert.assertEquals(0.0, slider.getElement().getProperty("min", 0.0), 0.0); - slider.setMin(10.0); - Assert.assertEquals(10.0, slider.getMin(), 0.0); - Assert.assertEquals(10.0, slider.getElement().getProperty("min", 0.0), + slider.setMin(-10.0); + Assert.assertEquals(-10.0, slider.getMin(), 0.0); + Assert.assertEquals(-10.0, slider.getElement().getProperty("min", 0.0), 0.0); } diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderWarningsTest.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderWarningsTest.java deleted file mode 100644 index 50655f3cf26..00000000000 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/SliderWarningsTest.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2000-2026 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.vaadin.flow.component.slider.tests; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.mockito.MockedStatic; -import org.mockito.Mockito; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.vaadin.experimental.FeatureFlags; -import com.vaadin.flow.component.slider.Slider; -import com.vaadin.flow.component.slider.SliderFeatureFlagProvider; -import com.vaadin.tests.MockUI; - -public class SliderWarningsTest { - - private Slider slider; - - private MockUI ui = new MockUI(); - private Logger logger = Mockito.mock(Logger.class); - private FeatureFlags featureFlags = Mockito.mock(FeatureFlags.class); - private MockedStatic featureFlagsStatic = Mockito - .mockStatic(FeatureFlags.class); - private MockedStatic loggerFactoryStatic = Mockito - .mockStatic(LoggerFactory.class); - - @Before - public void setup() { - featureFlagsStatic - .when(() -> FeatureFlags - .get(ui.getSession().getService().getContext())) - .thenReturn(featureFlags); - Mockito.when(featureFlags - .isEnabled(SliderFeatureFlagProvider.SLIDER_COMPONENT)) - .thenReturn(true); - loggerFactoryStatic.when(() -> LoggerFactory.getLogger(Slider.class)) - .thenReturn(logger); - - slider = new Slider(0, 100, 50); - ui.add(slider); - fakeClientCommunication(); - } - - @After - public void tearDown() { - featureFlagsStatic.close(); - loggerFactoryStatic.close(); - } - - @Test - public void setMin_valueBelowMin_warningIsShown() { - slider.setMin(60); - fakeClientCommunication(); - - Mockito.verify(logger).warn(Mockito.contains("below the minimum"), - Mockito.any(), Mockito.any()); - } - - @Test - public void setMin_valueAboveMin_noWarning() { - slider.setMin(40); - fakeClientCommunication(); - - Mockito.verify(logger, Mockito.never()).warn(Mockito.anyString(), - Mockito.any(), Mockito.any()); - } - - @Test - public void setMax_valueAboveMax_warningIsShown() { - slider.setMax(40); - fakeClientCommunication(); - - Mockito.verify(logger).warn(Mockito.contains("exceeds the maximum"), - Mockito.any(), Mockito.any()); - } - - @Test - public void setMax_valueBelowMax_noWarning() { - slider.setMax(60); - fakeClientCommunication(); - - Mockito.verify(logger, Mockito.never()).warn(Mockito.anyString(), - Mockito.any(), Mockito.any()); - } - - @Test - public void setStep_valueNotAligned_warningIsShown() { - slider.setStep(7); - fakeClientCommunication(); - - Mockito.verify(logger).warn( - Mockito.contains("not aligned with the step"), Mockito.any(), - Mockito.any()); - } - - @Test - public void setStep_valueAligned_noWarning() { - slider.setStep(10); - fakeClientCommunication(); - - Mockito.verify(logger, Mockito.never()).warn(Mockito.anyString(), - Mockito.any(), Mockito.any()); - } - - private void fakeClientCommunication() { - ui.getInternals().getStateTree().runExecutionsBeforeClientResponse(); - ui.getInternals().getStateTree().collectChanges(ignore -> { - }); - } -} From 9d9f4c09f3b1988410e4ce5969b07ab361e3bcae Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Mon, 26 Jan 2026 17:35:23 +0400 Subject: [PATCH 36/36] refactor: use SerializableBiFunction converters in RangeSlider Update RangeSlider to use SerializableBiFunction for value converters, allowing validation during conversion. Throw exceptions for invalid min/max/step changes instead of logging warnings. Remove RangeSliderWarningsTest as warnings are replaced with exceptions. Co-Authored-By: Claude Opus 4.5 --- .../flow/component/slider/RangeSlider.java | 126 ++++++++--------- .../slider/tests/RangeSliderTest.java | 6 +- .../slider/tests/RangeSliderWarningsTest.java | 128 ------------------ 3 files changed, 58 insertions(+), 202 deletions(-) delete mode 100644 vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderWarningsTest.java diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/RangeSlider.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/RangeSlider.java index daeead35d4d..40692381b68 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/RangeSlider.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/main/java/com/vaadin/flow/component/slider/RangeSlider.java @@ -18,10 +18,8 @@ import java.util.Arrays; import java.util.Objects; -import org.slf4j.LoggerFactory; - import com.vaadin.flow.component.Tag; -import com.vaadin.flow.function.SerializableFunction; +import com.vaadin.flow.function.SerializableBiFunction; import com.vaadin.flow.internal.JacksonUtils; import tools.jackson.databind.node.ArrayNode; @@ -37,21 +35,26 @@ // @NpmPackage(value = "@vaadin/slider", version = "25.1.0-alpha1") // @JsModule("@vaadin/slider/src/vaadin-range-slider.js") public class RangeSlider extends SliderBase { + private static final double DEFAULT_MIN = 0; + private static final double DEFAULT_MAX = 100; + private static final double DEFAULT_STEP = 1; + + private static final SerializableBiFunction PARSER = ( + component, arrayValue) -> { + RangeSliderValue value = new RangeSliderValue( + arrayValue.get(0).asDouble(), arrayValue.get(1).asDouble()); - private static final SerializableFunction PARSER = arrayNode -> { - return new RangeSliderValue(arrayNode.get(0).asDouble(), - arrayNode.get(1).asDouble()); + return component.requireValidValue(value); }; - private static final SerializableFunction FORMATTER = value -> { + private static final SerializableBiFunction FORMATTER = ( + component, value) -> { + component.requireValidValue(value); + return JacksonUtils .listToJson(Arrays.asList(value.start(), value.end())); }; - private final static double DEFAULT_MIN = 0; - private final static double DEFAULT_MAX = 100; - private final static double DEFAULT_STEP = 1; - /** * Constructs a {@code RangeSlider} with range 0-100 and initial value * 0-100. @@ -275,28 +278,6 @@ public RangeSlider(String label, double min, double max, double step, setLabel(label); } - /** - * @throws IllegalArgumentException - * if the value is not between min and max or not aligned with - * the step value - */ - @Override - public void setValue(RangeSliderValue value) { - Objects.requireNonNull(value, "Value cannot be null"); - - if (value.start() < getMin() || value.end() > getMax()) { - throw new IllegalArgumentException( - "The value must be between min and max"); - } - - if (value.start() % getStep() != 0 || value.end() % getStep() != 0) { - throw new IllegalArgumentException( - "The value is not aligned with the step value"); - } - - super.setValue(value); - } - /** * Sets the minimum value of the slider. * @@ -304,21 +285,17 @@ public void setValue(RangeSliderValue value) { * the minimum value * @throws IllegalArgumentException * if the min is greater than the max value + * @throws IllegalArgumentException + * if the current start value is below the new minimum value */ public void setMin(double min) { - super.setMinDouble(min); + if (getValue().start() < min) { + throw new IllegalArgumentException( + "The current start value {} is below the new minimum value {}" + .formatted(getValue().start(), min)); + } - scheduleBeforeClientResponse("min", () -> { - if (getValue().start() < getMin()) { - LoggerFactory.getLogger(RangeSlider.class).warn( - """ - Start value {} is below the minimum of {}. \ - This may happen when the minimum was changed but the value was not updated. \ - Increase the start value or decrease the minimum to avoid inconsistent UI behavior. - """, - getValue().start(), getMin()); - } - }); + super.setMinDouble(min); } /** @@ -337,21 +314,17 @@ public double getMin() { * the maximum value * @throws IllegalArgumentException * if the max is less than the min value + * @throws IllegalArgumentException + * if the current end value exceeds the new maximum value */ public void setMax(double max) { - super.setMaxDouble(max); + if (getValue().end() > max) { + throw new IllegalArgumentException( + "The current end value {} exceeds the new maximum value {}" + .formatted(getValue().end(), max)); + } - scheduleBeforeClientResponse("max", () -> { - if (getValue().end() > getMax()) { - LoggerFactory.getLogger(RangeSlider.class).warn( - """ - End value {} exceeds the maximum of {}. - This may happen when the maximum was changed but the value was not updated. \ - Decrease the end value or increase the maximum to avoid inconsistent UI behavior. - """, - getValue().end(), getMax()); - } - }); + super.setMaxDouble(max); } /** @@ -371,23 +344,18 @@ public double getMax() { * the step value * @throws IllegalArgumentException * if the step is less than or equal to zero + * @throws IllegalArgumentException + * if the current value is not aligned with the new step value */ public void setStep(double step) { - super.setStepDouble(step); + if (getValue().start() % step != 0 || getValue().end() % step != 0) { + throw new IllegalArgumentException( + "The current value [{}, {}] is not aligned with the new step value {}" + .formatted(getValue().start(), getValue().end(), + step)); + } - scheduleBeforeClientResponse("step", () -> { - RangeSliderValue value = getValue(); - if (value.start() % getStep() != 0 - || value.end() % getStep() != 0) { - LoggerFactory.getLogger(RangeSlider.class).warn( - """ - Value [{}, {}] is not aligned with the step {}. \ - This may happen when the step was changed but the value was not updated. \ - Update the value so that it aligns with the step to avoid inconsistent UI behavior. - """, - value.start(), value.end(), getStep()); - } - }); + super.setStepDouble(step); } /** @@ -398,4 +366,20 @@ public void setStep(double step) { public double getStep() { return getStepDouble(); } + + private RangeSliderValue requireValidValue(RangeSliderValue value) { + Objects.requireNonNull(value, "Value cannot be null"); + + if (value.start() < getMin() || value.end() > getMax()) { + throw new IllegalArgumentException( + "The value must be between min and max"); + } + + if (value.start() % getStep() != 0 || value.end() % getStep() != 0) { + throw new IllegalArgumentException( + "The value is not aligned with the step value"); + } + + return value; + } } diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderTest.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderTest.java index 30c8ffaef48..29ee039aaf6 100644 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderTest.java +++ b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderTest.java @@ -242,9 +242,9 @@ public void setMin_getMin() { Assert.assertEquals(0.0, slider.getElement().getProperty("min", 0.0), 0.0); - slider.setMin(10.0); - Assert.assertEquals(10.0, slider.getMin(), 0.0); - Assert.assertEquals(10.0, slider.getElement().getProperty("min", 0.0), + slider.setMin(-10.0); + Assert.assertEquals(-10.0, slider.getMin(), 0.0); + Assert.assertEquals(-10.0, slider.getElement().getProperty("min", 0.0), 0.0); } diff --git a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderWarningsTest.java b/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderWarningsTest.java deleted file mode 100644 index 10791720a27..00000000000 --- a/vaadin-slider-flow-parent/vaadin-slider-flow/src/test/java/com/vaadin/flow/component/slider/tests/RangeSliderWarningsTest.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2000-2026 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.vaadin.flow.component.slider.tests; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.mockito.MockedStatic; -import org.mockito.Mockito; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.vaadin.experimental.FeatureFlags; -import com.vaadin.flow.component.slider.RangeSlider; -import com.vaadin.flow.component.slider.RangeSliderValue; -import com.vaadin.flow.component.slider.SliderFeatureFlagProvider; -import com.vaadin.tests.MockUI; - -public class RangeSliderWarningsTest { - - private RangeSlider slider; - - private MockUI ui = new MockUI(); - private Logger logger = Mockito.mock(Logger.class); - private FeatureFlags featureFlags = Mockito.mock(FeatureFlags.class); - private MockedStatic featureFlagsStatic = Mockito - .mockStatic(FeatureFlags.class); - private MockedStatic loggerFactoryStatic = Mockito - .mockStatic(LoggerFactory.class); - - @Before - public void setup() { - featureFlagsStatic - .when(() -> FeatureFlags - .get(ui.getSession().getService().getContext())) - .thenReturn(featureFlags); - Mockito.when(featureFlags - .isEnabled(SliderFeatureFlagProvider.SLIDER_COMPONENT)) - .thenReturn(true); - loggerFactoryStatic - .when(() -> LoggerFactory.getLogger(RangeSlider.class)) - .thenReturn(logger); - - slider = new RangeSlider(0, 100, 1, new RangeSliderValue(25, 75)); - ui.add(slider); - fakeClientCommunication(); - } - - @After - public void tearDown() { - featureFlagsStatic.close(); - loggerFactoryStatic.close(); - } - - @Test - public void setMin_startValueBelowMin_warningIsShown() { - slider.setMin(30); - fakeClientCommunication(); - - Mockito.verify(logger).warn(Mockito.contains("below the minimum"), - Mockito.any(), Mockito.any()); - } - - @Test - public void setMin_startValueAboveMin_noWarning() { - slider.setMin(20); - fakeClientCommunication(); - - Mockito.verify(logger, Mockito.never()).warn(Mockito.anyString(), - Mockito.any(), Mockito.any()); - } - - @Test - public void setMax_endValueAboveMax_warningIsShown() { - slider.setMax(70); - fakeClientCommunication(); - - Mockito.verify(logger).warn(Mockito.contains("exceeds the maximum"), - Mockito.any(), Mockito.any()); - } - - @Test - public void setMax_endValueBelowMax_noWarning() { - slider.setMax(80); - fakeClientCommunication(); - - Mockito.verify(logger, Mockito.never()).warn(Mockito.anyString(), - Mockito.any(), Mockito.any()); - } - - @Test - public void setStep_valueNotAligned_warningIsShown() { - slider.setStep(7); - fakeClientCommunication(); - - Mockito.verify(logger).warn( - Mockito.contains("not aligned with the step"), Mockito.any(), - Mockito.any(), Mockito.any()); - } - - @Test - public void setStep_valueAligned_noWarning() { - slider.setStep(5); - fakeClientCommunication(); - - Mockito.verify(logger, Mockito.never()).warn(Mockito.anyString(), - Mockito.any(), Mockito.any(), Mockito.any()); - } - - private void fakeClientCommunication() { - ui.getInternals().getStateTree().runExecutionsBeforeClientResponse(); - ui.getInternals().getStateTree().collectChanges(ignore -> { - }); - } -}