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 2026/02/04 10:26:17 UTC
(myfaces-tobago) branch main updated: fix(columnSelector): immediate in composite component
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 e37e6da51e fix(columnSelector): immediate in composite component
e37e6da51e is described below
commit e37e6da51e23cfeea868bb8c8d9f2777d2d24c53
Author: Henning Noeth <hn...@apache.org>
AuthorDate: Mon Jan 26 15:02:39 2026 +0100
fix(columnSelector): immediate in composite component
Test for columnSelector immediate=true in a composite component with a facet.
State is now written in time.
Issue: TOBAGO-2465
---
.../tobago/internal/component/AbstractUISheet.java | 4 -
.../internal/renderkit/renderer/SheetRenderer.java | 3 +
.../immediate/immediate_compositeComponent.xhtml | 45 +++++
.../demo-components/columnSelectorImmediate.xhtml | 68 ++++++++
.../src/test/typescript/columnSelector.test.ts | 184 +++++++++++++++++++++
5 files changed, 300 insertions(+), 4 deletions(-)
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISheet.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISheet.java
index 956457a70e..75b1cad529 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISheet.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISheet.java
@@ -554,10 +554,6 @@ public void broadcast(final FacesEvent facesEvent) throws AbortProcessingExcepti
getSheetState(getFacesContext()).updateSortState(((SortActionEvent) facesEvent).getColumn().getId());
sort(getFacesContext(), (SortActionEvent) facesEvent);
} else if (facesEvent instanceof SheetRowSelectionChangeEvent sheetRowSelectionChangeEvent) {
- final SheetState sheetState = getSheetState(sheetRowSelectionChangeEvent.getFacesContext());
- if (sheetState != null) {
- sheetState.setSelectedRows(sheetRowSelectionChangeEvent.getNewSelectedRows());
- }
if (getClientId().equals(sheetRowSelectionChangeEvent.getSourceId())) {
final ActionListener defaultActionListener = getFacesContext().getApplication().getActionListener();
if (defaultActionListener != null) {
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SheetRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SheetRenderer.java
index 928d742c33..0cb38ed82c 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SheetRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SheetRenderer.java
@@ -168,6 +168,9 @@ public void decodeInternal(final FacesContext facesContext, final T component) {
final String sourceId = requestParameterMap.get(ClientBehaviorContext.BEHAVIOR_SOURCE_PARAM_NAME);
List<Integer> oldSelectedRows = component.getSheetState(facesContext).getSelectedRows();
if (!selectedRows.equals(oldSelectedRows)) {
+ if (state != null) {
+ state.setSelectedRows(selectedRows);
+ }
component.queueEvent(new SheetRowSelectionChangeEvent(component, sourceId, oldSelectedRows, selectedRows));
}
} else {
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/sheet/columnSelector/immediate/immediate_compositeComponent.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/sheet/columnSelector/immediate/immediate_compositeComponent.xhtml
new file mode 100644
index 0000000000..6b7ebf8e29
--- /dev/null
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/sheet/columnSelector/immediate/immediate_compositeComponent.xhtml
@@ -0,0 +1,45 @@
+<?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:f="jakarta.faces.core"
+ xmlns:dc="jakarta.faces.composite/demo-components"
+ xmlns:tc="http://myfaces.apache.org/tobago/component"
+ xmlns:ui="jakarta.faces.facelets">
+ <dc:columnSelectorImmediate id="compComp" columnSelectorAjaxController="#{columnSelectorAjaxController}">
+ <f:facet name="buttons">
+ <tc:out id="selectedCount" label="Selected count"
+ value="#{columnSelectorAjaxController.sheetState.selectedRows.size()}"/>
+ <tc:button id="submit" label="Submit"/>
+
+ <tc:separator/>
+ <tc:button id="saveSelectedRowsAjax" label="Save selected rows (Ajax)" immediate="true"
+ action="#{columnSelectorAjaxController.saveSelectedRows()}">
+ <f:ajax execute="sheet" render="sheet selectedCount selectedCountByAction"/>
+ </tc:button>
+ <tc:button id="saveSelectedRowsSubmit" label="Save selected rows (Submit)" immediate="true"
+ action="#{columnSelectorAjaxController.saveSelectedRows()}"/>
+ <tc:out id="selectedCountByAction" label="Selected count by action"
+ value="#{columnSelectorAjaxController.savedSelectedRows}"/>
+ </f:facet>
+ </dc:columnSelectorImmediate>
+</ui:composition>
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/resources/demo-components/columnSelectorImmediate.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/resources/demo-components/columnSelectorImmediate.xhtml
new file mode 100644
index 0000000000..51a186e318
--- /dev/null
+++ b/tobago-example/tobago-example-demo/src/main/webapp/resources/demo-components/columnSelectorImmediate.xhtml
@@ -0,0 +1,68 @@
+<?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.
+-->
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns:cc="jakarta.faces.composite"
+ xmlns:f="jakarta.faces.core"
+ xmlns:tc="http://myfaces.apache.org/tobago/component">
+<cc:interface>
+ <cc:attribute name="columnSelectorAjaxController"/>
+</cc:interface>
+<cc:implementation>
+ <tc:button id="resetImmediateTrueAjaxEnabled" label="Reset immediate=true ajax=enabled" immediate="true"
+ action="#{cc.attrs.columnSelectorAjaxController.reset(true, false)}"/>
+ <tc:button id="resetImmediateTrueAjaxDisabled" label="Reset immediate=true ajax=disabled" immediate="true"
+ action="#{cc.attrs.columnSelectorAjaxController.reset(true, true)}"/>
+ <tc:button id="resetImmediateFalseAjaxEnabled" label="Reset immediate=false ajax=enabled" immediate="true"
+ action="#{cc.attrs.columnSelectorAjaxController.reset(false, false)}"/>
+ <tc:button id="resetImmediateFalseAjaxDisabled" label="Reset immediate=false ajax=disabled" immediate="true"
+ action="#{cc.attrs.columnSelectorAjaxController.reset(false, true)}"/>
+
+ <tc:out label="immediate" value="#{cc.attrs.columnSelectorAjaxController.columnSelectorImmediate}"/>
+ <tc:out label="f:ajax"
+ value="#{cc.attrs.columnSelectorAjaxController.columnSelectorAjaxDisabled ? 'disabled' : 'enabled'}"/>
+
+ <tc:sheet id="sheet" value="#{cc.attrs.columnSelectorAjaxController.solarList}" var="object"
+ rows="5"
+ state="#{cc.attrs.columnSelectorAjaxController.sheetState}" selectable="multi">
+ <tc:columnSelector id="colSelect" immediate="#{cc.attrs.columnSelectorAjaxController.columnSelectorImmediate}">
+ <f:ajax render="sheet ::selectedCount ::::messages"
+ disabled="#{cc.attrs.columnSelectorAjaxController.columnSelectorAjaxDisabled}"/>
+ </tc:columnSelector>
+ <tc:column label="Name">
+ <tc:in value="#{object.name}" autoSpacing="false"/>
+ </tc:column>
+ <tc:column label="Orbit">
+ <tc:out value="#{object.orbit}" autoSpacing="false"/>
+ </tc:column>
+ <tc:column label="Period (Days)">
+ <tc:out value="#{object.period}" autoSpacing="false"/>
+ </tc:column>
+ <tc:column label="Discoverer">
+ <tc:out value="#{object.discoverer}" autoSpacing="false"/>
+ </tc:column>
+ <tc:column label="Required">
+ <tc:in required="true">
+ <tc:event event="click" stopPropagation="true" omit="true"/>
+ </tc:in>
+ </tc:column>
+ </tc:sheet>
+ <cc:renderFacet name="buttons"/>
+</cc:implementation>
+</html>
diff --git a/tobago-example/tobago-example-demo/src/test/typescript/columnSelector.test.ts b/tobago-example/tobago-example-demo/src/test/typescript/columnSelector.test.ts
index 4543b264e8..1dbfa60568 100644
--- a/tobago-example/tobago-example-demo/src/test/typescript/columnSelector.test.ts
+++ b/tobago-example/tobago-example-demo/src/test/typescript/columnSelector.test.ts
@@ -541,3 +541,187 @@ test.describe("sheet/columnSelector/immediate/immediate.xhtml", () => {
await expect(alert).not.toBeVisible();
});
});
+
+test.describe("sheet/columnSelector/immediate/immediate_compositeComponent.xhtml", () => {
+
+ test.beforeEach(async ({page}, testInfo) => {
+ await page.goto("/content/900-test/sheet/columnSelector/immediate/immediate_compositeComponent.xhtml");
+ });
+
+ test("immediate=true, ajax=enabled", async ({page}) => {
+ const alert = page.locator("tobago-messages[id='page:messages'] .alert");
+ const reset = page.locator(".tobago-button[id='page:mainForm:compComp:resetImmediateTrueAjaxEnabled']");
+ const sheet = page.locator("tobago-sheet[id='page:mainForm:compComp:sheet']");
+ const toggleAll = sheet.locator("th[id='page:mainForm:compComp:sheet:colSelect'] input");
+ const row0Checkbox = sheet.locator("input[name='page:mainForm:compComp:sheet_data_row_selector_0']");
+ const row4Orbit = sheet.locator("tr[row-index='4'] td:nth-child(3) tobago-out");
+ const selectedCount = page.locator("tobago-out[id='page:mainForm:compComp:selectedCount'] span.form-control-plaintext");
+
+ await reset.click();
+
+ await expect(sheet).toBeVisible();
+ await expect(selectedCount).toHaveText("0");
+ await expect(alert).not.toBeVisible();
+
+ await row0Checkbox.click();
+ await expect(selectedCount).toHaveText("1");
+ await expect(alert).not.toBeVisible();
+
+ await row4Orbit.click();
+ await expect(selectedCount).toHaveText("2");
+ await expect(alert).not.toBeVisible();
+
+ await toggleAll.click();
+ await expect(selectedCount).toHaveText("5");
+ await expect(alert).not.toBeVisible();
+
+ await toggleAll.click();
+ await expect(selectedCount).toHaveText("0");
+ await expect(alert).not.toBeVisible();
+ });
+
+ test("immediate=true, ajax=disabled", async ({page}) => {
+ const alert = page.locator("tobago-messages[id='page:messages'] .alert");
+ const reset = page.locator(".tobago-button[id='page:mainForm:compComp:resetImmediateTrueAjaxDisabled']");
+ const sheet = page.locator("tobago-sheet[id='page:mainForm:compComp:sheet']");
+ const toggleAll = sheet.locator("th[id='page:mainForm:compComp:sheet:colSelect'] input");
+ const row0Checkbox = sheet.locator("input[name='page:mainForm:compComp:sheet_data_row_selector_0']");
+ const row4Orbit = sheet.locator("tr[row-index='4'] td:nth-child(3) tobago-out");
+ const selectedCount = page.locator("tobago-out[id='page:mainForm:compComp:selectedCount'] span.form-control-plaintext");
+ const submit = page.locator("button[id='page:mainForm:compComp:submit']");
+
+ await reset.click();
+ await expect(sheet).toBeVisible();
+ await expect(selectedCount).toHaveText("0");
+ await expect(alert).not.toBeVisible();
+
+ await row0Checkbox.click();
+ await expect(selectedCount).toHaveText("0");
+ await submit.click();
+ await expect(selectedCount).toHaveText("1");
+ await expect(alert).toBeVisible();
+
+ await row4Orbit.click();
+ await expect(selectedCount).toHaveText("1");
+ await submit.click();
+ await expect(selectedCount).toHaveText("2");
+ await expect(alert).toBeVisible();
+
+ await toggleAll.click();
+ await expect(selectedCount).toHaveText("2");
+ await submit.click();
+ await expect(selectedCount).toHaveText("5");
+ await expect(alert).toBeVisible();
+
+ await toggleAll.click();
+ await expect(selectedCount).toHaveText("5");
+ await submit.click();
+ await expect(selectedCount).toHaveText("0");
+ await expect(alert).toBeVisible();
+ });
+
+ test("immediate=false, ajax=enabled", async ({page}) => {
+ const alert = page.locator("tobago-messages[id='page:messages'] .alert");
+ const reset = page.locator(".tobago-button[id='page:mainForm:compComp:resetImmediateFalseAjaxEnabled']");
+ const sheet = page.locator("tobago-sheet[id='page:mainForm:compComp:sheet']");
+ const toggleAll = sheet.locator("th[id='page:mainForm:compComp:sheet:colSelect'] input");
+ const row0Checkbox = sheet.locator("input[name='page:mainForm:compComp:sheet_data_row_selector_0']");
+ const row4Orbit = sheet.locator("tr[row-index='4'] td:nth-child(3) tobago-out");
+ const selectedCount = page.locator("tobago-out[id='page:mainForm:compComp:selectedCount'] span.form-control-plaintext");
+
+ await reset.click();
+
+ await expect(sheet).toBeVisible();
+ await expect(selectedCount).toHaveText("0");
+ await expect(alert).not.toBeVisible();
+
+ await row0Checkbox.click();
+ await expect(selectedCount).toHaveText("0");
+ await expect(alert).toBeVisible();
+
+ await row4Orbit.click();
+ await expect(selectedCount).toHaveText("0");
+ await expect(alert).toBeVisible();
+
+ await toggleAll.click();
+ await expect(selectedCount).toHaveText("0");
+ await expect(alert).toBeVisible();
+ });
+
+ test("immediate=false, ajax=disabled", async ({page}) => {
+ const alert = page.locator("tobago-messages[id='page:messages'] .alert");
+ const reset = page.locator(".tobago-button[id='page:mainForm:compComp:resetImmediateFalseAjaxDisabled']");
+ const sheet = page.locator("tobago-sheet[id='page:mainForm:compComp:sheet']");
+ const toggleAll = sheet.locator("th[id='page:mainForm:compComp:sheet:colSelect'] input");
+ const row0Checkbox = sheet.locator("input[name='page:mainForm:compComp:sheet_data_row_selector_0']");
+ const row4Orbit = sheet.locator("tr[row-index='4'] td:nth-child(3) tobago-out");
+ const selectedCount = page.locator("tobago-out[id='page:mainForm:compComp:selectedCount'] span.form-control-plaintext");
+ const submit = page.locator("button[id='page:mainForm:compComp:submit']");
+
+ await reset.click();
+ await expect(sheet).toBeVisible();
+ await expect(selectedCount).toHaveText("0");
+ await expect(alert).not.toBeVisible();
+
+ await row0Checkbox.click();
+ await submit.click();
+ await expect(selectedCount).toHaveText("0");
+ await expect(alert).toBeVisible();
+
+ await row4Orbit.click();
+ await submit.click();
+ await expect(selectedCount).toHaveText("0");
+ await expect(alert).toBeVisible();
+
+ await toggleAll.click();
+ await submit.click();
+ await expect(selectedCount).toHaveText("0");
+ await expect(alert).toBeVisible();
+ });
+
+ test("Save selected rows (Ajax)", async ({page}) => {
+ const alert = page.locator("tobago-messages[id='page:messages'] .alert");
+ const reset = page.locator(".tobago-button[id='page:mainForm:compComp:resetImmediateTrueAjaxDisabled']");
+ const sheet = page.locator("tobago-sheet[id='page:mainForm:compComp:sheet']");
+ const row0Checkbox = sheet.locator("input[name='page:mainForm:compComp:sheet_data_row_selector_0']");
+ const row1Checkbox = sheet.locator("input[name='page:mainForm:compComp:sheet_data_row_selector_1']");
+ const row2Checkbox = sheet.locator("input[name='page:mainForm:compComp:sheet_data_row_selector_2']");
+ const savedSelectedCount = page.locator("tobago-out[id='page:mainForm:compComp:selectedCountByAction'] span.form-control-plaintext");
+ const saveSelectedRowsButton = page.locator(".tobago-button[id='page:mainForm:compComp:saveSelectedRowsAjax']");
+
+ await reset.click();
+ await expect(sheet).toBeVisible();
+ await expect(savedSelectedCount).toHaveText("0");
+ await expect(alert).not.toBeVisible();
+
+ await row0Checkbox.click();
+ await row1Checkbox.click();
+ await row2Checkbox.click();
+ await saveSelectedRowsButton.click();
+
+ await expect(savedSelectedCount).toHaveText("3");
+ await expect(alert).not.toBeVisible();
+ });
+
+ test("Save selected rows (Submit)", async ({page}) => {
+ const alert = page.locator("tobago-messages[id='page:messages'] .alert");
+ const reset = page.locator(".tobago-button[id='page:mainForm:compComp:resetImmediateTrueAjaxDisabled']");
+ const sheet = page.locator("tobago-sheet[id='page:mainForm:compComp:sheet']");
+ const row0Checkbox = sheet.locator("input[name='page:mainForm:compComp:sheet_data_row_selector_0']");
+ const row2Checkbox = sheet.locator("input[name='page:mainForm:compComp:sheet_data_row_selector_2']");
+ const savedSelectedCount = page.locator("tobago-out[id='page:mainForm:compComp:selectedCountByAction'] span.form-control-plaintext");
+ const saveSelectedRowsButton = page.locator(".tobago-button[id='page:mainForm:compComp:saveSelectedRowsSubmit']");
+
+ await reset.click();
+ await expect(sheet).toBeVisible();
+ await expect(savedSelectedCount).toHaveText("0");
+ await expect(alert).not.toBeVisible();
+
+ await row0Checkbox.click();
+ await row2Checkbox.click();
+ await saveSelectedRowsButton.click();
+
+ await expect(savedSelectedCount).toHaveText("2");
+ await expect(alert).not.toBeVisible();
+ });
+});