diff --git a/assets/js/components/settings/SettingsAdmin.js b/assets/js/components/settings/SettingsAdmin.js index 64f1fc7456a..e50f086faa7 100644 --- a/assets/js/components/settings/SettingsAdmin.js +++ b/assets/js/components/settings/SettingsAdmin.js @@ -36,11 +36,12 @@ import OptIn from '../OptIn'; import ResetButton from '../ResetButton'; import UserInputPreview from '../user-input/UserInputPreview'; import { USER_INPUT_QUESTIONS_LIST } from '../user-input/util/constants'; -import UserInputSettings from '../notifications/UserInputSettings'; import { useFeature } from '../../hooks/useFeature'; import { trackEvent } from '../../util'; import SettingsPlugin from './SettingsPlugin'; import useViewContext from '../../hooks/useViewContext'; +import SettingsKeyMetrics from './SettingsKeyMetrics'; +import Link from '../Link'; const { useSelect, useDispatch } = Data; export default function SettingsAdmin() { @@ -80,29 +81,53 @@ export default function SettingsAdmin() { { userInputEnabled && ( - { isUserInputCompleted && ( - -
- - - -
-
- ) } - - { isUserInputCompleted === false && ( - - ) } + +
+ + + { isUserInputCompleted && ( + + + + + + ) } + { isUserInputCompleted === false && ( + + +

+ + { __( + 'Answer 3 quick questions to help us show the most relevant data for your site', + 'google-site-kit' + ) } + +

+ + { __( + 'Personalize your metrics', + 'google-site-kit' + ) } + +
+
+ ) } +
+
+
) } diff --git a/assets/js/components/settings/SettingsKeyMetrics.js b/assets/js/components/settings/SettingsKeyMetrics.js new file mode 100644 index 00000000000..5aade3bfdc2 --- /dev/null +++ b/assets/js/components/settings/SettingsKeyMetrics.js @@ -0,0 +1,80 @@ +/** + * SettingsKeyMetrics component. + * + * Site Kit by Google, Copyright 2023 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 { useCallback } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import { Switch } from 'googlesitekit-components'; +import Data from 'googlesitekit-data'; +import { CORE_USER } from '../../googlesitekit/datastore/user/constants'; +import { Cell, Grid, Row } from '../../material-components'; + +const { useSelect, useDispatch } = Data; + +export default function SettingsKeyMetrics() { + const keyMetricsWidgetHidden = useSelect( ( select ) => + select( CORE_USER ).isKeyMetricsWidgetHidden() + ); + + const keyMetrics = useSelect( ( select ) => + select( CORE_USER ).getKeyMetrics() + ); + + const { setKeyMetricsSetting, saveKeyMetricsSettings } = + useDispatch( CORE_USER ); + + const handleKeyMetricsToggle = useCallback( async () => { + await setKeyMetricsSetting( + 'isWidgetHidden', + ! keyMetricsWidgetHidden + ); + await saveKeyMetricsSettings(); + }, [ + keyMetricsWidgetHidden, + saveKeyMetricsSettings, + setKeyMetricsSetting, + ] ); + + if ( ! keyMetricsWidgetHidden === undefined || ! keyMetrics?.length ) { + return null; + } + + return ( + + + + + + + + ); +} diff --git a/assets/js/googlesitekit/datastore/user/constants.js b/assets/js/googlesitekit/datastore/user/constants.js index adf6ea42e3a..2b25bd23784 100644 --- a/assets/js/googlesitekit/datastore/user/constants.js +++ b/assets/js/googlesitekit/datastore/user/constants.js @@ -41,3 +41,18 @@ export const PERMISSION_MANAGE_MODULE_SHARING_OPTIONS = export const PERMISSION_DELEGATE_MODULE_SHARING_MANAGEMENT = 'googlesitekit_delegate_module_sharing_management'; export const PERMISSION_UPDATE_PLUGINS = 'googlesitekit_update_plugins'; + +// Key Metrics Widgets +export const KM_ANALYTICS_ENGAGED_TRAFFIC_SOURCE = + 'kmAnalyticsEngagedTrafficSource'; +export const KM_ANALYTICS_LOYAL_VISITORS = 'kmAnalyticsLoyalVisitors'; +export const KM_ANALYTICS_NEW_VISITORS = 'kmAnalyticsNewVisitors'; +export const KM_ANALYTICS_POPULAR_CONTENT = 'kmAnalyticsPopularContent'; +export const KM_ANALYTICS_POPULAR_PRODUCTS = 'kmAnalyticsPopularProducts'; +export const KM_ANALYTICS_TOP_CITIES = 'kmAnalyticsTopCities'; +export const KM_ANALYTICS_TOP_CONVERTING_TRAFFIC_SOURCE = + 'kmTopConvertingTrafficSource'; +export const KM_ANALYTICS_TOP_COUNTRIES = 'kmAnalyticsTopCountries'; +export const KM_ANALYTICS_TOP_TRAFFIC_SOURCE = 'kmAnalyticsTopTrafficSource'; +export const KM_SEARCH_CONSOLE_POPULAR_KEYWORDS = + 'kmSearchConsolePopularKeywords'; diff --git a/assets/js/googlesitekit/datastore/user/key-metrics.js b/assets/js/googlesitekit/datastore/user/key-metrics.js index 23375fe2c93..5a7500089a8 100644 --- a/assets/js/googlesitekit/datastore/user/key-metrics.js +++ b/assets/js/googlesitekit/datastore/user/key-metrics.js @@ -25,21 +25,32 @@ import { isPlainObject } from 'lodash'; */ import API from 'googlesitekit-api'; import Data from 'googlesitekit-data'; -import { CORE_USER } from './constants'; +import { + CORE_USER, + KM_ANALYTICS_ENGAGED_TRAFFIC_SOURCE, + KM_ANALYTICS_LOYAL_VISITORS, + KM_ANALYTICS_NEW_VISITORS, + KM_ANALYTICS_POPULAR_CONTENT, + KM_ANALYTICS_POPULAR_PRODUCTS, + KM_ANALYTICS_TOP_TRAFFIC_SOURCE, + KM_SEARCH_CONSOLE_POPULAR_KEYWORDS, +} from './constants'; +import { CORE_SITE } from '../../datastore/site/constants'; + import { createFetchStore } from '../../data/create-fetch-store'; import { actions as errorStoreActions } from '../../data/create-error-store'; -import { CORE_WIDGETS } from '../../widgets/datastore/constants'; + const { receiveError, clearError } = errorStoreActions; const { createRegistrySelector } = Data; const SET_KEY_METRICS_SETTING = 'SET_KEY_METRICS_SETTING'; const baseInitialState = { - keyMetrics: undefined, + keyMetricsSettings: undefined, }; -const fetchGetUserPickedMetricsStore = createFetchStore( { - baseName: 'getUserPickedMetrics', +const fetchGetKeyMetricsSettingsStore = createFetchStore( { + baseName: 'getKeyMetricsSettings', controlCallback: () => API.get( 'core', 'user', 'key-metrics', undefined, { // Never cache key metrics requests, we want them to be @@ -47,17 +58,20 @@ const fetchGetUserPickedMetricsStore = createFetchStore( { // make requests to Google APIs so it's not a slow request. useCache: false, } ), - reducerCallback: ( state, keyMetrics ) => ( { + reducerCallback: ( state, keyMetricsSettings ) => ( { ...state, - keyMetrics, + keyMetricsSettings, } ), } ); -const fetchSaveKeyMetricsStore = createFetchStore( { - baseName: 'saveKeyMetrics', +const fetchSaveKeyMetricsSettingsStore = createFetchStore( { + baseName: 'saveKeyMetricsSettings', controlCallback: ( settings ) => API.set( 'core', 'user', 'key-metrics', { settings } ), - reducerCallback: ( state, keyMetrics ) => ( { ...state, keyMetrics } ), + reducerCallback: ( state, keyMetricsSettings ) => ( { + ...state, + keyMetricsSettings, + } ), argsToParams: ( settings ) => settings, validateParams: ( settings ) => { invariant( isPlainObject( settings ), 'Settings should be an object.' ); @@ -68,13 +82,13 @@ const baseActions = { /** * Sets key metrics setting. * - * @since 1.94.0 + * @since n.e.x.t * * @param {string} settingID Setting key. * @param {Array.} value Setting value. * @return {Object} Redux-style action. */ - setKeyMetricSetting( settingID, value ) { + setKeyMetricsSetting( settingID, value ) { return { type: SET_KEY_METRICS_SETTING, payload: { @@ -87,23 +101,25 @@ const baseActions = { /** * Saves key metrics settings. * - * @since 1.94.0 + * @since n.e.x.t * * @return {Object} Object with `response` and `error`. */ - *saveKeyMetrics() { - yield clearError( 'saveKeyMetrics', [] ); + *saveKeyMetricsSettings() { + yield clearError( 'saveKeyMetricsSettings', [] ); const registry = yield Data.commonActions.getRegistry(); - const keyMetrics = registry.select( CORE_USER ).getUserPickedMetrics(); + const keyMetricsSettings = registry + .select( CORE_USER ) + .getKeyMetricsSettings(); const { response, error } = - yield fetchSaveKeyMetricsStore.actions.fetchSaveKeyMetrics( - keyMetrics + yield fetchSaveKeyMetricsSettingsStore.actions.fetchSaveKeyMetricsSettings( + keyMetricsSettings ); if ( error ) { // Store error manually since saveKeyMetrics signature differs from fetchSaveKeyMetricsStore. - yield receiveError( error, 'saveKeyMetrics', [] ); + yield receiveError( error, 'saveKeyMetricsSettings', [] ); } return { response, error }; @@ -117,8 +133,8 @@ const baseReducer = ( state, { type, payload } ) => { case SET_KEY_METRICS_SETTING: { return { ...state, - keyMetrics: { - ...state.keyMetrics, + keyMetricsSettings: { + ...state.keyMetricsSettings, [ payload.settingID ]: payload.value, }, }; @@ -130,32 +146,27 @@ const baseReducer = ( state, { type, payload } ) => { }; const baseResolvers = { - *getUserPickedMetrics() { + *getKeyMetricsSettings() { const registry = yield Data.commonActions.getRegistry(); - const userPickedKeyMetrics = registry + const keyMetricsSettings = registry .select( CORE_USER ) - .getUserPickedMetrics(); + .getKeyMetricsSettings(); - if ( userPickedKeyMetrics ) { - return userPickedKeyMetrics; + if ( keyMetricsSettings ) { + return; } - yield fetchGetUserPickedMetricsStore.actions.fetchGetUserPickedMetrics(); + yield fetchGetKeyMetricsSettingsStore.actions.fetchGetKeyMetricsSettings(); }, }; const baseSelectors = { /** - * Gets key metrics for this user, either from the user-selected - * key metrics selected by this user (if available) or—if the user has not - * manually selected their own key metrics—from the automatically-selected - * (eg. "answer-based") metrics based on their answers to our User Input - * questions. + * Gets currently selected key metrics based on either the user picked metrics or the answer based metrics. * - * @since 1.96.0 + * @since n.e.x.t * - * @param {Object} state Data store's state. - * @return {(Object|undefined)} Key metrics settings. + * @return {Array|undefined} An array of key metric slugs, or undefined while loading. */ getKeyMetrics: createRegistrySelector( ( select ) => () => { const userPickedMetrics = select( CORE_USER ).getUserPickedMetrics(); @@ -164,30 +175,121 @@ const baseSelectors = { return undefined; } - if ( userPickedMetrics?.widgetSlugs?.length ) { - return userPickedMetrics.widgetSlugs; + if ( userPickedMetrics.length ) { + return userPickedMetrics; + } + + return select( CORE_USER ).getAnswerBasedMetrics(); + } ), + + /** + * Gets the Key Metric widget slugs based on the user input settings. + * + * @since n.e.x.t + * + * @return {Array|undefined} An array of Key Metric widget slugs, or undefined if the user input settings are not loaded. + */ + getAnswerBasedMetrics: createRegistrySelector( ( select ) => () => { + const userInputSettings = select( CORE_USER ).getUserInputSettings(); + + if ( userInputSettings === undefined ) { + return undefined; + } + + const purpose = userInputSettings?.purpose?.values?.[ 0 ]; + + const hasProductPostType = () => { + const postTypes = select( CORE_SITE ).getPostTypes(); + return postTypes.some( ( { slug } ) => slug === 'product' ); + }; + + switch ( purpose ) { + case 'publish_blog': + case 'publish_news': + return [ + KM_ANALYTICS_LOYAL_VISITORS, + KM_ANALYTICS_NEW_VISITORS, + KM_ANALYTICS_TOP_TRAFFIC_SOURCE, + KM_ANALYTICS_ENGAGED_TRAFFIC_SOURCE, + ]; + case 'monetize_content': + return [ + KM_ANALYTICS_POPULAR_CONTENT, + KM_ANALYTICS_ENGAGED_TRAFFIC_SOURCE, + KM_ANALYTICS_NEW_VISITORS, + KM_ANALYTICS_TOP_TRAFFIC_SOURCE, + ]; + case 'sell_products_or_service': + return [ + hasProductPostType() + ? KM_ANALYTICS_POPULAR_PRODUCTS + : KM_ANALYTICS_POPULAR_CONTENT, + KM_ANALYTICS_ENGAGED_TRAFFIC_SOURCE, + KM_SEARCH_CONSOLE_POPULAR_KEYWORDS, + KM_ANALYTICS_TOP_TRAFFIC_SOURCE, + ]; + + case 'share_portfolio': + return [ + KM_ANALYTICS_NEW_VISITORS, + KM_ANALYTICS_TOP_TRAFFIC_SOURCE, + KM_ANALYTICS_ENGAGED_TRAFFIC_SOURCE, + KM_SEARCH_CONSOLE_POPULAR_KEYWORDS, + ]; + default: + return []; + } + } ), + + /** + * Gets the Key Metric widget slugs selected by the user. + * + * @since n.e.x.t + * + * @return {Array|undefined} An array of Key Metric widget slugs, or undefined if the key metrics settings are not loaded. + */ + getUserPickedMetrics: createRegistrySelector( ( select ) => () => { + const keyMetricsSettings = select( CORE_USER ).getKeyMetricsSettings(); + + if ( keyMetricsSettings === undefined ) { + return undefined; + } + return keyMetricsSettings.widgetSlugs; + } ), + + /** + * Gets whether the key metrics widget is hidden. + * + * @since n.e.x.t + * + * @return {boolean|undefined} True if the key metrics widget is hidden, false if it is not, or undefined if the key metrics settings are not loaded. + */ + isKeyMetricsWidgetHidden: createRegistrySelector( ( select ) => () => { + const keyMetricsSettings = select( CORE_USER ).getKeyMetricsSettings(); + + if ( keyMetricsSettings === undefined ) { + return undefined; } - return select( CORE_WIDGETS ).getAnswerBasedMetrics(); + return keyMetricsSettings.isWidgetHidden; } ), /** - * Gets key metrics selected by the user. + * Gets key metrics settings. * - * @since 1.94.0 Initially introduced as `getKeyMetrics`. - * @since 1.96.0 Updated selector name now that `getKeyMetrics` contains more advanced logic. + * @since n.e.x.t * * @param {Object} state Data store's state. - * @return {(Object|undefined)} Key metrics settings. + * @return {(Object|undefined)} Key metrics settings. Returns `undefined` if not loaded. */ - getUserPickedMetrics( state ) { - return state.keyMetrics; + getKeyMetricsSettings( state ) { + return state.keyMetricsSettings; }, }; const store = Data.combineStores( - fetchGetUserPickedMetricsStore, - fetchSaveKeyMetricsStore, + fetchGetKeyMetricsSettingsStore, + fetchSaveKeyMetricsSettingsStore, { initialState: baseInitialState, actions: baseActions, diff --git a/assets/js/googlesitekit/datastore/user/key-metrics.test.js b/assets/js/googlesitekit/datastore/user/key-metrics.test.js index 85c12bbe297..0e5cb549991 100644 --- a/assets/js/googlesitekit/datastore/user/key-metrics.test.js +++ b/assets/js/googlesitekit/datastore/user/key-metrics.test.js @@ -20,10 +20,22 @@ import API from 'googlesitekit-api'; import { createTestRegistry, + freezeFetch, + provideSiteInfo, unsubscribeFromAll, untilResolved, + waitForDefaultTimeouts, } from '../../../../../tests/js/utils'; -import { CORE_USER } from './constants'; +import { + CORE_USER, + KM_ANALYTICS_ENGAGED_TRAFFIC_SOURCE, + KM_ANALYTICS_LOYAL_VISITORS, + KM_ANALYTICS_NEW_VISITORS, + KM_ANALYTICS_POPULAR_CONTENT, + KM_ANALYTICS_POPULAR_PRODUCTS, + KM_ANALYTICS_TOP_TRAFFIC_SOURCE, + KM_SEARCH_CONSOLE_POPULAR_KEYWORDS, +} from './constants'; describe( 'core/user key metrics', () => { let registry; @@ -32,7 +44,6 @@ describe( 'core/user key metrics', () => { const coreKeyMetricsEndpointRegExp = new RegExp( '^/google-site-kit/v1/core/user/data/key-metrics' ); - const coreKeyMetricsExpectedResponse = { widgetSlugs: [ 'widget1', 'widget2' ], isWidgetHidden: false, @@ -54,7 +65,9 @@ describe( 'core/user key metrics', () => { beforeEach( () => { registry = createTestRegistry(); + provideSiteInfo( registry ); store = registry.stores[ CORE_USER ].store; + registry.dispatch( CORE_USER ).receiveIsUserInputCompleted( true ); } ); afterAll( () => { @@ -69,23 +82,217 @@ describe( 'core/user key metrics', () => { const settingID = 'test-setting'; const settingValue = 'test-value'; - describe( 'setKeyMetricSetting', () => { + describe( 'getKeyMetrics', () => { + it( 'should use answer-based key metrics if the user has not selected any widgets', async () => { + fetchMock.getOnce( coreKeyMetricsEndpointRegExp, { + body: { + widgetSlugs: [], + isWidgetHidden: false, + }, + status: 200, + } ); + + fetchMock.getOnce( + new RegExp( + '^/google-site-kit/v1/core/user/data/user-input-settings' + ), + { + body: { + purpose: { + values: [ 'publish_blog' ], + scope: 'site', + }, + }, + status: 200, + } + ); + + registry.select( CORE_USER ).getUserInputSettings(); + + await untilResolved( + registry, + CORE_USER + ).getUserInputSettings(); + + registry.select( CORE_USER ).getKeyMetrics(); + + await untilResolved( + registry, + CORE_USER + ).getKeyMetricsSettings(); + + expect( + registry.select( CORE_USER ).getKeyMetrics() + ).toMatchObject( [ + KM_ANALYTICS_LOYAL_VISITORS, + KM_ANALYTICS_NEW_VISITORS, + KM_ANALYTICS_TOP_TRAFFIC_SOURCE, + KM_ANALYTICS_ENGAGED_TRAFFIC_SOURCE, + ] ); + + expect( fetchMock ).toHaveFetchedTimes( 2 ); + } ); + + it( 'should use the user-selected key metrics if the user has selected any widgets', async () => { + fetchMock.getOnce( coreKeyMetricsEndpointRegExp, { + body: { + widgetSlugs: [ KM_ANALYTICS_LOYAL_VISITORS ], + isWidgetHidden: false, + }, + status: 200, + } ); + + registry.select( CORE_USER ).getKeyMetrics(); + + await untilResolved( + registry, + CORE_USER + ).getKeyMetricsSettings(); + + expect( + registry.select( CORE_USER ).getKeyMetrics() + ).toMatchObject( [ KM_ANALYTICS_LOYAL_VISITORS ] ); + + expect( fetchMock ).toHaveFetchedTimes( 1 ); + } ); + } ); + + describe( 'getAnswerBasedMetrics', () => { + it( 'should return undefined if user input settings are not resolved', async () => { + freezeFetch( + new RegExp( + '^/google-site-kit/v1/core/user/data/user-input-settings' + ) + ); + + expect( + registry.select( CORE_USER ).getAnswerBasedMetrics() + ).toBeUndefined(); + + // Wait for resolvers to run. + await waitForDefaultTimeouts(); + } ); + + it.each( [ + [ 'undefined', undefined ], + [ 'null', null ], + [ 'an empty object', {} ], + [ 'an object with empty purpose', { purpose: {} } ], + [ + 'an object with empty purpose values', + { purpose: { values: [] } }, + ], + ] )( + 'should return an empty array if user input settings are %s', + ( userInputSettings ) => { + registry + .dispatch( CORE_USER ) + .receiveGetUserInputSettings( userInputSettings ); + + expect( + registry.select( CORE_USER ).getAnswerBasedMetrics() + ).toEqual( [] ); + } + ); + + it.each( [ + [ + 'publish_blog', + [ + KM_ANALYTICS_LOYAL_VISITORS, + KM_ANALYTICS_NEW_VISITORS, + KM_ANALYTICS_TOP_TRAFFIC_SOURCE, + KM_ANALYTICS_ENGAGED_TRAFFIC_SOURCE, + ], + ], + [ + 'publish_news', + [ + KM_ANALYTICS_LOYAL_VISITORS, + KM_ANALYTICS_NEW_VISITORS, + KM_ANALYTICS_TOP_TRAFFIC_SOURCE, + KM_ANALYTICS_ENGAGED_TRAFFIC_SOURCE, + ], + ], + [ + 'monetize_content', + [ + KM_ANALYTICS_POPULAR_CONTENT, + KM_ANALYTICS_ENGAGED_TRAFFIC_SOURCE, + KM_ANALYTICS_NEW_VISITORS, + KM_ANALYTICS_TOP_TRAFFIC_SOURCE, + ], + ], + [ + 'sell_products_or_service', + [ + KM_ANALYTICS_POPULAR_CONTENT, + KM_ANALYTICS_ENGAGED_TRAFFIC_SOURCE, + KM_SEARCH_CONSOLE_POPULAR_KEYWORDS, + KM_ANALYTICS_TOP_TRAFFIC_SOURCE, + ], + ], + [ + 'share_portfolio', + [ + KM_ANALYTICS_NEW_VISITORS, + KM_ANALYTICS_TOP_TRAFFIC_SOURCE, + KM_ANALYTICS_ENGAGED_TRAFFIC_SOURCE, + KM_SEARCH_CONSOLE_POPULAR_KEYWORDS, + ], + ], + ] )( + 'should return the correct metrics for the %s purpose', + ( purpose, expectedMetrics ) => { + registry + .dispatch( CORE_USER ) + .receiveGetUserInputSettings( { + purpose: { values: [ purpose ] }, + } ); + + expect( + registry.select( CORE_USER ).getAnswerBasedMetrics() + ).toEqual( expectedMetrics ); + } + ); + + it( 'should return the correct metrics for the sell_products_or_service purposes when the site has a product post type', () => { + provideSiteInfo( registry, { + postTypes: [ { slug: 'product', label: 'Product' } ], + } ); + + registry.dispatch( CORE_USER ).receiveGetUserInputSettings( { + purpose: { values: [ 'sell_products_or_service' ] }, + } ); + + expect( + registry.select( CORE_USER ).getAnswerBasedMetrics() + ).toEqual( [ + KM_ANALYTICS_POPULAR_PRODUCTS, + KM_ANALYTICS_ENGAGED_TRAFFIC_SOURCE, + KM_SEARCH_CONSOLE_POPULAR_KEYWORDS, + KM_ANALYTICS_TOP_TRAFFIC_SOURCE, + ] ); + } ); + } ); + + describe( 'setKeyMetricsSetting', () => { it( 'should set the setting value to the store', async () => { await registry .dispatch( CORE_USER ) - .setKeyMetricSetting( settingID, settingValue ); + .setKeyMetricsSetting( settingID, settingValue ); - expect( store.getState().keyMetrics[ settingID ] ).toBe( + expect( store.getState().keyMetricsSettings[ settingID ] ).toBe( settingValue ); } ); } ); - describe( 'saveKeyMetrics', () => { + describe( 'saveKeyMetricsSettings', () => { beforeEach( async () => { await registry .dispatch( CORE_USER ) - .setKeyMetricSetting( settingID, settingValue ); + .setKeyMetricsSetting( settingID, settingValue ); } ); it( 'should save settings and add it to the store ', async () => { @@ -94,7 +301,7 @@ describe( 'core/user key metrics', () => { status: 200, } ); - await registry.dispatch( CORE_USER ).saveKeyMetrics(); + await registry.dispatch( CORE_USER ).saveKeyMetricsSettings(); // Ensure the proper body parameters were sent. expect( fetchMock ).toHaveFetched( @@ -110,7 +317,7 @@ describe( 'core/user key metrics', () => { } ); - expect( store.getState().keyMetrics ).toMatchObject( + expect( store.getState().keyMetricsSettings ).toMatchObject( coreKeyMetricsExpectedResponse ); @@ -129,12 +336,12 @@ describe( 'core/user key metrics', () => { status: 500, } ); - await registry.dispatch( CORE_USER ).saveKeyMetrics(); + await registry.dispatch( CORE_USER ).saveKeyMetricsSettings(); expect( registry .select( CORE_USER ) - .getErrorForAction( 'saveKeyMetrics', [] ) + .getErrorForAction( 'saveKeyMetricsSettings', [] ) ).toMatchObject( response ); expect( console ).toHaveErrored(); @@ -143,8 +350,8 @@ describe( 'core/user key metrics', () => { } ); describe( 'selectors', () => { - describe( 'getKeyMetrics', () => { - it( 'should fetch user key metrics from the API if none exist', async () => { + describe( 'getKeyMetricsSettings', () => { + it( 'should fetch user key metrics settings from the API if none exist', async () => { fetchMock.getOnce( coreKeyMetricsEndpointRegExp, { body: coreKeyMetricsExpectedResponse, status: 200, @@ -162,12 +369,12 @@ describe( 'core/user key metrics', () => { CORE_USER ).getUserInputSettings(); - registry.select( CORE_USER ).getKeyMetrics(); + registry.select( CORE_USER ).getKeyMetricsSettings(); await untilResolved( registry, CORE_USER - ).getUserPickedMetrics(); + ).getKeyMetricsSettings(); expect( fetchMock ).toHaveFetched( coreKeyMetricsEndpointRegExp, @@ -179,50 +386,111 @@ describe( 'core/user key metrics', () => { ); expect( - registry.select( CORE_USER ).getKeyMetrics() - ).toMatchObject( coreKeyMetricsExpectedResponse.widgetSlugs ); + registry.select( CORE_USER ).getKeyMetricsSettings() + ).toMatchObject( coreKeyMetricsExpectedResponse ); expect( fetchMock ).toHaveFetchedTimes( 2 ); } ); - it( 'should use answer-based key metrics if the user has not selected any widgets', async () => { + it( 'should not make a network request if settings exist', async () => { + registry + .dispatch( CORE_USER ) + .receiveGetKeyMetricsSettings( + coreKeyMetricsExpectedResponse + ); + + registry.select( CORE_USER ).getKeyMetricsSettings(); + + await untilResolved( + registry, + CORE_USER + ).getKeyMetricsSettings(); + + expect( fetchMock ).not.toHaveFetched( + coreKeyMetricsEndpointRegExp + ); + } ); + } ); + + describe( 'getUserPickedMetrics', () => { + it( 'should return undefined while settings are loading', async () => { + freezeFetch( coreKeyMetricsEndpointRegExp ); + + const { getUserPickedMetrics } = registry.select( CORE_USER ); + + expect( getUserPickedMetrics() ).toBeUndefined(); + + await waitForDefaultTimeouts(); + } ); + + it( 'uses a resolver to make a network request if settings are not available', async () => { fetchMock.getOnce( coreKeyMetricsEndpointRegExp, { - body: { - widgetSlugs: [], - isWidgetHidden: false, - }, + body: coreKeyMetricsExpectedResponse, status: 200, } ); - fetchMock.getOnce( coreUserInputSettingsEndpointRegExp, { - body: coreUserInputSettingsExpectedResponse, - status: 200, - } ); + const { getUserPickedMetrics } = registry.select( CORE_USER ); - registry.select( CORE_USER ).getUserInputSettings(); + expect( getUserPickedMetrics() ).toBeUndefined(); await untilResolved( registry, CORE_USER - ).getUserInputSettings(); + ).getKeyMetricsSettings(); - registry.select( CORE_USER ).getKeyMetrics(); + expect( + registry.select( CORE_USER ).getUserPickedMetrics() + ).toEqual( coreKeyMetricsExpectedResponse.widgetSlugs ); + + expect( fetchMock ).toHaveFetchedTimes( 1 ); + } ); + + it( 'should return user picked metrics', () => { + registry + .dispatch( CORE_USER ) + .receiveGetKeyMetricsSettings( + coreKeyMetricsExpectedResponse + ); + + expect( + registry.select( CORE_USER ).getUserPickedMetrics() + ).toEqual( coreKeyMetricsExpectedResponse.widgetSlugs ); + } ); + } ); + + describe( 'isKeyMetricsWidgetHidden', () => { + it( 'should return undefined while settings are loading', async () => { + freezeFetch( coreKeyMetricsEndpointRegExp ); + + const { isKeyMetricsWidgetHidden } = + registry.select( CORE_USER ); + + expect( isKeyMetricsWidgetHidden() ).toBeUndefined(); + + await waitForDefaultTimeouts(); + } ); + + it( 'uses a resolver to make a network request if settings are not available', async () => { + fetchMock.getOnce( coreKeyMetricsEndpointRegExp, { + body: coreKeyMetricsExpectedResponse, + status: 200, + } ); + + const { isKeyMetricsWidgetHidden } = + registry.select( CORE_USER ); + + expect( isKeyMetricsWidgetHidden() ).toBeUndefined(); await untilResolved( registry, CORE_USER - ).getUserPickedMetrics(); + ).getKeyMetricsSettings(); expect( - registry.select( CORE_USER ).getKeyMetrics() - ).toMatchObject( [ - 'kmAnalyticsLoyalVisitors', - 'kmAnalyticsNewVisitors', - 'kmAnalyticsTopTrafficSource', - 'kmAnalyticsEngagedTrafficSource', - ] ); + registry.select( CORE_USER ).isKeyMetricsWidgetHidden() + ).toEqual( coreKeyMetricsExpectedResponse.isWidgetHidden ); - expect( fetchMock ).toHaveFetchedTimes( 2 ); + expect( fetchMock ).toHaveFetchedTimes( 1 ); } ); } ); } ); diff --git a/assets/js/googlesitekit/widgets/datastore/index.js b/assets/js/googlesitekit/widgets/datastore/index.js index e1cb08c32c7..8e34055ebdb 100644 --- a/assets/js/googlesitekit/widgets/datastore/index.js +++ b/assets/js/googlesitekit/widgets/datastore/index.js @@ -23,7 +23,6 @@ import Data from 'googlesitekit-data'; import areas from './areas'; import widgets from './widgets'; import contexts from './contexts'; -import keyMetrics from './key-metrics'; import { createErrorStore } from '../../data/create-error-store'; import { CORE_WIDGETS } from './constants'; @@ -32,7 +31,6 @@ const store = Data.combineStores( areas, widgets, contexts, - keyMetrics, createErrorStore( CORE_WIDGETS ) ); diff --git a/assets/js/googlesitekit/widgets/datastore/key-metrics.js b/assets/js/googlesitekit/widgets/datastore/key-metrics.js deleted file mode 100644 index 822984af265..00000000000 --- a/assets/js/googlesitekit/widgets/datastore/key-metrics.js +++ /dev/null @@ -1,91 +0,0 @@ -/** - * `core/widgets` data store: key metrics data. - * - * Site Kit by Google, Copyright 2023 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 { CORE_SITE } from '../../datastore/site/constants'; -import { CORE_USER } from '../../datastore/user/constants'; - -const { createRegistrySelector } = Data; - -const selectors = { - /** - * Gets the Key Metric widget slugs based on the user input settings. - * - * @since 1.95.0 - * - * @return {Array|undefined} An array of Key Metric widget slugs, or undefined if the user input settings are not loaded. - */ - getAnswerBasedMetrics: createRegistrySelector( ( select ) => () => { - const userInputSettings = select( CORE_USER ).getUserInputSettings(); - - if ( userInputSettings === undefined ) { - return undefined; - } - - const purpose = userInputSettings?.purpose?.values?.[ 0 ]; - - const hasProductPostType = () => { - const postTypes = select( CORE_SITE ).getPostTypes(); - return postTypes.some( ( { slug } ) => slug === 'product' ); - }; - - switch ( purpose ) { - case 'publish_blog': - case 'publish_news': - return [ - 'kmAnalyticsLoyalVisitors', - 'kmAnalyticsNewVisitors', - 'kmAnalyticsTopTrafficSource', - 'kmAnalyticsEngagedTrafficSource', - ]; - case 'monetize_content': - return [ - 'kmAnalyticsPopularContent', - 'kmAnalyticsEngagedTrafficSource', - 'kmAnalyticsNewVisitors', - 'kmAnalyticsTopTrafficSource', - ]; - case 'sell_products_or_service': - return [ - hasProductPostType() - ? 'kmTopPopularProducts' - : 'kmAnalyticsPopularContent', - 'kmAnalyticsEngagedTrafficSource', - 'kmSearchConsolePopularKeywords', - 'kmAnalyticsTopTrafficSource', - ]; - - case 'share_portfolio': - return [ - 'kmAnalyticsNewVisitors', - 'kmAnalyticsTopTrafficSource', - 'kmAnalyticsEngagedTrafficSource', - 'kmSearchConsolePopularKeywords', - ]; - default: - return []; - } - } ), -}; - -export default { - selectors, -}; diff --git a/assets/js/googlesitekit/widgets/datastore/key-metrics.test.js b/assets/js/googlesitekit/widgets/datastore/key-metrics.test.js deleted file mode 100644 index bf99bea30ba..00000000000 --- a/assets/js/googlesitekit/widgets/datastore/key-metrics.test.js +++ /dev/null @@ -1,165 +0,0 @@ -/** - * `core/widgets` data store: key metrics tests. - * - * Site Kit by Google, Copyright 2023 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, - freezeFetch, - provideSiteInfo, - unsubscribeFromAll, - waitForDefaultTimeouts, -} from '../../../../../tests/js/utils'; -import { CORE_USER } from '../../datastore/user/constants'; -import { CORE_WIDGETS } from './constants'; - -let registry; - -describe( 'core/widgets key metrics', () => { - beforeEach( () => { - registry = createTestRegistry(); - registry.dispatch( CORE_USER ).receiveIsUserInputCompleted( true ); - provideSiteInfo( registry ); - } ); - - afterEach( () => { - unsubscribeFromAll( registry ); - } ); - - describe( 'selectors', () => { - describe( 'getAnswerBasedMetrics', () => { - it( 'should return undefined if user input settings are not resolved', async () => { - freezeFetch( - new RegExp( - '^/google-site-kit/v1/core/user/data/user-input-settings' - ) - ); - - expect( - registry.select( CORE_WIDGETS ).getAnswerBasedMetrics() - ).toBeUndefined(); - - // Wait for resolvers to run. - await waitForDefaultTimeouts(); - } ); - - it.each( [ - [ 'undefined', undefined ], - [ 'null', null ], - [ 'an empty object', {} ], - [ 'an object with empty purpose', { purpose: {} } ], - [ - 'an object with empty purpose values', - { purpose: { values: [] } }, - ], - ] )( - 'should return an empty array if user input settings are %s', - ( userInputSettings ) => { - registry - .dispatch( CORE_USER ) - .receiveGetUserInputSettings( userInputSettings ); - - expect( - registry.select( CORE_WIDGETS ).getAnswerBasedMetrics() - ).toEqual( [] ); - } - ); - - it.each( [ - [ - 'publish_blog', - [ - 'kmAnalyticsLoyalVisitors', - 'kmAnalyticsNewVisitors', - 'kmAnalyticsTopTrafficSource', - 'kmAnalyticsEngagedTrafficSource', - ], - ], - [ - 'publish_news', - [ - 'kmAnalyticsLoyalVisitors', - 'kmAnalyticsNewVisitors', - 'kmAnalyticsTopTrafficSource', - 'kmAnalyticsEngagedTrafficSource', - ], - ], - [ - 'monetize_content', - [ - 'kmAnalyticsPopularContent', - 'kmAnalyticsEngagedTrafficSource', - 'kmAnalyticsNewVisitors', - 'kmAnalyticsTopTrafficSource', - ], - ], - [ - 'sell_products_or_service', - [ - 'kmAnalyticsPopularContent', - 'kmAnalyticsEngagedTrafficSource', - 'kmSearchConsolePopularKeywords', - 'kmAnalyticsTopTrafficSource', - ], - ], - [ - 'share_portfolio', - [ - 'kmAnalyticsNewVisitors', - 'kmAnalyticsTopTrafficSource', - 'kmAnalyticsEngagedTrafficSource', - 'kmSearchConsolePopularKeywords', - ], - ], - ] )( - 'should return the correct metrics for the %s purpose', - ( purpose, expectedMetrics ) => { - registry - .dispatch( CORE_USER ) - .receiveGetUserInputSettings( { - purpose: { values: [ purpose ] }, - } ); - - expect( - registry.select( CORE_WIDGETS ).getAnswerBasedMetrics() - ).toEqual( expectedMetrics ); - } - ); - - it( 'should return the correct metrics for the sell_products_or_service purposes when the site has a product post type', () => { - provideSiteInfo( registry, { - postTypes: [ { slug: 'product', label: 'Product' } ], - } ); - - registry.dispatch( CORE_USER ).receiveGetUserInputSettings( { - purpose: { values: [ 'sell_products_or_service' ] }, - } ); - - expect( - registry.select( CORE_WIDGETS ).getAnswerBasedMetrics() - ).toEqual( [ - 'kmTopPopularProducts', - 'kmAnalyticsEngagedTrafficSource', - 'kmSearchConsolePopularKeywords', - 'kmAnalyticsTopTrafficSource', - ] ); - } ); - } ); - } ); -} ); diff --git a/assets/js/modules/analytics-4/components/widgets/EngagedTrafficSourceWidget.js b/assets/js/modules/analytics-4/components/widgets/EngagedTrafficSourceWidget.js index 27abd533ac5..deacccb60ec 100644 --- a/assets/js/modules/analytics-4/components/widgets/EngagedTrafficSourceWidget.js +++ b/assets/js/modules/analytics-4/components/widgets/EngagedTrafficSourceWidget.js @@ -16,6 +16,36 @@ * limitations under the License. */ -export default function EngagedTrafficSourceWidget() { - return
TODO: UI for EngagedTrafficSourceWidget
; +/** + * External dependencies + */ +import PropTypes from 'prop-types'; + +/** + * Internal dependencies + */ +import Data from 'googlesitekit-data'; +import { CORE_USER } from '../../../../googlesitekit/datastore/user/constants'; + +const { useSelect } = Data; + +export default function EngagedTrafficSourceWidget( { Widget, WidgetNull } ) { + const keyMetricsWidgetHidden = useSelect( ( select ) => + select( CORE_USER ).isKeyMetricsWidgetHidden() + ); + + if ( keyMetricsWidgetHidden !== false ) { + return ; + } + + return ( + +
TODO: UI for EngagedTrafficSourceWidget
+
+ ); } + +EngagedTrafficSourceWidget.propTypes = { + Widget: PropTypes.elementType.isRequired, + WidgetNull: PropTypes.elementType.isRequired, +}; diff --git a/assets/js/modules/analytics-4/components/widgets/EngagedTrafficSourceWidget.test.js b/assets/js/modules/analytics-4/components/widgets/EngagedTrafficSourceWidget.test.js deleted file mode 100644 index 94cdb0d6ade..00000000000 --- a/assets/js/modules/analytics-4/components/widgets/EngagedTrafficSourceWidget.test.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * EngagedTrafficSourceWidget tests. - * - * Site Kit by Google, Copyright 2023 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 EngagedTrafficSourceWidget from './EngagedTrafficSourceWidget'; - -describe( 'EngagedTrafficSourceWidget', () => { - it( 'should render the widget', () => { - const { getByText } = render( ); - - expect( - getByText( 'TODO: UI for EngagedTrafficSourceWidget' ) - ).toBeInTheDocument(); - } ); -} ); diff --git a/assets/js/modules/analytics-4/components/widgets/LoyalVisitorsWidget.js b/assets/js/modules/analytics-4/components/widgets/LoyalVisitorsWidget.js index 522d226a83b..5d0902e35d0 100644 --- a/assets/js/modules/analytics-4/components/widgets/LoyalVisitorsWidget.js +++ b/assets/js/modules/analytics-4/components/widgets/LoyalVisitorsWidget.js @@ -16,6 +16,36 @@ * limitations under the License. */ -export default function LoyalVisitorsWidget() { - return
TODO: UI for LoyalVisitorsWidget
; +/** + * External dependencies + */ +import PropTypes from 'prop-types'; + +/** + * Internal dependencies + */ +import Data from 'googlesitekit-data'; +import { CORE_USER } from '../../../../googlesitekit/datastore/user/constants'; + +const { useSelect } = Data; + +export default function LoyalVisitorsWidget( { Widget, WidgetNull } ) { + const keyMetricsWidgetHidden = useSelect( ( select ) => + select( CORE_USER ).isKeyMetricsWidgetHidden() + ); + + if ( keyMetricsWidgetHidden !== false ) { + return ; + } + + return ( + +
TODO: UI for LoyalVisitorsWidget
+
+ ); } + +LoyalVisitorsWidget.propTypes = { + Widget: PropTypes.elementType.isRequired, + WidgetNull: PropTypes.elementType.isRequired, +}; diff --git a/assets/js/modules/analytics-4/components/widgets/LoyalVisitorsWidget.test.js b/assets/js/modules/analytics-4/components/widgets/LoyalVisitorsWidget.test.js deleted file mode 100644 index 6301b87d42b..00000000000 --- a/assets/js/modules/analytics-4/components/widgets/LoyalVisitorsWidget.test.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * LoyalVisitorsWidget tests. - * - * Site Kit by Google, Copyright 2023 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 LoyalVisitorsWidget from './LoyalVisitorsWidget'; - -describe( 'LoyalVisitorsWidget', () => { - it( 'should render the widget', () => { - const { getByText } = render( ); - - expect( - getByText( 'TODO: UI for LoyalVisitorsWidget' ) - ).toBeInTheDocument(); - } ); -} ); diff --git a/assets/js/modules/analytics-4/components/widgets/NewVisitorsWidget.js b/assets/js/modules/analytics-4/components/widgets/NewVisitorsWidget.js index 01b54308568..05c9ac44d38 100644 --- a/assets/js/modules/analytics-4/components/widgets/NewVisitorsWidget.js +++ b/assets/js/modules/analytics-4/components/widgets/NewVisitorsWidget.js @@ -16,6 +16,11 @@ * limitations under the License. */ +/** + * External dependencies + */ +import PropTypes from 'prop-types'; + /** * WordPress dependencies */ @@ -25,15 +30,20 @@ import { __, sprintf } from '@wordpress/i18n'; * Internal dependencies */ import Data from 'googlesitekit-data'; +import MetricTileNumeric from '../../../../components/KeyMetrics/MetricTileNumeric'; import { CORE_USER } from '../../../../googlesitekit/datastore/user/constants'; import { DATE_RANGE_OFFSET, MODULES_ANALYTICS_4, } from '../../datastore/constants'; -import MetricTileNumeric from '../../../../components/KeyMetrics/MetricTileNumeric'; + const { useSelect, useInViewSelect } = Data; -export default function NewVisitorsWidget( widgetProps ) { +export default function NewVisitorsWidget( { Widget, WidgetNull } ) { + const keyMetricsWidgetHidden = useSelect( ( select ) => + select( CORE_USER ).isKeyMetricsWidgetHidden() + ); + const dates = useSelect( ( select ) => select( CORE_USER ).getDateRangeDates( { offsetDays: DATE_RANGE_OFFSET, @@ -75,9 +85,13 @@ export default function NewVisitorsWidget( widgetProps ) { parseInt( report?.rows?.[ 2 ]?.metricValues[ 0 ]?.value, 10 ) || 0; const compareTotalVisitors = compareNewVisitors + compareReturningVisitors; + if ( keyMetricsWidgetHidden !== false ) { + return ; + } + return ( ); } + +NewVisitorsWidget.propTypes = { + Widget: PropTypes.elementType.isRequired, + WidgetNull: PropTypes.elementType.isRequired, +}; diff --git a/assets/js/modules/analytics-4/components/widgets/NewVisitorsWidget.stories.js b/assets/js/modules/analytics-4/components/widgets/NewVisitorsWidget.stories.js index e9d8b4ffbcd..8f638be9198 100644 --- a/assets/js/modules/analytics-4/components/widgets/NewVisitorsWidget.stories.js +++ b/assets/js/modules/analytics-4/components/widgets/NewVisitorsWidget.stories.js @@ -19,7 +19,10 @@ /** * Internal dependencies */ -import { provideModules } from '../../../../../../tests/js/utils'; +import { + provideKeyMetrics, + provideModules, +} from '../../../../../../tests/js/utils'; import { withWidgetComponentProps } from '../../../../googlesitekit/widgets/util'; import WithRegistrySetup from '../../../../../../tests/js/WithRegistrySetup'; import NewVisitorsWidget from './NewVisitorsWidget'; @@ -110,6 +113,8 @@ export default { registry.dispatch( CORE_USER ).setReferenceDate( '2020-09-08' ); + provideKeyMetrics( registry ); + // Call story-specific setup. args.setupRegistry( registry ); }; diff --git a/assets/js/modules/analytics-4/components/widgets/PopularContentWidget.js b/assets/js/modules/analytics-4/components/widgets/PopularContentWidget.js index 819b6fc3091..c9b4937b89a 100644 --- a/assets/js/modules/analytics-4/components/widgets/PopularContentWidget.js +++ b/assets/js/modules/analytics-4/components/widgets/PopularContentWidget.js @@ -16,6 +16,36 @@ * limitations under the License. */ -export default function PopularContentWidget() { - return
TODO: UI for PopularContentWidget
; +/** + * External dependencies + */ +import PropTypes from 'prop-types'; + +/** + * Internal dependencies + */ +import Data from 'googlesitekit-data'; +import { CORE_USER } from '../../../../googlesitekit/datastore/user/constants'; + +const { useSelect } = Data; + +export default function PopularContentWidget( { Widget, WidgetNull } ) { + const keyMetricsWidgetHidden = useSelect( ( select ) => + select( CORE_USER ).isKeyMetricsWidgetHidden() + ); + + if ( keyMetricsWidgetHidden !== false ) { + return ; + } + + return ( + +
TODO: UI for PopularContentWidget
+
+ ); } + +PopularContentWidget.propTypes = { + Widget: PropTypes.elementType.isRequired, + WidgetNull: PropTypes.elementType.isRequired, +}; diff --git a/assets/js/modules/analytics-4/components/widgets/PopularContentWidget.test.js b/assets/js/modules/analytics-4/components/widgets/PopularContentWidget.test.js deleted file mode 100644 index 682e78b8247..00000000000 --- a/assets/js/modules/analytics-4/components/widgets/PopularContentWidget.test.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * PopularContentWidget component tests. - * - * Site Kit by Google, Copyright 2023 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 PopularContentWidget from './PopularContentWidget'; - -describe( 'PopularContentWidget', () => { - it( 'should render the widget', () => { - const { getByText } = render( ); - - expect( - getByText( 'TODO: UI for PopularContentWidget' ) - ).toBeInTheDocument(); - } ); -} ); diff --git a/assets/js/modules/analytics-4/components/widgets/PopularProductsWidget.js b/assets/js/modules/analytics-4/components/widgets/PopularProductsWidget.js index b5b8a84b88a..384030944ab 100644 --- a/assets/js/modules/analytics-4/components/widgets/PopularProductsWidget.js +++ b/assets/js/modules/analytics-4/components/widgets/PopularProductsWidget.js @@ -16,6 +16,36 @@ * limitations under the License. */ -export default function PopularProductsWidget() { - return
TODO: UI for PopularProductsWidget
; +/** + * External dependencies + */ +import PropTypes from 'prop-types'; + +/** + * Internal dependencies + */ +import Data from 'googlesitekit-data'; +import { CORE_USER } from '../../../../googlesitekit/datastore/user/constants'; + +const { useSelect } = Data; + +export default function PopularProductsWidget( { Widget, WidgetNull } ) { + const keyMetricsWidgetHidden = useSelect( ( select ) => + select( CORE_USER ).isKeyMetricsWidgetHidden() + ); + + if ( keyMetricsWidgetHidden !== false ) { + return ; + } + + return ( + +
TODO: UI for PopularProductsWidget
+
+ ); } + +PopularProductsWidget.propTypes = { + Widget: PropTypes.elementType.isRequired, + WidgetNull: PropTypes.elementType.isRequired, +}; diff --git a/assets/js/modules/analytics-4/components/widgets/PopularProductsWidget.test.js b/assets/js/modules/analytics-4/components/widgets/PopularProductsWidget.test.js deleted file mode 100644 index 154917d0e32..00000000000 --- a/assets/js/modules/analytics-4/components/widgets/PopularProductsWidget.test.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * PopularProductsWidget component tests. - * - * Site Kit by Google, Copyright 2023 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 PopularProductsWidget from './PopularProductsWidget'; - -describe( 'PopularProductsWidget', () => { - it( 'should render the widget', () => { - const { getByText } = render( ); - - expect( - getByText( 'TODO: UI for PopularProductsWidget' ) - ).toBeInTheDocument(); - } ); -} ); diff --git a/assets/js/modules/analytics-4/components/widgets/TopCitiesWidget.js b/assets/js/modules/analytics-4/components/widgets/TopCitiesWidget.js index cf574068a8f..71748110fd7 100644 --- a/assets/js/modules/analytics-4/components/widgets/TopCitiesWidget.js +++ b/assets/js/modules/analytics-4/components/widgets/TopCitiesWidget.js @@ -16,6 +16,36 @@ * limitations under the License. */ -export default function TopCitiesWidget() { - return
TODO: UI for TopCitiesWidget
; +/** + * External dependencies + */ +import PropTypes from 'prop-types'; + +/** + * Internal dependencies + */ +import Data from 'googlesitekit-data'; +import { CORE_USER } from '../../../../googlesitekit/datastore/user/constants'; + +const { useSelect } = Data; + +export default function TopCitiesWidget( { Widget, WidgetNull } ) { + const keyMetricsWidgetHidden = useSelect( ( select ) => + select( CORE_USER ).isKeyMetricsWidgetHidden() + ); + + if ( keyMetricsWidgetHidden !== false ) { + return ; + } + + return ( + +
TODO: UI for TopCitiesWidget
+
+ ); } + +TopCitiesWidget.propTypes = { + Widget: PropTypes.elementType.isRequired, + WidgetNull: PropTypes.elementType.isRequired, +}; diff --git a/assets/js/modules/analytics-4/components/widgets/TopCitiesWidget.test.js b/assets/js/modules/analytics-4/components/widgets/TopCitiesWidget.test.js deleted file mode 100644 index f0990af42d6..00000000000 --- a/assets/js/modules/analytics-4/components/widgets/TopCitiesWidget.test.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * TopCitiesWidget component tests. - * - * Site Kit by Google, Copyright 2023 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 TopCitiesWidget from './TopCitiesWidget'; - -describe( 'TopCitiesWidget', () => { - it( 'should render the widget', () => { - const { getByText } = render( ); - - expect( - getByText( 'TODO: UI for TopCitiesWidget' ) - ).toBeInTheDocument(); - } ); -} ); diff --git a/assets/js/modules/analytics-4/components/widgets/TopConvertingTrafficSourceWidget.js b/assets/js/modules/analytics-4/components/widgets/TopConvertingTrafficSourceWidget.js index ca14c04d95a..e5180827f30 100644 --- a/assets/js/modules/analytics-4/components/widgets/TopConvertingTrafficSourceWidget.js +++ b/assets/js/modules/analytics-4/components/widgets/TopConvertingTrafficSourceWidget.js @@ -16,6 +16,39 @@ * limitations under the License. */ -export default function TopConvertingTrafficSourceWidget() { - return
TODO: UI for TopConvertingTrafficSourceWidget
; +/** + * External dependencies + */ +import PropTypes from 'prop-types'; + +/** + * Internal dependencies + */ +import Data from 'googlesitekit-data'; +import { CORE_USER } from '../../../../googlesitekit/datastore/user/constants'; + +const { useSelect } = Data; + +export default function TopConvertingTrafficSourceWidget( { + Widget, + WidgetNull, +} ) { + const keyMetricsWidgetHidden = useSelect( ( select ) => + select( CORE_USER ).isKeyMetricsWidgetHidden() + ); + + if ( keyMetricsWidgetHidden !== false ) { + return ; + } + + return ( + +
TODO: UI for TopConvertingTrafficSourceWidget
+
+ ); } + +TopConvertingTrafficSourceWidget.propTypes = { + Widget: PropTypes.elementType.isRequired, + WidgetNull: PropTypes.elementType.isRequired, +}; diff --git a/assets/js/modules/analytics-4/components/widgets/TopConvertingTrafficSourceWidget.test.js b/assets/js/modules/analytics-4/components/widgets/TopConvertingTrafficSourceWidget.test.js deleted file mode 100644 index d3cb6108470..00000000000 --- a/assets/js/modules/analytics-4/components/widgets/TopConvertingTrafficSourceWidget.test.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * TopConvertingTrafficSourceWidget component tests. - * - * Site Kit by Google, Copyright 2023 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 TopConvertingTrafficSourceWidget from './TopConvertingTrafficSourceWidget'; - -describe( 'TopConvertingTrafficSourceWidget', () => { - it( 'should render the widget', () => { - const { getByText } = render( ); - - expect( - getByText( 'TODO: UI for TopConvertingTrafficSourceWidget' ) - ).toBeInTheDocument(); - } ); -} ); diff --git a/assets/js/modules/analytics-4/components/widgets/TopCountriesWidget.js b/assets/js/modules/analytics-4/components/widgets/TopCountriesWidget.js index 65c8840f708..ef9769d8355 100644 --- a/assets/js/modules/analytics-4/components/widgets/TopCountriesWidget.js +++ b/assets/js/modules/analytics-4/components/widgets/TopCountriesWidget.js @@ -16,6 +16,36 @@ * limitations under the License. */ -export default function TopCountriesWidget() { - return
TODO: UI for TopCountriesWidget
; +/** + * External dependencies + */ +import PropTypes from 'prop-types'; + +/** + * Internal dependencies + */ +import Data from 'googlesitekit-data'; +import { CORE_USER } from '../../../../googlesitekit/datastore/user/constants'; + +const { useSelect } = Data; + +export default function TopCountriesWidget( { Widget, WidgetNull } ) { + const keyMetricsWidgetHidden = useSelect( ( select ) => + select( CORE_USER ).isKeyMetricsWidgetHidden() + ); + + if ( keyMetricsWidgetHidden !== false ) { + return ; + } + + return ( + +
TODO: UI for TopCountriesWidget
+
+ ); } + +TopCountriesWidget.propTypes = { + Widget: PropTypes.elementType.isRequired, + WidgetNull: PropTypes.elementType.isRequired, +}; diff --git a/assets/js/modules/analytics-4/components/widgets/TopCountriesWidget.test.js b/assets/js/modules/analytics-4/components/widgets/TopCountriesWidget.test.js deleted file mode 100644 index 2c3ffed46b0..00000000000 --- a/assets/js/modules/analytics-4/components/widgets/TopCountriesWidget.test.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * TopCountriesWidget component tests. - * - * Site Kit by Google, Copyright 2023 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 TopCountriesWidget from './TopCountriesWidget'; - -describe( 'TopCountriesWidget', () => { - it( 'should render the widget', () => { - const { getByText } = render( ); - - expect( - getByText( 'TODO: UI for TopCountriesWidget' ) - ).toBeInTheDocument(); - } ); -} ); diff --git a/assets/js/modules/analytics-4/components/widgets/TopTrafficSourceWidget.js b/assets/js/modules/analytics-4/components/widgets/TopTrafficSourceWidget.js index c86a466f51d..d9d420b54f3 100644 --- a/assets/js/modules/analytics-4/components/widgets/TopTrafficSourceWidget.js +++ b/assets/js/modules/analytics-4/components/widgets/TopTrafficSourceWidget.js @@ -16,6 +16,36 @@ * limitations under the License. */ -export default function TopTrafficSourceWidget() { - return
TODO: UI for TopTrafficSourceWidget
; +/** + * External dependencies + */ +import PropTypes from 'prop-types'; + +/** + * Internal dependencies + */ +import Data from 'googlesitekit-data'; +import { CORE_USER } from '../../../../googlesitekit/datastore/user/constants'; + +const { useSelect } = Data; + +export default function TopTrafficSourceWidget( { Widget, WidgetNull } ) { + const keyMetricsWidgetHidden = useSelect( ( select ) => + select( CORE_USER ).isKeyMetricsWidgetHidden() + ); + + if ( keyMetricsWidgetHidden !== false ) { + return ; + } + + return ( + +
TODO: UI for TopTrafficSourceWidget
+
+ ); } + +TopTrafficSourceWidget.propTypes = { + Widget: PropTypes.elementType.isRequired, + WidgetNull: PropTypes.elementType.isRequired, +}; diff --git a/assets/js/modules/analytics-4/components/widgets/TopTrafficSourceWidget.test.js b/assets/js/modules/analytics-4/components/widgets/TopTrafficSourceWidget.test.js deleted file mode 100644 index 4cb452656e4..00000000000 --- a/assets/js/modules/analytics-4/components/widgets/TopTrafficSourceWidget.test.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * TopTrafficSourceWidget component tests. - * - * Site Kit by Google, Copyright 2023 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 TopTrafficSourceWidget from './TopTrafficSourceWidget'; - -describe( 'TopTrafficSourceWidget', () => { - it( 'should render the widget', () => { - const { getByText } = render( ); - - expect( - getByText( 'TODO: UI for TopTrafficSourceWidget' ) - ).toBeInTheDocument(); - } ); -} ); diff --git a/assets/js/modules/analytics-4/index.js b/assets/js/modules/analytics-4/index.js index 56cf6bc5f04..047d5610f94 100644 --- a/assets/js/modules/analytics-4/index.js +++ b/assets/js/modules/analytics-4/index.js @@ -34,6 +34,17 @@ import AnalyticsIcon from '../../../svg/graphics/analytics.svg'; import { MODULES_ANALYTICS_4 } from './datastore/constants'; import { AREA_MAIN_DASHBOARD_KEY_METRICS_PRIMARY } from '../../googlesitekit/widgets/default-areas'; import { isFeatureEnabled } from '../../features'; +import { + KM_ANALYTICS_ENGAGED_TRAFFIC_SOURCE, + KM_ANALYTICS_LOYAL_VISITORS, + KM_ANALYTICS_NEW_VISITORS, + KM_ANALYTICS_POPULAR_CONTENT, + KM_ANALYTICS_POPULAR_PRODUCTS, + KM_ANALYTICS_TOP_CITIES, + KM_ANALYTICS_TOP_CONVERTING_TRAFFIC_SOURCE, + KM_ANALYTICS_TOP_COUNTRIES, + KM_ANALYTICS_TOP_TRAFFIC_SOURCE, +} from '../../googlesitekit/datastore/user/constants'; export { registerStore } from './datastore'; @@ -50,7 +61,7 @@ export const registerWidgets = ( widgets ) => { * Key metrics widgets. */ widgets.registerWidget( - 'kmAnalyticsLoyalVisitors', + KM_ANALYTICS_LOYAL_VISITORS, { Component: LoyalVisitorsWidget, width: widgets.WIDGET_WIDTHS.QUARTER, @@ -62,7 +73,7 @@ export const registerWidgets = ( widgets ) => { ); widgets.registerWidget( - 'kmAnalyticsNewVisitors', + KM_ANALYTICS_NEW_VISITORS, { Component: NewVisitorsWidget, width: widgets.WIDGET_WIDTHS.QUARTER, @@ -74,7 +85,7 @@ export const registerWidgets = ( widgets ) => { ); widgets.registerWidget( - 'kmAnalyticsTopTrafficSource', + KM_ANALYTICS_TOP_TRAFFIC_SOURCE, { Component: TopTrafficSourceWidget, width: widgets.WIDGET_WIDTHS.QUARTER, @@ -86,7 +97,7 @@ export const registerWidgets = ( widgets ) => { ); widgets.registerWidget( - 'kmAnalyticsEngagedTrafficSource', + KM_ANALYTICS_ENGAGED_TRAFFIC_SOURCE, { Component: EngagedTrafficSourceWidget, width: widgets.WIDGET_WIDTHS.QUARTER, @@ -98,7 +109,7 @@ export const registerWidgets = ( widgets ) => { ); widgets.registerWidget( - 'kmAnalyticsPopularContent', + KM_ANALYTICS_POPULAR_CONTENT, { Component: PopularContentWidget, width: widgets.WIDGET_WIDTHS.QUARTER, @@ -110,7 +121,7 @@ export const registerWidgets = ( widgets ) => { ); widgets.registerWidget( - 'kmAnalyticsPopularProducts', + KM_ANALYTICS_POPULAR_PRODUCTS, { Component: PopularProductsWidget, width: widgets.WIDGET_WIDTHS.QUARTER, @@ -122,7 +133,7 @@ export const registerWidgets = ( widgets ) => { ); widgets.registerWidget( - 'kmAnalyticsTopCities', + KM_ANALYTICS_TOP_CITIES, { Component: TopCitiesWidget, width: widgets.WIDGET_WIDTHS.QUARTER, @@ -134,7 +145,7 @@ export const registerWidgets = ( widgets ) => { ); widgets.registerWidget( - 'kmAnalyticsTopCountries', + KM_ANALYTICS_TOP_COUNTRIES, { Component: TopCountriesWidget, width: widgets.WIDGET_WIDTHS.QUARTER, @@ -146,7 +157,7 @@ export const registerWidgets = ( widgets ) => { ); widgets.registerWidget( - 'kmTopConvertingTrafficSource', + KM_ANALYTICS_TOP_CONVERTING_TRAFFIC_SOURCE, { Component: TopConvertingTrafficSourceWidget, width: widgets.WIDGET_WIDTHS.QUARTER, diff --git a/assets/js/modules/search-console/components/widgets/PopularKeywordsWidget.js b/assets/js/modules/search-console/components/widgets/PopularKeywordsWidget.js index 07640785108..d9253490a39 100644 --- a/assets/js/modules/search-console/components/widgets/PopularKeywordsWidget.js +++ b/assets/js/modules/search-console/components/widgets/PopularKeywordsWidget.js @@ -16,6 +16,36 @@ * limitations under the License. */ -export default function PopularKeywordsWidget() { - return
TODO: UI for PopularKeywordsWidget
; +/** + * External dependencies + */ +import PropTypes from 'prop-types'; + +/** + * Internal dependencies + */ +import Data from 'googlesitekit-data'; +import { CORE_USER } from '../../../../googlesitekit/datastore/user/constants'; + +const { useSelect } = Data; + +export default function PopularKeywordsWidget( { Widget, WidgetNull } ) { + const keyMetricsWidgetHidden = useSelect( ( select ) => + select( CORE_USER ).isKeyMetricsWidgetHidden() + ); + + if ( keyMetricsWidgetHidden !== false ) { + return ; + } + + return ( + +
TODO: UI for PopularKeywordsWidget
+
+ ); } + +PopularKeywordsWidget.propTypes = { + Widget: PropTypes.elementType.isRequired, + WidgetNull: PropTypes.elementType.isRequired, +}; diff --git a/assets/js/modules/search-console/components/widgets/PopularKeywordsWidget.test.js b/assets/js/modules/search-console/components/widgets/PopularKeywordsWidget.test.js deleted file mode 100644 index 90714e56d04..00000000000 --- a/assets/js/modules/search-console/components/widgets/PopularKeywordsWidget.test.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * PopularKeywordsWidget component tests. - * - * Site Kit by Google, Copyright 2023 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 PopularKeywordsWidget from './PopularKeywordsWidget'; - -describe( 'PopularKeywordsWidget', () => { - it( 'should render the widget', () => { - const { getByText } = render( ); - - expect( - getByText( 'TODO: UI for PopularKeywordsWidget' ) - ).toBeInTheDocument(); - } ); -} ); diff --git a/assets/js/modules/search-console/index.js b/assets/js/modules/search-console/index.js index 31322e05f62..74137c23621 100644 --- a/assets/js/modules/search-console/index.js +++ b/assets/js/modules/search-console/index.js @@ -36,6 +36,7 @@ import PopularKeywordsWidget from './components/widgets/PopularKeywordsWidget'; import { isFeatureEnabled } from '../../features'; import { negateDefined } from '../../util/negate'; import { MODULES_ANALYTICS } from '../analytics/datastore/constants'; +import { KM_SEARCH_CONSOLE_POPULAR_KEYWORDS } from '../../googlesitekit/datastore/user/constants'; export { registerStore } from './datastore'; @@ -113,7 +114,7 @@ export const registerWidgets = ( widgets ) => { * Key metrics widgets. */ widgets.registerWidget( - 'kmSearchConsolePopularKeywords', + KM_SEARCH_CONSOLE_POPULAR_KEYWORDS, { Component: PopularKeywordsWidget, width: widgets.WIDGET_WIDTHS.QUARTER, diff --git a/includes/Core/Key_Metrics/Key_Metrics_Settings.php b/includes/Core/Key_Metrics/Key_Metrics_Settings.php index eb951c2c1fb..3055182718c 100644 --- a/includes/Core/Key_Metrics/Key_Metrics_Settings.php +++ b/includes/Core/Key_Metrics/Key_Metrics_Settings.php @@ -46,7 +46,10 @@ protected function get_type() { * @return array The default value. */ protected function get_default() { - return array(); + return array( + 'widgetSlugs' => array(), + 'isWidgetHidden' => false, + ); } /** @@ -91,7 +94,7 @@ protected function get_sanitize_callback() { $sanitized_settings = array(); - if ( isset( $settings['widgetSlugs'] ) && ! empty( $settings['widgetSlugs'] ) ) { + if ( isset( $settings['widgetSlugs'] ) ) { $sanitized_settings['widgetSlugs'] = Sanitize::sanitize_string_list( $settings['widgetSlugs'] ); } diff --git a/tests/js/utils.js b/tests/js/utils.js index 0896e3c1430..c7644c0fefc 100644 --- a/tests/js/utils.js +++ b/tests/js/utils.js @@ -401,6 +401,25 @@ export function provideTracking( registry, enabled = true ) { registry.dispatch( CORE_USER ).receiveGetTracking( { enabled } ); } +/** + * Provides key metrics settings data to the given registry. + * + * @since n.e.x.t + * + * @param {Object} registry The registry to set up. + * @param {Object} [extraData] Extra data to merge with the default settings. + */ +export const provideKeyMetrics = ( registry, extraData = {} ) => { + const defaults = { + widgetSlugs: [ 'test-slug' ], + isWidgetHidden: false, + }; + registry.dispatch( CORE_USER ).receiveGetKeyMetricsSettings( { + ...defaults, + ...extraData, + } ); +}; + /** * Mutes a fetch request to the given URL once. * diff --git a/tests/phpunit/integration/Core/Key_Metrics/Key_Metrics_SettingsTest.php b/tests/phpunit/integration/Core/Key_Metrics/Key_Metrics_SettingsTest.php index 88db0ef9657..5ddd4ffd1dd 100644 --- a/tests/phpunit/integration/Core/Key_Metrics/Key_Metrics_SettingsTest.php +++ b/tests/phpunit/integration/Core/Key_Metrics/Key_Metrics_SettingsTest.php @@ -39,7 +39,7 @@ public function set_up() { $this->key_metrics_settings->register(); } - public function data_answers() { + public function data_key_metrics_settings() { return array( 'empty by default' => array( null, @@ -58,7 +58,9 @@ public function data_answers() { 'widgetSlugs' => array(), 'isWidgetHidden' => null, ), - array(), + array( + 'widgetSlugs' => array(), + ), ), 'array of widgetSlugs with non-string elements' => array( array( 'widgetSlugs' => array( 'validWidgetSlug1', false, true, null, array(), 'validWidgetSlug2', '' ) ), @@ -118,7 +120,7 @@ public function data_answers() { } /** - * @dataProvider data_answers + * @dataProvider data_key_metrics_settings * * @param mixed $input Values to pass to the `set()` method. * @param array $expected The expected sanitized array.