dev: refactor core profiler pages (#38606)
* dev: refactor core-profiler - modularise each page - wrapped each page's pre, post, and main states into their top level states for tidiness - tagged them with id so that we can easily jump to them when doing routing - generalised component finder code such that it recursively traverses the state meta object until it finds a component key - fixed css label to use top level state key * moved initializing into introOptIn so it's not a special case by itself
This commit is contained in:
parent
532f3ca3f8
commit
1b1f86066f
|
@ -3,7 +3,7 @@
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import { createMachine, assign, DoneInvokeEvent, actions, spawn } from 'xstate';
|
import { createMachine, assign, DoneInvokeEvent, actions, spawn } from 'xstate';
|
||||||
import { useMachine } from '@xstate/react';
|
import { useMachine, useSelector } from '@xstate/react';
|
||||||
import { useEffect, useMemo } from '@wordpress/element';
|
import { useEffect, useMemo } from '@wordpress/element';
|
||||||
import { resolveSelect, dispatch } from '@wordpress/data';
|
import { resolveSelect, dispatch } from '@wordpress/data';
|
||||||
import {
|
import {
|
||||||
|
@ -48,6 +48,7 @@ import {
|
||||||
} from './services/installAndActivatePlugins';
|
} from './services/installAndActivatePlugins';
|
||||||
import { ProfileSpinner } from './components/profile-spinner/profile-spinner';
|
import { ProfileSpinner } from './components/profile-spinner/profile-spinner';
|
||||||
import recordTracksActions from './actions/tracks';
|
import recordTracksActions from './actions/tracks';
|
||||||
|
import { findComponentMeta } from './utils/find-component';
|
||||||
|
|
||||||
export type InitializationCompleteEvent = {
|
export type InitializationCompleteEvent = {
|
||||||
type: 'INITIALIZATION_COMPLETE';
|
type: 'INITIALIZATION_COMPLETE';
|
||||||
|
@ -458,7 +459,7 @@ const coreProfilerMachineServices = {
|
||||||
};
|
};
|
||||||
export const coreProfilerStateMachineDefinition = createMachine( {
|
export const coreProfilerStateMachineDefinition = createMachine( {
|
||||||
id: 'coreProfiler',
|
id: 'coreProfiler',
|
||||||
initial: 'initializing',
|
initial: 'introOptIn',
|
||||||
predictableActionArguments: true, // recommended setting: https://xstate.js.org/docs/guides/actions.html
|
predictableActionArguments: true, // recommended setting: https://xstate.js.org/docs/guides/actions.html
|
||||||
context: {
|
context: {
|
||||||
// these are safe default values if for some reason the steps fail to complete correctly
|
// these are safe default values if for some reason the steps fail to complete correctly
|
||||||
|
@ -480,7 +481,11 @@ export const coreProfilerStateMachineDefinition = createMachine( {
|
||||||
onboardingProfile: {} as OnboardingProfile,
|
onboardingProfile: {} as OnboardingProfile,
|
||||||
} as CoreProfilerStateMachineContext,
|
} as CoreProfilerStateMachineContext,
|
||||||
states: {
|
states: {
|
||||||
initializing: {
|
introOptIn: {
|
||||||
|
id: 'introOptIn',
|
||||||
|
initial: 'preIntroOptIn',
|
||||||
|
states: {
|
||||||
|
preIntroOptIn: {
|
||||||
entry: [
|
entry: [
|
||||||
// these prefetch tasks are spawned actors in the background and do not block progression of the state machine
|
// these prefetch tasks are spawned actors in the background and do not block progression of the state machine
|
||||||
'preFetchGetPlugins',
|
'preFetchGetPlugins',
|
||||||
|
@ -506,7 +511,9 @@ export const coreProfilerStateMachineDefinition = createMachine( {
|
||||||
src: 'getAllowTrackingOption',
|
src: 'getAllowTrackingOption',
|
||||||
onDone: [
|
onDone: [
|
||||||
{
|
{
|
||||||
actions: [ 'handleTrackingOption' ],
|
actions: [
|
||||||
|
'handleTrackingOption',
|
||||||
|
],
|
||||||
target: 'done',
|
target: 'done',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -523,7 +530,6 @@ export const coreProfilerStateMachineDefinition = createMachine( {
|
||||||
},
|
},
|
||||||
onDone: {
|
onDone: {
|
||||||
target: 'introOptIn',
|
target: 'introOptIn',
|
||||||
// TODO: at this point, we can handle the URL path param if any and jump to the correct page
|
|
||||||
},
|
},
|
||||||
meta: {
|
meta: {
|
||||||
progress: 0,
|
progress: 0,
|
||||||
|
@ -532,7 +538,7 @@ export const coreProfilerStateMachineDefinition = createMachine( {
|
||||||
introOptIn: {
|
introOptIn: {
|
||||||
on: {
|
on: {
|
||||||
INTRO_COMPLETED: {
|
INTRO_COMPLETED: {
|
||||||
target: 'preUserProfile',
|
target: '#userProfile',
|
||||||
actions: [
|
actions: [
|
||||||
'assignOptInDataSharing',
|
'assignOptInDataSharing',
|
||||||
'updateTrackingOption',
|
'updateTrackingOption',
|
||||||
|
@ -540,7 +546,7 @@ export const coreProfilerStateMachineDefinition = createMachine( {
|
||||||
},
|
},
|
||||||
INTRO_SKIPPED: {
|
INTRO_SKIPPED: {
|
||||||
// if the user skips the intro, we set the optInDataSharing to false and go to the Business Location page
|
// if the user skips the intro, we set the optInDataSharing to false and go to the Business Location page
|
||||||
target: 'preSkipFlowBusinessLocation',
|
target: '#skipGuidedSetup',
|
||||||
actions: [
|
actions: [
|
||||||
'assignOptInDataSharing',
|
'assignOptInDataSharing',
|
||||||
'updateTrackingOption',
|
'updateTrackingOption',
|
||||||
|
@ -548,7 +554,10 @@ export const coreProfilerStateMachineDefinition = createMachine( {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
entry: [
|
entry: [
|
||||||
{ type: 'recordTracksStepViewed', step: 'store_details' },
|
{
|
||||||
|
type: 'recordTracksStepViewed',
|
||||||
|
step: 'store_details',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
exit: actions.choose( [
|
exit: actions.choose( [
|
||||||
{
|
{
|
||||||
|
@ -557,7 +566,8 @@ export const coreProfilerStateMachineDefinition = createMachine( {
|
||||||
actions: 'recordTracksIntroCompleted',
|
actions: 'recordTracksIntroCompleted',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
cond: ( _context, event ) => event.type === 'INTRO_SKIPPED',
|
cond: ( _context, event ) =>
|
||||||
|
event.type === 'INTRO_SKIPPED',
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
type: 'recordTracksStepSkipped',
|
type: 'recordTracksStepSkipped',
|
||||||
|
@ -571,6 +581,12 @@ export const coreProfilerStateMachineDefinition = createMachine( {
|
||||||
component: IntroOptIn,
|
component: IntroOptIn,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
userProfile: {
|
||||||
|
id: 'userProfile',
|
||||||
|
initial: 'preUserProfile',
|
||||||
|
states: {
|
||||||
preUserProfile: {
|
preUserProfile: {
|
||||||
invoke: {
|
invoke: {
|
||||||
src: 'getOnboardingProfileOption',
|
src: 'getOnboardingProfileOption',
|
||||||
|
@ -589,8 +605,15 @@ export const coreProfilerStateMachineDefinition = createMachine( {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
userProfile: {
|
userProfile: {
|
||||||
|
meta: {
|
||||||
|
progress: 40,
|
||||||
|
component: UserProfile,
|
||||||
|
},
|
||||||
entry: [
|
entry: [
|
||||||
{ type: 'recordTracksStepViewed', step: 'user_profile' },
|
{
|
||||||
|
type: 'recordTracksStepViewed',
|
||||||
|
step: 'user_profile',
|
||||||
|
},
|
||||||
'preFetchGeolocation',
|
'preFetchGeolocation',
|
||||||
],
|
],
|
||||||
on: {
|
on: {
|
||||||
|
@ -598,8 +621,10 @@ export const coreProfilerStateMachineDefinition = createMachine( {
|
||||||
target: 'postUserProfile',
|
target: 'postUserProfile',
|
||||||
actions: [
|
actions: [
|
||||||
assign( {
|
assign( {
|
||||||
userProfile: ( context, event: UserProfileEvent ) =>
|
userProfile: (
|
||||||
event.payload.userProfile, // sets context.userProfile to the payload of the event
|
context,
|
||||||
|
event: UserProfileEvent
|
||||||
|
) => event.payload.userProfile, // sets context.userProfile to the payload of the event
|
||||||
} ),
|
} ),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -607,8 +632,10 @@ export const coreProfilerStateMachineDefinition = createMachine( {
|
||||||
target: 'postUserProfile',
|
target: 'postUserProfile',
|
||||||
actions: [
|
actions: [
|
||||||
assign( {
|
assign( {
|
||||||
userProfile: ( context, event: UserProfileEvent ) =>
|
userProfile: (
|
||||||
event.payload.userProfile, // assign context.userProfile to the payload of the event
|
context,
|
||||||
|
event: UserProfileEvent
|
||||||
|
) => event.payload.userProfile, // assign context.userProfile to the payload of the event
|
||||||
} ),
|
} ),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -630,10 +657,6 @@ export const coreProfilerStateMachineDefinition = createMachine( {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
] ),
|
] ),
|
||||||
meta: {
|
|
||||||
progress: 40,
|
|
||||||
component: UserProfile,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
postUserProfile: {
|
postUserProfile: {
|
||||||
invoke: {
|
invoke: {
|
||||||
|
@ -641,13 +664,19 @@ export const coreProfilerStateMachineDefinition = createMachine( {
|
||||||
return updateOnboardingProfileOption( context );
|
return updateOnboardingProfileOption( context );
|
||||||
},
|
},
|
||||||
onDone: {
|
onDone: {
|
||||||
target: 'preBusinessInfo',
|
target: '#businessInfo',
|
||||||
},
|
},
|
||||||
onError: {
|
onError: {
|
||||||
target: 'preBusinessInfo',
|
target: '#businessInfo',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
businessInfo: {
|
||||||
|
id: 'businessInfo',
|
||||||
|
initial: 'preBusinessInfo',
|
||||||
|
states: {
|
||||||
preBusinessInfo: {
|
preBusinessInfo: {
|
||||||
type: 'parallel',
|
type: 'parallel',
|
||||||
states: {
|
states: {
|
||||||
|
@ -690,7 +719,9 @@ export const coreProfilerStateMachineDefinition = createMachine( {
|
||||||
src: 'getStoreCountryOption',
|
src: 'getStoreCountryOption',
|
||||||
onDone: [
|
onDone: [
|
||||||
{
|
{
|
||||||
actions: [ 'handleStoreCountryOption' ],
|
actions: [
|
||||||
|
'handleStoreCountryOption',
|
||||||
|
],
|
||||||
target: 'done',
|
target: 'done',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -712,7 +743,9 @@ export const coreProfilerStateMachineDefinition = createMachine( {
|
||||||
src: 'getStoreNameOption',
|
src: 'getStoreNameOption',
|
||||||
onDone: [
|
onDone: [
|
||||||
{
|
{
|
||||||
actions: [ 'handleStoreNameOption' ],
|
actions: [
|
||||||
|
'handleStoreNameOption',
|
||||||
|
],
|
||||||
target: 'done',
|
target: 'done',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -748,28 +781,34 @@ export const coreProfilerStateMachineDefinition = createMachine( {
|
||||||
onDone: {
|
onDone: {
|
||||||
target: 'businessInfo',
|
target: 'businessInfo',
|
||||||
},
|
},
|
||||||
meta: {
|
|
||||||
progress: 50,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
businessInfo: {
|
businessInfo: {
|
||||||
|
meta: {
|
||||||
|
progress: 60,
|
||||||
|
component: BusinessInfo,
|
||||||
|
},
|
||||||
entry: [
|
entry: [
|
||||||
{ type: 'recordTracksStepViewed', step: 'business_info' },
|
{
|
||||||
|
type: 'recordTracksStepViewed',
|
||||||
|
step: 'business_info',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
on: {
|
on: {
|
||||||
BUSINESS_INFO_COMPLETED: {
|
BUSINESS_INFO_COMPLETED: {
|
||||||
target: 'prePlugins',
|
target: '#plugins',
|
||||||
actions: [
|
actions: [
|
||||||
'persistBusinessInfo',
|
'persistBusinessInfo',
|
||||||
'recordTracksBusinessInfoCompleted',
|
'recordTracksBusinessInfoCompleted',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
meta: {
|
|
||||||
progress: 60,
|
|
||||||
component: BusinessInfo,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
skipGuidedSetup: {
|
||||||
|
id: 'skipGuidedSetup',
|
||||||
|
initial: 'preSkipFlowBusinessLocation',
|
||||||
|
states: {
|
||||||
preSkipFlowBusinessLocation: {
|
preSkipFlowBusinessLocation: {
|
||||||
invoke: {
|
invoke: {
|
||||||
src: 'getCountries',
|
src: 'getCountries',
|
||||||
|
@ -791,12 +830,13 @@ export const coreProfilerStateMachineDefinition = createMachine( {
|
||||||
actions: [
|
actions: [
|
||||||
assign( {
|
assign( {
|
||||||
businessInfo: (
|
businessInfo: (
|
||||||
context,
|
_context,
|
||||||
event: BusinessLocationEvent
|
event: BusinessLocationEvent
|
||||||
) => {
|
) => {
|
||||||
return {
|
return {
|
||||||
...context.businessInfo,
|
..._context.businessInfo,
|
||||||
location: event.payload.storeLocation,
|
location:
|
||||||
|
event.payload.storeLocation,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
} ),
|
} ),
|
||||||
|
@ -873,13 +913,20 @@ export const coreProfilerStateMachineDefinition = createMachine( {
|
||||||
component: Loader,
|
component: Loader,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
id: 'plugins',
|
||||||
|
initial: 'prePlugins',
|
||||||
|
states: {
|
||||||
prePlugins: {
|
prePlugins: {
|
||||||
invoke: {
|
invoke: {
|
||||||
src: 'getPlugins',
|
src: 'getPlugins',
|
||||||
onDone: [
|
onDone: [
|
||||||
{
|
{
|
||||||
target: 'pluginsSkipped',
|
target: 'pluginsSkipped',
|
||||||
cond: ( context, event ) => event.data.length === 0,
|
cond: ( context, event ) =>
|
||||||
|
event.data.length === 0,
|
||||||
},
|
},
|
||||||
{ target: 'plugins', actions: 'handlePlugins' },
|
{ target: 'plugins', actions: 'handlePlugins' },
|
||||||
],
|
],
|
||||||
|
@ -887,7 +934,9 @@ export const coreProfilerStateMachineDefinition = createMachine( {
|
||||||
// add exit action to filter the extensions using a custom function here and assign it to context.extensionsAvailable
|
// add exit action to filter the extensions using a custom function here and assign it to context.extensionsAvailable
|
||||||
exit: assign( {
|
exit: assign( {
|
||||||
pluginsAvailable: ( context ) => {
|
pluginsAvailable: ( context ) => {
|
||||||
return context.pluginsAvailable.filter( () => true );
|
return context.pluginsAvailable.filter(
|
||||||
|
() => true
|
||||||
|
);
|
||||||
}, // TODO : define an extensible filter function here
|
}, // TODO : define an extensible filter function here
|
||||||
} ),
|
} ),
|
||||||
meta: {
|
meta: {
|
||||||
|
@ -902,7 +951,9 @@ export const coreProfilerStateMachineDefinition = createMachine( {
|
||||||
} ),
|
} ),
|
||||||
invoke: {
|
invoke: {
|
||||||
src: () => {
|
src: () => {
|
||||||
dispatch( ONBOARDING_STORE_NAME ).updateProfileItems( {
|
dispatch(
|
||||||
|
ONBOARDING_STORE_NAME
|
||||||
|
).updateProfileItems( {
|
||||||
plugins_page_skipped: true,
|
plugins_page_skipped: true,
|
||||||
completed: true,
|
completed: true,
|
||||||
} );
|
} );
|
||||||
|
@ -917,7 +968,9 @@ export const coreProfilerStateMachineDefinition = createMachine( {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: {
|
plugins: {
|
||||||
entry: [ { type: 'recordTracksStepViewed', step: 'plugins' } ],
|
entry: [
|
||||||
|
{ type: 'recordTracksStepViewed', step: 'plugins' },
|
||||||
|
],
|
||||||
on: {
|
on: {
|
||||||
PLUGINS_PAGE_SKIPPED: {
|
PLUGINS_PAGE_SKIPPED: {
|
||||||
actions: [
|
actions: [
|
||||||
|
@ -978,7 +1031,8 @@ export const coreProfilerStateMachineDefinition = createMachine( {
|
||||||
event: PluginInstalledAndActivatedEvent
|
event: PluginInstalledAndActivatedEvent
|
||||||
) => {
|
) => {
|
||||||
const progress = Math.round(
|
const progress = Math.round(
|
||||||
( event.payload.installedPluginIndex /
|
( event.payload
|
||||||
|
.installedPluginIndex /
|
||||||
event.payload.pluginsCount ) *
|
event.payload.pluginsCount ) *
|
||||||
100
|
100
|
||||||
);
|
);
|
||||||
|
@ -1004,17 +1058,24 @@ export const coreProfilerStateMachineDefinition = createMachine( {
|
||||||
target: 'prePlugins',
|
target: 'prePlugins',
|
||||||
actions: [
|
actions: [
|
||||||
assign( {
|
assign( {
|
||||||
pluginsInstallationErrors: ( _context, event ) =>
|
pluginsInstallationErrors: (
|
||||||
event.payload.errors,
|
_context,
|
||||||
|
event
|
||||||
|
) => event.payload.errors,
|
||||||
} ),
|
} ),
|
||||||
( _context, event ) => {
|
( _context, event ) => {
|
||||||
recordEvent(
|
recordEvent(
|
||||||
'storeprofiler_store_extensions_installed_and_activated',
|
'storeprofiler_store_extensions_installed_and_activated',
|
||||||
{
|
{
|
||||||
success: false,
|
success: false,
|
||||||
failed_extensions: event.payload.errors.map(
|
failed_extensions:
|
||||||
( error: PluginInstallError ) =>
|
event.payload.errors.map(
|
||||||
getPluginTrackKey( error.plugin )
|
(
|
||||||
|
error: PluginInstallError
|
||||||
|
) =>
|
||||||
|
getPluginTrackKey(
|
||||||
|
error.plugin
|
||||||
|
)
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -1026,7 +1087,8 @@ export const coreProfilerStateMachineDefinition = createMachine( {
|
||||||
actions: [
|
actions: [
|
||||||
( _context, event ) => {
|
( _context, event ) => {
|
||||||
const installationCompletedResult =
|
const installationCompletedResult =
|
||||||
event.payload.installationCompletedResult;
|
event.payload
|
||||||
|
.installationCompletedResult;
|
||||||
|
|
||||||
const trackData: {
|
const trackData: {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
|
@ -1041,7 +1103,9 @@ export const coreProfilerStateMachineDefinition = createMachine( {
|
||||||
success: true,
|
success: true,
|
||||||
installed_extensions:
|
installed_extensions:
|
||||||
installationCompletedResult.installedPlugins.map(
|
installationCompletedResult.installedPlugins.map(
|
||||||
( installedPlugin: InstalledPlugin ) =>
|
(
|
||||||
|
installedPlugin: InstalledPlugin
|
||||||
|
) =>
|
||||||
getPluginTrackKey(
|
getPluginTrackKey(
|
||||||
installedPlugin.plugin
|
installedPlugin.plugin
|
||||||
)
|
)
|
||||||
|
@ -1057,7 +1121,9 @@ export const coreProfilerStateMachineDefinition = createMachine( {
|
||||||
getPluginTrackKey(
|
getPluginTrackKey(
|
||||||
installedPlugin.plugin
|
installedPlugin.plugin
|
||||||
)
|
)
|
||||||
] = getTimeFrame( installedPlugin.installTime );
|
] = getTimeFrame(
|
||||||
|
installedPlugin.installTime
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
recordEvent(
|
recordEvent(
|
||||||
|
@ -1083,6 +1149,8 @@ export const coreProfilerStateMachineDefinition = createMachine( {
|
||||||
component: Loader,
|
component: Loader,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
settingUpStore: {},
|
settingUpStore: {},
|
||||||
},
|
},
|
||||||
} );
|
} );
|
||||||
|
@ -1108,20 +1176,24 @@ export const CoreProfilerController = ( {
|
||||||
} );
|
} );
|
||||||
}, [ actionOverrides, servicesOverrides ] );
|
}, [ actionOverrides, servicesOverrides ] );
|
||||||
|
|
||||||
const [ state, send ] = useMachine( augmentedStateMachine, {
|
const [ state, send, service ] = useMachine( augmentedStateMachine, {
|
||||||
devTools: process.env.NODE_ENV === 'development',
|
devTools: process.env.NODE_ENV === 'development',
|
||||||
} );
|
} );
|
||||||
const stateValue =
|
|
||||||
typeof state.value === 'object'
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- false positive due to function name match, this isn't from react std lib
|
||||||
? Object.keys( state.value )[ 0 ]
|
const currentNodeMeta = useSelector( service, ( currentState ) =>
|
||||||
: state.value;
|
findComponentMeta( currentState?.meta ?? undefined )
|
||||||
const currentNodeMeta = state.meta[ `coreProfiler.${ stateValue }` ]
|
);
|
||||||
? state.meta[ `coreProfiler.${ stateValue }` ]
|
|
||||||
: undefined;
|
const navigationProgress = currentNodeMeta?.progress;
|
||||||
const navigationProgress = currentNodeMeta?.progress; // This value is defined in each state node's meta tag, we can assume it is 0-100
|
|
||||||
const CurrentComponent =
|
const CurrentComponent =
|
||||||
currentNodeMeta?.component ?? ( () => <ProfileSpinner /> ); // If no component is defined for the state then its a loading state
|
currentNodeMeta?.component ?? ( () => <ProfileSpinner /> ); // If no component is defined for the state then its a loading state
|
||||||
|
|
||||||
|
const currentNodeCssLabel =
|
||||||
|
state.value instanceof Object
|
||||||
|
? Object.keys( state.value )[ 0 ]
|
||||||
|
: state.value;
|
||||||
|
|
||||||
useEffect( () => {
|
useEffect( () => {
|
||||||
document.body.classList.remove( 'woocommerce-admin-is-loading' );
|
document.body.classList.remove( 'woocommerce-admin-is-loading' );
|
||||||
document.body.classList.add( 'woocommerce-profile-wizard__body' );
|
document.body.classList.add( 'woocommerce-profile-wizard__body' );
|
||||||
|
@ -1139,7 +1211,7 @@ export const CoreProfilerController = ( {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
className={ `woocommerce-profile-wizard__container woocommerce-profile-wizard__step-${ state.value }` }
|
className={ `woocommerce-profile-wizard__container woocommerce-profile-wizard__step-${ currentNodeCssLabel }` }
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
<CurrentComponent
|
<CurrentComponent
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { useState, useEffect } from '@wordpress/element';
|
||||||
*/
|
*/
|
||||||
import { CoreProfilerStateMachineContext } from '..';
|
import { CoreProfilerStateMachineContext } from '..';
|
||||||
import ProgressBar from '../components/progress-bar/progress-bar';
|
import ProgressBar from '../components/progress-bar/progress-bar';
|
||||||
import { getLoaderStageMeta } from '../get-loader-stage-meta';
|
import { getLoaderStageMeta } from '../utils/get-loader-stage-meta';
|
||||||
|
|
||||||
export type Stage = {
|
export type Stage = {
|
||||||
title: string;
|
title: string;
|
||||||
|
|
|
@ -1897,7 +1897,7 @@ Object {
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="woocommerce-profile-wizard__container woocommerce-profile-wizard__step-skipFlowBusinessLocation"
|
class="woocommerce-profile-wizard__container woocommerce-profile-wizard__step-skipGuidedSetup"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="woocommerce-profiler-business-location"
|
class="woocommerce-profiler-business-location"
|
||||||
|
@ -2049,7 +2049,7 @@ Object {
|
||||||
</body>,
|
</body>,
|
||||||
"container": <div>
|
"container": <div>
|
||||||
<div
|
<div
|
||||||
class="woocommerce-profile-wizard__container woocommerce-profile-wizard__step-skipFlowBusinessLocation"
|
class="woocommerce-profile-wizard__container woocommerce-profile-wizard__step-skipGuidedSetup"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="woocommerce-profiler-business-location"
|
class="woocommerce-profiler-business-location"
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import { CoreProfilerStateMachineContext } from '..';
|
||||||
|
|
||||||
|
export type ComponentMeta = {
|
||||||
|
/** React component that is rendered when state matches the location this meta key is defined */
|
||||||
|
component: ( arg0: ComponentProps ) => JSX.Element;
|
||||||
|
/** number between 0 - 100 */
|
||||||
|
progress: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ComponentProps = {
|
||||||
|
navigationProgress: number | undefined;
|
||||||
|
sendEvent: unknown;
|
||||||
|
context: CoreProfilerStateMachineContext;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does a depth-first search of a meta object to find the first instance of a component.
|
||||||
|
*/
|
||||||
|
export function findComponentMeta(
|
||||||
|
obj: Record< string, unknown >
|
||||||
|
): ComponentMeta | undefined {
|
||||||
|
for ( const key in obj ) {
|
||||||
|
if ( key === 'component' ) {
|
||||||
|
return obj as ComponentMeta;
|
||||||
|
} else if ( typeof obj[ key ] === 'object' && obj[ key ] !== null ) {
|
||||||
|
const found = findComponentMeta(
|
||||||
|
obj[ key ] as Record< string, unknown >
|
||||||
|
);
|
||||||
|
if ( found !== undefined ) {
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
|
@ -6,11 +6,11 @@ import { __ } from '@wordpress/i18n';
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import LightBulbImage from './assets/images/loader-lightbulb.svg';
|
import LightBulbImage from '../assets/images/loader-lightbulb.svg';
|
||||||
import DevelopingImage from './assets/images/loader-developing.svg';
|
import DevelopingImage from '../assets/images/loader-developing.svg';
|
||||||
import LayoutImage from './assets/images/loader-layout.svg';
|
import LayoutImage from '../assets/images/loader-layout.svg';
|
||||||
|
|
||||||
import { Stages } from './pages/Loader';
|
import { Stages } from '../pages/Loader';
|
||||||
|
|
||||||
const LightbulbStage = {
|
const LightbulbStage = {
|
||||||
title: __( 'Turning on the lights', 'woocommerce' ),
|
title: __( 'Turning on the lights', 'woocommerce' ),
|
|
@ -0,0 +1,60 @@
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import { findComponentMeta, ComponentProps } from '../find-component';
|
||||||
|
|
||||||
|
describe( 'findComponentMeta', () => {
|
||||||
|
it( 'should return the whole object once "component" key is found in a nested object', () => {
|
||||||
|
const obj: Record< string, unknown > = {
|
||||||
|
'coreProfiler.skipGuidedSetup.skipFlowBusinessLocation': {
|
||||||
|
component: ( props: ComponentProps ) => (
|
||||||
|
<div>{ props.context }</div>
|
||||||
|
),
|
||||||
|
progress: 50,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = findComponentMeta( obj );
|
||||||
|
expect( result ).toEqual( {
|
||||||
|
component: expect.any( Function ),
|
||||||
|
progress: 50,
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
it( 'should return undefined if no "component" key is present', () => {
|
||||||
|
const obj: Record< string, unknown > = {
|
||||||
|
a: 1,
|
||||||
|
b: {
|
||||||
|
key: 'value',
|
||||||
|
},
|
||||||
|
c: 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = findComponentMeta( obj );
|
||||||
|
expect( result ).toBeUndefined();
|
||||||
|
} );
|
||||||
|
|
||||||
|
it( 'should handle deeply nested objects', () => {
|
||||||
|
const obj: Record< string, unknown > = {
|
||||||
|
a: 1,
|
||||||
|
b: {
|
||||||
|
key: 'value',
|
||||||
|
nested: {
|
||||||
|
anotherKey: 'anotherValue',
|
||||||
|
component: ( props: ComponentProps ) => (
|
||||||
|
<div>{ props.context }</div>
|
||||||
|
),
|
||||||
|
progress: 100,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
c: 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = findComponentMeta( obj );
|
||||||
|
expect( result ).toEqual( {
|
||||||
|
anotherKey: 'anotherValue',
|
||||||
|
component: expect.any( Function ),
|
||||||
|
progress: 100,
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
} );
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: patch
|
||||||
|
Type: dev
|
||||||
|
|
||||||
|
Refactored core profiler state machine by modularising each page
|
Loading…
Reference in New Issue