add: CYS task-completed intro flow (#40616)
* add: UI work for task completed flow * added spinner for intro page loading * add: save ai generated theme id to options * resolve rebase conflict * fixed tests
This commit is contained in:
parent
60b4502e40
commit
b436d40be3
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 5.4 MiB |
|
@ -1,3 +1,6 @@
|
|||
// @ts-expect-error -- No types for this exist yet.
|
||||
// eslint-disable-next-line @woocommerce/dependency-group
|
||||
import { store as coreStore } from '@wordpress/core-data';
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
|
@ -10,7 +13,8 @@ import {
|
|||
updateQueryString,
|
||||
} from '@woocommerce/navigation';
|
||||
import { OPTIONS_STORE_NAME } from '@woocommerce/data';
|
||||
import { dispatch } from '@wordpress/data';
|
||||
import { dispatch, resolveSelect } from '@wordpress/data';
|
||||
import { Spinner } from '@woocommerce/components';
|
||||
import { getAdminLink } from '@woocommerce/settings';
|
||||
|
||||
/**
|
||||
|
@ -71,8 +75,15 @@ const redirectToThemes = ( _context: customizeStoreStateMachineContext ) => {
|
|||
};
|
||||
|
||||
const markTaskComplete = async () => {
|
||||
const currentTemplate = await resolveSelect(
|
||||
coreStore
|
||||
// @ts-expect-error No types for this exist yet.
|
||||
).__experimentalGetTemplateForLink( '/' );
|
||||
return dispatch( OPTIONS_STORE_NAME ).updateOptions( {
|
||||
woocommerce_admin_customize_store_completed: 'yes',
|
||||
// we use this on the intro page to determine if this same theme was used in the last customization
|
||||
woocommerce_admin_customize_store_completed_theme_id:
|
||||
currentTemplate.id ?? undefined,
|
||||
} );
|
||||
};
|
||||
|
||||
|
@ -129,6 +140,7 @@ export const customizeStoreStateMachineDefinition = createMachine( {
|
|||
activeTheme: '',
|
||||
activeThemeHasMods: false,
|
||||
customizeStoreTaskCompleted: false,
|
||||
currentThemeIsAiGenerated: false,
|
||||
},
|
||||
} as customizeStoreStateMachineContext,
|
||||
invoke: {
|
||||
|
@ -196,6 +208,7 @@ export const customizeStoreStateMachineDefinition = createMachine( {
|
|||
'assignThemeData',
|
||||
'assignActiveThemeHasMods',
|
||||
'assignCustomizeStoreCompleted',
|
||||
'assignCurrentThemeIsAiGenerated',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
@ -365,7 +378,9 @@ export const CustomizeStoreController = ( {
|
|||
currentState={ state.value }
|
||||
/>
|
||||
) : (
|
||||
<div />
|
||||
<div className="woocommerce-customize-store__loading">
|
||||
<Spinner />
|
||||
</div>
|
||||
) }
|
||||
</div>
|
||||
</>
|
||||
|
|
|
@ -69,16 +69,15 @@ export const assignActiveThemeHasMods = assign<
|
|||
|
||||
export const assignCustomizeStoreCompleted = assign<
|
||||
customizeStoreStateMachineContext,
|
||||
customizeStoreStateMachineEvents // this is actually the wrong type for the event but I still don't know how to type this properly
|
||||
customizeStoreStateMachineEvents
|
||||
>( {
|
||||
intro: ( context, event ) => {
|
||||
const customizeStoreCompleted = (
|
||||
const customizeStoreTaskCompleted = (
|
||||
event as DoneInvokeEvent< {
|
||||
assignCustomizeStoreCompleted: boolean;
|
||||
customizeStoreTaskCompleted: boolean;
|
||||
} >
|
||||
).data.assignCustomizeStoreCompleted;
|
||||
// type coercion workaround for now
|
||||
return { ...context.intro, customizeStoreCompleted };
|
||||
).data.customizeStoreTaskCompleted;
|
||||
return { ...context.intro, customizeStoreTaskCompleted };
|
||||
},
|
||||
} );
|
||||
|
||||
|
@ -90,3 +89,17 @@ export const assignFetchIntroDataError = assign<
|
|||
return { ...context.intro, hasErrors: true };
|
||||
},
|
||||
} );
|
||||
|
||||
export const assignCurrentThemeIsAiGenerated = assign<
|
||||
customizeStoreStateMachineContext,
|
||||
customizeStoreStateMachineEvents
|
||||
>( {
|
||||
intro: ( context, event ) => {
|
||||
const currentThemeIsAiGenerated = (
|
||||
event as DoneInvokeEvent< {
|
||||
currentThemeIsAiGenerated: boolean;
|
||||
} >
|
||||
).data.currentThemeIsAiGenerated;
|
||||
return { ...context.intro, currentThemeIsAiGenerated };
|
||||
},
|
||||
} );
|
||||
|
|
|
@ -1,79 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { Button, Modal } from '@wordpress/components';
|
||||
import { Sender } from 'xstate';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { Link } from '@woocommerce/components';
|
||||
import { createInterpolateElement } from '@wordpress/element';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { customizeStoreStateMachineEvents } from '..';
|
||||
import { ADMIN_URL } from '~/utils/admin-settings';
|
||||
|
||||
export const DesignChangeWarningModal = ( {
|
||||
isOpen = false,
|
||||
setOpenDesignChangeWarningModal,
|
||||
sendEvent,
|
||||
classname = 'woocommerce-customize-store__design-change-warning-modal',
|
||||
}: {
|
||||
isOpen?: boolean;
|
||||
setOpenDesignChangeWarningModal: ( arg0: boolean ) => void;
|
||||
sendEvent: Sender< customizeStoreStateMachineEvents >;
|
||||
classname?: string;
|
||||
} ) => {
|
||||
if ( ! isOpen ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
className={ classname }
|
||||
title={ __(
|
||||
'Are you sure you want to start a new design?',
|
||||
'woocommerce'
|
||||
) }
|
||||
onRequestClose={ () => setOpenDesignChangeWarningModal( false ) }
|
||||
shouldCloseOnClickOutside={ false }
|
||||
>
|
||||
<p>
|
||||
{ createInterpolateElement(
|
||||
__(
|
||||
"The [AI designer*] will create a new store design for you, and you'll lose any changes you've made to your active theme. If you'd prefer to continue editing your theme, you can do so via the <EditorLink>Editor</EditorLink>.",
|
||||
'woocommerce'
|
||||
),
|
||||
{
|
||||
EditorLink: (
|
||||
<Link
|
||||
onClick={ () => {
|
||||
window.open(
|
||||
`${ ADMIN_URL }site-editor.php`,
|
||||
'_blank'
|
||||
);
|
||||
return false;
|
||||
} }
|
||||
href=""
|
||||
/>
|
||||
),
|
||||
}
|
||||
) }
|
||||
</p>
|
||||
<div className="woocommerce-customize-store__design-change-warning-modal-footer">
|
||||
<Button
|
||||
onClick={ () => setOpenDesignChangeWarningModal( false ) }
|
||||
variant="link"
|
||||
>
|
||||
{ __( 'Cancel', 'woocommerce' ) }
|
||||
</Button>
|
||||
<Button
|
||||
onClick={ () => sendEvent( { type: 'DESIGN_WITH_AI' } ) }
|
||||
variant="primary"
|
||||
>
|
||||
{ __( 'Design with AI', 'woocommerce' ) }
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
|
@ -16,7 +16,11 @@ import {
|
|||
import { CustomizeStoreComponent } from '../types';
|
||||
import { SiteHub } from '../assembler-hub/site-hub';
|
||||
import { ThemeCard } from './theme-card';
|
||||
import { DesignChangeWarningModal } from './design-change-warning-modal';
|
||||
import {
|
||||
DesignChangeWarningModal,
|
||||
StartNewDesignWarningModal,
|
||||
StartOverWarningModal,
|
||||
} from './warning-modals';
|
||||
import { useNetworkStatus } from '~/utils/react-hooks/use-network-status';
|
||||
import './intro.scss';
|
||||
import {
|
||||
|
@ -24,6 +28,8 @@ import {
|
|||
ThemeHasModsBanner,
|
||||
JetpackOfflineBanner,
|
||||
DefaultBanner,
|
||||
ExistingAiThemeBanner,
|
||||
ExistingThemeBanner,
|
||||
} from './intro-banners';
|
||||
|
||||
export type events =
|
||||
|
@ -43,22 +49,28 @@ const BANNER_COMPONENTS = {
|
|||
'network-offline': NetworkOfflineBanner,
|
||||
'task-incomplete-active-theme-has-mods': ThemeHasModsBanner,
|
||||
'jetpack-offline': JetpackOfflineBanner,
|
||||
'existing-ai-theme': DefaultBanner,
|
||||
'existing-ai-theme': ExistingAiThemeBanner,
|
||||
'existing-theme': ExistingThemeBanner,
|
||||
default: DefaultBanner,
|
||||
};
|
||||
|
||||
const MODAL_COMPONENTS = {
|
||||
'no-modal': null,
|
||||
'task-incomplete-override-design-changes': DesignChangeWarningModal,
|
||||
'task-complete-with-ai-theme': null,
|
||||
'task-complete-without-ai-theme': null,
|
||||
'task-complete-with-ai-theme': StartOverWarningModal,
|
||||
'task-complete-without-ai-theme': StartNewDesignWarningModal,
|
||||
};
|
||||
|
||||
type ModalStatus = keyof typeof MODAL_COMPONENTS;
|
||||
|
||||
export const Intro: CustomizeStoreComponent = ( { sendEvent, context } ) => {
|
||||
const {
|
||||
intro: { themeData, activeThemeHasMods, customizeStoreTaskCompleted },
|
||||
intro: {
|
||||
themeData,
|
||||
activeThemeHasMods,
|
||||
customizeStoreTaskCompleted,
|
||||
currentThemeIsAiGenerated,
|
||||
},
|
||||
} = context;
|
||||
|
||||
const isJetpackOffline = false;
|
||||
|
@ -78,12 +90,15 @@ export const Intro: CustomizeStoreComponent = ( { sendEvent, context } ) => {
|
|||
case isJetpackOffline as boolean:
|
||||
bannerStatus = 'jetpack-offline';
|
||||
break;
|
||||
case activeThemeHasMods && ! customizeStoreTaskCompleted:
|
||||
case ! customizeStoreTaskCompleted && activeThemeHasMods:
|
||||
bannerStatus = 'task-incomplete-active-theme-has-mods';
|
||||
break;
|
||||
case context.intro.currentThemeIsAiGenerated:
|
||||
case customizeStoreTaskCompleted && currentThemeIsAiGenerated:
|
||||
bannerStatus = 'existing-ai-theme';
|
||||
break;
|
||||
case customizeStoreTaskCompleted && ! currentThemeIsAiGenerated:
|
||||
bannerStatus = 'existing-theme';
|
||||
break;
|
||||
}
|
||||
|
||||
switch ( true ) {
|
||||
|
@ -93,6 +108,12 @@ export const Intro: CustomizeStoreComponent = ( { sendEvent, context } ) => {
|
|||
case bannerStatus === 'task-incomplete-active-theme-has-mods':
|
||||
modalStatus = 'task-incomplete-override-design-changes';
|
||||
break;
|
||||
case bannerStatus === 'existing-ai-theme':
|
||||
modalStatus = 'task-complete-with-ai-theme';
|
||||
break;
|
||||
case bannerStatus === 'existing-theme':
|
||||
modalStatus = 'task-complete-without-ai-theme';
|
||||
break;
|
||||
}
|
||||
|
||||
const ModalComponent = MODAL_COMPONENTS[ modalStatus ];
|
||||
|
@ -103,7 +124,6 @@ export const Intro: CustomizeStoreComponent = ( { sendEvent, context } ) => {
|
|||
<>
|
||||
{ ModalComponent && (
|
||||
<ModalComponent
|
||||
isOpen={ openDesignChangeWarningModal }
|
||||
sendEvent={ sendEvent }
|
||||
setOpenDesignChangeWarningModal={
|
||||
setOpenDesignChangeWarningModal
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
import { __ } from '@wordpress/i18n';
|
||||
import classNames from 'classnames';
|
||||
import { Button } from '@wordpress/components';
|
||||
import { getNewPath } from '@woocommerce/navigation';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -17,6 +18,7 @@ export const BaseIntroBanner = ( {
|
|||
buttonIsLink,
|
||||
bannerButtonOnClick,
|
||||
bannerButtonText,
|
||||
children,
|
||||
}: {
|
||||
bannerTitle: string;
|
||||
bannerText: string;
|
||||
|
@ -24,6 +26,7 @@ export const BaseIntroBanner = ( {
|
|||
buttonIsLink: boolean;
|
||||
bannerButtonOnClick: () => void;
|
||||
bannerButtonText: string;
|
||||
children?: React.ReactNode;
|
||||
} ) => {
|
||||
return (
|
||||
<div
|
||||
|
@ -41,6 +44,7 @@ export const BaseIntroBanner = ( {
|
|||
>
|
||||
{ bannerButtonText }
|
||||
</Button>
|
||||
{ children }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -92,6 +96,31 @@ export const JetpackOfflineBanner = ( {
|
|||
);
|
||||
};
|
||||
|
||||
export const ExistingThemeBanner = ( {
|
||||
setOpenDesignChangeWarningModal,
|
||||
}: {
|
||||
setOpenDesignChangeWarningModal: ( arg0: boolean ) => void;
|
||||
} ) => {
|
||||
return (
|
||||
<BaseIntroBanner
|
||||
bannerTitle={ __(
|
||||
'Use the power of AI to design your store',
|
||||
'woocommerce'
|
||||
) }
|
||||
bannerText={ __(
|
||||
'Design the look of your store, create pages, and generate copy using our built-in AI tools.',
|
||||
'woocommerce'
|
||||
) }
|
||||
bannerClass=""
|
||||
buttonIsLink={ false }
|
||||
bannerButtonOnClick={ () => {
|
||||
setOpenDesignChangeWarningModal( true );
|
||||
} }
|
||||
bannerButtonText={ __( 'Design with AI', 'woocommerce' ) }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const DefaultBanner = ( {
|
||||
sendEvent,
|
||||
}: {
|
||||
|
@ -143,3 +172,39 @@ export const ThemeHasModsBanner = ( {
|
|||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const ExistingAiThemeBanner = ( {
|
||||
setOpenDesignChangeWarningModal,
|
||||
}: {
|
||||
setOpenDesignChangeWarningModal: ( arg0: boolean ) => void;
|
||||
} ) => {
|
||||
return (
|
||||
<BaseIntroBanner
|
||||
bannerTitle={ __( 'Customize your custom theme', 'woocommerce' ) }
|
||||
bannerText={ __(
|
||||
'Keep customizing the look of your AI-generated store, or start over and create a new one.',
|
||||
'woocommerce'
|
||||
) }
|
||||
bannerClass="existing-ai-theme-banner"
|
||||
buttonIsLink={ false }
|
||||
bannerButtonOnClick={ () => {
|
||||
window.location.href = getNewPath(
|
||||
{},
|
||||
'/customize-store/assembler-hub',
|
||||
{}
|
||||
);
|
||||
} }
|
||||
bannerButtonText={ __( 'Customize', 'woocommerce' ) }
|
||||
>
|
||||
<Button
|
||||
className=""
|
||||
onClick={ () => {
|
||||
setOpenDesignChangeWarningModal( true );
|
||||
} }
|
||||
variant={ 'secondary' }
|
||||
>
|
||||
{ __( 'Create a new one', 'woocommerce' ) }
|
||||
</Button>
|
||||
</BaseIntroBanner>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -113,6 +113,13 @@
|
|||
background-position-y: 29px;
|
||||
}
|
||||
|
||||
&.existing-ai-theme-banner {
|
||||
background: rgba(246, 247, 247, 1) url(../assets/images/intro-banner-existing-ai.svg) no-repeat center right;
|
||||
background-size: auto 218px;
|
||||
background-position-y: 29px;
|
||||
background-position-x: 91%;
|
||||
}
|
||||
|
||||
.woocommerce-customize-store-banner-content {
|
||||
width: 375px;
|
||||
margin-left: 50px;
|
||||
|
@ -123,6 +130,10 @@
|
|||
font-weight: 500;
|
||||
}
|
||||
|
||||
button.components-button + button.components-button { // add left margin for all buttons with another button to its left
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.25rem;
|
||||
line-height: 23.87px;
|
||||
|
@ -236,7 +247,6 @@
|
|||
text-decoration: none !important;
|
||||
}
|
||||
h1 {
|
||||
width: 320px;
|
||||
line-height: 28px;
|
||||
font-size: 20px;
|
||||
color: #1e1e1e;
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
/* eslint-disable @woocommerce/dependency-group */
|
||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
// @ts-expect-error -- No types for this exist yet.
|
||||
// eslint-disable-next-line @woocommerce/dependency-group
|
||||
import { store as coreStore } from '@wordpress/core-data';
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { resolveSelect } from '@wordpress/data';
|
||||
import { ONBOARDING_STORE_NAME } from '@woocommerce/data';
|
||||
// @ts-ignore No types for this exist yet.
|
||||
import { store as coreStore } from '@wordpress/core-data';
|
||||
import { ONBOARDING_STORE_NAME, OPTIONS_STORE_NAME } from '@woocommerce/data';
|
||||
import apiFetch from '@wordpress/api-fetch';
|
||||
|
||||
// placeholder xstate async service that returns a set of theme cards
|
||||
export const fetchThemeCards = async () => {
|
||||
const themes = await apiFetch( {
|
||||
path: '/wc-admin/onboarding/themes/recommended',
|
||||
|
@ -20,10 +18,21 @@ export const fetchThemeCards = async () => {
|
|||
};
|
||||
|
||||
export const fetchIntroData = async () => {
|
||||
let currentThemeIsAiGenerated = false;
|
||||
const currentTemplate = await resolveSelect(
|
||||
coreStore
|
||||
// @ts-expect-error No types for this exist yet.
|
||||
).__experimentalGetTemplateForLink( '/' );
|
||||
const maybePreviousTemplate = await resolveSelect(
|
||||
OPTIONS_STORE_NAME
|
||||
).getOption( 'woocommerce_admin_customize_store_completed_theme_id' );
|
||||
|
||||
if (
|
||||
maybePreviousTemplate &&
|
||||
currentTemplate?.id === maybePreviousTemplate
|
||||
) {
|
||||
currentThemeIsAiGenerated = true;
|
||||
}
|
||||
|
||||
const styleRevs = await resolveSelect(
|
||||
coreStore
|
||||
|
@ -57,5 +66,6 @@ export const fetchIntroData = async () => {
|
|||
activeThemeHasMods,
|
||||
customizeStoreTaskCompleted,
|
||||
themeData,
|
||||
currentThemeIsAiGenerated,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -93,4 +93,37 @@ describe( 'Intro Banners', () => {
|
|||
type: 'DESIGN_WITH_AI',
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'should display the existing ai theme banner when customizeStoreTaskCompleted and currentThemeIsAiGenerated', () => {
|
||||
const sendEventMock = jest.fn();
|
||||
( useNetworkStatus as jest.Mock ).mockImplementation( () => false );
|
||||
render(
|
||||
<Intro
|
||||
sendEvent={ sendEventMock }
|
||||
context={ {
|
||||
intro: {
|
||||
hasErrors: false,
|
||||
activeTheme: '',
|
||||
themeData: {
|
||||
themes: [],
|
||||
_links: {
|
||||
browse_all: {
|
||||
href: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
activeThemeHasMods: false,
|
||||
customizeStoreTaskCompleted: true,
|
||||
currentThemeIsAiGenerated: true,
|
||||
},
|
||||
themeConfiguration: {},
|
||||
} }
|
||||
currentState={ 'intro' }
|
||||
parentMachine={ null as unknown as AnyInterpreter }
|
||||
/>
|
||||
);
|
||||
expect(
|
||||
screen.getByText( /Customize your custom theme/i )
|
||||
).toBeInTheDocument();
|
||||
} );
|
||||
} );
|
||||
|
|
|
@ -67,4 +67,104 @@ describe( 'Intro Modals', () => {
|
|||
type: 'DESIGN_WITH_AI',
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'should display StartOverWarningModal when customizeStoreTaskCompleted and currentThemeIsAiGenerated and button is clicked', async () => {
|
||||
const sendEventMock = jest.fn();
|
||||
render(
|
||||
<Intro
|
||||
sendEvent={ sendEventMock }
|
||||
context={ {
|
||||
intro: {
|
||||
hasErrors: false,
|
||||
activeTheme: '',
|
||||
themeData: {
|
||||
themes: [],
|
||||
_links: {
|
||||
browse_all: {
|
||||
href: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
activeThemeHasMods: false,
|
||||
customizeStoreTaskCompleted: true,
|
||||
currentThemeIsAiGenerated: true,
|
||||
},
|
||||
themeConfiguration: {},
|
||||
} }
|
||||
currentState={ 'intro' }
|
||||
parentMachine={ null as unknown as AnyInterpreter }
|
||||
/>
|
||||
);
|
||||
|
||||
const bannerButton = screen.getByRole( 'button', {
|
||||
name: /Create a new one/i,
|
||||
} );
|
||||
fireEvent.click( bannerButton );
|
||||
|
||||
await waitFor( () => {
|
||||
expect(
|
||||
screen.getByText( /Are you sure you want to start over?/i )
|
||||
).toBeInTheDocument();
|
||||
} );
|
||||
|
||||
const modalButton = screen.getByRole( 'button', {
|
||||
name: /Start again/i,
|
||||
} );
|
||||
fireEvent.click( modalButton );
|
||||
|
||||
expect( sendEventMock ).toHaveBeenCalledWith( {
|
||||
type: 'DESIGN_WITH_AI',
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'should display StartNewDesignWarningModal when customizeStoreTaskCompleted and not currentThemeIsAiGenerated and button is clicked', async () => {
|
||||
const sendEventMock = jest.fn();
|
||||
render(
|
||||
<Intro
|
||||
sendEvent={ sendEventMock }
|
||||
context={ {
|
||||
intro: {
|
||||
hasErrors: false,
|
||||
activeTheme: '',
|
||||
themeData: {
|
||||
themes: [],
|
||||
_links: {
|
||||
browse_all: {
|
||||
href: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
activeThemeHasMods: false,
|
||||
customizeStoreTaskCompleted: true,
|
||||
currentThemeIsAiGenerated: false,
|
||||
},
|
||||
themeConfiguration: {},
|
||||
} }
|
||||
currentState={ 'intro' }
|
||||
parentMachine={ null as unknown as AnyInterpreter }
|
||||
/>
|
||||
);
|
||||
|
||||
const bannerButton = screen.getByRole( 'button', {
|
||||
name: /Design with AI/i,
|
||||
} );
|
||||
fireEvent.click( bannerButton );
|
||||
|
||||
await waitFor( () => {
|
||||
expect(
|
||||
screen.getByText(
|
||||
/Are you sure you want to start a new design?/i
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
} );
|
||||
|
||||
const modalButton = screen.getByRole( 'button', {
|
||||
name: /Design with AI/i,
|
||||
} );
|
||||
fireEvent.click( modalButton );
|
||||
|
||||
expect( sendEventMock ).toHaveBeenCalledWith( {
|
||||
type: 'DESIGN_WITH_AI',
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
|
|
|
@ -0,0 +1,191 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { Button, Modal } from '@wordpress/components';
|
||||
import { Sender } from 'xstate';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { Link } from '@woocommerce/components';
|
||||
import { createInterpolateElement } from '@wordpress/element';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { customizeStoreStateMachineEvents } from '..';
|
||||
import { ADMIN_URL } from '~/utils/admin-settings';
|
||||
|
||||
export const DesignChangeWarningModal = ( {
|
||||
setOpenDesignChangeWarningModal,
|
||||
sendEvent,
|
||||
classname = 'woocommerce-customize-store__design-change-warning-modal',
|
||||
}: {
|
||||
setOpenDesignChangeWarningModal: ( arg0: boolean ) => void;
|
||||
sendEvent: Sender< customizeStoreStateMachineEvents >;
|
||||
classname?: string;
|
||||
} ) => {
|
||||
return (
|
||||
<Modal
|
||||
className={ classname }
|
||||
title={ __(
|
||||
'Are you sure you want to start a new design?',
|
||||
'woocommerce'
|
||||
) }
|
||||
onRequestClose={ () => setOpenDesignChangeWarningModal( false ) }
|
||||
shouldCloseOnClickOutside={ false }
|
||||
>
|
||||
<p>
|
||||
{ createInterpolateElement(
|
||||
__(
|
||||
"The [AI designer*] will create a new store design for you, and you'll lose any changes you've made to your active theme. If you'd prefer to continue editing your theme, you can do so via the <EditorLink>Editor</EditorLink>.",
|
||||
'woocommerce'
|
||||
),
|
||||
{
|
||||
EditorLink: (
|
||||
<Link
|
||||
onClick={ () => {
|
||||
window.open(
|
||||
`${ ADMIN_URL }site-editor.php`,
|
||||
'_blank'
|
||||
);
|
||||
return false;
|
||||
} }
|
||||
href=""
|
||||
/>
|
||||
),
|
||||
}
|
||||
) }
|
||||
</p>
|
||||
<div className="woocommerce-customize-store__design-change-warning-modal-footer">
|
||||
<Button
|
||||
onClick={ () => setOpenDesignChangeWarningModal( false ) }
|
||||
variant="link"
|
||||
>
|
||||
{ __( 'Cancel', 'woocommerce' ) }
|
||||
</Button>
|
||||
<Button
|
||||
onClick={ () => sendEvent( { type: 'DESIGN_WITH_AI' } ) }
|
||||
variant="primary"
|
||||
>
|
||||
{ __( 'Design with AI', 'woocommerce' ) }
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export const StartNewDesignWarningModal = ( {
|
||||
setOpenDesignChangeWarningModal,
|
||||
sendEvent,
|
||||
classname = 'woocommerce-customize-store__design-change-warning-modal',
|
||||
}: {
|
||||
setOpenDesignChangeWarningModal: ( arg0: boolean ) => void;
|
||||
sendEvent: Sender< customizeStoreStateMachineEvents >;
|
||||
classname?: string;
|
||||
} ) => {
|
||||
return (
|
||||
<Modal
|
||||
className={ classname }
|
||||
title={ __(
|
||||
'Are you sure you want to start a new design?',
|
||||
'woocommerce'
|
||||
) }
|
||||
onRequestClose={ () => setOpenDesignChangeWarningModal( false ) }
|
||||
shouldCloseOnClickOutside={ false }
|
||||
>
|
||||
<p>
|
||||
{ createInterpolateElement(
|
||||
__(
|
||||
"The [AI designer*] will create a new store design for you, and you'll lose any changes you've made to your active theme. If you'd prefer to continue editing your theme, you can do so via the <EditorLink>Editor</EditorLink>.",
|
||||
'woocommerce'
|
||||
),
|
||||
{
|
||||
EditorLink: (
|
||||
<Link
|
||||
onClick={ () => {
|
||||
window.open(
|
||||
`${ ADMIN_URL }site-editor.php`,
|
||||
'_blank'
|
||||
);
|
||||
return false;
|
||||
} }
|
||||
href=""
|
||||
/>
|
||||
),
|
||||
}
|
||||
) }
|
||||
</p>
|
||||
<div className="woocommerce-customize-store__design-change-warning-modal-footer">
|
||||
<Button
|
||||
onClick={ () => setOpenDesignChangeWarningModal( false ) }
|
||||
variant="link"
|
||||
>
|
||||
{ __( 'Cancel', 'woocommerce' ) }
|
||||
</Button>
|
||||
<Button
|
||||
onClick={ () => sendEvent( { type: 'DESIGN_WITH_AI' } ) }
|
||||
variant="primary"
|
||||
>
|
||||
{ __( 'Design with AI', 'woocommerce' ) }
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export const StartOverWarningModal = ( {
|
||||
setOpenDesignChangeWarningModal,
|
||||
sendEvent,
|
||||
classname = 'woocommerce-customize-store__design-change-warning-modal',
|
||||
}: {
|
||||
setOpenDesignChangeWarningModal: ( arg0: boolean ) => void;
|
||||
sendEvent: Sender< customizeStoreStateMachineEvents >;
|
||||
classname?: string;
|
||||
} ) => {
|
||||
return (
|
||||
<Modal
|
||||
className={ classname }
|
||||
title={ __(
|
||||
'Are you sure you want to start over?',
|
||||
'woocommerce'
|
||||
) }
|
||||
onRequestClose={ () => setOpenDesignChangeWarningModal( false ) }
|
||||
shouldCloseOnClickOutside={ false }
|
||||
>
|
||||
<p>
|
||||
{ createInterpolateElement(
|
||||
__(
|
||||
"You'll be asked to provide your business info again, and will lose your existing AI design. If you want to customize your existing design, you can do so via the <EditorLink>Editor</EditorLink>.",
|
||||
'woocommerce'
|
||||
),
|
||||
{
|
||||
EditorLink: (
|
||||
<Link
|
||||
onClick={ () => {
|
||||
window.open(
|
||||
`${ ADMIN_URL }site-editor.php`,
|
||||
'_blank'
|
||||
);
|
||||
return false;
|
||||
} }
|
||||
href=""
|
||||
/>
|
||||
),
|
||||
}
|
||||
) }
|
||||
</p>
|
||||
<div className="woocommerce-customize-store__design-change-warning-modal-footer">
|
||||
<Button
|
||||
onClick={ () => setOpenDesignChangeWarningModal( false ) }
|
||||
variant="link"
|
||||
>
|
||||
{ __( 'Cancel', 'woocommerce' ) }
|
||||
</Button>
|
||||
<Button
|
||||
onClick={ () => sendEvent( { type: 'DESIGN_WITH_AI' } ) }
|
||||
variant="primary"
|
||||
>
|
||||
{ __( 'Start again', 'woocommerce' ) }
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
|
@ -1,7 +1,13 @@
|
|||
.woocommerce-layout .woocommerce-layout__main {
|
||||
@include breakpoint( '<782px' ) {
|
||||
@include breakpoint('<782px') {
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
|
||||
.woocommerce-customize-store__loading {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
height: 100vh;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-customize-store {
|
||||
|
@ -71,7 +77,6 @@ body.woocommerce-customize-store.js.is-fullscreen-mode {
|
|||
}
|
||||
|
||||
.woocommerce-cys-design-with-ai {
|
||||
|
||||
.components-base-control {
|
||||
width: 404px;
|
||||
textarea {
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: add
|
||||
|
||||
Save ai generated theme ID to options and use it to determine if the intro page should warn about existing AI theme
|
Loading…
Reference in New Issue