From 9262cff718057c90330748216b0a6d7b9883e14d Mon Sep 17 00:00:00 2001 From: Matti Tahvonen Date: Wed, 5 Nov 2025 11:33:44 +0200 Subject: [PATCH 1/3] Initial Claude coded draft that will most likely need a lot of work Let's create a new Java API for Badge component. This component is special from it's client-side implementation in a way that there is no web component for it. Technically it is just a set of style rules. But for Java developers it is a component like others. A clean Java API will help users to use it from IDE. The official documentation currently makes developers use the lower level Element API (via getElement()), use "faceless" Span component (which many Java developers don't know of) and use "magic strings" in theme list (which is also a strange concept we want to hide). Although the component is essentially just CSS rules, the implementation should have a set of tests, from which we can ensure it looks appropriate at least manually. Also elsewhere (in project structure and Java code style), try to re-use the same principles as with other components, even if the client-side implementation is bit different to most other Vaadin components. Investigate also these as references: Issue that discribes the missing feature: https://github.com/vaadin/web-components/issues/5379 Current documentation how to use it: https://vaadin.com/docs/v25/components/badge A community developed version of this component (uses CSS copied into that project, this should use core Lumo theme rules directly): https://github.com/viritin/flow-viritin/blob/v24/src/main/java/org/vaadin/firitin/components/badge/Badge.java --- pom.xml | 1 + vaadin-badge-flow-parent/pom.xml | 31 +++ .../pom.xml | 156 +++++++++++++ .../flow/component/badge/tests/BadgePage.java | 213 ++++++++++++++++++ .../flow/component/badge/tests/BadgeIT.java | 72 ++++++ .../vaadin-badge-flow/pom.xml | 103 +++++++++ .../vaadin/flow/component/badge/Badge.java | 109 +++++++++ .../flow/component/badge/BadgeVariant.java | 55 +++++ .../badge/BadgeSerializableTest.java | 21 ++ .../flow/component/badge/BadgeTest.java | 163 ++++++++++++++ .../component/badge/BadgeVariantTest.java | 36 +++ .../vaadin-badge-testbench/pom.xml | 45 ++++ .../badge/testbench/BadgeElement.java | 36 +++ 13 files changed, 1041 insertions(+) create mode 100644 vaadin-badge-flow-parent/pom.xml create mode 100644 vaadin-badge-flow-parent/vaadin-badge-flow-integration-tests/pom.xml create mode 100644 vaadin-badge-flow-parent/vaadin-badge-flow-integration-tests/src/main/java/com/vaadin/flow/component/badge/tests/BadgePage.java create mode 100644 vaadin-badge-flow-parent/vaadin-badge-flow-integration-tests/src/test/java/com/vaadin/flow/component/badge/tests/BadgeIT.java create mode 100644 vaadin-badge-flow-parent/vaadin-badge-flow/pom.xml create mode 100644 vaadin-badge-flow-parent/vaadin-badge-flow/src/main/java/com/vaadin/flow/component/badge/Badge.java create mode 100644 vaadin-badge-flow-parent/vaadin-badge-flow/src/main/java/com/vaadin/flow/component/badge/BadgeVariant.java create mode 100644 vaadin-badge-flow-parent/vaadin-badge-flow/src/test/java/com/vaadin/flow/component/badge/BadgeSerializableTest.java create mode 100644 vaadin-badge-flow-parent/vaadin-badge-flow/src/test/java/com/vaadin/flow/component/badge/BadgeTest.java create mode 100644 vaadin-badge-flow-parent/vaadin-badge-flow/src/test/java/com/vaadin/flow/component/badge/BadgeVariantTest.java create mode 100644 vaadin-badge-flow-parent/vaadin-badge-testbench/pom.xml create mode 100644 vaadin-badge-flow-parent/vaadin-badge-testbench/src/main/java/com/vaadin/flow/component/badge/testbench/BadgeElement.java diff --git a/pom.xml b/pom.xml index 9172d331517..467fc4e7368 100644 --- a/pom.xml +++ b/pom.xml @@ -20,6 +20,7 @@ vaadin-avatar-flow-parent vaadin-app-layout-flow-parent vaadin-aura-theme-flow-parent + vaadin-badge-flow-parent vaadin-button-flow-parent vaadin-card-flow-parent vaadin-checkbox-flow-parent diff --git a/vaadin-badge-flow-parent/pom.xml b/vaadin-badge-flow-parent/pom.xml new file mode 100644 index 00000000000..b8cbe4c4b55 --- /dev/null +++ b/vaadin-badge-flow-parent/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + + com.vaadin + vaadin-flow-components + 25.0-SNAPSHOT + + vaadin-badge-flow-parent + pom + Vaadin Badge Parent + Vaadin Badge Parent + + vaadin-badge-flow + vaadin-badge-testbench + + + + + default + + + !release + + + + vaadin-badge-flow-integration-tests + + + + diff --git a/vaadin-badge-flow-parent/vaadin-badge-flow-integration-tests/pom.xml b/vaadin-badge-flow-parent/vaadin-badge-flow-integration-tests/pom.xml new file mode 100644 index 00000000000..12915d42967 --- /dev/null +++ b/vaadin-badge-flow-parent/vaadin-badge-flow-integration-tests/pom.xml @@ -0,0 +1,156 @@ + + + 4.0.0 + + com.vaadin + vaadin-badge-flow-parent + 25.0-SNAPSHOT + + vaadin-badge-integration-tests + war + Vaadin Badge Integration Tests + Vaadin Badge Integration Tests + + + com.vaadin + flow-client + + + com.vaadin + flow-data + + + com.vaadin + flow-html-components + + + com.vaadin + flow-test-util + test + + + com.vaadin + vaadin-badge-flow + ${project.version} + + + com.vaadin + vaadin-badge-testbench + ${project.version} + test + + + com.vaadin + vaadin-dev-server + + + com.vaadin + vaadin-flow-components-test-util + ${project.version} + test + + + com.vaadin + vaadin-lumo-theme + + + 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-badge-flow-parent/vaadin-badge-flow-integration-tests/src/main/java/com/vaadin/flow/component/badge/tests/BadgePage.java b/vaadin-badge-flow-parent/vaadin-badge-flow-integration-tests/src/main/java/com/vaadin/flow/component/badge/tests/BadgePage.java new file mode 100644 index 00000000000..3fc8fc9cc7b --- /dev/null +++ b/vaadin-badge-flow-parent/vaadin-badge-flow-integration-tests/src/main/java/com/vaadin/flow/component/badge/tests/BadgePage.java @@ -0,0 +1,213 @@ +/* + * Copyright 2000-2025 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.badge.tests; + +import com.vaadin.flow.component.badge.Badge; +import com.vaadin.flow.component.badge.BadgeVariant; +import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.component.html.H3; +import com.vaadin.flow.component.html.Hr; +import com.vaadin.flow.component.html.Span; +import com.vaadin.flow.router.Route; + +/** + * Comprehensive visual test page for the Badge component showing all variants, + * sizes, colors, and use cases. + */ +@Route("vaadin-badge") +public class BadgePage extends Div { + + public BadgePage() { + setWidthFull(); + getStyle().set("padding", "20px"); + + add(new H3("Badge Component - Visual Test Page")); + + // Basic badges + add(new H3("Basic Badges")); + Div basicSection = createSection(); + basicSection.add(new Badge("Default")); + basicSection.add(new Badge("New")); + basicSection.add(new Badge("Beta")); + basicSection.add(new Badge("123")); + add(basicSection); + + add(new Hr()); + + // Color variants + add(new H3("Color Variants")); + Div colorSection = createSection(); + + Badge primary = new Badge("Primary"); + primary.addThemeVariants(BadgeVariant.LUMO_PRIMARY); + primary.setId("badge-primary"); + colorSection.add(primary); + + Badge success = new Badge("Success"); + success.addThemeVariants(BadgeVariant.LUMO_SUCCESS); + success.setId("badge-success"); + colorSection.add(success); + + Badge warning = new Badge("Warning"); + warning.addThemeVariants(BadgeVariant.LUMO_WARNING); + warning.setId("badge-warning"); + colorSection.add(warning); + + Badge error = new Badge("Error"); + error.addThemeVariants(BadgeVariant.LUMO_ERROR); + error.setId("badge-error"); + colorSection.add(error); + + Badge contrast = new Badge("Contrast"); + contrast.addThemeVariants(BadgeVariant.LUMO_CONTRAST); + contrast.setId("badge-contrast"); + colorSection.add(contrast); + + add(colorSection); + + add(new Hr()); + + // Small size variant + add(new H3("Small Size")); + Div smallSection = createSection(); + + Badge smallDefault = new Badge("Small"); + smallDefault.addThemeVariants(BadgeVariant.LUMO_SMALL); + smallDefault.setId("badge-small"); + smallSection.add(smallDefault); + + Badge smallPrimary = new Badge("Small Primary"); + smallPrimary.addThemeVariants(BadgeVariant.LUMO_SMALL, + BadgeVariant.LUMO_PRIMARY); + smallSection.add(smallPrimary); + + Badge smallSuccess = new Badge("Small Success"); + smallSuccess.addThemeVariants(BadgeVariant.LUMO_SMALL, + BadgeVariant.LUMO_SUCCESS); + smallSection.add(smallSuccess); + + add(smallSection); + + add(new Hr()); + + // Pill shape variant + add(new H3("Pill Shape")); + Div pillSection = createSection(); + + Badge pill = new Badge("Pill"); + pill.addThemeVariants(BadgeVariant.LUMO_PILL); + pill.setId("badge-pill"); + pillSection.add(pill); + + Badge pillPrimary = new Badge("99+"); + pillPrimary.addThemeVariants(BadgeVariant.LUMO_PILL, + BadgeVariant.LUMO_PRIMARY); + pillSection.add(pillPrimary); + + Badge pillSmall = new Badge("3"); + pillSmall.addThemeVariants(BadgeVariant.LUMO_PILL, + BadgeVariant.LUMO_SMALL, BadgeVariant.LUMO_ERROR); + pillSection.add(pillSmall); + + add(pillSection); + + add(new Hr()); + + // Badges with components + add(new H3("Badges with Components")); + Div componentSection = createSection(); + + Badge withSpan = new Badge(); + withSpan.add(new Span("Status: "), new Span("Active")); + withSpan.addThemeVariants(BadgeVariant.LUMO_SUCCESS); + withSpan.setId("badge-with-components"); + componentSection.add(withSpan); + + Badge multiComponent = new Badge(); + multiComponent.add(new Span("Count: "), new Span("42")); + multiComponent.addThemeVariants(BadgeVariant.LUMO_PRIMARY); + componentSection.add(multiComponent); + + add(componentSection); + + add(new Hr()); + + // Accessibility examples + add(new H3("Accessibility Features")); + Div accessibilitySection = createSection(); + + Badge withAriaLabel = new Badge("!"); + withAriaLabel.setAriaLabel("Important notification"); + withAriaLabel.addThemeVariants(BadgeVariant.LUMO_ERROR, + BadgeVariant.LUMO_PILL); + withAriaLabel.setId("badge-aria-label"); + accessibilitySection.add(withAriaLabel); + + Badge withTooltip = new Badge("?"); + withTooltip.setTooltipText("This is a help badge"); + withTooltip.addThemeVariants(BadgeVariant.LUMO_CONTRAST, + BadgeVariant.LUMO_PILL); + withTooltip.setId("badge-tooltip"); + accessibilitySection.add(withTooltip); + + add(accessibilitySection); + + add(new Hr()); + + // Real-world use cases + add(new H3("Real-world Use Cases")); + Div useCasesSection = createSection(); + + // Notification badge + Span bellIcon = new Span("🔔"); + Badge notificationBadge = new Badge("5"); + notificationBadge.addThemeVariants(BadgeVariant.LUMO_ERROR, + BadgeVariant.LUMO_SMALL, BadgeVariant.LUMO_PILL); + Div notificationExample = new Div(bellIcon, notificationBadge); + notificationExample.getStyle().set("display", "inline-flex") + .set("align-items", "center").set("gap", "5px"); + useCasesSection.add(notificationExample); + + // Status badge + Badge statusBadge = new Badge("Online"); + statusBadge.addThemeVariants(BadgeVariant.LUMO_SUCCESS, + BadgeVariant.LUMO_SMALL); + useCasesSection.add(statusBadge); + + // Count badge + Badge countBadge = new Badge("NEW"); + countBadge.addThemeVariants(BadgeVariant.LUMO_PRIMARY); + useCasesSection.add(countBadge); + + add(useCasesSection); + + add(new Hr()); + + // Empty badge (for dynamic content) + add(new H3("Empty Badge (Dynamic Content)")); + Badge emptyBadge = new Badge(); + emptyBadge.setId("empty-badge"); + add(emptyBadge); + } + + private Div createSection() { + Div section = new Div(); + section.getStyle().set("display", "flex").set("gap", "10px") + .set("flex-wrap", "wrap").set("align-items", "center") + .set("padding", "10px 0"); + return section; + } +} diff --git a/vaadin-badge-flow-parent/vaadin-badge-flow-integration-tests/src/test/java/com/vaadin/flow/component/badge/tests/BadgeIT.java b/vaadin-badge-flow-parent/vaadin-badge-flow-integration-tests/src/test/java/com/vaadin/flow/component/badge/tests/BadgeIT.java new file mode 100644 index 00000000000..1ecdf0718da --- /dev/null +++ b/vaadin-badge-flow-parent/vaadin-badge-flow-integration-tests/src/test/java/com/vaadin/flow/component/badge/tests/BadgeIT.java @@ -0,0 +1,72 @@ +/* + * Copyright 2000-2025 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.badge.tests; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.flow.component.badge.testbench.BadgeElement; +import com.vaadin.flow.testutil.TestPath; +import com.vaadin.tests.AbstractComponentIT; + +@TestPath("vaadin-badge") +public class BadgeIT extends AbstractComponentIT { + + private BadgeElement badge; + + @Before + public void init() { + open(); + badge = $(BadgeElement.class).id("badge-primary"); + } + + @Test + public void rendersBadgeComponent() { + boolean hasShadowRoot = (Boolean) executeScript( + "return arguments[0].shadowRoot !== null", badge); + String componentName = (String) executeScript( + "return Object.getPrototypeOf(arguments[0]).constructor.is", + badge); + + Assert.assertTrue(hasShadowRoot); + Assert.assertEquals("vaadin-badge", componentName); + Assert.assertNotNull(badge.getText()); + Assert.assertFalse(badge.getText().isEmpty()); + } + + @Test + public void badgeHasThemeVariants() { + BadgeElement successBadge = $(BadgeElement.class) + .id("badge-success"); + String theme = successBadge.getAttribute("theme"); + Assert.assertTrue(theme.contains("success")); + } + + @Test + public void badgeCanBeSmall() { + BadgeElement smallBadge = $(BadgeElement.class).id("badge-small"); + String theme = smallBadge.getAttribute("theme"); + Assert.assertTrue(theme.contains("small")); + } + + @Test + public void badgeCanBePill() { + BadgeElement pillBadge = $(BadgeElement.class).id("badge-pill"); + String theme = pillBadge.getAttribute("theme"); + Assert.assertTrue(theme.contains("pill")); + } +} diff --git a/vaadin-badge-flow-parent/vaadin-badge-flow/pom.xml b/vaadin-badge-flow-parent/vaadin-badge-flow/pom.xml new file mode 100644 index 00000000000..880d811f2ed --- /dev/null +++ b/vaadin-badge-flow-parent/vaadin-badge-flow/pom.xml @@ -0,0 +1,103 @@ + + + 4.0.0 + + com.vaadin + vaadin-badge-flow-parent + 25.0-SNAPSHOT + + vaadin-badge-flow + jar + Vaadin Badge + Vaadin Badge + + + com.vaadin + flow-html-components + provided + + + com.vaadin + flow-test-generic + test + + + com.vaadin + flow-test-util + 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.mockito + mockito-core + test + + + 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-badge-flow-parent/vaadin-badge-flow/src/main/java/com/vaadin/flow/component/badge/Badge.java b/vaadin-badge-flow-parent/vaadin-badge-flow/src/main/java/com/vaadin/flow/component/badge/Badge.java new file mode 100644 index 00000000000..763b8f4eca2 --- /dev/null +++ b/vaadin-badge-flow-parent/vaadin-badge-flow/src/main/java/com/vaadin/flow/component/badge/Badge.java @@ -0,0 +1,109 @@ +/* + * Copyright 2000-2025 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.badge; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.HasAriaLabel; +import com.vaadin.flow.component.HasComponents; +import com.vaadin.flow.component.HasSize; +import com.vaadin.flow.component.HasStyle; +import com.vaadin.flow.component.HasText; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.component.dependency.JsModule; +import com.vaadin.flow.component.dependency.NpmPackage; +import com.vaadin.flow.component.shared.HasThemeVariant; +import com.vaadin.flow.component.shared.HasTooltip; + +/** + * Badge is a colored text element for labeling content, displaying metadata, + * or highlighting information. + *

+ * Badges are typically used to show statuses, categories, or counts. They + * support text content, icons, and various theme variants for different colors + * and sizes. + *

+ * + *

Usage Examples

+ * + *
+ * {@code
+ * // Simple badge with text
+ * Badge badge = new Badge("New");
+ *
+ * // Badge with theme variants
+ * Badge successBadge = new Badge("Success");
+ * successBadge.addThemeVariants(BadgeVariant.LUMO_SUCCESS);
+ *
+ * // Small pill-shaped badge
+ * Badge pillBadge = new Badge("3");
+ * pillBadge.addThemeVariants(BadgeVariant.LUMO_SMALL, BadgeVariant.LUMO_PILL);
+ *
+ * // Badge with icon
+ * Badge iconBadge = new Badge();
+ * iconBadge.add(new Icon(VaadinIcon.CHECK), new Span("Verified"));
+ * }
+ * 
+ * + *

Theme Variants

+ *

+ * The component supports several theme variants for styling: + *

+ * + * + *

Accessibility

+ *

+ * When using icon-only badges or when the badge's visual appearance alone + * doesn't convey the full meaning, use {@link #setAriaLabel(String)} to + * provide a text alternative for screen readers. Additionally, consider using + * {@link #setTooltipText(String)} to provide helpful information to all users. + *

+ * + * @author Vaadin Ltd + */ +@Tag("vaadin-badge") +@NpmPackage(value = "@vaadin/badge", version = "25.0.0-beta3") +@JsModule("@vaadin/badge/src/vaadin-badge.js") +public class Badge extends Component implements HasText, HasSize, HasStyle, + HasComponents, HasThemeVariant, HasTooltip, HasAriaLabel { + + /** + * Creates an empty badge. + *

+ * Use {@link #setText(String)} to set the badge text or {@link #add(Component...)} + * to add icons or other components. + *

+ */ + public Badge() { + } + + /** + * Creates a badge with the specified text. + * + * @param text + * the text content of the badge + */ + public Badge(String text) { + setText(text); + } +} diff --git a/vaadin-badge-flow-parent/vaadin-badge-flow/src/main/java/com/vaadin/flow/component/badge/BadgeVariant.java b/vaadin-badge-flow-parent/vaadin-badge-flow/src/main/java/com/vaadin/flow/component/badge/BadgeVariant.java new file mode 100644 index 00000000000..c8f3c0f6d62 --- /dev/null +++ b/vaadin-badge-flow-parent/vaadin-badge-flow/src/main/java/com/vaadin/flow/component/badge/BadgeVariant.java @@ -0,0 +1,55 @@ +/* + * Copyright 2000-2025 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.badge; + +import com.vaadin.flow.component.shared.ThemeVariant; + +/** + * Set of theme variants applicable for {@code vaadin-badge} component. + */ +public enum BadgeVariant implements ThemeVariant { + //@formatter:off + LUMO_PRIMARY("primary"), + LUMO_SUCCESS("success"), + LUMO_WARNING("warning"), + LUMO_ERROR("error"), + LUMO_CONTRAST("contrast"), + LUMO_SMALL("small"), + LUMO_PILL("pill"), + AURA_PRIMARY("primary"), + AURA_SUCCESS("success"), + AURA_WARNING("warning"), + AURA_ERROR("error"), + AURA_CONTRAST("contrast"), + AURA_SMALL("small"), + AURA_PILL("pill"); + //@formatter:on + + private final String variant; + + BadgeVariant(String variant) { + this.variant = variant; + } + + /** + * Gets the variant name. + * + * @return variant name + */ + public String getVariantName() { + return variant; + } +} diff --git a/vaadin-badge-flow-parent/vaadin-badge-flow/src/test/java/com/vaadin/flow/component/badge/BadgeSerializableTest.java b/vaadin-badge-flow-parent/vaadin-badge-flow/src/test/java/com/vaadin/flow/component/badge/BadgeSerializableTest.java new file mode 100644 index 00000000000..cccad5f0700 --- /dev/null +++ b/vaadin-badge-flow-parent/vaadin-badge-flow/src/test/java/com/vaadin/flow/component/badge/BadgeSerializableTest.java @@ -0,0 +1,21 @@ +/* + * Copyright 2000-2025 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.badge; + +import com.vaadin.flow.testutil.ClassesSerializableTest; + +public class BadgeSerializableTest extends ClassesSerializableTest { +} diff --git a/vaadin-badge-flow-parent/vaadin-badge-flow/src/test/java/com/vaadin/flow/component/badge/BadgeTest.java b/vaadin-badge-flow-parent/vaadin-badge-flow/src/test/java/com/vaadin/flow/component/badge/BadgeTest.java new file mode 100644 index 00000000000..1efcc7ed1a7 --- /dev/null +++ b/vaadin-badge-flow-parent/vaadin-badge-flow/src/test/java/com/vaadin/flow/component/badge/BadgeTest.java @@ -0,0 +1,163 @@ +/* + * Copyright 2000-2025 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.badge; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.flow.component.UI; +import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.component.html.Span; + +/** + * Unit tests for the {@link Badge} component. + */ +public class BadgeTest { + + private Badge badge; + + @Before + public void setup() { + var ui = new UI(); + UI.setCurrent(ui); + badge = new Badge(); + ui.add(badge); + } + + @After + public void tearDown() { + UI.setCurrent(null); + } + + @Test + public void defaultConstructor_textIsEmpty() { + Assert.assertEquals("", badge.getText()); + } + + @Test + public void constructorWithText_textIsSet() { + var badgeWithText = new Badge("New"); + Assert.assertEquals("New", badgeWithText.getText()); + } + + @Test + public void setText_textIsUpdated() { + badge.setText("Updated"); + Assert.assertEquals("Updated", badge.getText()); + } + + @Test + public void setTextNull_textIsEmpty() { + badge.setText("Text"); + badge.setText(null); + Assert.assertEquals("", badge.getText()); + } + + @Test + public void addComponent_componentIsAdded() { + var span = new Span("Content"); + badge.add(span); + Assert.assertTrue(badge.getChildren().anyMatch(c -> c.equals(span))); + Assert.assertTrue(span.isAttached()); + } + + @Test + public void addMultipleComponents_allComponentsAreAdded() { + var span1 = new Span("First"); + var span2 = new Span("Second"); + badge.add(span1, span2); + Assert.assertEquals(2, badge.getChildren().count()); + Assert.assertTrue(badge.getChildren().anyMatch(c -> c.equals(span1))); + Assert.assertTrue(badge.getChildren().anyMatch(c -> c.equals(span2))); + } + + @Test + public void removeComponent_componentIsRemoved() { + var span = new Span("Content"); + badge.add(span); + badge.remove(span); + Assert.assertFalse(badge.getChildren().anyMatch(c -> c.equals(span))); + Assert.assertFalse(span.isAttached()); + } + + @Test + public void removeAll_allComponentsAreRemoved() { + badge.add(new Span("First"), new Span("Second")); + badge.removeAll(); + Assert.assertEquals(0, badge.getChildren().count()); + } + + @Test + public void addThemeVariant_variantIsAdded() { + badge.addThemeVariants(BadgeVariant.LUMO_SUCCESS); + Assert.assertTrue(badge.getThemeNames().contains("success")); + } + + @Test + public void addMultipleThemeVariants_allVariantsAreAdded() { + badge.addThemeVariants(BadgeVariant.LUMO_SUCCESS, + BadgeVariant.LUMO_SMALL); + Assert.assertTrue(badge.getThemeNames().contains("success")); + Assert.assertTrue(badge.getThemeNames().contains("small")); + } + + @Test + public void removeThemeVariant_variantIsRemoved() { + badge.addThemeVariants(BadgeVariant.LUMO_SUCCESS); + badge.removeThemeVariants(BadgeVariant.LUMO_SUCCESS); + Assert.assertFalse(badge.getThemeNames().contains("success")); + } + + @Test + public void setAriaLabel_ariaLabelIsSet() { + badge.setAriaLabel("Status badge"); + Assert.assertEquals("Status badge", badge.getAriaLabel().orElse(null)); + } + + @Test + public void setAriaLabelNull_ariaLabelIsRemoved() { + badge.setAriaLabel("Status badge"); + badge.setAriaLabel(null); + Assert.assertTrue(badge.getAriaLabel().isEmpty()); + } + + @Test + public void setTooltipText_tooltipIsSet() { + badge.setTooltipText("This is a badge"); + // Tooltip is set, we can't easily test the content but we can verify no exception + Assert.assertNotNull(badge); + } + + @Test + public void setWidth_widthIsSet() { + badge.setWidth("100px"); + Assert.assertEquals("100px", badge.getWidth()); + } + + @Test + public void setHeight_heightIsSet() { + badge.setHeight("50px"); + Assert.assertEquals("50px", badge.getHeight()); + } + + @Test + public void addClassName_classNameIsAdded() { + badge.addClassName("custom-badge"); + Assert.assertTrue(badge.getClassNames().contains("custom-badge")); + } +} diff --git a/vaadin-badge-flow-parent/vaadin-badge-flow/src/test/java/com/vaadin/flow/component/badge/BadgeVariantTest.java b/vaadin-badge-flow-parent/vaadin-badge-flow/src/test/java/com/vaadin/flow/component/badge/BadgeVariantTest.java new file mode 100644 index 00000000000..c1b1e825d5e --- /dev/null +++ b/vaadin-badge-flow-parent/vaadin-badge-flow/src/test/java/com/vaadin/flow/component/badge/BadgeVariantTest.java @@ -0,0 +1,36 @@ +/* + * Copyright 2000-2025 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.badge; + +import org.junit.Test; + +import com.vaadin.tests.ThemeVariantTestHelper; + +public class BadgeVariantTest { + + @Test + public void addThemeVariant_themeNamesContainsThemeVariant() { + ThemeVariantTestHelper.addThemeVariant_themeNamesContainsThemeVariant( + new Badge(), BadgeVariant.LUMO_SUCCESS); + } + + @Test + public void addThemeVariant_removeThemeVariant_themeNamesDoesNotContainThemeVariant() { + ThemeVariantTestHelper + .addThemeVariant_removeThemeVariant_themeNamesDoesNotContainThemeVariant( + new Badge(), BadgeVariant.LUMO_SUCCESS); + } +} diff --git a/vaadin-badge-flow-parent/vaadin-badge-testbench/pom.xml b/vaadin-badge-flow-parent/vaadin-badge-testbench/pom.xml new file mode 100644 index 00000000000..d13978060fd --- /dev/null +++ b/vaadin-badge-flow-parent/vaadin-badge-testbench/pom.xml @@ -0,0 +1,45 @@ + + + 4.0.0 + + com.vaadin + vaadin-badge-flow-parent + 25.0-SNAPSHOT + + vaadin-badge-testbench + jar + Vaadin Badge Testbench API + Vaadin Badge 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-badge-flow-parent/vaadin-badge-testbench/src/main/java/com/vaadin/flow/component/badge/testbench/BadgeElement.java b/vaadin-badge-flow-parent/vaadin-badge-testbench/src/main/java/com/vaadin/flow/component/badge/testbench/BadgeElement.java new file mode 100644 index 00000000000..46216d4f8f4 --- /dev/null +++ b/vaadin-badge-flow-parent/vaadin-badge-testbench/src/main/java/com/vaadin/flow/component/badge/testbench/BadgeElement.java @@ -0,0 +1,36 @@ +/* + * Copyright 2000-2025 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.badge.testbench; + +import com.vaadin.testbench.TestBenchElement; +import com.vaadin.testbench.elementsbase.Element; + +/** + * A TestBench element representing a <vaadin-badge> + * element. + */ +@Element("vaadin-badge") +public class BadgeElement extends TestBenchElement { + + /** + * Gets the text content of the badge. + * + * @return the badge text + */ + public String getText() { + return getPropertyString("textContent"); + } +} From d3aeb197842d68edc32d600dd91ae5927d65e49e Mon Sep 17 00:00:00 2001 From: Matti Tahvonen Date: Wed, 5 Nov 2025 11:34:37 +0200 Subject: [PATCH 2/3] Because obviously Kalle doesn't listen to your instructions Now compiles and it project shows something meaningful --- .../vaadin/flow/component/badge/tests/BadgePage.java | 12 ++++++++++++ .../java/com/vaadin/flow/component/badge/Badge.java | 8 +++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/vaadin-badge-flow-parent/vaadin-badge-flow-integration-tests/src/main/java/com/vaadin/flow/component/badge/tests/BadgePage.java b/vaadin-badge-flow-parent/vaadin-badge-flow-integration-tests/src/main/java/com/vaadin/flow/component/badge/tests/BadgePage.java index 3fc8fc9cc7b..9ac327fd620 100644 --- a/vaadin-badge-flow-parent/vaadin-badge-flow-integration-tests/src/main/java/com/vaadin/flow/component/badge/tests/BadgePage.java +++ b/vaadin-badge-flow-parent/vaadin-badge-flow-integration-tests/src/main/java/com/vaadin/flow/component/badge/tests/BadgePage.java @@ -17,17 +17,22 @@ import com.vaadin.flow.component.badge.Badge; import com.vaadin.flow.component.badge.BadgeVariant; +import com.vaadin.flow.component.dependency.StyleSheet; import com.vaadin.flow.component.html.Div; import com.vaadin.flow.component.html.H3; import com.vaadin.flow.component.html.Hr; import com.vaadin.flow.component.html.Span; +import com.vaadin.flow.component.icon.VaadinIcon; import com.vaadin.flow.router.Route; +import com.vaadin.flow.theme.lumo.Lumo; /** * Comprehensive visual test page for the Badge component showing all variants, * sizes, colors, and use cases. */ @Route("vaadin-badge") +// Currently only Lumo is supported +@StyleSheet(Lumo.STYLESHEET) public class BadgePage extends Div { public BadgePage() { @@ -36,6 +41,13 @@ public BadgePage() { add(new H3("Badge Component - Visual Test Page")); + + Span rawOldStyleSpanBadge = new Span(new Span("Pending")); + rawOldStyleSpanBadge.getElement().getThemeList().add("badge"); + + add(rawOldStyleSpanBadge); + + // Basic badges add(new H3("Basic Badges")); Div basicSection = createSection(); diff --git a/vaadin-badge-flow-parent/vaadin-badge-flow/src/main/java/com/vaadin/flow/component/badge/Badge.java b/vaadin-badge-flow-parent/vaadin-badge-flow/src/main/java/com/vaadin/flow/component/badge/Badge.java index 763b8f4eca2..80b594e8870 100644 --- a/vaadin-badge-flow-parent/vaadin-badge-flow/src/main/java/com/vaadin/flow/component/badge/Badge.java +++ b/vaadin-badge-flow-parent/vaadin-badge-flow/src/main/java/com/vaadin/flow/component/badge/Badge.java @@ -22,14 +22,12 @@ import com.vaadin.flow.component.HasStyle; import com.vaadin.flow.component.HasText; import com.vaadin.flow.component.Tag; -import com.vaadin.flow.component.dependency.JsModule; -import com.vaadin.flow.component.dependency.NpmPackage; import com.vaadin.flow.component.shared.HasThemeVariant; import com.vaadin.flow.component.shared.HasTooltip; /** * Badge is a colored text element for labeling content, displaying metadata, - * or highlighting information. + * or highlighting information. Note, currently only Lumo theme supports badges. *

* Badges are typically used to show statuses, categories, or counts. They * support text content, icons, and various theme variants for different colors @@ -82,8 +80,6 @@ * @author Vaadin Ltd */ @Tag("vaadin-badge") -@NpmPackage(value = "@vaadin/badge", version = "25.0.0-beta3") -@JsModule("@vaadin/badge/src/vaadin-badge.js") public class Badge extends Component implements HasText, HasSize, HasStyle, HasComponents, HasThemeVariant, HasTooltip, HasAriaLabel { @@ -95,6 +91,7 @@ public class Badge extends Component implements HasText, HasSize, HasStyle, *

*/ public Badge() { + getElement().getThemeList().add("badge"); } /** @@ -104,6 +101,7 @@ public Badge() { * the text content of the badge */ public Badge(String text) { + this(); setText(text); } } From 39554f17a06fd54d886130d1647d8d44002a94d0 Mon Sep 17 00:00:00 2001 From: Matti Tahvonen Date: Wed, 5 Nov 2025 11:40:34 +0200 Subject: [PATCH 3/3] fix ITs --- .../java/com/vaadin/flow/component/badge/tests/BadgeIT.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vaadin-badge-flow-parent/vaadin-badge-flow-integration-tests/src/test/java/com/vaadin/flow/component/badge/tests/BadgeIT.java b/vaadin-badge-flow-parent/vaadin-badge-flow-integration-tests/src/test/java/com/vaadin/flow/component/badge/tests/BadgeIT.java index 1ecdf0718da..fc065112ff3 100644 --- a/vaadin-badge-flow-parent/vaadin-badge-flow-integration-tests/src/test/java/com/vaadin/flow/component/badge/tests/BadgeIT.java +++ b/vaadin-badge-flow-parent/vaadin-badge-flow-integration-tests/src/test/java/com/vaadin/flow/component/badge/tests/BadgeIT.java @@ -39,11 +39,11 @@ public void rendersBadgeComponent() { boolean hasShadowRoot = (Boolean) executeScript( "return arguments[0].shadowRoot !== null", badge); String componentName = (String) executeScript( - "return Object.getPrototypeOf(arguments[0]).constructor.is", + "return arguments[0].tagName", badge); - Assert.assertTrue(hasShadowRoot); - Assert.assertEquals("vaadin-badge", componentName); + Assert.assertFalse(hasShadowRoot); // there should be no + Assert.assertEquals("VAADIN-BADGE", componentName); Assert.assertNotNull(badge.getText()); Assert.assertFalse(badge.getText().isEmpty()); }