Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Onboarding: Connect to existing Ads account in Google Accounts Card #2640

Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
63b182b
Onboarding: Connect to existing Ads account in Google Accounts Card
ankitrox Oct 4, 2024
f73c4b2
Conflict resolution
ankitrox Oct 4, 2024
96e3ef2
Add rendering condition.
ankitrox Oct 4, 2024
7e73f98
Separate components for body and footer.
ankitrox Oct 4, 2024
ba5ca7b
Connected label styling
ankitrox Oct 4, 2024
df93a29
Pass isVisible prop to card component.
ankitrox Oct 4, 2024
6fce3e6
Use ref for keeping connected account selected.
ankitrox Oct 4, 2024
4173593
Add E2E tests.
ankitrox Oct 4, 2024
2466f7e
Fix lint css.
ankitrox Oct 4, 2024
a4d0eb4
Fix: E2E tests.
ankitrox Oct 8, 2024
f4fb5c2
Resolve conflicts and merge base branch.
ankitrox Oct 8, 2024
4ef6d25
Add styling for select.
ankitrox Oct 8, 2024
32864c3
CR feedback.
ankitrox Oct 14, 2024
2e6bcfe
Resolve conflicts and merge base branch.
ankitrox Oct 15, 2024
2b7c811
Update tests.
ankitrox Oct 15, 2024
aa6a670
Resolve conflicts and merge base branch.
ankitrox Oct 18, 2024
fbb199f
CR feedback.
ankitrox Oct 18, 2024
c20fbea
Fix: E2E tests.
ankitrox Oct 18, 2024
7c9c848
CR feedback.
ankitrox Oct 18, 2024
57023bc
Resolve conflicts and merge base branch.
ankitrox Oct 22, 2024
9617a63
CR feedback round 2.
ankitrox Oct 22, 2024
9475fa2
Fix: lint js.
ankitrox Oct 22, 2024
8a562d1
Rename ConnectCTA to ConnectButton.
asvinb Oct 22, 2024
693efd2
Merge branch 'feature/2567-kickoff-mc-ads-account-creation' into upda…
ankitrox Oct 22, 2024
63c4fba
Add nonInteractive prop to app-select.
asvinb Oct 22, 2024
a83c242
Merge branch 'update/2596-connect-ads-account' of github.com:woocomme…
asvinb Oct 22, 2024
73350ef
Merge branch 'feature/2567-kickoff-mc-ads-account-creation' into upda…
ankitrox Oct 22, 2024
d58278e
Fix: E2E.
ankitrox Oct 22, 2024
5c3c5c3
Resolve conflicts and merge base branch.
ankitrox Oct 23, 2024
47a0c3d
Merge branch 'feature/2567-kickoff-mc-ads-account-creation' into upda…
ankitrox Oct 23, 2024
713afc0
Remove redundant prop.
ankitrox Oct 23, 2024
5dd622e
Fix: E2E failing test.
ankitrox Oct 24, 2024
87c0686
Merge branch 'feature/2567-kickoff-mc-ads-account-creation' into upda…
ankitrox Oct 24, 2024
1299ad1
Merge branch 'feature/2567-kickoff-mc-ads-account-creation' into upda…
ankitrox Oct 24, 2024
efe843a
Resolve conflicts and merge base branch.
ankitrox Oct 24, 2024
da1a10e
Merge branch 'feature/2567-kickoff-mc-ads-account-creation' into upda…
ankitrox Oct 25, 2024
690067d
Fix: styling the tertiary link.
ankitrox Oct 25, 2024
fe1b2ec
Fix: lint css error.
ankitrox Oct 25, 2024
6f85011
Resolve conflicts and merge base branch.
ankitrox Oct 25, 2024
60a101a
Merge branch 'feature/2567-kickoff-mc-ads-account-creation' into upda…
ankitrox Oct 28, 2024
098b886
Fix: failing E2E test
ankitrox Oct 28, 2024
0046434
Fix: E2E tests.
ankitrox Oct 29, 2024
b449092
Merge branch 'feature/2509-consolidate-google-account-cards' into upd…
ankitrox Oct 29, 2024
31c35a8
CR feedback.
ankitrox Oct 29, 2024
f12e5e3
Update condition for isConnected google ads account.
ankitrox Oct 29, 2024
5632913
Use useGoogleAdsAccountReady hook.
ankitrox Oct 29, 2024
2724b0f
Revert the css change.
ankitrox Oct 29, 2024
e5edd82
Update AccountCard component
joemcgill Oct 30, 2024
4901758
Update ConnectAds to use new AccountCard
joemcgill Oct 30, 2024
0b23082
Update docblocks to AccountCard
joemcgill Oct 30, 2024
c7e0e70
Delete ConnectAccountCard
joemcgill Oct 30, 2024
4c9bb4b
Delete ConnectAccountCard (realy)
joemcgill Oct 30, 2024
79a66ff
Use ConnectButton component.
asvinb Oct 31, 2024
b77f799
Merge pull request #2658 from woocommerce/update/2596-connect-ads-ref…
asvinb Oct 31, 2024
e3bbd83
Add render conditions in the connected combo card.
asvinb Oct 31, 2024
84811bd
Update E2E tests.
asvinb Oct 31, 2024
4cba145
Make label non interactive.
asvinb Oct 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion js/src/components/ads-account-select-control/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import useExistingGoogleAdsAccounts from '.~/hooks/useExistingGoogleAdsAccounts'
*/
const AdsAccountSelectControl = ( props ) => {
const { existingAccounts } = useExistingGoogleAdsAccounts();
const accounts = props.accounts || existingAccounts;
asvinb marked this conversation as resolved.
Show resolved Hide resolved

const options = existingAccounts?.map( ( acc ) => ( {
const options = accounts?.map( ( acc ) => ( {
value: acc.id,
label: `${ acc.name } (${ acc.id })`,
} ) );
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* External dependencies
*/
import classNames from 'classnames';

/**
* Internal dependencies
*/
import Section from '.~/wcdl/section';
import AccountCard from '.~/components/account-card';
import './connect-account-card.scss';

/**
* ConnectAccountCard component renders an account card with a title, helper text, body, and footer sections.
*
* @param {Object} props Props.
* @param {string} props.title The title of the account card.
* @param {string} props.helperText The helper text for the account card.
* @param {JSX.Element} props.body The content for the body of the account card.
* @param {JSX.Element} props.footer The content for the footer of the account card.
* @param {string} props.className Additional class names for the account card.
asvinb marked this conversation as resolved.
Show resolved Hide resolved
* @return {JSX.Element} ConnectAccountCard component.
*/
const ConnectAccountCard = ( {
title,
helperText,
body,
footer,
className = '',
} ) => {
return (
<AccountCard
className={ classNames(
'gla-google-combo-service-account-card',
className
) }
title={ title }
helper={ helperText }
>
<Section.Card.Body className="gla-google-combo-service-account-card__body">
{ body }
</Section.Card.Body>
<Section.Card.Footer className="gla-google-combo-service-account-card__footer">
{ footer }
</Section.Card.Footer>
</AccountCard>
);
};

export default ConnectAccountCard;
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
.gla-google-combo-service-account-card {
padding: var(--large-gap);

> div {
display: flex;
flex-direction: column;
gap: calc(var(--main-gap) / 2);
}

.wcdl-section-card-body,
.gla-google-combo-service-account-card__body {
padding: 0;

.app-select-control {
flex-grow: 1;
}
}

.gla-google-combo-service-account-card__footer {
border: 0;
padding: 0;
}

.gla-connected-icon-label {
flex-basis: content;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import AdsAccountSelectControl from '.~/components/ads-account-select-control';
import ContentButtonLayout from '.~/components/content-button-layout';
import ConnectCTA from './connect-cta';
import LoadingLabel from '.~/components/loading-label/loading-label';

/**
* Clicking on the button to connect an existing Google Ads account.
*
* @event gla_ads_account_connect_button_click
asvinb marked this conversation as resolved.
Show resolved Hide resolved
* @property {number} id The account ID to be connected.
* @property {string} [context] Indicates the place where the button is located.
* @property {string} [step] Indicates the step in the onboarding process.
*/

/**
* ConnectAdsBody component.
*
* @param {Object} props Props.
* @param {Array} props.accounts Accounts.
* @param {Object} props.googleAdsAccount Google Ads account object.
* @param {boolean} props.isConnected Whether the account is connected.
* @param {Function} props.handleConnectClick Callback to handle the connect click.
* @param {boolean} props.isLoading Whether the card is in a loading state.
* @param {Function} props.setValue Callback to set the value.
* @param {string} props.value Ads account ID.
* @fires gla_ads_account_connect_button_click when "Connect" button is clicked.
* @return {JSX.Element} Body component.
*/
const ConnectAdsBody = ( {
accounts,
googleAdsAccount,
isConnected,
handleConnectClick,
eason9487 marked this conversation as resolved.
Show resolved Hide resolved
isLoading,
setValue,
eason9487 marked this conversation as resolved.
Show resolved Hide resolved
value,
} ) => {
const connectedAccount = accounts.filter( ( account ) => {
return isConnected && account.id === value;
} );

return (
<ContentButtonLayout>
<AdsAccountSelectControl
accounts={ isConnected ? connectedAccount : accounts }
asvinb marked this conversation as resolved.
Show resolved Hide resolved
value={ value }
onChange={ setValue }
autoSelectFirstOption={ true }
asvinb marked this conversation as resolved.
Show resolved Hide resolved
/>
{ isLoading ? (
<LoadingLabel
text={ __( 'Connecting…', 'google-listings-and-ads' ) }
/>
) : (
<ConnectCTA
isConnected={ isConnected }
id={ googleAdsAccount.id }
handleConnectClick={ handleConnectClick }
value={ value }
/>
) }
</ContentButtonLayout>
);
};

export default ConnectAdsBody;
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import AppButton from '.~/components/app-button';

/**
* Footer component.
*
* @return {JSX.Element} Footer component.
*/
const ConnectAdsFooter = ( { isConnected } ) => {
const text = isConnected
? __(
'Or, connect to a different Google Ads account',
'google-listings-and-ads'
)
: __(
'Or, create a new Google Ads account',
'google-listings-and-ads'
);
return <AppButton isTertiary>{ text }</AppButton>;
asvinb marked this conversation as resolved.
Show resolved Hide resolved
};

export default ConnectAdsFooter;
123 changes: 123 additions & 0 deletions js/src/components/google-combo-account-card/connect-ads/connect-ads.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/**
* External dependencies
*/
import { useEffect, useRef, useState } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import AccountCard from '.~/components/account-card';
import useApiFetchCallback from '.~/hooks/useApiFetchCallback';
import useDispatchCoreNotices from '.~/hooks/useDispatchCoreNotices';
import useGoogleAdsAccount from '.~/hooks/useGoogleAdsAccount';
import { useAppDispatch } from '.~/data';
import useExistingGoogleAdsAccounts from '.~/hooks/useExistingGoogleAdsAccounts';
import ConnectAccountCard from '../connect-account-card';
import ConnectAdsFooter from './connect-ads-footer';
import ConnectAdsBody from './connect-ads-body';

/**
* ConnectAds component renders an account card to connect to an existing Google Ads account.
*
* @fires gla_documentation_link_click with `{ context: 'setup-ads-connect-account', link_id: 'connect-sub-account', href: 'https://support.google.com/google-ads/answer/6139186' }`
eason9487 marked this conversation as resolved.
Show resolved Hide resolved
* @return {JSX.Element} {@link AccountCard} filled with content.
*/
const ConnectAds = () => {
eason9487 marked this conversation as resolved.
Show resolved Hide resolved
const {
existingAccounts: accounts,
hasFinishedResolution: hasFinishedResolutionForExistingAdsAccount,
} = useExistingGoogleAdsAccounts();

const {
googleAdsAccount,
hasFinishedResolution: hasFinishedResolutionForCurrentAccount,
} = useGoogleAdsAccount();

const isConnected =
googleAdsAccount?.status === 'connected' ||
( googleAdsAccount?.status === 'incomplete' &&
googleAdsAccount?.step === 'link_merchant' );
eason9487 marked this conversation as resolved.
Show resolved Hide resolved

const [ value, setValue ] = useState();
const [ isLoading, setLoading ] = useState( false );
const [ fetchConnectAdsAccount ] = useApiFetchCallback( {
eason9487 marked this conversation as resolved.
Show resolved Hide resolved
path: '/wc/gla/ads/accounts',
method: 'POST',
data: { id: value },
} );
const { refetchGoogleAdsAccount } = useGoogleAdsAccount();
const { createNotice } = useDispatchCoreNotices();
const { fetchGoogleAdsAccountStatus } = useAppDispatch();
const initialValueRef = useRef( null );

if ( googleAdsAccount?.id ) {
initialValueRef.current = googleAdsAccount.id;
asvinb marked this conversation as resolved.
Show resolved Hide resolved
}

useEffect( () => {
if ( initialValueRef.current ) {
setValue( initialValueRef.current );
}
}, [ initialValueRef ] );

const handleConnectClick = async () => {
if ( ! value ) {
return;
}

setLoading( true );
try {
await fetchConnectAdsAccount();
await fetchGoogleAdsAccountStatus();
await refetchGoogleAdsAccount();
setLoading( false );
} catch ( error ) {
setLoading( false );
createNotice(
'error',
__(
'Unable to connect your Google Ads account. Please try again later.',
'google-listings-and-ads'
)
);
}
};

// If the accounts are still being fetched, we don't want to show the card.
if (
! hasFinishedResolutionForExistingAdsAccount ||
! hasFinishedResolutionForCurrentAccount ||
accounts.length === 0
) {
return null;
}

return (
<ConnectAccountCard
className="gla-google-combo-service-account-card--ads"
title={ __(
'Connect to existing Google Ads account',
'google-listings-and-ads'
) }
helperText={ __(
'Required to set up conversion measurement for your store.',
'google-listings-and-ads'
) }
body={
<ConnectAdsBody
accounts={ accounts }
googleAdsAccount={ googleAdsAccount }
isConnected={ isConnected }
handleConnectClick={ handleConnectClick }
isLoading={ isLoading }
setValue={ setValue }
value={ value }
/>
}
footer={ <ConnectAdsFooter isConnected={ isConnected } /> }
eason9487 marked this conversation as resolved.
Show resolved Hide resolved
/>
);
};

export default ConnectAds;
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import AppButton from '.~/components/app-button';
import ConnectedIconLabel from '.~/components/connected-icon-label';
import useEventPropertiesFilter from '.~/hooks/useEventPropertiesFilter';
import { FILTER_ONBOARDING } from '.~/utils/tracks';

/**
* Connect CTA component.
*
* @param {Object} props Props.
* @param {boolean} props.isConnected Whether the account is connected.
* @param {number} props.id Account ID.
* @param {Function} props.handleConnectClick Callback to handle the connect click.
* @param {string} props.value Connected account ID.
*
* @return {JSX.Element} Connect CTA component.
*/
const ConnectCTA = ( { isConnected, id, handleConnectClick, value } ) => {
asvinb marked this conversation as resolved.
Show resolved Hide resolved
const getEventProps = useEventPropertiesFilter( FILTER_ONBOARDING );

if ( isConnected && id === Number( value ) ) {
asvinb marked this conversation as resolved.
Show resolved Hide resolved
return <ConnectedIconLabel />;
asvinb marked this conversation as resolved.
Show resolved Hide resolved
}

return (
<AppButton
isSecondary
disabled={ ! value }
eventName="gla_ads_account_connect_button_click"
eventProps={ getEventProps( {
id: Number( value ),
} ) }
onClick={ handleConnectClick }
>
{ __( 'Connect', 'google-listings-and-ads' ) }
</AppButton>
);
};

export default ConnectCTA;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/**
asvinb marked this conversation as resolved.
Show resolved Hide resolved
* Internal dependencies
*/

export { default } from './connect-ads';
Loading