Commit 7a11aee2975 for woocommerce
commit 7a11aee29755f5ad85546d5b99666c22dbc44a41
Author: Miroslav Mitev <m1r0@users.noreply.github.com>
Date: Tue Sep 22 11:48:22 2026 +0300
Fix Analytics calendar day labels reading out the wrong date (#68514)
diff --git a/packages/js/components/changelog/fix-calendar-day-aria-label-format b/packages/js/components/changelog/fix-calendar-day-aria-label-format
new file mode 100644
index 00000000000..0914207c2ec
--- /dev/null
+++ b/packages/js/components/changelog/fix-calendar-day-aria-label-format
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fix the DateRange calendar labelling each day with the wrong date.
diff --git a/packages/js/components/src/calendar/date-range.js b/packages/js/components/src/calendar/date-range.js
index b1c39388a9f..e55f006092b 100644
--- a/packages/js/components/src/calendar/date-range.js
+++ b/packages/js/components/src/calendar/date-range.js
@@ -23,6 +23,18 @@ import DateInput from './input';
import phrases from './phrases';
const isRTL = () => document.documentElement.dir === 'rtl';
+
+// react-dates builds each day's aria-label with moment, defaulting to 'dddd, LL'.
+// WordPress seeds `LL` with the PHP `date_format` option, which moment misreads as its
+// own tokens, so use moment tokens here instead. Called at render time so the
+// translation is read after the locale data loads.
+const getDayAriaLabelFormat = () =>
+ __(
+ /* translators: Moment.js format for the date a screen reader reads out on a calendar day. Keep the moment tokens (dddd, MMMM, D, YYYY) and reorder them to suit the locale. */
+ 'dddd, MMMM D, YYYY',
+ 'woocommerce'
+ );
+
// Blur event sources
const CONTAINER_DIV = 'container';
const NEXT_MONTH_CLICK = 'onNextMonthClick';
@@ -266,6 +278,7 @@ class DateRange extends Component {
before
) }
phrases={ phrases }
+ dayAriaLabelFormat={ getDayAriaLabelFormat() }
/>
</div>
</div>
diff --git a/packages/js/components/src/calendar/test/date-range.js b/packages/js/components/src/calendar/test/date-range.js
new file mode 100644
index 00000000000..8190dc02d3f
--- /dev/null
+++ b/packages/js/components/src/calendar/test/date-range.js
@@ -0,0 +1,101 @@
+/**
+ * External dependencies
+ */
+import { render } from '@testing-library/react';
+import { createElement } from '@wordpress/element';
+import { resetLocaleData, setLocaleData } from '@wordpress/i18n';
+import moment from 'moment';
+
+/**
+ * Internal dependencies
+ */
+import DateRange from '../date-range';
+
+// The moments have to be built inside the test: moment.updateLocale() swaps the
+// locale object, and existing moments (and their clones) keep the old one.
+const renderDateRange = () =>
+ render(
+ <DateRange
+ after={ moment( '2026-08-01' ) }
+ before={ moment( '2026-08-20' ) }
+ afterText="08/01/2026"
+ beforeText="08/20/2026"
+ focusedInput="startDate"
+ onUpdate={ () => {} }
+ shortDateFormat="MM/DD/YYYY"
+ />
+ );
+
+const getDayLabels = ( container ) =>
+ Array.from(
+ container.querySelectorAll( 'td.CalendarDay[aria-label]' )
+ ).map( ( day ) => day.getAttribute( 'aria-label' ) );
+
+// WordPress seeds moment's long date formats with the site's PHP `date_format`
+// option, which moment reads as its own tokens.
+const seedLongDateFormat = ( format ) =>
+ moment.updateLocale( moment.locale(), {
+ longDateFormat: { LL: format },
+ } );
+
+// 'F j, Y' is the WordPress default, and moment prints its tokens literally.
+// 'd/m/Y' is worse: moment reads `d` as the weekday index and `m` as minutes,
+// so it renders a plausible but wrong date.
+const phpDateFormats = [ 'F j, Y', 'd/m/Y' ];
+
+describe( 'DateRange', () => {
+ let originalLongDateFormat;
+
+ beforeEach( () => {
+ originalLongDateFormat = moment.localeData().longDateFormat( 'LL' );
+ } );
+
+ afterEach( () => {
+ seedLongDateFormat( originalLongDateFormat );
+ resetLocaleData();
+ } );
+
+ it.each( phpDateFormats )(
+ 'labels calendar days with a real date when `LL` is "%s"',
+ ( phpDateFormat ) => {
+ seedLongDateFormat( phpDateFormat );
+
+ const { container } = renderDateRange();
+ const labels = getDayLabels( container );
+
+ expect( labels ).toContain( 'Selected. Saturday, August 1, 2026' );
+ expect( labels ).toContain(
+ 'Select Wednesday, August 26, 2026 as a start date.'
+ );
+ }
+ );
+
+ it.each( phpDateFormats )(
+ 'labels every day with a real date when `LL` is "%s"',
+ ( phpDateFormat ) => {
+ seedLongDateFormat( phpDateFormat );
+
+ const { container } = renderDateRange();
+ const labels = getDayLabels( container );
+
+ expect( labels.length ).toBeGreaterThan( 0 );
+ labels.forEach( ( label ) => {
+ expect( label ).toMatch(
+ /[A-Z][a-z]+day, [A-Z][a-z]+ \d{1,2}, \d{4}/
+ );
+ } );
+ }
+ );
+
+ it( 'labels calendar days with the translated format', () => {
+ setLocaleData(
+ { 'dddd, MMMM D, YYYY': [ 'dddd, D. MMMM YYYY' ] },
+ 'woocommerce'
+ );
+
+ const { container } = renderDateRange();
+ const labels = getDayLabels( container );
+
+ expect( labels ).toContain( 'Selected. Saturday, 1. August 2026' );
+ } );
+} );
diff --git a/plugins/woocommerce/changelog/fix-calendar-day-aria-label-format b/plugins/woocommerce/changelog/fix-calendar-day-aria-label-format
new file mode 100644
index 00000000000..84ce1b7ce0c
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-calendar-day-aria-label-format
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Analytics: fix the custom date range calendar reading out the wrong date to screen reader users.