Add customize store transitional screen (#40122)
* Add CYS transitional screen * Add changefile(s) from automation for the following project(s): woocommerce * Add tests * Update preview loading style and add requeue param * Fix visible logic * Fix img size * Update comments * Fix lint * Pre-fetch image and wait a 5s before redirecting to transitional page after clicking on done button * Remove unneed overflow * Move pre-fetch logic to xstate and use spinner for button loading state --------- Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
parent
95d7a6b86d
commit
80eaece265
|
@ -4,11 +4,15 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { useContext, useEffect } from '@wordpress/element';
|
||||
import { useContext, useEffect, useState } from '@wordpress/element';
|
||||
import { useQuery } from '@woocommerce/navigation';
|
||||
import { useSelect, useDispatch } from '@wordpress/data';
|
||||
// @ts-ignore No types for this exist yet.
|
||||
import { Button, __experimentalHStack as HStack } from '@wordpress/components';
|
||||
import {
|
||||
// @ts-ignore No types for this exist yet.
|
||||
__experimentalHStack as HStack,
|
||||
Button,
|
||||
Spinner,
|
||||
} from '@wordpress/components';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
// @ts-ignore No types for this exist yet.
|
||||
import { store as coreStore } from '@wordpress/core-data';
|
||||
|
@ -35,6 +39,7 @@ export const SaveHub = () => {
|
|||
const saveNoticeId = 'site-edit-save-notice';
|
||||
const urlParams = useQuery();
|
||||
const { sendEvent } = useContext( CustomizeStoreContext );
|
||||
const [ isResolving, setIsResolving ] = useState< boolean >( false );
|
||||
|
||||
// @ts-ignore No types for this exist yet.
|
||||
const { __unstableMarkLastChangeAsPersistent } =
|
||||
|
@ -180,13 +185,14 @@ export const SaveHub = () => {
|
|||
<Button
|
||||
variant="primary"
|
||||
onClick={ () => {
|
||||
setIsResolving( true );
|
||||
sendEvent( 'FINISH_CUSTOMIZATION' );
|
||||
} }
|
||||
className="edit-site-save-hub__button"
|
||||
// @ts-ignore No types for this exist yet.
|
||||
__next40pxDefaultSize
|
||||
>
|
||||
{ __( 'Done', 'woocommerce' ) }
|
||||
{ isResolving ? <Spinner /> : __( 'Done', 'woocommerce' ) }
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,11 @@ import {
|
|||
} from './intro';
|
||||
import { DesignWithAi, events as designWithAiEvents } from './design-with-ai';
|
||||
import { AssemblerHub, events as assemblerHubEvents } from './assembler-hub';
|
||||
import {
|
||||
Transitional,
|
||||
events as transitionalEvents,
|
||||
services as transitionalServices,
|
||||
} from './transitional';
|
||||
import { findComponentMeta } from '~/utils/xstate/find-component';
|
||||
import {
|
||||
CustomizeStoreComponentMeta,
|
||||
|
@ -31,6 +36,7 @@ export type customizeStoreStateMachineEvents =
|
|||
| introEvents
|
||||
| designWithAiEvents
|
||||
| assemblerHubEvents
|
||||
| transitionalEvents
|
||||
| { type: 'AI_WIZARD_CLOSED_BEFORE_COMPLETION'; payload: { step: string } }
|
||||
| { type: 'EXTERNAL_URL_UPDATE' };
|
||||
|
||||
|
@ -73,6 +79,7 @@ export const customizeStoreStateMachineActions = {
|
|||
|
||||
export const customizeStoreStateMachineServices = {
|
||||
...introServices,
|
||||
...transitionalServices,
|
||||
browserPopstateHandler,
|
||||
};
|
||||
export const customizeStoreStateMachineDefinition = createMachine( {
|
||||
|
@ -205,16 +212,36 @@ export const customizeStoreStateMachineDefinition = createMachine( {
|
|||
component: AssemblerHub,
|
||||
},
|
||||
},
|
||||
postAssemblerHub: {
|
||||
after: {
|
||||
// Wait for 5 seconds before redirecting to the transitional page. This is to ensure that the site preview image is refreshed.
|
||||
5000: {
|
||||
target: '#customizeStore.transitionalScreen',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
on: {
|
||||
FINISH_CUSTOMIZATION: {
|
||||
target: 'backToHomescreen',
|
||||
// Pre-fetch the site preview image for the site for transitional page.
|
||||
actions: [ 'prefetchSitePreview' ],
|
||||
target: '.postAssemblerHub',
|
||||
},
|
||||
GO_BACK_TO_DESIGN_WITH_AI: {
|
||||
target: 'designWithAi',
|
||||
},
|
||||
},
|
||||
},
|
||||
transitionalScreen: {
|
||||
meta: {
|
||||
component: Transitional,
|
||||
},
|
||||
on: {
|
||||
GO_BACK_TO_HOME: {
|
||||
target: 'backToHomescreen',
|
||||
},
|
||||
},
|
||||
},
|
||||
backToHomescreen: {},
|
||||
appearanceTask: {},
|
||||
},
|
||||
|
@ -255,7 +282,6 @@ export const CustomizeStoreController = ( {
|
|||
const [ state, send, service ] = useMachine( augmentedStateMachine, {
|
||||
devTools: process.env.NODE_ENV === 'development',
|
||||
} );
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps -- false positive due to function name match, this isn't from react std lib
|
||||
const currentNodeMeta = useSelector( service, ( currentState ) =>
|
||||
findComponentMeta< CustomizeStoreComponentMeta >(
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
body.woocommerce-customize-store.js.is-fullscreen-mode {
|
||||
margin-top: 0 !important;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.woocommerce-cys-layout {
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { getSetting } from '@woocommerce/settings';
|
||||
import {
|
||||
Button,
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore No types for this exist yet.
|
||||
__unstableMotion as motion,
|
||||
} from '@wordpress/components';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { SiteHub } from '../assembler-hub/site-hub';
|
||||
import { MShotsImage } from './mshots-image';
|
||||
import { ADMIN_URL } from '~/utils/admin-settings';
|
||||
import './style.scss';
|
||||
export * as services from './services';
|
||||
|
||||
export type events = { type: 'GO_BACK_TO_HOME' };
|
||||
export const PREVIEW_IMAGE_OPTION = {
|
||||
vpw: 1200,
|
||||
vph: 742,
|
||||
w: 588,
|
||||
h: 363.58,
|
||||
requeue: true,
|
||||
};
|
||||
|
||||
export const Transitional = ( {
|
||||
sendEvent,
|
||||
}: {
|
||||
sendEvent: ( event: events ) => void;
|
||||
} ) => {
|
||||
const homeUrl: string = getSetting( 'homeUrl', '' );
|
||||
|
||||
return (
|
||||
<div className="woocommerce-customize-store__transitional">
|
||||
<SiteHub
|
||||
as={ motion.div }
|
||||
variants={ {
|
||||
view: { x: 0 },
|
||||
} }
|
||||
isTransparent={ false }
|
||||
className="edit-site-layout__hub"
|
||||
/>
|
||||
|
||||
<div className="woocommerce-customize-store__transitional-content">
|
||||
<h1 className="woocommerce-customize-store__transitional-heading">
|
||||
{ __( 'Your store looks great!', 'woocommerce' ) }
|
||||
</h1>
|
||||
<h2 className="woocommerce-customize-store__transitional-subheading">
|
||||
{ __(
|
||||
"Your store is a reflection of your unique style and personality, and we're thrilled to see it come to life.",
|
||||
'woocommerce'
|
||||
) }
|
||||
</h2>
|
||||
<Button
|
||||
className="woocommerce-customize-store__transitional-preview-button"
|
||||
variant="primary"
|
||||
href={ homeUrl }
|
||||
target="_blank"
|
||||
>
|
||||
{ __( 'Preview store', 'woocommerce' ) }
|
||||
</Button>
|
||||
|
||||
<div className="woocommerce-customize-store__transitional-site-img-container">
|
||||
<MShotsImage
|
||||
url={ homeUrl }
|
||||
alt={ __( 'Your store screenshot', 'woocommerce' ) }
|
||||
aria-labelledby={ __(
|
||||
'Your store screenshot',
|
||||
'woocommerce'
|
||||
) }
|
||||
options={ PREVIEW_IMAGE_OPTION }
|
||||
/>
|
||||
</div>
|
||||
<div className="woocommerce-customize-store__transitional-actions">
|
||||
<div className="woocommerce-customize-store__transitional-action">
|
||||
<h3>
|
||||
{ __( 'Fine-tune your design', 'woocommerce' ) }
|
||||
</h3>
|
||||
<p>
|
||||
{ __(
|
||||
'Head to the Editor to change your images and text, add more pages, and make any further customizations.',
|
||||
'woocommerce'
|
||||
) }
|
||||
</p>
|
||||
<Button
|
||||
variant="tertiary"
|
||||
href={ `${ ADMIN_URL }site-editor.php` }
|
||||
>
|
||||
{ __( 'Go to the Editor', 'woocommerce' ) }
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="woocommerce-customize-store__transitional-action">
|
||||
<h3>
|
||||
{ __(
|
||||
'Continue setting up your store',
|
||||
'woocommerce'
|
||||
) }
|
||||
</h3>
|
||||
<p>
|
||||
{ __(
|
||||
'Go back to the Home screen to complete your store setup and start selling',
|
||||
'woocommerce'
|
||||
) }
|
||||
</p>
|
||||
<Button
|
||||
variant="tertiary"
|
||||
onClick={ () =>
|
||||
sendEvent( {
|
||||
type: 'GO_BACK_TO_HOME',
|
||||
} )
|
||||
}
|
||||
>
|
||||
{ __( 'Back to Home', 'woocommerce' ) }
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,215 @@
|
|||
// See https://github.com/Automattic/wp-calypso/blob/6923bed938911a931e722a1efa6fcbbf942677a9/packages/onboarding/src/mshots-image/index.tsx
|
||||
// TODO: @automattic/onboarding is not published to npm, so we can't use it here. We'll need to add the "requeue" option to MShotsOptions and we could remove this when it's published.
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { addQueryArgs } from '@wordpress/url';
|
||||
import classnames from 'classnames';
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
|
||||
interface MShotsImageProps {
|
||||
url: string;
|
||||
alt: string;
|
||||
'aria-labelledby': string;
|
||||
options: MShotsOptions;
|
||||
scrollable?: boolean;
|
||||
}
|
||||
|
||||
export type MShotsOptions = {
|
||||
vpw: number;
|
||||
vph: number;
|
||||
w: number;
|
||||
h?: number;
|
||||
screen_height?: number;
|
||||
format?: 'png' | 'jpeg';
|
||||
requeue?: boolean;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
const debug = console.debug;
|
||||
|
||||
export function getMshotsUrl(
|
||||
targetUrl: string,
|
||||
options: MShotsOptions,
|
||||
count = 0
|
||||
): string {
|
||||
const mshotsUrl = 'https://s0.wp.com/mshots/v1/';
|
||||
const mshotsRequest = addQueryArgs(
|
||||
mshotsUrl + encodeURIComponent( targetUrl ),
|
||||
{
|
||||
...options,
|
||||
count,
|
||||
}
|
||||
);
|
||||
return mshotsRequest;
|
||||
}
|
||||
|
||||
const MAXTRIES = 10;
|
||||
|
||||
// This custom react hook returns undefined while the image is loading and
|
||||
// a HTMLImageElement (i.e. the class you get from `new Image()`) once loading
|
||||
// is complete.
|
||||
//
|
||||
// It also triggers a re-render (via setState()) when the value changes, so just
|
||||
// check if it's truthy and then treat it like any other Image.
|
||||
//
|
||||
// Note the loading may occur immediately and synchronously if the image is
|
||||
// already or may take up to several seconds if mshots has to generate and cache
|
||||
// new images.
|
||||
//
|
||||
// The calling code doesn't need to worry about the details except that you'll
|
||||
// want some sort of loading display.
|
||||
//
|
||||
// Inspired by https://stackoverflow.com/a/60458593
|
||||
const useMshotsImg = (
|
||||
src: string,
|
||||
options: MShotsOptions
|
||||
): HTMLImageElement | undefined => {
|
||||
const [ loadedImg, setLoadedImg ] = useState< HTMLImageElement >();
|
||||
const [ count, setCount ] = useState( 0 );
|
||||
const previousSrc = useRef( src );
|
||||
|
||||
const imgRef = useRef< HTMLImageElement >();
|
||||
const timeoutIdRef = useRef< number >();
|
||||
|
||||
const previousImg = useRef< HTMLImageElement >();
|
||||
const previousOptions = useRef< MShotsOptions >();
|
||||
// Oddly, we need to assign to current here after ref creation in order to
|
||||
// pass the equivalence check and avoid a spurious reset
|
||||
previousOptions.current = options;
|
||||
|
||||
// Note: Mshots doesn't care about the "count" param, but it is important
|
||||
// to browser caching. Getting this wrong looks like the url resolving
|
||||
// before the image is ready.
|
||||
useEffect( () => {
|
||||
// If there's been a "props" change we need to reset everything:
|
||||
if (
|
||||
options !== previousOptions.current ||
|
||||
( src !== previousSrc.current && imgRef.current )
|
||||
) {
|
||||
// Make sure an old image can't trigger a spurious state update
|
||||
debug( 'resetting mShotsUrl request' );
|
||||
if ( src !== previousSrc.current ) {
|
||||
debug( 'src changed\nfrom', previousSrc.current, '\nto', src );
|
||||
}
|
||||
if ( options !== previousOptions.current ) {
|
||||
debug(
|
||||
'options changed\nfrom',
|
||||
previousOptions.current,
|
||||
'\nto',
|
||||
options
|
||||
);
|
||||
}
|
||||
if ( previousImg.current && previousImg.current.onload ) {
|
||||
previousImg.current.onload = null;
|
||||
if ( timeoutIdRef.current ) {
|
||||
clearTimeout( timeoutIdRef.current );
|
||||
timeoutIdRef.current = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
setLoadedImg( undefined );
|
||||
setCount( 0 );
|
||||
previousImg.current = imgRef.current;
|
||||
|
||||
previousOptions.current = options;
|
||||
previousSrc.current = src;
|
||||
}
|
||||
|
||||
const srcUrl = getMshotsUrl( src, options, count );
|
||||
const newImage = new Image();
|
||||
newImage.onload = () => {
|
||||
// Detect default image (Don't request a 400x300 image).
|
||||
//
|
||||
// If this turns out to be a problem, it might help to know that the
|
||||
// http request status for the default is a 307. Unfortunately we
|
||||
// don't get the request through an img element so we'd need to
|
||||
// take a completely different approach using ajax.
|
||||
if (
|
||||
newImage.naturalWidth !== 400 ||
|
||||
newImage.naturalHeight !== 300
|
||||
) {
|
||||
// Note we're using the naked object here, not the ref, because
|
||||
// this is the callback on the image itself. We'd never want
|
||||
// the image to finish loading and set some other image.
|
||||
setLoadedImg( newImage );
|
||||
} else if ( count < MAXTRIES ) {
|
||||
// Only refresh 10 times
|
||||
// Triggers a target.src change with increasing timeouts
|
||||
timeoutIdRef.current = window.setTimeout(
|
||||
() => setCount( ( _count ) => _count + 1 ),
|
||||
count * 500
|
||||
);
|
||||
}
|
||||
};
|
||||
newImage.src = srcUrl;
|
||||
imgRef.current = newImage;
|
||||
|
||||
return () => {
|
||||
if ( imgRef.current && imgRef.current.onload ) {
|
||||
imgRef.current.onload = null;
|
||||
}
|
||||
clearTimeout( timeoutIdRef.current );
|
||||
};
|
||||
}, [ src, count, options ] );
|
||||
|
||||
return loadedImg;
|
||||
};
|
||||
|
||||
// For hover-scroll, we use a div with a background image (rather than an img element)
|
||||
// in order to use transitions between `top` and `bottom` on the
|
||||
// `background-position` property.
|
||||
// The "normal" top & bottom properties are problematic individually because we
|
||||
// don't know how big the images will be, and using both gets the
|
||||
// right positions but with no transition (as they're different properties).
|
||||
export const MShotsImage = ( {
|
||||
url,
|
||||
'aria-labelledby': labelledby,
|
||||
alt,
|
||||
options,
|
||||
scrollable = false,
|
||||
}: MShotsImageProps ) => {
|
||||
const maybeImage = useMshotsImg( url, options );
|
||||
const src: string = maybeImage?.src || '';
|
||||
const visible = !! src;
|
||||
const backgroundImage = maybeImage?.src && `url( ${ maybeImage?.src } )`;
|
||||
|
||||
const animationScrollSpeedInPixelsPerSecond = 400;
|
||||
const animationDuration =
|
||||
( maybeImage?.naturalHeight || 600 ) /
|
||||
animationScrollSpeedInPixelsPerSecond;
|
||||
|
||||
const scrollableStyles = {
|
||||
backgroundImage,
|
||||
transition: `background-position ${ animationDuration }s`,
|
||||
};
|
||||
|
||||
const style = {
|
||||
...( scrollable ? scrollableStyles : {} ),
|
||||
};
|
||||
|
||||
const className = classnames(
|
||||
'mshots-image__container',
|
||||
scrollable && 'hover-scroll',
|
||||
visible ? 'mshots-image-visible' : 'mshots-image__loader'
|
||||
);
|
||||
|
||||
// The "! visible" here is only to dodge a particularly specific css
|
||||
// rule effecting the placeholder while loading static images:
|
||||
// '.design-picker .design-picker__image-frame img { ..., height: auto }'
|
||||
return scrollable || ! visible ? (
|
||||
<div
|
||||
className={ className }
|
||||
style={ style }
|
||||
aria-labelledby={ labelledby }
|
||||
/>
|
||||
) : (
|
||||
<img
|
||||
{ ...{ className, style, src, alt } }
|
||||
aria-labelledby={ labelledby }
|
||||
alt={ alt }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default MShotsImage;
|
|
@ -0,0 +1,19 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { getSetting } from '@woocommerce/settings';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { getMshotsUrl } from './mshots-image';
|
||||
import { PREVIEW_IMAGE_OPTION } from './';
|
||||
|
||||
export const fetchSitePreviewImage = async () => {
|
||||
const homeUrl: string = getSetting( 'homeUrl', '' );
|
||||
return window
|
||||
.fetch( getMshotsUrl( homeUrl, PREVIEW_IMAGE_OPTION ) )
|
||||
.catch( () => {
|
||||
// Ignore errors
|
||||
} );
|
||||
};
|
|
@ -0,0 +1,154 @@
|
|||
.woocommerce-customize-store__transitional {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
|
||||
.edit-site-site-hub.edit-site-layout__hub {
|
||||
height: 64px;
|
||||
padding: 16px;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
position: initial;
|
||||
|
||||
.edit-site-site-hub__view-mode-toggle-container,
|
||||
.edit-site-site-icon__image,
|
||||
svg {
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.edit-site-site-hub__site-title {
|
||||
margin-left: 12px;
|
||||
color: $gray-900;
|
||||
font-size: 13px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 20px; /* 153.846% */
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-customize-store__transitional-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-top: 40px;
|
||||
}
|
||||
|
||||
.woocommerce-customize-store__transitional-heading {
|
||||
color: $gray-900;
|
||||
text-align: center;
|
||||
font-feature-settings: 'clig' off, 'liga' off;
|
||||
font-size: 32px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 60px; /* 187.5% */
|
||||
letter-spacing: -0.32px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.woocommerce-customize-store__transitional-subheading {
|
||||
color: $gray-700;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 24px; /* 150% */
|
||||
letter-spacing: -0.1px;
|
||||
margin: 4px 0 0;
|
||||
width: 560px;
|
||||
}
|
||||
|
||||
.woocommerce-customize-store__transitional-preview-button {
|
||||
padding: 8px 16px;
|
||||
height: 40px;
|
||||
margin: 20px 0 0;
|
||||
}
|
||||
|
||||
.woocommerce-customize-store__transitional-site-img-container {
|
||||
width: 600px;
|
||||
height: 371px;
|
||||
border-radius: 16px;
|
||||
background: #f6f7f7;
|
||||
box-shadow: 0 6px 6px 0 rgba(0, 0, 0, 0.02), 0 13px 10px 0 rgba(0, 0, 0, 0.03), 0 15px 20px 0 rgba(0, 0, 0, 0.04);
|
||||
margin-top: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.mshots-image__loader {
|
||||
width: 600px;
|
||||
height: 371px;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
img {
|
||||
border-radius: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-customize-store__transitional-actions {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-top: 50px;
|
||||
gap: 40px;
|
||||
|
||||
.woocommerce-customize-store__transitional-action {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 280px;
|
||||
|
||||
h3 {
|
||||
color: $gray-900;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 24px; /* 150% */
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 5px 0 0;
|
||||
color: $gray-700;
|
||||
font-size: 13px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px; /* 123.077% */
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.components-button {
|
||||
margin-top: 16px;
|
||||
padding: 0;
|
||||
margin-left: 0;
|
||||
height: 20px;
|
||||
width: fit-content;
|
||||
|
||||
&:hover {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// mshots component
|
||||
.mshots-image__loader {
|
||||
@include placeholder();
|
||||
}
|
||||
|
||||
.mshots-image-visible {
|
||||
animation: fadein 300ms;
|
||||
}
|
||||
|
||||
@keyframes fadein {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { render, screen } from '@testing-library/react';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { Transitional } from '../index';
|
||||
|
||||
jest.mock( '../mshots-image', () => ( {
|
||||
__esModule: true,
|
||||
MShotsImage: () => {
|
||||
return <img alt="preview-img" />;
|
||||
},
|
||||
} ) );
|
||||
|
||||
jest.mock( '../../assembler-hub/site-hub', () => ( {
|
||||
__esModule: true,
|
||||
SiteHub: () => {
|
||||
return <div />;
|
||||
},
|
||||
} ) );
|
||||
|
||||
describe( 'Transitional', () => {
|
||||
let props: {
|
||||
sendEvent: jest.Mock;
|
||||
};
|
||||
|
||||
beforeEach( () => {
|
||||
props = {
|
||||
sendEvent: jest.fn(),
|
||||
};
|
||||
} );
|
||||
|
||||
it( 'should render Transitional page', () => {
|
||||
// @ts-ignore
|
||||
render( <Transitional { ...props } /> );
|
||||
|
||||
expect(
|
||||
screen.getByText( /Your store looks great!/i )
|
||||
).toBeInTheDocument();
|
||||
|
||||
expect( screen.getByRole( 'img' ) ).toBeInTheDocument();
|
||||
|
||||
expect(
|
||||
screen.getByRole( 'link', {
|
||||
name: /Preview store/i,
|
||||
} )
|
||||
).toBeInTheDocument();
|
||||
|
||||
expect(
|
||||
screen.getByRole( 'link', {
|
||||
name: /Go to the Editor/i,
|
||||
} )
|
||||
).toBeInTheDocument();
|
||||
|
||||
expect(
|
||||
screen.getByRole( 'button', {
|
||||
name: /Back to Home/i,
|
||||
} )
|
||||
).toBeInTheDocument();
|
||||
} );
|
||||
|
||||
it( 'should send GO_BACK_TO_HOME event when clicking on "Back to Home" button', () => {
|
||||
// @ts-ignore
|
||||
render( <Transitional { ...props } /> );
|
||||
|
||||
screen
|
||||
.getByRole( 'button', {
|
||||
name: /Back to Home/i,
|
||||
} )
|
||||
.click();
|
||||
|
||||
expect( props.sendEvent ).toHaveBeenCalledWith( {
|
||||
type: 'GO_BACK_TO_HOME',
|
||||
} );
|
||||
} );
|
||||
} );
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: add
|
||||
|
||||
Add customize store transitional screen
|
Loading…
Reference in New Issue