Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
27 changes: 22 additions & 5 deletions packages/shared/src/components/cards/ad/AdCard.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,13 @@ it('should render advertise link on grid ad', () => {
});

const promotedMatcher = (_: string, element?: Element | null): boolean =>
getNormalizedText(element) === 'Promoted';
getNormalizedText(element) === 'Promoted' ||
getNormalizedText(element).startsWith('Promoted by ');

const promotedByMatcher =
(source: string) =>
(_: string, element?: Element | null): boolean =>
getNormalizedText(element) === `Promoted by ${source}`;

it('should render promoted attribution outside of list title clamp', async () => {
renderListComponent();
Expand All @@ -190,7 +196,7 @@ it('should render promoted attribution outside of list title clamp', async () =>
expect(await screen.findByText(promotedMatcher)).toBeInTheDocument();
});

it('should render plain Promoted attribution without source link', async () => {
it('should render promoted attribution with source link', async () => {
renderListComponent({
ad: {
...ad,
Expand All @@ -199,10 +205,21 @@ it('should render plain Promoted attribution without source link', async () => {
},
});

const attribution = await screen.findByText(promotedMatcher);
const attribution = await screen.findByText(promotedByMatcher('Carbon'));
const link = attribution.closest('a');

expect(link).toHaveAttribute('href', 'https://example.com/referral');
expect(link).toHaveAttribute('target', '_blank');
expect(link).toHaveAttribute('rel', 'noopener');
});

it('should render plain Promoted attribution without source link', async () => {
renderListComponent();

const attribution = await screen.findByText(
(_, element) => getNormalizedText(element) === 'Promoted',
);
expect(attribution.tagName).not.toBe('A');
expect(screen.queryByText(/Promoted by/)).not.toBeInTheDocument();
expect(screen.queryByText(/Carbon/)).not.toBeInTheDocument();
});

it('should render Promoted attribution in grid variant', async () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/shared/src/components/cards/ad/AdGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export const AdGrid = forwardRef<HTMLElement, AdCardProps>(function AdGrid(
className="!items-end"
/>
) : null}
<AdAttribution className={{ main: 'font-normal' }} />
<AdAttribution ad={ad} className={{ main: 'font-normal' }} />
</CardTextContainer>
{!useGlass && (
<AdImage className="mx-1 mb-0" ad={ad} ImageComponent={CardImage} />
Expand Down
5 changes: 4 additions & 1 deletion packages/shared/src/components/cards/ad/AdList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,10 @@ export const AdList = forwardRef<HTMLElement, AdCardProps>(function AdCard(
{adImprovementsV3 && matchingTags.length > 0 ? (
<PostTags post={{ tags: matchingTags.slice(0, 6) }} />
) : null}
<AdAttribution className={{ main: 'mt-2 block font-normal' }} />
<AdAttribution
ad={ad}
className={{ main: 'mt-2 block font-normal' }}
/>
</CardTextContainer>
<AdImage ad={ad} ImageComponent={CardImage} />
</CardContent>
Expand Down
1 change: 1 addition & 0 deletions packages/shared/src/components/cards/ad/SignalAdList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export const SignalAdList = forwardRef(function SignalAdList(
</span>
<span>&middot;</span>
<AdAttribution
ad={ad}
className={{
typo: 'typo-callout',
main: 'inline',
Expand Down
22 changes: 20 additions & 2 deletions packages/shared/src/components/cards/ad/common/AdAttribution.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import type { ReactElement } from 'react';
import classNames from 'classnames';
import type { Ad } from '../../../../graphql/posts';
import { useScrambler } from '../../../../hooks/useScrambler';

interface AdClassName {
Expand All @@ -9,19 +10,36 @@ interface AdClassName {
}

interface AdAttributionProps {
ad: Ad;
className?: AdClassName;
}

export default function AdAttribution({
ad,
className,
}: AdAttributionProps): ReactElement {
const elementClass = classNames(
'text-text-quaternary',
'text-text-quaternary no-underline',
className?.typo ?? 'typo-footnote',
className?.main,
);

const promotedText = useScrambler('Promoted');
const text = ad.referralLink ? `Promoted by ${ad.source}` : 'Promoted';
const promotedText = useScrambler(text);

if (ad.referralLink) {
return (
<a
href={ad.referralLink}
target="_blank"
rel="noopener"
className={elementClass}
suppressHydrationWarning
>
{promotedText}
</a>
);
}

return (
<div className={elementClass} suppressHydrationWarning>
Expand Down
4 changes: 2 additions & 2 deletions packages/shared/src/components/post/PostSidebarAdWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ export function PostSidebarAdWidget({
</Typography>
)}
<div className="flex items-center gap-1.5">
<AdAttribution className={{ main: 'relative z-1' }} />
<AdAttribution ad={ad} className={{ main: 'relative z-1' }} />
<span aria-hidden className="text-text-quaternary typo-footnote">
·
</span>
Expand Down Expand Up @@ -232,7 +232,7 @@ export function PostSidebarAdWidget({
{company}
</Typography>
)}
<AdAttribution className={{ main: 'relative z-1' }} />
<AdAttribution ad={ad} className={{ main: 'relative z-1' }} />
{hasDescription && (
<Typography
tag={TypographyTag.P}
Expand Down
Loading