Repository: primefaces/primefaces
Pull request #13617 (2025-03-24 23:36:12)

Fix spelling
I noticed `SelectCheckboxMenu#togglPanel()` in PrimeFaces Selenium had a typo, so I fixed it and did a once-over of most/all of that submodule. I also saw that PrimeFaces uses "toggleable" consistently except in one place where "togglable" is used, so I updated it, despite both being acceptable spellings. Since both of these changes affect end-users I updated the v16 What's New. 


Patch:
diff --git a/docs/16_0_0/gettingstarted/whatsnew.md b/docs/16_0_0/gettingstarted/whatsnew.md
index 8381cd7c74..f2d8f79409 100644
--- a/docs/16_0_0/gettingstarted/whatsnew.md
+++ b/docs/16_0_0/gettingstarted/whatsnew.md
@@ -2,88 +2,8 @@
 
 This page contains a list of big features. Please check the GitHub issues for all changes.
 
-## 15.0.0
+## 16.0.0
 
 ### PrimeFaces
 
-Look into [migration guide](https://primefaces.github.io/primefaces/15_0_0/#/../migrationguide/15_0_0) for more enhancements and changes.
-
-* Core
-    * Added `widgetPreConstruct` attribute to support custom logic before widget creation. See [widgets](../core/widgets.md) for more information.
-    
-* AutoComplete
-    * Added property `highlightSelector=""` so you can delcare the jQuery selector for what to find and highlight. See https://github.com/primefaces/primefaces/issues/11822 
-    
-* Captcha
-    * Added [hCaptcha](https://www.hcaptcha.com/) support
-    
-* Chart
-    * Added `canvasStyle` and `canvasStyleClass` attributes to support custom styling of the canvas element
-    
-* ConfirmDialog/ConfirmPopup
-    * `yesButtonLabel`: overrides label of 'Yes' button (and restores it before the global confirm dialog is reused elsewhere)
-    * `yesButtonClass`: adds given class to 'Yes' button (and removes it before the global confirm dialog is reused elsewhere)
-    * `yesButtonIcon`: overrides icon of 'Yes' button (and removes it before the global confirm dialog is reused elsewhere)
-    * `noButtonLabel`: overrides label of 'No' button (and restores it before the global confirm dialog is reused elsewhere)
-    * `noButtonClass`: adds given class to 'No' button (and removes it before the global confirm dialog is reused elsewhere)
-    * `noButtonIcon`: overrides icon of 'No' button (and removes it before the global confirm dialog is reused elsewhere)
-    * `confirmMessage`: facet on the parent component of the `p:confirm` behavior. Can be used as message content instead of the `p:confirm` `message` attribute.
-  
-* Dashboard
-    *  `scope`: Scope for dashboard drag and drop behaviour. Items can be dragged between multiple dashboards with the same scope. (js default: dashboard)
-
-* DataTable 
-    * Added attribute `frozenColumnsAlignment` to support alignment of frozen columns left and right
-    * Added `expandIcon`/`collapseIcon` attributes for RowToggler
-    * Added `rowsPerPage` in the PageEvent AJAX event
-    * Added `filterNormalize` attribute to normalize the filter values (remove accents)
-    * Added `toggleFilter()` JS Widget function to show/hide filter components (see showcase example for usage).
-
-* DatePicker
-    * Added `defaultHour`, `defaultMinute`, `defaultSecond`, `defaultMillisec` attributes to match legacy `Calendar` component
-    * Added ability to pick weeks by `view="week"`
-    * Added `showLongMonthNames` attribute to display long month names instead of short names in month picker and month navigator
-
-* FeedReader
-    * Added `podcast="true"` property if [Apple Itunes Podcast](https://help.apple.com/itc/podcasts_connect/#/itcb54353390) parsing and specific tags 
-
-* FileUpload
-    * Added new messages to support localization
-        * `primefaces.FileValidator.FILENAME_INVALID_CHAR=Invalid filename: {0} contains invalid character: {1}`
-        * `primefaces.FileValidator.FILENAME_INVALID_LINUX=Invalid Linux filename: {0}`
-        * `primefaces.FileValidator.FILENAME_INVALID_WINDOWS=Invalid Windows filename: {0}`
-        * `primefaces.FileValidator.FILENAME_EMPTY=Filename cannot be empty or null`
-    * Added `empty` facet to add placeholder content
-    * Added `ignoreAutoUpdate` attribute which if set to true, components which use `p:autoUpdate` will not be updated for this request.
-
-* InputNumber
-    * Added `modifyValueOnUpDownArrow` which allows the user to increment or decrement the element value with the up and down arrow keys. Default is true.
-    * Added `decimalPlacesRawValue` Specifies the number of decimal places to retain for the raw value and `decimalPlaces` will be used for display values. If this option is left as `null` (the default), the `decimalPlaces` value will be used. Note: Setting this to fewer decimal places than those displayed may cause user confusion.
-
-* Paginator 
-    * Added `rowsPerPage` in the PageEvent AJAX event
-
-* SelectCheckboxMenu
-    * Added `showSelectAll` attribute to display the select all checkbox
-
-* SelectOneMenu
-    * Added `clear` AJAX event when in `editable="true"` and you clear out a value with BACKSPACE/DELETE
-
-* Signature
-    * Ability to type your signature
-    * Added `textValue`, `fontSize`, `fontFamily` attributes to support typing of signature
-    * ARIA accessibility support with `role="img"` and `aria-label`
-
-* StaticMessage
-    * Added `severity="success"` to align with React/Vue/Angular
-
-* Tree
-    * Added `filterPlaceholder` property
-
-* TreeTable
-    * Added `filterNormalize` attribute to normalize the filter values (remove accents)
-
-* Wizard
-    * Added `highlightCompletedSteps` attribute to highlight completed steps
-
-    
\ No newline at end of file
+Look into [migration guide](https://primefaces.github.io/primefaces/16_0_0/#/../migrationguide/16_0_0) for more enhancements and changes.
diff --git a/docs/migrationguide/16_0_0.md b/docs/migrationguide/16_0_0.md
index b7216efc42..df3ae91260 100644
--- a/docs/migrationguide/16_0_0.md
+++ b/docs/migrationguide/16_0_0.md
@@ -117,4 +117,12 @@ Please use this instead:
 
 ### Separator
 
-* `p:separator` has been removed, just replace everywhere with a `p:divider`
\ No newline at end of file
+* `p:separator` has been removed, just replace everywhere with a `p:divider`
+
+### DataTable
+
+* Rename `ui-filter-togglable` to `ui-filter-toggleable`
+
+### PrimeFaces Selenium Components
+
+* SelectCheckboxMenu: Rename `togglPanel()` to `togglePanel()`
diff --git a/primefaces-selenium/pom.xml b/primefaces-selenium/pom.xml
index 8b27c5f43e..76d2385b47 100644
--- a/primefaces-selenium/pom.xml
+++ b/primefaces-selenium/pom.xml
@@ -16,7 +16,7 @@
 
     <name>PrimeFaces Selenium</name>
     <description>
-        <![CDATA[PrimeFaces testing support based on Selenium and the concept of page ojects / fragements.]]>
+        <![CDATA[PrimeFaces testing support based on Selenium and the concept of page objects / fragments.]]>
     </description>
 
     <modules>
diff --git a/primefaces-selenium/primefaces-selenium-components/src/main/java/org/primefaces/selenium/component/AutoComplete.java b/primefaces-selenium/primefaces-selenium-components/src/main/java/org/primefaces/selenium/component/AutoComplete.java
index dd7d56d555..a7c19057ab 100644
--- a/primefaces-selenium/primefaces-selenium-components/src/main/java/org/primefaces/selenium/component/AutoComplete.java
+++ b/primefaces-selenium/primefaces-selenium-components/src/main/java/org/primefaces/selenium/component/AutoComplete.java
@@ -123,7 +123,7 @@ public abstract class AutoComplete extends AbstractInputComponent {
     }
 
     /**
-     * Gets the actual token elements in mutliple mode.
+     * Gets the actual token elements in multiple mode.
      *
      * @return the List of tokens
      */
diff --git a/primefaces-selenium/primefaces-selenium-components/src/main/java/org/primefaces/selenium/component/DatePicker.java b/primefaces-selenium/primefaces-selenium-components/src/main/java/org/primefaces/selenium/component/DatePicker.java
index 00912dbd1f..ea4ebbdac8 100644
--- a/primefaces-selenium/primefaces-selenium-components/src/main/java/org/primefaces/selenium/component/DatePicker.java
+++ b/primefaces-selenium/primefaces-selenium-components/src/main/java/org/primefaces/selenium/component/DatePicker.java
@@ -299,7 +299,7 @@ public abstract class DatePicker extends AbstractInputComponent {
     }
 
     /**
-     * Select a month from the drodown.
+     * Select a month from the dropdown.
      *
      * @param month the month to select
      */
diff --git a/primefaces-selenium/primefaces-selenium-components/src/main/java/org/primefaces/selenium/component/SelectCheckboxMenu.java b/primefaces-selenium/primefaces-selenium-components/src/main/java/org/primefaces/selenium/component/SelectCheckboxMenu.java
index 23e2bc5e90..0e32e9a1ab 100644
--- a/primefaces-selenium/primefaces-selenium-components/src/main/java/org/primefaces/selenium/component/SelectCheckboxMenu.java
+++ b/primefaces-selenium/primefaces-selenium-components/src/main/java/org/primefaces/selenium/component/SelectCheckboxMenu.java
@@ -96,9 +96,9 @@ public abstract class SelectCheckboxMenu extends AbstractInputComponent {
     }
 
     /**
-     * Bring up the overlay panel if its not showing or hide it if it is showing.
+     * Bring up the overlay panel if it's not showing or hide it if it is showing.
      */
-    public void togglPanel() {
+    public void togglePanel() {
         PrimeSelenium.executeScript(getWidgetByIdScript() + ".togglePanel();");
     }
 
diff --git a/primefaces-selenium/primefaces-selenium-components/src/main/java/org/primefaces/selenium/component/model/tree/TreeNode.java b/primefaces-selenium/primefaces-selenium-components/src/main/java/org/primefaces/selenium/component/model/tree/TreeNode.java
index 7721da653a..39ea4d2330 100644
--- a/primefaces-selenium/primefaces-selenium-components/src/main/java/org/primefaces/selenium/component/model/tree/TreeNode.java
+++ b/primefaces-selenium/primefaces-selenium-components/src/main/java/org/primefaces/selenium/component/model/tree/TreeNode.java
@@ -82,7 +82,7 @@ public class TreeNode {
     }
 
     public List<TreeNode> getChildren() {
-        // we need a xpath selector as selenium doesnt support direct child selector like #findElements(">...")
+        // we need a xpath selector as selenium doesn't support direct child selector like #findElements(">...")
         return getWebElement()
                 .findElements(By.xpath("./ul/li[contains(@class, 'ui-treenode')]")).stream()
                 .map(e -> new TreeNode(tree, e.getDomAttribute("data-rowkey"), this))
diff --git a/primefaces-showcase/src/main/webapp/ui/data/datatable/filter.xhtml b/primefaces-showcase/src/main/webapp/ui/data/datatable/filter.xhtml
index debe00ec3a..db02fa3a32 100644
--- a/primefaces-showcase/src/main/webapp/ui/data/datatable/filter.xhtml
+++ b/primefaces-showcase/src/main/webapp/ui/data/datatable/filter.xhtml
@@ -196,12 +196,12 @@
 
         <div class="card">
             <h5>Filter toggling</h5>
-            <p>1. Hide filter components initially by adding <code>ui-filter-togglable</code> as <em>styleClass</em> to the datatable. <br/>
+            <p>1. Hide filter components initially by adding <code>ui-filter-toggleable</code> as <em>styleClass</em> to the datatable. <br/>
                 2. To show/hide the filter components call <code>PF('customersTable4').toggleFilter()</code>.</p>
             <h:form id="customersForm4">
                 <p:dataTable id="customers" var="customer" value="#{dtFilterView.customers1}" widgetVar="customersTable4"
                              emptyMessage="No customers found with given criteria" stickyHeader="true" stickyTopAt=".layout-topbar"
-                             filteredValue="#{dtFilterView.filteredCustomers1}" styleClass="ui-filter-togglable">
+                             filteredValue="#{dtFilterView.filteredCustomers1}" styleClass="ui-filter-toggleable">
 
                     <f:facet name="header">
                         <div class="flex align-items-center justify-content-between">
diff --git a/primefaces/src/main/frontend/packages/components/src/datatable/datatable.css b/primefaces/src/main/frontend/packages/components/src/datatable/datatable.css
index 1f8f874797..4cf6cc9a6a 100644
--- a/primefaces/src/main/frontend/packages/components/src/datatable/datatable.css
+++ b/primefaces/src/main/frontend/packages/components/src/datatable/datatable.css
@@ -69,8 +69,8 @@
     margin: .5rem auto auto auto;
 }
 
-.ui-datatable.ui-filter-togglable thead .ui-column-customfilter,
-.ui-datatable.ui-filter-togglable thead .ui-column-filter {
+.ui-datatable.ui-filter-toggleable thead .ui-column-customfilter,
+.ui-datatable.ui-filter-toggleable thead .ui-column-filter {
     display:none;
 }
 
@@ -111,7 +111,7 @@
 .ui-datatable-scrollable .ui-datatable-scrollable-header,
 .ui-datatable-scrollable .ui-datatable-scrollable-footer {
     position: relative;
-} 
+}
 
 .ui-datatable-scrollable .ui-datatable-scrollable-header td {
     font-weight: normal;
@@ -262,7 +262,7 @@
     direction: rtl;
 }
 
-.ui-datatable-rtl.ui-datatable thead th, 
+.ui-datatable-rtl.ui-datatable thead th,
 .ui-datatable-rtl.ui-datatable tfoot td {
     text-align: right;
 }
@@ -348,7 +348,7 @@
         display: none;
     }
 
-    .ui-datatable-reflow .ui-datatable-data td[role="gridcell"]:not(.ui-helper-hidden) { 
+    .ui-datatable-reflow .ui-datatable-data td[role="gridcell"]:not(.ui-helper-hidden) {
         -moz-box-sizing: border-box;
         -webkit-box-sizing: border-box;
         box-sizing: border-box;
@@ -359,36 +359,36 @@
         text-align: left !important;
         width: 100% !important;
     }
-    
+
     .ui-datatable-reflow .ui-datatable-data.ui-widget-content {
         border: 0px none;
     }
-    
+
     .ui-datatable-reflow .ui-datatable-data tr.ui-widget-content {
         border-left: 0px none;
         border-right: 0px none;
     }
 
-    .ui-datatable-reflow .ui-datatable-data td[role="gridcell"] .ui-column-title { 
+    .ui-datatable-reflow .ui-datatable-data td[role="gridcell"] .ui-column-title {
         display: inline-block;
         font-weight: bold;
         margin: -.4em 1em -.4em -.4em;
         min-width: 30%;
-        max-width: 35%; 
+        max-width: 35%;
         padding: .4em;
     }
-    
+
     .ui-datatable-reflow .ui-reflow-label,
     .ui-datatable-reflow .ui-reflow-dropdown {
         display: inline-block;
     }
-    
+
     .ui-datatable-reflow .ui-reflow-dropdown {
         margin-left: 5px;
         border-top-left-radius: 3px;
         border-bottom-left-radius: 3px;
     }
-    
+
     .ui-datatable-reflow tr.ui-datatable-empty-message > td {
         display: block;
         border: 0 none;
diff --git a/primefaces/src/main/frontend/packages/datepicker/datepicker/src/jquery.ui.prime.datepicker.cjs b/primefaces/src/main/frontend/packages/datepicker/datepicker/src/jquery.ui.prime.datepicker.cjs
index 656c42d51b..81be36e1ce 100644
--- a/primefaces/src/main/frontend/packages/datepicker/datepicker/src/jquery.ui.prime.datepicker.cjs
+++ b/primefaces/src/main/frontend/packages/datepicker/datepicker/src/jquery.ui.prime.datepicker.cjs
@@ -670,7 +670,7 @@ $.widget("prime.datePicker", {
                             formattedValue += ' ' + this.options.rangeSeparator + ' ' + this.formatDateTime(endDate);
                         }
 
-                        if (this.options.view === 'week') {
+                        if (this.options.view === 'week' && this.options.showWeek) {
                             var startDateMeta = { day: startDate.getDate(), month: startDate.getMonth(), year: startDate.getFullYear() };
                             var week = this.options.weekCalculator(startDateMeta);
                             formattedValue += ' (' + this.options.locale.weekHeader + ' ' + week + ')';
diff --git a/primefaces/src/main/java/org/primefaces/util/CalendarUtils.java b/primefaces/src/main/java/org/primefaces/util/CalendarUtils.java
index a8bfed15c6..c40a73faac 100644
--- a/primefaces/src/main/java/org/primefaces/util/CalendarUtils.java
+++ b/primefaces/src/main/java/org/primefaces/util/CalendarUtils.java
@@ -648,17 +648,22 @@ public class CalendarUtils {
         }
 
         // fast return
-        if (!input.contains(token)) {
+        if (input == null || !input.contains(token)) {
             return Collections.emptyList();
         }
 
         int tokenIndex = input.indexOf(token);
         String from = input.substring(0, tokenIndex);
         String to = input.substring(tokenIndex + token.length());
+
         if (week) {
             // hardcoded pattern, see JS:
             // formattedValue += ' (' + this.options.locale.weekHeader + ' ' + week + ')';
-            to = to.substring(0, to.lastIndexOf(" ("));
+            int weekNumberIndex = to.lastIndexOf(" (");
+            // there are cases when weeknumber might not get displayed on client and therefore not submitted
+            if (weekNumberIndex != -1) {
+                to = to.substring(0, weekNumberIndex);
+            }
         }
 
         return Arrays.asList(from, to);


Test patch:
diff --git a/primefaces-integration-tests/src/test/java/org/primefaces/integrationtests/datatable/DataTable002Test.java b/primefaces-integration-tests/src/test/java/org/primefaces/integrationtests/datatable/DataTable002Test.java
index a1d9e92a88..b7053e892f 100644
--- a/primefaces-integration-tests/src/test/java/org/primefaces/integrationtests/datatable/DataTable002Test.java
+++ b/primefaces-integration-tests/src/test/java/org/primefaces/integrationtests/datatable/DataTable002Test.java
@@ -193,7 +193,7 @@ class DataTable002Test extends AbstractDataTableTest {
         dataTable.selectPage(1);
         dataTable.sort("First Appeared");
         SelectCheckboxMenu filterType = getFilterType();
-        filterType.togglPanel();
+        filterType.togglePanel();
         List<WebElement> filterTypeCheckboxes = filterType.getPanel().findElements(By.cssSelector(".ui-chkbox-box"));
         PrimeSelenium.guardAjax(filterTypeCheckboxes.get(1)).click();
 
diff --git a/primefaces-integration-tests/src/test/java/org/primefaces/integrationtests/datatable/DataTable039Test.java b/primefaces-integration-tests/src/test/java/org/primefaces/integrationtests/datatable/DataTable039Test.java
index ba95d3d9ea..18bdf89601 100644
--- a/primefaces-integration-tests/src/test/java/org/primefaces/integrationtests/datatable/DataTable039Test.java
+++ b/primefaces-integration-tests/src/test/java/org/primefaces/integrationtests/datatable/DataTable039Test.java
@@ -67,7 +67,7 @@ class DataTable039Test extends AbstractDataTableTest {
         dataTable.selectPage(1);
         dataTable.sort("First Appeared");
         SelectCheckboxMenu filterType = getFilterType();
-        filterType.togglPanel();
+        filterType.togglePanel();
         List<WebElement> filterTypeCheckboxes = filterType.getPanel().findElements(By.cssSelector(".ui-chkbox-box"));
         PrimeSelenium.guardAjax(filterTypeCheckboxes.get(1)).click();
 
diff --git a/primefaces-integration-tests/src/test/java/org/primefaces/integrationtests/datatable/DataTable048Test.java b/primefaces-integration-tests/src/test/java/org/primefaces/integrationtests/datatable/DataTable048Test.java
index e5644591c3..b58ffea622 100644
--- a/primefaces-integration-tests/src/test/java/org/primefaces/integrationtests/datatable/DataTable048Test.java
+++ b/primefaces-integration-tests/src/test/java/org/primefaces/integrationtests/datatable/DataTable048Test.java
@@ -55,11 +55,11 @@ class DataTable048Test extends AbstractDataTableTest {
         assertNotNull(filter1);
         assertNotNull(filter2);
 
-        filter1.togglPanel();
+        filter1.togglePanel();
         List<WebElement> filterTypeCheckboxes = filter1.getPanel().findElements(By.cssSelector(".ui-chkbox-box"));
         // In filter all
         PrimeSelenium.guardAjax(filterTypeCheckboxes.get(0)).click();
-        filter1.togglPanel();
+        filter1.togglePanel();
 
         // Equals filter 2
         filter2.toggleDropdown();
@@ -76,12 +76,12 @@ class DataTable048Test extends AbstractDataTableTest {
         // Equals filter null (all)
         filter2.select("Empty");
 
-        filter1.togglPanel();
+        filter1.togglePanel();
         filterTypeCheckboxes = filter1.getPanel().findElements(By.cssSelector(".ui-chkbox-box"));
         PrimeSelenium.guardAjax(filterTypeCheckboxes.get(0)).click();
         // In filter null
         PrimeSelenium.guardAjax(filterTypeCheckboxes.get(1)).click();
-        filter1.togglPanel();
+        filter1.togglePanel();
 
         // Assert
         rows = dataTable.getRows();
diff --git a/primefaces/src/test/java/org/primefaces/util/CalendarUtilsTest.java b/primefaces/src/test/java/org/primefaces/util/CalendarUtilsTest.java
index 401933b6c8..b528df4bba 100644
--- a/primefaces/src/test/java/org/primefaces/util/CalendarUtilsTest.java
+++ b/primefaces/src/test/java/org/primefaces/util/CalendarUtilsTest.java
@@ -221,8 +221,10 @@ class CalendarUtilsTest {
         assertEquals(2, splitRange.size());
         assertEquals("2021-03-01", splitRange.get(0));
         assertEquals("2021-03-31", splitRange.get(1));
+
         splitRange = CalendarUtils.splitRange("2021-03-01", "yyyy-MM-dd", "-", false);
         assertTrue(splitRange.isEmpty());
+
         splitRange = CalendarUtils.splitRange("", "yyyy-MM-dd", "-", false);
         assertTrue(splitRange.isEmpty());
 
@@ -237,13 +239,26 @@ class CalendarUtilsTest {
         assertEquals(2, splitRange.size());
         assertEquals("2021-03-01", splitRange.get(0));
         assertEquals("2021-03-31", splitRange.get(1));
+
         splitRange = CalendarUtils.splitRange("2021-03-01 (Wk 21)", "yyyy-MM-dd", "-", true);
         assertTrue(splitRange.isEmpty());
+
         splitRange = CalendarUtils.splitRange("", "yyyy-MM-dd (Wk 21)", "-", true);
         assertTrue(splitRange.isEmpty());
 
+        splitRange = CalendarUtils.splitRange("2021-03-01 - 2021-03-31 (Wk NaN)", "yyyy-MM-dd", "-", true);
+        assertEquals(2, splitRange.size());
+        assertEquals("2021-03-01", splitRange.get(0));
+        assertEquals("2021-03-31", splitRange.get(1));
+
+        splitRange = CalendarUtils.splitRange("2021-03-01 - 2021-03-31", "yyyy-MM-dd", "-", true);
+        assertEquals(2, splitRange.size());
+        assertEquals("2021-03-01", splitRange.get(0));
+        assertEquals("2021-03-31", splitRange.get(1));
+
         assertThrows(FacesException.class, () -> {
             CalendarUtils.splitRange("2021 - 03 - 01 - 2021 - 03 - 31 (Wk 21)", "yyyy - MM - dd", "-", true);
         });
     }
+
 }