From a3d7e53a033ea96dd8e8890124a7a2e7e5ed5d0a Mon Sep 17 00:00:00 2001 From: Eason Su Date: Fri, 16 Sep 2022 18:45:06 +0800 Subject: [PATCH 1/9] Set different titles for the free listings setup of the onboarding and editing pages --- .../configure-product-listings/hero/index.js | 10 +++++----- .../free-listings/setup-free-listings/index.js | 4 +++- js/src/edit-free-campaign/index.js | 4 ++++ js/src/setup-mc/setup-stepper/saved-setup-stepper.js | 4 ++++ 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/js/src/components/free-listings/configure-product-listings/hero/index.js b/js/src/components/free-listings/configure-product-listings/hero/index.js index bde2fd05a5..db5580d21c 100644 --- a/js/src/components/free-listings/configure-product-listings/hero/index.js +++ b/js/src/components/free-listings/configure-product-listings/hero/index.js @@ -12,16 +12,16 @@ import './index.scss'; /** * Hero element for free listing configuration. + * + * @param {Object} props React props. + * @param {JSX.Element} props.headerTitle Title in the header block. */ -const Hero = () => { +const Hero = ( { headerTitle } ) => { return (

diff --git a/js/src/components/free-listings/setup-free-listings/index.js b/js/src/components/free-listings/setup-free-listings/index.js index cab01f6760..ddf7545c45 100644 --- a/js/src/components/free-listings/setup-free-listings/index.js +++ b/js/src/components/free-listings/setup-free-listings/index.js @@ -73,6 +73,7 @@ const getSettings = ( values ) => { * @param {(newValue: Object) => void} [props.onShippingTimesChange] Callback called with new data once shipping times are changed. Forwarded from {@link Form.Props.onChange}. * @param {() => void} [props.onContinue] Callback called once continue button is clicked. Could be async. While it's being resolved the form would turn into a saving state. * @param {string} [props.submitLabel] Submit button label, to be forwarded to `FormContent`. + * @param {JSX.Element} props.headerTitle Title in the header block of this setup. */ const SetupFreeListings = ( { targetAudience, @@ -86,6 +87,7 @@ const SetupFreeListings = ( { onShippingTimesChange = noop, onContinue = noop, submitLabel, + headerTitle, } ) => { const [ saving, setSaving ] = useState( false ); const formPropsDelegateeRef = useRef( [] ); @@ -137,7 +139,7 @@ const SetupFreeListings = ( { return (

- +
{ backHref={ dashboardURL } /> {} } ) => { ), content: ( Date: Fri, 16 Sep 2022 18:49:41 +0800 Subject: [PATCH 2/9] Change the URLs of the learn more links to the formal ones in the ads setup of onboarding flow --- .../setup-paid-ads/paid-ads-features-section.js | 5 ++--- .../setup-paid-ads/product-feed-status-section.js | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/js/src/setup-mc/setup-stepper/setup-paid-ads/paid-ads-features-section.js b/js/src/setup-mc/setup-stepper/setup-paid-ads/paid-ads-features-section.js index 006d1eb164..14099026e6 100644 --- a/js/src/setup-mc/setup-stepper/setup-paid-ads/paid-ads-features-section.js +++ b/js/src/setup-mc/setup-stepper/setup-paid-ads/paid-ads-features-section.js @@ -52,9 +52,8 @@ function FeatureList() { ); } -// TODO: `href` is not yet ready. Will be added later. /** - * @fires gla_documentation_link_click with `{ context: 'setup-paid-ads', link_id: 'paid-ads-with-performance-max-campaigns-learn-more', href: 'https://example.com' }` + * @fires gla_documentation_link_click with `{ context: 'setup-paid-ads', link_id: 'paid-ads-with-performance-max-campaigns-learn-more', href: 'https://support.google.com/google-ads/answer/10724817' }` */ /** @@ -93,7 +92,7 @@ export default function PaidAdsFeaturesSection( { { __( 'Learn more', 'google-listings-and-ads' ) } diff --git a/js/src/setup-mc/setup-stepper/setup-paid-ads/product-feed-status-section.js b/js/src/setup-mc/setup-stepper/setup-paid-ads/product-feed-status-section.js index 4fa748a599..bf5cc7b9db 100644 --- a/js/src/setup-mc/setup-stepper/setup-paid-ads/product-feed-status-section.js +++ b/js/src/setup-mc/setup-stepper/setup-paid-ads/product-feed-status-section.js @@ -42,9 +42,8 @@ function ProductQuantity( { quantity } ) { ); } -// TODO: `href`` is not yet ready. Will be added later. /** - * @fires gla_documentation_link_click with `{ context: 'setup-paid-ads', link_id: 'product-feed-status-learn-more', href: 'https://example.com' }` + * @fires gla_documentation_link_click with `{ context: 'setup-paid-ads', link_id: 'product-feed-status-learn-more', href: 'https://support.google.com/merchants/answer/7439882' }` */ /** @@ -80,7 +79,7 @@ export default function ProductFeedStatusSection() { { __( 'Learn more', 'google-listings-and-ads' ) } From af323141f3e0f8a7b1f1e1adb4e4dc87abf5b0bb Mon Sep 17 00:00:00 2001 From: Eason Su Date: Fri, 16 Sep 2022 18:53:39 +0800 Subject: [PATCH 3/9] Update the FAQs in the ads setup --- js/src/components/faqs-panel/index.scss | 3 +- js/src/components/paid-ads/faqs-section.js | 118 ++++++++++++--------- 2 files changed, 71 insertions(+), 50 deletions(-) diff --git a/js/src/components/faqs-panel/index.scss b/js/src/components/faqs-panel/index.scss index 371bc9ed43..df68272026 100644 --- a/js/src/components/faqs-panel/index.scss +++ b/js/src/components/faqs-panel/index.scss @@ -1,6 +1,7 @@ .gla-faqs-panel { .components-panel__row { - display: block; + flex-direction: column; + gap: 1.5em; } .components-panel__body-title { diff --git a/js/src/components/paid-ads/faqs-section.js b/js/src/components/paid-ads/faqs-section.js index 008d2d69ef..9d547a1c0c 100644 --- a/js/src/components/paid-ads/faqs-section.js +++ b/js/src/components/paid-ads/faqs-section.js @@ -2,16 +2,79 @@ * External dependencies */ import { __ } from '@wordpress/i18n'; -import { Panel, PanelBody, PanelRow } from '@wordpress/components'; -import { recordEvent } from '@woocommerce/tracks'; /** * Internal dependencies */ import Section from '.~/wcdl/section'; +import FaqsPanel from '.~/components/faqs-panel'; + +const faqItems = [ + { + trackId: 'how-does-google-ads-work', + question: __( 'How does Google Ads work?', 'google-listings-and-ads' ), + answer: __( + 'Google Ads works by displaying your ad when people search online for the products and services you offer. By leveraging smart technology, Google Ads helps get your ads in front of potential customers at just the moment they’re ready to take action.', + 'google-listings-and-ads' + ), + }, + { + trackId: 'what-is-a-product-feed', + question: __( 'What is a product feed?', 'google-listings-and-ads' ), + answer: __( + 'Your product feed is the central data source that contains a list of products you want to advertise through Merchant Center. By default, Google syncs all active products from your WooCommerce inventory. You can choose to exclude products later after this setup.', + 'google-listings-and-ads' + ), + }, + { + trackId: 'how-much-does-google-ads-cost', + question: __( + 'How much does Google Ads cost?', + 'google-listings-and-ads' + ), + answer: __( + 'With Google Ads, you decide how much to spend. There’s no minimum spend, and no time commitment. Your costs may vary from day to day, but you won’t be charged more than your daily budget times the number of days in a month. You pay only for the actual clicks and calls that your ad receives.', + 'google-listings-and-ads' + ), + }, + { + trackId: 'where-will-my-products-appear', + question: __( + 'Where will my products appear?', + 'google-listings-and-ads' + ), + answer: ( + <> +
+ { __( + 'If you’re selling in the US, then eligible free listings can appear in search results across Google Search, Google Images, and the Google Shopping tab. If you’re selling outside the US, free listings will appear on the Shopping tab.', + 'google-listings-and-ads' + ) } +
+
+ { __( + 'If you’re running a Performance Max Campaign, your approved products can appear on Google Search, Google Maps, the Shopping tab, Gmail, Youtube, the Google Display Network, and Discover feed.', + 'google-listings-and-ads' + ) } +
+ + ), + }, + { + trackId: 'how-long-until-i-see-results-with-google-ads', + question: __( + 'How long until I see results with Google Ads?', + 'google-listings-and-ads' + ), + answer: __( + 'Google’s Performance Max campaigns are powered by machine learning models. These models train and adapt based on the data you provide in your campaign. This means performance optimization can take time. Typically, this learning process takes 1—2 weeks.', + 'google-listings-and-ads' + ), + }, +]; /** - * Clicking on faq items to collapse or expand it in the Setup Ads page + * Clicking on faq items to collapse or expand it in the Onboarding Flow or creating/editing a campaign * * @event gla_setup_ads_faq * @property {string} id FAQ identifier @@ -19,57 +82,14 @@ import Section from '.~/wcdl/section'; */ /** + * Renders a toggleable FAQs section about Google Ads. + * * @fires gla_setup_ads_faq */ const FaqsSection = () => { - const getPanelToggleHandler = ( id ) => ( isOpened ) => { - recordEvent( 'gla_setup_ads_faq', { - id, - action: isOpened ? 'expand' : 'collapse', - } ); - }; - return (
- - - - { __( - 'You only pay when someone clicks on your product ads to your store.', - 'google-listings-and-ads' - ) } - - - - - { __( - 'Some days you might spend less than your daily average, and on others you might spend up to 4 times as much. But over a month, your total spend across the month will be approximately as calculated above.', - 'google-listings-and-ads' - ) } - - - +
); }; From 2a885769702204a31cd1712ac92bb33bbc55ba67 Mon Sep 17 00:00:00 2001 From: Eason Su Date: Fri, 16 Sep 2022 19:00:30 +0800 Subject: [PATCH 4/9] Add the `disabledLeft` prop to Section component for showing the left side in disabled style. --- js/src/wcdl/section/index.js | 3 +++ js/src/wcdl/section/index.scss | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/js/src/wcdl/section/index.js b/js/src/wcdl/section/index.js index b4e8a5918c..07ad59b10f 100644 --- a/js/src/wcdl/section/index.js +++ b/js/src/wcdl/section/index.js @@ -19,6 +19,7 @@ import './index.scss'; * @param {JSX.Element} [props.topContent] Content at the top of the section title. * @param {JSX.Element} [props.children] Section content at the right side. * @param {boolean} [props.disabled] Whether display the whole section in disabled style. + * @param {boolean} [props.disabledLeft] Whether display the left side of section in disabled style. */ const Section = ( { className, @@ -27,10 +28,12 @@ const Section = ( { topContent, children, disabled, + disabledLeft, } ) => { const sectionClassName = classnames( 'wcdl-section', disabled ? 'wcdl-section--is-disabled' : false, + disabledLeft ? 'wcdl-section--is-disabled-left' : false, className ); diff --git a/js/src/wcdl/section/index.scss b/js/src/wcdl/section/index.scss index d37eb437b6..a0872f4cf5 100644 --- a/js/src/wcdl/section/index.scss +++ b/js/src/wcdl/section/index.scss @@ -3,7 +3,8 @@ flex-direction: column; margin-bottom: var(--large-gap); - &--is-disabled { + &--is-disabled, + &--is-disabled-left header { opacity: 0.5; } From c3440ce27894f793a8c903e7bd8d6880f9655f64 Mon Sep 17 00:00:00 2001 From: Eason Su Date: Fri, 16 Sep 2022 18:58:49 +0800 Subject: [PATCH 5/9] Add the `isPreconditionReady` property to useGoogleMCAccount hook for telling whether it's ready to continue the connection processing. --- .../google-mc-account-card.js | 13 ++++++------ js/src/data/selectors.js | 6 ++++++ js/src/hooks/useGoogleMCAccount.js | 20 +++++++++++++++++++ 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/js/src/components/google-mc-account-card/google-mc-account-card.js b/js/src/components/google-mc-account-card/google-mc-account-card.js index 7f4f006fb6..90cd10c9c9 100644 --- a/js/src/components/google-mc-account-card/google-mc-account-card.js +++ b/js/src/components/google-mc-account-card/google-mc-account-card.js @@ -8,18 +8,17 @@ import DisabledCard from './disabled-card'; import NonConnected from './non-connected'; const GoogleMCAccountCard = () => { - const { hasFinishedResolution, googleMCAccount } = useGoogleMCAccount(); + const { + hasFinishedResolution, + isPreconditionReady, + googleMCAccount, + } = useGoogleMCAccount(); if ( ! hasFinishedResolution ) { return ; } - /** - * If there is no googleMCAccount, this means users have not connected their Google account, - * or have not granted necessary access permissions for Google Merchant Center, - * so we show a DisabledCard here. - */ - if ( ! googleMCAccount ) { + if ( ! isPreconditionReady ) { return ; } diff --git a/js/src/data/selectors.js b/js/src/data/selectors.js index c75f4c3523..866e803db1 100644 --- a/js/src/data/selectors.js +++ b/js/src/data/selectors.js @@ -30,6 +30,12 @@ export const getSettings = ( state ) => { * @property {string|''} displayName Owner name. Available for jetpack owner. */ +/** + * @typedef {Object} GoogleMCAccount + * @property {number} id Account ID. It's 0 if not yet connected. + * @property {string} status Connection status. + */ + /** * Select jetpack connection state. * diff --git a/js/src/hooks/useGoogleMCAccount.js b/js/src/hooks/useGoogleMCAccount.js index 89d4631807..be186a0d86 100644 --- a/js/src/hooks/useGoogleMCAccount.js +++ b/js/src/hooks/useGoogleMCAccount.js @@ -9,6 +9,21 @@ import { useSelect } from '@wordpress/data'; import { STORE_KEY } from '.~/data/constants'; import useGoogleAccount from './useGoogleAccount'; +/** + * @typedef {import('.~/data/selectors').GoogleMCAccount} GoogleMCAccount + * + * @typedef {Object} GoogleMCAccountPayload + * @property {GoogleMCAccount|undefined} googleMCAccount The connection data of Google Merchant Center account associated with GLA. + * @property {boolean} isResolving Whether resolution is in progress. + * @property {boolean} hasFinishedResolution Whether resolution has completed. + * @property {boolean} isPreconditionReady Whether the precondition of continued connection processing is fulfilled. + */ + +/** + * A hook to load the connection data of Google Merchant Center account. + * + * @return {GoogleMCAccountPayload} The data and its state. + */ const useGoogleMCAccount = () => { const { google, @@ -24,6 +39,10 @@ const useGoogleMCAccount = () => { googleMCAccount: undefined, isResolving: isResolvingGoogle, hasFinishedResolution: hasFinishedResolutionGoogle, + // If a user has not yet connected their Google account or the connected Google account + // has not been granted necessary access permissions for Google Merchant Center, then + // the precondition doesn't meet. + isPreconditionReady: false, }; } @@ -39,6 +58,7 @@ const useGoogleMCAccount = () => { hasFinishedResolution: hasFinishedResolution( 'getGoogleMCAccount' ), + isPreconditionReady: true, }; }, [ From e68dcfa4073dd151318911b69cc92038300edd54 Mon Sep 17 00:00:00 2001 From: Eason Su Date: Fri, 16 Sep 2022 19:02:32 +0800 Subject: [PATCH 6/9] Add the disclaimer of Comparison Shopping Service to the accounts setup of onboarding flow --- .../setup-stepper/setup-accounts/faqs.js | 13 ++-- .../setup-stepper/setup-accounts/index.js | 72 ++++++++++++++++++- .../setup-stepper/setup-accounts/index.scss | 20 ++++++ 3 files changed, 94 insertions(+), 11 deletions(-) create mode 100644 js/src/setup-mc/setup-stepper/setup-accounts/index.scss diff --git a/js/src/setup-mc/setup-stepper/setup-accounts/faqs.js b/js/src/setup-mc/setup-stepper/setup-accounts/faqs.js index d844ccf882..97a2aa5072 100644 --- a/js/src/setup-mc/setup-stepper/setup-accounts/faqs.js +++ b/js/src/setup-mc/setup-stepper/setup-accounts/faqs.js @@ -7,7 +7,6 @@ import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import Section from '.~/wcdl/section'; import FaqsPanel from '.~/components/faqs-panel'; import AppDocumentationLink from '.~/components/app-documentation-link'; @@ -68,13 +67,11 @@ const faqItems = [ */ const Faqs = () => { return ( -
- -
+ ); }; diff --git a/js/src/setup-mc/setup-stepper/setup-accounts/index.js b/js/src/setup-mc/setup-stepper/setup-accounts/index.js index b0d0c8dc34..4437415e1d 100644 --- a/js/src/setup-mc/setup-stepper/setup-accounts/index.js +++ b/js/src/setup-mc/setup-stepper/setup-accounts/index.js @@ -3,6 +3,7 @@ */ import { Button } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; +import { createInterpolateElement } from '@wordpress/element'; /** * Internal dependencies @@ -15,17 +16,75 @@ import StepContent from '.~/components/stepper/step-content'; import StepContentHeader from '.~/components/stepper/step-content-header'; import StepContentFooter from '.~/components/stepper/step-content-footer'; import Section from '.~/wcdl/section'; +import AppDocumentationLink from '.~/components/app-documentation-link'; import VerticalGapLayout from '.~/components/vertical-gap-layout'; import WPComAccountCard from '.~/components/wpcom-account-card'; import GoogleAccountCard from '.~/components/google-account-card'; import GoogleMCAccountCard from '.~/components/google-mc-account-card'; import Faqs from './faqs'; +import './index.scss'; + +/** + * Renders the disclaimer of Comparison Shopping Service (CSS). + * + * @fires gla_documentation_link_click with `{ context: 'setup-mc-accounts', link_id: 'comparison-shopping-services', href: 'https://support.google.com/merchants/topic/9080307' }` + * @fires gla_documentation_link_click with `{ context: 'setup-mc-accounts', link_id: 'comparison-shopping-partners-find-a-partner', href: 'https://comparisonshoppingpartners.withgoogle.com/find_a_partner/' }` + */ +const GoogleMCDisclaimer = () => { + return ( + <> +

+ { createInterpolateElement( + __( + 'If you are in the European Economic Area or Switzerland, your Merchant Center account must be associated with a Comparison Shopping Service (CSS). Please find more information at Google Merchant Center Help website.', + 'google-listings-and-ads' + ), + { + link: ( + + ), + } + ) } +

+

+ { createInterpolateElement( + __( + 'If you create a new Merchant Center account through this application, it will be associated with Google Shopping, Google’s CSS, by default. You can change the CSS associated with your account at any time. Please find more information about our CSS Partners here.', + 'google-listings-and-ads' + ), + { + link: ( + + ), + } + ) } +

+

+ { __( + 'Once you have set up your Merchant Center account you can use our onboarding tool regardless of which CSS you use.', + 'google-listings-and-ads' + ) } +

+ + ); +}; const SetupAccounts = ( props ) => { const { onContinue = () => {} } = props; const { jetpack } = useJetpackAccount(); const { google, scope } = useGoogleAccount(); - const { googleMCAccount } = useGoogleMCAccount(); + const { + googleMCAccount, + isPreconditionReady: isGMCPreconditionReady, + } = useGoogleMCAccount(); /** * When jetpack is loading, or when google account is loading, @@ -62,6 +121,7 @@ const SetupAccounts = ( props ) => { ) } />
{ -
- +
} + disabledLeft={ ! isGMCPreconditionReady } + > + + +
+ ); + } + return ( <>