From d3525f7c6cd42c2641423472a651a34f9fd75b90 Mon Sep 17 00:00:00 2001
From: Vicente Canales <1157901+vcanales@users.noreply.github.com>
Date: Mon, 7 Dec 2020 11:45:31 -0300
Subject: [PATCH 1/3] Revert date changes from branch 'replace-moment' (#27550)
* Revert "Fix c and r formats, add tests."
This reverts commit 1f737025dd65449707f3e8cde789ac0b9740ff7e.
* Revert date changes from branch 'replace-moment'
* fix merge error
---
package-lock.json | 32 +-
packages/date/README.md | 16 +-
packages/date/package.json | 2 -
packages/date/src/index.js | 508 +++++++-----------
packages/date/src/test/index.js | 326 -----------
.../src/components/post-schedule/index.js | 1 -
.../src/components/post-schedule/label.js | 18 +-
7 files changed, 215 insertions(+), 688 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 5417bc68aa9e2..86d373db12677 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17565,22 +17565,8 @@
"version": "file:packages/date",
"requires": {
"@babel/runtime": "^7.11.2",
- "date-fns": "^2.16.1",
- "date-fns-tz": "^1.0.12",
"moment": "^2.22.1",
"moment-timezone": "^0.5.31"
- },
- "dependencies": {
- "date-fns": {
- "version": "2.16.1",
- "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.16.1.tgz",
- "integrity": "sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ=="
- },
- "date-fns-tz": {
- "version": "1.0.12",
- "resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-1.0.12.tgz",
- "integrity": "sha512-Ca+9pjGkU90XDHnclfSjz9o7g/ZqyYyYI0aCYmbf65P75oy8gktuaRslO3UPXl3ADgAnF9/KCykQkpU3/xvtWQ=="
- }
}
},
"@wordpress/dependency-extraction-webpack-plugin": {
@@ -30479,12 +30465,6 @@
"integrity": "sha1-nfflL7Kgyw+4kFjugMMQQiXzfh0=",
"dev": true
},
- "date-fns": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz",
- "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==",
- "dev": true
- },
"has-ansi": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz",
@@ -31912,6 +31892,12 @@
"whatwg-url": "^7.0.0"
}
},
+ "date-fns": {
+ "version": "1.29.0",
+ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.29.0.tgz",
+ "integrity": "sha512-lbTXWZ6M20cWH8N9S6afb0SBm6tMk+uUg6z3MqHPKE9atmsY3kJkTm8vKe93izJ2B2+q5MV990sM2CHgtAZaOw==",
+ "dev": true
+ },
"dateformat": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz",
@@ -45399,12 +45385,6 @@
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
- },
- "date-fns": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz",
- "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==",
- "dev": true
}
}
},
diff --git a/packages/date/README.md b/packages/date/README.md
index 4708927a01033..954ea388ece67 100644
--- a/packages/date/README.md
+++ b/packages/date/README.md
@@ -28,7 +28,7 @@ _Related_
_Parameters_
- _dateFormat_ `string`: PHP-style formatting string. See php.net/date.
-- _dateValue_ `(Date|string|null)`: Date object or ISO string, parsable by moment.js.
+- _dateValue_ `(Date|string|Moment|null)`: Date object or string, parsable by moment.js.
- _timezone_ `(string|number|null)`: Timezone to output result in or a UTC offset. Defaults to timezone from site.
_Returns_
@@ -50,7 +50,7 @@ _Related_
_Parameters_
- _dateFormat_ `string`: PHP-style formatting string. See php.net/date.
-- _dateValue_ `(Date|string|null)`: Date object or string, parsable by moment.js.
+- _dateValue_ `(Date|string|Moment|null)`: Date object or string, parsable by moment.js.
- _timezone_ `(string|number|boolean|null)`: Timezone to output result in or a UTC offset. Defaults to timezone from site. Notice: `boolean` is effectively deprecated, but still supported for backward compatibility reasons.
_Returns_
@@ -64,7 +64,7 @@ Formats a date. Does not alter the date's timezone.
_Parameters_
- _dateFormat_ `string`: PHP-style formatting string. See php.net/date.
-- _dateValue_ `(Date|string|null)`: Date object or ISO string.
+- _dateValue_ `(Date|string|Moment|null)`: Date object or string, parsable by moment.js.
_Returns_
@@ -89,7 +89,7 @@ Formats a date (like `date()` in PHP), in the UTC timezone.
_Parameters_
- _dateFormat_ `string`: PHP-style formatting string. See php.net/date.
-- _dateValue_ `(Date|string|null)`: Date object or ISO string.
+- _dateValue_ `(Date|string|Moment|null)`: Date object or string, parsable by moment.js.
_Returns_
@@ -103,7 +103,7 @@ and using the UTC timezone.
_Parameters_
- _dateFormat_ `string`: PHP-style formatting string. See php.net/date.
-- _dateValue_ `(Date|string|null)`: Date object or ISO string.
+- _dateValue_ `(Date|string|Moment|null)`: Date object or string, parsable by moment.js.
_Returns_
@@ -115,7 +115,7 @@ Check whether a date is considered in the future according to the WordPress sett
_Parameters_
-- _dateValue_ `(string|Date)`: Date String or Date object in the Defined WP Timezone.
+- _dateValue_ `string`: Date String or Date object in the Defined WP Timezone.
_Returns_
@@ -129,10 +129,6 @@ _Parameters_
- _dateSettings_ `Object`: Settings, including locale data.
-# **zonedTimeToUtc**
-
-Undocumented declaration.
-
diff --git a/packages/date/package.json b/packages/date/package.json
index 30cd0f0481140..5f64325f5fde9 100644
--- a/packages/date/package.json
+++ b/packages/date/package.json
@@ -23,8 +23,6 @@
"react-native": "src/index",
"dependencies": {
"@babel/runtime": "^7.11.2",
- "date-fns": "^2.16.1",
- "date-fns-tz": "^1.0.12",
"moment": "^2.22.1",
"moment-timezone": "^0.5.31"
},
diff --git a/packages/date/src/index.js b/packages/date/src/index.js
index 510ae811d0a3a..4145e3fcd5223 100644
--- a/packages/date/src/index.js
+++ b/packages/date/src/index.js
@@ -1,23 +1,13 @@
/**
* External dependencies
*/
-import {
- addHours,
- format as dateFnsFormat,
- getDaysInMonth,
- isFuture,
- isLeapYear,
- parseISO,
-} from 'date-fns';
-import {
- format as formatTZ,
- utcToZonedTime,
- zonedTimeToUtc,
- toDate,
-} from 'date-fns-tz';
-import originalLocale from 'date-fns/locale/en-US/index';
-import buildLocalizeFn from 'date-fns/locale/_lib/buildLocalizeFn';
-import buildFormatLongFn from 'date-fns/locale/_lib/buildFormatLongFn';
+import momentLib from 'moment';
+import 'moment-timezone/moment-timezone';
+import 'moment-timezone/moment-timezone-utils';
+
+/** @typedef {import('moment').Moment} Moment */
+
+const WP_ZONE = 'WP';
// This regular expression tests positive for UTC offsets as described in ISO 8601.
// See: https://en.wikipedia.org/wiki/ISO_8601#Time_offsets_from_UTC
@@ -85,13 +75,14 @@ let settings = {
},
},
formats: {
- time: 'g:i a',
+ time: 'g: i a',
date: 'F j, Y',
datetime: 'F j, Y g: i a',
datetimeAbbreviated: 'M j, Y g: i a',
},
timezone: { offset: '0', string: '', abbr: '' },
};
+
/**
* Adds a locale to moment, using the format supplied by `wp_localize_script()`.
*
@@ -99,6 +90,41 @@ let settings = {
*/
export function setSettings( dateSettings ) {
settings = dateSettings;
+
+ // Backup and restore current locale.
+ const currentLocale = momentLib.locale();
+ momentLib.updateLocale( dateSettings.l10n.locale, {
+ // Inherit anything missing from the default locale.
+ parentLocale: currentLocale,
+ months: dateSettings.l10n.months,
+ monthsShort: dateSettings.l10n.monthsShort,
+ weekdays: dateSettings.l10n.weekdays,
+ weekdaysShort: dateSettings.l10n.weekdaysShort,
+ meridiem( hour, minute, isLowercase ) {
+ if ( hour < 12 ) {
+ return isLowercase
+ ? dateSettings.l10n.meridiem.am
+ : dateSettings.l10n.meridiem.AM;
+ }
+ return isLowercase
+ ? dateSettings.l10n.meridiem.pm
+ : dateSettings.l10n.meridiem.PM;
+ },
+ longDateFormat: {
+ LT: dateSettings.formats.time,
+ LTS: null,
+ L: null,
+ LL: dateSettings.formats.date,
+ LLL: dateSettings.formats.datetime,
+ LLLL: null,
+ },
+ // From human_time_diff?
+ // Set to `(number, withoutSuffix, key, isFuture) => {}` instead.
+ relativeTime: dateSettings.l10n.relative,
+ } );
+ momentLib.locale( currentLocale );
+
+ setupWPTimezone();
}
/**
@@ -110,6 +136,18 @@ export function __experimentalGetSettings() {
return settings;
}
+function setupWPTimezone() {
+ // Create WP timezone based off dateSettings.
+ momentLib.tz.add(
+ momentLib.tz.pack( {
+ name: WP_ZONE,
+ abbrs: [ WP_ZONE ],
+ untils: [ null ],
+ offsets: [ -settings.timezone.offset * 60 || 0 ],
+ } )
+ );
+}
+
// Date constants.
/**
* Number of seconds in one minute.
@@ -144,46 +182,41 @@ const HOUR_IN_SECONDS = 60 * MINUTE_IN_SECONDS;
*/
const formatMap = {
// Day
- d: 'dd',
- D: 'EEE',
- j: 'd',
- l: 'EEEE',
- N: 'i',
+ d: 'DD',
+ D: 'ddd',
+ j: 'D',
+ l: 'dddd',
+ N: 'E',
/**
* Gets the ordinal suffix.
*
- * @param {Date} dateValue Date ISO string or object.
+ * @param {Moment} momentDate Moment instance.
*
* @return {string} Formatted date.
*/
- S( dateValue ) {
- const num = dateFnsFormat( dateValue, 'd' );
- const withOrdinal = dateFnsFormat( dateValue, 'do' );
+ S( momentDate ) {
+ // Do - D
+ const num = momentDate.format( 'D' );
+ const withOrdinal = momentDate.format( 'Do' );
return withOrdinal.replace( num, '' );
},
- /**
- * Returns the day of the week (zero-indexed).
- *
- * @param {string} dateValue
- */
- w( dateValue ) {
- return `${ parseInt( dateFnsFormat( dateValue, 'i' ), 10 ) - 1 }`;
- },
+
+ w: 'd',
/**
* Gets the day of the year (zero-indexed).
*
- * @param {Date} dateValue Date ISO string or object.
+ * @param {Moment} momentDate Moment instance.
*
* @return {string} Formatted date.
*/
- z( dateValue ) {
+ z( momentDate ) {
// DDD - 1
- return `${ parseInt( dateFnsFormat( dateValue, 'DDD' ), 10 ) - 1 }`;
+ return '' + parseInt( momentDate.format( 'DDD' ), 10 ) - 1;
},
// Week
- W: 'II',
+ W: 'W',
// Month
F: 'MMMM',
@@ -193,58 +226,50 @@ const formatMap = {
/**
* Gets the days in the month.
*
- * @param {Date} dateValue Date ISO string or object.
+ * @param {Moment} momentDate Moment instance.
*
* @return {string} Formatted date.
*/
- t( dateValue ) {
- return getDaysInMonth( dateValue );
+ t( momentDate ) {
+ return momentDate.daysInMonth();
},
// Year
/**
* Gets whether the current year is a leap year.
*
- * @param {Date} dateValue Date ISO string or object.
+ * @param {Moment} momentDate Moment instance.
*
* @return {string} Formatted date.
*/
- L( dateValue ) {
- return isLeapYear( dateValue ) ? '1' : '0';
+ L( momentDate ) {
+ return momentDate.isLeapYear() ? '1' : '0';
},
- o: 'R',
- Y: 'yyyy',
- y: 'yy',
+ o: 'GGGG',
+ Y: 'YYYY',
+ y: 'YY',
// Time
- a( dateValue ) {
- return formatTZ( dateValue, 'aa', {
- timeZone: getActualTimezone(),
- } ).toLowerCase();
- },
- A: 'bb',
+ a: 'a',
+ A: 'A',
/**
- * Gets the given time in Swatch Internet Time (.beats).
+ * Gets the current time in Swatch Internet Time (.beats).
*
- * @param {Date} dateValue Date ISO string or object.
+ * @param {Moment} momentDate Moment instance.
*
* @return {string} Formatted date.
*/
- B( dateValue ) {
- const parsedDate = addHours( zonedTimeToUtc( dateValue ), 1 );
- const seconds = parseInt( dateFnsFormat( parsedDate, 's' ), 10 ),
- minutes = parseInt( dateFnsFormat( parsedDate, 'm' ), 10 ),
- hours = parseInt( dateFnsFormat( parsedDate, 'H' ), 10 );
-
- /*
- * Rounding up to match results on the same timestamp using
- * PHP's date_format.
- */
- return Math.ceil(
+ B( momentDate ) {
+ const timezoned = momentLib( momentDate ).utcOffset( 60 );
+ const seconds = parseInt( timezoned.format( 's' ), 10 ),
+ minutes = parseInt( timezoned.format( 'm' ), 10 ),
+ hours = parseInt( timezoned.format( 'H' ), 10 );
+ return parseInt(
( seconds +
minutes * MINUTE_IN_SECONDS +
hours * HOUR_IN_SECONDS ) /
- 86.4
+ 86.4,
+ 10
);
},
g: 'h',
@@ -256,49 +281,32 @@ const formatMap = {
u: 'SSSSSS',
v: 'SSS',
// Timezone
- /**
- * Return the timezone identifier for the given date.
- *
- * @param {Date} dateValue Date ISO string or object.
- *
- * @return {string} Formatted date.
- */
- e( dateValue ) {
- return formatTZ( dateValue, 'zzzz', { timeZone: getActualTimezone() } );
- },
+ e: 'zz',
/**
* Gets whether the timezone is in DST currently.
*
+ * @param {Moment} momentDate Moment instance.
*
* @return {string} Formatted date.
*/
- I() {
- return ''; // @todo
- },
- O( dateValue ) {
- return formatTZ( dateValue, 'xx', { timeZone: getActualTimezone() } );
- },
- P( dateValue ) {
- return formatTZ( dateValue, 'xxx', { timeZone: getActualTimezone() } );
- },
- T( dateValue ) {
- return formatTZ( dateValue, 'z', { timeZone: getActualTimezone() } );
+ I( momentDate ) {
+ return momentDate.isDST() ? '1' : '0';
},
+ O: 'ZZ',
+ P: 'Z',
+ T: 'z',
/**
* Gets the timezone offset in seconds.
*
- * @param {Date|string} dateValue Date ISO string or object.
+ * @param {Moment} momentDate Moment instance.
*
* @return {string} Formatted date.
*/
- Z( dateValue ) {
+ Z( momentDate ) {
// Timezone offset in seconds.
- const offset = dateFnsFormat(
- utcToZonedTime( dateValue, 'UTC' ),
- 'XXX'
- );
+ const offset = momentDate.format( 'Z' );
const sign = offset[ 0 ] === '-' ? -1 : 1;
- const parts = offset.substring( 1 ).split( ':' ).map( Number );
+ const parts = offset.substring( 1 ).split( ':' );
return (
sign *
( parts[ 0 ] * HOUR_IN_MINUTES + parts[ 1 ] ) *
@@ -306,203 +314,50 @@ const formatMap = {
);
},
// Full date/time
- c( dateValue ) {
- return formatTZ(
- utcToZonedTime(
- zonedTimeToUtc( dateValue, getActualTimezone() ),
- 'UTC'
- ), // Offsets the time to the correct timezone
- "yyyy-MM-dd'T'HH:mm:ssXXX",
- {
- timeZone: getActualTimezone(), // Adds the timezone offset to the Date object that will be formatted.
- }
- );
- }, // .toISOString
- r( dateValue ) {
- return formatTZ(
- utcToZonedTime(
- zonedTimeToUtc( dateValue, getActualTimezone() ),
- 'UTC'
- ), // Offsets the time to the correct timezone
- 'iii, d MMM yyyy HH:mm:ss XX',
- {
- timeZone: getActualTimezone(), // Adds the timezone offset to the Date object that will be formatted.
- }
- );
- },
- U( dateValue ) {
- return formatTZ(
- zonedTimeToUtc( dateValue, getActualTimezone() ),
- 't'
- );
- },
+ c: 'YYYY-MM-DDTHH:mm:ssZ', // .toISOString
+ r: 'ddd, D MMM YYYY HH:mm:ss ZZ',
+ U: 'X',
};
/**
- * Applies map of PHP formatting tokens into date-fns formatting tokens to the given format and date.
+ * Formats a date. Does not alter the date's timezone.
+ *
+ * @param {string} dateFormat PHP-style formatting string.
+ * See php.net/date.
+ * @param {Date|string|Moment|null} dateValue Date object or string,
+ * parsable by moment.js.
*
- * @param {string} formatString
- * @param {Date|string} dateValue
+ * @return {string} Formatted date.
*/
-function translateFormat( formatString, dateValue ) {
+export function format( dateFormat, dateValue = new Date() ) {
let i, char;
let newFormat = [];
-
- const parsedDate =
- typeof dateValue === 'string' ? parseISO( dateValue ) : dateValue;
-
- for ( i = 0; i < formatString.length; i++ ) {
- char = formatString[ i ];
+ const momentDate = momentLib( dateValue );
+ for ( i = 0; i < dateFormat.length; i++ ) {
+ char = dateFormat[ i ];
// Is this an escape?
if ( '\\' === char ) {
// Add next character, then move on.
i++;
- newFormat.push( "'" + formatString[ i ] + "'" );
+ newFormat.push( '[' + dateFormat[ i ] + ']' );
continue;
}
if ( char in formatMap ) {
if ( typeof formatMap[ char ] !== 'string' ) {
// If the format is a function, call it.
- newFormat.push( "'" + formatMap[ char ]( parsedDate ) + "'" );
+ newFormat.push( '[' + formatMap[ char ]( momentDate ) + ']' );
} else {
// Otherwise, add as a formatting string.
newFormat.push( formatMap[ char ] );
}
} else {
- newFormat.push( char );
+ newFormat.push( '[' + char + ']' );
}
}
// Join with [] between to separate characters, and replace
// unneeded separators with static text.
-
- newFormat = newFormat.join( '' );
-
- return newFormat;
-}
-
-/**
- * Build date-fns locale settings from WordPress localization settings.
- */
-function getLocalizationSettings() {
- const monthValues = {
- abbreviated: settings.l10n.monthsShort,
- wide: settings.l10n.months,
- };
-
- const dayValues = {
- abbreviated: settings.l10n.weekdaysShort,
- wide: settings.l10n.weekdays,
- };
-
- return {
- ...originalLocale.localize,
- month: buildLocalizeFn( {
- values: monthValues,
- defaultWidth: 'wide',
- } ),
- day: buildLocalizeFn( {
- values: dayValues,
- defaultWidth: 'wide',
- } ),
- formatLong: {
- date: buildFormatLongFn( {
- formats: {
- full: settings.formats.date,
- defaultWidth: 'full',
- },
- } ),
- time: buildFormatLongFn( {
- formats: {
- full: settings.formats.time,
- defaultWidth: 'full',
- },
- } ),
- dateTime: buildFormatLongFn( {
- formats: {
- full: settings.formats.datetime,
- short: settings.formats.datetimeAbbreviated,
- defaultWidth: 'full',
- },
- } ),
- },
- };
-}
-
-/**
- * Returns whether a certain UTC offset is valid or not.
- *
- * @param {number|string} offset a UTC offset.
- *
- * @return {boolean} whether a certain UTC offset is valid or not.
- */
-function isValidUTCOffset( offset ) {
- return VALID_UTC_OFFSET.test( offset );
-}
-
-/**
- * Transform the given integer into a valid UTC Offset in hours.
- *
- * @param {number} offset A UTC offset as an integer
- */
-function integerToUTCOffset( offset ) {
- const offsetInHours = offset > 23 ? offset / 60 : offset;
- const sign = offset < 0 ? '-' : '+';
- const absoluteOffset =
- offsetInHours < 0 ? offsetInHours * -1 : offsetInHours;
-
- return offsetInHours < 10
- ? `${ sign }0${ absoluteOffset }`
- : `${ sign }${ absoluteOffset }`;
-}
-
-/**
- * Determines whether or not the given value can be parsed as a UTC offset,
- * by checking if it is parseable as an integer and if it isn't a
- * valid UTC offset already.
- *
- * @param {string} offset An offset as an integer or a string.
- */
-function shouldParseAsUTCOffset( offset ) {
- const isNumber = ! Number.isNaN( Number.parseInt( offset, 10 ) );
- return isNumber && ! isValidUTCOffset( offset );
-}
-
-/**
- * Get a properly formatted timezone from a timezone string or offset.
- * Return system timezone or offset if no timezone was given.
- *
- * @param {string} timezone
- */
-function getActualTimezone( timezone = '' ) {
- if ( ! timezone ) {
- const { string, offset } = settings.timezone;
-
- if ( string ) {
- return string;
- }
-
- if ( shouldParseAsUTCOffset( offset ) ) {
- return integerToUTCOffset( offset );
- }
- }
-
- return shouldParseAsUTCOffset( timezone )
- ? integerToUTCOffset( timezone )
- : timezone;
-}
-
-/**
- * Formats a date. Does not alter the date's timezone.
- *
- * @param {string} dateFormat PHP-style formatting string.
- * See php.net/date.
- * @param {Date|string|null} dateValue Date object or ISO string.
- *
- * @return {string} Formatted date.
- */
-export function format( dateFormat, dateValue = new Date() ) {
- const formatString = translateFormat( dateFormat, dateValue );
- return dateFnsFormat( new Date( dateValue ), formatString );
+ newFormat = newFormat.join( '[]' );
+ return momentDate.format( newFormat );
}
/**
@@ -510,7 +365,7 @@ export function format( dateFormat, dateValue = new Date() ) {
*
* @param {string} dateFormat PHP-style formatting string.
* See php.net/date.
- * @param {Date|string|null} dateValue Date object or ISO string, parsable
+ * @param {Date|string|Moment|null} dateValue Date object or string, parsable
* by moment.js.
* @param {string|number|null} timezone Timezone to output result in or a
* UTC offset. Defaults to timezone from
@@ -522,10 +377,8 @@ export function format( dateFormat, dateValue = new Date() ) {
* @return {string} Formatted date in English.
*/
export function date( dateFormat, dateValue = new Date(), timezone ) {
- return format(
- dateFormat,
- utcToZonedTime( dateValue, getActualTimezone( timezone ) )
- );
+ const dateMoment = buildMoment( dateValue, timezone );
+ return format( dateFormat, dateMoment );
}
/**
@@ -533,12 +386,14 @@ export function date( dateFormat, dateValue = new Date(), timezone ) {
*
* @param {string} dateFormat PHP-style formatting string.
* See php.net/date.
- * @param {Date|string|null} dateValue Date object or ISO string.
+ * @param {Date|string|Moment|null} dateValue Date object or string,
+ * parsable by moment.js.
*
* @return {string} Formatted date in English.
*/
export function gmdate( dateFormat, dateValue = new Date() ) {
- return format( dateFormat, utcToZonedTime( dateValue, 'UTC' ) );
+ const dateMoment = momentLib( dateValue ).utc();
+ return format( dateFormat, dateMoment );
}
/**
@@ -549,7 +404,7 @@ export function gmdate( dateFormat, dateValue = new Date() ) {
*
* @param {string} dateFormat PHP-style formatting string.
* See php.net/date.
- * @param {Date|string|null} dateValue Date object or string, parsable by
+ * @param {Date|string|Moment|null} dateValue Date object or string, parsable by
* moment.js.
* @param {string|number|boolean|null} timezone Timezone to output result in or a
* UTC offset. Defaults to timezone from
@@ -571,22 +426,9 @@ export function dateI18n( dateFormat, dateValue = new Date(), timezone ) {
timezone = undefined;
}
- return formatTZ(
- utcToZonedTime(
- zonedTimeToUtc( dateValue, getActualTimezone() ),
- getActualTimezone( timezone )
- ),
- translateFormat( dateFormat, dateValue ),
- {
- timeZone: getActualTimezone( timezone ),
- locale: {
- ...originalLocale,
- locale: settings.l10n.locale,
- code: settings.l10n.locale,
- localize: getLocalizationSettings(),
- },
- }
- );
+ const dateMoment = buildMoment( dateValue, timezone );
+ dateMoment.locale( settings.l10n.locale );
+ return format( dateFormat, dateMoment );
}
/**
@@ -595,37 +437,29 @@ export function dateI18n( dateFormat, dateValue = new Date(), timezone ) {
*
* @param {string} dateFormat PHP-style formatting string.
* See php.net/date.
- * @param {Date|string|null} dateValue Date object or ISO string.
+ * @param {Date|string|Moment|null} dateValue Date object or string,
+ * parsable by moment.js.
*
* @return {string} Formatted date.
*/
export function gmdateI18n( dateFormat, dateValue = new Date() ) {
- return formatTZ(
- utcToZonedTime( dateValue, 'UTC' ),
- translateFormat( dateFormat, dateValue ),
- {
- timeZone: 'UTC',
- locale: {
- ...originalLocale,
- locale: settings.l10n.locale,
- code: settings.l10n.locale,
- localize: getLocalizationSettings(),
- },
- }
- );
+ const dateMoment = momentLib( dateValue ).utc();
+ dateMoment.locale( settings.l10n.locale );
+ return format( dateFormat, dateMoment );
}
/**
* Check whether a date is considered in the future according to the WordPress settings.
*
- * @param {string|Date} dateValue Date String or Date object in the Defined WP Timezone.
+ * @param {string} dateValue Date String or Date object in the Defined WP Timezone.
*
* @return {boolean} Is in the future.
*/
export function isInTheFuture( dateValue ) {
- const dateObject = toDate( dateValue, { timeZone: getActualTimezone() } );
+ const now = momentLib.tz( WP_ZONE );
+ const momentObject = momentLib.tz( dateValue, WP_ZONE );
- return isFuture( dateObject );
+ return momentObject.isAfter( now );
}
/**
@@ -636,8 +470,58 @@ export function isInTheFuture( dateValue ) {
* @return {Date} Date
*/
export function getDate( dateString ) {
- const actualDate = dateString ? new Date( dateString ) : new Date();
- return toDate( actualDate, { timeZone: getActualTimezone() } );
+ if ( ! dateString ) {
+ return momentLib.tz( WP_ZONE ).toDate();
+ }
+
+ return momentLib.tz( dateString, WP_ZONE ).toDate();
+}
+
+/**
+ * Creates a moment instance using the given timezone or, if none is provided, using global settings.
+ *
+ * @param {Date|string|Moment|null} dateValue Date object or string, parsable
+ * by moment.js.
+ * @param {string|number|null} timezone Timezone to output result in or a
+ * UTC offset. Defaults to timezone from
+ * site.
+ *
+ * @see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
+ * @see https://en.wikipedia.org/wiki/ISO_8601#Time_offsets_from_UTC
+ *
+ * @return {Moment} a moment instance.
+ */
+function buildMoment( dateValue, timezone = '' ) {
+ const dateMoment = momentLib( dateValue );
+
+ if ( timezone && ! isUTCOffset( timezone ) ) {
+ return dateMoment.tz( timezone );
+ }
+
+ if ( timezone && isUTCOffset( timezone ) ) {
+ return dateMoment.utcOffset( timezone );
+ }
+
+ if ( settings.timezone.string ) {
+ return dateMoment.tz( settings.timezone.string );
+ }
+
+ return dateMoment.utcOffset( settings.timezone.offset );
+}
+
+/**
+ * Returns whether a certain UTC offset is valid or not.
+ *
+ * @param {number|string} offset a UTC offset.
+ *
+ * @return {boolean} whether a certain UTC offset is valid or not.
+ */
+function isUTCOffset( offset ) {
+ if ( 'number' === typeof offset ) {
+ return true;
+ }
+
+ return VALID_UTC_OFFSET.test( offset );
}
-export { zonedTimeToUtc };
+setupWPTimezone();
diff --git a/packages/date/src/test/index.js b/packages/date/src/test/index.js
index 2c5479cfcd4c3..d9b832e528ce6 100644
--- a/packages/date/src/test/index.js
+++ b/packages/date/src/test/index.js
@@ -191,332 +191,6 @@ describe( 'Function date', () => {
} );
} );
-// Custom formatting token functions, in order to support PHP formatting tokens
-describe( 'PHP Format Tokens', () => {
- it( 'should support "d" to obtain day of the month, 2 digits with leading zeroes', () => {
- const formattedDate = dateNoI18n( 'd', '2019-06-06T11:00:00.000Z' );
-
- expect( formattedDate ).toBe( '06' );
- } );
-
- it( 'should support "D" to obtain textual representation of a day, three letters', () => {
- const formattedDate = dateNoI18n( 'D', '2019-06-18T11:00:00.000Z' );
-
- expect( formattedDate ).toBe( 'Tue' );
- } );
-
- it( 'should support "j" to obtain day of the month without leading zeroes', () => {
- const formattedDate = dateNoI18n( 'j', '2019-06-06T11:00:00.000Z' );
-
- expect( formattedDate ).toBe( '6' );
- } );
-
- it( 'should support "l" to obtain full textual representation of the day of the week', () => {
- const formattedDate = dateNoI18n( 'l', '2019-06-18T11:00:00.000Z' );
-
- expect( formattedDate ).toBe( 'Tuesday' );
- } );
-
- it( 'should support "N" to obtain ISO-8601 numeric representation of the day of the week', () => {
- const formattedDate = dateNoI18n( 'N', '2019-06-18T11:00:00.000Z' );
-
- expect( formattedDate ).toBe( '2' ); // 2 === Tuesday
- } );
-
- it( 'should support "S" to obtain ordinal suffix of day of the month', () => {
- const formattedDate = dateNoI18n( 'S', '2019-06-18T11:00:00.000Z' );
-
- // th for 18th
- expect( formattedDate ).toBe( 'th' );
- } );
-
- it( 'should support "w" to obtain day of the week starting from 0', () => {
- const formattedDate = dateNoI18n( 'w', '2020-01-01T12:00:00.000Z' ); // Wednesday Jan 1st, 2020
-
- expect( formattedDate ).toBe( '2' );
- } );
-
- it( 'should support "z" to obtain zero-indexed day of the year', () => {
- const formattedDate = dateNoI18n( 'z', '2019-01-01' );
-
- expect( formattedDate ).toBe( '0' );
- } );
-
- it( 'should support "W" to obtain ISO-8601 week number of year, weeks starting on Monday', () => {
- const formattedDate = dateNoI18n( 'W', '2019-01-06T11:00:00.000Z' );
-
- expect( formattedDate ).toBe( '01' );
- } );
-
- it( 'should support "F" to obtain a full textual representation of a month', () => {
- const formattedDate = dateNoI18n( 'F', '2019-06-18T11:00:00.000Z' );
-
- expect( formattedDate ).toBe( 'June' );
- } );
-
- it( 'should support "m" to obtain the numeric representation of a month, with leading zeroes', () => {
- const formattedDate = dateNoI18n( 'm', '2019-06-18T11:00:00.000Z' );
-
- expect( formattedDate ).toBe( '06' );
- } );
-
- it( 'should support "M" to obtain a three letter textual representation of a month', () => {
- const formattedDate = dateNoI18n( 'M', '2019-06-18T11:00:00.000Z' );
-
- expect( formattedDate ).toBe( 'Jun' );
- } );
-
- it( 'should "n" to obtain the numeric representation of a month without leading zeroes', () => {
- const formattedDate = dateNoI18n( 'n', '2019-06-18T11:00:00.000Z' );
-
- expect( formattedDate ).toBe( '6' );
- } );
-
- it( 'should support "t" to obtain the days in a given month', () => {
- const formattedDate = dateNoI18n( 't', '2019-02' );
-
- expect( formattedDate ).toBe( '28' );
- } );
-
- it( 'should support "L" to obtain whether or not the year is a leap year', () => {
- const formattedDate = dateNoI18n( 'L', '2020' );
-
- expect( formattedDate ).toBe( '1' );
- } );
-
- it( 'should support "o" to obtain the ISO-8601 week-numbering year. This has the same value as Y, except that if the ISO week number (W) belongs to the previous or next year, that year is used instead.', () => {
- const formattedDate = dateNoI18n( 'o', '2019-01-01T11:00:00.000Z' );
- const formattedDatePreviousYear = dateNoI18n(
- 'o',
- '2017-01-01T11:00:00.000Z'
- ); // ISO week number belongs to previous year
-
- expect( formattedDate ).toBe( '2019' );
- expect( formattedDatePreviousYear ).toBe( '2016' );
- } );
-
- it( 'should support "Y" to obtain a full numeric representation of a year', () => {
- const formattedDate = dateNoI18n( 'Y', '2019-06-18T11:00:00.000Z' );
-
- expect( formattedDate ).toBe( '2019' );
- } );
-
- it( 'should support "y" to obtain a two digit representation of a year', () => {
- const formattedDate = dateNoI18n( 'y', '2019-06-18T11:00:00.000Z' );
-
- expect( formattedDate ).toBe( '19' );
- } );
-
- it( 'should support "a" to obtain a lowercase ante meridiem and post meridiem', () => {
- const formattedDate = dateNoI18n( 'a', '2019-06-18T11:00:00.000Z' );
-
- expect( formattedDate ).toBe( 'am' );
- } );
-
- it( 'should support "A" to obtain uppercase ante meridiem and post meridiem', () => {
- const formattedDate = dateNoI18n( 'A', '2019-06-18T11:00:00.000Z' );
-
- expect( formattedDate ).toBe( 'AM' );
- } );
-
- it( 'should support "B" to obtain the time in Swatch Internet Time (.beats)', () => {
- const formattedDate = dateNoI18n( 'B', '2020-10-09T11:00:00.000Z' );
-
- expect( formattedDate ).toBe( '500' );
- } );
-
- it( 'should support "g" to obtain the 12-hour format of an hour without leading zeroes', () => {
- const formattedDate = dateNoI18n( 'g', '2019-06-18T14:00:00.000Z' );
-
- expect( formattedDate ).toBe( '2' );
- } );
-
- it( 'should support "G" to obtain the 24-hour format of an hour without leading zeroes', () => {
- const formattedDate = dateNoI18n( 'G', '2019-06-18T09:00:00.000Z' );
-
- expect( formattedDate ).toBe( '9' );
- } );
-
- it( 'should support "h" 12-hour format of an hour with leading zeroes', () => {
- const formattedDate = dateNoI18n( 'h', '2019-06-18T14:00:00.000Z' );
-
- expect( formattedDate ).toBe( '02' );
- } );
-
- it( 'should support "H" 24-hour format of an hour with leading zeroes', () => {
- const formattedDate = dateNoI18n( 'H', '2019-06-18T09:00:00.000Z' );
-
- expect( formattedDate ).toBe( '09' );
- } );
-
- it( 'should support "i" to obtain the minutes with leading zeroes', () => {
- const formattedDate = dateNoI18n( 'i', '2019-06-18T11:01:00.000Z' );
-
- expect( formattedDate ).toBe( '01' );
- } );
-
- it( 'should support "s" to obtain seconds with leading zeroes', () => {
- const formattedDate = dateNoI18n( 's', '2019-06-18T11:00:04.000Z' );
-
- expect( formattedDate ).toBe( '04' );
- } );
-
- /**
- * This format is not fully compatible with JavaScript out of the box,
- * as Date doesn't support sub-millisecond precision.
- */
- it( 'should support "u" to obtain microseconds', () => {
- const formattedDate = dateNoI18n(
- 'u',
- '2019-06-18T11:00:00.123456789Z'
- );
-
- expect( formattedDate ).toBe( '123000' );
- } );
-
- it( 'should support "v" to obtain milliseconds', () => {
- const formattedDate = dateNoI18n(
- 'v',
- '2019-06-18T11:00:00.123456789Z'
- );
-
- expect( formattedDate ).toBe( '123' );
- } );
-
- it( 'should support "e" to obtain timezone identifier', () => {
- const settings = __experimentalGetSettings();
-
- setSettings( {
- ...settings,
- timezone: { offset: -4, string: 'America/New_York' },
- } );
-
- const formattedDate = dateNoI18n( 'e', '2020-10-09T11:00:00.000Z' );
-
- expect( formattedDate ).toBe( 'Eastern Daylight Time' );
-
- setSettings( settings );
- } );
-
- it.skip( 'should support "I" to obtain whether or not the timezone is observing DST', () => {
- const formattedFall = dateNoI18n( 'I', '2020-10-09T11:00:00.000Z' );
-
- expect( formattedFall ).toBe( '1' );
-
- const formattedWinter = dateNoI18n( 'I', '2020-01-09T11:00:00.000Z' );
-
- expect( formattedWinter ).toBe( '0' );
- } );
-
- it( 'should support "O" to obtain difference to Greenwich time (GMT) without colon between hours and minutes', () => {
- const settings = __experimentalGetSettings();
-
- setSettings( {
- ...settings,
- timezone: { offset: -6 },
- } );
-
- const formattedDate = dateNoI18n( 'O', '2019-06-18T11:00:00.000Z' );
-
- expect( formattedDate ).toBe( '-0600' );
-
- setSettings( settings );
- } );
-
- it( 'should support "P" to obtain difference to Greenwich time (GMT) without colon between hours and minutes', () => {
- const settings = __experimentalGetSettings();
-
- setSettings( {
- ...settings,
- timezone: { offset: -6 },
- } );
-
- const formattedDate = dateNoI18n( 'P', '2019-06-18T11:00:00.000Z' );
-
- expect( formattedDate ).toBe( '-06:00' );
-
- setSettings( settings );
- } );
-
- it( 'should support "T" to obtain the timezone abbreviation for the given date', () => {
- const settings = __experimentalGetSettings();
-
- setSettings( {
- ...settings,
- timezone: { offset: -4, string: 'America/New_York' },
- } );
-
- const formattedDateStandard = dateNoI18n(
- 'T',
- '2020-01-01T11:00:00.000Z'
- );
-
- expect( formattedDateStandard ).toBe( 'EST' );
-
- setSettings( settings );
- } );
-
- it.skip( 'should support "Z" to obtain timezone offset in seconds', () => {
- const settings = __experimentalGetSettings();
-
- setSettings( {
- ...settings,
- timezone: { offset: -1 },
- } );
-
- const formattedDate = dateNoI18n( 'Z', '2020-10-09T11:00:00.000Z' );
-
- expect( formattedDate ).toBe( '3600' );
-
- setSettings( settings );
- } );
-
- it( 'should support "c" to obtain ISO 8601 date', () => {
- const settings = __experimentalGetSettings();
-
- setSettings( {
- ...settings,
- timezone: { offset: -5, string: 'America/Bogota' },
- } );
-
- const formattedDate = dateNoI18n( 'c', '2019-06-18T11:00:00.000Z' );
-
- expect( formattedDate ).toBe( '2019-06-18T11:00:00-05:00' );
-
- setSettings( settings );
- } );
-
- it( 'should support "r" RFC 2822 formatted date', () => {
- const settings = __experimentalGetSettings();
-
- setSettings( {
- ...settings,
- timezone: { offset: -5, string: 'America/Bogota' },
- } );
-
- const formattedDate = dateNoI18n( 'r', '2019-06-18T11:00:00.000Z' );
-
- expect( formattedDate ).toBe( 'Tue, 18 Jun 2019 11:00:00 -0500' );
-
- setSettings( settings );
- } );
-
- it( 'should support "U" to get epoc for given date', () => {
- const settings = __experimentalGetSettings();
-
- setSettings( {
- ...settings,
- timezone: { string: 'UTC' },
- } );
-
- const formattedDate = dateNoI18n( 'U', '2020-10-09T11:00:00.000Z' );
-
- expect( formattedDate ).toBe( '1602241200' );
-
- setSettings( settings );
- } );
-} );
-
describe( 'Function gmdate', () => {
it( 'should format date in English, ignoring locale settings', () => {
const settings = __experimentalGetSettings();
diff --git a/packages/editor/src/components/post-schedule/index.js b/packages/editor/src/components/post-schedule/index.js
index 115d5b96f1578..06266f1bd68d9 100644
--- a/packages/editor/src/components/post-schedule/index.js
+++ b/packages/editor/src/components/post-schedule/index.js
@@ -9,7 +9,6 @@ import { useRef } from '@wordpress/element';
export function PostSchedule( { date, onUpdateDate } ) {
const ref = useRef();
-
const settings = __experimentalGetSettings();
// To know if the current timezone is a 12 hour time with look for "a" in the time format
// We also make sure this a is not escaped by a "/"
diff --git a/packages/editor/src/components/post-schedule/label.js b/packages/editor/src/components/post-schedule/label.js
index d8a2a1b47bc2a..5f4c2cf7c5c77 100644
--- a/packages/editor/src/components/post-schedule/label.js
+++ b/packages/editor/src/components/post-schedule/label.js
@@ -2,22 +2,18 @@
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
-
-import { dateI18n, __experimentalGetSettings } from '@wordpress/date';
-
+import { format, __experimentalGetSettings } from '@wordpress/date';
import { withSelect } from '@wordpress/data';
export function PostScheduleLabel( { date, isFloating } ) {
- if ( isFloating || ! date ) {
- return __( 'Immediately' );
- }
-
const settings = __experimentalGetSettings();
- return dateI18n(
- `${ settings.formats.date } ${ settings.formats.time }`,
- date
- );
+ return date && ! isFloating
+ ? format(
+ `${ settings.formats.date } ${ settings.formats.time }`,
+ date
+ )
+ : __( 'Immediately' );
}
export default withSelect( ( select ) => {
From 3dafc9e9712b04a03738137907642238a1d79b58 Mon Sep 17 00:00:00 2001
From: Jon Surrell
Date: Thu, 3 Dec 2020 14:38:36 +0100
Subject: [PATCH 2/3] Popover: Fix issue with undefined getBoundingClientRect
(#27445)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Fix an issue where the `Popover` component throws a runtime `TypeError: Cannot read property 'getBoundingClientRect' of undefined.`
This may happen in WordPress if a Popover is rendered in an iframe. It's been observed by rendering a `BlockList` component containing `RichText` in an iframe 😵
The issue is that `instanceof` checks fail across iframe boundaries, where `anchorRef instanceof window.Element` fails because `anchorRef` when it _is_ an instance of Element in the frame but not `window.Element`.
Instead of `instanceof` checks that fail across iframe boundaries, check for expected methods as the predicate.
---
packages/components/src/popover/index.js | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/packages/components/src/popover/index.js b/packages/components/src/popover/index.js
index a71c6cc6ad895..6063c11c04e62 100644
--- a/packages/components/src/popover/index.js
+++ b/packages/components/src/popover/index.js
@@ -71,11 +71,17 @@ function computeAnchorRect(
return;
}
- if ( anchorRef instanceof window.Range ) {
+ // Duck-type to check if `anchorRef` is an instance of Range
+ // `anchorRef instanceof window.Range` checks will break across document boundaries
+ // such as in an iframe
+ if ( typeof anchorRef?.cloneRange === 'function' ) {
return getRectangleFromRange( anchorRef );
}
- if ( anchorRef instanceof window.Element ) {
+ // Duck-type to check if `anchorRef` is an instance of Element
+ // `anchorRef instanceof window.Element` checks will break across document boundaries
+ // such as in an iframe
+ if ( typeof anchorRef?.getBoundingClientRect === 'function' ) {
const rect = anchorRef.getBoundingClientRect();
if ( shouldAnchorIncludePadding ) {
From 94b3cabcf70cf18f2d304fdd2905d70fd7c1f848 Mon Sep 17 00:00:00 2001
From: Kai Hao
Date: Mon, 7 Dec 2020 11:01:10 +0800
Subject: [PATCH 3/3] Fallback to regular subscribe if the store doesn't exist
in useSelect (#27466)
---
.../src/components/use-select/test/index.js | 154 ++++++++++++++++++
packages/data/src/registry.js | 8 +
2 files changed, 162 insertions(+)
diff --git a/packages/data/src/components/use-select/test/index.js b/packages/data/src/components/use-select/test/index.js
index d5415191ffcdd..4b8d8a9ce35c5 100644
--- a/packages/data/src/components/use-select/test/index.js
+++ b/packages/data/src/components/use-select/test/index.js
@@ -283,6 +283,9 @@ describe( 'useSelect', () => {
expect( selectCount2 ).toHaveBeenCalledTimes( 3 );
expect( TestComponent ).toHaveBeenCalledTimes( 3 );
expect( testInstance.findByType( 'div' ).props.data ).toBe( 1 );
+
+ // Test if the unsubscribers get called correctly.
+ renderer.unmount();
} );
it( 'can subscribe to multiple stores at once', () => {
@@ -565,5 +568,156 @@ describe( 'useSelect', () => {
childCount: 0,
} );
} );
+
+ it( 'handles non-existing stores', () => {
+ registry.registerStore( 'store-1', counterStore );
+
+ let renderer;
+
+ const TestComponent = jest.fn( () => {
+ const state = useSelect(
+ ( select ) => ( {
+ count1: select( 'store-1' ).getCounter(),
+ blank: select( 'non-existing-store' )?.getCounter(),
+ } ),
+ []
+ );
+
+ return ;
+ } );
+
+ act( () => {
+ renderer = TestRenderer.create(
+
+
+
+ );
+ } );
+
+ const testInstance = renderer.root;
+
+ expect( testInstance.findByType( 'div' ).props.data ).toEqual( {
+ count1: 0,
+ blank: undefined,
+ } );
+
+ act( () => {
+ registry.dispatch( 'store-1' ).increment();
+ } );
+
+ expect( testInstance.findByType( 'div' ).props.data ).toEqual( {
+ count1: 1,
+ blank: undefined,
+ } );
+
+ // Test if the unsubscribers get called correctly.
+ renderer.unmount();
+ } );
+
+ it( 'handles registration of a non-existing store during rendering', () => {
+ let renderer;
+
+ const TestComponent = jest.fn( () => {
+ const state = useSelect(
+ ( select ) =>
+ select( 'not-yet-registered-store' )?.getCounter(),
+ []
+ );
+
+ return ;
+ } );
+
+ act( () => {
+ renderer = TestRenderer.create(
+
+
+
+ );
+ } );
+
+ const testInstance = renderer.root;
+
+ expect( testInstance.findByType( 'div' ).props.data ).toBe(
+ undefined
+ );
+
+ act( () => {
+ registry.registerStore(
+ 'not-yet-registered-store',
+ counterStore
+ );
+ } );
+
+ // This is not ideal, but is the way it's working before and we want to prevent breaking changes.
+ expect( testInstance.findByType( 'div' ).props.data ).toBe(
+ undefined
+ );
+
+ act( () => {
+ registry.dispatch( 'not-yet-registered-store' ).increment();
+ } );
+
+ expect( testInstance.findByType( 'div' ).props.data ).toBe( 1 );
+
+ // Test if the unsubscribers get called correctly.
+ renderer.unmount();
+ } );
+
+ it( 'handles registration of a non-existing store of sub-registry during rendering', () => {
+ let renderer;
+
+ const subRegistry = createRegistry( {}, registry );
+
+ const TestComponent = jest.fn( () => {
+ const state = useSelect(
+ ( select ) =>
+ select(
+ 'not-yet-registered-child-store'
+ )?.getCounter(),
+ []
+ );
+
+ return ;
+ } );
+
+ act( () => {
+ renderer = TestRenderer.create(
+
+
+
+
+
+ );
+ } );
+
+ const testInstance = renderer.root;
+
+ expect( testInstance.findByType( 'div' ).props.data ).toBe(
+ undefined
+ );
+
+ act( () => {
+ registry.registerStore(
+ 'not-yet-registered-child-store',
+ counterStore
+ );
+ } );
+
+ // This is not ideal, but is the way it's working before and we want to prevent breaking changes.
+ expect( testInstance.findByType( 'div' ).props.data ).toBe(
+ undefined
+ );
+
+ act( () => {
+ registry
+ .dispatch( 'not-yet-registered-child-store' )
+ .increment();
+ } );
+
+ expect( testInstance.findByType( 'div' ).props.data ).toBe( 1 );
+
+ // Test if the unsubscribers get called correctly.
+ renderer.unmount();
+ } );
} );
} );
diff --git a/packages/data/src/registry.js b/packages/data/src/registry.js
index cda0b555c5480..6a0ca9fc4a7a4 100644
--- a/packages/data/src/registry.js
+++ b/packages/data/src/registry.js
@@ -234,6 +234,14 @@ export function createRegistry( storeConfigs = {}, parent = null ) {
return stores[ storeName ].subscribe( handler );
}
+ // Trying to access a store that hasn't been registered,
+ // this is a pattern rarely used but seen in some places.
+ // We fallback to regular `subscribe` here for backward-compatibility for now.
+ // See https://github.com/WordPress/gutenberg/pull/27466 for more info.
+ if ( ! parent ) {
+ return subscribe( handler );
+ }
+
return parent.__experimentalSubscribeStore( storeName, handler );
}