woocommerce/plugins/woocommerce-admin/client/customize-store/assembler-hub/sidebar/sidebar-navigation-screen-h...

413 lines
12 KiB
TypeScript
Raw Normal View History

/* eslint-disable @woocommerce/dependency-group */
/* eslint-disable @typescript-eslint/ban-ts-comment */
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import {
createInterpolateElement,
useCallback,
useMemo,
useEffect,
useContext,
useState,
} from '@wordpress/element';
import { Link } from '@woocommerce/components';
import { Spinner, Button, Modal, CheckboxControl } from '@wordpress/components';
// @ts-expect-error Missing type.
import { unlock } from '@wordpress/edit-site/build-module/lock-unlock';
// @ts-expect-error No types for this exist yet.
import { store as coreStore } from '@wordpress/core-data';
import {
privateApis as blockEditorPrivateApis,
__experimentalBlockPatternsList as BlockPatternList,
store as blockEditorStore,
// @ts-expect-error No types for this exist yet.
} from '@wordpress/block-editor';
// @ts-expect-error Missing type in core-data.
import { useIsSiteEditorLoading } from '@wordpress/edit-site/build-module/components/layout/hooks';
import interpolateComponents from '@automattic/interpolate-components';
/**
* Internal dependencies
*/
import { SidebarNavigationScreen } from './sidebar-navigation-screen';
import { ADMIN_URL } from '~/utils/admin-settings';
import { useEditorBlocks } from '../hooks/use-editor-blocks';
import { useHomeTemplates } from '../hooks/use-home-templates';
import { BlockInstance } from '@wordpress/blocks';
import { useSelectedPattern } from '../hooks/use-selected-pattern';
import { useEditorScroll } from '../hooks/use-editor-scroll';
import { FlowType } from '~/customize-store/types';
import { CustomizeStoreContext } from '~/customize-store/assembler-hub';
import { select, useSelect } from '@wordpress/data';
import { trackEvent } from '~/customize-store/tracking';
import {
PRODUCT_HERO_PATTERN_BUTTON_STYLE,
findButtonBlockInsideCoverBlockProductHeroPatternAndUpdate,
} from '../utils/hero-pattern';
import { isEqual } from 'lodash';
import { COLOR_PALETTES } from './global-styles/color-palette-variations/constants';
import { OPTIONS_STORE_NAME } from '@woocommerce/data';
import { useNetworkStatus } from '~/utils/react-hooks/use-network-status';
import { isIframe, sendMessageToParent } from '~/customize-store/utils';
const { GlobalStylesContext } = unlock( blockEditorPrivateApis );
export const SidebarNavigationScreenHomepage = ( {
onNavigateBackClick,
}: {
onNavigateBackClick: () => void;
} ) => {
const { scroll } = useEditorScroll( {
editorSelector: '.woocommerce-customize-store__block-editor iframe',
scrollDirection: 'top',
} );
const { isLoading, homeTemplates } = useHomeTemplates();
// eslint-disable-next-line react-hooks/exhaustive-deps
const { selectedPattern, setSelectedPattern } = useSelectedPattern();
const currentTemplate = useSelect(
( sel ) =>
// @ts-expect-error No types for this exist yet.
sel( coreStore ).__experimentalGetTemplateForLink( '/' ),
[]
);
const [ blocks, , onChange ] = useEditorBlocks(
'wp_template',
currentTemplate?.id ?? ''
);
const onClickPattern = useCallback(
( pattern, selectedBlocks ) => {
if ( pattern === selectedPattern ) {
return;
}
setSelectedPattern( pattern );
onChange(
[ blocks[ 0 ], ...selectedBlocks, blocks[ blocks.length - 1 ] ],
{ selection: {} }
);
scroll();
},
[ selectedPattern, setSelectedPattern, onChange, blocks, scroll ]
);
const isEditorLoading = useIsSiteEditorLoading();
// @ts-expect-error No types for this exist yet.
const { user } = useContext( GlobalStylesContext );
const isActiveNewNeutralVariation = useMemo(
() =>
isEqual( COLOR_PALETTES[ 0 ].settings.color, user.settings.color ),
[ user ]
);
const homePatterns = useMemo( () => {
return Object.entries( homeTemplates ).map(
( [ templateName, patterns ] ) => {
if ( templateName === 'template1' ) {
return {
name: templateName,
title: templateName,
blocks: patterns.reduce(
( acc: BlockInstance[], pattern ) => {
const parsedPattern = unlock(
select( blockEditorStore )
).__experimentalGetParsedPattern(
pattern.name
);
if ( ! parsedPattern ) {
return acc;
}
if ( ! isActiveNewNeutralVariation ) {
return [ ...acc, ...parsedPattern.blocks ];
}
const updatedBlocks =
findButtonBlockInsideCoverBlockProductHeroPatternAndUpdate(
parsedPattern.blocks,
( buttonBlock: BlockInstance ) => {
buttonBlock.attributes.style =
PRODUCT_HERO_PATTERN_BUTTON_STYLE;
}
);
return [ ...acc, ...updatedBlocks ];
},
[]
),
blockTypes: [ '' ],
categories: [ '' ],
content: '',
source: '',
};
}
return {
name: templateName,
title: templateName,
blocks: patterns.reduce(
( acc: BlockInstance[], pattern ) => {
const parsedPattern = unlock(
select( blockEditorStore )
).__experimentalGetParsedPattern( pattern.name );
if ( ! parsedPattern ) {
return acc;
}
return [ ...acc, ...parsedPattern.blocks ];
},
[]
),
blockTypes: [ '' ],
categories: [ '' ],
content: '',
source: '',
};
}
);
}, [ homeTemplates, isActiveNewNeutralVariation ] );
useEffect( () => {
CYS - E2E tests: add homepage picker E2E tests (#46127) * Update checks for sale price * Update checks for product images * Update checks for linked products * Update default theme for e2e environment to twentytwentythree * Fix basic spec * Fix locator product block editor test * Make cart.spec.js theme agnostic * Updated mini-cart.spec.js * Updated cart-checkout-block-calculate-tax.spec.js * Updated cart-block.spec.js * Fix cart-block.spec.js * Update account-email-receiving.spec.js * CYS - E2E tests: fix flaky assembler-hub test * Use a value for pr_number * Update condition for the Slack alert job * Remove pr_number argument * Add changefile(s) from automation for the following project(s): woocommerce * Update conditions for the publish reports jobs * Rename jobs * Send GITHUB_SHA to report * Check for skipped status * Include Slack notification step in the reporting job * Rename jobs * Test update to trigger api tests * Revert test change for api tests * fix flakiness * Test change to trigger all tests * Test slack alert condition * add waitUntil * Force a test failure * Revert all test changes * improve logic * fix build error * try now * Update product-inventory-block-editor.spec.js * fix unit test * fix flakiness * Update checkout-block.spec.js * Update checkout-block.spec.js * Add utils/order * Remove the check for more items than the coupon max amount as it fails with block themes * Update create-simple-product-block-editor.spec.js * Update cart-checkout-calculate-tax.spec.js * Update checkout-block.spec.js * Update create-simple-product-block-editor.spec.js * Update wordpress-post.spec.js * Update my-account-addresses.spec.js * Update my-account-create-account.spec.js * Update my-account-downloads.spec.js * Update my-account-pay-order.spec.js * Update my-account.spec.js * Update order-email-receiving.spec.js * Update product-grouped.spec.js * Update product-simple.spec.js * Update product-tags-attributes.spec.js * Update product-variable.spec.js * Update shop-search-browse-sort.spec.js * Update checkout.spec.js * Update checkout-login.spec.js * Update checkout-create-account.spec.js * Update wordpress-post.spec.js * Update cart-checkout-calculate-tax.spec.js * Update wordpress-post.spec.js * Update mini-cart.spec.js * Remove logging of order id * Remove the log-out action as it invalidates the customer state and breaks other tests * Use disableWelcomeModal * CYS - E2E tests: add homepage picker E2E tests * update snapshots * add snapshots * use screenshot tests+ * use snapshot * try now * update snapshot * remove wp-container snapshot * remove only * add comment * add snapshot * enable color picker tests * revert to twentytwentythree * restore changes related to the color picker * use default theme --------- Co-authored-by: Adrian Moldovan <adim.moldovan@gmail.com> Co-authored-by: Adrian Moldovan <3854374+adimoldovan@users.noreply.github.com> Co-authored-by: github-actions <github-actions@github.com>
2024-04-08 10:43:41 +00:00
if (
selectedPattern ||
! blocks.length ||
! homePatterns.length ||
isLoading ||
isEditorLoading
) {
return;
}
const currentSelectedPattern = homePatterns.find( ( patterns ) => {
//'blocks' contains all blocks in the template, including the
// header and footer blocks, while the 'patterns.blocks' does
// not. For that reason we are removing the first and last
// blocks from the 'blocks' to be able to compare then
const homeBlocks = blocks.slice( 1, blocks.length - 1 );
if ( patterns.blocks.length !== homeBlocks.length ) {
return false;
}
return homeBlocks.every(
( block, i ) => block.name === patterns.blocks[ i ].name
);
} );
setSelectedPattern( currentSelectedPattern );
// eslint-disable-next-line react-hooks/exhaustive-deps -- we don't want to re-run this effect when currentSelectedPattern changes
CYS - E2E tests: add homepage picker E2E tests (#46127) * Update checks for sale price * Update checks for product images * Update checks for linked products * Update default theme for e2e environment to twentytwentythree * Fix basic spec * Fix locator product block editor test * Make cart.spec.js theme agnostic * Updated mini-cart.spec.js * Updated cart-checkout-block-calculate-tax.spec.js * Updated cart-block.spec.js * Fix cart-block.spec.js * Update account-email-receiving.spec.js * CYS - E2E tests: fix flaky assembler-hub test * Use a value for pr_number * Update condition for the Slack alert job * Remove pr_number argument * Add changefile(s) from automation for the following project(s): woocommerce * Update conditions for the publish reports jobs * Rename jobs * Send GITHUB_SHA to report * Check for skipped status * Include Slack notification step in the reporting job * Rename jobs * Test update to trigger api tests * Revert test change for api tests * fix flakiness * Test change to trigger all tests * Test slack alert condition * add waitUntil * Force a test failure * Revert all test changes * improve logic * fix build error * try now * Update product-inventory-block-editor.spec.js * fix unit test * fix flakiness * Update checkout-block.spec.js * Update checkout-block.spec.js * Add utils/order * Remove the check for more items than the coupon max amount as it fails with block themes * Update create-simple-product-block-editor.spec.js * Update cart-checkout-calculate-tax.spec.js * Update checkout-block.spec.js * Update create-simple-product-block-editor.spec.js * Update wordpress-post.spec.js * Update my-account-addresses.spec.js * Update my-account-create-account.spec.js * Update my-account-downloads.spec.js * Update my-account-pay-order.spec.js * Update my-account.spec.js * Update order-email-receiving.spec.js * Update product-grouped.spec.js * Update product-simple.spec.js * Update product-tags-attributes.spec.js * Update product-variable.spec.js * Update shop-search-browse-sort.spec.js * Update checkout.spec.js * Update checkout-login.spec.js * Update checkout-create-account.spec.js * Update wordpress-post.spec.js * Update cart-checkout-calculate-tax.spec.js * Update wordpress-post.spec.js * Update mini-cart.spec.js * Remove logging of order id * Remove the log-out action as it invalidates the customer state and breaks other tests * Use disableWelcomeModal * CYS - E2E tests: add homepage picker E2E tests * update snapshots * add snapshots * use screenshot tests+ * use snapshot * try now * update snapshot * remove wp-container snapshot * remove only * add comment * add snapshot * enable color picker tests * revert to twentytwentythree * restore changes related to the color picker * use default theme --------- Co-authored-by: Adrian Moldovan <adim.moldovan@gmail.com> Co-authored-by: Adrian Moldovan <3854374+adimoldovan@users.noreply.github.com> Co-authored-by: github-actions <github-actions@github.com>
2024-04-08 10:43:41 +00:00
}, [ blocks, homePatterns, isLoading, isEditorLoading ] );
const { context, sendEvent } = useContext( CustomizeStoreContext );
const aiOnline = context.flowType === FlowType.AIOnline;
const title = aiOnline
? __( 'Change your homepage', 'woocommerce' )
: __( 'Choose your homepage', 'woocommerce' );
const sidebarMessage = aiOnline
? __(
'Based on the most successful stores in your industry and location, our AI tool has recommended this template for your business. Prefer a different layout? Choose from the templates below now, or later via the <EditorLink>Editor</EditorLink>.',
'woocommerce'
)
: __(
'Create an engaging homepage by selecting one of our pre-designed layouts. You can continue customizing this page, including the content, later via the <EditorLink>Editor</EditorLink>.',
'woocommerce'
);
const isNetworkOffline = useNetworkStatus();
const isPTKPatternsAPIAvailable = context.isPTKPatternsAPIAvailable;
const trackingAllowed = useSelect( ( sel ) =>
sel( OPTIONS_STORE_NAME ).getOption( 'woocommerce_allow_tracking' )
);
const isTrackingDisallowed = trackingAllowed === 'no' || ! trackingAllowed;
let notice;
if ( isNetworkOffline ) {
notice = __(
"Looks like we can't detect your network. Please double-check your internet connection and refresh the page.",
'woocommerce'
);
} else if ( ! isPTKPatternsAPIAvailable ) {
notice = __(
"Unfortunately, we're experiencing some technical issues — please come back later to access more patterns.",
'woocommerce'
);
} else if ( isTrackingDisallowed ) {
notice = __(
'Opt in to <OptInModal>usage tracking</OptInModal> to get access to more patterns.',
'woocommerce'
);
}
const [ isModalOpen, setIsModalOpen ] = useState( false );
const openModal = () => setIsModalOpen( true );
const closeModal = () => setIsModalOpen( false );
const [ optInDataSharing, setIsOptInDataSharing ] =
useState< boolean >( true );
const optIn = () => {
trackEvent(
'customize_your_store_assembler_hub_opt_in_usage_tracking'
);
};
const skipOptIn = () => {
trackEvent(
'customize_your_store_assembler_hub_skip_opt_in_usage_tracking'
);
};
return (
<SidebarNavigationScreen
title={ title }
onNavigateBackClick={ onNavigateBackClick }
description={ createInterpolateElement( sidebarMessage, {
EditorLink: (
<Link
onClick={ () => {
trackEvent(
'customize_your_store_assembler_hub_editor_link_click',
{
source: 'homepage',
}
);
window.open(
`${ ADMIN_URL }site-editor.php`,
'_blank'
);
return false;
} }
href=""
/>
),
} ) }
content={
<div className="woocommerce-customize-store__sidebar-homepage-content">
<div className="edit-site-sidebar-navigation-screen-patterns__group-homepage">
{ /* This is necessary to fix this issue: https://github.com/woocommerce/woocommerce/issues/45711
If the user switch the homepage while the editor is loading, header and footer could disappear.
For more details check: https://github.com/woocommerce/woocommerce/pull/45735
*/ }
{ isLoading || isEditorLoading ? (
<span className="components-placeholder__preview">
<Spinner />
</span>
) : (
<BlockPatternList
shownPatterns={ homePatterns }
blockPatterns={ homePatterns }
onClickPattern={ onClickPattern }
label={ 'Homepage' }
orientation="vertical"
category={ 'homepage' }
isDraggable={ false }
showTitlesAsTooltip={ false }
/>
) }
{ notice && (
<div className="woocommerce-customize-store_sidebar-patterns-upgrade-notice">
<h4>
{ __(
'Want more patterns?',
'woocommerce'
) }
</h4>
<p>
{ createInterpolateElement( notice, {
OptInModal: (
<Button
onClick={ () => {
openModal();
} }
variant="link"
/>
),
} ) }
</p>
{ isModalOpen && (
<Modal
className={
'woocommerce-customize-store__opt-in-usage-tracking-modal'
}
title={ __(
'Opt in to usage tracking',
'woocommerce'
) }
onRequestClose={ closeModal }
shouldCloseOnClickOutside={ false }
>
<CheckboxControl
className="core-profiler__checkbox"
label={ interpolateComponents( {
mixedString: __(
'I agree to share my data to tailor my store setup experience, get more relevant content, and help make WooCommerce better for everyone. You can opt out at any time in WooCommerce settings. {{link}}Learn more about usage tracking{{/link}}.',
'woocommerce'
),
components: {
link: (
<Link
href="https://woocommerce.com/usage-tracking?utm_medium=product"
target="_blank"
type="external"
/>
),
},
} ) }
checked={ optInDataSharing }
onChange={ setIsOptInDataSharing }
/>
<div className="woocommerce-customize-store__design-change-warning-modal-footer">
<Button
onClick={ () => {
skipOptIn();
closeModal();
} }
variant="link"
>
{ __(
'Cancel',
'woocommerce'
) }
</Button>
<Button
onClick={ () => {
optIn();
if ( isIframe( window ) ) {
sendMessageToParent( {
type: 'INSTALL_PATTERNS',
} );
} else {
sendEvent(
'INSTALL_PATTERNS'
);
}
} }
variant="primary"
disabled={ ! optInDataSharing }
>
{ __(
'Opt in',
'woocommerce'
) }
</Button>
</div>
</Modal>
) }
</div>
) }
</div>
</div>
}
/>
);
};