Skip to content
Closed
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
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { CSSProperties, ReactElement } from 'react';
import React, { useMemo } from 'react';
import classNames from 'classnames';
import { AdPixel } from '../../../components/cards/ad/common/AdPixel';
import { getViewedPixels } from '../../../components/cards/ad/common/getViewedPixels';
import { anchorSponsoredRel } from '../../../lib/strings';
Expand Down Expand Up @@ -28,17 +27,8 @@ interface SponsorLogoProps {
* the gold slot takes its natural width instead.
*/
boxWidth?: number;
/**
* Draw the mark as a single-colour silhouette that takes the surrounding
* text colour, instead of the file's own inks. This is what makes a wall of
* marks from a dozen advertisers read as one row in both themes — full
* colour there is a patchwork, and half the marks die against one ground.
* The gold slot is the exception: its brand colour is what was sold.
*/
monochrome?: boolean;
/** Height ceiling in px, so a mark cannot outgrow the row it sits in. */
maxHeight?: number;
className?: string;
}

export const SponsorLogo = ({
Expand All @@ -47,9 +37,7 @@ export const SponsorLogo = ({
cap,
exactHeight,
boxWidth,
monochrome = false,
maxHeight,
className,
}: SponsorLogoProps): ReactElement => {
const { ref, isViewable, onClick } = useSponsorSlotLog<HTMLAnchorElement>({
sponsor,
Expand All @@ -76,17 +64,7 @@ export const SponsorLogo = ({

const size: CSSProperties = {
height: `${height}px`,
// A mask paints whatever box it is handed, and a boxed wall slot has to
// stay a predictable width, so both take the width the ratio implies. A
// bare `<img>` carries its own ratio, and with no dimensions on the wire
// the file beats the stand-in: the gold slot is sized by height and lets
// the width follow, which is what the slot was sold as. Handing it the
// stand-in width instead would letterbox a wide lockup down to two thirds
// of the height the row reserves for it.
width:
monochrome || boxWidth
? `${Math.round(height * sponsor.ratio)}px`
: 'auto',
width: boxWidth ? `${Math.round(height * sponsor.ratio)}px` : 'auto',
};

return (
Expand All @@ -97,41 +75,15 @@ export const SponsorLogo = ({
rel={anchorSponsoredRel}
title={sponsor.company}
onClick={onClick}
className={classNames(
'relative flex shrink-0 items-center justify-center',
className,
)}
className="relative flex shrink-0 items-center justify-center"
style={boxWidth ? { width: `${boxWidth}px` } : undefined}
>
{monochrome ? (
<span
role="img"
aria-label={sponsor.company}
className="block"
style={{
...size,
// `bg-current` is not in this palette — the design tokens replace
// the default colours — so the ink is painted directly and the
// logo file is what shapes it.
backgroundColor: 'currentColor',
maskImage: `url(${sponsor.logo})`,
maskRepeat: 'no-repeat',
maskPosition: 'center',
maskSize: 'contain',
WebkitMaskImage: `url(${sponsor.logo})`,
WebkitMaskRepeat: 'no-repeat',
WebkitMaskPosition: 'center',
WebkitMaskSize: 'contain',
}}
/>
) : (
<img
src={sponsor.logo}
alt={sponsor.company}
className="object-contain"
style={size}
/>
)}
<img
src={sponsor.logo}
alt={sponsor.company}
className="object-contain"
style={size}
/>
{/* No `fireOnMount`: the ad server should count the impression when the
mark reaches the viewport, which is how every other placement counts
it. Only the viewable re-fire below is already gated by then. */}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,11 +238,6 @@ it('should log an impression per logo with its tier and slot', async () => {
);
});

// The wire sends no dimensions, so every mark carries the stand-in ratio. A
// masked wall mark needs a box computed from it — a mask paints whatever box it
// gets — but the gold slot is an `<img>` with its own ratio, and handing it the
// stand-in width letterboxes a wide lockup to two thirds of the height the row
// reserves for the one slot somebody paid for.
it('should let the gold mark take its own width', async () => {
renderStrip();
await settle();
Expand All @@ -254,18 +249,27 @@ it('should let the gold mark take its own width', async () => {
expect(gold).toHaveStyle({ height: '20px', width: 'auto' });
});

it('should give a masked wall mark a box to paint into', async () => {
renderStrip();
await settle();

// Whichever premium mark the deck dealt, not a named one: the pool is
// shuffled per page load and holds more creatives than the row has slots,
// so naming one picks a mark that is only usually there.
const company = shownLogos().find((name) => PREMIUM.includes(name)) as string;
const wall = within(screen.getByTitle(company)).getByLabelText(company);

expect(wall).toHaveStyle({ height: '17px', width: '60px' });
});
it.each([
{ companies: PREMIUM, height: '17px', width: '60px' },
{ companies: COMMUNITY, height: '15px', width: '53px' },
])(
'should render a wall logo image at height $height',
async ({ companies, height, width }) => {
renderStrip();
await settle();

// Whichever mark the deck dealt, not a named one: the pool is
// shuffled per page load and holds more creatives than the row has slots,
// so naming one picks a mark that is only usually there.
const company = shownLogos().find((name) =>
companies.includes(name),
) as string;
const wall = within(screen.getByTitle(company)).getByAltText(company);

expect(wall).toHaveAttribute('src', `icon-${company}`);
expect(wall).toHaveStyle({ height, width });
},
);

it('should open air time for every logo on the row', async () => {
renderStrip();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,11 @@ const SponsorRow = ({
<span className="whitespace-nowrap text-text-quaternary typo-caption2">
Made possible by
</span>
{/* The gold mark is the one slot that keeps its own inks and its
own size: one coloured mark at full height against a silhouetted
wall is the whole hierarchy of the row, without a hover effect
on top. */}
{gold && (
<SponsorLogo
sponsor={gold}
slotIndex={0}
exactHeight={GOLD_HEIGHT}
className="text-text-primary"
/>
)}
</div>
Expand Down Expand Up @@ -114,8 +109,6 @@ const SponsorRow = ({
cap={PREMIUM_CAP}
boxWidth={SLOT_WIDTH}
maxHeight={WALL_MAX_HEIGHT}
monochrome
className="text-text-secondary transition-colors hover:text-text-primary"
/>
))}
{community.map((sponsor, index) => (
Expand All @@ -129,8 +122,6 @@ const SponsorRow = ({
cap={COMMUNITY_CAP}
boxWidth={SLOT_WIDTH}
maxHeight={WALL_MAX_HEIGHT}
monochrome
className="text-text-tertiary transition-colors hover:text-text-primary"
/>
))}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@
* That normalising exists to stop a dozen unrelated wall marks fighting each
* other, and it works by trading height for width, so it punishes exactly the
* wide mark-plus-wordmark lockup a paid slot is most likely to supply: the
* lockup came out shorter than the tallest silhouette beside it, which is the
* lockup came out shorter than the tallest mark beside it, which is the
* opposite of what the slot is sold as. One known creative in one slot does
* not need normalising, it needs to be the biggest thing on the row.
*
* It is the box height, not the cap height, and a lockup that is all wordmark
* spends nearly all of that box on letterforms where a mark-plus-wordmark
* spends it on the mark. Sized for the former: matching the wall's ceiling in
* height, it still leads the row on width and on being the one slot in
* colour.
* height, it still leads the row on width.
*/
export const GOLD_HEIGHT = 20;
/** The wall's two tiers differ by a hair of ink, not by a wash of opacity. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,7 @@ export const parseSponsors = (raw: unknown): SponsorStripCreative[] => {
};

/**
* The bar ships one flat asset per advertiser and no dimensions, so both the
* themed pair and the measured ratio the row was built around are gone. The
* wall masks its marks to the row's text colour, which is what lets one file
* serve either ground; the gold slot keeps the file's own inks, so a mark drawn
* for a single theme is on its own there.
* The bar ships one flat asset per advertiser and no dimensions.
*
* Every mark therefore takes the ratio the optical sizing is calibrated around,
* which makes `opticalHeight` hand back the cap exactly and leaves `contain` to
Expand Down
Loading