Skip to content

Commit

Permalink
Merge pull request #2650 from woocommerce/feature/2538-performance-ca…
Browse files Browse the repository at this point in the history
…rd-sep

Show Ads value prop in Paid Campaign performance card
  • Loading branch information
joemcgill authored Nov 7, 2024
2 parents e7a0f64 + 1a516c7 commit 2c3f089
Show file tree
Hide file tree
Showing 17 changed files with 401 additions and 147 deletions.
2 changes: 1 addition & 1 deletion js/src/components/free-ad-credit/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

svg {
flex: 0 0 auto;
fill: #007017;
fill: $gla-color-green;
}

&__title {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import AppDocumentationLink from '.~/components/app-documentation-link';
import CampaignPreview from '.~/components/paid-ads/campaign-preview';
import FreeAdCredit from '.~/components/free-ad-credit';
import VerticalGapLayout from '.~/components/vertical-gap-layout';
import useFreeAdCredit from '.~/hooks/useFreeAdCredit';
import './paid-ads-features-section.scss';

function FeatureList() {
Expand Down Expand Up @@ -49,8 +48,6 @@ function FeatureList() {
* for the next actions: skip or continue the paid ads setup.
*/
export default function PaidAdsFeaturesSection() {
const hasFreeAdCredit = useFreeAdCredit();

return (
<Section
className="gla-paid-ads-features-section"
Expand Down Expand Up @@ -111,8 +108,7 @@ export default function PaidAdsFeaturesSection() {
<CampaignPreview />
</FlexItem>
</Flex>

{ hasFreeAdCredit && <FreeAdCredit /> }
<FreeAdCredit />
</VerticalGapLayout>
</Section.Card.Body>
</Section.Card>
Expand Down
1 change: 1 addition & 0 deletions js/src/dashboard/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
margin-bottom: var(--main-gap);

@include break-medium {
align-items: flex-start;
flex-direction: row;
}

Expand Down
15 changes: 10 additions & 5 deletions js/src/dashboard/summary-section/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import { SummaryNumber } from '@woocommerce/components';
/**
* Internal dependencies
*/
import { glaData, REPORT_SOURCE_PAID, REPORT_SOURCE_FREE } from '.~/constants';
import { REPORT_SOURCE_PAID, REPORT_SOURCE_FREE } from '.~/constants';
import useAdsCampaigns from '.~/hooks/useAdsCampaigns';
import useAdsCurrency from '.~/hooks/useAdsCurrency';
import useCurrencyFormat from '.~/hooks/useCurrencyFormat';
import usePerformance from './usePerformance';
Expand Down Expand Up @@ -106,17 +107,21 @@ const PaidPerformanceCard = () => {
};

export default function SummarySection() {
const { adsSetupComplete } = glaData;
const { loaded, data: adsCampaignsData } = useAdsCampaigns();
if ( ! loaded ) {
return null;

Check warning on line 112 in js/src/dashboard/summary-section/index.js

View check run for this annotation

Codecov / codecov/patch

js/src/dashboard/summary-section/index.js#L112

Added line #L112 was not covered by tests
}
const showCampaignPromotionCard = ! adsCampaignsData?.length;

return (
<>
<SummaryCard
title={ __( 'Google Ads', 'google-listings-and-ads' ) }
>
{ adsSetupComplete ? (
<PaidPerformanceCard />
) : (
{ showCampaignPromotionCard ? (
<PaidCampaignPromotionCard />

Check warning on line 122 in js/src/dashboard/summary-section/index.js

View check run for this annotation

Codecov / codecov/patch

js/src/dashboard/summary-section/index.js#L122

Added line #L122 was not covered by tests
) : (
<PaidPerformanceCard />
) }
</SummaryCard>
<SummaryCard
Expand Down
35 changes: 2 additions & 33 deletions js/src/dashboard/summary-section/paid-campaign-promotion-card.js
Original file line number Diff line number Diff line change
@@ -1,51 +1,20 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Spinner } from '@woocommerce/components';

/**
* Internal dependencies
*/
import useGoogleAdsAccount from '.~/hooks/useGoogleAdsAccount';
import AddPaidCampaignButton from '.~/components/paid-ads/add-paid-campaign-button';
import { GOOGLE_ADS_ACCOUNT_STATUS } from '.~/constants';

const PromotionContent = ( { adsAccount } ) => {
const showFreeCredit =
adsAccount.sub_account ||
adsAccount.status === GOOGLE_ADS_ACCOUNT_STATUS.DISCONNECTED;

return (
<>
<p>
{ showFreeCredit
? __(
'Create your first campaign and get $500 in ad credit*',
'google-listings-and-ads'
)
: __(
'Create your first campaign',
'google-listings-and-ads'
) }
</p>
<AddPaidCampaignButton
eventProps={ { context: 'add-paid-campaign-promotion' } }
/>
</>
);
};
import PaidFeatures from './paid-features';

function PaidCampaignPromotionCard() {
const { googleAdsAccount } = useGoogleAdsAccount();

return (
<div className="gla-summary-card__body">
{ googleAdsAccount ? (
<PromotionContent adsAccount={ googleAdsAccount } />
) : (
<Spinner />
) }
{ googleAdsAccount ? <PaidFeatures /> : <Spinner /> }

Check warning on line 17 in js/src/dashboard/summary-section/paid-campaign-promotion-card.js

View check run for this annotation

Codecov / codecov/patch

js/src/dashboard/summary-section/paid-campaign-promotion-card.js#L17

Added line #L17 was not covered by tests
</div>
);
}
Expand Down
44 changes: 44 additions & 0 deletions js/src/dashboard/summary-section/paid-features/free-ad-credit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import GridiconGift from 'gridicons/dist/gift';
import { createInterpolateElement } from '@wordpress/element';

/**
* Internal dependencies
*/
import AppDocumentationLink from '.~/components/app-documentation-link';

/**
* Render the Free Ads Credit inside the Paid Features component.
*
* @fires gla_documentation_link_click with `{ context: 'dashboard', link_id: 'free-ad-credit-terms', href: 'https://www.google.com/ads/coupons/terms/' }`
* @return {JSX.Element} Free Ads Credit component.
*/
const FreeAdCredit = () => {
return (

Check warning on line 20 in js/src/dashboard/summary-section/paid-features/free-ad-credit.js

View check run for this annotation

Codecov / codecov/patch

js/src/dashboard/summary-section/paid-features/free-ad-credit.js#L20

Added line #L20 was not covered by tests
<div className="gla-free-ad-credit-claim">
<GridiconGift />
<div>
{ createInterpolateElement(
__(
'Claim $500 in ads credit when you spend your first $500 with Google Ads. <termLink>Terms and conditions apply</termLink>.',
'google-listings-and-ads'
),
{
termLink: (
<AppDocumentationLink
context="dashboard"
linkId="free-ad-credit-terms"
href="https://www.google.com/ads/coupons/terms/"
/>
),
}
) }
</div>
</div>
);
};

export default FreeAdCredit;
101 changes: 101 additions & 0 deletions js/src/dashboard/summary-section/paid-features/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { createInterpolateElement } from '@wordpress/element';
import { Flex, FlexBlock, FlexItem } from '@wordpress/components';
import GridiconCheckmark from 'gridicons/dist/checkmark';

/**
* Internal dependencies
*/
import { ContentLink } from '.~/components/guide-page-content';
import CampaignPreview from '.~/components/paid-ads/campaign-preview';
import AddPaidCampaignButton from '.~/components/paid-ads/add-paid-campaign-button';
import FreeAdCredit from './free-ad-credit';
import VerticalGapLayout from '.~/components/vertical-gap-layout';
import './index.scss';

function FeatureList() {
const featuresItems = [

Check warning on line 20 in js/src/dashboard/summary-section/paid-features/index.js

View check run for this annotation

Codecov / codecov/patch

js/src/dashboard/summary-section/paid-features/index.js#L19-L20

Added lines #L19 - L20 were not covered by tests
{
Icon: GridiconCheckmark,
content: __(
'Reach more customer by advertising your products across Google Ads channels like Search, YouTube and Discover.',
'google-listings-and-ads'
),
},
{
Icon: GridiconCheckmark,
content: __(
'Set a daily budget and only pay when people click on your ads.',
'google-listings-and-ads'
),
},
{
Icon: GridiconCheckmark,
content: createInterpolateElement(
__(
"Performance Max uses the best of Google's AI to show the most impactful ads for your products at the right time and place. <link>Learn more about Performance Max technology.</link>",
'google-listings-and-ads'
),
{
link: (
<ContentLink
href="https://support.google.com/google-ads/answer/10724817"
context="campaign-creation-performance-max"
/>
),
}
),
},
];

return (

Check warning on line 54 in js/src/dashboard/summary-section/paid-features/index.js

View check run for this annotation

Codecov / codecov/patch

js/src/dashboard/summary-section/paid-features/index.js#L54

Added line #L54 was not covered by tests
<div className="gla-paid-features__feature-list">
{ featuresItems.map( ( { Icon, content }, idx ) => (
<Flex key={ idx } align="flex-start">

Check warning on line 57 in js/src/dashboard/summary-section/paid-features/index.js

View check run for this annotation

Codecov / codecov/patch

js/src/dashboard/summary-section/paid-features/index.js#L57

Added line #L57 was not covered by tests
<Icon size="18" />
<FlexBlock>{ content }</FlexBlock>
</Flex>
) ) }
</div>
);
}

/**
* Returns a component with paid features content.
*
* @return {JSX.Element} Paid Features component.
*/
const PaidFeatures = () => {
return (

Check warning on line 72 in js/src/dashboard/summary-section/paid-features/index.js

View check run for this annotation

Codecov / codecov/patch

js/src/dashboard/summary-section/paid-features/index.js#L72

Added line #L72 was not covered by tests
<VerticalGapLayout size="medium" className="gla-paid-features">
<Flex
align="center"
gap={ 9 }
className="gla-paid-features__content"
>
<FlexItem>
<CampaignPreview />
</FlexItem>
<FlexBlock>
<FeatureList />
</FlexBlock>
</Flex>
<FreeAdCredit />
<AddPaidCampaignButton
isPrimary
isSecondary={ false }
isSmall={ false }
eventProps={ {
context: 'add-paid-campaign-promotion',
} }
>
{ __( 'Create Campaign', 'google-listings-and-ads' ) }
</AddPaidCampaignButton>
</VerticalGapLayout>
);
};

export default PaidFeatures;
44 changes: 44 additions & 0 deletions js/src/dashboard/summary-section/paid-features/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
.gla-paid-features {

&__content {
flex-direction: column;

@media (min-width: $break-small) {
align-items: flex-start;
flex-direction: row;
gap: $grid-unit-20;
}
}

&__feature-list {
display: flex;
flex-direction: column;
gap: $grid-unit-20;
line-height: $gla-line-height-smaller;
color: $gray-800;
text-align: left;

.gridicon {
fill: $alert-green;
}
}

.gla-free-ad-credit-claim {
text-align: left;
background-color: $white;
display: flex;
align-items: flex-start;
gap: $grid-unit;
padding: $grid-unit-20 $grid-unit-15;
color: $gray-900;

.gridicon {
fill: $gla-color-green;
flex: 0 0 auto;
}
}

.components-button {
align-self: flex-end;
}
}
37 changes: 30 additions & 7 deletions js/src/dashboard/summary-section/summary-section.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { render } from '@testing-library/react';
* Internal dependencies
*/
import SummarySection from '.~/dashboard/summary-section';
import useAdsCampaigns from '.~/hooks/useAdsCampaigns';

// Mimic no data loaded.
jest.mock( './usePerformance', () =>
Expand All @@ -22,34 +23,56 @@ jest.mock( '.~/hooks/useAdsCurrency', () =>
} )
);
jest.mock( '.~/hooks/useCurrencyFormat', () => jest.fn() );
jest.mock( '.~/hooks/useAdsCampaigns', () =>
jest.fn().mockName( 'useAdsCampaigns' )
);

describe( 'SummarySection when no data is loaded', () => {
beforeAll( () => {
useAdsCampaigns.mockImplementation( () => {
return {
loading: false,
loaded: true,
data: [
{
id: 10,
name: 'PMax Campaign',
status: 'enabled',
type: 'performance_max',
amount: 20,
displayCountries: [ 'US' ],
},
],
};
} );
} );

it( 'Shows no data message for Free Campaigns', async () => {
const { queryByText } = render( <SummarySection /> );
const { findByText } = render( <SummarySection /> );

expect(
queryByText(
await findByText(
"We're having trouble loading this data. Try again later, or track your performance in Google Merchant Center."
)
).toBeTruthy();

const link = queryByText( 'Open Google Merchant Center' );
const link = await findByText( 'Open Google Merchant Center' );

expect( link ).toBeTruthy();
expect( link.href ).toBe(
'https://merchants.google.com/mc/reporting/dashboard'
);
} );
it( 'Shows no data message for Paid Campaigns', () => {
const { queryByText } = render( <SummarySection /> );
it( 'Shows no data message for Paid Campaigns', async () => {
const { findByText } = render( <SummarySection /> );

expect(
queryByText(
await findByText(
"We're having trouble loading this data. Try again later, or track your performance in Google Ads."
)
).toBeTruthy();

const link = queryByText( 'Open Google Ads' );
const link = await findByText( 'Open Google Ads' );

expect( link ).toBeTruthy();
expect( link.href ).toBe( 'https://ads.google.com/' );
Expand Down
Loading

0 comments on commit 2c3f089

Please sign in to comment.