Update the footer section in "Add products" task (#49782)
* Update printful feature flag * Remove footer * Add upload icon * Add importCSVItem constant * Add footer stack * Delete footer test * Remove redundant printful inclusion * fixed types * Update CSS * Changelog * Lint * Fix test * Fix test again * Fix SVG to use theme color --------- Co-authored-by: Ilyas Foo <foo.ilyas@gmail.com> Co-authored-by: rjchow <me@rjchow.com>
This commit is contained in:
parent
726e5fb0d8
commit
7e13bbcbf4
|
@ -6,8 +6,8 @@ import ProductIcon from 'gridicons/dist/product';
|
|||
import CloudOutlineIcon from 'gridicons/dist/cloud-outline';
|
||||
import TypesIcon from 'gridicons/dist/types';
|
||||
import { Icon, chevronRight } from '@wordpress/icons';
|
||||
import { addFilter } from '@wordpress/hooks';
|
||||
import { recordEvent } from '@woocommerce/tracks';
|
||||
import { getAdminLink } from '@woocommerce/settings';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -16,6 +16,7 @@ import Link from './icon/link_24px.js';
|
|||
import Widget from './icon/widgets_24px.js';
|
||||
import LightBulb from './icon/lightbulb_24px.js';
|
||||
import PrintfulIcon from './icon/printful.png';
|
||||
import Upload from './icon/upload_40px.js';
|
||||
|
||||
export const productTypes = Object.freeze( [
|
||||
{
|
||||
|
@ -90,7 +91,7 @@ export const PrintfulAdvertProductPlacement = {
|
|||
'Design and easily sell custom print products online with Printful.',
|
||||
'woocommerce'
|
||||
),
|
||||
className: 'woocommerce-products-list__item-printful-advert',
|
||||
className: 'woocommerce-products-list__item-advert',
|
||||
before: (
|
||||
<img
|
||||
className="printful-sponsored__icon"
|
||||
|
@ -105,12 +106,35 @@ export const PrintfulAdvertProductPlacement = {
|
|||
},
|
||||
};
|
||||
|
||||
export const SponsoredProductPlacementType = PrintfulAdvertProductPlacement;
|
||||
export const ImportCSVItem = {
|
||||
key: 'import-csv' as const,
|
||||
title: (
|
||||
<span className="printful-sponsored__text">
|
||||
{ __( 'Are you already selling somewhere else?', 'woocommerce' ) }
|
||||
</span>
|
||||
),
|
||||
content: __( 'Import your products from a CSV file.', 'woocommerce' ),
|
||||
className: 'woocommerce-products-list__item-advert',
|
||||
before: <Upload />,
|
||||
after: <Icon icon={ chevronRight } />,
|
||||
onClick: () => {
|
||||
recordEvent( 'tasklist_add_product', {
|
||||
method: 'import',
|
||||
} );
|
||||
window.location.href = getAdminLink(
|
||||
'edit.php?post_type=product&page=product_importer&wc_onboarding_active_task=products'
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export type SponsoredProductPlacementType =
|
||||
| typeof PrintfulAdvertProductPlacement
|
||||
| typeof ImportCSVItem;
|
||||
|
||||
export type ProductType =
|
||||
| ( typeof productTypes )[ number ]
|
||||
| typeof LoadSampleProductType
|
||||
| typeof SponsoredProductPlacementType;
|
||||
| SponsoredProductPlacementType;
|
||||
export type ProductTypeKey = ProductType[ 'key' ];
|
||||
|
||||
export const onboardingProductTypesToSurfaced: Readonly<
|
||||
|
@ -131,13 +155,3 @@ export const SETUP_TASKLIST_PRODUCT_TYPES_FILTER =
|
|||
|
||||
export const SETUP_TASKLIST_PRODUCTS_AFTER_FILTER =
|
||||
'woocommerce_admin_task_products_after';
|
||||
|
||||
if ( window.wcAdminFeatures && window.wcAdminFeatures.printful === true ) {
|
||||
addFilter(
|
||||
SETUP_TASKLIST_PRODUCTS_AFTER_FILTER,
|
||||
'woocommerce/task-lists/products-sponsored-placement',
|
||||
( products ) => {
|
||||
return [ ...products, PrintfulAdvertProductPlacement ];
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import interpolateComponents from '@automattic/interpolate-components';
|
||||
import { Text } from '@woocommerce/experimental';
|
||||
import { Link } from '@woocommerce/components';
|
||||
import { getAdminLink } from '@woocommerce/settings';
|
||||
import { recordEvent } from '@woocommerce/tracks';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import useRecordCompletionTime from '../use-record-completion-time';
|
||||
|
||||
const Footer: React.FC = () => {
|
||||
const { recordCompletionTime } = useRecordCompletionTime( 'products' );
|
||||
|
||||
return (
|
||||
<div className="woocommerce-products-footer">
|
||||
<Text className="woocommerce-products-footer__selling-somewhere-else">
|
||||
{ __(
|
||||
'Are you already selling somewhere else?',
|
||||
'woocommerce'
|
||||
) }
|
||||
</Text>
|
||||
<Text className="woocommerce-products-footer__import-options">
|
||||
{ interpolateComponents( {
|
||||
mixedString: __(
|
||||
'{{importCSVLink}}Import your products from a CSV file{{/importCSVLink}}.',
|
||||
'woocommerce'
|
||||
),
|
||||
components: {
|
||||
importCSVLink: (
|
||||
<Link
|
||||
onClick={ () => {
|
||||
recordEvent( 'tasklist_add_product', {
|
||||
method: 'import',
|
||||
} );
|
||||
recordCompletionTime();
|
||||
window.location.href = getAdminLink(
|
||||
'edit.php?post_type=product&page=product_importer&wc_onboarding_active_task=products'
|
||||
);
|
||||
return false;
|
||||
} }
|
||||
href=""
|
||||
type="wc-admin"
|
||||
>
|
||||
<></>
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
} ) }
|
||||
</Text>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Footer;
|
|
@ -0,0 +1,35 @@
|
|||
const Upload = () => {
|
||||
return (
|
||||
<svg
|
||||
width="40"
|
||||
height="40"
|
||||
viewBox="0 0 40 40"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<circle cx="20" cy="20" r="20" fill="#007CBA" fillOpacity="0.05" />
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M26.5 23L26.5 28L28 28L28 23L26.5 23Z"
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M12 23L12 28L13.5 28L13.5 23L12 23Z"
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M12 28H28V26.5H12V28Z"
|
||||
/>
|
||||
<path
|
||||
d="M20.25 13L26 18.25M20.25 13L20.25 28M20.25 13L15 18.25"
|
||||
strokeWidth="1.5"
|
||||
className="stroke-admin-theme"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default Upload;
|
|
@ -53,7 +53,18 @@
|
|||
|
||||
.woocommerce-products-stack {
|
||||
max-width: 550px;
|
||||
margin-top: 24px;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-list__item-before > svg {
|
||||
fill: var(--wp-admin-theme-color);
|
||||
.stroke-admin-theme {
|
||||
stroke: var(--wp-admin-theme-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,12 +20,15 @@ import { getAdminSetting } from '~/utils/admin-settings';
|
|||
import { getSurfacedProductTypeKeys, getProductTypes } from './utils';
|
||||
import useProductTypeListItems from './use-product-types-list-items';
|
||||
import Stack from './stack';
|
||||
import Footer from './footer';
|
||||
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 { SETUP_TASKLIST_PRODUCTS_AFTER_FILTER } from './constants';
|
||||
import {
|
||||
SETUP_TASKLIST_PRODUCTS_AFTER_FILTER,
|
||||
ImportCSVItem,
|
||||
PrintfulAdvertProductPlacement,
|
||||
} from './constants';
|
||||
|
||||
const getOnboardingProductType = (): string[] => {
|
||||
const onboardingData = getAdminSetting( 'onboarding' );
|
||||
|
@ -72,7 +75,7 @@ export const Products = () => {
|
|||
() =>
|
||||
productTypes.map( ( productType ) => ( {
|
||||
...productType,
|
||||
onClick: () => {
|
||||
onClick: (): void => {
|
||||
productType.onClick();
|
||||
recordCompletionTime();
|
||||
},
|
||||
|
@ -113,6 +116,27 @@ export const Products = () => {
|
|||
return surfacedProductTypesAndAppendedProducts;
|
||||
}, [ surfacedProductTypeKeys, isExpanded, productTypesWithTimeRecord ] );
|
||||
|
||||
const footerStack = useMemo( () => {
|
||||
const options = [];
|
||||
const importCSVItemWithTimeRecord = {
|
||||
...ImportCSVItem,
|
||||
onClick: () => {
|
||||
ImportCSVItem.onClick();
|
||||
recordCompletionTime();
|
||||
},
|
||||
};
|
||||
|
||||
options.push( importCSVItemWithTimeRecord );
|
||||
|
||||
if (
|
||||
window.wcAdminFeatures &&
|
||||
window.wcAdminFeatures.printful === true
|
||||
) {
|
||||
options.push( PrintfulAdvertProductPlacement );
|
||||
}
|
||||
return options;
|
||||
}, [ recordCompletionTime ] );
|
||||
|
||||
return (
|
||||
<div className="woocommerce-task-products">
|
||||
<Text
|
||||
|
@ -143,7 +167,11 @@ export const Products = () => {
|
|||
setIsExpanded( ! isExpanded );
|
||||
} }
|
||||
/>
|
||||
<Footer />
|
||||
<Stack
|
||||
items={ footerStack }
|
||||
showOtherOptions={ false }
|
||||
isTaskListItemClicked={ isRequesting }
|
||||
/>
|
||||
</div>
|
||||
{ isLoadingSampleProducts ? (
|
||||
<LoadSampleProductModal />
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
margin-top: 0;
|
||||
}
|
||||
|
||||
&.woocommerce-products-list__item-printful-advert {
|
||||
&.woocommerce-products-list__item-advert {
|
||||
.woocommerce-list__item-before {
|
||||
background: none;
|
||||
border-radius: 0;
|
||||
|
|
|
@ -15,7 +15,9 @@ import { ProductType } from './constants';
|
|||
import './stack.scss';
|
||||
import useRecordCompletionTime from '../use-record-completion-time';
|
||||
|
||||
type StackProps = {
|
||||
type StackProps = StackWithLoadSampleBlurb | StackWithoutText;
|
||||
|
||||
type StackWithLoadSampleBlurb = {
|
||||
items: ( ProductType & {
|
||||
onClick: () => void;
|
||||
} )[];
|
||||
|
@ -24,9 +26,18 @@ type StackProps = {
|
|||
isTaskListItemClicked?: boolean;
|
||||
};
|
||||
|
||||
type StackWithoutText = {
|
||||
items: ( ProductType & {
|
||||
onClick: () => void;
|
||||
} )[];
|
||||
showOtherOptions: false;
|
||||
onClickLoadSampleProduct?: () => void;
|
||||
isTaskListItemClicked?: boolean;
|
||||
};
|
||||
|
||||
const Stack: React.FC< StackProps > = ( {
|
||||
items,
|
||||
onClickLoadSampleProduct,
|
||||
onClickLoadSampleProduct = () => {},
|
||||
showOtherOptions = true,
|
||||
isTaskListItemClicked = false,
|
||||
} ) => {
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { render } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { recordEvent } from '@woocommerce/tracks';
|
||||
|
||||
jest.mock( '@woocommerce/tracks', () => ( { recordEvent: jest.fn() } ) );
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Footer from '../footer';
|
||||
|
||||
describe( 'Footer', () => {
|
||||
beforeEach( () => {
|
||||
( recordEvent as jest.Mock ).mockClear();
|
||||
} );
|
||||
it( 'should render footer with one links', () => {
|
||||
const { queryAllByRole } = render( <Footer /> );
|
||||
expect( queryAllByRole( 'link' ) ).toHaveLength( 1 );
|
||||
} );
|
||||
|
||||
it( 'clicking on import CSV should fire event tasklist_add_product with method:import and task_completion_time', () => {
|
||||
const { getByText } = render( <Footer /> );
|
||||
userEvent.click( getByText( 'Import your products from a CSV file' ) );
|
||||
expect( recordEvent ).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
'tasklist_add_product',
|
||||
{ method: 'import' }
|
||||
);
|
||||
expect( recordEvent ).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
'task_completion_time',
|
||||
{ task_name: 'products', time: '0-2s' }
|
||||
);
|
||||
} );
|
||||
} );
|
|
@ -72,10 +72,11 @@ describe( 'Products', () => {
|
|||
product_types: [ 'downloads' ],
|
||||
},
|
||||
} ) );
|
||||
const { queryByText, queryByRole } = render( <Products /> );
|
||||
const { queryByText, queryAllByRole } = render( <Products /> );
|
||||
|
||||
const productTypeList = queryAllByRole( 'menu' )?.[ 0 ];
|
||||
expect( queryByText( 'Digital product' ) ).toBeInTheDocument();
|
||||
expect( queryByRole( 'menu' )?.childElementCount ).toBe( 1 );
|
||||
expect( productTypeList?.childElementCount ).toBe( 1 );
|
||||
expect( queryByText( 'View more product types' ) ).toBeInTheDocument();
|
||||
} );
|
||||
|
||||
|
@ -116,7 +117,9 @@ describe( 'Products', () => {
|
|||
product_types: [ 'downloads' ],
|
||||
},
|
||||
} ) );
|
||||
const { queryByText, getByRole, queryByRole } = render( <Products /> );
|
||||
const { queryByText, getByRole, queryAllByRole } = render(
|
||||
<Products />
|
||||
);
|
||||
|
||||
expect( queryByText( 'View more product types' ) ).toBeInTheDocument();
|
||||
|
||||
|
@ -124,11 +127,13 @@ describe( 'Products', () => {
|
|||
getByRole( 'button', { name: 'View more product types' } )
|
||||
);
|
||||
|
||||
await waitFor( () =>
|
||||
expect( queryByRole( 'menu' )?.childElementCount ).toBe(
|
||||
await waitFor( () => {
|
||||
const productTypeList = queryAllByRole( 'menu' )?.[ 0 ];
|
||||
expect( productTypeList?.childElementCount ).toBe(
|
||||
productTypes.length
|
||||
)
|
||||
);
|
||||
} );
|
||||
|
||||
userEvent.click(
|
||||
getByRole( 'menuitem', {
|
||||
name: 'Grouped product A collection of related products.',
|
||||
|
@ -162,7 +167,9 @@ describe( 'Products', () => {
|
|||
product_types: [ 'downloads' ],
|
||||
},
|
||||
} ) );
|
||||
const { queryByText, getByRole, queryByRole } = render( <Products /> );
|
||||
const { queryByText, getByRole, queryAllByRole } = render(
|
||||
<Products />
|
||||
);
|
||||
|
||||
expect( queryByText( 'View more product types' ) ).toBeInTheDocument();
|
||||
|
||||
|
@ -170,11 +177,12 @@ describe( 'Products', () => {
|
|||
getByRole( 'button', { name: 'View more product types' } )
|
||||
);
|
||||
|
||||
await waitFor( () =>
|
||||
expect( queryByRole( 'menu' )?.childElementCount ).toBe(
|
||||
await waitFor( () => {
|
||||
const productTypeList = queryAllByRole( 'menu' )?.[ 0 ];
|
||||
expect( productTypeList?.childElementCount ).toBe(
|
||||
productTypes.length
|
||||
)
|
||||
);
|
||||
} );
|
||||
|
||||
expect( queryByText( 'View less product types' ) ).toBeInTheDocument();
|
||||
} );
|
||||
|
@ -237,8 +245,10 @@ describe( 'Products', () => {
|
|||
|
||||
it( 'should render stacked layout', async () => {
|
||||
const { container } = render( <Products /> );
|
||||
|
||||
expect(
|
||||
container.getElementsByClassName( 'woocommerce-products-stack' )
|
||||
).toHaveLength( 1 );
|
||||
.length
|
||||
).toBeGreaterThanOrEqual( 1 );
|
||||
} );
|
||||
} );
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: update
|
||||
|
||||
Update the footer section in "Add products" task
|
|
@ -25,7 +25,7 @@
|
|||
"remote-inbox-notifications": true,
|
||||
"remote-free-extensions": true,
|
||||
"payment-gateway-suggestions": true,
|
||||
"printful": false,
|
||||
"printful": true,
|
||||
"settings": false,
|
||||
"shipping-label-banner": true,
|
||||
"subscriptions": true,
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
"payment-gateway-suggestions": true,
|
||||
"product-pre-publish-modal": false,
|
||||
"product-custom-fields": true,
|
||||
"printful": false,
|
||||
"printful": true,
|
||||
"remote-inbox-notifications": true,
|
||||
"remote-free-extensions": true,
|
||||
"settings": false,
|
||||
|
|
Loading…
Reference in New Issue