diff --git a/dotcom-rendering/src/components/PuzzlesBranding.tsx b/dotcom-rendering/src/components/PuzzlesBranding.tsx index 5c76e3c2384..e394e246617 100644 --- a/dotcom-rendering/src/components/PuzzlesBranding.tsx +++ b/dotcom-rendering/src/components/PuzzlesBranding.tsx @@ -1,4 +1,4 @@ -import { css } from '@emotion/react'; +import { css, type SerializedStyles } from '@emotion/react'; import { from, palette, @@ -76,42 +76,78 @@ const aboutLinkStyles = css` color: var(--puzzles-text-colour, ${palette.neutral[7]}); `; +const containerLockupStyles = css` + display: flex; + align-items: center; + gap: 7px; + white-space: nowrap; + color: var(--puzzles-text-colour, ${palette.neutral[7]}); + a { + text-decoration: none; + } + ${from.leftCol} { + flex-direction: column; + align-items: flex-start; + margin-top: ${space[3]}px; + } +`; + +const containerLogoStyles = css` + display: block; + width: auto; + height: auto; + max-width: 100px; + max-height: 40px; + object-fit: contain; + ${from.leftCol} { + max-width: 120px; + max-height: 60px; + } +`; + type Props = { branding: Branding }; -export const PuzzlesBranding = ({ branding }: Props) => { +const SponsorLogoLink = ({ + branding, + imageStyles, +}: Props & { imageStyles: SerializedStyles }) => { const { ophanComponentName, ophanComponentLink } = getOphanComponents({ branding, locationPrefix: 'front-container', }); return ( -
-
- {branding.logo.label} - - {branding.sponsorName} - -
-
+ {branding.sponsorName} + ); }; +export const PuzzlesBranding = ({ branding }: Props) => ( +
+
+ {branding.logo.label} + +
+
+); + export const PuzzlesBrandingAboutLink = ({ branding }: Props) => (
@@ -119,3 +155,13 @@ export const PuzzlesBrandingAboutLink = ({ branding }: Props) => (
); + +export const PuzzlesContainerBranding = ({ branding }: Props) => ( +
+ {branding.logo.label} + +
+); diff --git a/dotcom-rendering/src/components/PuzzlesDirectory.stories.tsx b/dotcom-rendering/src/components/PuzzlesDirectory.stories.tsx index ab4693cf7e4..8bcfe3134fc 100644 --- a/dotcom-rendering/src/components/PuzzlesDirectory.stories.tsx +++ b/dotcom-rendering/src/components/PuzzlesDirectory.stories.tsx @@ -129,6 +129,14 @@ export const AdFree = () => ( ); +export const SponsoredContainer = () => ( + +); + export const Sponsored = () => { const page = createPuzzlesPage({ layout, isAdFreeUser: true }); return ( diff --git a/dotcom-rendering/src/components/PuzzlesDirectory.test.tsx b/dotcom-rendering/src/components/PuzzlesDirectory.test.tsx index cca9bd7125e..c764f3259df 100644 --- a/dotcom-rendering/src/components/PuzzlesDirectory.test.tsx +++ b/dotcom-rendering/src/components/PuzzlesDirectory.test.tsx @@ -1,6 +1,7 @@ import '@testing-library/jest-dom'; import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import type { ReactNode } from 'react'; +import { puzzlesBranding } from '../../fixtures/manual/puzzlesBranding'; import type { PuzzleContainer, PuzzleItem, @@ -34,6 +35,35 @@ const section = ( }); describe('PuzzlesDirectory', () => { + it('shows sponsorship only on the sponsored container', () => { + render( + , + ); + + const [crosswords, wordGames] = screen.getAllByRole('region'); + expect(crosswords).toHaveTextContent('Supported by'); + expect( + screen.getByRole('link', { + name: 'Visit the Example sponsor website', + }), + ).toHaveAttribute('href', 'https://example.com'); + expect( + screen.queryByRole('link', { name: 'About this content' }), + ).not.toBeInTheDocument(); + expect( + screen.getByRole('heading', { level: 2, name: 'Crosswords' }), + ).toHaveAttribute('id', 'crosswords-title'); + expect(wordGames).not.toHaveTextContent('Supported by'); + }); it('renders puzzle tile titles as H3s with descriptive crossword text', () => { render( >; }; const sectionStyles = (isFeatured: boolean, hideTopRule: boolean) => css` @@ -70,21 +75,12 @@ const sectionStyles = (isFeatured: boolean, hideTopRule: boolean) => css` const titleStyles = css` min-width: 0; - margin: 0; padding: 6px 10px 12px; - ${headlineBold24}; - color: var(--puzzles-headline-colour, ${palette.neutral[7]}); - line-height: 1; - overflow-wrap: break-word; ${from.mobileMedium} { padding-right: 20px; padding-left: 20px; } - ${from.tablet} { - ${headlineBold28}; - } - ${from.desktop} { /* Include the 1px side border in the 20px inset, as the cards do. */ padding-left: 19px; @@ -95,6 +91,30 @@ const titleStyles = css` } `; +// Title left with the sponsor right on one row, stacked under the title from leftCol. +const brandedTitleStyles = css` + display: flex; + align-items: center; + justify-content: space-between; + gap: ${space[2]}px; + ${from.leftCol} { + display: block; + } +`; + +const headingStyles = css` + min-width: 0; + margin: 0; + ${headlineBold24}; + color: var(--puzzles-headline-colour, ${palette.neutral[7]}); + line-height: 1; + overflow-wrap: break-word; + + ${from.tablet} { + ${headlineBold28}; + } +`; + const headingColumnStyles = css` ${from.leftCol} { position: relative; @@ -233,9 +253,11 @@ const hasContent = (container: PuzzleContainer): boolean => const DirectorySection = ({ container, hideTopRule, + branding, }: { container: PuzzleContainer; hideTopRule: boolean; + branding?: Branding; }) => { if (!hasContent(container)) { return null; @@ -247,9 +269,14 @@ const DirectorySection = ({ id={container.id} >
-

- {container.title} -

+
+

+ {container.title} +

+ {branding && ( + + )} +
( <> {layout.containers.map((container) => { @@ -328,6 +356,7 @@ export const PuzzlesDirectory = ({ } return (