Skip to content

Commit 4af65b6

Browse files
Add optional image field to PuzzleConfig for share/preview images
Per new PR review feedback, adding a placeholder capability for a per-puzzle preview/share image, to eventually populate og:image/ twitter:image. Confirmed by investigation (per the review request) that DCR has no site-wide default/fallback share image anywhere for pages without one - frontend's MetaData.opengraphProperties/SimplePage only add og:image via explicit per-page overrides, never a default. So an unset image is expected to simply omit og:image/twitter:image, matching existing sitewide behaviour, rather than needing a placeholder asset. - src/model/puzzles/puzzleConfigs.ts: added an optional image?: string field to PuzzleConfig, documented as intentionally unset on every current registry entry - none of the 6 V0 games have a real, licensed preview image yet, and this commit deliberately does NOT invent placeholder image URLs for any of them. isValidPuzzleConfig updated to allow image being absent, but reject it if present-and-empty or whitespace-only (same validation style already used for description). - src/model/puzzles/puzzleConfigs.test.ts: added tests confirming every current entry has no image set, that validatePuzzleConfigs does not throw when a valid non-empty image is present, and that it rejects an empty-string or whitespace-only image. tsc --noEmit clean, eslint clean, 17/17 tests passing in this suite (4 new tests added). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent 067215b commit 4af65b6

2 files changed

Lines changed: 54 additions & 3 deletions

File tree

dotcom-rendering/src/model/puzzles/puzzleConfigs.test.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,44 @@ describe('puzzleConfigs registry', () => {
9292
).toThrow(TypeError);
9393
});
9494

95+
it('allows every entry to have no image configured (the current state)', () => {
96+
expect(
97+
Object.values(puzzleConfigs).every(
98+
(config) => config.image === undefined,
99+
),
100+
).toBe(true);
101+
});
102+
103+
it('does not throw when an entry has a valid, non-empty image set', () => {
104+
expect(() =>
105+
validatePuzzleConfigs({
106+
...puzzleConfigs,
107+
wordiply: {
108+
...puzzleConfigs.wordiply!,
109+
image: 'https://example.com/wordiply.jpg',
110+
},
111+
}),
112+
).not.toThrow();
113+
});
114+
115+
it('rejects an entry with an empty-string image', () => {
116+
expect(() =>
117+
validatePuzzleConfigs({
118+
...puzzleConfigs,
119+
wordiply: { ...puzzleConfigs.wordiply!, image: '' },
120+
}),
121+
).toThrow(TypeError);
122+
});
123+
124+
it('rejects an entry with a whitespace-only image', () => {
125+
expect(() =>
126+
validatePuzzleConfigs({
127+
...puzzleConfigs,
128+
wordiply: { ...puzzleConfigs.wordiply!, image: ' ' },
129+
}),
130+
).toThrow(TypeError);
131+
});
132+
95133
describe('getPuzzleConfig', () => {
96134
it('returns the config for a known slug', () => {
97135
expect(getPuzzleConfig('sudoku-easy')?.puzzleGroup).toBe(

dotcom-rendering/src/model/puzzles/puzzleConfigs.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,18 @@ export interface PuzzleConfig {
5454
* only the slug swapped in.
5555
*/
5656
description: string;
57+
/**
58+
* An optional, full preview/share image URL for this puzzle, used to
59+
* populate `og:image`/`twitter:image` in `render.puzzlePage.web.tsx`.
60+
* Deliberately optional and unset on every current registry entry - DCR
61+
* has no site-wide default/fallback share image for pages without one
62+
* (confirmed by investigation; see docs/puzzle-page.md), so an unset
63+
* `image` simply omits `og:image`/`twitter:image` entirely, matching
64+
* existing sitewide behaviour rather than needing a placeholder. Leave
65+
* unset until a real, licensed preview image is provided for a given
66+
* puzzle - do not invent a placeholder URL here.
67+
*/
68+
image?: string;
5769
}
5870

5971
const amuseLabsUrlTemplate =
@@ -138,14 +150,15 @@ const isValidPuzzleConfig = (key: string, config: PuzzleConfig): boolean => {
138150
if (!puzzleGroups.includes(config.puzzleGroup)) return false;
139151
if (!config.iframe.provider || !config.iframe.urlTemplate) return false;
140152
if (!config.description.trim()) return false;
153+
if (config.image !== undefined && !config.image.trim()) return false;
141154
return true;
142155
};
143156

144157
/**
145158
* Fail fast if the registry itself is malformed (e.g. a mismatched slug key,
146-
* a missing/empty `iframe` config, or a missing/empty `description`). Run
147-
* once at module load so a bad registry entry surfaces immediately rather
148-
* than at request time.
159+
* a missing/empty `iframe` config, a missing/empty `description`, or a
160+
* present-but-empty `image`). Run once at module load so a bad registry
161+
* entry surfaces immediately rather than at request time.
149162
*/
150163
export const validatePuzzleConfigs = (
151164
configs: Record<string, PuzzleConfig>,

0 commit comments

Comments
 (0)