You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by hn...@apache.org on 2025/11/25 19:40:20 UTC

(myfaces-tobago) branch main updated: fix(selectManyList): deselect

This is an automated email from the ASF dual-hosted git repository.

hnoeth pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git


The following commit(s) were added to refs/heads/main by this push:
     new f420217a19 fix(selectManyList): deselect
f420217a19 is described below

commit f420217a19b1c858f3732335179a102939eda9bc
Author: Henning Noeth <hn...@apache.org>
AuthorDate: Tue Nov 25 14:49:15 2025 +0100

    fix(selectManyList): deselect
    
    * add test
    * fix
    
    Issue: TOBAGO-2450
---
 .../2100-selectManyList/deselect/deselect.xhtml    | 31 ++++++++++++++
 .../src/test/typescript/selectManyList.test.ts     | 50 ++++++++++++++++++++++
 .../src/main/ts/tobago-select-many-list.ts         | 26 ++++++++++-
 3 files changed, 105 insertions(+), 2 deletions(-)

diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/2100-selectManyList/deselect/deselect.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/2100-selectManyList/deselect/deselect.xhtml
new file mode 100644
index 0000000000..b7733265e0
--- /dev/null
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/2100-selectManyList/deselect/deselect.xhtml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+-->
+
+<ui:composition template="/main.xhtml"
+                xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:tc="http://myfaces.apache.org/tobago/component"
+                xmlns:ui="jakarta.faces.facelets">
+
+  <tc:button id="reset" label="Reset" action="#{selectManyListController.resetSelectedPlanets()}"/>
+
+  <tc:selectManyList id="selectManyList" value="#{selectManyListController.selectedPlanets}">
+    <tc:selectItems value="#{selectManyListController.planets}" var="planet"
+                    itemLabel="#{planet.name}" itemValue="#{planet}" itemDisabled="#{planet.name == 'Earth'}"/>
+  </tc:selectManyList>
+</ui:composition>
diff --git a/tobago-example/tobago-example-demo/src/test/typescript/selectManyList.test.ts b/tobago-example/tobago-example-demo/src/test/typescript/selectManyList.test.ts
new file mode 100644
index 0000000000..6c6cbb767c
--- /dev/null
+++ b/tobago-example/tobago-example-demo/src/test/typescript/selectManyList.test.ts
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+import {expect, test} from "@playwright/test";
+
+test.describe("900-test/2100-selectManyList/deselect/deselect.xhtml", () => {
+
+  test.beforeEach(async ({page}) => {
+    await page.goto("http://localhost:8080/content/900-test/2100-selectManyList/deselect/deselect.xhtml");
+  });
+
+  test("tc:selectManyList: deselect", async ({page}) => {
+    const reset = page.locator("button[id='page:mainForm:reset']");
+    await reset.click();
+
+    const selectManyList = page.locator("tobago-select-many-list[id='page:mainForm:selectManyList']");
+    const venus = selectManyList.locator(".btn-group[data-tobago-value='Venus']");
+    const earth = selectManyList.locator(".btn-group[data-tobago-value='Earth']");
+    const jupiter = selectManyList.locator(".btn-group[data-tobago-value='Jupiter']");
+    await expect(venus).toBeVisible();
+    await expect(earth).toBeVisible();
+    await expect(jupiter).toBeVisible();
+
+    await venus.locator(".tobago-button").click();
+    await expect(venus).not.toBeVisible();
+    await expect(earth).toBeVisible();
+    await expect(jupiter).toBeVisible();
+
+    await jupiter.locator(".tobago-button").click();
+    await expect(venus).not.toBeVisible();
+    await expect(earth).toBeVisible();
+    await expect(jupiter).not.toBeVisible();
+  });
+});
diff --git a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-select-many-list.ts b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-select-many-list.ts
index 879e870cf6..df1999c0f8 100644
--- a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-select-many-list.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-select-many-list.ts
@@ -135,8 +135,8 @@ class SelectManyList extends SelectListBase {
     option.selected = false;
 
     const badge = this.badges.querySelector(`[data-tobago-value="${itemValue}"]`);
-    const previousBadge = badge.previousElementSibling;
-    const nextBadge = badge.nextElementSibling?.tagName === "SPAN" ? badge.nextElementSibling : null;
+    const previousBadge = this.getPreviousEnabledBadge(badge);
+    const nextBadge = this.getNextEnabledBadge(badge);
     if (previousBadge) {
       previousBadge.querySelector<HTMLButtonElement>("button.btn.badge").focus();
     } else if (nextBadge) {
@@ -162,6 +162,28 @@ class SelectManyList extends SelectListBase {
     this.hiddenSelect.dispatchEvent(new Event("change", {bubbles: true}));
   }
 
+  private getPreviousEnabledBadge(currentBadge: Element): Element {
+    const previousElementSibling = currentBadge.previousElementSibling;
+    if (previousElementSibling === null) {
+      return null;
+    } else if (previousElementSibling.querySelector<HTMLButtonElement>(".tobago-button")) {
+      return previousElementSibling;
+    } else {
+      return this.getPreviousEnabledBadge(previousElementSibling);
+    }
+  }
+
+  private getNextEnabledBadge(currentBadge: Element): Element {
+    const NextElementSibling = currentBadge.nextElementSibling;
+    if (NextElementSibling === null) {
+      return null;
+    } else if (NextElementSibling.querySelector<HTMLButtonElement>(".tobago-button")) {
+      return NextElementSibling;
+    } else {
+      return this.getNextEnabledBadge(NextElementSibling);
+    }
+  }
+
   protected leaveComponent(): void {
     if (this.focused) {
       if (!this.serverSideFiltering || (this.lastSuccessfulSearchQuery && this.lastSuccessfulSearchQuery.length > 0)) {