From c4487421d07c3d9224bb30f57f64106a5cf39c9d Mon Sep 17 00:00:00 2001 From: Arafat Zahan Date: Mon, 20 May 2024 14:42:00 +0600 Subject: [PATCH 001/105] Upgrade `@wordpress/data` to `v4.27.3`. --- package-lock.json | 178 ++++++++++++++++++---------------------------- package.json | 2 +- 2 files changed, 70 insertions(+), 110 deletions(-) diff --git a/package-lock.json b/package-lock.json index aaaba31613e..9ae35d5e3a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22049,17 +22049,17 @@ } }, "@wordpress/data": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/@wordpress/data/-/data-4.23.0.tgz", - "integrity": "sha512-plJZkf4otm8TupoA4B0RSmhdZptJOJshiMS02ihkvj0c2WdDomV5ERjOGBSVtJK30mm5ItOQjRIeH95v+EJURg==", + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@wordpress/data/-/data-4.27.3.tgz", + "integrity": "sha512-5763NgNV9IIa1CC3Q80dAvrH6108tJtj3IrHfUCZmUk1atSNsOMBCkLdQ7tGTTi2JFejeGEMg1LJI22JD5zM6Q==", "requires": { - "@babel/runtime": "^7.9.2", - "@wordpress/compose": "^3.20.0", - "@wordpress/deprecated": "^2.9.0", - "@wordpress/element": "^2.17.0", - "@wordpress/is-shallow-equal": "^2.2.0", - "@wordpress/priority-queue": "^1.8.0", - "@wordpress/redux-routine": "^3.11.0", + "@babel/runtime": "^7.13.10", + "@wordpress/compose": "^3.25.3", + "@wordpress/deprecated": "^2.12.3", + "@wordpress/element": "^2.20.3", + "@wordpress/is-shallow-equal": "^3.1.3", + "@wordpress/priority-queue": "^1.11.2", + "@wordpress/redux-routine": "^3.14.2", "equivalent-key-map": "^0.2.2", "is-promise": "^4.0.0", "lodash": "^4.17.19", @@ -22069,6 +22069,14 @@ "use-memo-one": "^1.1.1" }, "dependencies": { + "@babel/runtime": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz", + "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==", + "requires": { + "regenerator-runtime": "^0.14.0" + } + }, "@wordpress/compose": { "version": "3.25.3", "resolved": "https://registry.npmjs.org/@wordpress/compose/-/compose-3.25.3.tgz", @@ -22087,24 +22095,6 @@ "mousetrap": "^1.6.5", "react-resize-aware": "^3.1.0", "use-memo-one": "^1.1.1" - }, - "dependencies": { - "@babel/runtime": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.6.tgz", - "integrity": "sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==", - "requires": { - "regenerator-runtime": "^0.13.11" - } - }, - "@wordpress/is-shallow-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@wordpress/is-shallow-equal/-/is-shallow-equal-3.1.3.tgz", - "integrity": "sha512-eDLhfC4aaSgklzqwc6F/F4zmJVpTVTAvhqX+q0SP/8LPcP2HuKErPHVrEc75PMWqIutja2wJg98YSNPdewrj1w==", - "requires": { - "@babel/runtime": "^7.13.10" - } - } } }, "@wordpress/i18n": { @@ -22119,34 +22109,14 @@ "memize": "^1.1.0", "sprintf-js": "^1.1.1", "tannin": "^1.2.0" - }, - "dependencies": { - "@babel/runtime": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.6.tgz", - "integrity": "sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==", - "requires": { - "regenerator-runtime": "^0.13.11" - } - } } }, "@wordpress/is-shallow-equal": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@wordpress/is-shallow-equal/-/is-shallow-equal-2.3.0.tgz", - "integrity": "sha512-BUVCYZNDoT5fRJGoam/nI2Sn8QELu5z/pFe7UL+szFqQqNnMibdWqN/KoW/YO7WLJqqqTRhAs/Fa51g4oXRyHQ==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@wordpress/is-shallow-equal/-/is-shallow-equal-3.1.3.tgz", + "integrity": "sha512-eDLhfC4aaSgklzqwc6F/F4zmJVpTVTAvhqX+q0SP/8LPcP2HuKErPHVrEc75PMWqIutja2wJg98YSNPdewrj1w==", "requires": { - "@babel/runtime": "^7.11.2" - }, - "dependencies": { - "@babel/runtime": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.6.tgz", - "integrity": "sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==", - "requires": { - "regenerator-runtime": "^0.13.11" - } - } + "@babel/runtime": "^7.13.10" } }, "@wordpress/keycodes": { @@ -22157,32 +22127,22 @@ "@babel/runtime": "^7.13.10", "@wordpress/i18n": "^3.20.0", "lodash": "^4.17.19" - }, - "dependencies": { - "@babel/runtime": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.6.tgz", - "integrity": "sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==", - "requires": { - "regenerator-runtime": "^0.13.11" - } - } } }, "react-resize-aware": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/react-resize-aware/-/react-resize-aware-3.1.2.tgz", - "integrity": "sha512-sBtMIEy/9oI+Xf2o7IdWdkTokpZSPo9TWn60gqWKPG3BXg44Rg3FCIMiIjmgvRUF4eQptw6pqYTUhYwkeVSxXA==" + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/react-resize-aware/-/react-resize-aware-3.1.3.tgz", + "integrity": "sha512-dPacJZfDczrGOljY8MBnQRudxjI8+M9qG5GXQzU4wIl5q2T4e8UcrSl2rsEBYn9DBuGflhDnI2uYGGFJ991DfA==" }, "regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", - "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" } } }, @@ -22196,17 +22156,17 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.6.tgz", - "integrity": "sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz", + "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==", "requires": { - "regenerator-runtime": "^0.13.11" + "regenerator-runtime": "^0.14.0" } }, "regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" } } }, @@ -22220,17 +22180,17 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.6.tgz", - "integrity": "sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz", + "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==", "requires": { - "regenerator-runtime": "^0.13.11" + "regenerator-runtime": "^0.14.0" } }, "regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" } } }, @@ -22725,17 +22685,17 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.6.tgz", - "integrity": "sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz", + "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==", "requires": { - "regenerator-runtime": "^0.13.11" + "regenerator-runtime": "^0.14.0" } }, "regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" } } }, @@ -23072,17 +23032,17 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.6.tgz", - "integrity": "sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz", + "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==", "requires": { - "regenerator-runtime": "^0.13.11" + "regenerator-runtime": "^0.14.0" } }, "regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" } } }, @@ -23098,17 +23058,17 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.6.tgz", - "integrity": "sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz", + "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==", "requires": { - "regenerator-runtime": "^0.13.11" + "regenerator-runtime": "^0.14.0" } }, "regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" } } }, @@ -45623,9 +45583,9 @@ } }, "redux": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz", - "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", "requires": { "@babel/runtime": "^7.9.2" } diff --git a/package.json b/package.json index 8d147f3c649..a07e6b38425 100644 --- a/package.json +++ b/package.json @@ -120,7 +120,7 @@ "@react-hook/window-size": "^3.0.7", "@wordpress/api-fetch": "^3.18.0", "@wordpress/compose": "^3.19.1", - "@wordpress/data": "^4.23.0", + "@wordpress/data": "^4.27.3", "@wordpress/dom-ready": "^2.10.0", "@wordpress/element": "^2.20.3", "@wordpress/i18n": "^3.14.0", From f5cc00bb67f38a631f74901c4edfc0ae171c41a0 Mon Sep 17 00:00:00 2001 From: Arafat Zahan Date: Mon, 20 May 2024 15:10:39 +0600 Subject: [PATCH 002/105] Replace the usage of `__experimentalResolveSelect` with `resolveSelect`. --- .../SettingsActiveModule/index.test.js | 2 +- .../datastore/site/enable-auto-update.js | 4 +- .../js/googlesitekit/datastore/site/html.js | 4 +- .../datastore/user/feature-tours.js | 10 ++-- .../googlesitekit/datastore/user/surveys.js | 6 +- .../datastore/user/surveys.test.js | 8 +-- .../datastore/user/user-input-settings.js | 5 +- .../modules/create-info-store.js | 7 +-- .../modules/datastore/modules.js | 21 +++---- .../modules/datastore/modules.test.js | 4 +- .../datastore/sharing-settings.test.js | 58 +++++++++---------- assets/js/modules/ads/index.js | 2 +- assets/js/modules/ads/pax/services.js | 2 +- assets/js/modules/adsense/index.js | 2 +- .../datastore/audience-settings.js | 2 +- .../datastore/conversion-events.test.js | 4 +- .../custom-dimensions-gathering-data.js | 19 +++--- .../datastore/custom-dimensions.js | 22 +++---- .../datastore/enhanced-measurement.js | 4 +- .../datastore/enhanced-measurement.test.js | 4 +- .../analytics-4/datastore/partial-data.js | 23 +++----- .../analytics-4/datastore/properties.js | 46 ++++++--------- .../js/modules/analytics-4/datastore/tags.js | 2 +- .../analytics-4/datastore/webdatastreams.js | 8 +-- 24 files changed, 115 insertions(+), 154 deletions(-) diff --git a/assets/js/components/settings/SettingsActiveModule/index.test.js b/assets/js/components/settings/SettingsActiveModule/index.test.js index c79d71eca75..80ceb0c9ca3 100644 --- a/assets/js/components/settings/SettingsActiveModule/index.test.js +++ b/assets/js/components/settings/SettingsActiveModule/index.test.js @@ -247,7 +247,7 @@ describe( 'SettingsModule', () => { // Hack to avoid act error due to state change during render. await act( () => registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .canActivateModule( 'tagmanager' ) ); diff --git a/assets/js/googlesitekit/datastore/site/enable-auto-update.js b/assets/js/googlesitekit/datastore/site/enable-auto-update.js index 02c3d15078a..db8aac08c55 100644 --- a/assets/js/googlesitekit/datastore/site/enable-auto-update.js +++ b/assets/js/googlesitekit/datastore/site/enable-auto-update.js @@ -80,10 +80,10 @@ const baseActions = { const registry = yield Data.commonActions.getRegistry(); yield Data.commonActions.await( - registry.__experimentalResolveSelect( CORE_USER ).getNonces() + registry.resolveSelect( CORE_USER ).getNonces() ); yield Data.commonActions.await( - registry.__experimentalResolveSelect( CORE_SITE ).getSiteInfo() + registry.resolveSelect( CORE_SITE ).getSiteInfo() ); const nonce = registry.select( CORE_USER ).getNonce( 'updates' ); diff --git a/assets/js/googlesitekit/datastore/site/html.js b/assets/js/googlesitekit/datastore/site/html.js index 06d01e0101d..7330a5e20c0 100644 --- a/assets/js/googlesitekit/datastore/site/html.js +++ b/assets/js/googlesitekit/datastore/site/html.js @@ -146,9 +146,7 @@ const baseControls = { [ WAIT_FOR_HTML_FOR_URL ]: createRegistryControl( ( registry ) => ( { payload: { url } } ) => - registry - .__experimentalResolveSelect( CORE_SITE ) - .getHTMLForURL( url ) + registry.resolveSelect( CORE_SITE ).getHTMLForURL( url ) ), [ CHECK_FOR_SETUP_TAG ]: createRegistryControl( ( registry ) => async () => { diff --git a/assets/js/googlesitekit/datastore/user/feature-tours.js b/assets/js/googlesitekit/datastore/user/feature-tours.js index 711400e60d2..4935af236a3 100644 --- a/assets/js/googlesitekit/datastore/user/feature-tours.js +++ b/assets/js/googlesitekit/datastore/user/feature-tours.js @@ -198,10 +198,10 @@ const baseActions = { }, *triggerTourForView( viewContext ) { - const { select, __experimentalResolveSelect } = yield getRegistry(); + const { select, resolveSelect } = yield getRegistry(); yield Data.commonActions.await( - __experimentalResolveSelect( CORE_USER ).getLastDismissedAt() + resolveSelect( CORE_USER ).getLastDismissedAt() ); if ( select( CORE_USER ).areFeatureToursOnCooldown() ) { @@ -241,7 +241,7 @@ const baseControls = { // Only tours with a version after a user's initial Site Kit version should qualify. const initialVersion = await registry - .__experimentalResolveSelect( CORE_USER ) + .resolveSelect( CORE_USER ) .getInitialSiteKitVersion(); if ( ! initialVersion ) { return false; @@ -258,7 +258,7 @@ const baseControls = { // Check if the tour has already been dismissed. // Here we need to first await the underlying selector with the asynchronous resolver. await registry - .__experimentalResolveSelect( CORE_USER ) + .resolveSelect( CORE_USER ) .getDismissedFeatureTourSlugs(); if ( registry.select( CORE_USER ).isTourDismissed( tour.slug ) @@ -281,7 +281,7 @@ const baseControls = { // Check if the tour has already been dismissed. // Here we need to first await the underlying selector with the asynchronous resolver. await registry - .__experimentalResolveSelect( CORE_USER ) + .resolveSelect( CORE_USER ) .getDismissedFeatureTourSlugs(); if ( registry.select( CORE_USER ).isTourDismissed( tour.slug ) diff --git a/assets/js/googlesitekit/datastore/user/surveys.js b/assets/js/googlesitekit/datastore/user/surveys.js index e4673fd7a65..17d6463e0ca 100644 --- a/assets/js/googlesitekit/datastore/user/surveys.js +++ b/assets/js/googlesitekit/datastore/user/surveys.js @@ -182,12 +182,12 @@ const baseActions = { }, function* ( triggerID, options = {} ) { const { ttl = 0 } = options; - const { select, dispatch, __experimentalResolveSelect } = + const { select, dispatch, resolveSelect } = yield Data.commonActions.getRegistry(); // Wait for user authentication state to be available before selecting. yield Data.commonActions.await( - __experimentalResolveSelect( CORE_USER ).getAuthentication() + resolveSelect( CORE_USER ).getAuthentication() ); if ( ! select( CORE_USER ).isAuthenticated() ) { @@ -196,7 +196,7 @@ const baseActions = { // Await for surveys to be resolved before checking timeouts. yield Data.commonActions.await( - __experimentalResolveSelect( CORE_USER ).getSurveyTimeouts() + resolveSelect( CORE_USER ).getSurveyTimeouts() ); const isTimedOut = diff --git a/assets/js/googlesitekit/datastore/user/surveys.test.js b/assets/js/googlesitekit/datastore/user/surveys.test.js index 60b84e2ec50..6165868f200 100644 --- a/assets/js/googlesitekit/datastore/user/surveys.test.js +++ b/assets/js/googlesitekit/datastore/user/surveys.test.js @@ -222,18 +222,14 @@ describe( 'core/user surveys', () => { provideUserAuthentication( registry ); - await registry - .__experimentalResolveSelect( CORE_USER ) - .getAuthentication(); + await registry.resolveSelect( CORE_USER ).getAuthentication(); muteFetch( surveyTriggerEndpoint ); muteFetch( surveyTimeoutEndpoint ); registry.dispatch( CORE_USER ).receiveGetSurveyTimeouts( [] ); - await registry - .__experimentalResolveSelect( CORE_USER ) - .getSurveyTimeouts(); + await registry.resolveSelect( CORE_USER ).getSurveyTimeouts(); jest.useFakeTimers(); diff --git a/assets/js/googlesitekit/datastore/user/user-input-settings.js b/assets/js/googlesitekit/datastore/user/user-input-settings.js index c5308cb1dc2..aab3c7929c8 100644 --- a/assets/js/googlesitekit/datastore/user/user-input-settings.js +++ b/assets/js/googlesitekit/datastore/user/user-input-settings.js @@ -162,11 +162,10 @@ const baseActions = { * @return {Object} Object with `response` and `error`. */ *maybeTriggerUserInputSurvey() { - const { __experimentalResolveSelect, dispatch } = - yield commonActions.getRegistry(); + const { resolveSelect, dispatch } = yield commonActions.getRegistry(); const settings = yield commonActions.await( - __experimentalResolveSelect( CORE_USER ).getUserInputSettings() + resolveSelect( CORE_USER ).getUserInputSettings() ); const settingsAnsweredOther = Object.keys( settings ).filter( ( key ) => diff --git a/assets/js/googlesitekit/modules/create-info-store.js b/assets/js/googlesitekit/modules/create-info-store.js index 313276f7ece..c3abb40a0ff 100644 --- a/assets/js/googlesitekit/modules/create-info-store.js +++ b/assets/js/googlesitekit/modules/create-info-store.js @@ -61,11 +61,10 @@ export const createInfoStore = ( const controls = { [ WAIT_FOR_REAUTH_RESOLVERS ]: createRegistryControl( ( registry ) => async () => { - const { __experimentalResolveSelect } = registry; + const { resolveSelect } = registry; const { getAuthentication, getConnectURL } = - __experimentalResolveSelect( CORE_USER ); - const { getSiteInfo } = - __experimentalResolveSelect( CORE_SITE ); + resolveSelect( CORE_USER ); + const { getSiteInfo } = resolveSelect( CORE_SITE ); await Promise.all( [ // Authentication is needed for checking `needsReauthentication`. diff --git a/assets/js/googlesitekit/modules/datastore/modules.js b/assets/js/googlesitekit/modules/datastore/modules.js index 70cfcfaad34..ef9cfcf4f14 100644 --- a/assets/js/googlesitekit/modules/datastore/modules.js +++ b/assets/js/googlesitekit/modules/datastore/modules.js @@ -560,13 +560,11 @@ export const baseControls = { } ), [ SELECT_MODULE_REAUTH_URL ]: createRegistryControl( - ( { select, __experimentalResolveSelect } ) => + ( { select, resolveSelect } ) => async ( { payload } ) => { const { slug } = payload; // Ensure the module is loaded before selecting the store name. - await __experimentalResolveSelect( CORE_MODULES ).getModule( - slug - ); + await resolveSelect( CORE_MODULES ).getModule( slug ); const storeName = select( CORE_MODULES ).getModuleStoreName( slug ); @@ -577,9 +575,7 @@ export const baseControls = { } if ( select( storeName )?.getAdminReauthURL ) { - return await __experimentalResolveSelect( - storeName - ).getAdminReauthURL(); + return await resolveSelect( storeName ).getAdminReauthURL(); } return select( CORE_SITE ).getAdminURL( 'googlesitekit-dashboard' @@ -662,11 +658,10 @@ const baseReducer = ( state, { type, payload } ) => { }; function* waitForModules() { - const { __experimentalResolveSelect } = - yield Data.commonActions.getRegistry(); + const { resolveSelect } = yield Data.commonActions.getRegistry(); yield Data.commonActions.await( - __experimentalResolveSelect( CORE_MODULES ).getModules() + resolveSelect( CORE_MODULES ).getModules() ); } @@ -683,9 +678,9 @@ const baseResolvers = { *canActivateModule( slug ) { const registry = yield Data.commonActions.getRegistry(); - const { select, __experimentalResolveSelect } = registry; + const { select, resolveSelect } = registry; const module = yield Data.commonActions.await( - __experimentalResolveSelect( CORE_MODULES ).getModule( slug ) + resolveSelect( CORE_MODULES ).getModule( slug ) ); // At this point, all modules are loaded so we can safely select getModule below. @@ -751,7 +746,7 @@ const baseResolvers = { *getRecoverableModules() { const registry = yield Data.commonActions.getRegistry(); const modules = yield Data.commonActions.await( - registry.__experimentalResolveSelect( CORE_MODULES ).getModules() + registry.resolveSelect( CORE_MODULES ).getModules() ); const recoverableModules = Object.entries( modules || {} ).reduce( diff --git a/assets/js/googlesitekit/modules/datastore/modules.test.js b/assets/js/googlesitekit/modules/datastore/modules.test.js index fc0a69d86e9..de11d30a3eb 100644 --- a/assets/js/googlesitekit/modules/datastore/modules.test.js +++ b/assets/js/googlesitekit/modules/datastore/modules.test.js @@ -2043,7 +2043,7 @@ describe( 'core/modules modules', () => { provideModules( registry, FIXTURES ); const sharedOwnershipModules = await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharedOwnershipModules(); expect( sharedOwnershipModules ).toMatchObject( {} ); @@ -2056,7 +2056,7 @@ describe( 'core/modules modules', () => { provideModules( registry, FIXTURES ); const sharedOwnershipModules = await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharedOwnershipModules(); expect( sharedOwnershipModules ).toMatchObject( diff --git a/assets/js/googlesitekit/modules/datastore/sharing-settings.test.js b/assets/js/googlesitekit/modules/datastore/sharing-settings.test.js index 43622662155..b097a631d8e 100644 --- a/assets/js/googlesitekit/modules/datastore/sharing-settings.test.js +++ b/assets/js/googlesitekit/modules/datastore/sharing-settings.test.js @@ -240,7 +240,7 @@ describe( 'core/modules sharing-settings', () => { ); await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); await registry @@ -368,7 +368,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = undefined; const sharingSettingsObj = await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); expect( console ).toHaveErrored(); @@ -381,7 +381,7 @@ describe( 'core/modules sharing-settings', () => { }; const sharingSettingsObj = await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); expect( sharingSettingsObj ).toMatchObject( {} ); @@ -391,7 +391,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; const sharingSettingsObj = await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); expect( sharingSettingsObj ).toMatchObject( sharingSettings ); @@ -403,7 +403,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = undefined; const shareableRolesObj = await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getShareableRoles(); expect( console ).toHaveErrored(); @@ -416,7 +416,7 @@ describe( 'core/modules sharing-settings', () => { }; const shareableRolesObj = await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getShareableRoles(); expect( shareableRolesObj ).toMatchObject( [] ); @@ -426,7 +426,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; const shareableRolesObj = await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getShareableRoles(); expect( shareableRolesObj ).toMatchObject( shareableRoles ); @@ -444,7 +444,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = undefined; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); const sharingManagement = registry @@ -459,7 +459,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); const sharingManagement = registry @@ -472,7 +472,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); const sharingManagement = registry @@ -494,7 +494,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = undefined; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); const sharedRoles = registry @@ -509,7 +509,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); const sharedRoles = registry @@ -522,7 +522,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); const sharedRoles = registry @@ -545,7 +545,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); // Still false after getting the sharing settings from the global variable. @@ -592,7 +592,7 @@ describe( 'core/modules sharing-settings', () => { it( 'compares all keys when keys argument is not supplied', async () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); // Update the sharing settings so they differ. All values are being checked here. @@ -607,7 +607,7 @@ describe( 'core/modules sharing-settings', () => { it( 'compares select keys when keys argument is supplied', async () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); // Update the sharing settings so they differ. Only `search-console` should trigger @@ -650,7 +650,7 @@ describe( 'core/modules sharing-settings', () => { it( 'informs whether client-side sharing-settings differ from server-side ones', async () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); // True after updating module's `sharedRoles` on the client. @@ -686,7 +686,7 @@ describe( 'core/modules sharing-settings', () => { it( 'should be set to TRUE after starting submiting sharing changes', async () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); freezeFetch( @@ -707,7 +707,7 @@ describe( 'core/modules sharing-settings', () => { it( 'should be set to FALSE after finishing submitting sharing changes', async () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); fetchMock.postOnce( @@ -763,7 +763,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); // Still false after getting the sharing settings from the global variable. @@ -816,7 +816,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); // Still false after getting the sharing settings from the global variable. @@ -862,7 +862,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); // Still false after getting the sharing settings from the global variable. @@ -898,7 +898,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); // Still false after getting the sharing settings from the global variable. @@ -955,7 +955,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); // Still false after getting the sharing settings from the global variable. @@ -1024,7 +1024,7 @@ describe( 'core/modules sharing-settings', () => { it( 'compares all keys when keys argument is not supplied', async () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); // Update the sharing settings so they differ. All values are being checked here. @@ -1041,7 +1041,7 @@ describe( 'core/modules sharing-settings', () => { it( 'compares selected keys when keys argument is supplied', async () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); // Update the sharing settings so they differ. Only `search-console` should trigger @@ -1099,7 +1099,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = undefined; const defaultSharedOwnershipModuleSettingsObj = await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getDefaultSharedOwnershipModuleSettings(); expect( console ).toHaveErrored(); @@ -1114,7 +1114,7 @@ describe( 'core/modules sharing-settings', () => { }; const defaultSharedOwnershipModuleSettingsObj = await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getDefaultSharedOwnershipModuleSettings(); expect( defaultSharedOwnershipModuleSettingsObj ).toMatchObject( @@ -1128,7 +1128,7 @@ describe( 'core/modules sharing-settings', () => { }; const defaultSharedOwnershipModuleSettingsObj = await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getDefaultSharedOwnershipModuleSettings(); expect( defaultSharedOwnershipModuleSettingsObj ).toMatchObject( diff --git a/assets/js/modules/ads/index.js b/assets/js/modules/ads/index.js index b9f6255b612..9b88d735da7 100644 --- a/assets/js/modules/ads/index.js +++ b/assets/js/modules/ads/index.js @@ -57,7 +57,7 @@ export const registerModule = ( modules ) => { ], checkRequirements: async ( registry ) => { const adBlockerActive = await registry - .__experimentalResolveSelect( CORE_USER ) + .resolveSelect( CORE_USER ) .isAdBlockerActive(); if ( ! adBlockerActive ) { diff --git a/assets/js/modules/ads/pax/services.js b/assets/js/modules/ads/pax/services.js index dc74c7191b5..6306b22f6af 100644 --- a/assets/js/modules/ads/pax/services.js +++ b/assets/js/modules/ads/pax/services.js @@ -61,7 +61,7 @@ const restFetchWpPages = async () => { export function createPaxServices( registry, options = {} ) { const { onCampaignCreated = null, _global = global } = options; - const { select, __experimentalResolveSelect: resolveSelect } = registry; + const { select, resolveSelect: resolveSelect } = registry; const accessToken = _global?._googlesitekitPAXConfig?.authAccess?.oauthTokenAccess?.token; diff --git a/assets/js/modules/adsense/index.js b/assets/js/modules/adsense/index.js index 29f38c0cb46..2f7b734145c 100644 --- a/assets/js/modules/adsense/index.js +++ b/assets/js/modules/adsense/index.js @@ -68,7 +68,7 @@ export const registerModule = ( modules ) => { ], checkRequirements: async ( registry ) => { const adBlockerActive = await registry - .__experimentalResolveSelect( CORE_USER ) + .resolveSelect( CORE_USER ) .isAdBlockerActive(); if ( ! adBlockerActive ) { diff --git a/assets/js/modules/analytics-4/datastore/audience-settings.js b/assets/js/modules/analytics-4/datastore/audience-settings.js index fe3bf40e31c..15071bff614 100644 --- a/assets/js/modules/analytics-4/datastore/audience-settings.js +++ b/assets/js/modules/analytics-4/datastore/audience-settings.js @@ -118,7 +118,7 @@ const baseActions = { const registry = yield Data.commonActions.getRegistry(); const audienceSettings = yield Data.commonActions.await( registry - .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) + .resolveSelect( MODULES_ANALYTICS_4 ) .getAudienceSettings() ); diff --git a/assets/js/modules/analytics-4/datastore/conversion-events.test.js b/assets/js/modules/analytics-4/datastore/conversion-events.test.js index 5e90c7df6bb..80ff5d35fdc 100644 --- a/assets/js/modules/analytics-4/datastore/conversion-events.test.js +++ b/assets/js/modules/analytics-4/datastore/conversion-events.test.js @@ -64,7 +64,7 @@ describe( 'modules/analytics-4 conversion-events', () => { expect( initialConversionEvents ).toBeUndefined(); const conversionEvents = await registry - .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) + .resolveSelect( MODULES_ANALYTICS_4 ) .getConversionEvents(); expect( fetchMock ).toHaveFetchedTimes( 1 ); @@ -110,7 +110,7 @@ describe( 'modules/analytics-4 conversion-events', () => { ); await registry - .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) + .resolveSelect( MODULES_ANALYTICS_4 ) .getConversionEvents(); expect( fetchMock ).toHaveFetchedTimes( 1 ); diff --git a/assets/js/modules/analytics-4/datastore/custom-dimensions-gathering-data.js b/assets/js/modules/analytics-4/datastore/custom-dimensions-gathering-data.js index 90809e5fc92..2cf7fd47863 100644 --- a/assets/js/modules/analytics-4/datastore/custom-dimensions-gathering-data.js +++ b/assets/js/modules/analytics-4/datastore/custom-dimensions-gathering-data.js @@ -115,11 +115,11 @@ const baseActions = { * @param {string} customDimension Custom dimension slug. */ *checkCustomDimensionDataAvailability( customDimension ) { - const { select, __experimentalResolveSelect } = + const { select, resolveSelect } = yield Data.commonActions.getRegistry(); yield Data.commonActions.await( - __experimentalResolveSelect( MODULES_ANALYTICS_4 ).getSettings() + resolveSelect( MODULES_ANALYTICS_4 ).getSettings() ); if ( @@ -135,7 +135,7 @@ const baseActions = { } yield Data.commonActions.await( - __experimentalResolveSelect( CORE_USER ).getAuthentication() + resolveSelect( CORE_USER ).getAuthentication() ); if ( ! select( CORE_USER ).isAuthenticated() ) { @@ -147,7 +147,7 @@ const baseActions = { } const reportArgs = yield Data.commonActions.await( - __experimentalResolveSelect( + resolveSelect( MODULES_ANALYTICS_4 ).getDataAvailabilityReportOptions( customDimension ) ); @@ -161,9 +161,7 @@ const baseActions = { } const report = yield Data.commonActions.await( - __experimentalResolveSelect( MODULES_ANALYTICS_4 ).getReport( - reportArgs - ) + resolveSelect( MODULES_ANALYTICS_4 ).getReport( reportArgs ) ); const hasReportError = !! select( @@ -242,13 +240,10 @@ const baseResolvers = { }, *getDataAvailabilityReportOptions() { - const { __experimentalResolveSelect } = - yield Data.commonActions.getRegistry(); + const { resolveSelect } = yield Data.commonActions.getRegistry(); yield Data.commonActions.await( - __experimentalResolveSelect( - MODULES_ANALYTICS_4 - ).getPropertyCreateTime() + resolveSelect( MODULES_ANALYTICS_4 ).getPropertyCreateTime() ); }, }; diff --git a/assets/js/modules/analytics-4/datastore/custom-dimensions.js b/assets/js/modules/analytics-4/datastore/custom-dimensions.js index 3eda0bcdd8a..8e2db657bd4 100644 --- a/assets/js/modules/analytics-4/datastore/custom-dimensions.js +++ b/assets/js/modules/analytics-4/datastore/custom-dimensions.js @@ -115,15 +115,9 @@ const baseActions = { // Wait for the necessary settings to be loaded before checking. yield Data.commonActions.await( Promise.all( [ - registry - .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) - .getSettings(), - registry - .__experimentalResolveSelect( CORE_USER ) - .getKeyMetricsSettings(), - registry - .__experimentalResolveSelect( CORE_USER ) - .getUserInputSettings(), + registry.resolveSelect( MODULES_ANALYTICS_4 ).getSettings(), + registry.resolveSelect( CORE_USER ).getKeyMetricsSettings(), + registry.resolveSelect( CORE_USER ).getUserInputSettings(), ] ) ); @@ -287,14 +281,12 @@ export const baseReducer = ( state, { type, payload } ) => { const baseResolvers = { *getAvailableCustomDimensions() { - const { select, __experimentalResolveSelect } = + const { select, resolveSelect } = yield Data.commonActions.getRegistry(); const { isAuthenticated, hasCapability } = select( CORE_USER ); const isGA4Connected = yield Data.commonActions.await( - __experimentalResolveSelect( CORE_MODULES ).isModuleConnected( - 'analytics-4' - ) + resolveSelect( CORE_MODULES ).isModuleConnected( 'analytics-4' ) ); if ( ! isGA4Connected ) { @@ -303,7 +295,7 @@ const baseResolvers = { // Wait for settings to be loaded before proceeding. yield Data.commonActions.await( - __experimentalResolveSelect( MODULES_ANALYTICS_4 ).getSettings() + resolveSelect( MODULES_ANALYTICS_4 ).getSettings() ); const availableCustomDimensions = @@ -315,7 +307,7 @@ const baseResolvers = { // Wait for permissions to be loaded before checking if the user can manage options. yield Data.commonActions.await( - __experimentalResolveSelect( CORE_USER ).getCapabilities() + resolveSelect( CORE_USER ).getCapabilities() ); if ( ! hasCapability( PERMISSION_MANAGE_OPTIONS ) ) { diff --git a/assets/js/modules/analytics-4/datastore/enhanced-measurement.js b/assets/js/modules/analytics-4/datastore/enhanced-measurement.js index 415f6178a8a..afc439cbd00 100644 --- a/assets/js/modules/analytics-4/datastore/enhanced-measurement.js +++ b/assets/js/modules/analytics-4/datastore/enhanced-measurement.js @@ -224,7 +224,7 @@ const baseActions = { const currentSettings = yield Data.commonActions.await( registry - .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) + .resolveSelect( MODULES_ANALYTICS_4 ) .getEnhancedMeasurementSettings( propertyID, webDataStreamID @@ -298,7 +298,7 @@ const baseActions = { const currentSettings = yield Data.commonActions.await( registry - .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) + .resolveSelect( MODULES_ANALYTICS_4 ) .getEnhancedMeasurementSettings( propertyID, webDataStreamID diff --git a/assets/js/modules/analytics-4/datastore/enhanced-measurement.test.js b/assets/js/modules/analytics-4/datastore/enhanced-measurement.test.js index e1bc176ba60..20d7efd783e 100644 --- a/assets/js/modules/analytics-4/datastore/enhanced-measurement.test.js +++ b/assets/js/modules/analytics-4/datastore/enhanced-measurement.test.js @@ -356,7 +356,7 @@ describe( 'modules/analytics-4 enhanced-measurement', () => { expect( initialSettings ).toBeUndefined(); const finalSettings = await registry - .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) + .resolveSelect( MODULES_ANALYTICS_4 ) .getEnhancedMeasurementSettings( propertyID, webDataStreamID @@ -488,7 +488,7 @@ describe( 'modules/analytics-4 enhanced-measurement', () => { expect( initialStreamEnabled ).toBeUndefined(); const finalStreamEnabled = await registry - .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) + .resolveSelect( MODULES_ANALYTICS_4 ) .isEnhancedMeasurementStreamAlreadyEnabled( propertyID, webDataStreamID diff --git a/assets/js/modules/analytics-4/datastore/partial-data.js b/assets/js/modules/analytics-4/datastore/partial-data.js index 360d6f6ab90..e4895234e6a 100644 --- a/assets/js/modules/analytics-4/datastore/partial-data.js +++ b/assets/js/modules/analytics-4/datastore/partial-data.js @@ -203,7 +203,7 @@ const baseResolvers = { }, *getResourceDataAvailabilityDate( resourceSlug, resourceType ) { - const { select, __experimentalResolveSelect } = + const { select, resolveSelect } = yield Data.commonActions.getRegistry(); if ( @@ -216,7 +216,7 @@ const baseResolvers = { } const resourceAvailabilityDates = yield Data.commonActions.await( - __experimentalResolveSelect( + resolveSelect( MODULES_ANALYTICS_4 ).getResourceDataAvailabilityDates() ); @@ -227,14 +227,14 @@ const baseResolvers = { ) { // Ensure the settings are loaded. yield Data.commonActions.await( - __experimentalResolveSelect( MODULES_ANALYTICS_4 ).getSettings() + resolveSelect( MODULES_ANALYTICS_4 ).getSettings() ); // Validate if the resourceSlug is a valid resource. switch ( resourceType ) { case RESOURCE_TYPE_AUDIENCE: yield Data.commonActions.await( - __experimentalResolveSelect( + resolveSelect( MODULES_ANALYTICS_4 ).getAvailableAudiences() ); @@ -272,7 +272,7 @@ const baseResolvers = { } yield Data.commonActions.await( - __experimentalResolveSelect( CORE_USER ).getAuthentication() + resolveSelect( CORE_USER ).getAuthentication() ); // Return early if user is not authenticated. @@ -286,7 +286,7 @@ const baseResolvers = { } const reportArgs = yield Data.commonActions.await( - __experimentalResolveSelect( + resolveSelect( MODULES_ANALYTICS_4 ).getPartialDataReportOptions( resourceSlug, resourceType ) ); @@ -297,9 +297,7 @@ const baseResolvers = { } const report = yield Data.commonActions.await( - __experimentalResolveSelect( MODULES_ANALYTICS_4 ).getReport( - reportArgs - ) + resolveSelect( MODULES_ANALYTICS_4 ).getReport( reportArgs ) ); const hasReportError = !! select( @@ -337,13 +335,10 @@ const baseResolvers = { }, *getPartialDataReportOptions() { - const { __experimentalResolveSelect } = - yield Data.commonActions.getRegistry(); + const { resolveSelect } = yield Data.commonActions.getRegistry(); yield Data.commonActions.await( - __experimentalResolveSelect( - MODULES_ANALYTICS_4 - ).getPropertyCreateTime() + resolveSelect( MODULES_ANALYTICS_4 ).getPropertyCreateTime() ); }, }; diff --git a/assets/js/modules/analytics-4/datastore/properties.js b/assets/js/modules/analytics-4/datastore/properties.js index 860f3491deb..ced9c1ee64a 100644 --- a/assets/js/modules/analytics-4/datastore/properties.js +++ b/assets/js/modules/analytics-4/datastore/properties.js @@ -254,7 +254,7 @@ const baseActions = { if ( propertyID ) { const property = yield Data.commonActions.await( registry - .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) + .resolveSelect( MODULES_ANALYTICS_4 ) .getProperty( propertyID ) ); @@ -302,9 +302,7 @@ const baseActions = { *findMatchedProperty() { const registry = yield commonActions.getRegistry(); const accounts = yield Data.commonActions.await( - registry - .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) - .getAccountSummaries() + registry.resolveSelect( MODULES_ANALYTICS_4 ).getAccountSummaries() ); if ( ! Array.isArray( accounts ) || accounts.length === 0 ) { @@ -408,7 +406,7 @@ const baseActions = { ); const webdatastreams = yield commonActions.await( registry - .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) + .resolveSelect( MODULES_ANALYTICS_4 ) .getWebDataStreamsBatch( chunk ) ); @@ -424,9 +422,7 @@ const baseActions = { ) { return yield commonActions.await( registry - .__experimentalResolveSelect( - MODULES_ANALYTICS_4 - ) + .resolveSelect( MODULES_ANALYTICS_4 ) .getProperty( propertyID ) ); } @@ -464,7 +460,7 @@ const baseActions = { ); const webdatastreams = yield commonActions.await( registry - .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) + .resolveSelect( MODULES_ANALYTICS_4 ) .getWebDataStreamsBatch( chunk ) ); @@ -477,9 +473,7 @@ const baseActions = { ) { return yield commonActions.await( registry - .__experimentalResolveSelect( - MODULES_ANALYTICS_4 - ) + .resolveSelect( MODULES_ANALYTICS_4 ) .getProperty( propertyID ) ); } @@ -511,7 +505,7 @@ const baseActions = { * @param {string} measurementID Measurement ID. */ *updateSettingsForMeasurementID( measurementID ) { - const { select, dispatch, __experimentalResolveSelect } = + const { select, dispatch, resolveSelect } = yield commonActions.getRegistry(); if ( ! measurementID ) { @@ -528,7 +522,7 @@ const baseActions = { // Wait for authentication to be resolved to check scopes. yield commonActions.await( - __experimentalResolveSelect( CORE_USER ).getAuthentication() + resolveSelect( CORE_USER ).getAuthentication() ); if ( ! select( CORE_USER ).hasScope( TAGMANAGER_READ_SCOPE ) ) { return; @@ -595,7 +589,7 @@ const baseActions = { * @since 1.95.0 */ *syncGoogleTagSettings() { - const { select, dispatch, __experimentalResolveSelect } = + const { select, dispatch, resolveSelect } = yield Data.commonActions.getRegistry(); const hasTagManagerReadScope = select( CORE_USER ).hasScope( @@ -608,7 +602,7 @@ const baseActions = { // Wait for modules to be available before selecting. yield Data.commonActions.await( - __experimentalResolveSelect( CORE_MODULES ).getModules() + resolveSelect( CORE_MODULES ).getModules() ); const { isModuleConnected } = select( CORE_MODULES ); @@ -619,7 +613,7 @@ const baseActions = { // Wait for module settings to be available before selecting. yield Data.commonActions.await( - __experimentalResolveSelect( MODULES_ANALYTICS_4 ).getSettings() + resolveSelect( MODULES_ANALYTICS_4 ).getSettings() ); const { @@ -649,9 +643,9 @@ const baseActions = { if ( !! googleTagID ) { const googleTagContainer = yield Data.commonActions.await( - __experimentalResolveSelect( - MODULES_ANALYTICS_4 - ).getGoogleTagContainer( measurementID ) + resolveSelect( MODULES_ANALYTICS_4 ).getGoogleTagContainer( + measurementID + ) ); if ( ! googleTagContainer ) { @@ -667,7 +661,7 @@ const baseActions = { const googleTagContainerID = getGoogleTagContainerID(); const googleTagContainerDestinations = yield Data.commonActions.await( - __experimentalResolveSelect( + resolveSelect( MODULES_ANALYTICS_4 ).getGoogleTagContainerDestinations( googleTagAccountID, @@ -692,9 +686,9 @@ const baseActions = { const baseControls = { [ WAIT_FOR_PROPERTY_SUMMARIES ]: createRegistryControl( - ( { __experimentalResolveSelect } ) => { + ( { resolveSelect } ) => { return async () => { - await __experimentalResolveSelect( + await resolveSelect( MODULES_ANALYTICS_4 ).getAccountSummaries(); }; @@ -751,9 +745,7 @@ const baseResolvers = { const registry = yield Data.commonActions.getRegistry(); // Ensure settings are available to select. yield Data.commonActions.await( - registry - .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) - .getSettings() + registry.resolveSelect( MODULES_ANALYTICS_4 ).getSettings() ); const propertyID = registry @@ -784,7 +776,7 @@ const baseResolvers = { const property = yield Data.commonActions.await( registry - .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) + .resolveSelect( MODULES_ANALYTICS_4 ) .getProperty( propertyID ) ); diff --git a/assets/js/modules/analytics-4/datastore/tags.js b/assets/js/modules/analytics-4/datastore/tags.js index 2b6b57fdbb9..310c7673a17 100644 --- a/assets/js/modules/analytics-4/datastore/tags.js +++ b/assets/js/modules/analytics-4/datastore/tags.js @@ -47,7 +47,7 @@ existingTagStore.resolvers.getExistingTag = function* () { if ( existingTag !== null ) { const container = yield Data.commonActions.await( registry - .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) + .resolveSelect( MODULES_ANALYTICS_4 ) .getGoogleTagContainer( existingTag ) ); diff --git a/assets/js/modules/analytics-4/datastore/webdatastreams.js b/assets/js/modules/analytics-4/datastore/webdatastreams.js index c69495fbeea..067a4d0be5a 100644 --- a/assets/js/modules/analytics-4/datastore/webdatastreams.js +++ b/assets/js/modules/analytics-4/datastore/webdatastreams.js @@ -213,12 +213,12 @@ const baseActions = { const baseControls = { [ WAIT_FOR_WEBDATASTREAMS ]: createRegistryControl( - ( { __experimentalResolveSelect } ) => { + ( { resolveSelect } ) => { return async ( { payload } ) => { const { propertyID } = payload; - await __experimentalResolveSelect( - MODULES_ANALYTICS_4 - ).getWebDataStreams( propertyID ); + await resolveSelect( MODULES_ANALYTICS_4 ).getWebDataStreams( + propertyID + ); }; } ), From 40e47502b9bf0d69330102d123adb78294cd9c92 Mon Sep 17 00:00:00 2001 From: Arafat Zahan Date: Mon, 20 May 2024 17:28:47 +0600 Subject: [PATCH 003/105] Align `@wordpress/compose` versions. --- package-lock.json | 106 ++++++++++++++++++++++++++++++++-------------- package.json | 2 +- 2 files changed, 76 insertions(+), 32 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9ae35d5e3a9..1fadd5bbfcc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22014,37 +22014,66 @@ "dev": true }, "@wordpress/compose": { - "version": "3.19.1", - "resolved": "https://registry.npmjs.org/@wordpress/compose/-/compose-3.19.1.tgz", - "integrity": "sha512-D9IoSQJKgQ2Yqz/SDRjoeT26USR7PoTxzyO7Hk2EyolfPoB0sko0LjwbbndfLYYLksAcZ4x1js4cK9+tHkFJNA==", + "version": "3.25.3", + "resolved": "https://registry.npmjs.org/@wordpress/compose/-/compose-3.25.3.tgz", + "integrity": "sha512-tCO2EnJCkCH548OqA0uU8V1k/1skz2QwBlHs8ZQSpimqUS4OWWsAlndCEFe4U4vDTqFt2ow7tzAir+05Cw8MAg==", "requires": { - "@babel/runtime": "^7.9.2", - "@wordpress/element": "^2.16.0", - "@wordpress/is-shallow-equal": "^2.1.0", - "@wordpress/priority-queue": "^1.7.0", + "@babel/runtime": "^7.13.10", + "@wordpress/deprecated": "^2.12.3", + "@wordpress/dom": "^2.18.0", + "@wordpress/element": "^2.20.3", + "@wordpress/is-shallow-equal": "^3.1.3", + "@wordpress/keycodes": "^2.19.3", + "@wordpress/priority-queue": "^1.11.2", "clipboard": "^2.0.1", - "lodash": "^4.17.15", + "lodash": "^4.17.19", + "memize": "^1.1.0", "mousetrap": "^1.6.5", - "react-resize-aware": "^3.0.1" + "react-resize-aware": "^3.1.0", + "use-memo-one": "^1.1.1" }, "dependencies": { - "@wordpress/priority-queue": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@wordpress/priority-queue/-/priority-queue-1.10.0.tgz", - "integrity": "sha512-3ejPX/6ECUN1FAqbL1BvqP77aRrGx5C41HeNZZT9ZzErJWVGfE0NRFfCt7knT0/LumdERApHkswBp3DQ5J18RQ==", + "@babel/runtime": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz", + "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==", "requires": { - "@babel/runtime": "^7.12.5" - }, - "dependencies": { - "@babel/runtime": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz", - "integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==", - "requires": { - "regenerator-runtime": "^0.13.4" - } - } + "regenerator-runtime": "^0.14.0" + } + }, + "@wordpress/i18n": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-3.20.0.tgz", + "integrity": "sha512-SIoOJFB4UrrYAScS4H91CYCLW9dX3Ghv8pBKc/yHGculb1AdGr6gRMlmJxZV62Cn3CZ4Ga86c+FfR+GiBu0JPg==", + "requires": { + "@babel/runtime": "^7.13.10", + "@wordpress/hooks": "^2.12.3", + "gettext-parser": "^1.3.1", + "lodash": "^4.17.19", + "memize": "^1.1.0", + "sprintf-js": "^1.1.1", + "tannin": "^1.2.0" + } + }, + "@wordpress/keycodes": { + "version": "2.19.3", + "resolved": "https://registry.npmjs.org/@wordpress/keycodes/-/keycodes-2.19.3.tgz", + "integrity": "sha512-8rNdmP5M1ifTgLIL0dt/N1uTGsq/Rx1ydCXy+gg24WdxBRhyu5sudNVCtascVXo26aIfOH9OJRdqRZZTEORhog==", + "requires": { + "@babel/runtime": "^7.13.10", + "@wordpress/i18n": "^3.20.0", + "lodash": "^4.17.19" } + }, + "regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" } } }, @@ -22750,11 +22779,26 @@ } }, "@wordpress/is-shallow-equal": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@wordpress/is-shallow-equal/-/is-shallow-equal-2.1.0.tgz", - "integrity": "sha512-xCphAZG60mnLhn+LitwfoercNxsPMvc0Yo96kBY7HAZgrPt+jNQ5Rv4M+FTlVnyLrkyxVxNdtGyuyR+Hpgi8Pg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@wordpress/is-shallow-equal/-/is-shallow-equal-3.1.3.tgz", + "integrity": "sha512-eDLhfC4aaSgklzqwc6F/F4zmJVpTVTAvhqX+q0SP/8LPcP2HuKErPHVrEc75PMWqIutja2wJg98YSNPdewrj1w==", "requires": { - "@babel/runtime": "^7.9.2" + "@babel/runtime": "^7.13.10" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz", + "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==", + "requires": { + "regenerator-runtime": "^0.14.0" + } + }, + "regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + } } }, "@wordpress/jest-console": { @@ -45251,9 +45295,9 @@ "dev": true }, "react-resize-aware": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/react-resize-aware/-/react-resize-aware-3.0.1.tgz", - "integrity": "sha512-HdPzwdcAv+BMFQEgyacFB40G4IxNMO7tSqaMjbnAouot8LXi5/Rx3/Fv+LU2cQekqiivE1LF4sGnwQ7SnoHrpg==" + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/react-resize-aware/-/react-resize-aware-3.1.3.tgz", + "integrity": "sha512-dPacJZfDczrGOljY8MBnQRudxjI8+M9qG5GXQzU4wIl5q2T4e8UcrSl2rsEBYn9DBuGflhDnI2uYGGFJ991DfA==" }, "react-router": { "version": "5.2.0", diff --git a/package.json b/package.json index a07e6b38425..39f8676cc4d 100644 --- a/package.json +++ b/package.json @@ -119,7 +119,7 @@ "@react-hook/merged-ref": "^1.3.0", "@react-hook/window-size": "^3.0.7", "@wordpress/api-fetch": "^3.18.0", - "@wordpress/compose": "^3.19.1", + "@wordpress/compose": "^3.25.3", "@wordpress/data": "^4.27.3", "@wordpress/dom-ready": "^2.10.0", "@wordpress/element": "^2.20.3", From aa1c42400428df9b24397b831db6b65d17359651 Mon Sep 17 00:00:00 2001 From: Arafat Zahan Date: Mon, 20 May 2024 22:03:18 +0600 Subject: [PATCH 004/105] Fix failing tests. --- .../modules/create-submit-changes-store.test.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/assets/js/googlesitekit/modules/create-submit-changes-store.test.js b/assets/js/googlesitekit/modules/create-submit-changes-store.test.js index 829da29e510..3343b725bb7 100644 --- a/assets/js/googlesitekit/modules/create-submit-changes-store.test.js +++ b/assets/js/googlesitekit/modules/create-submit-changes-store.test.js @@ -155,11 +155,15 @@ describe( 'createSubmitChangesStore', () => { ] )( '%s', ( selector ) => { it( 'should use provided validateCanSubmitChanges function', () => { const validateCanSubmitChanges = jest.fn(); - const { selectors } = createSubmitChangesStore( { + const store = createSubmitChangesStore( { validateCanSubmitChanges, } ); - selectors[ selector ](); + const registry = createRegistry(); + registry.registerStore( storeName, store ); + + store.selectors[ selector ](); + expect( validateCanSubmitChanges ).toHaveBeenCalled(); } ); } ); From e52e008113ba3fe5d2d3725e6a6ecfd2d41a808d Mon Sep 17 00:00:00 2001 From: Arafat Zahan Date: Sun, 26 May 2024 11:17:31 +0600 Subject: [PATCH 005/105] Add `patch-package`. --- package-lock.json | 308 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 4 +- 2 files changed, 311 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 1fadd5bbfcc..066f03d061e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23223,6 +23223,12 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true }, + "@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true + }, "abab": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz", @@ -32502,6 +32508,66 @@ "locate-path": "^3.0.0" } }, + "find-yarn-workspace-root": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz", + "integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==", + "dev": true, + "requires": { + "micromatch": "^4.0.2" + }, + "dependencies": { + "braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "requires": { + "fill-range": "^7.1.1" + } + }, + "fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "micromatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "dev": true, + "requires": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + } + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } + } + }, "flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -38187,6 +38253,70 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, + "json-stable-stringify": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.1.1.tgz", + "integrity": "sha512-SU/971Kt5qVQfJpyDveVhQ/vya+5hvrjClFOcr8c0Fq5aODJjMwutrOfCU+eCnVD5gpx1Q3fEqkyom77zH1iIg==", + "dev": true, + "requires": { + "call-bind": "^1.0.5", + "isarray": "^2.0.5", + "jsonify": "^0.0.1", + "object-keys": "^1.1.1" + }, + "dependencies": { + "call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, + "requires": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + } + }, + "function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true + }, + "get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + } + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true + }, + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + } + } + }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", @@ -38223,6 +38353,12 @@ } } }, + "jsonify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", + "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", + "dev": true + }, "jsprim": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", @@ -38359,6 +38495,15 @@ "is-buffer": "^1.1.5" } }, + "klaw-sync": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", + "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11" + } + }, "kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -40963,6 +41108,12 @@ "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", "dev": true }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true + }, "overlayscrollbars": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/overlayscrollbars/-/overlayscrollbars-1.13.1.tgz", @@ -41215,6 +41366,154 @@ "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==", "dev": true }, + "patch-package": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.0.tgz", + "integrity": "sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA==", + "dev": true, + "requires": { + "@yarnpkg/lockfile": "^1.1.0", + "chalk": "^4.1.2", + "ci-info": "^3.7.0", + "cross-spawn": "^7.0.3", + "find-yarn-workspace-root": "^2.0.0", + "fs-extra": "^9.0.0", + "json-stable-stringify": "^1.0.2", + "klaw-sync": "^6.0.0", + "minimist": "^1.2.6", + "open": "^7.4.2", + "rimraf": "^2.6.3", + "semver": "^7.5.3", + "slash": "^2.0.0", + "tmp": "^0.0.33", + "yaml": "^2.2.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "yaml": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.2.tgz", + "integrity": "sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==", + "dev": true + } + } + }, "path": { "version": "0.12.7", "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", @@ -50397,6 +50696,15 @@ "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, "tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", diff --git a/package.json b/package.json index 39f8676cc4d..bce0ef06428 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,8 @@ "lint:css-fix": "stylelint ./assets/sass ./.storybook/assets/sass --fix", "env:start": "./bin/local-env/start.sh", "env:stop": "./bin/local-env/stop.sh", - "env:reset-site": "./bin/local-env/install-wordpress.sh --reset-site" + "env:reset-site": "./bin/local-env/install-wordpress.sh --reset-site", + "postinstall": "patch-package" }, "browserslist": [ "extends @wordpress/browserslist-config" @@ -230,6 +231,7 @@ "lint-staged": "^10.2.11", "mini-css-extract-plugin": "^1.2.1", "node-fetch": "^2.6.8", + "patch-package": "^8.0.0", "postcss": "^8.4.31", "postcss-import-url": "^7.0.0", "postcss-loader": "^4.0.0", From 01535fcaf8d40247ee620eef88fb4fe5071d0a9d Mon Sep 17 00:00:00 2001 From: Arafat Zahan Date: Sun, 26 May 2024 11:21:41 +0600 Subject: [PATCH 006/105] Patch `useSelect` hook of `@wordpress/data` to restore previous behavior. --- patches/@wordpress+data+4.27.3.patch | 79 ++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 patches/@wordpress+data+4.27.3.patch diff --git a/patches/@wordpress+data+4.27.3.patch b/patches/@wordpress+data+4.27.3.patch new file mode 100644 index 00000000000..4f05799d196 --- /dev/null +++ b/patches/@wordpress+data+4.27.3.patch @@ -0,0 +1,79 @@ +diff --git a/node_modules/@wordpress/data/build-module/components/use-select/index.js b/node_modules/@wordpress/data/build-module/components/use-select/index.js +index 1c11195..6f8cec8 100644 +--- a/node_modules/@wordpress/data/build-module/components/use-select/index.js ++++ b/node_modules/@wordpress/data/build-module/components/use-select/index.js +@@ -121,10 +121,7 @@ export default function useSelect(_mapSelect, deps) { + var isMountedAndNotUnsubscribing = useRef(); // Keep track of the stores being selected in the mapSelect function, + // and only subscribe to those stores later. + +- var listeningStores = useRef([]); +- var trapSelect = useCallback(function (callback) { +- return registry.__experimentalMarkListeningStores(callback, listeningStores); +- }, [registry]); // Generate a "flag" for used in the effect dependency array. ++ // Generate a "flag" for used in the effect dependency array. + // It's different than just using `mapSelect` since deps could be undefined, + // in that case, we would still want to memoize it. + +@@ -136,9 +133,7 @@ export default function useSelect(_mapSelect, deps) { + if (!isWithoutMapping) { + try { + if (latestMapSelect.current !== mapSelect || latestMapOutputError.current) { +- mapOutput = trapSelect(function () { +- return mapSelect(registry.select, registry); +- }); ++ mapOutput = mapSelect( registry.select, registry ); + } else { + mapOutput = latestMapOutput.current; + } +@@ -183,9 +178,10 @@ export default function useSelect(_mapSelect, deps) { + var onStoreChange = function onStoreChange() { + if (isMountedAndNotUnsubscribing.current) { + try { +- var newMapOutput = trapSelect(function () { +- return latestMapSelect.current(registry.select, registry); +- }); ++ var newMapOutput = latestMapSelect.current( ++ registry.select, ++ registry ++ ); + + if (isShallowEqual(latestMapOutput.current, newMapOutput)) { + return; +@@ -208,26 +204,22 @@ export default function useSelect(_mapSelect, deps) { + onStoreChange(); + } + +- var onChange = function onChange() { +- if (latestIsAsync.current) { +- renderQueue.add(queueContext, onStoreChange); +- } else { +- onStoreChange(); +- } +- }; ++ var unsubscribe = registry.subscribe( () => { ++ if ( latestIsAsync.current ) { ++ renderQueue.add( queueContext, onStoreChange ); ++ } else { ++ onStoreChange(); ++ } ++ } ); + +- var unsubscribers = listeningStores.current.map(function (storeName) { +- return registry.__experimentalSubscribeStore(storeName, onChange); +- }); + return function () { + isMountedAndNotUnsubscribing.current = false; // The return value of the subscribe function could be undefined if the store is a custom generic store. + +- unsubscribers.forEach(function (unsubscribe) { +- return unsubscribe === null || unsubscribe === void 0 ? void 0 : unsubscribe(); +- }); ++ unsubscribe(); ++ + renderQueue.flush(queueContext); + }; +- }, [registry, trapSelect, depsChangedFlag, isWithoutMapping]); ++ }, [registry, depsChangedFlag, isWithoutMapping]); + return isWithoutMapping ? registry.select(_mapSelect) : mapOutput; + } + //# sourceMappingURL=index.js.map +\ No newline at end of file From a832b5f96fc95f7e62a4de35cb6a91273ef66c58 Mon Sep 17 00:00:00 2001 From: Arafat Zahan Date: Sun, 26 May 2024 11:52:31 +0600 Subject: [PATCH 007/105] Add patch for the build file which is used by jest. --- patches/@wordpress+data+4.27.3.patch | 79 ++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/patches/@wordpress+data+4.27.3.patch b/patches/@wordpress+data+4.27.3.patch index 4f05799d196..29cfdc2caa4 100644 --- a/patches/@wordpress+data+4.27.3.patch +++ b/patches/@wordpress+data+4.27.3.patch @@ -77,3 +77,82 @@ index 1c11195..6f8cec8 100644 } //# sourceMappingURL=index.js.map \ No newline at end of file +diff --git a/node_modules/@wordpress/data/build/components/use-select/index.js b/node_modules/@wordpress/data/build/components/use-select/index.js +index 76e8a7a..0992fde 100644 +--- a/node_modules/@wordpress/data/build/components/use-select/index.js ++++ b/node_modules/@wordpress/data/build/components/use-select/index.js +@@ -137,10 +137,7 @@ function useSelect(_mapSelect, deps) { + var isMountedAndNotUnsubscribing = (0, _element.useRef)(); // Keep track of the stores being selected in the mapSelect function, + // and only subscribe to those stores later. + +- var listeningStores = (0, _element.useRef)([]); +- var trapSelect = (0, _element.useCallback)(function (callback) { +- return registry.__experimentalMarkListeningStores(callback, listeningStores); +- }, [registry]); // Generate a "flag" for used in the effect dependency array. ++ // Generate a "flag" for used in the effect dependency array. + // It's different than just using `mapSelect` since deps could be undefined, + // in that case, we would still want to memoize it. + +@@ -152,9 +149,7 @@ function useSelect(_mapSelect, deps) { + if (!isWithoutMapping) { + try { + if (latestMapSelect.current !== mapSelect || latestMapOutputError.current) { +- mapOutput = trapSelect(function () { +- return mapSelect(registry.select, registry); +- }); ++ mapOutput = mapSelect( registry.select, registry ); + } else { + mapOutput = latestMapOutput.current; + } +@@ -199,9 +194,10 @@ function useSelect(_mapSelect, deps) { + var onStoreChange = function onStoreChange() { + if (isMountedAndNotUnsubscribing.current) { + try { +- var newMapOutput = trapSelect(function () { +- return latestMapSelect.current(registry.select, registry); +- }); ++ var newMapOutput = latestMapSelect.current( ++ registry.select, ++ registry ++ ); + + if ((0, _isShallowEqual.default)(latestMapOutput.current, newMapOutput)) { + return; +@@ -224,26 +220,22 @@ function useSelect(_mapSelect, deps) { + onStoreChange(); + } + +- var onChange = function onChange() { +- if (latestIsAsync.current) { +- renderQueue.add(queueContext, onStoreChange); +- } else { +- onStoreChange(); +- } +- }; ++ var unsubscribe = registry.subscribe( () => { ++ if ( latestIsAsync.current ) { ++ renderQueue.add( queueContext, onStoreChange ); ++ } else { ++ onStoreChange(); ++ } ++ } ); + +- var unsubscribers = listeningStores.current.map(function (storeName) { +- return registry.__experimentalSubscribeStore(storeName, onChange); +- }); + return function () { + isMountedAndNotUnsubscribing.current = false; // The return value of the subscribe function could be undefined if the store is a custom generic store. + +- unsubscribers.forEach(function (unsubscribe) { +- return unsubscribe === null || unsubscribe === void 0 ? void 0 : unsubscribe(); +- }); ++ unsubscribe(); ++ + renderQueue.flush(queueContext); + }; +- }, [registry, trapSelect, depsChangedFlag, isWithoutMapping]); ++ }, [registry, depsChangedFlag, isWithoutMapping]); + return isWithoutMapping ? registry.select(_mapSelect) : mapOutput; + } + //# sourceMappingURL=index.js.map +\ No newline at end of file From 2a04765701b3d3e4b16bab69362a6c75068b0016 Mon Sep 17 00:00:00 2001 From: Arafat Zahan Date: Sun, 26 May 2024 11:59:27 +0600 Subject: [PATCH 008/105] Replace additonal instances of `__experimentalResolveSelect` with `resolveSelect`. --- .../js/modules/analytics-4/datastore/audiences.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/assets/js/modules/analytics-4/datastore/audiences.js b/assets/js/modules/analytics-4/datastore/audiences.js index 28946459364..d377acd9596 100644 --- a/assets/js/modules/analytics-4/datastore/audiences.js +++ b/assets/js/modules/analytics-4/datastore/audiences.js @@ -55,7 +55,7 @@ async function getNonZeroDataAudiencesSortedByTotalUsers( startDate, endDate ) { - const { select, __experimentalResolveSelect } = registry; + const { select, resolveSelect } = registry; const reportOptions = { metrics: [ { name: 'totalUsers' } ], @@ -67,9 +67,9 @@ async function getNonZeroDataAudiencesSortedByTotalUsers( endDate, }; - const report = await __experimentalResolveSelect( - MODULES_ANALYTICS_4 - ).getReport( reportOptions ); + const report = await resolveSelect( MODULES_ANALYTICS_4 ).getReport( + reportOptions + ); const error = select( MODULES_ANALYTICS_4 ).getErrorForSelector( 'getReport', @@ -189,7 +189,7 @@ const baseActions = { *enableAudienceGroup() { const registry = yield Data.commonActions.getRegistry(); - const { dispatch, select, __experimentalResolveSelect } = registry; + const { dispatch, select, resolveSelect } = registry; const { response: availableAudiences, error: syncError } = yield Data.commonActions.await( @@ -303,9 +303,7 @@ const baseActions = { // Create custom dimension if it doesn't exist. yield Data.commonActions.await( - __experimentalResolveSelect( - MODULES_ANALYTICS_4 - ).getAvailableCustomDimensions() + resolveSelect( MODULES_ANALYTICS_4 ).getAvailableCustomDimensions() ); if ( From 0afa3c6a7c11e97b9f4874509ed97b0ac22495b0 Mon Sep 17 00:00:00 2001 From: Jimmy Madon Date: Fri, 31 May 2024 13:23:03 +0100 Subject: [PATCH 009/105] Make user input heading container expand to full width. --- .../components/user-input/UserInputPreview.js | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/assets/js/components/user-input/UserInputPreview.js b/assets/js/components/user-input/UserInputPreview.js index 2111d7d05cb..61f7c77c412 100644 --- a/assets/js/components/user-input/UserInputPreview.js +++ b/assets/js/components/user-input/UserInputPreview.js @@ -50,7 +50,6 @@ import useQueryArg from '../../hooks/useQueryArg'; import ErrorNotice from '../ErrorNotice'; import LoadingWrapper from '../LoadingWrapper'; import CancelUserInputButton from './CancelUserInputButton'; -import { Row, Cell } from '../../material-components'; import { hasErrorForAnswer } from './util/validation'; const { useSelect } = Data; @@ -129,22 +128,20 @@ export default function UserInputPreview( props ) {

) } { settingsView && ( - - - -

- { __( - 'Edit your answers for more personalized metrics:', - 'google-site-kit' - ) } -

-
-
-
+
+ +

+ { __( + 'Edit your answers for more personalized metrics:', + 'google-site-kit' + ) } +

+
+
) } Date: Wed, 12 Jun 2024 12:53:30 +0530 Subject: [PATCH 010/105] Handle errors. --- .../analytics-4/datastore/audiences.js | 67 ++++++++++--------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/assets/js/modules/analytics-4/datastore/audiences.js b/assets/js/modules/analytics-4/datastore/audiences.js index 2f2e0f3ff67..2e3a5b55c1d 100644 --- a/assets/js/modules/analytics-4/datastore/audiences.js +++ b/assets/js/modules/analytics-4/datastore/audiences.js @@ -77,7 +77,6 @@ async function getNonZeroDataAudiencesSortedByTotalUsers( ); if ( error ) { - // TODO: Full error handling will be implemented via https://github.com/google/site-kit-wp/issues/8134. return { error }; } @@ -185,6 +184,8 @@ const baseActions = { * If the `googlesitekit_post_type` custom dimension doesn't exist, creates it. * * @since 1.128.0 + * + * @return {Object} Object with `response` and `error`. */ *enableAudienceGroup() { const registry = yield Data.commonActions.getRegistry(); @@ -197,8 +198,7 @@ const baseActions = { ); if ( syncError ) { - // TODO: Full error handling will be implemented via https://github.com/google/site-kit-wp/issues/8134. - return; + return { error: syncError }; } const userAudiences = availableAudiences.filter( @@ -228,12 +228,13 @@ const baseActions = { ) ); - if ( ! error ) { - // TODO: Full error handling will be implemented via https://github.com/google/site-kit-wp/issues/8134. - configuredAudiences.push( - ...audienceResourceNames.slice( 0, MAX_INITIAL_AUDIENCES ) - ); + if ( error ) { + return { error }; } + + configuredAudiences.push( + ...audienceResourceNames.slice( 0, MAX_INITIAL_AUDIENCES ) + ); } if ( configuredAudiences.length < MAX_INITIAL_AUDIENCES ) { @@ -284,17 +285,19 @@ const baseActions = { ] ) ); - // TODO: Full error handling will be implemented via https://github.com/google/site-kit-wp/issues/8134. - if ( ! newVisitorsResult.error ) { - configuredAudiences.push( newVisitorsResult.response.name ); + if ( newVisitorsResult.error ) { + return { error: newVisitorsResult.error }; } - if ( ! returningVisitorsResult.error ) { - configuredAudiences.push( - returningVisitorsResult.response.name - ); + if ( returningVisitorsResult.error ) { + return { error: returningVisitorsResult.error }; } + configuredAudiences.push( + newVisitorsResult.response.name, + returningVisitorsResult.response.name + ); + // Resync available audiences to ensure the newly created audiences are available. yield Data.commonActions.await( dispatch( MODULES_ANALYTICS_4 ).syncAvailableAudiences() @@ -323,24 +326,24 @@ const baseActions = { ); if ( error ) { - // TODO: Full error handling will be implemented via https://github.com/google/site-kit-wp/issues/8134. - } else { - // If the custom dimension was created successfully, mark it as gathering - // data immediately so that it doesn't cause unnecessary report requests. + return { error }; + } + + // If the custom dimension was created successfully, mark it as gathering + // data immediately so that it doesn't cause unnecessary report requests. + dispatch( + MODULES_ANALYTICS_4 + ).receiveIsCustomDimensionGatheringData( + 'googlesitekit_post_type', + true + ); + + // Resync available custom dimensions to ensure the newly created custom dimension is available. + yield Data.commonActions.await( dispatch( MODULES_ANALYTICS_4 - ).receiveIsCustomDimensionGatheringData( - 'googlesitekit_post_type', - true - ); - - // Resync available custom dimensions to ensure the newly created custom dimension is available. - yield Data.commonActions.await( - dispatch( - MODULES_ANALYTICS_4 - ).fetchSyncAvailableCustomDimensions() - ); - } + ).fetchSyncAvailableCustomDimensions() + ); } dispatch( MODULES_ANALYTICS_4 ).setConfiguredAudiences( @@ -352,7 +355,7 @@ const baseActions = { ); if ( error ) { - // TODO: Full error handling will be implemented via https://github.com/google/site-kit-wp/issues/8134. + return { error }; } }, }; From f59c18dcca001980b14b1e20a9062f6f6a88e309 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Wed, 12 Jun 2024 14:13:19 +0530 Subject: [PATCH 011/105] Add failedSiteKitAudienceResourceNames param. --- .../analytics-4/datastore/audiences.js | 104 ++++++++++-------- 1 file changed, 56 insertions(+), 48 deletions(-) diff --git a/assets/js/modules/analytics-4/datastore/audiences.js b/assets/js/modules/analytics-4/datastore/audiences.js index 2e3a5b55c1d..625dd77428f 100644 --- a/assets/js/modules/analytics-4/datastore/audiences.js +++ b/assets/js/modules/analytics-4/datastore/audiences.js @@ -184,10 +184,13 @@ const baseActions = { * If the `googlesitekit_post_type` custom dimension doesn't exist, creates it. * * @since 1.128.0 + * @since n.e.x.t Added `failedSiteKitAudienceResourceNames` parameter to retry failed Site Kit audience creation. * + * @param {Object} args Arguments for enabling audience group. + * @param {Array} args.failedSiteKitAudienceResourceNames List of failed Site Kit audience resource names to retry. * @return {Object} Object with `response` and `error`. */ - *enableAudienceGroup() { + *enableAudienceGroup( { failedSiteKitAudienceResourceNames } ) { const registry = yield Data.commonActions.getRegistry(); const { dispatch, select, __experimentalResolveSelect } = registry; @@ -207,65 +210,70 @@ const baseActions = { const configuredAudiences = []; - if ( userAudiences.length > 0 ) { - // If there are user audiences, filter and sort them by total users over the last 90 days, - // and add the top two (MAX_INITIAL_AUDIENCES) which have users to the configured audiences. + if ( ! failedSiteKitAudienceResourceNames ) { + if ( userAudiences.length > 0 ) { + // If there are user audiences, filter and sort them by total users over the last 90 days, + // and add the top two (MAX_INITIAL_AUDIENCES) which have users to the configured audiences. - const endDate = select( CORE_USER ).getReferenceDate(); + const endDate = select( CORE_USER ).getReferenceDate(); - const startDate = getPreviousDate( - endDate, - 90 + DATE_RANGE_OFFSET // Add offset to ensure we have data for the entirety of the last 90 days. - ); - - const { audienceResourceNames, error } = - yield Data.commonActions.await( - getNonZeroDataAudiencesSortedByTotalUsers( - registry, - userAudiences, - startDate, - endDate - ) + const startDate = getPreviousDate( + endDate, + 90 + DATE_RANGE_OFFSET // Add offset to ensure we have data for the entirety of the last 90 days. ); - if ( error ) { - return { error }; - } + const { audienceResourceNames, error } = + yield Data.commonActions.await( + getNonZeroDataAudiencesSortedByTotalUsers( + registry, + userAudiences, + startDate, + endDate + ) + ); - configuredAudiences.push( - ...audienceResourceNames.slice( 0, MAX_INITIAL_AUDIENCES ) - ); - } + if ( error ) { + return { error }; + } - if ( configuredAudiences.length < MAX_INITIAL_AUDIENCES ) { - // If there are less than two (MAX_INITIAL_AUDIENCES) configured user audiences, add the Site Kit-created audiences if they exist, - // up to the limit of two. + configuredAudiences.push( + ...audienceResourceNames.slice( 0, MAX_INITIAL_AUDIENCES ) + ); + } - const siteKitAudiences = availableAudiences.filter( - ( { audienceType } ) => audienceType === 'SITE_KIT_AUDIENCE' - ); + if ( configuredAudiences.length < MAX_INITIAL_AUDIENCES ) { + // If there are less than two (MAX_INITIAL_AUDIENCES) configured user audiences, add the Site Kit-created audiences if they exist, + // up to the limit of two. - // Audience slugs to sort by: - const sortedSlugs = [ 'new-visitors', 'returning-visitors' ]; + const siteKitAudiences = availableAudiences.filter( + ( { audienceType } ) => audienceType === 'SITE_KIT_AUDIENCE' + ); - const sortedSiteKitAudiences = siteKitAudiences.sort( - ( audienceA, audienceB ) => { - const indexA = sortedSlugs.indexOf( - audienceA.audienceSlug - ); - const indexB = sortedSlugs.indexOf( - audienceB.audienceSlug - ); + // Audience slugs to sort by: + const sortedSlugs = [ 'new-visitors', 'returning-visitors' ]; - return indexA - indexB; - } - ); + const sortedSiteKitAudiences = siteKitAudiences.sort( + ( audienceA, audienceB ) => { + const indexA = sortedSlugs.indexOf( + audienceA.audienceSlug + ); + const indexB = sortedSlugs.indexOf( + audienceB.audienceSlug + ); + + return indexA - indexB; + } + ); - const audienceResourceNames = sortedSiteKitAudiences - .slice( 0, MAX_INITIAL_AUDIENCES - configuredAudiences.length ) - .map( ( { name } ) => name ); + const audienceResourceNames = sortedSiteKitAudiences + .slice( + 0, + MAX_INITIAL_AUDIENCES - configuredAudiences.length + ) + .map( ( { name } ) => name ); - configuredAudiences.push( ...audienceResourceNames ); + configuredAudiences.push( ...audienceResourceNames ); + } } if ( configuredAudiences.length === 0 ) { From 91bbe568b28012d537b4151152ce5363d31f6bc9 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Thu, 13 Jun 2024 11:01:02 +0530 Subject: [PATCH 012/105] Handle retry logic. --- .../analytics-4/datastore/audiences.js | 47 ++++++++++++------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/assets/js/modules/analytics-4/datastore/audiences.js b/assets/js/modules/analytics-4/datastore/audiences.js index 625dd77428f..55d59a0b1ee 100644 --- a/assets/js/modules/analytics-4/datastore/audiences.js +++ b/assets/js/modules/analytics-4/datastore/audiences.js @@ -188,7 +188,7 @@ const baseActions = { * * @param {Object} args Arguments for enabling audience group. * @param {Array} args.failedSiteKitAudienceResourceNames List of failed Site Kit audience resource names to retry. - * @return {Object} Object with `response` and `error`. + * @return {Object} Object with `failedSiteKitAudienceResourceNames` and `error`. */ *enableAudienceGroup( { failedSiteKitAudienceResourceNames } ) { const registry = yield Data.commonActions.getRegistry(); @@ -277,34 +277,47 @@ const baseActions = { } if ( configuredAudiences.length === 0 ) { + const audiencesToCreate = failedSiteKitAudienceResourceNames || [ + 'new-visitors', + 'returning-visitors', + ]; + // If there are no configured audiences by this point, create the "new-visitors" and "returning-visitors" audiences, // and add them to the configured audiences. const [ newVisitorsResult, returningVisitorsResult ] = yield Data.commonActions.await( - Promise.all( [ - dispatch( MODULES_ANALYTICS_4 ).createAudience( - SITE_KIT_AUDIENCE_DEFINITIONS[ 'new-visitors' ] - ), - dispatch( MODULES_ANALYTICS_4 ).createAudience( - SITE_KIT_AUDIENCE_DEFINITIONS[ - 'returning-visitors' - ] - ), - ] ) + Promise.all( + audiencesToCreate.map( ( audienceSlug ) => { + return dispatch( + MODULES_ANALYTICS_4 + ).createAudience( + SITE_KIT_AUDIENCE_DEFINITIONS[ audienceSlug ] + ); + } ) + ) ); + const failedAudiencesToRetry = []; + if ( newVisitorsResult.error ) { - return { error: newVisitorsResult.error }; + failedAudiencesToRetry.push( 'new-visitors' ); + } else { + configuredAudiences.push( newVisitorsResult.response.name ); } if ( returningVisitorsResult.error ) { - return { error: returningVisitorsResult.error }; + failedAudiencesToRetry.push( 'returning-visitors' ); + } else { + configuredAudiences.push( + returningVisitorsResult.response.name + ); } - configuredAudiences.push( - newVisitorsResult.response.name, - returningVisitorsResult.response.name - ); + if ( failedAudiencesToRetry.length > 0 ) { + return { + failedSiteKitAudienceResourceNames: failedAudiencesToRetry, + }; + } // Resync available audiences to ensure the newly created audiences are available. yield Data.commonActions.await( From bbb52a350d616eb43e89be08adb7acf0b6dc8b6a Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Mon, 24 Jun 2024 19:31:58 +0530 Subject: [PATCH 013/105] Add buttonLink prop to ModalDialog. --- assets/js/components/ModalDialog.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/assets/js/components/ModalDialog.js b/assets/js/components/ModalDialog.js index 19e30472d07..ff25cc76324 100644 --- a/assets/js/components/ModalDialog.js +++ b/assets/js/components/ModalDialog.js @@ -57,6 +57,7 @@ function ModalDialog( { inProgress = false, small = false, medium = false, + buttonLink = null, } ) { const instanceID = useInstanceId( ModalDialog ); const describedByID = `googlesitekit-dialog-description-${ instanceID }`; @@ -130,6 +131,8 @@ function ModalDialog( { danger={ danger } disabled={ inProgress } isSaving={ inProgress } + href={ buttonLink || undefined } + target={ buttonLink ? '_blank' : undefined } > { confirmButton || __( 'Disconnect', 'google-site-kit' ) } @@ -150,6 +153,7 @@ ModalDialog.propTypes = { danger: PropTypes.bool, small: PropTypes.bool, medium: PropTypes.bool, + buttonLink: PropTypes.string, }; export default ModalDialog; From d99fbae7d2bd13c9e9b7762c36db5a3d0d31dc18 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Tue, 25 Jun 2024 12:32:53 +0530 Subject: [PATCH 014/105] Fix merge conflicts. --- .../analytics-4/datastore/audiences.js | 223 +++++++++--------- 1 file changed, 107 insertions(+), 116 deletions(-) diff --git a/assets/js/modules/analytics-4/datastore/audiences.js b/assets/js/modules/analytics-4/datastore/audiences.js index b62bc7502d7..2edbce87637 100644 --- a/assets/js/modules/analytics-4/datastore/audiences.js +++ b/assets/js/modules/analytics-4/datastore/audiences.js @@ -241,150 +241,141 @@ const baseActions = { configuredAudiences.push( ...audienceResourceNames.slice( 0, MAX_INITIAL_AUDIENCES ) ); + } - if ( configuredAudiences.length < MAX_INITIAL_AUDIENCES ) { - // If there are less than two (MAX_INITIAL_AUDIENCES) configured user audiences, add the Site Kit-created audiences if they exist, - // up to the limit of two. + if ( configuredAudiences.length < MAX_INITIAL_AUDIENCES ) { + // If there are less than two (MAX_INITIAL_AUDIENCES) configured user audiences, add the Site Kit-created audiences if they exist, + // up to the limit of two. - const siteKitAudiences = availableAudiences.filter( - ( { audienceType } ) => - audienceType === 'SITE_KIT_AUDIENCE' - ); + const siteKitAudiences = availableAudiences.filter( + ( { audienceType } ) => audienceType === 'SITE_KIT_AUDIENCE' + ); - // Audience slugs to sort by: - const sortedSlugs = [ - 'new-visitors', - 'returning-visitors', - ]; + // Audience slugs to sort by: + const sortedSlugs = [ 'new-visitors', 'returning-visitors' ]; - const sortedSiteKitAudiences = siteKitAudiences.sort( - ( audienceA, audienceB ) => { - const indexA = sortedSlugs.indexOf( - audienceA.audienceSlug - ); - const indexB = sortedSlugs.indexOf( - audienceB.audienceSlug - ); + const sortedSiteKitAudiences = siteKitAudiences.sort( + ( audienceA, audienceB ) => { + const indexA = sortedSlugs.indexOf( + audienceA.audienceSlug + ); + const indexB = sortedSlugs.indexOf( + audienceB.audienceSlug + ); - return indexA - indexB; - } - ); + return indexA - indexB; + } + ); - const sortedAudienceResourceNames = sortedSiteKitAudiences - .slice( - 0, - MAX_INITIAL_AUDIENCES - configuredAudiences.length - ) - .map( ( { name } ) => name ); + const audienceResourceNames = sortedSiteKitAudiences + .slice( + 0, + MAX_INITIAL_AUDIENCES - configuredAudiences.length + ) + .map( ( { name } ) => name ); - configuredAudiences.push( ...sortedAudienceResourceNames ); - } + configuredAudiences.push( ...audienceResourceNames ); } + } - if ( configuredAudiences.length === 0 ) { - const audiencesToCreate = - failedSiteKitAudienceResourceNames || [ - 'new-visitors', - 'returning-visitors', - ]; - - // If there are no configured audiences by this point, create the "new-visitors" and "returning-visitors" audiences, - // and add them to the configured audiences. - const [ newVisitorsResult, returningVisitorsResult ] = - yield commonActions.await( - Promise.all( - audiencesToCreate.map( ( audienceSlug ) => { - return dispatch( - MODULES_ANALYTICS_4 - ).createAudience( - SITE_KIT_AUDIENCE_DEFINITIONS[ - audienceSlug - ] - ); - } ) - ) - ); - - const failedAudiencesToRetry = []; + if ( configuredAudiences.length === 0 ) { + const audiencesToCreate = failedSiteKitAudienceResourceNames || [ + 'new-visitors', + 'returning-visitors', + ]; - if ( newVisitorsResult.error ) { - failedAudiencesToRetry.push( 'new-visitors' ); - } else { - configuredAudiences.push( newVisitorsResult.response.name ); - } + // If there are no configured audiences by this point, create the "new-visitors" and "returning-visitors" audiences, + // and add them to the configured audiences. + const [ newVisitorsResult, returningVisitorsResult ] = + yield commonActions.await( + Promise.all( + audiencesToCreate.map( ( audienceSlug ) => { + return dispatch( + MODULES_ANALYTICS_4 + ).createAudience( + SITE_KIT_AUDIENCE_DEFINITIONS[ audienceSlug ] + ); + } ) + ) + ); - if ( returningVisitorsResult.error ) { - failedAudiencesToRetry.push( 'returning-visitors' ); - } else { - configuredAudiences.push( - returningVisitorsResult.response.name - ); - } + const failedAudiencesToRetry = []; - if ( failedAudiencesToRetry.length > 0 ) { - return { - failedSiteKitAudienceResourceNames: - failedAudiencesToRetry, - }; - } + if ( newVisitorsResult.error ) { + failedAudiencesToRetry.push( 'new-visitors' ); + } else { + configuredAudiences.push( newVisitorsResult.response.name ); + } - // Resync available audiences to ensure the newly created audiences are available. - yield commonActions.await( - dispatch( MODULES_ANALYTICS_4 ).syncAvailableAudiences() + if ( returningVisitorsResult.error ) { + failedAudiencesToRetry.push( 'returning-visitors' ); + } else { + configuredAudiences.push( + returningVisitorsResult.response.name ); } - // Create custom dimension if it doesn't exist. + if ( failedAudiencesToRetry.length > 0 ) { + return { + failedSiteKitAudienceResourceNames: failedAudiencesToRetry, + }; + } + + // Resync available audiences to ensure the newly created audiences are available. yield commonActions.await( - __experimentalResolveSelect( - MODULES_ANALYTICS_4 - ).getAvailableCustomDimensions() + dispatch( MODULES_ANALYTICS_4 ).syncAvailableAudiences() ); + } + + // Create custom dimension if it doesn't exist. + yield commonActions.await( + __experimentalResolveSelect( + MODULES_ANALYTICS_4 + ).getAvailableCustomDimensions() + ); - if ( - ! select( MODULES_ANALYTICS_4 ).hasCustomDimensions( - 'googlesitekit_post_type' + if ( + ! select( MODULES_ANALYTICS_4 ).hasCustomDimensions( + 'googlesitekit_post_type' + ) + ) { + const propertyID = select( MODULES_ANALYTICS_4 ).getPropertyID(); + + const { error } = yield commonActions.await( + dispatch( MODULES_ANALYTICS_4 ).fetchCreateCustomDimension( + propertyID, + CUSTOM_DIMENSION_DEFINITIONS.googlesitekit_post_type ) - ) { - const propertyID = - select( MODULES_ANALYTICS_4 ).getPropertyID(); - - const { error } = yield commonActions.await( - dispatch( MODULES_ANALYTICS_4 ).fetchCreateCustomDimension( - propertyID, - CUSTOM_DIMENSION_DEFINITIONS.googlesitekit_post_type - ) - ); + ); - if ( error ) { - return { error }; - } + if ( error ) { + return { error }; + } - // If the custom dimension was created successfully, mark it as gathering - // data immediately so that it doesn't cause unnecessary report requests. + // If the custom dimension was created successfully, mark it as gathering + // data immediately so that it doesn't cause unnecessary report requests. + dispatch( + MODULES_ANALYTICS_4 + ).receiveIsCustomDimensionGatheringData( + 'googlesitekit_post_type', + true + ); + + yield commonActions.await( dispatch( MODULES_ANALYTICS_4 ).receiveIsCustomDimensionGatheringData( 'googlesitekit_post_type', true - ); - - yield commonActions.await( - dispatch( - MODULES_ANALYTICS_4 - ).receiveIsCustomDimensionGatheringData( - 'googlesitekit_post_type', - true - ) - ); + ) + ); - // Resync available custom dimensions to ensure the newly created custom dimension is available. - yield commonActions.await( - dispatch( - MODULES_ANALYTICS_4 - ).fetchSyncAvailableCustomDimensions() - ); - } + // Resync available custom dimensions to ensure the newly created custom dimension is available. + yield commonActions.await( + dispatch( + MODULES_ANALYTICS_4 + ).fetchSyncAvailableCustomDimensions() + ); } dispatch( MODULES_ANALYTICS_4 ).setConfiguredAudiences( From d22d36e18f96dc68acfb68b75b5fad327d03b62b Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Tue, 25 Jun 2024 12:41:06 +0530 Subject: [PATCH 015/105] Remove code duplication. --- assets/js/modules/analytics-4/datastore/audiences.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/assets/js/modules/analytics-4/datastore/audiences.js b/assets/js/modules/analytics-4/datastore/audiences.js index 2edbce87637..b01c7295e0f 100644 --- a/assets/js/modules/analytics-4/datastore/audiences.js +++ b/assets/js/modules/analytics-4/datastore/audiences.js @@ -361,15 +361,6 @@ const baseActions = { true ); - yield commonActions.await( - dispatch( - MODULES_ANALYTICS_4 - ).receiveIsCustomDimensionGatheringData( - 'googlesitekit_post_type', - true - ) - ); - // Resync available custom dimensions to ensure the newly created custom dimension is available. yield commonActions.await( dispatch( From 64caec5b3df38f33c8792b97115a7908b8d571ac Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Tue, 25 Jun 2024 21:33:31 +0530 Subject: [PATCH 016/105] Add AudienceErrorModal component. --- .../dashboard/AudienceErrorModal.js | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal.js diff --git a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal.js b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal.js new file mode 100644 index 00000000000..19ed151b945 --- /dev/null +++ b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal.js @@ -0,0 +1,127 @@ +/** + * Audience Segmentation AudienceErrorModal component. + * + * Site Kit by Google, Copyright 2024 Google LLC + * + * Licensed 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 + * + * https://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. + */ + +/** + * External dependencies + */ +import PropTypes from 'prop-types'; + +/** + * WordPress dependencies + */ +import { createInterpolateElement } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import { useSelect } from 'googlesitekit-data'; +import ModalDialog from '../../../../../components/ModalDialog'; +import { isInsufficientPermissionsError } from '../../../../../util/errors'; +import { CORE_SITE } from '../../../../../googlesitekit/datastore/site/constants'; +import Link from '../../../../../components/Link'; +import { MODULES_ANALYTICS_4 } from '../../../datastore/constants'; + +export default function AudienceErrorModal( { + hasOAuthError, + apiErrors, + onRetry = () => {}, + inProgress, +} ) { + const errors = Array.isArray( apiErrors ) ? apiErrors : [ apiErrors ]; + const hasInsufficientPermissionsError = errors.some( ( error ) => + isInsufficientPermissionsError( error ) + ); + + const helpLink = useSelect( ( select ) => + select( CORE_SITE ).getErrorTroubleshootingLinkURL( { + code: 'analytics-4_insufficient_permissions', + } ) + ); + + const requestAccessURL = useSelect( ( select ) => + select( MODULES_ANALYTICS_4 ).getServiceEntityAccessURL() + ); + + let title, description, confirmButton, buttonLink; + + if ( hasOAuthError ) { + title = __( 'Analytics update failed', 'google-site-kit' ); + description = createInterpolateElement( + __( + 'Setup was interrupted because you did not grant the necessary permissions. ', + 'google-site-kit' + ), + { + HelpLink: ( + + { __( 'Get help', 'google-site-kit' ) } + + ), + } + ); + confirmButton = __( 'Retry', 'google-site-kit' ); + } else if ( hasInsufficientPermissionsError ) { + title = __( 'Insufficient permissions', 'google-site-kit' ); + description = createInterpolateElement( + __( + 'You’ll need to contact your administrator. Trouble getting access? ', + 'google-site-kit' + ), + { + HelpLink: ( + + { __( 'Get help', 'google-site-kit' ) } + + ), + } + ); + confirmButton = __( 'Request access', 'google-site-kit' ); + buttonLink = requestAccessURL; + } else { + title = __( 'Failed to set up visitor groups', 'google-site-kit' ); + description = __( + 'Oops! Something went wrong. Retry enabling groups.', + 'google-site-kit' + ); + confirmButton = __( 'Retry', 'google-site-kit' ); + } + + return ( + + ); +} + +AudienceErrorModal.propTypes = { + hasOAuthError: PropTypes.bool, + apiErrors: PropTypes.oneOfType( [ + PropTypes.arrayOf( PropTypes.object ), + PropTypes.object, + ] ), + onRetry: PropTypes.func, + inProgress: PropTypes.bool, +}; From 58470bd8367f34893119a28c0bd0ad50c701e2aa Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Tue, 25 Jun 2024 21:34:02 +0530 Subject: [PATCH 017/105] Render Button component if buttonLink is provided. --- assets/js/components/ModalDialog.js | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/assets/js/components/ModalDialog.js b/assets/js/components/ModalDialog.js index ff25cc76324..2a517aceaf4 100644 --- a/assets/js/components/ModalDialog.js +++ b/assets/js/components/ModalDialog.js @@ -126,16 +126,25 @@ function ModalDialog( { > { __( 'Cancel', 'google-site-kit' ) } - - { confirmButton || __( 'Disconnect', 'google-site-kit' ) } - + { buttonLink ? ( + + ) : ( + + { confirmButton || + __( 'Disconnect', 'google-site-kit' ) } + + ) } ); From eeb3242c655cefcb32be2cefb57c447953f96f31 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Tue, 25 Jun 2024 21:34:30 +0530 Subject: [PATCH 018/105] Return null if skipDefaultErrorNotifications available in ErrorNotifications. --- assets/js/components/notifications/ErrorNotifications.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/assets/js/components/notifications/ErrorNotifications.js b/assets/js/components/notifications/ErrorNotifications.js index 37c4f45ff76..b3b53e84b76 100644 --- a/assets/js/components/notifications/ErrorNotifications.js +++ b/assets/js/components/notifications/ErrorNotifications.js @@ -108,6 +108,12 @@ export default function ErrorNotifications() { } } + if ( + temporaryPersistedPermissionsError?.data?.skipDefaultErrorNotifications + ) { + return null; + } + return ( From 67744271b27d1d42c5aa8a3d08541f176b93813e Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Tue, 25 Jun 2024 21:34:50 +0530 Subject: [PATCH 019/105] Return null if skipDefaultErrorNotifications available in SetupErrorNotification. --- .../notifications/SetupErrorNotification.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/assets/js/components/notifications/SetupErrorNotification.js b/assets/js/components/notifications/SetupErrorNotification.js index 1c3be41aa6d..a74043271b1 100644 --- a/assets/js/components/notifications/SetupErrorNotification.js +++ b/assets/js/components/notifications/SetupErrorNotification.js @@ -27,6 +27,8 @@ import { __ } from '@wordpress/i18n'; import { useSelect } from 'googlesitekit-data'; import BannerNotification from './BannerNotification'; import { CORE_SITE } from '../../googlesitekit/datastore/site/constants'; +import { CORE_FORMS } from '../../googlesitekit/datastore/forms/constants'; +import { FORM_TEMPORARY_PERSIST_PERMISSION_ERROR } from '../../googlesitekit/datastore/user/constants'; export default function SetupErrorNotification() { // These will be `null` if no errors exist. @@ -37,7 +39,19 @@ export default function SetupErrorNotification() { select( CORE_SITE ).getSetupErrorRedoURL() ); - if ( ! setupErrorMessage ) { + const { data: permissionsErrorData } = useSelect( + ( select ) => + select( CORE_FORMS ).getValue( + FORM_TEMPORARY_PERSIST_PERMISSION_ERROR, + 'permissionsError' + ) || {} + ); + + // If there's no setup error message or the temporary persisted permissions error has skipDefaultErrorNotifications flag set, return null. + if ( + ! setupErrorMessage || + permissionsErrorData?.skipDefaultErrorNotifications + ) { return null; } From ee5986ac4abfad2fbadbef3f5890d75a533a78b0 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Tue, 25 Jun 2024 21:35:15 +0530 Subject: [PATCH 020/105] Add AudienceErrorModal stories - WIP. --- .../dashboard/AudienceErrorModal.stories.js | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal.stories.js diff --git a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal.stories.js b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal.stories.js new file mode 100644 index 00000000000..c4ba13e64c5 --- /dev/null +++ b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal.stories.js @@ -0,0 +1,111 @@ +/** + * AudienceErrorModal Component Stories. + * + * Site Kit by Google, Copyright 2024 Google LLC + * + * Licensed 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 + * + * https://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. + */ + +/** + * Internal dependencies + */ +import { ERROR_REASON_INSUFFICIENT_PERMISSIONS } from '../../../../../util/errors'; +import { MODULES_ANALYTICS_4 } from '../../../datastore/constants'; +// import { useSelect } from 'googlesitekit-data'; +import { + // WithTestRegistry, + // createTestRegistry, + provideModules, + provideModuleRegistrations, + // provideUserInfo, + provideSiteInfo, +} from '../../../../../../../tests/js/test-utils'; +import WithRegistrySetup from '../../../../../../../tests/js/WithRegistrySetup'; +import AudienceErrorModal from './AudienceErrorModal'; + +function Template( args ) { + return ; +} + +export const Default = Template.bind( {} ); +Default.storyName = 'Default'; +Default.args = { + onRetry: () => {}, +}; + +export const WithInsufficientPermissionsError = Template.bind( {} ); +WithInsufficientPermissionsError.storyName = 'Insufficient permissions error'; +WithInsufficientPermissionsError.args = { + apiErrors: { + code: 'test_error', + message: 'Error message.', + data: { reason: ERROR_REASON_INSUFFICIENT_PERMISSIONS }, + }, +}; +WithInsufficientPermissionsError.scenario = { + label: 'Modules/Analytics4/Components/AudienceSegmentation/Dashboard/AudienceErrorModal/WithInsufficientPermissionsError', +}; + +export const WithOAuthError = Template.bind( {} ); +WithOAuthError.storyName = 'OAuth error'; +WithOAuthError.args = { + hasOAuthError: true, + onRetry: () => {}, +}; +WithOAuthError.scenario = { + label: 'Modules/Analytics4/Components/AudienceSegmentation/Dashboard/AudienceErrorModal/WithOAuthError', +}; + +// export const AudienceSyncError = Template.bind( {} ); +// AudienceSyncError.storyName = 'Audience sync error'; +// AudienceSyncError.args = { +// setupRegistry: ( registry ) => { +// const error = { +// code: 'test_error', +// message: 'Error message.', +// data: {}, +// }; + +// registry +// .dispatch( MODULES_ANALYTICS_4 ) +// .receiveError( error, 'syncAvailableAudiences' ); +// }, +// }; + +export default { + title: 'Modules/Analytics4/Components/AudienceSegmentation/Dashboard/AudienceErrorModal', + // component: AudienceErrorModal, + decorators: [ + ( Story ) => { + const setupRegistry = ( registry ) => { + provideSiteInfo( registry ); + provideModules( registry ); + provideModuleRegistrations( registry ); + + // This is necessary to populate the service entity access URL. + registry.dispatch( MODULES_ANALYTICS_4 ).receiveGetSettings( { + accountID: '12345', + propertyID: '34567', + measurementID: '56789', + webDataStreamID: '78901', + } ); + }; + + return ( + + + + ); + }, + ], +}; From 284cf8dbb7fc758a429fd16a648080e617c5f153 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Tue, 25 Jun 2024 21:35:40 +0530 Subject: [PATCH 021/105] Add logic to render AudienceErrorModal in the setup CTA. --- .../AudienceSegmentationSetupCTAWidget.js | 123 +++++++++++++----- 1 file changed, 92 insertions(+), 31 deletions(-) diff --git a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js index 2cda3a19e17..399c0fa13f2 100644 --- a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js +++ b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js @@ -59,6 +59,8 @@ import { } from '../../../../../components/AdminMenuTooltip'; import { withWidgetComponentProps } from '../../../../../googlesitekit/widgets/util'; import { WEEK_IN_SECONDS } from '../../../../../util'; +import AudienceErrorModal from './AudienceErrorModal'; +import { CORE_SITE } from '../../../../../googlesitekit/datastore/site/constants'; export const AUDIENCE_SEGMENTATION_SETUP_CTA_NOTIFICATION = 'audience_segmentation_setup_cta-notification'; @@ -110,43 +112,66 @@ function AudienceSegmentationSetupCTAWidget( { Widget, WidgetNull } ) { notification: 'audience_segmentation', } ); - const onEnableGroups = useCallback( async () => { - setIsSaving( true ); + const [ apiErrors, setApiErrors ] = useState( [] ); + const [ failedAudiences, setFailedAudiences ] = useState( [] ); + const onEnableGroups = useCallback( + async ( retryFailedAudiences ) => { + setIsSaving( true ); + + // If scope not granted, trigger scope error right away. These are + // typically handled automatically based on API responses, but + // this particular case has some special handling to improve UX. + if ( ! hasAnalytics4EditScope ) { + setValues( AUDIENCE_SEGMENTATION_SETUP_FORM, { + autoSubmit: true, + } ); + + setPermissionScopeError( { + code: ERROR_CODE_MISSING_REQUIRED_SCOPE, + message: __( + 'Additional permissions are required to create new audiences in Analytics.', + 'google-site-kit' + ), + data: { + status: 403, + scopes: [ EDIT_SCOPE ], + skipModal: true, + skipDefaultErrorNotifications: true, + redirectURL, + }, + } ); + + setIsSaving( false ); + return; + } - // If scope not granted, trigger scope error right away. These are - // typically handled automatically based on API responses, but - // this particular case has some special handling to improve UX. - if ( ! hasAnalytics4EditScope ) { setValues( AUDIENCE_SEGMENTATION_SETUP_FORM, { - autoSubmit: true, + autoSubmit: false, } ); - setPermissionScopeError( { - code: ERROR_CODE_MISSING_REQUIRED_SCOPE, - message: __( - 'Additional permissions are required to create new audiences in Analytics.', - 'google-site-kit' - ), - data: { - status: 403, - scopes: [ EDIT_SCOPE ], - skipModal: true, - redirectURL, - }, - } ); + const { error, failedSiteKitAudienceResourceNames } = + await enableAudienceGroup( { + failedSiteKitAudienceResourceNames: retryFailedAudiences, + } ); - return; - } + if ( error ) { + setApiErrors( [ error ] ); + } - setValues( AUDIENCE_SEGMENTATION_SETUP_FORM, { autoSubmit: false } ); - await enableAudienceGroup(); - }, [ - enableAudienceGroup, - hasAnalytics4EditScope, - setPermissionScopeError, - redirectURL, - setValues, - ] ); + if ( failedSiteKitAudienceResourceNames ) { + setFailedAudiences( failedSiteKitAudienceResourceNames ); + } + + setIsSaving( false ); + }, + [ + enableAudienceGroup, + hasAnalytics4EditScope, + setPermissionScopeError, + redirectURL, + setValues, + ] + ); // If the user ends up back on this component with the required scope granted, // and already submitted the form, trigger the submit again. @@ -182,6 +207,12 @@ function AudienceSegmentationSetupCTAWidget( { Widget, WidgetNull } ) { } }; + const setupErrorCode = useSelect( ( select ) => + select( CORE_SITE ).getSetupErrorCode() + ); + + const hasOAuthError = autoSubmit && setupErrorCode; + if ( isTooltipVisible ) { return ( @@ -213,6 +244,36 @@ function AudienceSegmentationSetupCTAWidget( { Widget, WidgetNull } ) { return null; } + if ( hasOAuthError ) { + return ( + + ); + } + + if ( apiErrors.length ) { + return ( + + ); + } + + if ( failedAudiences.length ) { + return ( + onEnableGroups( failedAudiences ) } + inProgress={ isSaving } + /> + ); + } + // TODO: We need to refactor this and the ConsentModeSetupCTAWidget to avoid this duplicate inlining of the widget context and area structure, // and to ensure only one of these setup CTAs is shown at a time. This will be handled in a subsequent issue. return ( From 939fe94c696cbbd38d42958b40f6968f085462f0 Mon Sep 17 00:00:00 2001 From: Arafat Zahan Date: Thu, 27 Jun 2024 15:45:53 +0600 Subject: [PATCH 022/105] Revert `__experimentalResolveSelect` replacement. --- .../SettingsActiveModule/index.test.js | 2 +- .../datastore/site/enable-auto-update.js | 4 +- .../js/googlesitekit/datastore/site/html.js | 4 +- .../datastore/user/feature-tours.js | 10 ++-- .../googlesitekit/datastore/user/surveys.js | 6 +- .../datastore/user/surveys.test.js | 8 ++- .../datastore/user/user-input-settings.js | 5 +- .../modules/create-info-store.js | 7 ++- .../modules/datastore/modules.js | 21 ++++--- .../modules/datastore/modules.test.js | 4 +- .../datastore/sharing-settings.test.js | 58 +++++++++---------- assets/js/modules/ads/index.js | 2 +- assets/js/modules/ads/pax/services.js | 9 ++- assets/js/modules/adsense/index.js | 2 +- .../datastore/audience-settings.js | 2 +- .../analytics-4/datastore/audiences.js | 14 +++-- .../datastore/conversion-events.test.js | 4 +- .../custom-dimensions-gathering-data.js | 19 +++--- .../datastore/custom-dimensions.js | 22 ++++--- .../datastore/enhanced-measurement.js | 4 +- .../datastore/enhanced-measurement.test.js | 4 +- .../analytics-4/datastore/partial-data.js | 23 +++++--- .../analytics-4/datastore/properties.js | 46 +++++++++------ .../js/modules/analytics-4/datastore/tags.js | 2 +- .../analytics-4/datastore/webdatastreams.js | 8 +-- 25 files changed, 167 insertions(+), 123 deletions(-) diff --git a/assets/js/components/settings/SettingsActiveModule/index.test.js b/assets/js/components/settings/SettingsActiveModule/index.test.js index 80ceb0c9ca3..c79d71eca75 100644 --- a/assets/js/components/settings/SettingsActiveModule/index.test.js +++ b/assets/js/components/settings/SettingsActiveModule/index.test.js @@ -247,7 +247,7 @@ describe( 'SettingsModule', () => { // Hack to avoid act error due to state change during render. await act( () => registry - .resolveSelect( CORE_MODULES ) + .__experimentalResolveSelect( CORE_MODULES ) .canActivateModule( 'tagmanager' ) ); diff --git a/assets/js/googlesitekit/datastore/site/enable-auto-update.js b/assets/js/googlesitekit/datastore/site/enable-auto-update.js index db8aac08c55..02c3d15078a 100644 --- a/assets/js/googlesitekit/datastore/site/enable-auto-update.js +++ b/assets/js/googlesitekit/datastore/site/enable-auto-update.js @@ -80,10 +80,10 @@ const baseActions = { const registry = yield Data.commonActions.getRegistry(); yield Data.commonActions.await( - registry.resolveSelect( CORE_USER ).getNonces() + registry.__experimentalResolveSelect( CORE_USER ).getNonces() ); yield Data.commonActions.await( - registry.resolveSelect( CORE_SITE ).getSiteInfo() + registry.__experimentalResolveSelect( CORE_SITE ).getSiteInfo() ); const nonce = registry.select( CORE_USER ).getNonce( 'updates' ); diff --git a/assets/js/googlesitekit/datastore/site/html.js b/assets/js/googlesitekit/datastore/site/html.js index 7330a5e20c0..06d01e0101d 100644 --- a/assets/js/googlesitekit/datastore/site/html.js +++ b/assets/js/googlesitekit/datastore/site/html.js @@ -146,7 +146,9 @@ const baseControls = { [ WAIT_FOR_HTML_FOR_URL ]: createRegistryControl( ( registry ) => ( { payload: { url } } ) => - registry.resolveSelect( CORE_SITE ).getHTMLForURL( url ) + registry + .__experimentalResolveSelect( CORE_SITE ) + .getHTMLForURL( url ) ), [ CHECK_FOR_SETUP_TAG ]: createRegistryControl( ( registry ) => async () => { diff --git a/assets/js/googlesitekit/datastore/user/feature-tours.js b/assets/js/googlesitekit/datastore/user/feature-tours.js index 4935af236a3..711400e60d2 100644 --- a/assets/js/googlesitekit/datastore/user/feature-tours.js +++ b/assets/js/googlesitekit/datastore/user/feature-tours.js @@ -198,10 +198,10 @@ const baseActions = { }, *triggerTourForView( viewContext ) { - const { select, resolveSelect } = yield getRegistry(); + const { select, __experimentalResolveSelect } = yield getRegistry(); yield Data.commonActions.await( - resolveSelect( CORE_USER ).getLastDismissedAt() + __experimentalResolveSelect( CORE_USER ).getLastDismissedAt() ); if ( select( CORE_USER ).areFeatureToursOnCooldown() ) { @@ -241,7 +241,7 @@ const baseControls = { // Only tours with a version after a user's initial Site Kit version should qualify. const initialVersion = await registry - .resolveSelect( CORE_USER ) + .__experimentalResolveSelect( CORE_USER ) .getInitialSiteKitVersion(); if ( ! initialVersion ) { return false; @@ -258,7 +258,7 @@ const baseControls = { // Check if the tour has already been dismissed. // Here we need to first await the underlying selector with the asynchronous resolver. await registry - .resolveSelect( CORE_USER ) + .__experimentalResolveSelect( CORE_USER ) .getDismissedFeatureTourSlugs(); if ( registry.select( CORE_USER ).isTourDismissed( tour.slug ) @@ -281,7 +281,7 @@ const baseControls = { // Check if the tour has already been dismissed. // Here we need to first await the underlying selector with the asynchronous resolver. await registry - .resolveSelect( CORE_USER ) + .__experimentalResolveSelect( CORE_USER ) .getDismissedFeatureTourSlugs(); if ( registry.select( CORE_USER ).isTourDismissed( tour.slug ) diff --git a/assets/js/googlesitekit/datastore/user/surveys.js b/assets/js/googlesitekit/datastore/user/surveys.js index 17d6463e0ca..e4673fd7a65 100644 --- a/assets/js/googlesitekit/datastore/user/surveys.js +++ b/assets/js/googlesitekit/datastore/user/surveys.js @@ -182,12 +182,12 @@ const baseActions = { }, function* ( triggerID, options = {} ) { const { ttl = 0 } = options; - const { select, dispatch, resolveSelect } = + const { select, dispatch, __experimentalResolveSelect } = yield Data.commonActions.getRegistry(); // Wait for user authentication state to be available before selecting. yield Data.commonActions.await( - resolveSelect( CORE_USER ).getAuthentication() + __experimentalResolveSelect( CORE_USER ).getAuthentication() ); if ( ! select( CORE_USER ).isAuthenticated() ) { @@ -196,7 +196,7 @@ const baseActions = { // Await for surveys to be resolved before checking timeouts. yield Data.commonActions.await( - resolveSelect( CORE_USER ).getSurveyTimeouts() + __experimentalResolveSelect( CORE_USER ).getSurveyTimeouts() ); const isTimedOut = diff --git a/assets/js/googlesitekit/datastore/user/surveys.test.js b/assets/js/googlesitekit/datastore/user/surveys.test.js index 6165868f200..60b84e2ec50 100644 --- a/assets/js/googlesitekit/datastore/user/surveys.test.js +++ b/assets/js/googlesitekit/datastore/user/surveys.test.js @@ -222,14 +222,18 @@ describe( 'core/user surveys', () => { provideUserAuthentication( registry ); - await registry.resolveSelect( CORE_USER ).getAuthentication(); + await registry + .__experimentalResolveSelect( CORE_USER ) + .getAuthentication(); muteFetch( surveyTriggerEndpoint ); muteFetch( surveyTimeoutEndpoint ); registry.dispatch( CORE_USER ).receiveGetSurveyTimeouts( [] ); - await registry.resolveSelect( CORE_USER ).getSurveyTimeouts(); + await registry + .__experimentalResolveSelect( CORE_USER ) + .getSurveyTimeouts(); jest.useFakeTimers(); diff --git a/assets/js/googlesitekit/datastore/user/user-input-settings.js b/assets/js/googlesitekit/datastore/user/user-input-settings.js index aab3c7929c8..c5308cb1dc2 100644 --- a/assets/js/googlesitekit/datastore/user/user-input-settings.js +++ b/assets/js/googlesitekit/datastore/user/user-input-settings.js @@ -162,10 +162,11 @@ const baseActions = { * @return {Object} Object with `response` and `error`. */ *maybeTriggerUserInputSurvey() { - const { resolveSelect, dispatch } = yield commonActions.getRegistry(); + const { __experimentalResolveSelect, dispatch } = + yield commonActions.getRegistry(); const settings = yield commonActions.await( - resolveSelect( CORE_USER ).getUserInputSettings() + __experimentalResolveSelect( CORE_USER ).getUserInputSettings() ); const settingsAnsweredOther = Object.keys( settings ).filter( ( key ) => diff --git a/assets/js/googlesitekit/modules/create-info-store.js b/assets/js/googlesitekit/modules/create-info-store.js index c3abb40a0ff..313276f7ece 100644 --- a/assets/js/googlesitekit/modules/create-info-store.js +++ b/assets/js/googlesitekit/modules/create-info-store.js @@ -61,10 +61,11 @@ export const createInfoStore = ( const controls = { [ WAIT_FOR_REAUTH_RESOLVERS ]: createRegistryControl( ( registry ) => async () => { - const { resolveSelect } = registry; + const { __experimentalResolveSelect } = registry; const { getAuthentication, getConnectURL } = - resolveSelect( CORE_USER ); - const { getSiteInfo } = resolveSelect( CORE_SITE ); + __experimentalResolveSelect( CORE_USER ); + const { getSiteInfo } = + __experimentalResolveSelect( CORE_SITE ); await Promise.all( [ // Authentication is needed for checking `needsReauthentication`. diff --git a/assets/js/googlesitekit/modules/datastore/modules.js b/assets/js/googlesitekit/modules/datastore/modules.js index ef9cfcf4f14..70cfcfaad34 100644 --- a/assets/js/googlesitekit/modules/datastore/modules.js +++ b/assets/js/googlesitekit/modules/datastore/modules.js @@ -560,11 +560,13 @@ export const baseControls = { } ), [ SELECT_MODULE_REAUTH_URL ]: createRegistryControl( - ( { select, resolveSelect } ) => + ( { select, __experimentalResolveSelect } ) => async ( { payload } ) => { const { slug } = payload; // Ensure the module is loaded before selecting the store name. - await resolveSelect( CORE_MODULES ).getModule( slug ); + await __experimentalResolveSelect( CORE_MODULES ).getModule( + slug + ); const storeName = select( CORE_MODULES ).getModuleStoreName( slug ); @@ -575,7 +577,9 @@ export const baseControls = { } if ( select( storeName )?.getAdminReauthURL ) { - return await resolveSelect( storeName ).getAdminReauthURL(); + return await __experimentalResolveSelect( + storeName + ).getAdminReauthURL(); } return select( CORE_SITE ).getAdminURL( 'googlesitekit-dashboard' @@ -658,10 +662,11 @@ const baseReducer = ( state, { type, payload } ) => { }; function* waitForModules() { - const { resolveSelect } = yield Data.commonActions.getRegistry(); + const { __experimentalResolveSelect } = + yield Data.commonActions.getRegistry(); yield Data.commonActions.await( - resolveSelect( CORE_MODULES ).getModules() + __experimentalResolveSelect( CORE_MODULES ).getModules() ); } @@ -678,9 +683,9 @@ const baseResolvers = { *canActivateModule( slug ) { const registry = yield Data.commonActions.getRegistry(); - const { select, resolveSelect } = registry; + const { select, __experimentalResolveSelect } = registry; const module = yield Data.commonActions.await( - resolveSelect( CORE_MODULES ).getModule( slug ) + __experimentalResolveSelect( CORE_MODULES ).getModule( slug ) ); // At this point, all modules are loaded so we can safely select getModule below. @@ -746,7 +751,7 @@ const baseResolvers = { *getRecoverableModules() { const registry = yield Data.commonActions.getRegistry(); const modules = yield Data.commonActions.await( - registry.resolveSelect( CORE_MODULES ).getModules() + registry.__experimentalResolveSelect( CORE_MODULES ).getModules() ); const recoverableModules = Object.entries( modules || {} ).reduce( diff --git a/assets/js/googlesitekit/modules/datastore/modules.test.js b/assets/js/googlesitekit/modules/datastore/modules.test.js index de11d30a3eb..fc0a69d86e9 100644 --- a/assets/js/googlesitekit/modules/datastore/modules.test.js +++ b/assets/js/googlesitekit/modules/datastore/modules.test.js @@ -2043,7 +2043,7 @@ describe( 'core/modules modules', () => { provideModules( registry, FIXTURES ); const sharedOwnershipModules = await registry - .resolveSelect( CORE_MODULES ) + .__experimentalResolveSelect( CORE_MODULES ) .getSharedOwnershipModules(); expect( sharedOwnershipModules ).toMatchObject( {} ); @@ -2056,7 +2056,7 @@ describe( 'core/modules modules', () => { provideModules( registry, FIXTURES ); const sharedOwnershipModules = await registry - .resolveSelect( CORE_MODULES ) + .__experimentalResolveSelect( CORE_MODULES ) .getSharedOwnershipModules(); expect( sharedOwnershipModules ).toMatchObject( diff --git a/assets/js/googlesitekit/modules/datastore/sharing-settings.test.js b/assets/js/googlesitekit/modules/datastore/sharing-settings.test.js index b097a631d8e..43622662155 100644 --- a/assets/js/googlesitekit/modules/datastore/sharing-settings.test.js +++ b/assets/js/googlesitekit/modules/datastore/sharing-settings.test.js @@ -240,7 +240,7 @@ describe( 'core/modules sharing-settings', () => { ); await registry - .resolveSelect( CORE_MODULES ) + .__experimentalResolveSelect( CORE_MODULES ) .getSharingSettings(); await registry @@ -368,7 +368,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = undefined; const sharingSettingsObj = await registry - .resolveSelect( CORE_MODULES ) + .__experimentalResolveSelect( CORE_MODULES ) .getSharingSettings(); expect( console ).toHaveErrored(); @@ -381,7 +381,7 @@ describe( 'core/modules sharing-settings', () => { }; const sharingSettingsObj = await registry - .resolveSelect( CORE_MODULES ) + .__experimentalResolveSelect( CORE_MODULES ) .getSharingSettings(); expect( sharingSettingsObj ).toMatchObject( {} ); @@ -391,7 +391,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; const sharingSettingsObj = await registry - .resolveSelect( CORE_MODULES ) + .__experimentalResolveSelect( CORE_MODULES ) .getSharingSettings(); expect( sharingSettingsObj ).toMatchObject( sharingSettings ); @@ -403,7 +403,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = undefined; const shareableRolesObj = await registry - .resolveSelect( CORE_MODULES ) + .__experimentalResolveSelect( CORE_MODULES ) .getShareableRoles(); expect( console ).toHaveErrored(); @@ -416,7 +416,7 @@ describe( 'core/modules sharing-settings', () => { }; const shareableRolesObj = await registry - .resolveSelect( CORE_MODULES ) + .__experimentalResolveSelect( CORE_MODULES ) .getShareableRoles(); expect( shareableRolesObj ).toMatchObject( [] ); @@ -426,7 +426,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; const shareableRolesObj = await registry - .resolveSelect( CORE_MODULES ) + .__experimentalResolveSelect( CORE_MODULES ) .getShareableRoles(); expect( shareableRolesObj ).toMatchObject( shareableRoles ); @@ -444,7 +444,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = undefined; await registry - .resolveSelect( CORE_MODULES ) + .__experimentalResolveSelect( CORE_MODULES ) .getSharingSettings(); const sharingManagement = registry @@ -459,7 +459,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .resolveSelect( CORE_MODULES ) + .__experimentalResolveSelect( CORE_MODULES ) .getSharingSettings(); const sharingManagement = registry @@ -472,7 +472,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .resolveSelect( CORE_MODULES ) + .__experimentalResolveSelect( CORE_MODULES ) .getSharingSettings(); const sharingManagement = registry @@ -494,7 +494,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = undefined; await registry - .resolveSelect( CORE_MODULES ) + .__experimentalResolveSelect( CORE_MODULES ) .getSharingSettings(); const sharedRoles = registry @@ -509,7 +509,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .resolveSelect( CORE_MODULES ) + .__experimentalResolveSelect( CORE_MODULES ) .getSharingSettings(); const sharedRoles = registry @@ -522,7 +522,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .resolveSelect( CORE_MODULES ) + .__experimentalResolveSelect( CORE_MODULES ) .getSharingSettings(); const sharedRoles = registry @@ -545,7 +545,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .resolveSelect( CORE_MODULES ) + .__experimentalResolveSelect( CORE_MODULES ) .getSharingSettings(); // Still false after getting the sharing settings from the global variable. @@ -592,7 +592,7 @@ describe( 'core/modules sharing-settings', () => { it( 'compares all keys when keys argument is not supplied', async () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .resolveSelect( CORE_MODULES ) + .__experimentalResolveSelect( CORE_MODULES ) .getSharingSettings(); // Update the sharing settings so they differ. All values are being checked here. @@ -607,7 +607,7 @@ describe( 'core/modules sharing-settings', () => { it( 'compares select keys when keys argument is supplied', async () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .resolveSelect( CORE_MODULES ) + .__experimentalResolveSelect( CORE_MODULES ) .getSharingSettings(); // Update the sharing settings so they differ. Only `search-console` should trigger @@ -650,7 +650,7 @@ describe( 'core/modules sharing-settings', () => { it( 'informs whether client-side sharing-settings differ from server-side ones', async () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .resolveSelect( CORE_MODULES ) + .__experimentalResolveSelect( CORE_MODULES ) .getSharingSettings(); // True after updating module's `sharedRoles` on the client. @@ -686,7 +686,7 @@ describe( 'core/modules sharing-settings', () => { it( 'should be set to TRUE after starting submiting sharing changes', async () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .resolveSelect( CORE_MODULES ) + .__experimentalResolveSelect( CORE_MODULES ) .getSharingSettings(); freezeFetch( @@ -707,7 +707,7 @@ describe( 'core/modules sharing-settings', () => { it( 'should be set to FALSE after finishing submitting sharing changes', async () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .resolveSelect( CORE_MODULES ) + .__experimentalResolveSelect( CORE_MODULES ) .getSharingSettings(); fetchMock.postOnce( @@ -763,7 +763,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .resolveSelect( CORE_MODULES ) + .__experimentalResolveSelect( CORE_MODULES ) .getSharingSettings(); // Still false after getting the sharing settings from the global variable. @@ -816,7 +816,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .resolveSelect( CORE_MODULES ) + .__experimentalResolveSelect( CORE_MODULES ) .getSharingSettings(); // Still false after getting the sharing settings from the global variable. @@ -862,7 +862,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .resolveSelect( CORE_MODULES ) + .__experimentalResolveSelect( CORE_MODULES ) .getSharingSettings(); // Still false after getting the sharing settings from the global variable. @@ -898,7 +898,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .resolveSelect( CORE_MODULES ) + .__experimentalResolveSelect( CORE_MODULES ) .getSharingSettings(); // Still false after getting the sharing settings from the global variable. @@ -955,7 +955,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .resolveSelect( CORE_MODULES ) + .__experimentalResolveSelect( CORE_MODULES ) .getSharingSettings(); // Still false after getting the sharing settings from the global variable. @@ -1024,7 +1024,7 @@ describe( 'core/modules sharing-settings', () => { it( 'compares all keys when keys argument is not supplied', async () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .resolveSelect( CORE_MODULES ) + .__experimentalResolveSelect( CORE_MODULES ) .getSharingSettings(); // Update the sharing settings so they differ. All values are being checked here. @@ -1041,7 +1041,7 @@ describe( 'core/modules sharing-settings', () => { it( 'compares selected keys when keys argument is supplied', async () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .resolveSelect( CORE_MODULES ) + .__experimentalResolveSelect( CORE_MODULES ) .getSharingSettings(); // Update the sharing settings so they differ. Only `search-console` should trigger @@ -1099,7 +1099,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = undefined; const defaultSharedOwnershipModuleSettingsObj = await registry - .resolveSelect( CORE_MODULES ) + .__experimentalResolveSelect( CORE_MODULES ) .getDefaultSharedOwnershipModuleSettings(); expect( console ).toHaveErrored(); @@ -1114,7 +1114,7 @@ describe( 'core/modules sharing-settings', () => { }; const defaultSharedOwnershipModuleSettingsObj = await registry - .resolveSelect( CORE_MODULES ) + .__experimentalResolveSelect( CORE_MODULES ) .getDefaultSharedOwnershipModuleSettings(); expect( defaultSharedOwnershipModuleSettingsObj ).toMatchObject( @@ -1128,7 +1128,7 @@ describe( 'core/modules sharing-settings', () => { }; const defaultSharedOwnershipModuleSettingsObj = await registry - .resolveSelect( CORE_MODULES ) + .__experimentalResolveSelect( CORE_MODULES ) .getDefaultSharedOwnershipModuleSettings(); expect( defaultSharedOwnershipModuleSettingsObj ).toMatchObject( diff --git a/assets/js/modules/ads/index.js b/assets/js/modules/ads/index.js index 09c85f9c45a..e704f53000f 100644 --- a/assets/js/modules/ads/index.js +++ b/assets/js/modules/ads/index.js @@ -62,7 +62,7 @@ export const registerModule = ( modules ) => { ], checkRequirements: async ( registry ) => { const adBlockerActive = await registry - .resolveSelect( CORE_USER ) + .__experimentalResolveSelect( CORE_USER ) .isAdBlockerActive(); if ( ! adBlockerActive ) { diff --git a/assets/js/modules/ads/pax/services.js b/assets/js/modules/ads/pax/services.js index f1a16ab1cbb..4722260138b 100644 --- a/assets/js/modules/ads/pax/services.js +++ b/assets/js/modules/ads/pax/services.js @@ -60,7 +60,8 @@ const restFetchWpPages = async () => { export function createPaxServices( registry, options = {} ) { const { onCampaignCreated = null, _global = global } = options; - const { select, resolveSelect: resolveSelect } = registry; + const { select, __experimentalResolveSelect: __experimentalResolveSelect } = + registry; const accessToken = _global?._googlesitekitPAXConfig?.authAccess?.oauthTokenAccess?.token; @@ -85,7 +86,7 @@ export function createPaxServices( registry, options = {} ) { }, businessService: { getBusinessInfo: async () => { - await resolveSelect( CORE_SITE ).getSiteInfo(); + await __experimentalResolveSelect( CORE_SITE ).getSiteInfo(); /* eslint-disable sitekit/acronym-case */ // Disabling rule because businessName and businessUrl are expected by PAX API. @@ -102,7 +103,9 @@ export function createPaxServices( registry, options = {} ) { }, conversionTrackingService: { getSupportedConversionLabels: async () => { - await resolveSelect( MODULES_ADS ).getModuleData(); + await __experimentalResolveSelect( + MODULES_ADS + ).getModuleData(); const conversionEvents = select( MODULES_ADS ).getSupportedConversionEvents() || []; diff --git a/assets/js/modules/adsense/index.js b/assets/js/modules/adsense/index.js index 2f7b734145c..29f38c0cb46 100644 --- a/assets/js/modules/adsense/index.js +++ b/assets/js/modules/adsense/index.js @@ -68,7 +68,7 @@ export const registerModule = ( modules ) => { ], checkRequirements: async ( registry ) => { const adBlockerActive = await registry - .resolveSelect( CORE_USER ) + .__experimentalResolveSelect( CORE_USER ) .isAdBlockerActive(); if ( ! adBlockerActive ) { diff --git a/assets/js/modules/analytics-4/datastore/audience-settings.js b/assets/js/modules/analytics-4/datastore/audience-settings.js index 15071bff614..fe3bf40e31c 100644 --- a/assets/js/modules/analytics-4/datastore/audience-settings.js +++ b/assets/js/modules/analytics-4/datastore/audience-settings.js @@ -118,7 +118,7 @@ const baseActions = { const registry = yield Data.commonActions.getRegistry(); const audienceSettings = yield Data.commonActions.await( registry - .resolveSelect( MODULES_ANALYTICS_4 ) + .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) .getAudienceSettings() ); diff --git a/assets/js/modules/analytics-4/datastore/audiences.js b/assets/js/modules/analytics-4/datastore/audiences.js index d377acd9596..28946459364 100644 --- a/assets/js/modules/analytics-4/datastore/audiences.js +++ b/assets/js/modules/analytics-4/datastore/audiences.js @@ -55,7 +55,7 @@ async function getNonZeroDataAudiencesSortedByTotalUsers( startDate, endDate ) { - const { select, resolveSelect } = registry; + const { select, __experimentalResolveSelect } = registry; const reportOptions = { metrics: [ { name: 'totalUsers' } ], @@ -67,9 +67,9 @@ async function getNonZeroDataAudiencesSortedByTotalUsers( endDate, }; - const report = await resolveSelect( MODULES_ANALYTICS_4 ).getReport( - reportOptions - ); + const report = await __experimentalResolveSelect( + MODULES_ANALYTICS_4 + ).getReport( reportOptions ); const error = select( MODULES_ANALYTICS_4 ).getErrorForSelector( 'getReport', @@ -189,7 +189,7 @@ const baseActions = { *enableAudienceGroup() { const registry = yield Data.commonActions.getRegistry(); - const { dispatch, select, resolveSelect } = registry; + const { dispatch, select, __experimentalResolveSelect } = registry; const { response: availableAudiences, error: syncError } = yield Data.commonActions.await( @@ -303,7 +303,9 @@ const baseActions = { // Create custom dimension if it doesn't exist. yield Data.commonActions.await( - resolveSelect( MODULES_ANALYTICS_4 ).getAvailableCustomDimensions() + __experimentalResolveSelect( + MODULES_ANALYTICS_4 + ).getAvailableCustomDimensions() ); if ( diff --git a/assets/js/modules/analytics-4/datastore/conversion-events.test.js b/assets/js/modules/analytics-4/datastore/conversion-events.test.js index 80ff5d35fdc..5e90c7df6bb 100644 --- a/assets/js/modules/analytics-4/datastore/conversion-events.test.js +++ b/assets/js/modules/analytics-4/datastore/conversion-events.test.js @@ -64,7 +64,7 @@ describe( 'modules/analytics-4 conversion-events', () => { expect( initialConversionEvents ).toBeUndefined(); const conversionEvents = await registry - .resolveSelect( MODULES_ANALYTICS_4 ) + .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) .getConversionEvents(); expect( fetchMock ).toHaveFetchedTimes( 1 ); @@ -110,7 +110,7 @@ describe( 'modules/analytics-4 conversion-events', () => { ); await registry - .resolveSelect( MODULES_ANALYTICS_4 ) + .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) .getConversionEvents(); expect( fetchMock ).toHaveFetchedTimes( 1 ); diff --git a/assets/js/modules/analytics-4/datastore/custom-dimensions-gathering-data.js b/assets/js/modules/analytics-4/datastore/custom-dimensions-gathering-data.js index 2cf7fd47863..90809e5fc92 100644 --- a/assets/js/modules/analytics-4/datastore/custom-dimensions-gathering-data.js +++ b/assets/js/modules/analytics-4/datastore/custom-dimensions-gathering-data.js @@ -115,11 +115,11 @@ const baseActions = { * @param {string} customDimension Custom dimension slug. */ *checkCustomDimensionDataAvailability( customDimension ) { - const { select, resolveSelect } = + const { select, __experimentalResolveSelect } = yield Data.commonActions.getRegistry(); yield Data.commonActions.await( - resolveSelect( MODULES_ANALYTICS_4 ).getSettings() + __experimentalResolveSelect( MODULES_ANALYTICS_4 ).getSettings() ); if ( @@ -135,7 +135,7 @@ const baseActions = { } yield Data.commonActions.await( - resolveSelect( CORE_USER ).getAuthentication() + __experimentalResolveSelect( CORE_USER ).getAuthentication() ); if ( ! select( CORE_USER ).isAuthenticated() ) { @@ -147,7 +147,7 @@ const baseActions = { } const reportArgs = yield Data.commonActions.await( - resolveSelect( + __experimentalResolveSelect( MODULES_ANALYTICS_4 ).getDataAvailabilityReportOptions( customDimension ) ); @@ -161,7 +161,9 @@ const baseActions = { } const report = yield Data.commonActions.await( - resolveSelect( MODULES_ANALYTICS_4 ).getReport( reportArgs ) + __experimentalResolveSelect( MODULES_ANALYTICS_4 ).getReport( + reportArgs + ) ); const hasReportError = !! select( @@ -240,10 +242,13 @@ const baseResolvers = { }, *getDataAvailabilityReportOptions() { - const { resolveSelect } = yield Data.commonActions.getRegistry(); + const { __experimentalResolveSelect } = + yield Data.commonActions.getRegistry(); yield Data.commonActions.await( - resolveSelect( MODULES_ANALYTICS_4 ).getPropertyCreateTime() + __experimentalResolveSelect( + MODULES_ANALYTICS_4 + ).getPropertyCreateTime() ); }, }; diff --git a/assets/js/modules/analytics-4/datastore/custom-dimensions.js b/assets/js/modules/analytics-4/datastore/custom-dimensions.js index 8e2db657bd4..3eda0bcdd8a 100644 --- a/assets/js/modules/analytics-4/datastore/custom-dimensions.js +++ b/assets/js/modules/analytics-4/datastore/custom-dimensions.js @@ -115,9 +115,15 @@ const baseActions = { // Wait for the necessary settings to be loaded before checking. yield Data.commonActions.await( Promise.all( [ - registry.resolveSelect( MODULES_ANALYTICS_4 ).getSettings(), - registry.resolveSelect( CORE_USER ).getKeyMetricsSettings(), - registry.resolveSelect( CORE_USER ).getUserInputSettings(), + registry + .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) + .getSettings(), + registry + .__experimentalResolveSelect( CORE_USER ) + .getKeyMetricsSettings(), + registry + .__experimentalResolveSelect( CORE_USER ) + .getUserInputSettings(), ] ) ); @@ -281,12 +287,14 @@ export const baseReducer = ( state, { type, payload } ) => { const baseResolvers = { *getAvailableCustomDimensions() { - const { select, resolveSelect } = + const { select, __experimentalResolveSelect } = yield Data.commonActions.getRegistry(); const { isAuthenticated, hasCapability } = select( CORE_USER ); const isGA4Connected = yield Data.commonActions.await( - resolveSelect( CORE_MODULES ).isModuleConnected( 'analytics-4' ) + __experimentalResolveSelect( CORE_MODULES ).isModuleConnected( + 'analytics-4' + ) ); if ( ! isGA4Connected ) { @@ -295,7 +303,7 @@ const baseResolvers = { // Wait for settings to be loaded before proceeding. yield Data.commonActions.await( - resolveSelect( MODULES_ANALYTICS_4 ).getSettings() + __experimentalResolveSelect( MODULES_ANALYTICS_4 ).getSettings() ); const availableCustomDimensions = @@ -307,7 +315,7 @@ const baseResolvers = { // Wait for permissions to be loaded before checking if the user can manage options. yield Data.commonActions.await( - resolveSelect( CORE_USER ).getCapabilities() + __experimentalResolveSelect( CORE_USER ).getCapabilities() ); if ( ! hasCapability( PERMISSION_MANAGE_OPTIONS ) ) { diff --git a/assets/js/modules/analytics-4/datastore/enhanced-measurement.js b/assets/js/modules/analytics-4/datastore/enhanced-measurement.js index afc439cbd00..415f6178a8a 100644 --- a/assets/js/modules/analytics-4/datastore/enhanced-measurement.js +++ b/assets/js/modules/analytics-4/datastore/enhanced-measurement.js @@ -224,7 +224,7 @@ const baseActions = { const currentSettings = yield Data.commonActions.await( registry - .resolveSelect( MODULES_ANALYTICS_4 ) + .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) .getEnhancedMeasurementSettings( propertyID, webDataStreamID @@ -298,7 +298,7 @@ const baseActions = { const currentSettings = yield Data.commonActions.await( registry - .resolveSelect( MODULES_ANALYTICS_4 ) + .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) .getEnhancedMeasurementSettings( propertyID, webDataStreamID diff --git a/assets/js/modules/analytics-4/datastore/enhanced-measurement.test.js b/assets/js/modules/analytics-4/datastore/enhanced-measurement.test.js index 20d7efd783e..e1bc176ba60 100644 --- a/assets/js/modules/analytics-4/datastore/enhanced-measurement.test.js +++ b/assets/js/modules/analytics-4/datastore/enhanced-measurement.test.js @@ -356,7 +356,7 @@ describe( 'modules/analytics-4 enhanced-measurement', () => { expect( initialSettings ).toBeUndefined(); const finalSettings = await registry - .resolveSelect( MODULES_ANALYTICS_4 ) + .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) .getEnhancedMeasurementSettings( propertyID, webDataStreamID @@ -488,7 +488,7 @@ describe( 'modules/analytics-4 enhanced-measurement', () => { expect( initialStreamEnabled ).toBeUndefined(); const finalStreamEnabled = await registry - .resolveSelect( MODULES_ANALYTICS_4 ) + .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) .isEnhancedMeasurementStreamAlreadyEnabled( propertyID, webDataStreamID diff --git a/assets/js/modules/analytics-4/datastore/partial-data.js b/assets/js/modules/analytics-4/datastore/partial-data.js index e4895234e6a..360d6f6ab90 100644 --- a/assets/js/modules/analytics-4/datastore/partial-data.js +++ b/assets/js/modules/analytics-4/datastore/partial-data.js @@ -203,7 +203,7 @@ const baseResolvers = { }, *getResourceDataAvailabilityDate( resourceSlug, resourceType ) { - const { select, resolveSelect } = + const { select, __experimentalResolveSelect } = yield Data.commonActions.getRegistry(); if ( @@ -216,7 +216,7 @@ const baseResolvers = { } const resourceAvailabilityDates = yield Data.commonActions.await( - resolveSelect( + __experimentalResolveSelect( MODULES_ANALYTICS_4 ).getResourceDataAvailabilityDates() ); @@ -227,14 +227,14 @@ const baseResolvers = { ) { // Ensure the settings are loaded. yield Data.commonActions.await( - resolveSelect( MODULES_ANALYTICS_4 ).getSettings() + __experimentalResolveSelect( MODULES_ANALYTICS_4 ).getSettings() ); // Validate if the resourceSlug is a valid resource. switch ( resourceType ) { case RESOURCE_TYPE_AUDIENCE: yield Data.commonActions.await( - resolveSelect( + __experimentalResolveSelect( MODULES_ANALYTICS_4 ).getAvailableAudiences() ); @@ -272,7 +272,7 @@ const baseResolvers = { } yield Data.commonActions.await( - resolveSelect( CORE_USER ).getAuthentication() + __experimentalResolveSelect( CORE_USER ).getAuthentication() ); // Return early if user is not authenticated. @@ -286,7 +286,7 @@ const baseResolvers = { } const reportArgs = yield Data.commonActions.await( - resolveSelect( + __experimentalResolveSelect( MODULES_ANALYTICS_4 ).getPartialDataReportOptions( resourceSlug, resourceType ) ); @@ -297,7 +297,9 @@ const baseResolvers = { } const report = yield Data.commonActions.await( - resolveSelect( MODULES_ANALYTICS_4 ).getReport( reportArgs ) + __experimentalResolveSelect( MODULES_ANALYTICS_4 ).getReport( + reportArgs + ) ); const hasReportError = !! select( @@ -335,10 +337,13 @@ const baseResolvers = { }, *getPartialDataReportOptions() { - const { resolveSelect } = yield Data.commonActions.getRegistry(); + const { __experimentalResolveSelect } = + yield Data.commonActions.getRegistry(); yield Data.commonActions.await( - resolveSelect( MODULES_ANALYTICS_4 ).getPropertyCreateTime() + __experimentalResolveSelect( + MODULES_ANALYTICS_4 + ).getPropertyCreateTime() ); }, }; diff --git a/assets/js/modules/analytics-4/datastore/properties.js b/assets/js/modules/analytics-4/datastore/properties.js index ced9c1ee64a..860f3491deb 100644 --- a/assets/js/modules/analytics-4/datastore/properties.js +++ b/assets/js/modules/analytics-4/datastore/properties.js @@ -254,7 +254,7 @@ const baseActions = { if ( propertyID ) { const property = yield Data.commonActions.await( registry - .resolveSelect( MODULES_ANALYTICS_4 ) + .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) .getProperty( propertyID ) ); @@ -302,7 +302,9 @@ const baseActions = { *findMatchedProperty() { const registry = yield commonActions.getRegistry(); const accounts = yield Data.commonActions.await( - registry.resolveSelect( MODULES_ANALYTICS_4 ).getAccountSummaries() + registry + .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) + .getAccountSummaries() ); if ( ! Array.isArray( accounts ) || accounts.length === 0 ) { @@ -406,7 +408,7 @@ const baseActions = { ); const webdatastreams = yield commonActions.await( registry - .resolveSelect( MODULES_ANALYTICS_4 ) + .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) .getWebDataStreamsBatch( chunk ) ); @@ -422,7 +424,9 @@ const baseActions = { ) { return yield commonActions.await( registry - .resolveSelect( MODULES_ANALYTICS_4 ) + .__experimentalResolveSelect( + MODULES_ANALYTICS_4 + ) .getProperty( propertyID ) ); } @@ -460,7 +464,7 @@ const baseActions = { ); const webdatastreams = yield commonActions.await( registry - .resolveSelect( MODULES_ANALYTICS_4 ) + .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) .getWebDataStreamsBatch( chunk ) ); @@ -473,7 +477,9 @@ const baseActions = { ) { return yield commonActions.await( registry - .resolveSelect( MODULES_ANALYTICS_4 ) + .__experimentalResolveSelect( + MODULES_ANALYTICS_4 + ) .getProperty( propertyID ) ); } @@ -505,7 +511,7 @@ const baseActions = { * @param {string} measurementID Measurement ID. */ *updateSettingsForMeasurementID( measurementID ) { - const { select, dispatch, resolveSelect } = + const { select, dispatch, __experimentalResolveSelect } = yield commonActions.getRegistry(); if ( ! measurementID ) { @@ -522,7 +528,7 @@ const baseActions = { // Wait for authentication to be resolved to check scopes. yield commonActions.await( - resolveSelect( CORE_USER ).getAuthentication() + __experimentalResolveSelect( CORE_USER ).getAuthentication() ); if ( ! select( CORE_USER ).hasScope( TAGMANAGER_READ_SCOPE ) ) { return; @@ -589,7 +595,7 @@ const baseActions = { * @since 1.95.0 */ *syncGoogleTagSettings() { - const { select, dispatch, resolveSelect } = + const { select, dispatch, __experimentalResolveSelect } = yield Data.commonActions.getRegistry(); const hasTagManagerReadScope = select( CORE_USER ).hasScope( @@ -602,7 +608,7 @@ const baseActions = { // Wait for modules to be available before selecting. yield Data.commonActions.await( - resolveSelect( CORE_MODULES ).getModules() + __experimentalResolveSelect( CORE_MODULES ).getModules() ); const { isModuleConnected } = select( CORE_MODULES ); @@ -613,7 +619,7 @@ const baseActions = { // Wait for module settings to be available before selecting. yield Data.commonActions.await( - resolveSelect( MODULES_ANALYTICS_4 ).getSettings() + __experimentalResolveSelect( MODULES_ANALYTICS_4 ).getSettings() ); const { @@ -643,9 +649,9 @@ const baseActions = { if ( !! googleTagID ) { const googleTagContainer = yield Data.commonActions.await( - resolveSelect( MODULES_ANALYTICS_4 ).getGoogleTagContainer( - measurementID - ) + __experimentalResolveSelect( + MODULES_ANALYTICS_4 + ).getGoogleTagContainer( measurementID ) ); if ( ! googleTagContainer ) { @@ -661,7 +667,7 @@ const baseActions = { const googleTagContainerID = getGoogleTagContainerID(); const googleTagContainerDestinations = yield Data.commonActions.await( - resolveSelect( + __experimentalResolveSelect( MODULES_ANALYTICS_4 ).getGoogleTagContainerDestinations( googleTagAccountID, @@ -686,9 +692,9 @@ const baseActions = { const baseControls = { [ WAIT_FOR_PROPERTY_SUMMARIES ]: createRegistryControl( - ( { resolveSelect } ) => { + ( { __experimentalResolveSelect } ) => { return async () => { - await resolveSelect( + await __experimentalResolveSelect( MODULES_ANALYTICS_4 ).getAccountSummaries(); }; @@ -745,7 +751,9 @@ const baseResolvers = { const registry = yield Data.commonActions.getRegistry(); // Ensure settings are available to select. yield Data.commonActions.await( - registry.resolveSelect( MODULES_ANALYTICS_4 ).getSettings() + registry + .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) + .getSettings() ); const propertyID = registry @@ -776,7 +784,7 @@ const baseResolvers = { const property = yield Data.commonActions.await( registry - .resolveSelect( MODULES_ANALYTICS_4 ) + .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) .getProperty( propertyID ) ); diff --git a/assets/js/modules/analytics-4/datastore/tags.js b/assets/js/modules/analytics-4/datastore/tags.js index 310c7673a17..2b6b57fdbb9 100644 --- a/assets/js/modules/analytics-4/datastore/tags.js +++ b/assets/js/modules/analytics-4/datastore/tags.js @@ -47,7 +47,7 @@ existingTagStore.resolvers.getExistingTag = function* () { if ( existingTag !== null ) { const container = yield Data.commonActions.await( registry - .resolveSelect( MODULES_ANALYTICS_4 ) + .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) .getGoogleTagContainer( existingTag ) ); diff --git a/assets/js/modules/analytics-4/datastore/webdatastreams.js b/assets/js/modules/analytics-4/datastore/webdatastreams.js index 067a4d0be5a..c69495fbeea 100644 --- a/assets/js/modules/analytics-4/datastore/webdatastreams.js +++ b/assets/js/modules/analytics-4/datastore/webdatastreams.js @@ -213,12 +213,12 @@ const baseActions = { const baseControls = { [ WAIT_FOR_WEBDATASTREAMS ]: createRegistryControl( - ( { resolveSelect } ) => { + ( { __experimentalResolveSelect } ) => { return async ( { payload } ) => { const { propertyID } = payload; - await resolveSelect( MODULES_ANALYTICS_4 ).getWebDataStreams( - propertyID - ); + await __experimentalResolveSelect( + MODULES_ANALYTICS_4 + ).getWebDataStreams( propertyID ); }; } ), From 6d671b78199143722a112ead146e7b7d4fc66c0b Mon Sep 17 00:00:00 2001 From: Arafat Zahan Date: Thu, 27 Jun 2024 15:53:26 +0600 Subject: [PATCH 023/105] Update package-locl.json. --- package-lock.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/package-lock.json b/package-lock.json index 91e4e49e3b1..e522be0e865 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33231,6 +33231,12 @@ "@types/glob": "*" } }, + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, "global": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", @@ -33345,6 +33351,12 @@ "delegate": "^3.1.2" } }, + "google-pax-sdk": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/google-pax-sdk/-/google-pax-sdk-1.0.9.tgz", + "integrity": "sha512-faog5mRUIw0PD8RC5KvPXrw1m//oEDFjzu33A9m044C4+TejO/C0azDxud8BoVY7Dl9FfJ2lOmJPO61t2/rR+A==", + "dev": true + }, "gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", From 933a7592aaacd59318a7a80032b09f7a13fb8593 Mon Sep 17 00:00:00 2001 From: Arafat Zahan Date: Thu, 27 Jun 2024 15:54:32 +0600 Subject: [PATCH 024/105] Replace usage of `__experimentalResolveSelect` with `resolveSelect`. --- .../SettingsActiveModule/index.test.js | 2 +- assets/js/components/setup/ModuleSetup.js | 4 +- .../datastore/site/enable-auto-update.js | 4 +- .../js/googlesitekit/datastore/site/html.js | 4 +- .../datastore/user/feature-tours.js | 10 ++-- .../googlesitekit/datastore/user/surveys.js | 6 +- .../datastore/user/surveys.test.js | 8 +-- .../datastore/user/user-input-settings.js | 5 +- .../modules/create-info-store.js | 7 +-- .../modules/datastore/modules.js | 22 +++---- .../modules/datastore/modules.test.js | 4 +- .../datastore/sharing-settings.test.js | 58 +++++++++---------- .../ads/components/setup/SetupMainPAX.js | 4 +- assets/js/modules/ads/index.js | 2 +- assets/js/modules/ads/pax/services.js | 8 +-- assets/js/modules/adsense/index.js | 2 +- .../datastore/audience-settings.js | 4 +- .../analytics-4/datastore/audiences.js | 14 ++--- .../datastore/conversion-events.test.js | 4 +- .../custom-dimensions-gathering-data.js | 20 +++---- .../datastore/custom-dimensions.js | 23 +++----- .../datastore/enhanced-measurement.js | 4 +- .../datastore/enhanced-measurement.test.js | 4 +- .../analytics-4/datastore/partial-data.js | 24 +++----- .../analytics-4/datastore/properties.js | 48 ++++++--------- .../js/modules/analytics-4/datastore/tags.js | 2 +- .../analytics-4/datastore/webdatastreams.js | 8 +-- 27 files changed, 128 insertions(+), 177 deletions(-) diff --git a/assets/js/components/settings/SettingsActiveModule/index.test.js b/assets/js/components/settings/SettingsActiveModule/index.test.js index c79d71eca75..80ceb0c9ca3 100644 --- a/assets/js/components/settings/SettingsActiveModule/index.test.js +++ b/assets/js/components/settings/SettingsActiveModule/index.test.js @@ -247,7 +247,7 @@ describe( 'SettingsModule', () => { // Hack to avoid act error due to state change during render. await act( () => registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .canActivateModule( 'tagmanager' ) ); diff --git a/assets/js/components/setup/ModuleSetup.js b/assets/js/components/setup/ModuleSetup.js index e1437082525..78c1c76754b 100644 --- a/assets/js/components/setup/ModuleSetup.js +++ b/assets/js/components/setup/ModuleSetup.js @@ -71,8 +71,8 @@ export default function ModuleSetup( { moduleSlug } ) { return; } - const { select, __experimentalResolveSelect } = registry; - await __experimentalResolveSelect( CORE_SITE ).getSiteInfo(); + const { select, resolveSelect } = registry; + await resolveSelect( CORE_SITE ).getSiteInfo(); const adminURL = select( CORE_SITE ).getAdminURL( 'googlesitekit-dashboard', { diff --git a/assets/js/googlesitekit/datastore/site/enable-auto-update.js b/assets/js/googlesitekit/datastore/site/enable-auto-update.js index e286480abb2..b8b39e885bd 100644 --- a/assets/js/googlesitekit/datastore/site/enable-auto-update.js +++ b/assets/js/googlesitekit/datastore/site/enable-auto-update.js @@ -83,10 +83,10 @@ const baseActions = { const registry = yield commonActions.getRegistry(); yield commonActions.await( - registry.__experimentalResolveSelect( CORE_USER ).getNonces() + registry.resolveSelect( CORE_USER ).getNonces() ); yield commonActions.await( - registry.__experimentalResolveSelect( CORE_SITE ).getSiteInfo() + registry.resolveSelect( CORE_SITE ).getSiteInfo() ); const nonce = registry.select( CORE_USER ).getNonce( 'updates' ); diff --git a/assets/js/googlesitekit/datastore/site/html.js b/assets/js/googlesitekit/datastore/site/html.js index bac9069f042..20022da0fda 100644 --- a/assets/js/googlesitekit/datastore/site/html.js +++ b/assets/js/googlesitekit/datastore/site/html.js @@ -151,9 +151,7 @@ const baseControls = { [ WAIT_FOR_HTML_FOR_URL ]: createRegistryControl( ( registry ) => ( { payload: { url } } ) => - registry - .__experimentalResolveSelect( CORE_SITE ) - .getHTMLForURL( url ) + registry.resolveSelect( CORE_SITE ).getHTMLForURL( url ) ), [ CHECK_FOR_SETUP_TAG ]: createRegistryControl( ( registry ) => async () => { diff --git a/assets/js/googlesitekit/datastore/user/feature-tours.js b/assets/js/googlesitekit/datastore/user/feature-tours.js index 2cdae4a27d7..7ea2fd9b61f 100644 --- a/assets/js/googlesitekit/datastore/user/feature-tours.js +++ b/assets/js/googlesitekit/datastore/user/feature-tours.js @@ -204,10 +204,10 @@ const baseActions = { }, *triggerTourForView( viewContext ) { - const { select, __experimentalResolveSelect } = yield getRegistry(); + const { select, resolveSelect } = yield getRegistry(); yield commonActions.await( - __experimentalResolveSelect( CORE_USER ).getLastDismissedAt() + resolveSelect( CORE_USER ).getLastDismissedAt() ); if ( select( CORE_USER ).areFeatureToursOnCooldown() ) { @@ -247,7 +247,7 @@ const baseControls = { // Only tours with a version after a user's initial Site Kit version should qualify. const initialVersion = await registry - .__experimentalResolveSelect( CORE_USER ) + .resolveSelect( CORE_USER ) .getInitialSiteKitVersion(); if ( ! initialVersion ) { return false; @@ -264,7 +264,7 @@ const baseControls = { // Check if the tour has already been dismissed. // Here we need to first await the underlying selector with the asynchronous resolver. await registry - .__experimentalResolveSelect( CORE_USER ) + .resolveSelect( CORE_USER ) .getDismissedFeatureTourSlugs(); if ( registry.select( CORE_USER ).isTourDismissed( tour.slug ) @@ -287,7 +287,7 @@ const baseControls = { // Check if the tour has already been dismissed. // Here we need to first await the underlying selector with the asynchronous resolver. await registry - .__experimentalResolveSelect( CORE_USER ) + .resolveSelect( CORE_USER ) .getDismissedFeatureTourSlugs(); if ( registry.select( CORE_USER ).isTourDismissed( tour.slug ) diff --git a/assets/js/googlesitekit/datastore/user/surveys.js b/assets/js/googlesitekit/datastore/user/surveys.js index a6316310750..102bb8aaef3 100644 --- a/assets/js/googlesitekit/datastore/user/surveys.js +++ b/assets/js/googlesitekit/datastore/user/surveys.js @@ -185,12 +185,12 @@ const baseActions = { }, function* ( triggerID, options = {} ) { const { ttl = 0 } = options; - const { select, dispatch, __experimentalResolveSelect } = + const { select, dispatch, resolveSelect } = yield commonActions.getRegistry(); // Wait for user authentication state to be available before selecting. yield commonActions.await( - __experimentalResolveSelect( CORE_USER ).getAuthentication() + resolveSelect( CORE_USER ).getAuthentication() ); if ( ! select( CORE_USER ).isAuthenticated() ) { @@ -199,7 +199,7 @@ const baseActions = { // Await for surveys to be resolved before checking timeouts. yield commonActions.await( - __experimentalResolveSelect( CORE_USER ).getSurveyTimeouts() + resolveSelect( CORE_USER ).getSurveyTimeouts() ); const isTimedOut = diff --git a/assets/js/googlesitekit/datastore/user/surveys.test.js b/assets/js/googlesitekit/datastore/user/surveys.test.js index 60b84e2ec50..6165868f200 100644 --- a/assets/js/googlesitekit/datastore/user/surveys.test.js +++ b/assets/js/googlesitekit/datastore/user/surveys.test.js @@ -222,18 +222,14 @@ describe( 'core/user surveys', () => { provideUserAuthentication( registry ); - await registry - .__experimentalResolveSelect( CORE_USER ) - .getAuthentication(); + await registry.resolveSelect( CORE_USER ).getAuthentication(); muteFetch( surveyTriggerEndpoint ); muteFetch( surveyTimeoutEndpoint ); registry.dispatch( CORE_USER ).receiveGetSurveyTimeouts( [] ); - await registry - .__experimentalResolveSelect( CORE_USER ) - .getSurveyTimeouts(); + await registry.resolveSelect( CORE_USER ).getSurveyTimeouts(); jest.useFakeTimers(); diff --git a/assets/js/googlesitekit/datastore/user/user-input-settings.js b/assets/js/googlesitekit/datastore/user/user-input-settings.js index 75f46bb9932..fca02182ca0 100644 --- a/assets/js/googlesitekit/datastore/user/user-input-settings.js +++ b/assets/js/googlesitekit/datastore/user/user-input-settings.js @@ -165,11 +165,10 @@ const baseActions = { * @return {Object} Object with `response` and `error`. */ *maybeTriggerUserInputSurvey() { - const { __experimentalResolveSelect, dispatch } = - yield commonActions.getRegistry(); + const { resolveSelect, dispatch } = yield commonActions.getRegistry(); const settings = yield commonActions.await( - __experimentalResolveSelect( CORE_USER ).getUserInputSettings() + resolveSelect( CORE_USER ).getUserInputSettings() ); const settingsAnsweredOther = Object.keys( settings ).filter( ( key ) => diff --git a/assets/js/googlesitekit/modules/create-info-store.js b/assets/js/googlesitekit/modules/create-info-store.js index a857920dba6..83357383f4b 100644 --- a/assets/js/googlesitekit/modules/create-info-store.js +++ b/assets/js/googlesitekit/modules/create-info-store.js @@ -62,11 +62,10 @@ export const createInfoStore = ( const controls = { [ WAIT_FOR_REAUTH_RESOLVERS ]: createRegistryControl( ( registry ) => async () => { - const { __experimentalResolveSelect } = registry; + const { resolveSelect } = registry; const { getAuthentication, getConnectURL } = - __experimentalResolveSelect( CORE_USER ); - const { getSiteInfo } = - __experimentalResolveSelect( CORE_SITE ); + resolveSelect( CORE_USER ); + const { getSiteInfo } = resolveSelect( CORE_SITE ); await Promise.all( [ // Authentication is needed for checking `needsReauthentication`. diff --git a/assets/js/googlesitekit/modules/datastore/modules.js b/assets/js/googlesitekit/modules/datastore/modules.js index a637625bb14..2fe618e71b7 100644 --- a/assets/js/googlesitekit/modules/datastore/modules.js +++ b/assets/js/googlesitekit/modules/datastore/modules.js @@ -563,13 +563,11 @@ export const baseControls = { } ), [ SELECT_MODULE_REAUTH_URL ]: createRegistryControl( - ( { select, __experimentalResolveSelect } ) => + ( { select, resolveSelect } ) => async ( { payload } ) => { const { slug } = payload; // Ensure the module is loaded before selecting the store name. - await __experimentalResolveSelect( CORE_MODULES ).getModule( - slug - ); + await resolveSelect( CORE_MODULES ).getModule( slug ); const storeName = select( CORE_MODULES ).getModuleStoreName( slug ); @@ -580,9 +578,7 @@ export const baseControls = { } if ( select( storeName )?.getAdminReauthURL ) { - return await __experimentalResolveSelect( - storeName - ).getAdminReauthURL(); + return await resolveSelect( storeName ).getAdminReauthURL(); } return select( CORE_SITE ).getAdminURL( 'googlesitekit-dashboard' @@ -665,11 +661,9 @@ const baseReducer = ( state, { type, payload } ) => { }; function* waitForModules() { - const { __experimentalResolveSelect } = yield commonActions.getRegistry(); + const { resolveSelect } = yield commonActions.getRegistry(); - yield commonActions.await( - __experimentalResolveSelect( CORE_MODULES ).getModules() - ); + yield commonActions.await( resolveSelect( CORE_MODULES ).getModules() ); } const baseResolvers = { @@ -685,9 +679,9 @@ const baseResolvers = { *canActivateModule( slug ) { const registry = yield commonActions.getRegistry(); - const { select, __experimentalResolveSelect } = registry; + const { select, resolveSelect } = registry; const module = yield commonActions.await( - __experimentalResolveSelect( CORE_MODULES ).getModule( slug ) + resolveSelect( CORE_MODULES ).getModule( slug ) ); // At this point, all modules are loaded so we can safely select getModule below. @@ -753,7 +747,7 @@ const baseResolvers = { *getRecoverableModules() { const registry = yield commonActions.getRegistry(); const modules = yield commonActions.await( - registry.__experimentalResolveSelect( CORE_MODULES ).getModules() + registry.resolveSelect( CORE_MODULES ).getModules() ); const recoverableModules = Object.entries( modules || {} ).reduce( diff --git a/assets/js/googlesitekit/modules/datastore/modules.test.js b/assets/js/googlesitekit/modules/datastore/modules.test.js index 3ced06e4be5..1889886fe52 100644 --- a/assets/js/googlesitekit/modules/datastore/modules.test.js +++ b/assets/js/googlesitekit/modules/datastore/modules.test.js @@ -2051,7 +2051,7 @@ describe( 'core/modules modules', () => { provideModules( registry, FIXTURES ); const sharedOwnershipModules = await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharedOwnershipModules(); expect( sharedOwnershipModules ).toMatchObject( {} ); @@ -2064,7 +2064,7 @@ describe( 'core/modules modules', () => { provideModules( registry, FIXTURES ); const sharedOwnershipModules = await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharedOwnershipModules(); expect( sharedOwnershipModules ).toMatchObject( diff --git a/assets/js/googlesitekit/modules/datastore/sharing-settings.test.js b/assets/js/googlesitekit/modules/datastore/sharing-settings.test.js index 43622662155..b097a631d8e 100644 --- a/assets/js/googlesitekit/modules/datastore/sharing-settings.test.js +++ b/assets/js/googlesitekit/modules/datastore/sharing-settings.test.js @@ -240,7 +240,7 @@ describe( 'core/modules sharing-settings', () => { ); await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); await registry @@ -368,7 +368,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = undefined; const sharingSettingsObj = await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); expect( console ).toHaveErrored(); @@ -381,7 +381,7 @@ describe( 'core/modules sharing-settings', () => { }; const sharingSettingsObj = await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); expect( sharingSettingsObj ).toMatchObject( {} ); @@ -391,7 +391,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; const sharingSettingsObj = await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); expect( sharingSettingsObj ).toMatchObject( sharingSettings ); @@ -403,7 +403,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = undefined; const shareableRolesObj = await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getShareableRoles(); expect( console ).toHaveErrored(); @@ -416,7 +416,7 @@ describe( 'core/modules sharing-settings', () => { }; const shareableRolesObj = await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getShareableRoles(); expect( shareableRolesObj ).toMatchObject( [] ); @@ -426,7 +426,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; const shareableRolesObj = await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getShareableRoles(); expect( shareableRolesObj ).toMatchObject( shareableRoles ); @@ -444,7 +444,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = undefined; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); const sharingManagement = registry @@ -459,7 +459,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); const sharingManagement = registry @@ -472,7 +472,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); const sharingManagement = registry @@ -494,7 +494,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = undefined; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); const sharedRoles = registry @@ -509,7 +509,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); const sharedRoles = registry @@ -522,7 +522,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); const sharedRoles = registry @@ -545,7 +545,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); // Still false after getting the sharing settings from the global variable. @@ -592,7 +592,7 @@ describe( 'core/modules sharing-settings', () => { it( 'compares all keys when keys argument is not supplied', async () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); // Update the sharing settings so they differ. All values are being checked here. @@ -607,7 +607,7 @@ describe( 'core/modules sharing-settings', () => { it( 'compares select keys when keys argument is supplied', async () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); // Update the sharing settings so they differ. Only `search-console` should trigger @@ -650,7 +650,7 @@ describe( 'core/modules sharing-settings', () => { it( 'informs whether client-side sharing-settings differ from server-side ones', async () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); // True after updating module's `sharedRoles` on the client. @@ -686,7 +686,7 @@ describe( 'core/modules sharing-settings', () => { it( 'should be set to TRUE after starting submiting sharing changes', async () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); freezeFetch( @@ -707,7 +707,7 @@ describe( 'core/modules sharing-settings', () => { it( 'should be set to FALSE after finishing submitting sharing changes', async () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); fetchMock.postOnce( @@ -763,7 +763,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); // Still false after getting the sharing settings from the global variable. @@ -816,7 +816,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); // Still false after getting the sharing settings from the global variable. @@ -862,7 +862,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); // Still false after getting the sharing settings from the global variable. @@ -898,7 +898,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); // Still false after getting the sharing settings from the global variable. @@ -955,7 +955,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); // Still false after getting the sharing settings from the global variable. @@ -1024,7 +1024,7 @@ describe( 'core/modules sharing-settings', () => { it( 'compares all keys when keys argument is not supplied', async () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); // Update the sharing settings so they differ. All values are being checked here. @@ -1041,7 +1041,7 @@ describe( 'core/modules sharing-settings', () => { it( 'compares selected keys when keys argument is supplied', async () => { global[ dashboardSharingDataBaseVar ] = dashboardSharingData; await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getSharingSettings(); // Update the sharing settings so they differ. Only `search-console` should trigger @@ -1099,7 +1099,7 @@ describe( 'core/modules sharing-settings', () => { global[ dashboardSharingDataBaseVar ] = undefined; const defaultSharedOwnershipModuleSettingsObj = await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getDefaultSharedOwnershipModuleSettings(); expect( console ).toHaveErrored(); @@ -1114,7 +1114,7 @@ describe( 'core/modules sharing-settings', () => { }; const defaultSharedOwnershipModuleSettingsObj = await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getDefaultSharedOwnershipModuleSettings(); expect( defaultSharedOwnershipModuleSettingsObj ).toMatchObject( @@ -1128,7 +1128,7 @@ describe( 'core/modules sharing-settings', () => { }; const defaultSharedOwnershipModuleSettingsObj = await registry - .__experimentalResolveSelect( CORE_MODULES ) + .resolveSelect( CORE_MODULES ) .getDefaultSharedOwnershipModuleSettings(); expect( defaultSharedOwnershipModuleSettingsObj ).toMatchObject( diff --git a/assets/js/modules/ads/components/setup/SetupMainPAX.js b/assets/js/modules/ads/components/setup/SetupMainPAX.js index a3548aa174c..e97a5f9fe10 100644 --- a/assets/js/modules/ads/components/setup/SetupMainPAX.js +++ b/assets/js/modules/ads/components/setup/SetupMainPAX.js @@ -131,8 +131,8 @@ export default function SetupMainPAX( { finishSetup } ) { const registry = useRegistry(); const onCompleteSetup = useCallbackOne( async () => { // Encapsulate dependencies to avoid function changing after launch. - const { select, __experimentalResolveSelect } = registry; - await __experimentalResolveSelect( CORE_SITE ).getSiteInfo(); + const { select, resolveSelect } = registry; + await resolveSelect( CORE_SITE ).getSiteInfo(); const redirectURL = select( CORE_SITE ).getAdminURL( 'googlesitekit-dashboard', { diff --git a/assets/js/modules/ads/index.js b/assets/js/modules/ads/index.js index b9f6255b612..9b88d735da7 100644 --- a/assets/js/modules/ads/index.js +++ b/assets/js/modules/ads/index.js @@ -57,7 +57,7 @@ export const registerModule = ( modules ) => { ], checkRequirements: async ( registry ) => { const adBlockerActive = await registry - .__experimentalResolveSelect( CORE_USER ) + .resolveSelect( CORE_USER ) .isAdBlockerActive(); if ( ! adBlockerActive ) { diff --git a/assets/js/modules/ads/pax/services.js b/assets/js/modules/ads/pax/services.js index d0e4fb61ce0..ed5f24dd70b 100644 --- a/assets/js/modules/ads/pax/services.js +++ b/assets/js/modules/ads/pax/services.js @@ -98,7 +98,7 @@ export function createPaxServices( registry, options = {} ) { const { onCampaignCreated = null, onFinishAndCloseSignUpFlow = null } = options; - const { select, __experimentalResolveSelect } = registry; + const { select, resolveSelect } = registry; const getToken = createMemoizedGetToken(); const services = { @@ -126,7 +126,7 @@ export function createPaxServices( registry, options = {} ) { }, businessService: { getBusinessInfo: async () => { - await __experimentalResolveSelect( CORE_SITE ).getSiteInfo(); + await resolveSelect( CORE_SITE ).getSiteInfo(); /* eslint-disable sitekit/acronym-case */ // Disabling rule because businessName and businessUrl are expected by PAX API. @@ -151,9 +151,7 @@ export function createPaxServices( registry, options = {} ) { }, conversionTrackingService: { getSupportedConversionLabels: async () => { - await __experimentalResolveSelect( - MODULES_ADS - ).getModuleData(); + await resolveSelect( MODULES_ADS ).getModuleData(); const conversionEvents = select( MODULES_ADS ).getSupportedConversionEvents() || []; diff --git a/assets/js/modules/adsense/index.js b/assets/js/modules/adsense/index.js index 29f38c0cb46..2f7b734145c 100644 --- a/assets/js/modules/adsense/index.js +++ b/assets/js/modules/adsense/index.js @@ -68,7 +68,7 @@ export const registerModule = ( modules ) => { ], checkRequirements: async ( registry ) => { const adBlockerActive = await registry - .__experimentalResolveSelect( CORE_USER ) + .resolveSelect( CORE_USER ) .isAdBlockerActive(); if ( ! adBlockerActive ) { diff --git a/assets/js/modules/analytics-4/datastore/audience-settings.js b/assets/js/modules/analytics-4/datastore/audience-settings.js index 200d96bf8b5..841f8361d1a 100644 --- a/assets/js/modules/analytics-4/datastore/audience-settings.js +++ b/assets/js/modules/analytics-4/datastore/audience-settings.js @@ -121,7 +121,7 @@ const baseActions = { const registry = yield commonActions.getRegistry(); const audienceSettings = yield commonActions.await( registry - .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) + .resolveSelect( MODULES_ANALYTICS_4 ) .getAudienceSettings() ); const finalSettings = { @@ -131,7 +131,7 @@ const baseActions = { const availableAudiences = yield commonActions.await( registry - .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) + .resolveSelect( MODULES_ANALYTICS_4 ) .getAvailableAudiences() ); diff --git a/assets/js/modules/analytics-4/datastore/audiences.js b/assets/js/modules/analytics-4/datastore/audiences.js index f9548aa9b58..e413b84d032 100644 --- a/assets/js/modules/analytics-4/datastore/audiences.js +++ b/assets/js/modules/analytics-4/datastore/audiences.js @@ -57,15 +57,15 @@ async function getNonZeroDataAudiencesSortedByTotalUsers( startDate, endDate ) { - const { select, __experimentalResolveSelect } = registry; + const { select, resolveSelect } = registry; const reportOptions = select( MODULES_ANALYTICS_4 ).getAudiencesUserCountReportOptions( audiences, { startDate, endDate } ); - const report = await __experimentalResolveSelect( - MODULES_ANALYTICS_4 - ).getReport( reportOptions ); + const report = await resolveSelect( MODULES_ANALYTICS_4 ).getReport( + reportOptions + ); const error = select( MODULES_ANALYTICS_4 ).getErrorForSelector( 'getReport', @@ -185,7 +185,7 @@ const baseActions = { *enableAudienceGroup() { const registry = yield commonActions.getRegistry(); - const { dispatch, select, __experimentalResolveSelect } = registry; + const { dispatch, select, resolveSelect } = registry; const { response: availableAudiences, error: syncError } = yield commonActions.await( @@ -298,9 +298,7 @@ const baseActions = { // Create custom dimension if it doesn't exist. yield commonActions.await( - __experimentalResolveSelect( - MODULES_ANALYTICS_4 - ).getAvailableCustomDimensions() + resolveSelect( MODULES_ANALYTICS_4 ).getAvailableCustomDimensions() ); if ( diff --git a/assets/js/modules/analytics-4/datastore/conversion-events.test.js b/assets/js/modules/analytics-4/datastore/conversion-events.test.js index 5e90c7df6bb..80ff5d35fdc 100644 --- a/assets/js/modules/analytics-4/datastore/conversion-events.test.js +++ b/assets/js/modules/analytics-4/datastore/conversion-events.test.js @@ -64,7 +64,7 @@ describe( 'modules/analytics-4 conversion-events', () => { expect( initialConversionEvents ).toBeUndefined(); const conversionEvents = await registry - .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) + .resolveSelect( MODULES_ANALYTICS_4 ) .getConversionEvents(); expect( fetchMock ).toHaveFetchedTimes( 1 ); @@ -110,7 +110,7 @@ describe( 'modules/analytics-4 conversion-events', () => { ); await registry - .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) + .resolveSelect( MODULES_ANALYTICS_4 ) .getConversionEvents(); expect( fetchMock ).toHaveFetchedTimes( 1 ); diff --git a/assets/js/modules/analytics-4/datastore/custom-dimensions-gathering-data.js b/assets/js/modules/analytics-4/datastore/custom-dimensions-gathering-data.js index debffe968cf..7dd535bf7cf 100644 --- a/assets/js/modules/analytics-4/datastore/custom-dimensions-gathering-data.js +++ b/assets/js/modules/analytics-4/datastore/custom-dimensions-gathering-data.js @@ -117,11 +117,10 @@ const baseActions = { * @param {string} customDimension Custom dimension slug. */ *checkCustomDimensionDataAvailability( customDimension ) { - const { select, __experimentalResolveSelect } = - yield commonActions.getRegistry(); + const { select, resolveSelect } = yield commonActions.getRegistry(); yield commonActions.await( - __experimentalResolveSelect( MODULES_ANALYTICS_4 ).getSettings() + resolveSelect( MODULES_ANALYTICS_4 ).getSettings() ); if ( @@ -137,7 +136,7 @@ const baseActions = { } yield commonActions.await( - __experimentalResolveSelect( CORE_USER ).getAuthentication() + resolveSelect( CORE_USER ).getAuthentication() ); if ( ! select( CORE_USER ).isAuthenticated() ) { @@ -149,7 +148,7 @@ const baseActions = { } const reportArgs = yield commonActions.await( - __experimentalResolveSelect( + resolveSelect( MODULES_ANALYTICS_4 ).getDataAvailabilityReportOptions( customDimension ) ); @@ -163,9 +162,7 @@ const baseActions = { } const report = yield commonActions.await( - __experimentalResolveSelect( MODULES_ANALYTICS_4 ).getReport( - reportArgs - ) + resolveSelect( MODULES_ANALYTICS_4 ).getReport( reportArgs ) ); const hasReportError = !! select( @@ -244,13 +241,10 @@ const baseResolvers = { }, *getDataAvailabilityReportOptions() { - const { __experimentalResolveSelect } = - yield commonActions.getRegistry(); + const { resolveSelect } = yield commonActions.getRegistry(); yield commonActions.await( - __experimentalResolveSelect( - MODULES_ANALYTICS_4 - ).getPropertyCreateTime() + resolveSelect( MODULES_ANALYTICS_4 ).getPropertyCreateTime() ); }, }; diff --git a/assets/js/modules/analytics-4/datastore/custom-dimensions.js b/assets/js/modules/analytics-4/datastore/custom-dimensions.js index 367ba2ae91e..b587eff1254 100644 --- a/assets/js/modules/analytics-4/datastore/custom-dimensions.js +++ b/assets/js/modules/analytics-4/datastore/custom-dimensions.js @@ -118,15 +118,9 @@ const baseActions = { // Wait for the necessary settings to be loaded before checking. yield commonActions.await( Promise.all( [ - registry - .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) - .getSettings(), - registry - .__experimentalResolveSelect( CORE_USER ) - .getKeyMetricsSettings(), - registry - .__experimentalResolveSelect( CORE_USER ) - .getUserInputSettings(), + registry.resolveSelect( MODULES_ANALYTICS_4 ).getSettings(), + registry.resolveSelect( CORE_USER ).getKeyMetricsSettings(), + registry.resolveSelect( CORE_USER ).getUserInputSettings(), ] ) ); @@ -290,14 +284,11 @@ export const baseReducer = ( state, { type, payload } ) => { const baseResolvers = { *getAvailableCustomDimensions() { - const { select, __experimentalResolveSelect } = - yield commonActions.getRegistry(); + const { select, resolveSelect } = yield commonActions.getRegistry(); const { isAuthenticated, hasCapability } = select( CORE_USER ); const isGA4Connected = yield commonActions.await( - __experimentalResolveSelect( CORE_MODULES ).isModuleConnected( - 'analytics-4' - ) + resolveSelect( CORE_MODULES ).isModuleConnected( 'analytics-4' ) ); if ( ! isGA4Connected ) { @@ -306,7 +297,7 @@ const baseResolvers = { // Wait for settings to be loaded before proceeding. yield commonActions.await( - __experimentalResolveSelect( MODULES_ANALYTICS_4 ).getSettings() + resolveSelect( MODULES_ANALYTICS_4 ).getSettings() ); const availableCustomDimensions = @@ -318,7 +309,7 @@ const baseResolvers = { // Wait for permissions to be loaded before checking if the user can manage options. yield commonActions.await( - __experimentalResolveSelect( CORE_USER ).getCapabilities() + resolveSelect( CORE_USER ).getCapabilities() ); if ( ! hasCapability( PERMISSION_MANAGE_OPTIONS ) ) { diff --git a/assets/js/modules/analytics-4/datastore/enhanced-measurement.js b/assets/js/modules/analytics-4/datastore/enhanced-measurement.js index 32fbccd097e..b165a97d7be 100644 --- a/assets/js/modules/analytics-4/datastore/enhanced-measurement.js +++ b/assets/js/modules/analytics-4/datastore/enhanced-measurement.js @@ -224,7 +224,7 @@ const baseActions = { const currentSettings = yield commonActions.await( registry - .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) + .resolveSelect( MODULES_ANALYTICS_4 ) .getEnhancedMeasurementSettings( propertyID, webDataStreamID @@ -298,7 +298,7 @@ const baseActions = { const currentSettings = yield commonActions.await( registry - .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) + .resolveSelect( MODULES_ANALYTICS_4 ) .getEnhancedMeasurementSettings( propertyID, webDataStreamID diff --git a/assets/js/modules/analytics-4/datastore/enhanced-measurement.test.js b/assets/js/modules/analytics-4/datastore/enhanced-measurement.test.js index e1bc176ba60..20d7efd783e 100644 --- a/assets/js/modules/analytics-4/datastore/enhanced-measurement.test.js +++ b/assets/js/modules/analytics-4/datastore/enhanced-measurement.test.js @@ -356,7 +356,7 @@ describe( 'modules/analytics-4 enhanced-measurement', () => { expect( initialSettings ).toBeUndefined(); const finalSettings = await registry - .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) + .resolveSelect( MODULES_ANALYTICS_4 ) .getEnhancedMeasurementSettings( propertyID, webDataStreamID @@ -488,7 +488,7 @@ describe( 'modules/analytics-4 enhanced-measurement', () => { expect( initialStreamEnabled ).toBeUndefined(); const finalStreamEnabled = await registry - .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) + .resolveSelect( MODULES_ANALYTICS_4 ) .isEnhancedMeasurementStreamAlreadyEnabled( propertyID, webDataStreamID diff --git a/assets/js/modules/analytics-4/datastore/partial-data.js b/assets/js/modules/analytics-4/datastore/partial-data.js index 4f5aec4e565..f72853a9c0a 100644 --- a/assets/js/modules/analytics-4/datastore/partial-data.js +++ b/assets/js/modules/analytics-4/datastore/partial-data.js @@ -205,8 +205,7 @@ const baseResolvers = { }, *getResourceDataAvailabilityDate( resourceSlug, resourceType ) { - const { select, __experimentalResolveSelect } = - yield commonActions.getRegistry(); + const { select, resolveSelect } = yield commonActions.getRegistry(); if ( select( MODULES_ANALYTICS_4 ).getResourceDataAvailabilityDate( @@ -218,7 +217,7 @@ const baseResolvers = { } const resourceAvailabilityDates = yield commonActions.await( - __experimentalResolveSelect( + resolveSelect( MODULES_ANALYTICS_4 ).getResourceDataAvailabilityDates() ); @@ -229,14 +228,14 @@ const baseResolvers = { ) { // Ensure the settings are loaded. yield commonActions.await( - __experimentalResolveSelect( MODULES_ANALYTICS_4 ).getSettings() + resolveSelect( MODULES_ANALYTICS_4 ).getSettings() ); // Validate if the resourceSlug is a valid resource. switch ( resourceType ) { case RESOURCE_TYPE_AUDIENCE: yield commonActions.await( - __experimentalResolveSelect( + resolveSelect( MODULES_ANALYTICS_4 ).getAvailableAudiences() ); @@ -274,7 +273,7 @@ const baseResolvers = { } yield commonActions.await( - __experimentalResolveSelect( CORE_USER ).getAuthentication() + resolveSelect( CORE_USER ).getAuthentication() ); // Return early if user is not authenticated. @@ -288,7 +287,7 @@ const baseResolvers = { } const reportArgs = yield commonActions.await( - __experimentalResolveSelect( + resolveSelect( MODULES_ANALYTICS_4 ).getPartialDataReportOptions( resourceSlug, resourceType ) ); @@ -299,9 +298,7 @@ const baseResolvers = { } const report = yield commonActions.await( - __experimentalResolveSelect( MODULES_ANALYTICS_4 ).getReport( - reportArgs - ) + resolveSelect( MODULES_ANALYTICS_4 ).getReport( reportArgs ) ); const hasReportError = !! select( @@ -339,13 +336,10 @@ const baseResolvers = { }, *getPartialDataReportOptions() { - const { __experimentalResolveSelect } = - yield commonActions.getRegistry(); + const { resolveSelect } = yield commonActions.getRegistry(); yield commonActions.await( - __experimentalResolveSelect( - MODULES_ANALYTICS_4 - ).getPropertyCreateTime() + resolveSelect( MODULES_ANALYTICS_4 ).getPropertyCreateTime() ); }, }; diff --git a/assets/js/modules/analytics-4/datastore/properties.js b/assets/js/modules/analytics-4/datastore/properties.js index b81893e694e..b6d01cda786 100644 --- a/assets/js/modules/analytics-4/datastore/properties.js +++ b/assets/js/modules/analytics-4/datastore/properties.js @@ -287,7 +287,7 @@ const baseActions = { if ( propertyID ) { const property = yield commonActions.await( registry - .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) + .resolveSelect( MODULES_ANALYTICS_4 ) .getProperty( propertyID ) ); @@ -335,9 +335,7 @@ const baseActions = { *findMatchedProperty() { const registry = yield commonActions.getRegistry(); const accounts = yield commonActions.await( - registry - .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) - .getAccountSummaries() + registry.resolveSelect( MODULES_ANALYTICS_4 ).getAccountSummaries() ); if ( ! Array.isArray( accounts ) || accounts.length === 0 ) { @@ -441,7 +439,7 @@ const baseActions = { ); const webdatastreams = yield commonActions.await( registry - .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) + .resolveSelect( MODULES_ANALYTICS_4 ) .getWebDataStreamsBatch( chunk ) ); @@ -457,9 +455,7 @@ const baseActions = { ) { return yield commonActions.await( registry - .__experimentalResolveSelect( - MODULES_ANALYTICS_4 - ) + .resolveSelect( MODULES_ANALYTICS_4 ) .getProperty( propertyID ) ); } @@ -497,7 +493,7 @@ const baseActions = { ); const webdatastreams = yield commonActions.await( registry - .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) + .resolveSelect( MODULES_ANALYTICS_4 ) .getWebDataStreamsBatch( chunk ) ); @@ -510,9 +506,7 @@ const baseActions = { ) { return yield commonActions.await( registry - .__experimentalResolveSelect( - MODULES_ANALYTICS_4 - ) + .resolveSelect( MODULES_ANALYTICS_4 ) .getProperty( propertyID ) ); } @@ -544,7 +538,7 @@ const baseActions = { * @param {string} measurementID Measurement ID. */ *updateSettingsForMeasurementID( measurementID ) { - const { select, dispatch, __experimentalResolveSelect } = + const { select, dispatch, resolveSelect } = yield commonActions.getRegistry(); if ( ! measurementID ) { @@ -561,7 +555,7 @@ const baseActions = { // Wait for authentication to be resolved to check scopes. yield commonActions.await( - __experimentalResolveSelect( CORE_USER ).getAuthentication() + resolveSelect( CORE_USER ).getAuthentication() ); if ( ! select( CORE_USER ).hasScope( TAGMANAGER_READ_SCOPE ) ) { return; @@ -642,7 +636,7 @@ const baseActions = { * @since 1.95.0 */ *syncGoogleTagSettings() { - const { select, dispatch, __experimentalResolveSelect } = + const { select, dispatch, resolveSelect } = yield commonActions.getRegistry(); const hasTagManagerReadScope = select( CORE_USER ).hasScope( @@ -654,9 +648,7 @@ const baseActions = { } // Wait for modules to be available before selecting. - yield commonActions.await( - __experimentalResolveSelect( CORE_MODULES ).getModules() - ); + yield commonActions.await( resolveSelect( CORE_MODULES ).getModules() ); const { isModuleConnected } = select( CORE_MODULES ); @@ -666,7 +658,7 @@ const baseActions = { // Wait for module settings to be available before selecting. yield commonActions.await( - __experimentalResolveSelect( MODULES_ANALYTICS_4 ).getSettings() + resolveSelect( MODULES_ANALYTICS_4 ).getSettings() ); const { @@ -698,9 +690,9 @@ const baseActions = { if ( !! googleTagID ) { const googleTagContainer = yield commonActions.await( - __experimentalResolveSelect( - MODULES_ANALYTICS_4 - ).getGoogleTagContainer( measurementID ) + resolveSelect( MODULES_ANALYTICS_4 ).getGoogleTagContainer( + measurementID + ) ); if ( ! googleTagContainer ) { @@ -716,7 +708,7 @@ const baseActions = { const googleTagContainerID = getGoogleTagContainerID(); const googleTagContainerDestinations = yield commonActions.await( - __experimentalResolveSelect( + resolveSelect( MODULES_ANALYTICS_4 ).getGoogleTagContainerDestinations( googleTagAccountID, @@ -743,9 +735,9 @@ const baseActions = { const baseControls = { [ WAIT_FOR_PROPERTY_SUMMARIES ]: createRegistryControl( - ( { __experimentalResolveSelect } ) => { + ( { resolveSelect } ) => { return async () => { - await __experimentalResolveSelect( + await resolveSelect( MODULES_ANALYTICS_4 ).getAccountSummaries(); }; @@ -802,9 +794,7 @@ const baseResolvers = { const registry = yield commonActions.getRegistry(); // Ensure settings are available to select. yield commonActions.await( - registry - .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) - .getSettings() + registry.resolveSelect( MODULES_ANALYTICS_4 ).getSettings() ); const propertyID = registry @@ -835,7 +825,7 @@ const baseResolvers = { const property = yield commonActions.await( registry - .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) + .resolveSelect( MODULES_ANALYTICS_4 ) .getProperty( propertyID ) ); diff --git a/assets/js/modules/analytics-4/datastore/tags.js b/assets/js/modules/analytics-4/datastore/tags.js index 14358b086d6..6f8bcfec594 100644 --- a/assets/js/modules/analytics-4/datastore/tags.js +++ b/assets/js/modules/analytics-4/datastore/tags.js @@ -47,7 +47,7 @@ existingTagStore.resolvers.getExistingTag = function* () { if ( existingTag !== null ) { const container = yield commonActions.await( registry - .__experimentalResolveSelect( MODULES_ANALYTICS_4 ) + .resolveSelect( MODULES_ANALYTICS_4 ) .getGoogleTagContainer( existingTag ) ); diff --git a/assets/js/modules/analytics-4/datastore/webdatastreams.js b/assets/js/modules/analytics-4/datastore/webdatastreams.js index 9481f50bc10..a43b139aadc 100644 --- a/assets/js/modules/analytics-4/datastore/webdatastreams.js +++ b/assets/js/modules/analytics-4/datastore/webdatastreams.js @@ -217,12 +217,12 @@ const baseActions = { const baseControls = { [ WAIT_FOR_WEBDATASTREAMS ]: createRegistryControl( - ( { __experimentalResolveSelect } ) => { + ( { resolveSelect } ) => { return async ( { payload } ) => { const { propertyID } = payload; - await __experimentalResolveSelect( - MODULES_ANALYTICS_4 - ).getWebDataStreams( propertyID ); + await resolveSelect( MODULES_ANALYTICS_4 ).getWebDataStreams( + propertyID + ); }; } ), From b511ddd90909fb88cd66313e7dbf17056a7fb03c Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Thu, 27 Jun 2024 18:18:40 +0530 Subject: [PATCH 025/105] Add enableAudienceGroup tests coverage for error handling. --- .../analytics-4/datastore/audiences.test.js | 282 ++++++++++++++++++ 1 file changed, 282 insertions(+) diff --git a/assets/js/modules/analytics-4/datastore/audiences.test.js b/assets/js/modules/analytics-4/datastore/audiences.test.js index 0d53eedb37f..9d55ecbcbb1 100644 --- a/assets/js/modules/analytics-4/datastore/audiences.test.js +++ b/assets/js/modules/analytics-4/datastore/audiences.test.js @@ -989,6 +989,288 @@ describe( 'modules/analytics-4 audiences', () => { ).toBe( true ); } ); } ); + + describe( 'error handling and retrying failed audience creations', () => { + const errorResponse = { + code: 'internal_server_error', + message: 'Internal server error', + data: { status: 500 }, + }; + + it( 'should return and dispatch an error if syncing available audiences request fails', async () => { + fetchMock.post( syncAvailableAudiencesEndpoint, { + body: errorResponse, + status: 500, + } ); + + const { response, error } = await registry + .dispatch( MODULES_ANALYTICS_4 ) + .enableAudienceGroup(); + + expect( response ).toBeUndefined(); + expect( error ).toEqual( errorResponse ); + + expect( + registry + .select( MODULES_ANALYTICS_4 ) + .getErrorForAction( 'syncAvailableAudiences' ) + ).toEqual( errorResponse ); + + expect( console ).toHaveErrored(); + } ); + + it( 'should return failed audience names when creating new visitors and returning visitors audiences fails', async () => { + const expectedFailedAudiences = [ + 'new-visitors', + 'returning-visitors', + ]; + + fetchMock.postOnce( syncAvailableAudiencesEndpoint, { + body: [], + status: 200, + } ); + + // Mocking createAudience API call with failure response. + fetchMock.post( + { url: createAudienceEndpoint, repeat: 2 }, + { + body: errorResponse, + status: 500, + } + ); + + const result = await registry + .dispatch( MODULES_ANALYTICS_4 ) + .enableAudienceGroup(); + + // Verifying the result contains the expected failed audiences. + expect( result ).toEqual( { + failedSiteKitAudienceResourceNames: + expectedFailedAudiences, + } ); + + // Ensuring no configured audiences are set when all creation attempts fail. + expect( + registry + .select( MODULES_ANALYTICS_4 ) + .getConfiguredAudiences() + ).toBeNull(); + + expect( console ).toHaveErrored(); + expect( console ).toHaveErrored(); + } ); + + it( 'should create one audience and return failed audience names for the other', async () => { + const failedAudiencesToRetry = [ + 'new-visitors', + 'returning-visitors', + ]; + + const createdNewVisitorsAudienceName = + 'properties/12345/audiences/888'; + const expectedFailedAudiences = [ 'returning-visitors' ]; + + // Mocking createAudience API call with mixed responses. + fetchMock.post( + { url: createAudienceEndpoint, repeat: 2 }, + ( url, opts ) => { + if ( opts.body.includes( 'new_visitors' ) ) { + return { + body: { + ...SITE_KIT_AUDIENCE_DEFINITIONS[ + 'new-visitors' + ], + name: createdNewVisitorsAudienceName, + }, + status: 200, + }; + } + return { + body: errorResponse, + status: 500, + }; + } + ); + + fetchMock.postOnce( syncAvailableAudiencesEndpoint, { + body: [], + status: 200, + } ); + + const result = await registry + .dispatch( MODULES_ANALYTICS_4 ) + .enableAudienceGroup( failedAudiencesToRetry ); + + // Verifying the result contains the expected failed audiences. + expect( result ).toEqual( { + failedSiteKitAudienceResourceNames: + expectedFailedAudiences, + } ); + + // Ensure no configured audiences are set when one creation attempt fails. + expect( + registry + .select( MODULES_ANALYTICS_4 ) + .getConfiguredAudiences() + ).toBeNull(); + + // Ensuring the API calls were made as expected + expect( fetchMock ).toHaveFetchedTimes( + 2, + createAudienceEndpoint + ); + + expect( fetchMock ).toHaveFetchedTimes( + 1, + syncAvailableAudiencesEndpoint + ); + + // Ensure conse error is logged only once. + expect( console ).toHaveErrored(); + } ); + + it( 'should create provided "failedSiteKitAudienceResourceNames" correctly', async () => { + const failedAudiencesToRetry = [ + 'new-visitors', + 'returning-visitors', + ]; + + const createdNewVisitorsAudienceName = + 'properties/12345/audiences/888'; + const createdReturningVisitorsAudienceName = + 'properties/12345/audiences/999'; + + const expectedConfiguredAudiences = [ + createdNewVisitorsAudienceName, + createdReturningVisitorsAudienceName, + ]; + + const finalAvailableAudiences = [ + [ + ...availableUserAudiences, + { + ...availableNewVisitorsAudienceFixture, + name: createdNewVisitorsAudienceName, + }, + { + ...availableReturningVisitorsAudienceFixture, + name: createdReturningVisitorsAudienceName, + }, + ], + ]; + + fetchMock.post( + { + url: syncAvailableAudiencesEndpoint, + repeat: 2, + }, + () => { + const callCount = fetchMock.calls( + syncAvailableAudiencesEndpoint + ).length; + + return { + body: + callCount === 1 + ? availableUserAudiences + : finalAvailableAudiences, + status: 200, + }; + } + ); + + fetchMock.postOnce( audienceSettingsEndpoint, { + body: { + configuredAudiences: expectedConfiguredAudiences, + isAudienceSegmentationWidgetHidden, + }, + status: 200, + } ); + + fetchMock.post( + { url: createAudienceEndpoint, repeat: 2 }, + ( url, opts ) => { + return { + body: opts.body.includes( 'new_visitors' ) + ? { + ...SITE_KIT_AUDIENCE_DEFINITIONS[ + 'new-visitors' + ], + name: createdNewVisitorsAudienceName, + } + : { + ...SITE_KIT_AUDIENCE_DEFINITIONS[ + 'returning-visitors' + ], + name: createdReturningVisitorsAudienceName, + }, + status: 200, + }; + } + ); + + await registry + .dispatch( MODULES_ANALYTICS_4 ) + .enableAudienceGroup( failedAudiencesToRetry ); + + expect( fetchMock ).toHaveFetchedTimes( + 2, + syncAvailableAudiencesEndpoint + ); + + expect( fetchMock ).toHaveFetchedTimes( + 1, + createAudienceEndpoint, + { + body: { + data: { + audience: + SITE_KIT_AUDIENCE_DEFINITIONS[ + 'new-visitors' + ], + }, + }, + } + ); + + expect( fetchMock ).toHaveFetchedTimes( + 1, + createAudienceEndpoint, + { + body: { + data: { + audience: + SITE_KIT_AUDIENCE_DEFINITIONS[ + 'returning-visitors' + ], + }, + }, + } + ); + + expect( fetchMock ).toHaveFetchedTimes( + 1, + audienceSettingsEndpoint, + { + body: { + data: { + settings: { + configuredAudiences: + expectedConfiguredAudiences, + isAudienceSegmentationWidgetHidden, + }, + }, + }, + } + ); + + expect( + registry + .select( MODULES_ANALYTICS_4 ) + .getConfiguredAudiences() + ).toEqual( expectedConfiguredAudiences ); + } ); + } ); } ); } ); From 5a5e82f4243ed0182bd46fe8e651abe97017398c Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Thu, 27 Jun 2024 18:19:32 +0530 Subject: [PATCH 026/105] Pass failedSiteKitAudienceResourceNames as a non object param. --- assets/js/modules/analytics-4/datastore/audiences.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/assets/js/modules/analytics-4/datastore/audiences.js b/assets/js/modules/analytics-4/datastore/audiences.js index b01c7295e0f..0c8988aa75d 100644 --- a/assets/js/modules/analytics-4/datastore/audiences.js +++ b/assets/js/modules/analytics-4/datastore/audiences.js @@ -188,11 +188,10 @@ const baseActions = { * @since 1.128.0 * @since n.e.x.t Added `failedSiteKitAudienceResourceNames` parameter to retry failed Site Kit audience creation. * - * @param {Object} args Arguments for enabling audience group. - * @param {Array} args.failedSiteKitAudienceResourceNames List of failed Site Kit audience resource names to retry. + * @param {Array} failedSiteKitAudienceResourceNames List of failed Site Kit audience resource names to retry. * @return {Object} Object with `failedSiteKitAudienceResourceNames` and `error`. */ - *enableAudienceGroup( { failedSiteKitAudienceResourceNames } ) { + *enableAudienceGroup( failedSiteKitAudienceResourceNames ) { const registry = yield commonActions.getRegistry(); const { dispatch, select, __experimentalResolveSelect } = registry; @@ -279,10 +278,9 @@ const baseActions = { } if ( configuredAudiences.length === 0 ) { - const audiencesToCreate = failedSiteKitAudienceResourceNames || [ - 'new-visitors', - 'returning-visitors', - ]; + const audiencesToCreate = failedSiteKitAudienceResourceNames?.length + ? failedSiteKitAudienceResourceNames + : [ 'new-visitors', 'returning-visitors' ]; // If there are no configured audiences by this point, create the "new-visitors" and "returning-visitors" audiences, // and add them to the configured audiences. From 0ea5b01b48814c9d0167d8bdefe92189672beaff Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Thu, 27 Jun 2024 18:26:01 +0530 Subject: [PATCH 027/105] Do not navigate to OAuth flow if skipDefaultErrorNotifications is available. --- .../PermissionsModal/AuthenticatedPermissionsModal.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/assets/js/components/PermissionsModal/AuthenticatedPermissionsModal.js b/assets/js/components/PermissionsModal/AuthenticatedPermissionsModal.js index 0f0132f1063..123a1f74556 100644 --- a/assets/js/components/PermissionsModal/AuthenticatedPermissionsModal.js +++ b/assets/js/components/PermissionsModal/AuthenticatedPermissionsModal.js @@ -69,7 +69,11 @@ function AuthenticatedPermissionsModal() { // If we have a datastores to snapshot before navigating away to the // authorization page, do that first. await snapshotAllStores( registry ); - navigateTo( connectURL ); + + // Navigate to the authorization page. + if ( ! permissionsError?.data?.skipDefaultErrorNotifications ) { + navigateTo( connectURL ); + } }, [ registry, connectURL, navigateTo, permissionsError, setValues ] ); useEffect( () => { @@ -101,6 +105,10 @@ function AuthenticatedPermissionsModal() { return null; } + if ( permissionsError?.data?.skipDefaultErrorNotifications ) { + return null; + } + if ( permissionsError?.data?.skipModal ) { return null; } From aa113560f037a188e745a991d8a4ddfd481d9b30 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Thu, 27 Jun 2024 18:28:03 +0530 Subject: [PATCH 028/105] Add onCancel prop. Rearrage imports and props. --- .../dashboard/AudienceErrorModal.js | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal.js b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal.js index 19ed151b945..c6379177845 100644 --- a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal.js +++ b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal.js @@ -31,17 +31,19 @@ import { __ } from '@wordpress/i18n'; * Internal dependencies */ import { useSelect } from 'googlesitekit-data'; +import Link from '../../../../../components/Link'; import ModalDialog from '../../../../../components/ModalDialog'; -import { isInsufficientPermissionsError } from '../../../../../util/errors'; +import Portal from '../../../../../components/Portal'; import { CORE_SITE } from '../../../../../googlesitekit/datastore/site/constants'; -import Link from '../../../../../components/Link'; import { MODULES_ANALYTICS_4 } from '../../../datastore/constants'; +import { isInsufficientPermissionsError } from '../../../../../util/errors'; export default function AudienceErrorModal( { - hasOAuthError, apiErrors, - onRetry = () => {}, + hasOAuthError, inProgress, + onCancel = () => {}, + onRetry = () => {}, } ) { const errors = Array.isArray( apiErrors ) ? apiErrors : [ apiErrors ]; const hasInsufficientPermissionsError = errors.some( ( error ) => @@ -103,25 +105,29 @@ export default function AudienceErrorModal( { } return ( - + + + ); } AudienceErrorModal.propTypes = { - hasOAuthError: PropTypes.bool, apiErrors: PropTypes.oneOfType( [ PropTypes.arrayOf( PropTypes.object ), PropTypes.object, ] ), - onRetry: PropTypes.func, + hasOAuthError: PropTypes.bool, inProgress: PropTypes.bool, + onCancel: PropTypes.func, + onRetry: PropTypes.func, }; From 5cdc31b17e09fdcd61cfeccb5a0977d8c9d697b7 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Thu, 27 Jun 2024 18:31:59 +0530 Subject: [PATCH 029/105] Render AudienceErrorModal. Retry failed audience creation. --- .../AudienceSegmentationSetupCTAWidget.js | 80 ++++++++++++------- 1 file changed, 50 insertions(+), 30 deletions(-) diff --git a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js index 399c0fa13f2..be10c986233 100644 --- a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js +++ b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js @@ -38,7 +38,9 @@ import BannerGraphicsSVGTablet from '../../../../../../svg/graphics/audience-seg import BannerGraphicsSVGMobile from '../../../../../../svg/graphics/audience-segmentation-setup-mobile.svg'; import whenActive from '../../../../../util/when-active'; import { CORE_FORMS } from '../../../../../googlesitekit/datastore/forms/constants'; +import { CORE_LOCATION } from '../../../../../googlesitekit/datastore/location/constants'; import { CORE_USER } from '../../../../../googlesitekit/datastore/user/constants'; +import { CORE_SITE } from '../../../../../googlesitekit/datastore/site/constants'; import { MODULES_ANALYTICS_4, EDIT_SCOPE, @@ -60,7 +62,6 @@ import { import { withWidgetComponentProps } from '../../../../../googlesitekit/widgets/util'; import { WEEK_IN_SECONDS } from '../../../../../util'; import AudienceErrorModal from './AudienceErrorModal'; -import { CORE_SITE } from '../../../../../googlesitekit/datastore/site/constants'; export const AUDIENCE_SEGMENTATION_SETUP_CTA_NOTIFICATION = 'audience_segmentation_setup_cta-notification'; @@ -114,8 +115,9 @@ function AudienceSegmentationSetupCTAWidget( { Widget, WidgetNull } ) { const [ apiErrors, setApiErrors ] = useState( [] ); const [ failedAudiences, setFailedAudiences ] = useState( [] ); + const [ showErrorModal, setShowErrorModal ] = useState( false ); const onEnableGroups = useCallback( - async ( retryFailedAudiences ) => { + async ( retryFailedAudiences = [] ) => { setIsSaving( true ); // If scope not granted, trigger scope error right away. These are @@ -141,6 +143,7 @@ function AudienceSegmentationSetupCTAWidget( { Widget, WidgetNull } ) { }, } ); + setShowErrorModal( true ); setIsSaving( false ); return; } @@ -150,18 +153,22 @@ function AudienceSegmentationSetupCTAWidget( { Widget, WidgetNull } ) { } ); const { error, failedSiteKitAudienceResourceNames } = - await enableAudienceGroup( { - failedSiteKitAudienceResourceNames: retryFailedAudiences, - } ); + ( await enableAudienceGroup( retryFailedAudiences ) ) || {}; if ( error ) { setApiErrors( [ error ] ); - } - - if ( failedSiteKitAudienceResourceNames ) { + setFailedAudiences( [] ); + } else if ( Array.isArray( failedSiteKitAudienceResourceNames ) ) { setFailedAudiences( failedSiteKitAudienceResourceNames ); + setApiErrors( [] ); + } else { + setApiErrors( [] ); + setFailedAudiences( [] ); } + setShowErrorModal( + !! error || !! failedSiteKitAudienceResourceNames + ); setIsSaving( false ); }, [ @@ -207,6 +214,31 @@ function AudienceSegmentationSetupCTAWidget( { Widget, WidgetNull } ) { } }; + const permissionsError = useSelect( ( select ) => + select( CORE_USER ).getPermissionScopeError() + ); + + const connectURL = useSelect( ( select ) => + select( CORE_USER ).getConnectURL( { + additionalScopes: permissionsError?.data?.scopes, + redirectURL: + permissionsError?.data?.redirectURL || global.location.href, + } ) + ); + + const { navigateTo } = useDispatch( CORE_LOCATION ); + const { clearPermissionScopeError } = useDispatch( CORE_USER ); + + const handleOAuthError = useCallback( () => { + setIsSaving( true ); + navigateTo( connectURL ); + }, [ connectURL, navigateTo ] ); + + const onCancel = useCallback( () => { + clearPermissionScopeError(); + setShowErrorModal( false ); + }, [ clearPermissionScopeError ] ); + const setupErrorCode = useSelect( ( select ) => select( CORE_SITE ).getSetupErrorCode() ); @@ -244,32 +276,20 @@ function AudienceSegmentationSetupCTAWidget( { Widget, WidgetNull } ) { return null; } - if ( hasOAuthError ) { - return ( - - ); - } - - if ( apiErrors.length ) { - return ( - - ); - } - - if ( failedAudiences.length ) { + if ( showErrorModal ) { return ( onEnableGroups( failedAudiences ) } + onRetry={ + hasOAuthError + ? handleOAuthError + : () => onEnableGroups( failedAudiences ) + } inProgress={ isSaving } + onCancel={ + hasOAuthError ? onCancel : () => setShowErrorModal( false ) + } /> ); } From 9772b4d2fbf442bd0f7bd664b208fa10a76f7b81 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Thu, 27 Jun 2024 20:46:24 +0530 Subject: [PATCH 030/105] Add AudienceErrorModal tests. --- .../dashboard/AudienceErrorModal.test.js | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal.test.js diff --git a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal.test.js b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal.test.js new file mode 100644 index 00000000000..6609d7c73fe --- /dev/null +++ b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal.test.js @@ -0,0 +1,146 @@ +/** + * AudienceErrorModal component tests. + * + * Site Kit by Google, Copyright 2024 Google LLC + * + * Licensed 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 + * + * https://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. + */ + +/** + * Internal dependencies + */ +import { CORE_SITE } from '../../../../../googlesitekit/datastore/site/constants'; +import { ERROR_REASON_INSUFFICIENT_PERMISSIONS } from '../../../../../util/errors'; +import { MODULES_ANALYTICS_4 } from '../../../datastore/constants'; +import { availableAudiences } from '../../../datastore/__fixtures__'; +import { + createTestRegistry, + provideModuleRegistrations, + provideModules, + provideUserInfo, + render, +} from '../../../../../../../tests/js/test-utils'; +import AudienceErrorModal from './AudienceErrorModal'; + +describe( 'AudienceErrorModal', () => { + let registry; + + beforeEach( () => { + registry = createTestRegistry(); + provideUserInfo( registry ); + provideModules( registry ); + provideModuleRegistrations( registry ); + + registry.dispatch( MODULES_ANALYTICS_4 ).receiveGetSettings( { + accountID: '12345', + propertyID: '34567', + measurementID: '56789', + webDataStreamID: '78901', + availableAudiences, + } ); + } ); + + it( 'should not render if there are no errors', () => { + const { container } = render( , { + registry, + } ); + + expect( container ).toBeEmptyDOMElement(); + } ); + + it( 'should display the error modal when there is an OAuth error', () => { + const { getByText, getByRole } = render( + , + { + registry, + } + ); + + // Verify the error is an OAuth error. + expect( getByText( /Analytics update failed/i ) ).toBeInTheDocument(); + + // Verify the "Get help" link is displayed and points to the correct URL. + expect( getByText( /get help/i ) ).toBeInTheDocument(); + expect( getByRole( 'link', { name: /get help/i } ) ).toHaveAttribute( + 'href', + registry.select( CORE_SITE ).getErrorTroubleshootingLinkURL( { + code: 'access_denied', + } ) + ); + + // Verify the "Retry" button is displayed. + expect( getByText( /retry/i ) ).toBeInTheDocument(); + } ); + + it( 'should display the error modal when there is an insufficient permissions error', () => { + const error = { + code: 'test_error', + message: 'Error message.', + data: { reason: ERROR_REASON_INSUFFICIENT_PERMISSIONS }, + }; + + const { getByText, getByRole } = render( + , + { + registry, + } + ); + + // Verify the error is "Insufficient permissions" variant. + expect( getByText( /Insufficient permissions/i ) ).toBeInTheDocument(); + + // Verify the "Get help" link is displayed and points to the correct URL. + expect( getByText( /get help/i ) ).toBeInTheDocument(); + expect( getByRole( 'link', { name: /get help/i } ) ).toHaveAttribute( + 'href', + registry.select( CORE_SITE ).getErrorTroubleshootingLinkURL( { + code: 'analytics-4_insufficient_permissions', + } ) + ); + + // Verify the "Request access" button is displayed and points to the correct URL. + expect( getByText( /request access/i ) ).toBeInTheDocument(); + expect( + getByRole( 'button', { name: /request access/i } ) + ).toHaveAttribute( + 'href', + registry.select( MODULES_ANALYTICS_4 ).getServiceEntityAccessURL() + ); + } ); + + it( 'should display the error modal when there is generic error', () => { + const error = { + code: 'internal_server_error', + message: 'Internal server error', + data: { status: 500 }, + }; + + const { queryByText, getByText } = render( + , + { + registry, + } + ); + + // Verify the error is a generic error. + expect( + getByText( /Failed to set up visitor groups/i ) + ).toBeInTheDocument(); + + // Verify the "Get help" link is not displayed. + expect( queryByText( /get help/i ) ).not.toBeInTheDocument(); + + // Verify the "Request access" button is not displayed. + expect( queryByText( /request access/i ) ).not.toBeInTheDocument(); + } ); +} ); From 68611c1bc3c23af67f73c5c33b8aa1261be84b44 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Thu, 27 Jun 2024 20:47:04 +0530 Subject: [PATCH 031/105] Don not render the modal if no errors. Pass correct get help link ot the oauth variant. --- .../dashboard/AudienceErrorModal.js | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal.js b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal.js index c6379177845..ef723ec4027 100644 --- a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal.js +++ b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal.js @@ -46,9 +46,6 @@ export default function AudienceErrorModal( { onRetry = () => {}, } ) { const errors = Array.isArray( apiErrors ) ? apiErrors : [ apiErrors ]; - const hasInsufficientPermissionsError = errors.some( ( error ) => - isInsufficientPermissionsError( error ) - ); const helpLink = useSelect( ( select ) => select( CORE_SITE ).getErrorTroubleshootingLinkURL( { @@ -60,6 +57,20 @@ export default function AudienceErrorModal( { select( MODULES_ANALYTICS_4 ).getServiceEntityAccessURL() ); + const errorTroubleshootingLinkURL = useSelect( ( select ) => + select( CORE_SITE ).getErrorTroubleshootingLinkURL( { + code: 'access_denied', + } ) + ); + + if ( ! errors.length && ! hasOAuthError ) { + return null; + } + + const hasInsufficientPermissionsError = errors.some( ( error ) => + isInsufficientPermissionsError( error ) + ); + let title, description, confirmButton, buttonLink; if ( hasOAuthError ) { @@ -71,7 +82,11 @@ export default function AudienceErrorModal( { ), { HelpLink: ( - + { __( 'Get help', 'google-site-kit' ) } ), From 81d4c82774d7e4ab06be4c8c46be58930d2271cd Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Thu, 27 Jun 2024 20:47:19 +0530 Subject: [PATCH 032/105] Export default AudienceErrorModal. --- .../components/audience-segmentation/dashboard/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/index.js b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/index.js index 1e66d3ebe28..3afd3d3740c 100644 --- a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/index.js +++ b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/index.js @@ -16,6 +16,7 @@ * limitations under the License. */ +export { default as AudienceErrorModal } from './AudienceErrorModal'; export { default as AudienceTile } from './AudienceTile'; export { default as AudienceSegmentationErrorWidget } from './AudienceSegmentationErrorWidget'; export { default as AudienceSegmentationIntroductoryOverlayNotification } from './AudienceSegmentationIntroductoryOverlayNotification'; From 35a8ed45967ab0a6894e3124cc1027a85c0bf62c Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Fri, 28 Jun 2024 01:02:55 +0530 Subject: [PATCH 033/105] Fix existing AudienceSegmentationSetupCTAWidget tests. --- ...AudienceSegmentationSetupCTAWidget.test.js | 94 +++++++++++++++---- 1 file changed, 74 insertions(+), 20 deletions(-) diff --git a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.test.js b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.test.js index 6bbffea9501..e7d5fdc530e 100644 --- a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.test.js +++ b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.test.js @@ -33,6 +33,7 @@ import { provideUserAuthentication, unsubscribeFromAll, waitForDefaultTimeouts, + waitForTimeouts, } from '../../../../../../../tests/js/utils'; import { CORE_USER } from '../../../../../googlesitekit/datastore/user/constants'; import { CORE_FORMS } from '../../../../../googlesitekit/datastore/forms/constants'; @@ -40,6 +41,7 @@ import { MODULES_ANALYTICS_4, EDIT_SCOPE, AUDIENCE_SEGMENTATION_SETUP_FORM, + SITE_KIT_AUDIENCE_DEFINITIONS, } from '../../../datastore/constants'; import { availableAudiences as audiencesFixture, @@ -70,6 +72,10 @@ describe( 'AudienceSegmentationSetupCTAWidget', () => { '^/google-site-kit/v1/modules/analytics-4/data/sync-audiences' ); + const createAudienceEndpoint = new RegExp( + '^/google-site-kit/v1/modules/analytics-4/data/create-audience' + ); + const testPropertyID = propertiesFixture[ 0 ]._id; beforeEach( () => { @@ -392,7 +398,7 @@ describe( 'AudienceSegmentationSetupCTAWidget', () => { ).not.toBeInTheDocument(); } ); - it( 'should show the `Don’t show again` CTA when the dismissCount is 1', () => { + it( 'should show the `Don’t show again` CTA when the dismissCount is 1', async () => { registry.dispatch( CORE_USER ).receiveGetDismissedPrompts( { [ AUDIENCE_SEGMENTATION_SETUP_CTA_NOTIFICATION ]: { expires: 1000, @@ -400,7 +406,7 @@ describe( 'AudienceSegmentationSetupCTAWidget', () => { }, } ); - const { getByRole, queryByText } = render( + const { getByRole, queryByText, waitForRegistry } = render( { } ); + await act( waitForRegistry ); + expect( getByRole( 'button', { name: /Don’t show again/i } ) ).toBeInTheDocument(); @@ -432,24 +440,6 @@ describe( 'AudienceSegmentationSetupCTAWidget', () => { .dispatch( MODULES_ANALYTICS_4 ) .receiveGetAudienceSettings( settings ); - const { getByRole } = render( - , - { - registry, - } - ); - - expect( - getByRole( 'button', { name: /Enable groups/i } ) - ).toBeInTheDocument(); - - // eslint-disable-next-line require-await - await act( async () => { - fireEvent.click( - getByRole( 'button', { name: /Enable groups/i } ) - ); - } ); - fetchMock.post( syncAvailableAudiencesEndpoint, { status: 200, body: audiencesFixture, @@ -468,11 +458,53 @@ describe( 'AudienceSegmentationSetupCTAWidget', () => { body: settingsBody, } ); + fetchMock.post( + { url: createAudienceEndpoint, repeat: 2 }, + ( url, opts ) => { + return { + body: opts.body.includes( 'new_visitors' ) + ? { + ...SITE_KIT_AUDIENCE_DEFINITIONS[ + 'new-visitors' + ], + name: audiencesFixture[ 2 ].name, + } + : { + ...SITE_KIT_AUDIENCE_DEFINITIONS[ + 'returning-visitors' + ], + name: audiencesFixture[ 3 ].name, + }, + status: 200, + }; + } + ); + muteFetch( reportEndpoint ); + const { getByRole } = render( + , + { + registry, + } + ); + + expect( + getByRole( 'button', { name: /Enable groups/i } ) + ).toBeInTheDocument(); + + // eslint-disable-next-line require-await + await act( async () => { + fireEvent.click( + getByRole( 'button', { name: /Enable groups/i } ) + ); + } ); + expect( getByRole( 'button', { name: /Enabling groups/i } ) ).toBeInTheDocument(); + + await act( () => waitForTimeouts( 30 ) ); } ); it( 'should initialise the list of configured audiences when autoSubmit is set to true.', async () => { @@ -515,6 +547,28 @@ describe( 'AudienceSegmentationSetupCTAWidget', () => { body: settingsBody, } ); + fetchMock.post( + { url: createAudienceEndpoint, repeat: 2 }, + ( url, opts ) => { + return { + body: opts.body.includes( 'new_visitors' ) + ? { + ...SITE_KIT_AUDIENCE_DEFINITIONS[ + 'new-visitors' + ], + name: audiencesFixture[ 2 ].name, + } + : { + ...SITE_KIT_AUDIENCE_DEFINITIONS[ + 'returning-visitors' + ], + name: audiencesFixture[ 3 ].name, + }, + status: 200, + }; + } + ); + muteFetch( reportEndpoint ); const { getByRole, waitForRegistry } = render( From ffe6c1dd6b37b2894f8c1479f7541117ae0575fb Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Fri, 28 Jun 2024 01:52:35 +0530 Subject: [PATCH 034/105] Fix hasOAuthError condition to return bool. --- .../dashboard/AudienceSegmentationSetupCTAWidget.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js index be10c986233..5048a128b79 100644 --- a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js +++ b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js @@ -116,6 +116,7 @@ function AudienceSegmentationSetupCTAWidget( { Widget, WidgetNull } ) { const [ apiErrors, setApiErrors ] = useState( [] ); const [ failedAudiences, setFailedAudiences ] = useState( [] ); const [ showErrorModal, setShowErrorModal ] = useState( false ); + const onEnableGroups = useCallback( async ( retryFailedAudiences = [] ) => { setIsSaving( true ); @@ -243,7 +244,7 @@ function AudienceSegmentationSetupCTAWidget( { Widget, WidgetNull } ) { select( CORE_SITE ).getSetupErrorCode() ); - const hasOAuthError = autoSubmit && setupErrorCode; + const hasOAuthError = autoSubmit && setupErrorCode === 'access_denied'; if ( isTooltipVisible ) { return ( From 362778793499815075a7ccb4e7754602c95b7742 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Fri, 28 Jun 2024 01:59:35 +0530 Subject: [PATCH 035/105] Add OAuth error modal test. --- ...AudienceSegmentationSetupCTAWidget.test.js | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.test.js b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.test.js index e7d5fdc530e..bfddb86b153 100644 --- a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.test.js +++ b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.test.js @@ -24,6 +24,8 @@ import { act, fireEvent, render, + screen, + waitFor, } from '../../../../../../../tests/js/test-utils'; import { createTestRegistry, @@ -37,6 +39,7 @@ import { } from '../../../../../../../tests/js/utils'; import { CORE_USER } from '../../../../../googlesitekit/datastore/user/constants'; import { CORE_FORMS } from '../../../../../googlesitekit/datastore/forms/constants'; +import { CORE_SITE } from '../../../../../googlesitekit/datastore/site/constants'; import { MODULES_ANALYTICS_4, EDIT_SCOPE, @@ -586,5 +589,79 @@ describe( 'AudienceSegmentationSetupCTAWidget', () => { await act( waitForDefaultTimeouts ); } ); + + describe( 'AudienceErrorModal', () => { + it( 'should show the OAuth error modal when the required scopes are not granted', async () => { + provideSiteInfo( registry, { + setupErrorCode: 'access_denied', + } ); + + provideUserAuthentication( registry, { + grantedScopes: [], + } ); + + const settings = { + configuredAudiences: [], + isAudienceSegmentationWidgetHidden: false, + }; + + // Set the data availability on page load to true. + registry + .dispatch( MODULES_ANALYTICS_4 ) + .receiveIsDataAvailableOnLoad( true ); + + registry + .dispatch( MODULES_ANALYTICS_4 ) + .receiveGetAudienceSettings( settings ); + + // eslint-disable-next-line require-await + await act( async () => { + render( + , + { + registry, + } + ); + } ); + + expect( + screen.getByRole( 'button', { name: /Enable groups/i } ) + ).toBeInTheDocument(); + + act( () => { + fireEvent.click( + screen.getByRole( 'button', { name: /Enable groups/i } ) + ); + } ); + + // Verify the error is an OAuth error. + await waitFor( () => { + expect( + screen.getByText( /Analytics update failed/i ) + ).toBeInTheDocument(); + + expect( + screen.getByText( /get help/i ) + ).toBeInTheDocument(); + + expect( + screen.getByRole( 'link', { name: /get help/i } ) + ).toHaveAttribute( + 'href', + registry + .select( CORE_SITE ) + .getErrorTroubleshootingLinkURL( { + code: 'access_denied', + } ) + ); + + expect( screen.getByText( /retry/i ) ).toBeInTheDocument(); + } ); + + await act( waitForDefaultTimeouts ); + } ); + } ); } ); } ); From 0af641d32f5e035fdb23819e07469465a913b2e2 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Fri, 28 Jun 2024 15:32:34 +0530 Subject: [PATCH 036/105] Add insufficient error variant test. --- ...AudienceSegmentationSetupCTAWidget.test.js | 59 ++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.test.js b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.test.js index bfddb86b153..d5d37213787 100644 --- a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.test.js +++ b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.test.js @@ -50,6 +50,7 @@ import { availableAudiences as audiencesFixture, properties as propertiesFixture, } from '../../../datastore/__fixtures__'; +import { ERROR_REASON_INSUFFICIENT_PERMISSIONS } from '../../../../../util/errors'; import { getWidgetComponentProps } from '../../../../../googlesitekit/widgets/util'; import { getAnalytics4MockResponse } from '../../../utils/data-mock'; import AudienceSegmentationSetupCTAWidget, { @@ -636,12 +637,13 @@ describe( 'AudienceSegmentationSetupCTAWidget', () => { ); } ); - // Verify the error is an OAuth error. + // Verify the error is an OAuth error variant. await waitFor( () => { expect( screen.getByText( /Analytics update failed/i ) ).toBeInTheDocument(); + // Verify the "Get help" link is displayed. expect( screen.getByText( /get help/i ) ).toBeInTheDocument(); @@ -657,11 +659,66 @@ describe( 'AudienceSegmentationSetupCTAWidget', () => { } ) ); + // Verify the "Retry" button is displayed. expect( screen.getByText( /retry/i ) ).toBeInTheDocument(); } ); await act( waitForDefaultTimeouts ); } ); + + it( 'should show the insufficient permissio error modal when the user does not have the required permissions', async () => { + const errorResponse = { + code: 'test_error', + message: 'Error message.', + data: { reason: ERROR_REASON_INSUFFICIENT_PERMISSIONS }, + }; + + fetchMock.post( syncAvailableAudiencesEndpoint, { + body: errorResponse, + status: 500, + } ); + + // eslint-disable-next-line require-await + await act( async () => { + render( + , + { + registry, + } + ); + } ); + + expect( + screen.getByRole( 'button', { name: /Enable groups/i } ) + ).toBeInTheDocument(); + + act( () => { + fireEvent.click( + screen.getByRole( 'button', { name: /Enable groups/i } ) + ); + } ); + + // Verify the error is "Insufficient permissions" variant. + await waitFor( () => { + expect( + screen.getByText( /Insufficient permissions/i ) + ).toBeInTheDocument(); + + // Verify the "Get help" link is displayed. + expect( + screen.getByText( /get help/i ) + ).toBeInTheDocument(); + + // Verify the "Request access" button is displayed. + expect( + screen.getByText( /request access/i ) + ).toBeInTheDocument(); + } ); + + await act( waitForDefaultTimeouts ); + } ); } ); } ); } ); From e292955888a0bc9b8844a957cde53f46b8dfa2ab Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Fri, 28 Jun 2024 15:50:55 +0530 Subject: [PATCH 037/105] Assert missing retry button assertion. --- .../dashboard/AudienceErrorModal.test.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal.test.js b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal.test.js index 6609d7c73fe..9bac77eaafc 100644 --- a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal.test.js +++ b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal.test.js @@ -125,7 +125,7 @@ describe( 'AudienceErrorModal', () => { data: { status: 500 }, }; - const { queryByText, getByText } = render( + const { getByRole, getByText, queryByText } = render( , { registry, @@ -137,6 +137,9 @@ describe( 'AudienceErrorModal', () => { getByText( /Failed to set up visitor groups/i ) ).toBeInTheDocument(); + // Verify the "Retry" button is displayed. + expect( getByRole( 'button', { name: /retry/i } ) ).toBeInTheDocument(); + // Verify the "Get help" link is not displayed. expect( queryByText( /get help/i ) ).not.toBeInTheDocument(); From e78f06dfcca8e8b322ad1e64956eee3be927ba19 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Fri, 28 Jun 2024 15:51:48 +0530 Subject: [PATCH 038/105] Add generic error variant test case. --- ...AudienceSegmentationSetupCTAWidget.test.js | 51 ++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.test.js b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.test.js index d5d37213787..b9d2407357c 100644 --- a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.test.js +++ b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.test.js @@ -666,7 +666,7 @@ describe( 'AudienceSegmentationSetupCTAWidget', () => { await act( waitForDefaultTimeouts ); } ); - it( 'should show the insufficient permissio error modal when the user does not have the required permissions', async () => { + it( 'should show the insufficient permission error modal when the user does not have the required permissions', async () => { const errorResponse = { code: 'test_error', message: 'Error message.', @@ -719,6 +719,55 @@ describe( 'AudienceSegmentationSetupCTAWidget', () => { await act( waitForDefaultTimeouts ); } ); + + it( 'should show the generic error modal when an internal server error occurs', async () => { + const errorResponse = { + code: 'internal_server_error', + message: 'Internal server error', + data: { status: 500 }, + }; + + fetchMock.post( syncAvailableAudiencesEndpoint, { + body: errorResponse, + status: 500, + } ); + + // eslint-disable-next-line require-await + await act( async () => { + render( + , + { + registry, + } + ); + } ); + + expect( + screen.getByRole( 'button', { name: /Enable groups/i } ) + ).toBeInTheDocument(); + + act( () => { + fireEvent.click( + screen.getByRole( 'button', { name: /Enable groups/i } ) + ); + } ); + + // Verify the error is "Insufficient permissions" variant. + await waitFor( () => { + expect( + screen.getByText( /Failed to set up visitor groups/i ) + ).toBeInTheDocument(); + + // Verify the "Retry" button is displayed. + expect( + screen.getByRole( 'button', { name: /retry/i } ) + ).toBeInTheDocument(); + } ); + + await act( waitForDefaultTimeouts ); + } ); } ); } ); } ); From baec29c5aa73f532663321664b80cd08edb81673 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Fri, 28 Jun 2024 16:02:55 +0530 Subject: [PATCH 039/105] Add VRT scenario and clean up. --- .../dashboard/AudienceErrorModal.stories.js | 24 +++---------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal.stories.js b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal.stories.js index c4ba13e64c5..87ce4adca41 100644 --- a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal.stories.js +++ b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal.stories.js @@ -21,13 +21,9 @@ */ import { ERROR_REASON_INSUFFICIENT_PERMISSIONS } from '../../../../../util/errors'; import { MODULES_ANALYTICS_4 } from '../../../datastore/constants'; -// import { useSelect } from 'googlesitekit-data'; import { - // WithTestRegistry, - // createTestRegistry, provideModules, provideModuleRegistrations, - // provideUserInfo, provideSiteInfo, } from '../../../../../../../tests/js/test-utils'; import WithRegistrySetup from '../../../../../../../tests/js/WithRegistrySetup'; @@ -42,6 +38,9 @@ Default.storyName = 'Default'; Default.args = { onRetry: () => {}, }; +Default.scenario = { + label: 'Modules/Analytics4/Components/AudienceSegmentation/Dashboard/AudienceErrorModal/Default', +}; export const WithInsufficientPermissionsError = Template.bind( {} ); WithInsufficientPermissionsError.storyName = 'Insufficient permissions error'; @@ -66,25 +65,8 @@ WithOAuthError.scenario = { label: 'Modules/Analytics4/Components/AudienceSegmentation/Dashboard/AudienceErrorModal/WithOAuthError', }; -// export const AudienceSyncError = Template.bind( {} ); -// AudienceSyncError.storyName = 'Audience sync error'; -// AudienceSyncError.args = { -// setupRegistry: ( registry ) => { -// const error = { -// code: 'test_error', -// message: 'Error message.', -// data: {}, -// }; - -// registry -// .dispatch( MODULES_ANALYTICS_4 ) -// .receiveError( error, 'syncAvailableAudiences' ); -// }, -// }; - export default { title: 'Modules/Analytics4/Components/AudienceSegmentation/Dashboard/AudienceErrorModal', - // component: AudienceErrorModal, decorators: [ ( Story ) => { const setupRegistry = ( registry ) => { From a31c8c119c7893bbb9732a16cf842638d9aba738 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Fri, 28 Jun 2024 16:10:11 +0530 Subject: [PATCH 040/105] Add VRT images for AudienceErrorModal. --- ...nceErrorModal_Default_0_document_0_small.png | Bin 0 -> 22996 bytes ...ceErrorModal_Default_0_document_1_medium.png | Bin 0 -> 25539 bytes ...nceErrorModal_Default_0_document_2_large.png | Bin 0 -> 26949 bytes ...cientPermissionsError_0_document_0_small.png | Bin 0 -> 22095 bytes ...ientPermissionsError_0_document_1_medium.png | Bin 0 -> 27429 bytes ...cientPermissionsError_0_document_2_large.png | Bin 0 -> 28604 bytes ...rModal_WithOAuthError_0_document_0_small.png | Bin 0 -> 22943 bytes ...Modal_WithOAuthError_0_document_1_medium.png | Bin 0 -> 28541 bytes ...rModal_WithOAuthError_0_document_2_large.png | Bin 0 -> 29173 bytes 9 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/backstop/reference/google-site-kit_Modules_Analytics4_Components_AudienceSegmentation_Dashboard_AudienceErrorModal_Default_0_document_0_small.png create mode 100644 tests/backstop/reference/google-site-kit_Modules_Analytics4_Components_AudienceSegmentation_Dashboard_AudienceErrorModal_Default_0_document_1_medium.png create mode 100644 tests/backstop/reference/google-site-kit_Modules_Analytics4_Components_AudienceSegmentation_Dashboard_AudienceErrorModal_Default_0_document_2_large.png create mode 100644 tests/backstop/reference/google-site-kit_Modules_Analytics4_Components_AudienceSegmentation_Dashboard_AudienceErrorModal_WithInsufficientPermissionsError_0_document_0_small.png create mode 100644 tests/backstop/reference/google-site-kit_Modules_Analytics4_Components_AudienceSegmentation_Dashboard_AudienceErrorModal_WithInsufficientPermissionsError_0_document_1_medium.png create mode 100644 tests/backstop/reference/google-site-kit_Modules_Analytics4_Components_AudienceSegmentation_Dashboard_AudienceErrorModal_WithInsufficientPermissionsError_0_document_2_large.png create mode 100644 tests/backstop/reference/google-site-kit_Modules_Analytics4_Components_AudienceSegmentation_Dashboard_AudienceErrorModal_WithOAuthError_0_document_0_small.png create mode 100644 tests/backstop/reference/google-site-kit_Modules_Analytics4_Components_AudienceSegmentation_Dashboard_AudienceErrorModal_WithOAuthError_0_document_1_medium.png create mode 100644 tests/backstop/reference/google-site-kit_Modules_Analytics4_Components_AudienceSegmentation_Dashboard_AudienceErrorModal_WithOAuthError_0_document_2_large.png diff --git a/tests/backstop/reference/google-site-kit_Modules_Analytics4_Components_AudienceSegmentation_Dashboard_AudienceErrorModal_Default_0_document_0_small.png b/tests/backstop/reference/google-site-kit_Modules_Analytics4_Components_AudienceSegmentation_Dashboard_AudienceErrorModal_Default_0_document_0_small.png new file mode 100644 index 0000000000000000000000000000000000000000..aaa2f55e63cf23aee5384df50d2163a958f85d03 GIT binary patch literal 22996 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 TO=8wR#DdPByLgu2aV`5lLE<8R literal 0 HcmV?d00001 diff --git a/tests/backstop/reference/google-site-kit_Modules_Analytics4_Components_AudienceSegmentation_Dashboard_AudienceErrorModal_WithInsufficientPermissionsError_0_document_1_medium.png b/tests/backstop/reference/google-site-kit_Modules_Analytics4_Components_AudienceSegmentation_Dashboard_AudienceErrorModal_WithInsufficientPermissionsError_0_document_1_medium.png new file mode 100644 index 0000000000000000000000000000000000000000..688d01e46b293a455b4194b0af4d32ab2d1f636a GIT binary patch literal 27429 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

V6Bbv3 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 DvN=yt literal 0 HcmV?d00001 diff --git a/tests/backstop/reference/google-site-kit_Modules_Analytics4_Components_AudienceSegmentation_Dashboard_AudienceErrorModal_WithInsufficientPermissionsError_0_document_2_large.png b/tests/backstop/reference/google-site-kit_Modules_Analytics4_Components_AudienceSegmentation_Dashboard_AudienceErrorModal_WithInsufficientPermissionsError_0_document_2_large.png new file mode 100644 index 0000000000000000000000000000000000000000..9a041a127c4c4e088c26b6906fb7e6263f8d4488 GIT binary patch literal 28604 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

`W?*sb05G9OVIrt65reIm0WO?!*xu$O_CSL(0 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 literal 0 HcmV?d00001 diff --git a/tests/backstop/reference/google-site-kit_Modules_Analytics4_Components_AudienceSegmentation_Dashboard_AudienceErrorModal_WithOAuthError_0_document_0_small.png b/tests/backstop/reference/google-site-kit_Modules_Analytics4_Components_AudienceSegmentation_Dashboard_AudienceErrorModal_WithOAuthError_0_document_0_small.png new file mode 100644 index 0000000000000000000000000000000000000000..92105b37cc4e420d9b28e9e3a612772d9a5befa2 GIT binary patch literal 22943 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

Kj8J&q4FLoy;LYPO-ZsSZ` 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≀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 F{{yWnfW`m- literal 0 HcmV?d00001 From e0cdc391543e783c8f74f3a3445ad4094aa97ae1 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Fri, 28 Jun 2024 19:55:54 +0530 Subject: [PATCH 041/105] Improve conditions. --- .../audience-segmentation/dashboard/AudienceErrorModal.js | 1 + .../dashboard/AudienceSegmentationSetupCTAWidget.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal.js b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal.js index ef723ec4027..237ae032c7f 100644 --- a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal.js +++ b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal.js @@ -140,6 +140,7 @@ AudienceErrorModal.propTypes = { apiErrors: PropTypes.oneOfType( [ PropTypes.arrayOf( PropTypes.object ), PropTypes.object, + PropTypes.array, ] ), hasOAuthError: PropTypes.bool, inProgress: PropTypes.bool, diff --git a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js index 5048a128b79..eaad0c5cf19 100644 --- a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js +++ b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js @@ -281,7 +281,7 @@ function AudienceSegmentationSetupCTAWidget( { Widget, WidgetNull } ) { return ( Date: Fri, 28 Jun 2024 20:08:28 +0530 Subject: [PATCH 042/105] Wrap the render call with act. --- ...AudienceSegmentationSetupCTAWidget.test.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.test.js b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.test.js index b9d2407357c..bb85f490a3f 100644 --- a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.test.js +++ b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.test.js @@ -575,17 +575,18 @@ describe( 'AudienceSegmentationSetupCTAWidget', () => { muteFetch( reportEndpoint ); - const { getByRole, waitForRegistry } = render( - , - { - registry, - } - ); - - await act( waitForRegistry ); + // eslint-disable-next-line require-await + await act( async () => { + render( + , + { + registry, + } + ); + } ); expect( - getByRole( 'button', { name: /Enabling groups/i } ) + screen.getByRole( 'button', { name: /Enabling groups/i } ) ).toBeInTheDocument(); await act( waitForDefaultTimeouts ); From 5ef4f9c28e27f52ebf627d472181f9eb70d4b83e Mon Sep 17 00:00:00 2001 From: Arafat Zahan Date: Sat, 29 Jun 2024 06:11:01 +0600 Subject: [PATCH 043/105] Update failing test. --- assets/js/googlesitekit/data/create-settings-store.test.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/assets/js/googlesitekit/data/create-settings-store.test.js b/assets/js/googlesitekit/data/create-settings-store.test.js index d2c9af2f191..f13b9d5879d 100644 --- a/assets/js/googlesitekit/data/create-settings-store.test.js +++ b/assets/js/googlesitekit/data/create-settings-store.test.js @@ -400,13 +400,20 @@ describe( 'createSettingsStore store', () => { ] )( '%s', ( selector ) => { it( 'should use provided validateHaveSettingsChanged function', () => { const validateHaveSettingsChanged = jest.fn(); + storeDefinition = createSettingsStore( ...STORE_ARGS, { settingSlugs: [ 'isSkyBlue' ], validateHaveSettingsChanged, registry, } ); + registry.registerStore( + storeDefinition.STORE_NAME, + storeDefinition + ); + storeDefinition.selectors[ selector ](); + expect( validateHaveSettingsChanged ).toHaveBeenCalled(); } ); } ); From 2316d1a8b351f605c9f7983a00ca360b7cecc169 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Sun, 30 Jun 2024 22:06:35 +0530 Subject: [PATCH 044/105] Fix state update leaks in tests. --- .../dashboard/AudienceSegmentationSetupCTAWidget.test.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.test.js b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.test.js index bb85f490a3f..82583615fbd 100644 --- a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.test.js +++ b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.test.js @@ -585,9 +585,11 @@ describe( 'AudienceSegmentationSetupCTAWidget', () => { ); } ); - expect( - screen.getByRole( 'button', { name: /Enabling groups/i } ) - ).toBeInTheDocument(); + await waitFor( () => { + expect( + screen.getByRole( 'button', { name: /Enabling groups/i } ) + ).toBeInTheDocument(); + } ); await act( waitForDefaultTimeouts ); } ); From 78579b37f3489ec15d1abe049b373400b934549d Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Mon, 1 Jul 2024 23:26:02 +0530 Subject: [PATCH 045/105] Add Reader Revenue Manager Settings components. --- .../components/settings/index.js | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 assets/js/modules/reader-revenue-manager/components/settings/index.js diff --git a/assets/js/modules/reader-revenue-manager/components/settings/index.js b/assets/js/modules/reader-revenue-manager/components/settings/index.js new file mode 100644 index 00000000000..a5495ca2804 --- /dev/null +++ b/assets/js/modules/reader-revenue-manager/components/settings/index.js @@ -0,0 +1,20 @@ +/** + * Reader Revenue Manager Settings components. + * + * Site Kit by Google, Copyright 2024 Google LLC + * + * Licensed 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 + * + * https://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. + */ + +export { default as SettingsEdit } from './SettingsEdit'; +export { default as SettingsView } from './SettingsView'; From 0dd60bdbad3d8409977fafdd3d89561a22e7946a Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Mon, 1 Jul 2024 23:26:19 +0530 Subject: [PATCH 046/105] Add Reader Revenue Manager module entrypoint. --- ...esitekit-modules-reader-revenue-manager.js | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 assets/js/googlesitekit-modules-reader-revenue-manager.js diff --git a/assets/js/googlesitekit-modules-reader-revenue-manager.js b/assets/js/googlesitekit-modules-reader-revenue-manager.js new file mode 100644 index 00000000000..beff1a09e08 --- /dev/null +++ b/assets/js/googlesitekit-modules-reader-revenue-manager.js @@ -0,0 +1,30 @@ +/** + * Reader Revenue Manager module entrypoint. + * + * Site Kit by Google, Copyright 2024 Google LLC + * + * Licensed 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 + * + * https://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. + */ + +/** + * Internal dependencies + */ +import Data from 'googlesitekit-data'; +import Modules from 'googlesitekit-modules'; +import { + registerStore, + registerModule, +} from './modules/reader-revenue-manager'; + +registerStore( Data ); +registerModule( Modules ); From cb85d3f266d4a76b9eb3845c258218c7c54c1a42 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Mon, 1 Jul 2024 23:26:44 +0530 Subject: [PATCH 047/105] Add Settings constants. --- assets/js/components/settings/constants.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 assets/js/components/settings/constants.js diff --git a/assets/js/components/settings/constants.js b/assets/js/components/settings/constants.js new file mode 100644 index 00000000000..60a60c78933 --- /dev/null +++ b/assets/js/components/settings/constants.js @@ -0,0 +1,19 @@ +/** + * Settings constants. + * + * Site Kit by Google, Copyright 2024 Google LLC + * + * Licensed 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 + * + * https://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. + */ + +export const NEW_MODULES = [ 'ads', 'reader-revenue-manager' ]; From 2bebd7f1b6ab893f3ebca63f7554cc29804a228d Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Mon, 1 Jul 2024 23:27:26 +0530 Subject: [PATCH 048/105] Add new badge for RRM in SetupModule. --- assets/js/components/settings/SetupModule.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/assets/js/components/settings/SetupModule.js b/assets/js/components/settings/SetupModule.js index fa5f9d4ce57..5849eaaf796 100644 --- a/assets/js/components/settings/SetupModule.js +++ b/assets/js/components/settings/SetupModule.js @@ -42,6 +42,7 @@ import { CORE_SITE } from '../../googlesitekit/datastore/site/constants'; import { CORE_MODULES } from '../../googlesitekit/modules/datastore/constants'; import { CORE_LOCATION } from '../../googlesitekit/datastore/location/constants'; import { EXPERIMENTAL_MODULES } from '../dashboard-sharing/DashboardSharingSettings/constants'; +import { NEW_MODULES } from './constants'; import { trackEvent } from '../../util'; import useViewContext from '../../hooks/useViewContext'; @@ -114,7 +115,9 @@ export default function SetupModule( { slug, name, description } ) { label={ __( 'Experimental', 'google-site-kit' ) } /> ) } - { slug === 'ads' && } + { NEW_MODULES.includes( slug ) && ( + + ) }

From cb5cb2bfa4ec32963e3bd42e094ebfd4ad548b86 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Mon, 1 Jul 2024 23:28:06 +0530 Subject: [PATCH 049/105] Add new badge for RRM in SettingsActiveModule Header. --- assets/js/components/settings/SettingsActiveModule/Header.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/assets/js/components/settings/SettingsActiveModule/Header.js b/assets/js/components/settings/SettingsActiveModule/Header.js index 4a54bc343b9..7500cf8320b 100644 --- a/assets/js/components/settings/SettingsActiveModule/Header.js +++ b/assets/js/components/settings/SettingsActiveModule/Header.js @@ -37,6 +37,7 @@ import { useSelect } from 'googlesitekit-data'; import { Button } from 'googlesitekit-components'; import { CORE_MODULES } from '../../../googlesitekit/modules/datastore/constants'; import { EXPERIMENTAL_MODULES } from '../../dashboard-sharing/DashboardSharingSettings/constants'; +import { NEW_MODULES } from '../constants'; import { Grid, Row, Cell } from '../../../material-components'; import { useKeyCodesInside } from '../../../hooks/useKeyCodesInside'; import ModuleIcon from '../../ModuleIcon'; @@ -180,7 +181,9 @@ export default function Header( { slug } ) { hasLeftSpacing /> ) } - { slug === 'ads' && } + { NEW_MODULES.includes( slug ) && ( + + ) } From 3c8bfdb71d5fd3b5d83eceabf46bd5a859ac961b Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Mon, 1 Jul 2024 23:28:22 +0530 Subject: [PATCH 050/105] Add Reader Revenue Manager module initialization. --- .../modules/reader-revenue-manager/index.js | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 assets/js/modules/reader-revenue-manager/index.js diff --git a/assets/js/modules/reader-revenue-manager/index.js b/assets/js/modules/reader-revenue-manager/index.js new file mode 100644 index 00000000000..f9d717be37d --- /dev/null +++ b/assets/js/modules/reader-revenue-manager/index.js @@ -0,0 +1,77 @@ +/** + * Reader Revenue Manager module initialization. + * + * Site Kit by Google, Copyright 2024 Google LLC + * + * Licensed 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 + * + * https://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. + */ + +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import { SetupMain } from './components/setup'; +import { SettingsEdit, SettingsView } from './components/settings'; +import ReaderRevenueManagerIcon from '../../../svg/graphics/reader-revenue-manager.svg'; +import { + MODULES_READER_REVENUE_MANAGER, + ERROR_CODE_NON_HTTPS_SITE, +} from './datastore/constants'; +import { CORE_SITE } from '../../googlesitekit/datastore/site/constants'; +import { isFeatureEnabled } from '../../features'; +import { isURLUsingHTTPS } from './utils/validation'; + +export { registerStore } from './datastore'; + +const isRrmModuleEnabled = + ( func ) => + ( ...args ) => { + if ( isFeatureEnabled( 'rrmModule' ) ) { + func( ...args ); + } + }; + +export const registerModule = isRrmModuleEnabled( ( modules ) => { + modules.registerModule( 'reader-revenue-manager', { + storeName: MODULES_READER_REVENUE_MANAGER, + SettingsEditComponent: SettingsEdit, + SettingsViewComponent: SettingsView, + SetupComponent: SetupMain, + Icon: ReaderRevenueManagerIcon, + features: [ + __( 'Create tags without updating code', 'google-site-kit' ), + ], + checkRequirements: async ( registry ) => { + const homeURL = await registry + .__experimentalResolveSelect( CORE_SITE ) + .getHomeURL(); + + if ( isURLUsingHTTPS( homeURL ) ) { + return; + } + + throw { + code: ERROR_CODE_NON_HTTPS_SITE, + message: __( + 'The site should use HTTPS to set up Reader Revenue Manager', + 'google-site-kit' + ), + data: null, + }; + }, + } ); +} ); From 35e600b27e8ba5f4a2a206c67cba1bc5efc7aaea Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Mon, 1 Jul 2024 23:28:35 +0530 Subject: [PATCH 051/105] Add Reader Revenue Manager SettingsEdit component. --- .../components/settings/SettingsEdit.js | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 assets/js/modules/reader-revenue-manager/components/settings/SettingsEdit.js diff --git a/assets/js/modules/reader-revenue-manager/components/settings/SettingsEdit.js b/assets/js/modules/reader-revenue-manager/components/settings/SettingsEdit.js new file mode 100644 index 00000000000..19e6cf56ad3 --- /dev/null +++ b/assets/js/modules/reader-revenue-manager/components/settings/SettingsEdit.js @@ -0,0 +1,37 @@ +/** + * Reader Revenue Manager SettingsEdit component. + * + * Site Kit by Google, Copyright 2024 Google LLC + * + * Licensed 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 + * + * https://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. + */ + +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +export default function SettingsEdit() { + return ( +

+

+ { __( + 'Reader Revenue Manager Settings Edit', + 'google-site-kit' + ) } +

+ + { /* TODO: Add the rest of the settings steps */ } +
+ ); +} From 5968db24ed2371c6cbaee2ba9c0fa0ac8b857f0b Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Mon, 1 Jul 2024 23:28:49 +0530 Subject: [PATCH 052/105] Add Reader Revenue Manager SettingsView component. --- .../components/settings/SettingsView.js | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 assets/js/modules/reader-revenue-manager/components/settings/SettingsView.js diff --git a/assets/js/modules/reader-revenue-manager/components/settings/SettingsView.js b/assets/js/modules/reader-revenue-manager/components/settings/SettingsView.js new file mode 100644 index 00000000000..dfe95f39571 --- /dev/null +++ b/assets/js/modules/reader-revenue-manager/components/settings/SettingsView.js @@ -0,0 +1,37 @@ +/** + * Reader Revenue Manager SettingsView component. + * + * Site Kit by Google, Copyright 2024 Google LLC + * + * Licensed 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 + * + * https://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. + */ + +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +export default function SettingsView() { + return ( +
+

+ { __( + 'Reader Revenue Manager Settings View', + 'google-site-kit' + ) } +

+ + { /* TODO: Add the rest of the settings steps */ } +
+ ); +} From cc32f18dc257d630557a0990703d041c1355720b Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Mon, 1 Jul 2024 23:29:12 +0530 Subject: [PATCH 053/105] Add Reader Revenue Manager SetupMain component. --- .../components/setup/SetupMain.js | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 assets/js/modules/reader-revenue-manager/components/setup/SetupMain.js diff --git a/assets/js/modules/reader-revenue-manager/components/setup/SetupMain.js b/assets/js/modules/reader-revenue-manager/components/setup/SetupMain.js new file mode 100644 index 00000000000..97fc654097e --- /dev/null +++ b/assets/js/modules/reader-revenue-manager/components/setup/SetupMain.js @@ -0,0 +1,47 @@ +/** + * Reader Revenue Manager SetupMain component. + * + * Site Kit by Google, Copyright 2024 Google LLC + * + * Licensed 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 + * + * https://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. + */ + +/** + * WordPress dependencies + */ +import { _x } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import ReaderRevenueManagerIcon from '../../../../../svg/graphics/reader-revenue-manager.svg'; + +export default function SetupMain() { + return ( +
+
+ +
+ +

+ { _x( + 'Reader Revenue Manager', + 'Service name', + 'google-site-kit' + ) } +

+ + { /* TODO: Add the rest of the setup steps */ } +
+ ); +} From 45a3be24706da682efaa0f5924fa08ebefb11448 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Mon, 1 Jul 2024 23:29:49 +0530 Subject: [PATCH 054/105] Add RRM base data store. --- .../reader-revenue-manager/datastore/base.js | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 assets/js/modules/reader-revenue-manager/datastore/base.js diff --git a/assets/js/modules/reader-revenue-manager/datastore/base.js b/assets/js/modules/reader-revenue-manager/datastore/base.js new file mode 100644 index 00000000000..da7cfb7a972 --- /dev/null +++ b/assets/js/modules/reader-revenue-manager/datastore/base.js @@ -0,0 +1,29 @@ +/** + * `modules/reader-revenue-manager` base data store. + * + * Site Kit by Google, Copyright 2024 Google LLC + * + * Licensed 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 + * + * https://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. + */ + +/** + * Internal dependencies + */ +import Modules from 'googlesitekit-modules'; +import { MODULES_READER_REVENUE_MANAGER } from './constants'; +import { validateCanSubmitChanges } from './settings'; + +export default Modules.createModuleStore( 'reader-revenue-manager', { + storeName: MODULES_READER_REVENUE_MANAGER, + validateCanSubmitChanges, +} ); From 2a9f3b45be6d0ff0dbbbf0b42b54b7bd2bddbbc7 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Mon, 1 Jul 2024 23:30:24 +0530 Subject: [PATCH 055/105] Add RRM data store constants. --- .../datastore/constants.js | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 assets/js/modules/reader-revenue-manager/datastore/constants.js diff --git a/assets/js/modules/reader-revenue-manager/datastore/constants.js b/assets/js/modules/reader-revenue-manager/datastore/constants.js new file mode 100644 index 00000000000..bd47a04d973 --- /dev/null +++ b/assets/js/modules/reader-revenue-manager/datastore/constants.js @@ -0,0 +1,21 @@ +/** + * `modules/reader-revenue-manager` data store constants. + * + * Site Kit by Google, Copyright 2024 Google LLC + * + * Licensed 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 + * + * https://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. + */ + +export const MODULES_READER_REVENUE_MANAGER = 'modules/reader-revenue-manager'; + +export const ERROR_CODE_NON_HTTPS_SITE = 'non_https_site'; From e175967dcc8b3775c231837fb361188b88095703 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Mon, 1 Jul 2024 23:30:48 +0530 Subject: [PATCH 056/105] Add RRM registerStore. --- .../reader-revenue-manager/datastore/index.js | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 assets/js/modules/reader-revenue-manager/datastore/index.js diff --git a/assets/js/modules/reader-revenue-manager/datastore/index.js b/assets/js/modules/reader-revenue-manager/datastore/index.js new file mode 100644 index 00000000000..9ce3ea73875 --- /dev/null +++ b/assets/js/modules/reader-revenue-manager/datastore/index.js @@ -0,0 +1,39 @@ +/** + * `modules/reader-revenue-manager` data store. + * + * Site Kit by Google, Copyright 2024 Google LLC + * + * Licensed 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 + * + * https://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. + */ + +/** + * Internal dependencies + */ +import { combineStores } from 'googlesitekit-data'; +import { MODULES_READER_REVENUE_MANAGER } from './constants'; +import baseModuleStore from './base'; + +const store = combineStores( baseModuleStore ); + +export const initialState = store.initialState; +export const actions = store.actions; +export const controls = store.controls; +export const reducer = store.reducer; +export const resolvers = store.resolvers; +export const selectors = store.selectors; + +export const registerStore = ( registry ) => { + registry.registerStore( MODULES_READER_REVENUE_MANAGER, store ); +}; + +export default store; From e56ecf59b6c71701c7c5ec511977b62ab8f1160a Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Mon, 1 Jul 2024 23:33:04 +0530 Subject: [PATCH 057/105] Add RRM setup_assets method. --- includes/Modules/Reader_Revenue_Manager.php | 37 ++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/includes/Modules/Reader_Revenue_Manager.php b/includes/Modules/Reader_Revenue_Manager.php index bd49d782cef..e44e05bee03 100644 --- a/includes/Modules/Reader_Revenue_Manager.php +++ b/includes/Modules/Reader_Revenue_Manager.php @@ -10,7 +10,10 @@ namespace Google\Site_Kit\Modules; +use Google\Site_Kit\Core\Assets\Script; use Google\Site_Kit\Core\Modules\Module; +use Google\Site_Kit\Core\Modules\Module_With_Assets; +use Google\Site_Kit\Core\Modules\Module_With_Assets_Trait; use Google\Site_Kit\Core\Modules\Module_With_Scopes; use Google\Site_Kit\Core\Modules\Module_With_Scopes_Trait; @@ -21,7 +24,8 @@ * @access private * @ignore */ -final class Reader_Revenue_Manager extends Module implements Module_With_Scopes { +final class Reader_Revenue_Manager extends Module implements Module_With_Scopes, Module_With_Assets { + use Module_With_Assets_Trait; use Module_With_Scopes_Trait; /** @@ -67,4 +71,35 @@ protected function setup_info() { 'homepage' => __( 'https://readerrevenue.withgoogle.com/', 'google-site-kit' ), ); } + + /** + * Sets up the module's assets to register. + * + * @since n.e.x.t + * + * @return Asset[] List of Asset objects. + */ + protected function setup_assets() { + $base_url = $this->context->url( 'dist/assets/' ); + + return array( + new Script( + 'googlesitekit-modules-reader-revenue-manager', + array( + 'src' => $base_url . 'js/googlesitekit-modules-reader-revenue-manager.js', + 'dependencies' => array( + 'googlesitekit-vendor', + 'googlesitekit-api', + 'googlesitekit-data', + 'googlesitekit-modules', + 'googlesitekit-datastore-site', + 'googlesitekit-datastore-user', + 'googlesitekit-datastore-forms', + 'googlesitekit-components', + 'googlesitekit-modules-data', + ), + ) + ), + ); + } } From c64fd9789674df66cd6a220f12b037c57d9a2da9 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Mon, 1 Jul 2024 23:33:26 +0530 Subject: [PATCH 058/105] Add RRM settings datastore. --- .../datastore/settings.js | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 assets/js/modules/reader-revenue-manager/datastore/settings.js diff --git a/assets/js/modules/reader-revenue-manager/datastore/settings.js b/assets/js/modules/reader-revenue-manager/datastore/settings.js new file mode 100644 index 00000000000..9365891d8c3 --- /dev/null +++ b/assets/js/modules/reader-revenue-manager/datastore/settings.js @@ -0,0 +1,56 @@ +/** + * `modules/reader-revenue-manager` data store: settings actions. + * + * Site Kit by Google, Copyright 2024 Google LLC + * + * Licensed 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 + * + * https://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. + */ + +/** + * External dependencies + */ +import invariant from 'invariant'; + +/** + * Internal dependencies + */ +import { MODULES_READER_REVENUE_MANAGER } from './constants'; +import { + INVARIANT_DOING_SUBMIT_CHANGES, + INVARIANT_SETTINGS_NOT_CHANGED, +} from '../../../googlesitekit/data/create-settings-store'; +import { createStrictSelect } from '../../../googlesitekit/data/utils'; +import { isValidPublicationID } from '../utils/validation'; + +// Invariant error messages. +export const INVARIANT_INVALID_PUBLICATION_ID = + 'a valid publicationID is required'; + +export function validateCanSubmitChanges( select ) { + const strictSelect = createStrictSelect( select ); + // Strict select will cause all selector functions to throw an error + // if `undefined` is returned, otherwise it behaves the same as `select`. + // This ensures that the selector returns `false` until all data dependencies are resolved. + const { haveSettingsChanged, isDoingSubmitChanges, getPublicationID } = + strictSelect( MODULES_READER_REVENUE_MANAGER ); + + // Note: these error messages are referenced in test assertions. + invariant( ! isDoingSubmitChanges(), INVARIANT_DOING_SUBMIT_CHANGES ); + invariant( haveSettingsChanged(), INVARIANT_SETTINGS_NOT_CHANGED ); + + const publicationID = getPublicationID(); + invariant( + isValidPublicationID( publicationID ), + INVARIANT_INVALID_PUBLICATION_ID + ); +} From 74b9219bf460e30694290aa2cadbfcd32aea0d95 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Mon, 1 Jul 2024 23:33:40 +0530 Subject: [PATCH 059/105] Add RRM Validation utilities.. --- .../utils/validation.js | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 assets/js/modules/reader-revenue-manager/utils/validation.js diff --git a/assets/js/modules/reader-revenue-manager/utils/validation.js b/assets/js/modules/reader-revenue-manager/utils/validation.js new file mode 100644 index 00000000000..9d53e8eda5c --- /dev/null +++ b/assets/js/modules/reader-revenue-manager/utils/validation.js @@ -0,0 +1,49 @@ +/** + * Validation utilities. + * + * Site Kit by Google, Copyright 2024 Google LLC + * + * Licensed 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 + * + * https://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. + */ + +/** + * Checks if the given publication ID appears to be a valid. + * + * @since n.e.x.t + * + * @param {string} publicationID Publication ID to test. + * @return {boolean} `true` if the given publication ID is valid, `false` otherwise. + */ +export function isValidPublicationID( publicationID ) { + return ( + typeof publicationID === 'string' && + /^[A-Za-z0-9_.-]+$/.test( publicationID ) + ); +} + +/** + * Checks if a given URL uses HTTPS. + * + * @since n.e.x.t + * + * @param {string} url The URL to check. + * @return {boolean} True if the URL uses HTTPS, false otherwise. + */ +export const isURLUsingHTTPS = ( url ) => { + try { + const parsedURL = new URL( url ); + return parsedURL.protocol === 'https:'; + } catch ( error ) { + return false; + } +}; From 82c4cdb6a5c4b0ca24881b97b603fe7832a7ea35 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Mon, 1 Jul 2024 23:33:55 +0530 Subject: [PATCH 060/105] Add RRM svg logo. --- .../svg/graphics/reader-revenue-manager.svg | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 assets/svg/graphics/reader-revenue-manager.svg diff --git a/assets/svg/graphics/reader-revenue-manager.svg b/assets/svg/graphics/reader-revenue-manager.svg new file mode 100644 index 00000000000..8860e5d866d --- /dev/null +++ b/assets/svg/graphics/reader-revenue-manager.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + From b2a4e602d91032d388dcc26116a13275f7b1b84c Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Mon, 1 Jul 2024 23:34:27 +0530 Subject: [PATCH 061/105] Add RRM module entry in webpack config. --- webpack/modules.config.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/webpack/modules.config.js b/webpack/modules.config.js index d8301f79f48..31d115159e3 100644 --- a/webpack/modules.config.js +++ b/webpack/modules.config.js @@ -74,6 +74,8 @@ module.exports = ( mode, rules, ANALYZE ) => { './assets/js/googlesitekit-modules-analytics-4.js', 'googlesitekit-modules-pagespeed-insights': 'assets/js/googlesitekit-modules-pagespeed-insights.js', + 'googlesitekit-modules-reader-revenue-manager': + './assets/js/googlesitekit-modules-reader-revenue-manager.js', 'googlesitekit-modules-search-console': './assets/js/googlesitekit-modules-search-console.js', 'googlesitekit-modules-tagmanager': From 71cbe5af64b121f6e15ea1920a575f9253cc4760 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Mon, 1 Jul 2024 23:38:18 +0530 Subject: [PATCH 062/105] Add Reader Revenue Manager Setup components. --- .../components/setup/index.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 assets/js/modules/reader-revenue-manager/components/setup/index.js diff --git a/assets/js/modules/reader-revenue-manager/components/setup/index.js b/assets/js/modules/reader-revenue-manager/components/setup/index.js new file mode 100644 index 00000000000..d0c584f8d23 --- /dev/null +++ b/assets/js/modules/reader-revenue-manager/components/setup/index.js @@ -0,0 +1,19 @@ +/** + * Reader Revenue Manager Setup components. + * + * Site Kit by Google, Copyright 2024 Google LLC + * + * Licensed 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 + * + * https://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. + */ + +export { default as SetupMain } from './SetupMain'; From d3b1623009dda7a5574758d369390f79db196365 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Tue, 2 Jul 2024 12:09:34 +0530 Subject: [PATCH 063/105] Add rrm base data store tests. --- .../datastore/base.test.js | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 assets/js/modules/reader-revenue-manager/datastore/base.test.js diff --git a/assets/js/modules/reader-revenue-manager/datastore/base.test.js b/assets/js/modules/reader-revenue-manager/datastore/base.test.js new file mode 100644 index 00000000000..98413b2567e --- /dev/null +++ b/assets/js/modules/reader-revenue-manager/datastore/base.test.js @@ -0,0 +1,51 @@ +/** + * `modules/reader-revenue-manager` base data store tests. + * + * Site Kit by Google, Copyright 2024 Google LLC + * + * Licensed 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 + * + * https://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. + */ + +/** + * Internal dependencies + */ +import { + createTestRegistry, + provideSiteInfo, +} from '../../../../../tests/js/utils'; +import { MODULES_READER_REVENUE_MANAGER } from './constants'; + +describe( 'modules/reader-revenue-manager base data store', () => { + let registry; + let store; + + beforeEach( () => { + jest.resetModules(); + + registry = createTestRegistry(); + provideSiteInfo( registry ); + } ); + + it( 'does not define the admin page', () => { + store = require( './base' ).default; + registry.registerStore( MODULES_READER_REVENUE_MANAGER, store ); + + expect( + registry + .select( MODULES_READER_REVENUE_MANAGER ) + .getAdminScreenURL() + ).toBe( + 'http://example.com/wp-admin/admin.php?page=googlesitekit-dashboard' + ); + } ); +} ); From ef742746c56e69bdc739ad1deacfec9c1bfde9f3 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Tue, 2 Jul 2024 12:09:58 +0530 Subject: [PATCH 064/105] Add rrm index test base. --- .../datastore/index.test.js | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 assets/js/modules/reader-revenue-manager/datastore/index.test.js diff --git a/assets/js/modules/reader-revenue-manager/datastore/index.test.js b/assets/js/modules/reader-revenue-manager/datastore/index.test.js new file mode 100644 index 00000000000..4d86c0d6034 --- /dev/null +++ b/assets/js/modules/reader-revenue-manager/datastore/index.test.js @@ -0,0 +1,43 @@ +/** + * `modules/reader-revenue-manager` data store: selectors test. + * + * Site Kit by Google, Copyright 2024 Google LLC + * + * Licensed 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 + * + * https://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. + */ + +/** + * Internal dependencies + */ +import { + createTestRegistry, + unsubscribeFromAll, +} from '../../../../../tests/js/utils'; + +describe( 'modules/reader-revenue-manager', () => { + let registry; + + beforeEach( () => { + registry = createTestRegistry(); + } ); + + afterEach( () => { + unsubscribeFromAll( registry ); + } ); + + describe( 'store', () => { + it( 'is registered correctly', () => { + // TODO: Implement tests for the reader-revenue-manager store. + } ); + } ); +} ); From f941c7dc55b61ce3cecec70f7112a2bc712e88fd Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Tue, 2 Jul 2024 12:42:09 +0530 Subject: [PATCH 065/105] Add rrm settings test base file. --- .../datastore/index.test.js | 4 +- .../datastore/settings.test.js | 50 +++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 assets/js/modules/reader-revenue-manager/datastore/settings.test.js diff --git a/assets/js/modules/reader-revenue-manager/datastore/index.test.js b/assets/js/modules/reader-revenue-manager/datastore/index.test.js index 4d86c0d6034..1d7dfd0f5f9 100644 --- a/assets/js/modules/reader-revenue-manager/datastore/index.test.js +++ b/assets/js/modules/reader-revenue-manager/datastore/index.test.js @@ -24,7 +24,7 @@ import { unsubscribeFromAll, } from '../../../../../tests/js/utils'; -describe( 'modules/reader-revenue-manager', () => { +describe( 'modules/reader-revenue-manager properties', () => { let registry; beforeEach( () => { @@ -37,7 +37,7 @@ describe( 'modules/reader-revenue-manager', () => { describe( 'store', () => { it( 'is registered correctly', () => { - // TODO: Implement tests for the reader-revenue-manager store. + // TODO: Implement tests as part of #8793. } ); } ); } ); diff --git a/assets/js/modules/reader-revenue-manager/datastore/settings.test.js b/assets/js/modules/reader-revenue-manager/datastore/settings.test.js new file mode 100644 index 00000000000..eae66f48b0b --- /dev/null +++ b/assets/js/modules/reader-revenue-manager/datastore/settings.test.js @@ -0,0 +1,50 @@ +/** + * `modules/reader-revenue-manager` data store: settings tests. + * + * Site Kit by Google, Copyright 2024 Google LLC + * + * Licensed 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 + * + * https://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. + */ + +/** + * Internal dependencies + */ +import API from 'googlesitekit-api'; +import { + createTestRegistry, + unsubscribeFromAll, +} from '../../../../../tests/js/utils'; + +describe( 'modules/reader-revenue-manager settings', () => { + let registry; + + beforeAll( () => { + API.setUsingCache( false ); + } ); + + beforeEach( () => { + registry = createTestRegistry(); + } ); + + afterEach( () => { + unsubscribeFromAll( registry ); + } ); + + afterAll( () => { + API.setUsingCache( true ); + } ); + + describe( 'validateCanSubmitChanges', () => { + // TODO: Implement tests as part of #8793. + } ); +} ); From c556ad5052dcae60196b9bc709888df6dcf8555d Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Tue, 2 Jul 2024 12:48:54 +0530 Subject: [PATCH 066/105] Add Validation function tests. --- .../utils/validation.test.js | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 assets/js/modules/reader-revenue-manager/utils/validation.test.js diff --git a/assets/js/modules/reader-revenue-manager/utils/validation.test.js b/assets/js/modules/reader-revenue-manager/utils/validation.test.js new file mode 100644 index 00000000000..c2ff6e87105 --- /dev/null +++ b/assets/js/modules/reader-revenue-manager/utils/validation.test.js @@ -0,0 +1,57 @@ +/** + * Validation function tests. + * + * Site Kit by Google, Copyright 2024 Google LLC + * + * Licensed 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 + * + * https://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. + */ + +/** + * Internal dependencies + */ +import { isValidPublicationID, isURLUsingHTTPS } from './validation'; + +describe( 'utility functions', () => { + describe( 'isValidPublicationID', () => { + it( 'should return TRUE when a valid publication ID is passed', () => { + expect( isValidPublicationID( 'valid-publication_123.ID' ) ).toBe( + true + ); + } ); + + it.each( [ + [ 'false', false ], + [ 'an integer', 12345 ], + [ 'an empty string', '' ], + [ 'a string with invalid characters', 'invalid-publication!ID' ], + ] )( 'should return FALSE when %s is passed', ( _, publicationID ) => { + expect( isValidPublicationID( publicationID ) ).toBe( false ); + } ); + } ); + + describe( 'isURLUsingHTTPS', () => { + it( 'should return TRUE when a URL with HTTPS is passed', () => { + expect( isURLUsingHTTPS( 'https://example.com' ) ).toBe( true ); + } ); + + it.each( [ + [ 'an HTTP URL', 'http://example.com' ], + [ 'an invalid URL', 'htp://example.com' ], + [ 'a string without protocol', 'example.com' ], + [ 'an empty string', '' ], + [ 'false', false ], + ] )( 'should return FALSE when %s is passed', ( _, url ) => { + expect( isURLUsingHTTPS( url ) ).toBe( false ); + } ); + } ); +} ); From 306e1a62905aad32376951d7e4ebf60c3cd4b0a1 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Tue, 2 Jul 2024 15:44:55 +0530 Subject: [PATCH 067/105] Add SettingsEdit storybook. --- .../settings/SettingsEdit.stories.js | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 assets/js/modules/reader-revenue-manager/components/settings/SettingsEdit.stories.js diff --git a/assets/js/modules/reader-revenue-manager/components/settings/SettingsEdit.stories.js b/assets/js/modules/reader-revenue-manager/components/settings/SettingsEdit.stories.js new file mode 100644 index 00000000000..95bdae39dbf --- /dev/null +++ b/assets/js/modules/reader-revenue-manager/components/settings/SettingsEdit.stories.js @@ -0,0 +1,34 @@ +/** + * Reader Revenue Manager SettingsEdit component stories. + * + * Site Kit by Google, Copyright 2022 Google LLC + * + * Licensed 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 + * + * https://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. + */ + +/** + * Internal dependencies + */ +import SettingsEdit from './SettingsEdit'; + +function Template() { + return ; +} + +export const Default = Template.bind( {} ); +Default.storyName = 'Default'; + +export default { + title: 'Modules/ReaderRevenueManager/Settings/SettingsEdit', + component: SettingsEdit, +}; From d0c88862d10bc3c9f4d2f8253c6e810a30864c44 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Tue, 2 Jul 2024 15:45:07 +0530 Subject: [PATCH 068/105] Add SettingsEdit test file. --- .../components/settings/SettingsEdit.test.js | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 assets/js/modules/reader-revenue-manager/components/settings/SettingsEdit.test.js diff --git a/assets/js/modules/reader-revenue-manager/components/settings/SettingsEdit.test.js b/assets/js/modules/reader-revenue-manager/components/settings/SettingsEdit.test.js new file mode 100644 index 00000000000..66bf0edc12c --- /dev/null +++ b/assets/js/modules/reader-revenue-manager/components/settings/SettingsEdit.test.js @@ -0,0 +1,30 @@ +/** + * Reader Revenue Manager SettingsEdit component tests. + * + * Site Kit by Google, Copyright 2024 Google LLC + * + * Licensed 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 + * + * https://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 { render } from '../../../../../../tests/js/test-utils'; +import SettingsEdit from './SettingsEdit'; + +describe( 'SettingsEdit', () => { + it( 'should render the component', () => { + const { getByText } = render( ); + + expect( + getByText( /Reader Revenue Manager Settings Edit/i ) + ).toBeInTheDocument(); + } ); +} ); From 712ed3db7caedbc433e905b6a08068706ca0fcb7 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Tue, 2 Jul 2024 15:45:20 +0530 Subject: [PATCH 069/105] Add SettingsView storybook. --- .../settings/SettingsView.stories.js | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 assets/js/modules/reader-revenue-manager/components/settings/SettingsView.stories.js diff --git a/assets/js/modules/reader-revenue-manager/components/settings/SettingsView.stories.js b/assets/js/modules/reader-revenue-manager/components/settings/SettingsView.stories.js new file mode 100644 index 00000000000..086602a2a8a --- /dev/null +++ b/assets/js/modules/reader-revenue-manager/components/settings/SettingsView.stories.js @@ -0,0 +1,34 @@ +/** + * Reader Revenue Manager SettingsView component stories. + * + * Site Kit by Google, Copyright 2022 Google LLC + * + * Licensed 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 + * + * https://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. + */ + +/** + * Internal dependencies + */ +import SettingsView from './SettingsView'; + +function Template() { + return ; +} + +export const Default = Template.bind( {} ); +Default.storyName = 'Default'; + +export default { + title: 'Modules/ReaderRevenueManager/Settings/SettingsView', + component: SettingsView, +}; From 16531146c7a5edc9a22c80abddfc1dbf8642fc2a Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Tue, 2 Jul 2024 15:45:31 +0530 Subject: [PATCH 070/105] Add SettingsView test file. --- .../components/settings/SettingsView.test.js | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 assets/js/modules/reader-revenue-manager/components/settings/SettingsView.test.js diff --git a/assets/js/modules/reader-revenue-manager/components/settings/SettingsView.test.js b/assets/js/modules/reader-revenue-manager/components/settings/SettingsView.test.js new file mode 100644 index 00000000000..96e585ac452 --- /dev/null +++ b/assets/js/modules/reader-revenue-manager/components/settings/SettingsView.test.js @@ -0,0 +1,30 @@ +/** + * Reader Revenue Manager SettingsView component tests. + * + * Site Kit by Google, Copyright 2024 Google LLC + * + * Licensed 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 + * + * https://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 { render } from '../../../../../../tests/js/test-utils'; +import SettingsView from './SettingsView'; + +describe( 'SettingsView', () => { + it( 'should render the component', () => { + const { getByText } = render( ); + + expect( + getByText( /Reader Revenue Manager Settings View/i ) + ).toBeInTheDocument(); + } ); +} ); From d1a9dded0fe4cd31f4e5c7bd96d3094e4dcb9799 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Tue, 2 Jul 2024 15:45:42 +0530 Subject: [PATCH 071/105] Add SetupMain storybook. --- .../components/setup/SetupMain.stories.js | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 assets/js/modules/reader-revenue-manager/components/setup/SetupMain.stories.js diff --git a/assets/js/modules/reader-revenue-manager/components/setup/SetupMain.stories.js b/assets/js/modules/reader-revenue-manager/components/setup/SetupMain.stories.js new file mode 100644 index 00000000000..c938d28c75d --- /dev/null +++ b/assets/js/modules/reader-revenue-manager/components/setup/SetupMain.stories.js @@ -0,0 +1,34 @@ +/** + * Reader Revenue Manager SetupMain component stories. + * + * Site Kit by Google, Copyright 2022 Google LLC + * + * Licensed 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 + * + * https://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. + */ + +/** + * Internal dependencies + */ +import SetupMain from './SetupMain'; + +function Template() { + return ; +} + +export const Default = Template.bind( {} ); +Default.storyName = 'Default'; + +export default { + title: 'Modules/ReaderRevenueManager/Setup/SetupMain', + component: SetupMain, +}; From 741087e9a90cb1c8af4090f276cdde15b5d22b33 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Tue, 2 Jul 2024 15:45:53 +0530 Subject: [PATCH 072/105] Add SetupMain test file. --- .../components/setup/SetupMain.test.js | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 assets/js/modules/reader-revenue-manager/components/setup/SetupMain.test.js diff --git a/assets/js/modules/reader-revenue-manager/components/setup/SetupMain.test.js b/assets/js/modules/reader-revenue-manager/components/setup/SetupMain.test.js new file mode 100644 index 00000000000..62b8d736342 --- /dev/null +++ b/assets/js/modules/reader-revenue-manager/components/setup/SetupMain.test.js @@ -0,0 +1,28 @@ +/** + * Reader Revenue Manager SetupMain component tests. + * + * Site Kit by Google, Copyright 2024 Google LLC + * + * Licensed 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 + * + * https://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 { render } from '../../../../../../tests/js/test-utils'; +import SetupMain from './SetupMain'; + +describe( 'SetupMain', () => { + it( 'should render the component', () => { + const { getByText } = render( ); + + expect( getByText( /Reader Revenue Manager/i ) ).toBeInTheDocument(); + } ); +} ); From 1640fd2be1eb7d42f1b6a9162201cb2b7731c792 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Tue, 2 Jul 2024 15:58:55 +0530 Subject: [PATCH 073/105] Add todo comment in the registerModule features. --- assets/js/modules/reader-revenue-manager/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/modules/reader-revenue-manager/index.js b/assets/js/modules/reader-revenue-manager/index.js index f9d717be37d..cbf6c1be88b 100644 --- a/assets/js/modules/reader-revenue-manager/index.js +++ b/assets/js/modules/reader-revenue-manager/index.js @@ -53,7 +53,7 @@ export const registerModule = isRrmModuleEnabled( ( modules ) => { SetupComponent: SetupMain, Icon: ReaderRevenueManagerIcon, features: [ - __( 'Create tags without updating code', 'google-site-kit' ), + // TODO: Implement the features as part of #8845. ], checkRequirements: async ( registry ) => { const homeURL = await registry From 35ab985e242ecfec4c145d16303ba0abe5fa1ac0 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Tue, 2 Jul 2024 16:22:01 +0530 Subject: [PATCH 074/105] Add modulesReaderRevenueManager in tests/js/utils. --- tests/js/utils.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/js/utils.js b/tests/js/utils.js index ad379800e2b..45c9f13343c 100644 --- a/tests/js/utils.js +++ b/tests/js/utils.js @@ -42,6 +42,7 @@ import * as modulesAds from '../../assets/js/modules/ads'; import * as modulesAdSense from '../../assets/js/modules/adsense'; import * as modulesAnalytics4 from '../../assets/js/modules/analytics-4'; import * as modulesPageSpeedInsights from '../../assets/js/modules/pagespeed-insights'; +import * as modulesReaderRevenueManager from '../../assets/js/modules/reader-revenue-manager'; import * as modulesSearchConsole from '../../assets/js/modules/search-console'; import * as modulesTagManager from '../../assets/js/modules/tagmanager'; import { CORE_SITE } from '../../assets/js/googlesitekit/datastore/site/constants'; @@ -74,6 +75,7 @@ const allCoreModules = [ modulesAdSense, modulesAnalytics4, modulesPageSpeedInsights, + modulesReaderRevenueManager, modulesSearchConsole, modulesTagManager, ]; From 328ca751310883a7b42fe972d87cd027844d4160 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Tue, 2 Jul 2024 16:22:19 +0530 Subject: [PATCH 075/105] Fix settings test. --- .../modules/reader-revenue-manager/datastore/settings.test.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/assets/js/modules/reader-revenue-manager/datastore/settings.test.js b/assets/js/modules/reader-revenue-manager/datastore/settings.test.js index eae66f48b0b..f772e88a2bb 100644 --- a/assets/js/modules/reader-revenue-manager/datastore/settings.test.js +++ b/assets/js/modules/reader-revenue-manager/datastore/settings.test.js @@ -45,6 +45,8 @@ describe( 'modules/reader-revenue-manager settings', () => { } ); describe( 'validateCanSubmitChanges', () => { - // TODO: Implement tests as part of #8793. + it( 'it validates', () => { + // TODO: Implement tests as part of #8793. + } ); } ); } ); From cbf8f7e52b0133c1b62a7f3e4e6405055a708010 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Tue, 2 Jul 2024 16:26:13 +0530 Subject: [PATCH 076/105] Order imports. --- assets/js/modules/reader-revenue-manager/index.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/assets/js/modules/reader-revenue-manager/index.js b/assets/js/modules/reader-revenue-manager/index.js index cbf6c1be88b..955b600b4a5 100644 --- a/assets/js/modules/reader-revenue-manager/index.js +++ b/assets/js/modules/reader-revenue-manager/index.js @@ -24,14 +24,14 @@ import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import { SetupMain } from './components/setup'; -import { SettingsEdit, SettingsView } from './components/settings'; -import ReaderRevenueManagerIcon from '../../../svg/graphics/reader-revenue-manager.svg'; +import { CORE_SITE } from '../../googlesitekit/datastore/site/constants'; import { MODULES_READER_REVENUE_MANAGER, ERROR_CODE_NON_HTTPS_SITE, } from './datastore/constants'; -import { CORE_SITE } from '../../googlesitekit/datastore/site/constants'; +import { SetupMain } from './components/setup'; +import { SettingsEdit, SettingsView } from './components/settings'; +import ReaderRevenueManagerIcon from '../../../svg/graphics/reader-revenue-manager.svg'; import { isFeatureEnabled } from '../../features'; import { isURLUsingHTTPS } from './utils/validation'; From 2761f760257f114fa450396070d5b37adedc5971 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Tue, 2 Jul 2024 22:20:23 +0530 Subject: [PATCH 077/105] Improve isURLUsingHTTPS fn. --- .../reader-revenue-manager/utils/validation.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/assets/js/modules/reader-revenue-manager/utils/validation.js b/assets/js/modules/reader-revenue-manager/utils/validation.js index 9d53e8eda5c..b26015bf27e 100644 --- a/assets/js/modules/reader-revenue-manager/utils/validation.js +++ b/assets/js/modules/reader-revenue-manager/utils/validation.js @@ -41,9 +41,18 @@ export function isValidPublicationID( publicationID ) { */ export const isURLUsingHTTPS = ( url ) => { try { + if ( typeof url !== 'string' || ! url ) { + throw new TypeError( `Invalid URL: ${ url }` ); + } + const parsedURL = new URL( url ); - return parsedURL.protocol === 'https:'; + if ( parsedURL.protocol !== 'https:' ) { + throw new TypeError( `Invalid URL: ${ url }` ); + } + + return true; } catch ( error ) { + global.console.warn( 'Invalid URL:', error ); return false; } }; From 861a4646de5894e6572f0bb59442ee19604b34ce Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Tue, 2 Jul 2024 22:20:35 +0530 Subject: [PATCH 078/105] Improve isURLUsingHTTPS tests. --- .../utils/validation.test.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/assets/js/modules/reader-revenue-manager/utils/validation.test.js b/assets/js/modules/reader-revenue-manager/utils/validation.test.js index c2ff6e87105..1d419ebb678 100644 --- a/assets/js/modules/reader-revenue-manager/utils/validation.test.js +++ b/assets/js/modules/reader-revenue-manager/utils/validation.test.js @@ -40,8 +40,24 @@ describe( 'utility functions', () => { } ); describe( 'isURLUsingHTTPS', () => { + beforeAll( () => { + jest.spyOn( console, 'warn' ).mockImplementation( () => {} ); + } ); + + afterAll( () => { + // eslint-disable-next-line no-console + console.warn.mockRestore(); + } ); + + afterEach( () => { + // eslint-disable-next-line no-console + console.warn.mockClear(); + } ); + it( 'should return TRUE when a URL with HTTPS is passed', () => { expect( isURLUsingHTTPS( 'https://example.com' ) ).toBe( true ); + // eslint-disable-next-line no-console + expect( console.warn ).not.toHaveBeenCalled(); } ); it.each( [ @@ -52,6 +68,8 @@ describe( 'utility functions', () => { [ 'false', false ], ] )( 'should return FALSE when %s is passed', ( _, url ) => { expect( isURLUsingHTTPS( url ) ).toBe( false ); + // eslint-disable-next-line no-console + expect( console.warn ).toHaveBeenCalled(); } ); } ); } ); From 914360964410c7c6a2ea3b00b04682e9f70e9b4c Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Tue, 2 Jul 2024 22:22:40 +0530 Subject: [PATCH 079/105] Ensure the site info is resolved to get the home URL. --- assets/js/modules/reader-revenue-manager/index.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/assets/js/modules/reader-revenue-manager/index.js b/assets/js/modules/reader-revenue-manager/index.js index 955b600b4a5..8f95b53edac 100644 --- a/assets/js/modules/reader-revenue-manager/index.js +++ b/assets/js/modules/reader-revenue-manager/index.js @@ -56,9 +56,11 @@ export const registerModule = isRrmModuleEnabled( ( modules ) => { // TODO: Implement the features as part of #8845. ], checkRequirements: async ( registry ) => { - const homeURL = await registry + // Ensure the site info is resolved to get the home URL. + await registry .__experimentalResolveSelect( CORE_SITE ) - .getHomeURL(); + .getSiteInfo(); + const homeURL = registry.select( CORE_SITE ).getHomeURL(); if ( isURLUsingHTTPS( homeURL ) ) { return; From fff0ef4d35be3969fb50d3fb2bf4e366d3a2691a Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Tue, 2 Jul 2024 22:23:02 +0530 Subject: [PATCH 080/105] Address review feedback. --- .../components/settings/SettingsEdit.stories.js | 2 +- .../components/settings/SettingsView.stories.js | 2 +- .../components/setup/SetupMain.stories.js | 2 +- assets/js/modules/reader-revenue-manager/datastore/settings.js | 1 - 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/assets/js/modules/reader-revenue-manager/components/settings/SettingsEdit.stories.js b/assets/js/modules/reader-revenue-manager/components/settings/SettingsEdit.stories.js index 95bdae39dbf..9901e1830c6 100644 --- a/assets/js/modules/reader-revenue-manager/components/settings/SettingsEdit.stories.js +++ b/assets/js/modules/reader-revenue-manager/components/settings/SettingsEdit.stories.js @@ -1,7 +1,7 @@ /** * Reader Revenue Manager SettingsEdit component stories. * - * Site Kit by Google, Copyright 2022 Google LLC + * Site Kit by Google, Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/assets/js/modules/reader-revenue-manager/components/settings/SettingsView.stories.js b/assets/js/modules/reader-revenue-manager/components/settings/SettingsView.stories.js index 086602a2a8a..3f317700f12 100644 --- a/assets/js/modules/reader-revenue-manager/components/settings/SettingsView.stories.js +++ b/assets/js/modules/reader-revenue-manager/components/settings/SettingsView.stories.js @@ -1,7 +1,7 @@ /** * Reader Revenue Manager SettingsView component stories. * - * Site Kit by Google, Copyright 2022 Google LLC + * Site Kit by Google, Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/assets/js/modules/reader-revenue-manager/components/setup/SetupMain.stories.js b/assets/js/modules/reader-revenue-manager/components/setup/SetupMain.stories.js index c938d28c75d..f83618bbdc4 100644 --- a/assets/js/modules/reader-revenue-manager/components/setup/SetupMain.stories.js +++ b/assets/js/modules/reader-revenue-manager/components/setup/SetupMain.stories.js @@ -1,7 +1,7 @@ /** * Reader Revenue Manager SetupMain component stories. * - * Site Kit by Google, Copyright 2022 Google LLC + * Site Kit by Google, Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/assets/js/modules/reader-revenue-manager/datastore/settings.js b/assets/js/modules/reader-revenue-manager/datastore/settings.js index 9365891d8c3..57c408bef23 100644 --- a/assets/js/modules/reader-revenue-manager/datastore/settings.js +++ b/assets/js/modules/reader-revenue-manager/datastore/settings.js @@ -44,7 +44,6 @@ export function validateCanSubmitChanges( select ) { const { haveSettingsChanged, isDoingSubmitChanges, getPublicationID } = strictSelect( MODULES_READER_REVENUE_MANAGER ); - // Note: these error messages are referenced in test assertions. invariant( ! isDoingSubmitChanges(), INVARIANT_DOING_SUBMIT_CHANGES ); invariant( haveSettingsChanged(), INVARIANT_SETTINGS_NOT_CHANGED ); From 01d1757c05279eed5d8768bc5201b8ff69fcfa52 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Tue, 2 Jul 2024 23:10:43 +0530 Subject: [PATCH 081/105] Revert the changes in auth permissions modal. --- .../PermissionsModal/AuthenticatedPermissionsModal.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/assets/js/components/PermissionsModal/AuthenticatedPermissionsModal.js b/assets/js/components/PermissionsModal/AuthenticatedPermissionsModal.js index 123a1f74556..5efa33da904 100644 --- a/assets/js/components/PermissionsModal/AuthenticatedPermissionsModal.js +++ b/assets/js/components/PermissionsModal/AuthenticatedPermissionsModal.js @@ -71,9 +71,7 @@ function AuthenticatedPermissionsModal() { await snapshotAllStores( registry ); // Navigate to the authorization page. - if ( ! permissionsError?.data?.skipDefaultErrorNotifications ) { - navigateTo( connectURL ); - } + navigateTo( connectURL ); }, [ registry, connectURL, navigateTo, permissionsError, setValues ] ); useEffect( () => { @@ -105,10 +103,6 @@ function AuthenticatedPermissionsModal() { return null; } - if ( permissionsError?.data?.skipDefaultErrorNotifications ) { - return null; - } - if ( permissionsError?.data?.skipModal ) { return null; } From 4424a12b4d8476f9fc6a198abf125e60ed2543e7 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Tue, 2 Jul 2024 23:27:52 +0530 Subject: [PATCH 082/105] Fix logic errors. Address CR feedback. Improve conditions. --- .../AudienceSegmentationSetupCTAWidget.js | 139 +++++++----------- 1 file changed, 55 insertions(+), 84 deletions(-) diff --git a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js index eaad0c5cf19..d6bc2381c56 100644 --- a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js +++ b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js @@ -38,7 +38,6 @@ import BannerGraphicsSVGTablet from '../../../../../../svg/graphics/audience-seg import BannerGraphicsSVGMobile from '../../../../../../svg/graphics/audience-segmentation-setup-mobile.svg'; import whenActive from '../../../../../util/when-active'; import { CORE_FORMS } from '../../../../../googlesitekit/datastore/forms/constants'; -import { CORE_LOCATION } from '../../../../../googlesitekit/datastore/location/constants'; import { CORE_USER } from '../../../../../googlesitekit/datastore/user/constants'; import { CORE_SITE } from '../../../../../googlesitekit/datastore/site/constants'; import { @@ -117,69 +116,63 @@ function AudienceSegmentationSetupCTAWidget( { Widget, WidgetNull } ) { const [ failedAudiences, setFailedAudiences ] = useState( [] ); const [ showErrorModal, setShowErrorModal ] = useState( false ); - const onEnableGroups = useCallback( - async ( retryFailedAudiences = [] ) => { - setIsSaving( true ); - - // If scope not granted, trigger scope error right away. These are - // typically handled automatically based on API responses, but - // this particular case has some special handling to improve UX. - if ( ! hasAnalytics4EditScope ) { - setValues( AUDIENCE_SEGMENTATION_SETUP_FORM, { - autoSubmit: true, - } ); - - setPermissionScopeError( { - code: ERROR_CODE_MISSING_REQUIRED_SCOPE, - message: __( - 'Additional permissions are required to create new audiences in Analytics.', - 'google-site-kit' - ), - data: { - status: 403, - scopes: [ EDIT_SCOPE ], - skipModal: true, - skipDefaultErrorNotifications: true, - redirectURL, - }, - } ); - - setShowErrorModal( true ); - setIsSaving( false ); - return; - } + const onEnableGroups = useCallback( async () => { + setIsSaving( true ); + // If scope not granted, trigger scope error right away. These are + // typically handled automatically based on API responses, but + // this particular case has some special handling to improve UX. + if ( ! hasAnalytics4EditScope ) { setValues( AUDIENCE_SEGMENTATION_SETUP_FORM, { - autoSubmit: false, + autoSubmit: true, } ); - const { error, failedSiteKitAudienceResourceNames } = - ( await enableAudienceGroup( retryFailedAudiences ) ) || {}; - - if ( error ) { - setApiErrors( [ error ] ); - setFailedAudiences( [] ); - } else if ( Array.isArray( failedSiteKitAudienceResourceNames ) ) { - setFailedAudiences( failedSiteKitAudienceResourceNames ); - setApiErrors( [] ); - } else { - setApiErrors( [] ); - setFailedAudiences( [] ); - } - - setShowErrorModal( - !! error || !! failedSiteKitAudienceResourceNames - ); - setIsSaving( false ); - }, - [ - enableAudienceGroup, - hasAnalytics4EditScope, - setPermissionScopeError, - redirectURL, - setValues, - ] - ); + setPermissionScopeError( { + code: ERROR_CODE_MISSING_REQUIRED_SCOPE, + message: __( + 'Additional permissions are required to create new audiences in Analytics.', + 'google-site-kit' + ), + data: { + status: 403, + scopes: [ EDIT_SCOPE ], + skipModal: true, + skipDefaultErrorNotifications: true, + redirectURL, + }, + } ); + + return; + } + + setValues( AUDIENCE_SEGMENTATION_SETUP_FORM, { + autoSubmit: false, + } ); + + const { error, failedSiteKitAudienceResourceNames } = + ( await enableAudienceGroup( failedAudiences ) ) || {}; + + if ( error ) { + setApiErrors( [ error ] ); + setFailedAudiences( [] ); + } else if ( Array.isArray( failedSiteKitAudienceResourceNames ) ) { + setFailedAudiences( failedSiteKitAudienceResourceNames ); + setApiErrors( [] ); + } else { + setApiErrors( [] ); + setFailedAudiences( [] ); + } + + setShowErrorModal( !! error || !! failedSiteKitAudienceResourceNames ); + setIsSaving( false ); + }, [ + hasAnalytics4EditScope, + setValues, + enableAudienceGroup, + failedAudiences, + setPermissionScopeError, + redirectURL, + ] ); // If the user ends up back on this component with the required scope granted, // and already submitted the form, trigger the submit again. @@ -215,26 +208,8 @@ function AudienceSegmentationSetupCTAWidget( { Widget, WidgetNull } ) { } }; - const permissionsError = useSelect( ( select ) => - select( CORE_USER ).getPermissionScopeError() - ); - - const connectURL = useSelect( ( select ) => - select( CORE_USER ).getConnectURL( { - additionalScopes: permissionsError?.data?.scopes, - redirectURL: - permissionsError?.data?.redirectURL || global.location.href, - } ) - ); - - const { navigateTo } = useDispatch( CORE_LOCATION ); const { clearPermissionScopeError } = useDispatch( CORE_USER ); - const handleOAuthError = useCallback( () => { - setIsSaving( true ); - navigateTo( connectURL ); - }, [ connectURL, navigateTo ] ); - const onCancel = useCallback( () => { clearPermissionScopeError(); setShowErrorModal( false ); @@ -277,16 +252,12 @@ function AudienceSegmentationSetupCTAWidget( { Widget, WidgetNull } ) { return null; } - if ( showErrorModal ) { + if ( showErrorModal || hasOAuthError ) { return ( onEnableGroups( failedAudiences ) - } + onRetry={ onEnableGroups } inProgress={ isSaving } onCancel={ hasOAuthError ? onCancel : () => setShowErrorModal( false ) From 650fde33a8acf96b18f97cf5bf55e081a7913e31 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Tue, 2 Jul 2024 23:28:53 +0530 Subject: [PATCH 083/105] Render AudienceErrorModal inside the main JSX. --- .../AudienceSegmentationSetupCTAWidget.js | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js index d6bc2381c56..9cd77505228 100644 --- a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js +++ b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js @@ -252,20 +252,6 @@ function AudienceSegmentationSetupCTAWidget( { Widget, WidgetNull } ) { return null; } - if ( showErrorModal || hasOAuthError ) { - return ( - setShowErrorModal( false ) - } - /> - ); - } - // TODO: We need to refactor this and the ConsentModeSetupCTAWidget to avoid this duplicate inlining of the widget context and area structure, // and to ensure only one of these setup CTAs is shown at a time. This will be handled in a subsequent issue. return ( @@ -369,6 +355,19 @@ function AudienceSegmentationSetupCTAWidget( { Widget, WidgetNull } ) { + { ( showErrorModal || hasOAuthError ) && ( + setShowErrorModal( false ) + } + /> + ) } ); } From e1f755bd45050b709c4d72bc9075e0fdb5d48a93 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Wed, 3 Jul 2024 11:43:11 +0530 Subject: [PATCH 084/105] Update publication ID validation to match base64 characters. --- assets/js/modules/reader-revenue-manager/utils/validation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/modules/reader-revenue-manager/utils/validation.js b/assets/js/modules/reader-revenue-manager/utils/validation.js index b26015bf27e..6693dc8bbdf 100644 --- a/assets/js/modules/reader-revenue-manager/utils/validation.js +++ b/assets/js/modules/reader-revenue-manager/utils/validation.js @@ -27,7 +27,7 @@ export function isValidPublicationID( publicationID ) { return ( typeof publicationID === 'string' && - /^[A-Za-z0-9_.-]+$/.test( publicationID ) + /^[A-Za-z0-9_-]+$/.test( publicationID ) ); } From 3fbfd99a58ced7cefabe0f127216fdfa358e45e3 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Wed, 3 Jul 2024 11:43:59 +0530 Subject: [PATCH 085/105] Fix tests for the updated validation. --- .../modules/reader-revenue-manager/utils/validation.test.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/assets/js/modules/reader-revenue-manager/utils/validation.test.js b/assets/js/modules/reader-revenue-manager/utils/validation.test.js index 1d419ebb678..77e22b848e9 100644 --- a/assets/js/modules/reader-revenue-manager/utils/validation.test.js +++ b/assets/js/modules/reader-revenue-manager/utils/validation.test.js @@ -24,7 +24,7 @@ import { isValidPublicationID, isURLUsingHTTPS } from './validation'; describe( 'utility functions', () => { describe( 'isValidPublicationID', () => { it( 'should return TRUE when a valid publication ID is passed', () => { - expect( isValidPublicationID( 'valid-publication_123.ID' ) ).toBe( + expect( isValidPublicationID( 'valid_publication-123' ) ).toBe( true ); } ); @@ -33,7 +33,8 @@ describe( 'utility functions', () => { [ 'false', false ], [ 'an integer', 12345 ], [ 'an empty string', '' ], - [ 'a string with invalid characters', 'invalid-publication!ID' ], + [ 'a string with invalid characters', 'invalid.publication!ID' ], + [ 'a string with periods', 'invalid.publication.ID' ], ] )( 'should return FALSE when %s is passed', ( _, publicationID ) => { expect( isValidPublicationID( publicationID ) ).toBe( false ); } ); From 9973a4a3f94f95c6c8924a852eb1e723ba4a9ec1 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Wed, 3 Jul 2024 11:58:38 +0530 Subject: [PATCH 086/105] Rename param to failedSiteKitAudienceSlugs. --- .../modules/analytics-4/datastore/audiences.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/assets/js/modules/analytics-4/datastore/audiences.js b/assets/js/modules/analytics-4/datastore/audiences.js index 622e6c9c482..f3474d3e047 100644 --- a/assets/js/modules/analytics-4/datastore/audiences.js +++ b/assets/js/modules/analytics-4/datastore/audiences.js @@ -180,12 +180,12 @@ const baseActions = { * If the `googlesitekit_post_type` custom dimension doesn't exist, creates it. * * @since 1.128.0 - * @since n.e.x.t Added `failedSiteKitAudienceResourceNames` parameter to retry failed Site Kit audience creation. + * @since n.e.x.t Added `failedSiteKitAudienceSlugs` parameter to retry failed Site Kit audience creation. * - * @param {Array} failedSiteKitAudienceResourceNames List of failed Site Kit audience resource names to retry. - * @return {Object} Object with `failedSiteKitAudienceResourceNames` and `error`. + * @param {Array} failedSiteKitAudienceSlugs List of failed Site Kit audience resource names to retry. + * @return {Object} Object with `failedSiteKitAudienceSlugs` and `error`. */ - *enableAudienceGroup( failedSiteKitAudienceResourceNames ) { + *enableAudienceGroup( failedSiteKitAudienceSlugs ) { const registry = yield commonActions.getRegistry(); const { dispatch, select, __experimentalResolveSelect } = registry; @@ -205,7 +205,7 @@ const baseActions = { const configuredAudiences = []; - if ( ! failedSiteKitAudienceResourceNames ) { + if ( ! failedSiteKitAudienceSlugs ) { if ( userAudiences.length > 0 ) { // If there are user audiences, filter and sort them by total users over the last 90 days, // and add the top two (MAX_INITIAL_AUDIENCES) which have users to the configured audiences. @@ -272,8 +272,8 @@ const baseActions = { } if ( configuredAudiences.length === 0 ) { - const audiencesToCreate = failedSiteKitAudienceResourceNames?.length - ? failedSiteKitAudienceResourceNames + const audiencesToCreate = failedSiteKitAudienceSlugs?.length + ? failedSiteKitAudienceSlugs : [ 'new-visitors', 'returning-visitors' ]; // If there are no configured audiences by this point, create the "new-visitors" and "returning-visitors" audiences, @@ -309,7 +309,7 @@ const baseActions = { if ( failedAudiencesToRetry.length > 0 ) { return { - failedSiteKitAudienceResourceNames: failedAudiencesToRetry, + failedSiteKitAudienceSlugs: failedAudiencesToRetry, }; } From a32b53c5bfd456c887ff27b19e5e195280b36e57 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Wed, 3 Jul 2024 15:00:34 +0530 Subject: [PATCH 087/105] Improve logic to handle successully create audiences - WIP. --- .../analytics-4/datastore/audiences.js | 60 ++++++++++--------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/assets/js/modules/analytics-4/datastore/audiences.js b/assets/js/modules/analytics-4/datastore/audiences.js index f3474d3e047..27683105783 100644 --- a/assets/js/modules/analytics-4/datastore/audiences.js +++ b/assets/js/modules/analytics-4/datastore/audiences.js @@ -182,10 +182,14 @@ const baseActions = { * @since 1.128.0 * @since n.e.x.t Added `failedSiteKitAudienceSlugs` parameter to retry failed Site Kit audience creation. * - * @param {Array} failedSiteKitAudienceSlugs List of failed Site Kit audience resource names to retry. - * @return {Object} Object with `failedSiteKitAudienceSlugs` and `error`. + * @param {Array} failedSiteKitAudienceSlugs List of failed Site Kit audience slugs to retry. + * @param {Array} createdSiteKitAudienceSlugs List of successfully created Site Kit audience slugs. + * @return {Object} Object with `failedSiteKitAudienceSlugs`, `createdSiteKitAudienceSlugs` and `error`. */ - *enableAudienceGroup( failedSiteKitAudienceSlugs ) { + *enableAudienceGroup( + failedSiteKitAudienceSlugs, + createdSiteKitAudienceSlugs = [] + ) { const registry = yield commonActions.getRegistry(); const { dispatch, select, __experimentalResolveSelect } = registry; @@ -278,41 +282,39 @@ const baseActions = { // If there are no configured audiences by this point, create the "new-visitors" and "returning-visitors" audiences, // and add them to the configured audiences. - const [ newVisitorsResult, returningVisitorsResult ] = - yield commonActions.await( - Promise.all( - audiencesToCreate.map( ( audienceSlug ) => { - return dispatch( - MODULES_ANALYTICS_4 - ).createAudience( - SITE_KIT_AUDIENCE_DEFINITIONS[ audienceSlug ] - ); - } ) - ) - ); + const audienceCreationResults = yield commonActions.await( + Promise.all( + audiencesToCreate.map( ( audienceSlug ) => { + return dispatch( MODULES_ANALYTICS_4 ).createAudience( + SITE_KIT_AUDIENCE_DEFINITIONS[ audienceSlug ] + ); + } ) + ) + ); const failedAudiencesToRetry = []; - - if ( newVisitorsResult.error ) { - failedAudiencesToRetry.push( 'new-visitors' ); - } else { - configuredAudiences.push( newVisitorsResult.response.name ); - } - - if ( returningVisitorsResult.error ) { - failedAudiencesToRetry.push( 'returning-visitors' ); - } else { - configuredAudiences.push( - returningVisitorsResult.response.name - ); - } + // const createdAudiences = []; + const createdAudiences = [ ...createdSiteKitAudienceSlugs ]; + + audienceCreationResults.forEach( ( result, index ) => { + const audienceSlug = audiencesToCreate[ index ]; + if ( result.error ) { + failedAudiencesToRetry.push( audienceSlug ); + } else { + createdAudiences.push( result.response.name ); + } + } ); if ( failedAudiencesToRetry.length > 0 ) { return { failedSiteKitAudienceSlugs: failedAudiencesToRetry, + createdSiteKitAudienceSlugs: createdAudiences, }; } + // Add created audiences to configured audiences. + configuredAudiences.push( ...createdAudiences ); + // Resync available audiences to ensure the newly created audiences are available. yield commonActions.await( dispatch( MODULES_ANALYTICS_4 ).syncAvailableAudiences() From c802cadfae8443b1246af8639ce2601a18089c0c Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Wed, 3 Jul 2024 18:24:12 +0530 Subject: [PATCH 088/105] Improve isURLUsingHTTPS logic. --- .../js/modules/reader-revenue-manager/utils/validation.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/assets/js/modules/reader-revenue-manager/utils/validation.js b/assets/js/modules/reader-revenue-manager/utils/validation.js index 6693dc8bbdf..507a2693c06 100644 --- a/assets/js/modules/reader-revenue-manager/utils/validation.js +++ b/assets/js/modules/reader-revenue-manager/utils/validation.js @@ -46,11 +46,7 @@ export const isURLUsingHTTPS = ( url ) => { } const parsedURL = new URL( url ); - if ( parsedURL.protocol !== 'https:' ) { - throw new TypeError( `Invalid URL: ${ url }` ); - } - - return true; + return parsedURL.protocol === 'https:'; } catch ( error ) { global.console.warn( 'Invalid URL:', error ); return false; From 0be6bd6b39490525ab4a2c9346d6550c26960863 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Wed, 3 Jul 2024 18:24:34 +0530 Subject: [PATCH 089/105] Improve isURLUsingHTTPS tests. --- .../utils/validation.test.js | 35 ++++++++----------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/assets/js/modules/reader-revenue-manager/utils/validation.test.js b/assets/js/modules/reader-revenue-manager/utils/validation.test.js index 77e22b848e9..b2000b896a8 100644 --- a/assets/js/modules/reader-revenue-manager/utils/validation.test.js +++ b/assets/js/modules/reader-revenue-manager/utils/validation.test.js @@ -41,36 +41,29 @@ describe( 'utility functions', () => { } ); describe( 'isURLUsingHTTPS', () => { - beforeAll( () => { - jest.spyOn( console, 'warn' ).mockImplementation( () => {} ); - } ); - - afterAll( () => { - // eslint-disable-next-line no-console - console.warn.mockRestore(); - } ); - - afterEach( () => { - // eslint-disable-next-line no-console - console.warn.mockClear(); - } ); - it( 'should return TRUE when a URL with HTTPS is passed', () => { expect( isURLUsingHTTPS( 'https://example.com' ) ).toBe( true ); - // eslint-disable-next-line no-console - expect( console.warn ).not.toHaveBeenCalled(); + expect( console ).not.toHaveWarned(); } ); it.each( [ - [ 'an HTTP URL', 'http://example.com' ], - [ 'an invalid URL', 'htp://example.com' ], [ 'a string without protocol', 'example.com' ], [ 'an empty string', '' ], [ 'false', false ], - ] )( 'should return FALSE when %s is passed', ( _, url ) => { + ] )( 'should return FALSE and warn when %s is passed', ( _, url ) => { expect( isURLUsingHTTPS( url ) ).toBe( false ); - // eslint-disable-next-line no-console - expect( console.warn ).toHaveBeenCalled(); + expect( console ).toHaveWarned(); } ); + + it.each( [ + [ 'an HTTP URL', 'http://example.com' ], + [ 'an invalid URL', 'htp://example.com' ], + ] )( + 'should return FALSE but not warn when %s is passed', + ( _, url ) => { + expect( isURLUsingHTTPS( url ) ).toBe( false ); + expect( console ).not.toHaveWarned(); + } + ); } ); } ); From 180e164bbf526db5a907beceb0f949d1df26e301 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Wed, 3 Jul 2024 18:26:49 +0530 Subject: [PATCH 090/105] Remove datastore-forms and modules-data from RRM assets. --- includes/Modules/Reader_Revenue_Manager.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/includes/Modules/Reader_Revenue_Manager.php b/includes/Modules/Reader_Revenue_Manager.php index e44e05bee03..c19d98202f5 100644 --- a/includes/Modules/Reader_Revenue_Manager.php +++ b/includes/Modules/Reader_Revenue_Manager.php @@ -94,9 +94,7 @@ protected function setup_assets() { 'googlesitekit-modules', 'googlesitekit-datastore-site', 'googlesitekit-datastore-user', - 'googlesitekit-datastore-forms', 'googlesitekit-components', - 'googlesitekit-modules-data', ), ) ), From 2daea42735d79af89f251a57be4d4c1a4cbc3213 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Wed, 3 Jul 2024 19:29:52 +0530 Subject: [PATCH 091/105] Dispatch setConfiguredAudiences appropriately. Fix param name. --- .../analytics-4/datastore/audiences.js | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/assets/js/modules/analytics-4/datastore/audiences.js b/assets/js/modules/analytics-4/datastore/audiences.js index 27683105783..5c9f44ebcb7 100644 --- a/assets/js/modules/analytics-4/datastore/audiences.js +++ b/assets/js/modules/analytics-4/datastore/audiences.js @@ -182,14 +182,10 @@ const baseActions = { * @since 1.128.0 * @since n.e.x.t Added `failedSiteKitAudienceSlugs` parameter to retry failed Site Kit audience creation. * - * @param {Array} failedSiteKitAudienceSlugs List of failed Site Kit audience slugs to retry. - * @param {Array} createdSiteKitAudienceSlugs List of successfully created Site Kit audience slugs. + * @param {Array} failedSiteKitAudienceSlugs List of failed Site Kit audience slugs to retry. * @return {Object} Object with `failedSiteKitAudienceSlugs`, `createdSiteKitAudienceSlugs` and `error`. */ - *enableAudienceGroup( - failedSiteKitAudienceSlugs, - createdSiteKitAudienceSlugs = [] - ) { + *enableAudienceGroup( failedSiteKitAudienceSlugs ) { const registry = yield commonActions.getRegistry(); const { dispatch, select, __experimentalResolveSelect } = registry; @@ -293,28 +289,32 @@ const baseActions = { ); const failedAudiencesToRetry = []; - // const createdAudiences = []; - const createdAudiences = [ ...createdSiteKitAudienceSlugs ]; audienceCreationResults.forEach( ( result, index ) => { const audienceSlug = audiencesToCreate[ index ]; if ( result.error ) { failedAudiencesToRetry.push( audienceSlug ); } else { - createdAudiences.push( result.response.name ); + configuredAudiences.push( result.response.name ); } } ); + const existingConfiguredAudiences = + select( MODULES_ANALYTICS_4 ).getConfiguredAudiences() || []; + + configuredAudiences.push( ...existingConfiguredAudiences ); + + // If the audiences were created successfully, set them as configured audiences. + dispatch( MODULES_ANALYTICS_4 ).setConfiguredAudiences( + configuredAudiences + ); + if ( failedAudiencesToRetry.length > 0 ) { return { failedSiteKitAudienceSlugs: failedAudiencesToRetry, - createdSiteKitAudienceSlugs: createdAudiences, }; } - // Add created audiences to configured audiences. - configuredAudiences.push( ...createdAudiences ); - // Resync available audiences to ensure the newly created audiences are available. yield commonActions.await( dispatch( MODULES_ANALYTICS_4 ).syncAvailableAudiences() From 65ec299a9cecfeaeb188e0509e7cc8b3b0ad27f9 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Wed, 3 Jul 2024 19:31:08 +0530 Subject: [PATCH 092/105] Fix and improvements. --- .../AudienceSegmentationSetupCTAWidget.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js index 9cd77505228..0126ef7d80e 100644 --- a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js +++ b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js @@ -149,21 +149,21 @@ function AudienceSegmentationSetupCTAWidget( { Widget, WidgetNull } ) { autoSubmit: false, } ); - const { error, failedSiteKitAudienceResourceNames } = + const { error, failedSiteKitAudienceSlugs } = ( await enableAudienceGroup( failedAudiences ) ) || {}; if ( error ) { setApiErrors( [ error ] ); setFailedAudiences( [] ); - } else if ( Array.isArray( failedSiteKitAudienceResourceNames ) ) { - setFailedAudiences( failedSiteKitAudienceResourceNames ); + } else if ( Array.isArray( failedSiteKitAudienceSlugs ) ) { + setFailedAudiences( failedSiteKitAudienceSlugs ); setApiErrors( [] ); } else { setApiErrors( [] ); setFailedAudiences( [] ); } - setShowErrorModal( !! error || !! failedSiteKitAudienceResourceNames ); + setShowErrorModal( !! error || !! failedSiteKitAudienceSlugs ); setIsSaving( false ); }, [ hasAnalytics4EditScope, @@ -213,7 +213,10 @@ function AudienceSegmentationSetupCTAWidget( { Widget, WidgetNull } ) { const onCancel = useCallback( () => { clearPermissionScopeError(); setShowErrorModal( false ); - }, [ clearPermissionScopeError ] ); + setValues( AUDIENCE_SEGMENTATION_SETUP_FORM, { + autoSubmit: false, + } ); + }, [ clearPermissionScopeError, setValues ] ); const setupErrorCode = useSelect( ( select ) => select( CORE_SITE ).getSetupErrorCode() @@ -245,7 +248,7 @@ function AudienceSegmentationSetupCTAWidget( { Widget, WidgetNull } ) { if ( configuredAudiences === undefined || - configuredAudiences?.length || + ( configuredAudiences?.length && ! failedAudiences.length ) || ! analyticsIsDataAvailableOnLoad || isDismissed ) { From 0be47cd5c04565c32b6fee44fd7e3f73b145d5af Mon Sep 17 00:00:00 2001 From: Aleksej Date: Wed, 3 Jul 2024 17:19:29 +0200 Subject: [PATCH 093/105] Add learn more link. --- .../components/settings/SettingsForm.js | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/assets/js/modules/analytics-4/components/settings/SettingsForm.js b/assets/js/modules/analytics-4/components/settings/SettingsForm.js index 53c6dcfc6c5..5645cf494b4 100644 --- a/assets/js/modules/analytics-4/components/settings/SettingsForm.js +++ b/assets/js/modules/analytics-4/components/settings/SettingsForm.js @@ -24,7 +24,7 @@ import PropTypes from 'prop-types'; /** * WordPress dependencies */ -import { Fragment } from '@wordpress/element'; +import { Fragment, createInterpolateElement } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; /** @@ -39,12 +39,20 @@ import EntityOwnershipChangeNotice from '../../../../components/settings/EntityO import { isValidAccountID } from '../../utils/validation'; import ConversionTrackingToggle from '../../../../components/conversion-tracking/ConversionTrackingToggle'; import { useFeature } from '../../../../hooks/useFeature'; +import { CORE_SITE } from '../../../../googlesitekit/datastore/site/constants'; +import Link from '../../../../components/Link'; export default function SettingsForm( { hasModuleAccess } ) { const accountID = useSelect( ( select ) => select( MODULES_ANALYTICS_4 ).getAccountID() ); + const conversionTrackingDocumentationURL = useSelect( ( select ) => + select( CORE_SITE ).getDocumentationLinkURL( + 'enhanced-conversion-tracking' + ) + ); + const iceEnabled = useFeature( 'conversionInfra' ); return ( @@ -59,9 +67,23 @@ export default function SettingsForm( { hasModuleAccess } ) { { iceEnabled && ( - { __( - 'Conversion tracking is used for tracking additional conversion-related events via Analytics', - 'google-site-kit' + { createInterpolateElement( + __( + 'Conversion tracking is used for tracking additional conversion-related events via Analytics.
Learn more', + 'google-site-kit' + ), + { + a: ( + + ), + } ) } ) } From 980e0e57fc25b4e908a516a5bb2a5604ba24cd3d Mon Sep 17 00:00:00 2001 From: Aleksej Date: Wed, 3 Jul 2024 17:19:43 +0200 Subject: [PATCH 094/105] Update VRT. --- ...gs_SettingsEdit_ICE_0_document_0_small.png | Bin 53443 -> 54759 bytes ...s_SettingsEdit_ICE_0_document_1_medium.png | Bin 52012 -> 53157 bytes ...gs_SettingsEdit_ICE_0_document_2_large.png | Bin 51426 -> 52590 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/backstop/reference/google-site-kit_Modules_Analytics4_Settings_SettingsEdit_ICE_0_document_0_small.png b/tests/backstop/reference/google-site-kit_Modules_Analytics4_Settings_SettingsEdit_ICE_0_document_0_small.png index b38582a4d1a3ad544ccca3c4e2e8516f88e88d5b..c934a36282c1e43e067399f766e7bb28cdb7b657 100644 GIT binary patch delta 2902 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 FKLGi{kW&Bv diff --git a/tests/backstop/reference/google-site-kit_Modules_Analytics4_Settings_SettingsEdit_ICE_0_document_2_large.png b/tests/backstop/reference/google-site-kit_Modules_Analytics4_Settings_SettingsEdit_ICE_0_document_2_large.png index 59690408b8d9f1383639af052065491ea7e59921..85ce602eff46aa54bb0265b0e3a1834886bfb846 100644 GIT binary patch delta 18926 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 OT1`nyvF6ujzx^))juM0b delta 17753 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 From c4cdd12672c8607fe16130cc0d6d064c218f5839 Mon Sep 17 00:00:00 2001 From: Ben Bowler Date: Wed, 3 Jul 2024 16:30:12 +0100 Subject: [PATCH 095/105] Update spacing between Audience widget area and other widget areas. --- .../sass/components/analytics-4/_index.scss | 1 + .../_googlesitekit-audience-widget-area.scss | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 assets/sass/components/analytics-4/audience-segmentation/_googlesitekit-audience-widget-area.scss diff --git a/assets/sass/components/analytics-4/_index.scss b/assets/sass/components/analytics-4/_index.scss index fd728b4c73d..c8e3d7303be 100644 --- a/assets/sass/components/analytics-4/_index.scss +++ b/assets/sass/components/analytics-4/_index.scss @@ -28,6 +28,7 @@ @import "audience-segmentation/googlesitekit-audience-segmentation-info-notice"; @import "audience-segmentation/googlesitekit-audience-segmentation-error"; @import "audience-segmentation/googlesitekit-audience-tiles"; +@import "audience-segmentation/googlesitekit-audience-widget-area"; @import "audience-segmentation/googlesitekit-partial-data-badge"; @import "audience-segmentation/googlesitekit-partial-data-notice"; @import "audience-segmentation/googlesitekit-audience-selection-panel"; diff --git a/assets/sass/components/analytics-4/audience-segmentation/_googlesitekit-audience-widget-area.scss b/assets/sass/components/analytics-4/audience-segmentation/_googlesitekit-audience-widget-area.scss new file mode 100644 index 00000000000..96344db5739 --- /dev/null +++ b/assets/sass/components/analytics-4/audience-segmentation/_googlesitekit-audience-widget-area.scss @@ -0,0 +1,27 @@ +/** + * Audience Widget Area styles. + * + * Site Kit by Google, Copyright 2024 Google LLC + * + * Licensed 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 + * + * https://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. + */ + +.googlesitekit-plugin { + .mdc-layout-grid.googlesitekit-widget-area--mainDashboardTrafficAudienceSegmentation { + padding-top: 3px; + + @media (min-width: $width-desktop + 1 + px) { + padding-top: 8px; + } + } +} From 4734c2e77bef9c10ad0210fdb0484584f990efa1 Mon Sep 17 00:00:00 2001 From: Ben Bowler Date: Wed, 3 Jul 2024 16:45:20 +0100 Subject: [PATCH 096/105] Update Audience Widget area, New badge left margin. --- assets/sass/widgets/_widget-area.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/assets/sass/widgets/_widget-area.scss b/assets/sass/widgets/_widget-area.scss index d480993660b..846e7f34270 100644 --- a/assets/sass/widgets/_widget-area.scss +++ b/assets/sass/widgets/_widget-area.scss @@ -48,6 +48,10 @@ letter-spacing: $ls-s; line-height: $lh-body-md; margin: 0; + + .googlesitekit-new-badge { + margin-left: 14px; + } } .googlesitekit-widget-area-header__details { From bac7eab9007fea0ed3e50ea17fb9cbc5dd2dbdbb Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Wed, 3 Jul 2024 22:03:11 +0530 Subject: [PATCH 097/105] Add setSetupErrorCode action. --- .../js/googlesitekit/datastore/site/info.js | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/assets/js/googlesitekit/datastore/site/info.js b/assets/js/googlesitekit/datastore/site/info.js index e1f85d0f8a7..75240f3bf79 100644 --- a/assets/js/googlesitekit/datastore/site/info.js +++ b/assets/js/googlesitekit/datastore/site/info.js @@ -47,6 +47,7 @@ const RECEIVE_SITE_INFO = 'RECEIVE_SITE_INFO'; const RECEIVE_PERMALINK_PARAM = 'RECEIVE_PERMALINK_PARAM'; const SET_SITE_KIT_AUTO_UPDATES_ENABLED = 'SET_SITE_KIT_AUTO_UPDATES_ENABLED'; const SET_KEY_METRICS_SETUP_COMPLETED_BY = 'SET_KEY_METRICS_SETUP_COMPLETED_BY'; +const SET_SETUP_ERROR_CODE = 'SET_SETUP_ERROR_CODE'; export const initialState = { siteInfo: undefined, @@ -126,6 +127,27 @@ export const actions = { type: SET_KEY_METRICS_SETUP_COMPLETED_BY, }; }, + + /** + * Sets `setupErrorCode` value. + * + * @since n.e.x.t + * + * @param {string|null} setupErrorCode Error code from setup, or `null` if no error. + * @return {Object} Redux-style action. + */ + setSetupErrorCode( setupErrorCode ) { + // setupErrorCode can be a string or null. + invariant( + typeof setupErrorCode === 'string' || setupErrorCode === null, + 'setupErrorCode must be a string or null.' + ); + + return { + payload: { setupErrorCode }, + type: SET_SETUP_ERROR_CODE, + }; + }, }; export const controls = {}; @@ -228,6 +250,16 @@ export const reducer = ( state, { payload, type } ) => { }, }; + case SET_SETUP_ERROR_CODE: + const { setupErrorCode } = payload; + return { + ...state, + siteInfo: { + ...state.siteInfo, + setupErrorCode, + }, + }; + default: { return state; } From 77fff952e29e7d2301daed9d8916601c5221532a Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Wed, 3 Jul 2024 22:03:26 +0530 Subject: [PATCH 098/105] Add setSetupErrorCode action tests. --- .../googlesitekit/datastore/site/info.test.js | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/assets/js/googlesitekit/datastore/site/info.test.js b/assets/js/googlesitekit/datastore/site/info.test.js index 7bcf38d113c..32d0f16f593 100644 --- a/assets/js/googlesitekit/datastore/site/info.test.js +++ b/assets/js/googlesitekit/datastore/site/info.test.js @@ -190,6 +190,44 @@ describe( 'core/site site info', () => { ); } ); } ); + + describe( 'setSetupErrorCode', () => { + it( 'sets the `setupErrorCode` property', () => { + registry + .dispatch( CORE_SITE ) + .setSetupErrorCode( 'error_code' ); + + expect( store.getState().siteInfo.setupErrorCode ).toBe( + 'error_code' + ); + } ); + + it( 'requires a string or null argument', () => { + expect( () => { + registry.dispatch( CORE_SITE ).setSetupErrorCode(); + } ).toThrow( 'setupErrorCode must be a string or null.' ); + + expect( () => { + registry + .dispatch( CORE_SITE ) + .setSetupErrorCode( undefined ); + } ).toThrow( 'setupErrorCode must be a string or null.' ); + + expect( () => { + registry.dispatch( CORE_SITE ).setSetupErrorCode( true ); + } ).toThrow( 'setupErrorCode must be a string or null.' ); + + expect( () => { + registry.dispatch( CORE_SITE ).setSetupErrorCode( 1 ); + } ).toThrow( 'setupErrorCode must be a string or null.' ); + + expect( () => { + registry.dispatch( CORE_SITE ).setSetupErrorCode( null ); + + registry.dispatch( CORE_SITE ).setSetupErrorCode( 'error' ); + } ).not.toThrow( 'setupErrorCode must be a string or null.' ); + } ); + } ); } ); describe( 'selectors', () => { From 6adc446b922164e2d97d0e516ab15a35b2f9586f Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Wed, 3 Jul 2024 22:04:05 +0530 Subject: [PATCH 099/105] Clear OAuth error onCancel. --- .../dashboard/AudienceSegmentationSetupCTAWidget.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js index 0126ef7d80e..dcca5c0ecc0 100644 --- a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js +++ b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js @@ -209,14 +209,16 @@ function AudienceSegmentationSetupCTAWidget( { Widget, WidgetNull } ) { }; const { clearPermissionScopeError } = useDispatch( CORE_USER ); + const { setSetupErrorCode } = useDispatch( CORE_SITE ); const onCancel = useCallback( () => { - clearPermissionScopeError(); - setShowErrorModal( false ); setValues( AUDIENCE_SEGMENTATION_SETUP_FORM, { autoSubmit: false, } ); - }, [ clearPermissionScopeError, setValues ] ); + clearPermissionScopeError(); + setSetupErrorCode( null ); + setShowErrorModal( false ); + }, [ clearPermissionScopeError, setSetupErrorCode, setValues ] ); const setupErrorCode = useSelect( ( select ) => select( CORE_SITE ).getSetupErrorCode() From d24032ca8100985c201d4aae02af2ec073e728e8 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Wed, 3 Jul 2024 22:06:35 +0530 Subject: [PATCH 100/105] Update the logic to look up for the newly available audiences and sync with the configured audience. --- .../analytics-4/datastore/audiences.js | 46 ++++++++++++++----- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/assets/js/modules/analytics-4/datastore/audiences.js b/assets/js/modules/analytics-4/datastore/audiences.js index 5c9f44ebcb7..b3a6925c321 100644 --- a/assets/js/modules/analytics-4/datastore/audiences.js +++ b/assets/js/modules/analytics-4/datastore/audiences.js @@ -272,9 +272,13 @@ const baseActions = { } if ( configuredAudiences.length === 0 ) { + const requiredAudienceSlugs = [ + 'new-visitors', + 'returning-visitors', + ]; const audiencesToCreate = failedSiteKitAudienceSlugs?.length ? failedSiteKitAudienceSlugs - : [ 'new-visitors', 'returning-visitors' ]; + : requiredAudienceSlugs; // If there are no configured audiences by this point, create the "new-visitors" and "returning-visitors" audiences, // and add them to the configured audiences. @@ -299,14 +303,10 @@ const baseActions = { } } ); - const existingConfiguredAudiences = - select( MODULES_ANALYTICS_4 ).getConfiguredAudiences() || []; - - configuredAudiences.push( ...existingConfiguredAudiences ); - - // If the audiences were created successfully, set them as configured audiences. - dispatch( MODULES_ANALYTICS_4 ).setConfiguredAudiences( - configuredAudiences + yield commonActions.await( + __experimentalResolveSelect( + MODULES_ANALYTICS_4 + ).getAudienceSettings() ); if ( failedAudiencesToRetry.length > 0 ) { @@ -315,10 +315,32 @@ const baseActions = { }; } + const existingConfiguredAudiences = + select( MODULES_ANALYTICS_4 ).getConfiguredAudiences() || []; + + configuredAudiences.push( ...existingConfiguredAudiences ); + // Resync available audiences to ensure the newly created audiences are available. - yield commonActions.await( - dispatch( MODULES_ANALYTICS_4 ).syncAvailableAudiences() - ); + const { response: newAvailableAudiences } = + yield commonActions.await( + dispatch( MODULES_ANALYTICS_4 ).syncAvailableAudiences() + ); + + // Find the audience in the newly available audiences that matches the required slug. + // If a matching audience is found and it's not already in the configured audiences, add it. + // This is to ensure if one audience was created successfully but the other failed, + // the successful one is still added on the retry. + requiredAudienceSlugs.forEach( ( slug ) => { + const matchingAudience = newAvailableAudiences.find( + ( item ) => item.audienceSlug === slug + ); + if ( + matchingAudience && + ! configuredAudiences.includes( matchingAudience.name ) + ) { + configuredAudiences.push( matchingAudience.name ); + } + } ); } // Create custom dimension if it doesn't exist. From 1f32174de1232dd2ef21f4cff7c8aae61ed21891 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Thu, 4 Jul 2024 11:14:49 +0530 Subject: [PATCH 101/105] Remove unnecessary condition. --- .../dashboard/AudienceSegmentationSetupCTAWidget.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js index dcca5c0ecc0..5cf5c56b943 100644 --- a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js +++ b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.js @@ -250,7 +250,7 @@ function AudienceSegmentationSetupCTAWidget( { Widget, WidgetNull } ) { if ( configuredAudiences === undefined || - ( configuredAudiences?.length && ! failedAudiences.length ) || + configuredAudiences?.length || ! analyticsIsDataAvailableOnLoad || isDismissed ) { From 04e6bb63338c37beabbc7884c689b7acd38ba4ea Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Thu, 4 Jul 2024 15:07:02 +0530 Subject: [PATCH 102/105] Fix tests. --- assets/js/modules/analytics-4/datastore/audiences.test.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/assets/js/modules/analytics-4/datastore/audiences.test.js b/assets/js/modules/analytics-4/datastore/audiences.test.js index 5b5cf1ece39..d268284060a 100644 --- a/assets/js/modules/analytics-4/datastore/audiences.test.js +++ b/assets/js/modules/analytics-4/datastore/audiences.test.js @@ -1047,8 +1047,7 @@ describe( 'modules/analytics-4 audiences', () => { // Verifying the result contains the expected failed audiences. expect( result ).toEqual( { - failedSiteKitAudienceResourceNames: - expectedFailedAudiences, + failedSiteKitAudienceSlugs: expectedFailedAudiences, } ); // Ensuring no configured audiences are set when all creation attempts fail. @@ -1105,8 +1104,7 @@ describe( 'modules/analytics-4 audiences', () => { // Verifying the result contains the expected failed audiences. expect( result ).toEqual( { - failedSiteKitAudienceResourceNames: - expectedFailedAudiences, + failedSiteKitAudienceSlugs: expectedFailedAudiences, } ); // Ensure no configured audiences are set when one creation attempt fails. @@ -1131,7 +1129,7 @@ describe( 'modules/analytics-4 audiences', () => { expect( console ).toHaveErrored(); } ); - it( 'should create provided "failedSiteKitAudienceResourceNames" correctly', async () => { + it( 'should create provided "failedSiteKitAudienceSlugs" correctly', async () => { const failedAudiencesToRetry = [ 'new-visitors', 'returning-visitors', From 40bc8042e7e4cc5ded74b5cd6bb8cf0339940fb5 Mon Sep 17 00:00:00 2001 From: Hussain Thajutheen Date: Thu, 4 Jul 2024 15:18:16 +0530 Subject: [PATCH 103/105] Increase timeout. --- .../dashboard/AudienceSegmentationSetupCTAWidget.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.test.js b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.test.js index 82583615fbd..a8ab4b53229 100644 --- a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.test.js +++ b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSegmentationSetupCTAWidget.test.js @@ -591,7 +591,7 @@ describe( 'AudienceSegmentationSetupCTAWidget', () => { ).toBeInTheDocument(); } ); - await act( waitForDefaultTimeouts ); + await act( () => waitForTimeouts( 30 ) ); } ); describe( 'AudienceErrorModal', () => { From 8a2dbf52c153c7e62e1d399a8a657ca039a17b45 Mon Sep 17 00:00:00 2001 From: Tom Rees-Herdman Date: Thu, 4 Jul 2024 18:46:16 +0100 Subject: [PATCH 104/105] Replace __experimentalResolveSelect with resolveSelect. --- assets/js/modules/analytics-4/datastore/audiences.js | 4 +--- assets/js/modules/reader-revenue-manager/index.js | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/assets/js/modules/analytics-4/datastore/audiences.js b/assets/js/modules/analytics-4/datastore/audiences.js index c54bc6dc837..4101d198767 100644 --- a/assets/js/modules/analytics-4/datastore/audiences.js +++ b/assets/js/modules/analytics-4/datastore/audiences.js @@ -304,9 +304,7 @@ const baseActions = { } ); yield commonActions.await( - __experimentalResolveSelect( - MODULES_ANALYTICS_4 - ).getAudienceSettings() + resolveSelect( MODULES_ANALYTICS_4 ).getAudienceSettings() ); if ( failedAudiencesToRetry.length > 0 ) { diff --git a/assets/js/modules/reader-revenue-manager/index.js b/assets/js/modules/reader-revenue-manager/index.js index 8f95b53edac..7a1f3d694af 100644 --- a/assets/js/modules/reader-revenue-manager/index.js +++ b/assets/js/modules/reader-revenue-manager/index.js @@ -57,9 +57,7 @@ export const registerModule = isRrmModuleEnabled( ( modules ) => { ], checkRequirements: async ( registry ) => { // Ensure the site info is resolved to get the home URL. - await registry - .__experimentalResolveSelect( CORE_SITE ) - .getSiteInfo(); + await registry.resolveSelect( CORE_SITE ).getSiteInfo(); const homeURL = registry.select( CORE_SITE ).getHomeURL(); if ( isURLUsingHTTPS( homeURL ) ) { From 56ec1a7ebde83578dc4866e4ac3874eb1abc72f7 Mon Sep 17 00:00:00 2001 From: "Matthew Riley MacPherson (tofumatt)" Date: Thu, 4 Jul 2024 19:03:20 +0100 Subject: [PATCH 105/105] Fix `googlesitekit-data` import in Error Notice. --- .../dashboard/AudienceSelectionPanel/ErrorNotice.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSelectionPanel/ErrorNotice.js b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSelectionPanel/ErrorNotice.js index b2f472a2ebd..ef655decd9c 100644 --- a/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSelectionPanel/ErrorNotice.js +++ b/assets/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceSelectionPanel/ErrorNotice.js @@ -25,7 +25,7 @@ import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import Data from 'googlesitekit-data'; +import { useSelect, useDispatch } from 'googlesitekit-data'; import { CORE_SITE } from '../../../../../../googlesitekit/datastore/site/constants'; import { MODULES_ANALYTICS_4 } from '../../../../datastore/constants'; import { isInsufficientPermissionsError } from '../../../../../../util/errors'; @@ -34,8 +34,6 @@ import ReportErrorActions from '../../../../../../components/ReportErrorActions' import RequestAccessButton from './RequestAccessButton'; import RetryButton from './RetryButton'; -const { useSelect, useDispatch } = Data; - export default function ErrorNotice() { const syncAvailableAudiencesError = useSelect( ( select ) => select( MODULES_ANALYTICS_4 ).getErrorForAction(