Add confirmation dialog before loading the sample products

This commit is contained in:
Chi-Hsuan Huang 2022-05-24 15:07:03 +08:00
parent 86553fbe1f
commit e2cc91dd60
5 changed files with 154 additions and 22 deletions

View File

@ -12,17 +12,16 @@ import './load-sample-product-confirm-modal.scss';
type Props = {
onCancel: () => void;
onOK: () => void;
onImport: () => void;
};
export const LoadSampleProductConfirmModal: React.VFC< Props > = ( {
onCancel,
onOK,
onImport,
} ) => {
return (
<Modal
className="woocommerce-products-load-sample-product-confirm-modal"
overlayClassName="woocommerce-products-load-sample-product-confirm-modal-overlay"
title="Load sample products"
onRequestClose={ onCancel }
>
@ -35,7 +34,7 @@ export const LoadSampleProductConfirmModal: React.VFC< Props > = ( {
<Button isSecondary onClick={ onCancel }>
{ __( 'Cancel' ) }
</Button>
<Button isPrimary onClick={ onOK }>
<Button isPrimary onClick={ onImport }>
{ __( 'Import sample products' ) }
</Button>
</div>

View File

@ -21,11 +21,16 @@ import useProductTypeListItems from '../experimental-products/use-product-types-
import { getProductTypes } from '../experimental-products/utils';
import LoadSampleProductModal from '../components/load-sample-product-modal';
import useLoadSampleProducts from '../components/use-load-sample-products';
import LoadSampleProductConfirmModal from '../components/load-sample-product-confirm-modal';
import useRecordCompletionTime from '../use-record-completion-time';
export const Products = () => {
const [ showStacks, setStackVisibility ] = useState< boolean >( false );
const { recordCompletionTime } = useRecordCompletionTime( 'products' );
const [
isConfirmingLoadSampleProducts,
setIsConfirmingLoadSampleProducts,
] = useState( false );
const importTypesWithTimeRecord = useMemo(
() =>
@ -61,7 +66,9 @@ export const Products = () => {
const StacksComponent = (
<Stacks
items={ productTypeListItems }
onClickLoadSampleProduct={ loadSampleProduct }
onClickLoadSampleProduct={ () =>
setIsConfirmingLoadSampleProducts( true )
}
/>
);
@ -83,7 +90,21 @@ export const Products = () => {
</Button>
{ showStacks && StacksComponent }
</div>
{ isLoadingSampleProducts && <LoadSampleProductModal /> }
{ isLoadingSampleProducts ? (
<LoadSampleProductModal />
) : (
isConfirmingLoadSampleProducts && (
<LoadSampleProductConfirmModal
onCancel={ () =>
setIsConfirmingLoadSampleProducts( false )
}
onImport={ () => {
setIsConfirmingLoadSampleProducts( false );
loadSampleProduct();
} }
/>
)
) }
</div>
);
};

View File

@ -11,6 +11,16 @@ import { Products } from '../';
jest.mock( '@woocommerce/tracks', () => ( { recordEvent: jest.fn() } ) );
global.fetch = jest.fn().mockImplementation( () =>
Promise.resolve( {
json: () => Promise.resolve( {} ),
status: 200,
} )
);
const confirmModalText =
"We'll import images from woocommerce.com to set up your sample products.";
describe( 'Products', () => {
beforeEach( () => {
( recordEvent as jest.Mock ).mockClear();
@ -103,4 +113,57 @@ describe( 'Products', () => {
);
} );
} );
it( 'should send a request to load sample products when the "Import sample products" button is clicked', async () => {
const fetchMock = jest.spyOn( global, 'fetch' );
const { queryByText, getByRole } = render( <Products /> );
userEvent.click(
getByRole( 'button', { name: 'Or add your products from scratch' } )
);
expect( queryByText( 'Load Sample Products' ) ).toBeInTheDocument();
userEvent.click(
getByRole( 'link', { name: 'Load Sample Products' } )
);
await waitFor( () =>
expect( queryByText( confirmModalText ) ).toBeInTheDocument()
);
userEvent.click(
getByRole( 'button', { name: 'Import sample products' } )
);
await waitFor( () =>
expect( queryByText( confirmModalText ) ).not.toBeInTheDocument()
);
expect( fetchMock ).toHaveBeenCalledWith(
'/wc-admin/onboarding/tasks/import_sample_products?_locale=user',
{
body: undefined,
credentials: 'include',
headers: { Accept: 'application/json, */*;q=0.1' },
method: 'POST',
}
);
} );
it( 'should close the confirmation modal when the cancel button is clicked', async () => {
const { queryByText, getByRole } = render( <Products /> );
userEvent.click(
getByRole( 'button', { name: 'Or add your products from scratch' } )
);
expect( queryByText( 'Load Sample Products' ) ).toBeInTheDocument();
userEvent.click(
getByRole( 'link', { name: 'Load Sample Products' } )
);
await waitFor( () =>
expect( queryByText( confirmModalText ) ).toBeInTheDocument()
);
userEvent.click( getByRole( 'button', { name: 'Cancel' } ) );
expect( queryByText( confirmModalText ) ).not.toBeInTheDocument();
} );
} );

View File

@ -25,6 +25,7 @@ import CardLayout from './card-layout';
import { LoadSampleProductType } from './constants';
import LoadSampleProductModal from '../components/load-sample-product-modal';
import useLoadSampleProducts from '../components/use-load-sample-products';
import LoadSampleProductConfirmModal from '../components/load-sample-product-confirm-modal';
import useRecordCompletionTime from '../use-record-completion-time';
import { getCountryCode } from '~/dashboard/utils';
import { useProductTaskExperiment } from './use-product-layout-experiment';
@ -54,6 +55,10 @@ const ViewControlButton: React.FC< {
export const Products = () => {
const [ isExpanded, setIsExpanded ] = useState< boolean >( false );
const [
isConfirmingLoadSampleProducts,
setIsConfirmingLoadSampleProducts,
] = useState( false );
const {
isLoading: isLoadingExperiment,
experimentLayout,
@ -125,7 +130,7 @@ export const Products = () => {
if ( experimentLayout === 'card' ) {
surfacedProductTypes.push( {
...LoadSampleProductType,
onClick: loadSampleProduct,
onClick: () => setIsConfirmingLoadSampleProducts( true ),
} );
}
}
@ -135,7 +140,6 @@ export const Products = () => {
isExpanded,
productTypesWithTimeRecord,
experimentLayout,
loadSampleProduct,
] );
return (
@ -159,7 +163,9 @@ export const Products = () => {
{ experimentLayout === 'stacked' ? (
<Stack
items={ visibleProductTypes }
onClickLoadSampleProduct={ loadSampleProduct }
onClickLoadSampleProduct={ () =>
setIsConfirmingLoadSampleProducts( true )
}
showOtherOptions={ isExpanded }
/>
) : (
@ -178,7 +184,21 @@ export const Products = () => {
/>
<Footer />
</div>
{ isLoadingSampleProducts && <LoadSampleProductModal /> }
{ isLoadingSampleProducts ? (
<LoadSampleProductModal />
) : (
isConfirmingLoadSampleProducts && (
<LoadSampleProductConfirmModal
onCancel={ () =>
setIsConfirmingLoadSampleProducts( false )
}
onImport={ () => {
setIsConfirmingLoadSampleProducts( false );
loadSampleProduct();
} }
/>
)
) }
</>
) }
</div>

View File

@ -45,6 +45,9 @@ global.fetch = jest.fn().mockImplementation( () =>
jest.mock( '@woocommerce/tracks', () => ( { recordEvent: jest.fn() } ) );
const confirmModalText =
"We'll import images from woocommerce.com to set up your sample products.";
describe( 'Products', () => {
beforeEach( () => {
jest.clearAllMocks();
@ -238,31 +241,57 @@ describe( 'Products', () => {
expect( queryByText( 'View less product types' ) ).toBeInTheDocument();
} );
it( 'should send a request to load sample products when the link is clicked', async () => {
it( 'should send a request to load sample products when the "Import sample products" button is clicked', async () => {
const fetchMock = jest.spyOn( global, 'fetch' );
const { queryByText, getByRole } = render( <Products /> );
userEvent.click(
getByRole( 'button', { name: 'View more product types' } )
);
expect( queryByText( 'Load Sample Products' ) ).toBeInTheDocument();
userEvent.click(
getByRole( 'link', { name: 'Load Sample Products' } )
);
await waitFor( () =>
expect( fetchMock ).toHaveBeenCalledWith(
'/wc-admin/onboarding/tasks/import_sample_products?_locale=user',
{
body: undefined,
credentials: 'include',
headers: { Accept: 'application/json, */*;q=0.1' },
method: 'POST',
}
)
expect( queryByText( confirmModalText ) ).toBeInTheDocument()
);
userEvent.click(
getByRole( 'button', { name: 'Import sample products' } )
);
await waitFor( () =>
expect( queryByText( confirmModalText ) ).not.toBeInTheDocument()
);
expect( fetchMock ).toHaveBeenCalledWith(
'/wc-admin/onboarding/tasks/import_sample_products?_locale=user',
{
body: undefined,
credentials: 'include',
headers: { Accept: 'application/json, */*;q=0.1' },
method: 'POST',
}
);
} );
it( 'should close the confirmation modal when the cancel button is clicked', async () => {
const { queryByText, getByRole } = render( <Products /> );
userEvent.click(
getByRole( 'button', { name: 'View more product types' } )
);
expect( queryByText( 'Load Sample Products' ) ).toBeInTheDocument();
userEvent.click(
getByRole( 'link', { name: 'Load Sample Products' } )
);
await waitFor( () =>
expect( queryByText( confirmModalText ) ).toBeInTheDocument()
);
userEvent.click( getByRole( 'button', { name: 'Cancel' } ) );
expect( queryByText( confirmModalText ) ).not.toBeInTheDocument();
} );
it( 'should show spinner when layout experiment is loading', async () => {