From 920977a40bdb64f0045d2c52256dc05610a5ab60 Mon Sep 17 00:00:00 2001 From: Jake Lee Kennedy <1731150+Jakeii@users.noreply.github.com> Date: Mon, 10 Aug 2026 10:05:40 +0100 Subject: [PATCH 1/5] delete old scorecard pages --- .../fixtures/generated/cricket-match.ts | 2151 ----------------- .../fixtures/manual/cricketMatch.ts | 2 +- .../CricketMatchHeader.stories.tsx | 2 +- .../CricketMatchHeader/CricketMatchHeader.tsx | 2 +- .../CricketMatchHeader/headerData.ts | 4 +- .../src/components/CricketMatchStat.tsx | 2 +- .../CricketMiniMatchStats.stories.tsx | 2 +- .../src/components/CricketMiniMatchStats.tsx | 6 +- .../components/CricketScorecard.stories.tsx | 122 +- .../src/components/CricketScorecard.tsx | 850 +++++-- .../CricketScorecardNew.stories.tsx | 377 --- .../src/components/CricketScorecardNew.tsx | 743 ------ .../src/components/CricketScorecardPage.tsx | 81 - .../src/components/CricketScorecardTab.tsx | 4 +- dotcom-rendering/src/cricketMatch.test.ts | 132 +- dotcom-rendering/src/cricketMatch.ts | 378 ++- dotcom-rendering/src/cricketMatchV2.test.ts | 78 - dotcom-rendering/src/cricketMatchV2.ts | 401 --- .../src/devServer/docs/cricketScorecard.tsx | 17 - .../src/devServer/routers/dotcom.tsx | 5 - .../src/devServer/routers/pages.tsx | 5 - .../src/frontend/feCricketMatchData.ts | 108 + .../src/frontend/feCricketMatchHeader.ts | 2 +- .../src/frontend/feCricketMatchPage.ts | 17 - .../layouts/SportDataPageLayout.stories.tsx | 18 - .../src/layouts/SportDataPageLayout.tsx | 8 - dotcom-rendering/src/model/validate.ts | 22 +- .../src/server/handler.sportDataPage.ts | 41 - .../src/server/render.sportDataPage.web.tsx | 9 - dotcom-rendering/src/server/server.dev.ts | 3 - dotcom-rendering/src/server/server.prod.ts | 2 - dotcom-rendering/src/sportDataPage.ts | 11 +- 32 files changed, 1219 insertions(+), 4386 deletions(-) delete mode 100644 dotcom-rendering/fixtures/generated/cricket-match.ts delete mode 100644 dotcom-rendering/src/components/CricketScorecardNew.stories.tsx delete mode 100644 dotcom-rendering/src/components/CricketScorecardNew.tsx delete mode 100644 dotcom-rendering/src/components/CricketScorecardPage.tsx delete mode 100644 dotcom-rendering/src/cricketMatchV2.test.ts delete mode 100644 dotcom-rendering/src/cricketMatchV2.ts delete mode 100644 dotcom-rendering/src/devServer/docs/cricketScorecard.tsx create mode 100644 dotcom-rendering/src/frontend/feCricketMatchData.ts diff --git a/dotcom-rendering/fixtures/generated/cricket-match.ts b/dotcom-rendering/fixtures/generated/cricket-match.ts deleted file mode 100644 index 9fad68405be..00000000000 --- a/dotcom-rendering/fixtures/generated/cricket-match.ts +++ /dev/null @@ -1,2151 +0,0 @@ -/** - * DO NOT EDIT THIS FILE! - * - * This file was automatically generated using the gen-fixtures.js script. Any edits - * you make here will be lost. - * - * If the data in these fixtures is not what you expect then - * - * 1. Refresh the data using 'make gen-fixtures' or - * 2. if the latest live data is not what you need, then consider editing - * gen-fixtures.js directly. - */ - -import type { FECricketMatchPage } from '../../src/frontend/feCricketMatchPage'; - -export const cricketMatchData: FECricketMatchPage = { - cricketMatch: { - teams: [ - { - name: 'England', - id: 'a359844f-fc07-9cfa-d4cc-9a9ac0d5d075', - home: true, - lineup: [ - 'Emilio Gay', - 'Ben Duckett', - 'Jacob Bethell', - 'Joe Root', - 'Harry Brook', - 'Jamie Smith', - 'Ben Stokes', - 'Gus Atkinson', - 'Ollie Robinson', - 'Josh Tongue', - 'Shoaib Bashir', - ], - teamTagId: 'sport/england-cricket-team', - }, - { - name: 'New Zealand', - id: '110c70b5-c05f-3be7-6670-baecd50a8c6b', - home: false, - lineup: [ - 'Tom Latham', - 'Devon Conway', - 'Kane Williamson', - 'Rachin Ravindra', - 'Daryl Mitchell', - 'Tom Blundell', - 'Glenn Phillips', - 'Nathan Smith', - 'Kyle Jamieson', - 'Matt Henry', - "Will O'Rourke", - ], - teamTagId: 'sport/new-zealand-cricket-team', - }, - ], - innings: [ - { - order: 1, - battingTeam: 'England', - runsScored: 140, - wickets: 10, - overs: '39.4', - declared: false, - forfeited: false, - description: 'England first innings', - batters: [ - { - name: 'Ben Duckett', - order: 1, - ballsFaced: 37, - runs: 19, - fours: 2, - sixes: 0, - out: true, - howOut: 'lbw b Smith', - onStrike: false, - nonStrike: true, - }, - { - name: 'Emilio Gay', - order: 2, - ballsFaced: 14, - runs: 8, - fours: 2, - sixes: 0, - out: true, - howOut: 'c Mitchell b Jamieson', - onStrike: false, - nonStrike: true, - }, - { - name: 'Jacob Bethell', - order: 3, - ballsFaced: 22, - runs: 6, - fours: 0, - sixes: 0, - out: true, - howOut: "lbw b O'Rourke", - onStrike: false, - nonStrike: true, - }, - { - name: 'Joe Root', - order: 4, - ballsFaced: 8, - runs: 1, - fours: 0, - sixes: 0, - out: true, - howOut: "c Blundell b O'Rourke", - onStrike: false, - nonStrike: true, - }, - { - name: 'Harry Brook', - order: 5, - ballsFaced: 71, - runs: 56, - fours: 10, - sixes: 0, - out: true, - howOut: 'c Jamieson b Smith', - onStrike: false, - nonStrike: true, - }, - { - name: 'Jamie Smith', - order: 6, - ballsFaced: 6, - runs: 1, - fours: 0, - sixes: 0, - out: true, - howOut: 'b Jamieson', - onStrike: false, - nonStrike: true, - }, - { - name: 'Ben Stokes', - order: 7, - ballsFaced: 23, - runs: 12, - fours: 2, - sixes: 0, - out: true, - howOut: 'c Williamson b Jamieson', - onStrike: false, - nonStrike: true, - }, - { - name: 'Gus Atkinson', - order: 8, - ballsFaced: 11, - runs: 4, - fours: 0, - sixes: 0, - out: true, - howOut: 'lbw b Jamieson', - onStrike: false, - nonStrike: true, - }, - { - name: 'Ollie Robinson', - order: 9, - ballsFaced: 5, - runs: 1, - fours: 0, - sixes: 0, - out: true, - howOut: 'c Blundell b Jamieson', - onStrike: false, - nonStrike: true, - }, - { - name: 'Josh Tongue', - order: 10, - ballsFaced: 23, - runs: 10, - fours: 2, - sixes: 0, - out: false, - howOut: 'Not Out', - onStrike: false, - nonStrike: true, - }, - { - name: 'Shoaib Bashir', - order: 11, - ballsFaced: 19, - runs: 14, - fours: 1, - sixes: 0, - out: true, - howOut: 'c Williamson b Smith', - onStrike: false, - nonStrike: true, - }, - ], - bowlers: [ - { - name: 'Matt Henry', - order: 1, - overs: 4, - maidens: 1, - runs: 8, - wickets: 0, - balls: 24, - }, - { - name: 'Kyle Jamieson', - order: 2, - overs: 14, - maidens: 0, - runs: 62, - wickets: 5, - balls: 84, - }, - { - name: 'Nathan Smith', - order: 3, - overs: 10, - maidens: 1, - runs: 38, - wickets: 3, - balls: 64, - }, - { - name: "Will O'Rourke", - order: 4, - overs: 11, - maidens: 3, - runs: 25, - wickets: 2, - balls: 66, - }, - ], - fallOfWicket: [ - { - order: 1, - name: 'Emilio Gay', - runs: 16, - }, - { - order: 2, - name: 'Ben Duckett', - runs: 31, - }, - { - order: 3, - name: 'Jacob Bethell', - runs: 33, - }, - { - order: 4, - name: 'Joe Root', - runs: 34, - }, - { - order: 5, - name: 'Jamie Smith', - runs: 55, - }, - { - order: 6, - name: 'Ben Stokes', - runs: 94, - }, - { - order: 7, - name: 'Gus Atkinson', - runs: 108, - }, - { - order: 8, - name: 'Harry Brook', - runs: 113, - }, - { - order: 9, - name: 'Ollie Robinson', - runs: 118, - }, - { - order: 10, - name: 'Shoaib Bashir', - runs: 140, - }, - ], - byes: 6, - legByes: 1, - noBalls: 1, - penalties: 0, - wides: 0, - extras: 8, - }, - { - order: 2, - battingTeam: 'New Zealand', - runsScored: 113, - wickets: 10, - overs: '29.5', - declared: false, - forfeited: false, - description: 'New Zealand first innings', - batters: [ - { - name: 'Tom Latham', - order: 1, - ballsFaced: 21, - runs: 3, - fours: 0, - sixes: 0, - out: true, - howOut: 'lbw b Atkinson', - onStrike: false, - nonStrike: true, - }, - { - name: 'Devon Conway', - order: 2, - ballsFaced: 5, - runs: 1, - fours: 0, - sixes: 0, - out: true, - howOut: 'lbw b Robinson', - onStrike: false, - nonStrike: true, - }, - { - name: 'Kane Williamson', - order: 3, - ballsFaced: 2, - runs: 0, - fours: 0, - sixes: 0, - out: true, - howOut: 'c Gay b Robinson', - onStrike: false, - nonStrike: false, - }, - { - name: 'Rachin Ravindra', - order: 4, - ballsFaced: 1, - runs: 0, - fours: 0, - sixes: 0, - out: true, - howOut: 'lbw b Robinson', - onStrike: false, - nonStrike: false, - }, - { - name: 'Daryl Mitchell', - order: 5, - ballsFaced: 23, - runs: 12, - fours: 1, - sixes: 0, - out: true, - howOut: 'b Robinson', - onStrike: false, - nonStrike: true, - }, - { - name: 'Tom Blundell', - order: 6, - ballsFaced: 21, - runs: 4, - fours: 0, - sixes: 0, - out: true, - howOut: 'b Tongue', - onStrike: false, - nonStrike: true, - }, - { - name: 'Glenn Phillips', - order: 7, - ballsFaced: 39, - runs: 34, - fours: 6, - sixes: 0, - out: true, - howOut: 'b Tongue', - onStrike: false, - nonStrike: true, - }, - { - name: 'Nathan Smith', - order: 8, - ballsFaced: 18, - runs: 15, - fours: 2, - sixes: 0, - out: true, - howOut: 'b Tongue', - onStrike: false, - nonStrike: true, - }, - { - name: 'Kyle Jamieson', - order: 9, - ballsFaced: 29, - runs: 38, - fours: 2, - sixes: 3, - out: false, - howOut: 'Not Out', - onStrike: false, - nonStrike: true, - }, - { - name: "Will O'Rourke", - order: 10, - ballsFaced: 17, - runs: 1, - fours: 0, - sixes: 0, - out: true, - howOut: 'c Brook b Atkinson', - onStrike: false, - nonStrike: true, - }, - { - name: 'Matt Henry', - order: 11, - ballsFaced: 5, - runs: 0, - fours: 0, - sixes: 0, - out: true, - howOut: 'b Robinson', - onStrike: false, - nonStrike: false, - }, - ], - bowlers: [ - { - name: 'Gus Atkinson', - order: 1, - overs: 5, - maidens: 0, - runs: 9, - wickets: 2, - balls: 30, - }, - { - name: 'Ollie Robinson', - order: 2, - overs: 10, - maidens: 3, - runs: 39, - wickets: 5, - balls: 65, - }, - { - name: 'Josh Tongue', - order: 3, - overs: 10, - maidens: 0, - runs: 40, - wickets: 3, - balls: 60, - }, - { - name: 'Ben Stokes', - order: 4, - overs: 4, - maidens: 0, - runs: 22, - wickets: 0, - balls: 24, - }, - ], - fallOfWicket: [ - { - order: 1, - name: 'Devon Conway', - runs: 2, - }, - { - order: 2, - name: 'Kane Williamson', - runs: 2, - }, - { - order: 3, - name: 'Rachin Ravindra', - runs: 2, - }, - { - order: 4, - name: 'Tom Latham', - runs: 12, - }, - { - order: 5, - name: 'Daryl Mitchell', - runs: 20, - }, - { - order: 6, - name: 'Tom Blundell', - runs: 29, - }, - { - order: 7, - name: 'Glenn Phillips', - runs: 65, - }, - { - order: 8, - name: 'Nathan Smith', - runs: 82, - }, - { - order: 9, - name: "Will O'Rourke", - runs: 108, - }, - { - order: 10, - name: 'Matt Henry', - runs: 113, - }, - ], - byes: 0, - legByes: 3, - noBalls: 2, - penalties: 0, - wides: 0, - extras: 5, - }, - { - order: 3, - battingTeam: 'England', - runsScored: 226, - wickets: 10, - overs: '56.0', - declared: false, - forfeited: false, - description: 'England second innings', - batters: [ - { - name: 'Ben Duckett', - order: 1, - ballsFaced: 46, - runs: 33, - fours: 6, - sixes: 0, - out: true, - howOut: "c Phillips b O'Rourke", - onStrike: false, - nonStrike: true, - }, - { - name: 'Emilio Gay', - order: 2, - ballsFaced: 95, - runs: 57, - fours: 8, - sixes: 0, - out: true, - howOut: 'c Blundell b Smith', - onStrike: false, - nonStrike: true, - }, - { - name: 'Jacob Bethell', - order: 3, - ballsFaced: 35, - runs: 14, - fours: 2, - sixes: 0, - out: true, - howOut: 'b Henry', - onStrike: false, - nonStrike: true, - }, - { - name: 'Joe Root', - order: 4, - ballsFaced: 19, - runs: 8, - fours: 1, - sixes: 0, - out: true, - howOut: 'lbw b Smith', - onStrike: false, - nonStrike: true, - }, - { - name: 'Harry Brook', - order: 5, - ballsFaced: 4, - runs: 0, - fours: 0, - sixes: 0, - out: true, - howOut: "lbw b O'Rourke", - onStrike: false, - nonStrike: false, - }, - { - name: 'Jamie Smith', - order: 6, - ballsFaced: 52, - runs: 39, - fours: 6, - sixes: 0, - out: true, - howOut: 'b Smith', - onStrike: false, - nonStrike: true, - }, - { - name: 'Ben Stokes', - order: 7, - ballsFaced: 3, - runs: 0, - fours: 0, - sixes: 0, - out: true, - howOut: 'b Smith', - onStrike: false, - nonStrike: false, - }, - { - name: 'Gus Atkinson', - order: 8, - ballsFaced: 32, - runs: 14, - fours: 1, - sixes: 0, - out: true, - howOut: 'c & b Jamieson', - onStrike: false, - nonStrike: true, - }, - { - name: 'Ollie Robinson', - order: 9, - ballsFaced: 30, - runs: 29, - fours: 3, - sixes: 0, - out: true, - howOut: 'c Phillips b Smith', - onStrike: true, - nonStrike: true, - }, - { - name: 'Josh Tongue', - order: 10, - ballsFaced: 13, - runs: 5, - fours: 1, - sixes: 0, - out: true, - howOut: 'b Smith', - onStrike: false, - nonStrike: true, - }, - { - name: 'Shoaib Bashir', - order: 11, - ballsFaced: 8, - runs: 0, - fours: 0, - sixes: 0, - out: false, - howOut: 'Not Out', - onStrike: false, - nonStrike: false, - }, - ], - bowlers: [ - { - name: 'Kyle Jamieson', - order: 1, - overs: 12, - maidens: 2, - runs: 41, - wickets: 1, - balls: 72, - }, - { - name: 'Nathan Smith', - order: 2, - overs: 17, - maidens: 4, - runs: 70, - wickets: 6, - balls: 102, - }, - { - name: "Will O'Rourke", - order: 3, - overs: 16, - maidens: 4, - runs: 46, - wickets: 2, - balls: 96, - }, - { - name: 'Matt Henry', - order: 4, - overs: 11, - maidens: 1, - runs: 43, - wickets: 1, - balls: 66, - }, - ], - fallOfWicket: [ - { - order: 1, - name: 'Ben Duckett', - runs: 52, - }, - { - order: 2, - name: 'Jacob Bethell', - runs: 99, - }, - { - order: 3, - name: 'Emilio Gay', - runs: 126, - }, - { - order: 4, - name: 'Harry Brook', - runs: 127, - }, - { - order: 5, - name: 'Joe Root', - runs: 127, - }, - { - order: 6, - name: 'Ben Stokes', - runs: 127, - }, - { - order: 7, - name: 'Gus Atkinson', - runs: 184, - }, - { - order: 8, - name: 'Jamie Smith', - runs: 213, - }, - { - order: 9, - name: 'Josh Tongue', - runs: 225, - }, - { - order: 10, - name: 'Ollie Robinson', - runs: 226, - }, - ], - byes: 14, - legByes: 12, - noBalls: 1, - penalties: 0, - wides: 0, - extras: 27, - }, - { - order: 4, - battingTeam: 'New Zealand', - runsScored: 138, - wickets: 10, - overs: '40.3', - declared: false, - forfeited: false, - description: 'New Zealand second innings', - batters: [ - { - name: 'Tom Latham', - order: 1, - ballsFaced: 3, - runs: 0, - fours: 0, - sixes: 0, - out: true, - howOut: 'c Brook b Atkinson', - onStrike: false, - nonStrike: false, - }, - { - name: 'Devon Conway', - order: 2, - ballsFaced: 91, - runs: 41, - fours: 4, - sixes: 0, - out: true, - howOut: 'c Bethell b Stokes', - onStrike: false, - nonStrike: true, - }, - { - name: 'Kane Williamson', - order: 3, - ballsFaced: 36, - runs: 18, - fours: 2, - sixes: 0, - out: true, - howOut: 'lbw b Tongue', - onStrike: false, - nonStrike: true, - }, - { - name: "Will O'Rourke", - order: 4, - ballsFaced: 6, - runs: 0, - fours: 0, - sixes: 0, - out: true, - howOut: 'b Atkinson', - onStrike: false, - nonStrike: false, - }, - { - name: 'Rachin Ravindra', - order: 5, - ballsFaced: 21, - runs: 8, - fours: 1, - sixes: 0, - out: true, - howOut: 'b Robinson', - onStrike: false, - nonStrike: true, - }, - { - name: 'Daryl Mitchell', - order: 6, - ballsFaced: 3, - runs: 0, - fours: 0, - sixes: 0, - out: true, - howOut: 'lbw b Robinson', - onStrike: false, - nonStrike: false, - }, - { - name: 'Tom Blundell', - order: 7, - ballsFaced: 12, - runs: 4, - fours: 0, - sixes: 0, - out: true, - howOut: 'lbw b Tongue', - onStrike: false, - nonStrike: true, - }, - { - name: 'Glenn Phillips', - order: 8, - ballsFaced: 52, - runs: 44, - fours: 7, - sixes: 1, - out: false, - howOut: 'Not Out', - onStrike: false, - nonStrike: true, - }, - { - name: 'Nathan Smith', - order: 9, - ballsFaced: 3, - runs: 4, - fours: 1, - sixes: 0, - out: true, - howOut: 'c Smith b Atkinson', - onStrike: false, - nonStrike: true, - }, - { - name: 'Kyle Jamieson', - order: 10, - ballsFaced: 9, - runs: 6, - fours: 1, - sixes: 0, - out: true, - howOut: 'c Duckett b Atkinson', - onStrike: false, - nonStrike: true, - }, - { - name: 'Matt Henry', - order: 11, - ballsFaced: 10, - runs: 0, - fours: 0, - sixes: 0, - out: true, - howOut: 'b Atkinson', - onStrike: false, - nonStrike: false, - }, - ], - bowlers: [ - { - name: 'Gus Atkinson', - order: 1, - overs: 11, - maidens: 3, - runs: 30, - wickets: 5, - balls: 69, - }, - { - name: 'Ollie Robinson', - order: 2, - overs: 13, - maidens: 2, - runs: 38, - wickets: 2, - balls: 78, - }, - { - name: 'Josh Tongue', - order: 3, - overs: 13, - maidens: 2, - runs: 48, - wickets: 2, - balls: 78, - }, - { - name: 'Ben Stokes', - order: 4, - overs: 3, - maidens: 0, - runs: 12, - wickets: 1, - balls: 18, - }, - ], - fallOfWicket: [ - { - order: 1, - name: 'Tom Latham', - runs: 0, - }, - { - order: 2, - name: 'Kane Williamson', - runs: 29, - }, - { - order: 3, - name: "Will O'Rourke", - runs: 36, - }, - { - order: 4, - name: 'Rachin Ravindra', - runs: 53, - }, - { - order: 5, - name: 'Daryl Mitchell', - runs: 53, - }, - { - order: 6, - name: 'Tom Blundell', - runs: 58, - }, - { - order: 7, - name: 'Devon Conway', - runs: 111, - }, - { - order: 8, - name: 'Nathan Smith', - runs: 116, - }, - { - order: 9, - name: 'Kyle Jamieson', - runs: 124, - }, - { - order: 10, - name: 'Matt Henry', - runs: 138, - }, - ], - byes: 1, - legByes: 9, - noBalls: 3, - penalties: 0, - wides: 0, - extras: 13, - }, - ], - competitionName: 'Test Match Series', - stage: 'First Test Match', - venueName: "Lord's", - result: 'result', - currentDay: 4, - totalDays: 5, - gameDate: '2026-06-04T10:00:00', - officials: [ - 'A T Holdstock', - 'R J Tucker', - 'N N Menon', - 'M Burns', - 'A J Pycroft', - ], - matchId: '6efd7984-0cae-b667-cbaf-754105c422dd', - fullResult: { - resultType: 'home-win', - description: 'England win by 115 runs', - winner: { - winType: 'runs', - margin: '115', - team: 'England', - }, - }, - }, - nav: { - currentUrl: '/cricket', - pillars: [ - { - title: 'News', - url: '/', - longTitle: 'Headlines', - iconName: 'home', - children: [ - { - title: 'UK', - url: '/uk-news', - longTitle: 'UK news', - children: [ - { - title: 'UK politics', - url: '/politics', - }, - { - title: 'Education', - url: '/education', - children: [ - { - title: 'Schools', - url: '/education/schools', - }, - { - title: 'Teachers', - url: '/teacher-network', - }, - { - title: 'Universities', - url: '/education/universities', - }, - { - title: 'Students', - url: '/education/students', - }, - ], - }, - { - title: 'Media', - url: '/media', - }, - { - title: 'Society', - url: '/society', - }, - { - title: 'Law', - url: '/law', - }, - { - title: 'Scotland', - url: '/uk/scotland', - }, - { - title: 'Wales', - url: '/uk/wales', - }, - { - title: 'Northern Ireland', - url: '/uk/northernireland', - }, - ], - }, - { - title: 'World Cup 2026', - url: '/football/world-cup-2026', - }, - { - title: 'US politics', - url: '/us-news/us-politics', - }, - { - title: 'World', - url: '/world', - longTitle: 'World news', - children: [ - { - title: 'Europe', - url: '/world/europe-news', - }, - { - title: 'US news', - url: '/us-news', - longTitle: 'US news', - }, - { - title: 'Americas', - url: '/world/americas', - }, - { - title: 'Asia', - url: '/world/asia', - }, - { - title: 'Australia', - url: '/australia-news', - longTitle: 'Australia news', - }, - { - title: 'Middle East', - url: '/world/middleeast', - }, - { - title: 'Africa', - url: '/world/africa', - }, - { - title: 'Inequality', - url: '/inequality', - }, - { - title: 'Global development', - url: '/global-development', - }, - ], - }, - { - title: 'Climate crisis', - url: '/environment/climate-crisis', - }, - { - title: 'Middle East', - url: '/world/middleeast', - }, - { - title: 'Ukraine', - url: '/world/ukraine', - }, - { - title: 'Football', - url: '/football', - children: [ - { - title: 'Live scores', - url: '/football/live', - longTitle: 'football/live', - }, - { - title: 'Tables', - url: '/football/tables', - longTitle: 'football/tables', - }, - { - title: 'Fixtures', - url: '/football/fixtures', - longTitle: 'football/fixtures', - }, - { - title: 'Results', - url: '/football/results', - longTitle: 'football/results', - }, - { - title: 'World Cup 2026', - url: '/football/world-cup-2026', - }, - { - title: 'Competitions', - url: '/football/competitions', - longTitle: 'football/competitions', - }, - { - title: 'Clubs', - url: '/football/teams', - longTitle: 'football/teams', - }, - ], - }, - { - title: 'Newsletters', - url: '/email-newsletters', - }, - { - title: 'Business', - url: '/business', - children: [ - { - title: 'Economics', - url: '/business/economics', - }, - { - title: 'Banking', - url: '/business/banking', - }, - { - title: 'Money', - url: '/money', - children: [ - { - title: 'Property', - url: '/money/property', - }, - { - title: 'Pensions', - url: '/money/pensions', - }, - { - title: 'Savings', - url: '/money/savings', - }, - { - title: 'Borrowing', - url: '/money/debt', - }, - { - title: 'Careers', - url: '/money/work-and-careers', - }, - ], - }, - { - title: 'Markets', - url: '/business/stock-markets', - }, - { - title: 'Project Syndicate', - url: '/business/series/project-syndicate-economists', - }, - { - title: 'B2B', - url: '/business-to-business', - }, - { - title: 'Retail', - url: '/business/retail', - }, - ], - }, - { - title: 'Environment', - url: '/environment', - children: [ - { - title: 'Climate crisis', - url: '/environment/climate-crisis', - }, - { - title: 'Wildlife', - url: '/environment/wildlife', - }, - { - title: 'Energy', - url: '/environment/energy', - }, - { - title: 'Pollution', - url: '/environment/pollution', - }, - ], - }, - { - title: 'UK politics', - url: '/politics', - }, - { - title: 'Science', - url: '/science', - }, - { - title: 'Tech', - url: '/technology', - }, - { - title: 'Global development', - url: '/global-development', - }, - { - title: 'Obituaries', - url: '/obituaries', - }, - ], - }, - { - title: 'Opinion', - url: '/commentisfree', - longTitle: 'Opinion home', - iconName: 'home', - children: [ - { - title: 'The Guardian view', - url: '/profile/editorial', - }, - { - title: 'Columnists', - url: '/uk/columnists', - }, - { - title: 'Cartoons', - url: '/tone/cartoons', - }, - { - title: 'Opinion videos', - url: '/type/video+tone/comment', - }, - { - title: 'Letters', - url: '/tone/letters', - }, - ], - }, - { - title: 'Sport', - url: '/sport', - longTitle: 'Sport home', - iconName: 'home', - children: [ - { - title: 'World Cup 2026', - url: '/football/world-cup-2026', - }, - { - title: 'Football', - url: '/football', - children: [ - { - title: 'Live scores', - url: '/football/live', - longTitle: 'football/live', - }, - { - title: 'Tables', - url: '/football/tables', - longTitle: 'football/tables', - }, - { - title: 'Fixtures', - url: '/football/fixtures', - longTitle: 'football/fixtures', - }, - { - title: 'Results', - url: '/football/results', - longTitle: 'football/results', - }, - { - title: 'World Cup 2026', - url: '/football/world-cup-2026', - }, - { - title: 'Competitions', - url: '/football/competitions', - longTitle: 'football/competitions', - }, - { - title: 'Clubs', - url: '/football/teams', - longTitle: 'football/teams', - }, - ], - }, - { - title: 'Cricket', - url: '/sport/cricket', - }, - { - title: 'Rugby union', - url: '/sport/rugby-union', - }, - { - title: 'Tennis', - url: '/sport/tennis', - }, - { - title: 'Cycling', - url: '/sport/cycling', - }, - { - title: 'F1', - url: '/sport/formulaone', - }, - { - title: 'Golf', - url: '/sport/golf', - }, - { - title: 'Boxing', - url: '/sport/boxing', - }, - { - title: 'Rugby league', - url: '/sport/rugbyleague', - }, - { - title: 'Racing', - url: '/sport/horse-racing', - }, - { - title: 'US sports', - url: '/sport/us-sport', - }, - ], - }, - { - title: 'Culture', - url: '/culture', - longTitle: 'Culture home', - iconName: 'home', - children: [ - { - title: 'Film', - url: '/film', - }, - { - title: 'Music', - url: '/music', - }, - { - title: 'TV & radio', - url: '/tv-and-radio', - }, - { - title: 'Books', - url: '/books', - }, - { - title: 'Art & design', - url: '/artanddesign', - }, - { - title: 'Stage', - url: '/stage', - }, - { - title: 'Games', - url: '/games', - }, - { - title: 'Classical', - url: '/music/classicalmusicandopera', - }, - ], - }, - { - title: 'Lifestyle', - url: '/lifeandstyle', - longTitle: 'Lifestyle home', - iconName: 'home', - children: [ - { - title: 'The Filter', - url: '/uk/thefilter', - }, - { - title: 'Fashion', - url: '/fashion', - }, - { - title: 'Food', - url: '/food', - }, - { - title: 'Recipes', - url: '/tone/recipes', - }, - { - title: 'Travel', - url: '/travel', - children: [ - { - title: 'UK', - url: '/travel/uk', - }, - { - title: 'Europe', - url: '/travel/europe', - }, - { - title: 'US', - url: '/travel/usa', - }, - ], - }, - { - title: 'Health & fitness', - url: '/lifeandstyle/health-and-wellbeing', - }, - { - title: 'Women', - url: '/lifeandstyle/women', - }, - { - title: 'Men', - url: '/lifeandstyle/men', - }, - { - title: 'Love & sex', - url: '/lifeandstyle/love-and-sex', - }, - { - title: 'Beauty', - url: '/fashion/beauty', - }, - { - title: 'Home & garden', - url: '/lifeandstyle/home-and-garden', - }, - { - title: 'Money', - url: '/money', - children: [ - { - title: 'Property', - url: '/money/property', - }, - { - title: 'Pensions', - url: '/money/pensions', - }, - { - title: 'Savings', - url: '/money/savings', - }, - { - title: 'Borrowing', - url: '/money/debt', - }, - { - title: 'Careers', - url: '/money/work-and-careers', - }, - ], - }, - { - title: 'Cars', - url: '/technology/motoring', - }, - ], - }, - ], - otherLinks: [ - { - title: 'The Guardian app', - url: 'https://app.adjust.com/16xt6hai', - }, - { - title: 'Video', - url: '/video', - }, - { - title: 'Podcasts', - url: '/podcasts', - }, - { - title: 'Pictures', - url: '/inpictures', - }, - { - title: 'Newsletters', - url: '/email-newsletters', - }, - { - title: "Today's paper", - url: '/theguardian', - children: [ - { - title: 'Obituaries', - url: '/obituaries', - }, - { - title: 'G2', - url: '/theguardian/g2', - }, - { - title: 'Journal', - url: '/theguardian/journal', - }, - { - title: 'Saturday', - url: '/theguardian/saturday', - }, - ], - }, - { - title: 'Inside the Guardian', - url: 'https://www.theguardian.com/insidetheguardian', - }, - { - title: 'Guardian Weekly', - url: 'https://www.theguardian.com/weekly?INTCMP=gdnwb_mawns_editorial_gweekly_GW_TopNav_UK', - }, - { - title: 'Crosswords', - url: '/crosswords', - children: [ - { - title: 'Blog', - url: '/crosswords/crossword-blog', - }, - { - title: 'Quick', - url: '/crosswords/series/quick', - }, - { - title: 'Sunday quick', - url: '/crosswords/series/sunday-quick', - }, - { - title: 'Mini', - url: '/crosswords/series/mini-crossword', - }, - { - title: 'Quick cryptic', - url: '/crosswords/series/quick-cryptic', - }, - { - title: 'Quiptic', - url: '/crosswords/series/quiptic', - }, - { - title: 'Cryptic', - url: '/crosswords/series/cryptic', - }, - { - title: 'Prize', - url: '/crosswords/series/prize', - }, - { - title: 'Genius', - url: '/crosswords/series/genius', - }, - { - title: 'Weekend', - url: '/crosswords/series/weekend-crossword', - }, - { - title: 'Special', - url: '/crosswords/series/special', - }, - ], - }, - { - title: 'Wordiply', - url: 'https://www.wordiply.com', - }, - { - title: 'Corrections', - url: '/theguardian/series/corrections-and-clarifications', - }, - { - title: 'Tips', - url: 'https://www.theguardian.com/tips', - }, - ], - brandExtensions: [ - { - title: 'Search jobs', - url: 'https://jobs.theguardian.com', - }, - { - title: 'Hire with Guardian Jobs', - url: 'https://recruiters.theguardian.com/?utm_source=gdnwb&utm_medium=navbar&utm_campaign=Guardian_Navbar_Recruiters&CMP_TU=trdmkt&CMP_BUNIT=jobs', - }, - { - title: 'Holidays', - url: 'https://holidays.theguardian.com?INTCMP=holidays_uk_web_newheader', - }, - { - title: 'Live events', - url: 'https://www.theguardian.com/guardian-live-events?INTCMP=live_uk_header_dropdown', - }, - { - title: 'About Us', - url: '/about', - }, - { - title: 'Digital Archive', - url: 'https://theguardian.newspapers.com', - }, - { - title: 'Guardian Print Shop', - url: '/artanddesign/series/gnm-print-sales', - }, - { - title: 'Patrons', - url: 'https://patrons.theguardian.com/?INTCMP=header_patrons', - }, - { - title: 'Guardian Licensing', - url: 'https://licensing.theguardian.com/', - }, - ], - readerRevenueLinks: { - header: { - contribute: - 'https://support.theguardian.com/contribute?INTCMP=header_support_contribute&acquisitionData=%7B%22source%22:%22GUARDIAN_WEB%22,%22componentType%22:%22ACQUISITIONS_HEADER%22,%22componentId%22:%22header_support_contribute%22%7D', - subscribe: - 'https://support.theguardian.com/subscribe?INTCMP=header_support_subscribe&acquisitionData=%7B%22source%22:%22GUARDIAN_WEB%22,%22componentType%22:%22ACQUISITIONS_HEADER%22,%22componentId%22:%22header_support_subscribe%22%7D', - support: - 'https://support.theguardian.com?INTCMP=header_support&acquisitionData=%7B%22source%22:%22GUARDIAN_WEB%22,%22componentType%22:%22ACQUISITIONS_HEADER%22,%22componentId%22:%22header_support%22%7D', - supporter: - 'https://support.theguardian.com/subscribe?INTCMP=header_supporter_cta&acquisitionData=%7B%22source%22:%22GUARDIAN_WEB%22,%22componentType%22:%22ACQUISITIONS_HEADER%22,%22componentId%22:%22header_supporter_cta%22%7D', - }, - footer: { - contribute: - 'https://support.theguardian.com/contribute?INTCMP=footer_support_contribute&acquisitionData=%7B%22source%22:%22GUARDIAN_WEB%22,%22componentType%22:%22ACQUISITIONS_FOOTER%22,%22componentId%22:%22footer_support_contribute%22%7D', - subscribe: - 'https://support.theguardian.com/subscribe?INTCMP=footer_support_subscribe&acquisitionData=%7B%22source%22:%22GUARDIAN_WEB%22,%22componentType%22:%22ACQUISITIONS_FOOTER%22,%22componentId%22:%22footer_support_subscribe%22%7D', - support: - 'https://support.theguardian.com?INTCMP=footer_support&acquisitionData=%7B%22source%22:%22GUARDIAN_WEB%22,%22componentType%22:%22ACQUISITIONS_FOOTER%22,%22componentId%22:%22footer_support%22%7D', - supporter: - 'https://support.theguardian.com/subscribe?INTCMP=footer_supporter_cta&acquisitionData=%7B%22source%22:%22GUARDIAN_WEB%22,%22componentType%22:%22ACQUISITIONS_FOOTER%22,%22componentId%22:%22footer_supporter_cta%22%7D', - }, - sideMenu: { - contribute: - 'https://support.theguardian.com/contribute?INTCMP=side_menu_support_contribute&acquisitionData=%7B%22source%22:%22GUARDIAN_WEB%22,%22componentType%22:%22ACQUISITIONS_HEADER%22,%22componentId%22:%22side_menu_support_contribute%22%7D', - subscribe: - 'https://support.theguardian.com/subscribe?INTCMP=side_menu_support_subscribe&acquisitionData=%7B%22source%22:%22GUARDIAN_WEB%22,%22componentType%22:%22ACQUISITIONS_HEADER%22,%22componentId%22:%22side_menu_support_subscribe%22%7D', - support: - 'https://support.theguardian.com?INTCMP=side_menu_support&acquisitionData=%7B%22source%22:%22GUARDIAN_WEB%22,%22componentType%22:%22ACQUISITIONS_HEADER%22,%22componentId%22:%22side_menu_support%22%7D', - supporter: - 'https://support.theguardian.com/subscribe?INTCMP=mobilenav_print_cta&acquisitionData=%7B%22source%22:%22GUARDIAN_WEB%22,%22componentType%22:%22ACQUISITIONS_HEADER%22,%22componentId%22:%22mobilenav_print_cta%22%7D', - }, - ampHeader: { - contribute: - 'https://support.theguardian.com/contribute?INTCMP=header_support_contribute&acquisitionData=%7B%22source%22:%22GUARDIAN_WEB%22,%22componentType%22:%22ACQUISITIONS_HEADER%22,%22componentId%22:%22header_support_contribute%22%7D', - subscribe: - 'https://support.theguardian.com/subscribe?INTCMP=header_support_subscribe&acquisitionData=%7B%22source%22:%22GUARDIAN_WEB%22,%22componentType%22:%22ACQUISITIONS_HEADER%22,%22componentId%22:%22header_support_subscribe%22%7D', - support: - 'https://support.theguardian.com?INTCMP=amp_header_support&acquisitionData=%7B%22source%22:%22GUARDIAN_WEB%22,%22componentType%22:%22ACQUISITIONS_HEADER%22,%22componentId%22:%22amp_header_support%22%7D', - supporter: - 'https://support.theguardian.com/subscribe?INTCMP=header_supporter_cta&acquisitionData=%7B%22source%22:%22GUARDIAN_WEB%22,%22componentType%22:%22ACQUISITIONS_HEADER%22,%22componentId%22:%22header_supporter_cta%22%7D', - }, - ampFooter: { - contribute: - 'https://support.theguardian.com/contribute?INTCMP=amp_footer_support_contribute&acquisitionData=%7B%22source%22:%22GUARDIAN_WEB%22,%22componentType%22:%22ACQUISITIONS_FOOTER%22,%22componentId%22:%22amp_footer_support_contribute%22%7D', - subscribe: - 'https://support.theguardian.com/subscribe?INTCMP=amp_footer_support_subscribe&acquisitionData=%7B%22source%22:%22GUARDIAN_WEB%22,%22componentType%22:%22ACQUISITIONS_FOOTER%22,%22componentId%22:%22amp_footer_support_subscribe%22%7D', - support: - 'https://support.theguardian.com?INTCMP=footer_support&acquisitionData=%7B%22source%22:%22GUARDIAN_WEB%22,%22componentType%22:%22ACQUISITIONS_FOOTER%22,%22componentId%22:%22footer_support%22%7D', - supporter: - 'https://support.theguardian.com/subscribe?INTCMP=amp_footer_supporter_cta&acquisitionData=%7B%22source%22:%22GUARDIAN_WEB%22,%22componentType%22:%22ACQUISITIONS_FOOTER%22,%22componentId%22:%22amp_footer_supporter_cta%22%7D', - }, - }, - }, - editionId: 'UK', - guardianBaseURL: 'https://www.theguardian.com', - config: { - switches: { - prebidCriteo: true, - externalVideoEmbeds: true, - lightbox: true, - googleOneTapSwitch: true, - hideNewsletterSignupComponentForSubscribers: true, - prebidAppnexusUkRow: true, - prebidMagnite: true, - commercialMetrics: true, - prebidTrustx: false, - scAdFreeBanner: false, - adaptiveSite: true, - prebidPermutiveAudience: true, - compareVariantDecision: false, - manyNewsletterVisibleRecaptcha: false, - enableSentryReporting: true, - lazyLoadContainers: true, - filterAtAGlance: true, - ampArticleSwitch: false, - remarketing: true, - articleEndSlot: true, - keyEventsCarousel: true, - registerWithPhone: false, - targeting: true, - remoteHeader: true, - ampPrebidOzone: false, - slotBodyEnd: true, - emailInlineInFooter: true, - showNewPrivacyWordingOnEmailSignupEmbeds: true, - prebidAnalytics: true, - extendedMostPopular: true, - ampContentAbTesting: false, - imrWorldwide: true, - prebidTeads: true, - acast: true, - twitterUwt: true, - prebidAppnexusInvcode: true, - ampPrebidPubmatic: false, - a9HeaderBidding: true, - prebidAppnexus: true, - enableDiscussionSwitch: true, - prebidXaxis: true, - stickyVideos: true, - discussionAllPageSize: true, - showNewNewsletterSignupCard: true, - prebidUserSync: true, - audioOnwardJourneySwitch: true, - brazeTaylorReport: false, - callouts: true, - sentinelLogger: true, - geoMostPopular: true, - weAreHiring: false, - relatedContent: true, - thirdPartyEmbedTracking: true, - prebidOzone: true, - ampLiveblogSwitch: false, - ampAmazon: false, - mostViewedFronts: true, - optOutAdvertising: true, - googleSearch: true, - brazeSwitch: true, - signInGate: true, - prebidKargo: true, - consentManagement: true, - disableChildDirected: true, - productLeftColCards: true, - personaliseSignInGateAfterCheckout: true, - idProfileNavigation: true, - confiantAdVerification: true, - discussionAllowAnonymousRecommendsSwitch: false, - permutive: true, - comscore: true, - ampPrebidCriteo: false, - prebidLiveramp: true, - prebidTheTradeDesk: true, - newsletterOnwards: false, - youtubeIma: true, - usSignupHideMarketingToggle: true, - webFonts: true, - liveBlogTopSponsorship: true, - lineItemJobs: true, - ophan: true, - crosswordSvgThumbnails: true, - prebidTriplelift: true, - prebidPubmatic: true, - serverShareCounts: false, - autoRefresh: true, - enhanceTweets: true, - prebidIndexExchange: true, - prebidOpenx: true, - prebidHeaderBidding: true, - idCookieRefresh: true, - discussionPageSize: true, - smartAppBanner: false, - historyTags: true, - brazeContentCards: true, - remoteBanner: true, - emailSignupRecaptcha: true, - prebidSmart: true, - shouldLoadGoogletag: true, - inizio: true, - }, - serverSideABTests: {}, - googletagUrl: '//securepubads.g.doubleclick.net/tag/js/gpt.js', - stage: 'PROD', - frontendAssetsFullURL: 'https://assets.guim.co.uk/', - ampIframeUrl: - 'https://assets.guim.co.uk/data/vendor/2533d5cb94302889e6a8f1b24b5329e7/amp-iframe.html', - googleRecaptchaSiteKey: '6Le7mFsrAAAAAD07aRCTHZe9u0EfvOiNe8Y0DMMV', - fbAppId: '180444840287', - facebookIaAdUnitRoot: 'facebook-instant-articles', - locationapiurl: '/weatherapi/locations?query=', - avatarImagesUrl: 'https://avatar.guim.co.uk', - avatarApiUrl: 'https://avatar.theguardian.com', - membershipUrl: 'https://membership.theguardian.com', - ipsosTag: 'guardian', - isProd: true, - membershipAccess: '', - allowUserGeneratedContent: false, - forecastsapiurl: '/weatherapi/forecast', - supportUrl: 'https://support.theguardian.com', - commercialBundleUrl: - 'https://assets.guim.co.uk/commercial/5fc9045c642159f82da0/graun.standalone.commercial.js', - idOAuthUrl: 'https://oauth.theguardian.com', - webTitle: "First Test Match, Lord's", - isFront: false, - idWebAppUrl: 'https://oauth.theguardian.com', - a9PublisherId: '3722', - googleSearchUrl: '//www.google.co.uk/cse/cse.js', - googleSearchId: '007466294097402385199:m2ealvuxh1i', - idUrl: 'https://profile.theguardian.com', - hasSurveyAd: false, - omnitureAmpAccount: 'guardiangu-thirdpartyapps', - dfpAdUnitRoot: 'theguardian.com', - host: 'https://www.theguardian.com', - dfpAccountId: '59666047', - plistaPublicApiKey: '462925f4f131001fd974bebe', - dcrSentryDsn: - 'https://1937ab71c8804b2b8438178dfdd6468f@sentry.io/1377847', - cardStyle: '', - adUnit: '/59666047/theguardian.com/cricket/ng', - discussionApiUrl: 'https://discussion.theguardian.com/discussion-api', - isSensitive: false, - ophanEmbedJsUrl: '//j.ophan.co.uk/ophan.embed', - edition: 'UK', - brazeApiKey: '7f28c639-8bda-48ff-a3f6-24345abfc07c', - discussionApiClientHeader: 'nextgen', - section: 'cricket', - dfpHost: 'pubads.g.doubleclick.net', - sentryPublicApiKey: '344003a8d11c41d8800fbad8383fdc50', - pillar: '', - pageId: '/sport/cricket/match/2026-06-06/england-cricket-team', - beaconUrl: '//phar.gu-web.net', - discussionD2Uid: 'zHoBy6HNKsk', - ophanJsUrl: '//j.ophan.co.uk/ophan.ng', - contentType: '', - isDev: false, - stripePublicToken: 'pk_live_2O6zPMHXNs2AGea4bAmq5R7Z', - omnitureAccount: 'guardiangu-network', - externalEmbedHost: 'https://embed.theguardian.com', - thirdPartyAppsAccount: 'guardiangu-thirdpartyapps', - ajaxUrl: 'https://api.nextgen.guardianapps.co.uk', - mobileAppsAdUnitRoot: 'beta-guardian-app', - hasPageSkin: false, - requiresMembershipAccess: false, - revisionNumber: 'DEV', - optimizeEpicUrl: - 'https://support.theguardian.com/epic/control/index.html', - assetsPath: 'https://assets.guim.co.uk/', - mmaUrl: 'https://manage.theguardian.com', - sentryHost: 'app.getsentry.com/35463', - buildNumber: 'DEV', - sharedAdTargeting: {}, - onwardWebSocket: - 'ws://api.nextgen.guardianapps.co.uk/recently-published', - shouldHideAdverts: false, - idApiUrl: 'https://idapi.theguardian.com', - pbIndexSites: [], - googletagJsUrl: '//securepubads.g.doubleclick.net/tag/js/gpt.js', - calloutsUrl: - 'https://callouts.guardianapis.com/formstack-campaign/submit', - isPreview: false, - }, - pageFooter: { - footerLinks: [ - [ - { - text: 'About us', - url: '/about', - dataLinkName: 'uk : footer : about us', - extraClasses: '', - }, - { - text: 'Help', - url: 'https://manage.theguardian.com/help-centre', - dataLinkName: 'uk : footer : tech feedback', - extraClasses: 'js-tech-feedback-report', - }, - { - text: 'Complaints & corrections', - url: '/info/complaints-and-corrections', - dataLinkName: 'complaints', - extraClasses: '', - }, - { - text: 'Contact us', - url: '/help/contact-us', - dataLinkName: 'uk : footer : contact us', - extraClasses: '', - }, - { - text: 'Tip us off', - url: 'https://www.theguardian.com/tips', - dataLinkName: 'uk : footer : tips', - extraClasses: '', - }, - { - text: 'SecureDrop', - url: 'https://www.theguardian.com/securedrop', - dataLinkName: 'securedrop', - extraClasses: '', - }, - { - text: 'Privacy policy', - url: '/info/privacy', - dataLinkName: 'privacy', - extraClasses: '', - }, - { - text: 'Cookie policy', - url: '/info/cookies', - dataLinkName: 'cookie', - extraClasses: '', - }, - { - text: 'Modern Slavery Act', - url: 'https://uploads.guim.co.uk/2025/09/05/Modern_Slavery_Statement_2025.pdf', - dataLinkName: 'uk : footer : modern slavery act statement', - extraClasses: '', - }, - { - text: 'Tax strategy', - url: 'https://uploads.guim.co.uk/2025/09/05/Tax_strategy_for_the_year_ended_31_March_2025.pdf', - dataLinkName: 'uk : footer : tax strategy', - extraClasses: '', - }, - { - text: 'Terms & conditions', - url: '/help/terms-of-service', - dataLinkName: 'terms', - extraClasses: '', - }, - ], - [ - { - text: 'All topics', - url: '/index/subjects/a', - dataLinkName: 'uk : footer : all topics', - extraClasses: '', - }, - { - text: 'All writers', - url: '/index/contributors', - dataLinkName: 'uk : footer : all contributors', - extraClasses: '', - }, - { - text: 'Newsletters', - url: '/email-newsletters?INTCMP=DOTCOM_FOOTER_NEWSLETTER_UK', - dataLinkName: 'uk : footer : newsletters', - extraClasses: '', - }, - { - text: 'Digital newspaper archive', - url: 'https://theguardian.newspapers.com', - dataLinkName: 'digital newspaper archive', - extraClasses: '', - }, - { - text: 'Bluesky', - url: 'https://bsky.app/profile/theguardian.com', - dataLinkName: 'uk : footer : Bluesky', - extraClasses: '', - }, - { - text: 'Facebook', - url: 'https://www.facebook.com/theguardian', - dataLinkName: 'uk : footer : Facebook', - extraClasses: '', - }, - { - text: 'Instagram', - url: 'https://www.instagram.com/guardian', - dataLinkName: 'uk : footer : Instagram', - extraClasses: '', - }, - { - text: 'LinkedIn', - url: 'https://www.linkedin.com/company/theguardian', - dataLinkName: 'uk : footer : LinkedIn', - extraClasses: '', - }, - { - text: 'Threads', - url: 'https://www.threads.com/@guardian', - dataLinkName: 'uk : footer : Threads', - extraClasses: '', - }, - { - text: 'TikTok', - url: 'https://www.tiktok.com/@guardian', - dataLinkName: 'uk : footer : TikTok', - extraClasses: '', - }, - { - text: 'YouTube', - url: 'https://www.youtube.com/user/TheGuardian', - dataLinkName: 'uk : footer : YouTube', - extraClasses: '', - }, - ], - [ - { - text: 'Advertise with us', - url: 'https://advertising.theguardian.com', - dataLinkName: 'uk : footer : advertise with us', - extraClasses: '', - }, - { - text: 'Guardian Labs', - url: '/guardian-labs', - dataLinkName: 'uk : footer : guardian labs', - extraClasses: '', - }, - { - text: 'Search jobs', - url: 'https://jobs.theguardian.com', - dataLinkName: 'uk : footer : jobs', - extraClasses: '', - }, - { - text: 'Patrons', - url: 'https://patrons.theguardian.com?INTCMP=footer_patrons', - dataLinkName: 'uk : footer : patrons', - extraClasses: '', - }, - { - text: 'Work with us', - url: 'https://workwithus.theguardian.com/', - dataLinkName: 'uk : footer : work with us', - extraClasses: '', - }, - { - text: 'Accessibility settings', - url: '/help/accessibility-help', - dataLinkName: 'accessibility settings', - extraClasses: '', - }, - ], - ], - }, - isAdFreeUser: false, - contributionsServiceUrl: 'https://contributions.guardianapis.com', - canonicalUrl: - 'https://www.theguardian.com//sport/cricket/match/2026-06-06/england-cricket-team', - pageId: '/sport/cricket/match/2026-06-06/england-cricket-team', -}; diff --git a/dotcom-rendering/fixtures/manual/cricketMatch.ts b/dotcom-rendering/fixtures/manual/cricketMatch.ts index 08d7e0dfffc..6c5887270f6 100644 --- a/dotcom-rendering/fixtures/manual/cricketMatch.ts +++ b/dotcom-rendering/fixtures/manual/cricketMatch.ts @@ -1,4 +1,4 @@ -import type { FECricketMatch } from '../../src/frontend/feCricketMatchPage'; +import type { FECricketMatch } from '../../src/frontend/feCricketMatchData'; export const liveMatch: FECricketMatch = { teams: [ diff --git a/dotcom-rendering/src/components/CricketMatchHeader/CricketMatchHeader.stories.tsx b/dotcom-rendering/src/components/CricketMatchHeader/CricketMatchHeader.stories.tsx index c17b5dd895b..28f183942b5 100644 --- a/dotcom-rendering/src/components/CricketMatchHeader/CricketMatchHeader.stories.tsx +++ b/dotcom-rendering/src/components/CricketMatchHeader/CricketMatchHeader.stories.tsx @@ -5,7 +5,7 @@ import type { FECricketMatchHeader } from '../../frontend/feCricketMatchHeader'; import type { FECricketInnings, FECricketMatch, -} from '../../frontend/feCricketMatchPage'; +} from '../../frontend/feCricketMatchData'; import type { ArticleFormat } from '../../lib/articleFormat'; import type { ArticleDeprecated } from '../../types/article'; import { CricketMatchHeader } from './CricketMatchHeader'; diff --git a/dotcom-rendering/src/components/CricketMatchHeader/CricketMatchHeader.tsx b/dotcom-rendering/src/components/CricketMatchHeader/CricketMatchHeader.tsx index 8db2affb1e3..543d0191205 100644 --- a/dotcom-rendering/src/components/CricketMatchHeader/CricketMatchHeader.tsx +++ b/dotcom-rendering/src/components/CricketMatchHeader/CricketMatchHeader.tsx @@ -21,7 +21,7 @@ import type { CricketMatch, CricketResult, CricketTeam, -} from '../../cricketMatchV2'; +} from '../../cricketMatch'; import { grid } from '../../grid'; import { ArticleDesign, type ArticleFormat } from '../../lib/articleFormat'; import { getCommercialClient } from '../../lib/bridgetApi'; diff --git a/dotcom-rendering/src/components/CricketMatchHeader/headerData.ts b/dotcom-rendering/src/components/CricketMatchHeader/headerData.ts index 0d3f33e2bd5..9584e3af71c 100644 --- a/dotcom-rendering/src/components/CricketMatchHeader/headerData.ts +++ b/dotcom-rendering/src/components/CricketMatchHeader/headerData.ts @@ -1,5 +1,5 @@ import { safeParse } from 'valibot'; -import { type CricketMatch, parseCricketMatchV2 } from '../../cricketMatchV2'; +import { type CricketMatch, parseCricketMatch } from '../../cricketMatch'; import type { FECricketMatchHeader } from '../../frontend/feCricketMatchHeader'; import { feCricketMatchHeaderSchema } from '../../frontend/feCricketMatchHeader'; import { safeParseURL } from '../../lib/parse'; @@ -22,7 +22,7 @@ export const parse = return error('Failed to validate match header json'); } - const parsedMatch = parseCricketMatchV2(feData.value.cricketMatch); + const parsedMatch = parseCricketMatch(feData.value.cricketMatch); if (!parsedMatch.ok) { return error('Failed to parse the match from the header json'); diff --git a/dotcom-rendering/src/components/CricketMatchStat.tsx b/dotcom-rendering/src/components/CricketMatchStat.tsx index 84e2927e3be..2ca1baa01b3 100644 --- a/dotcom-rendering/src/components/CricketMatchStat.tsx +++ b/dotcom-rendering/src/components/CricketMatchStat.tsx @@ -8,7 +8,7 @@ import { textSansBold15, visuallyHidden, } from '@guardian/source/foundations'; -import type { Batter } from '../cricketMatchV2'; +import type { Batter } from '../cricketMatch'; import { palette } from '../palette'; const containerCss = css` diff --git a/dotcom-rendering/src/components/CricketMiniMatchStats.stories.tsx b/dotcom-rendering/src/components/CricketMiniMatchStats.stories.tsx index 3b56f24846b..0366babf02b 100644 --- a/dotcom-rendering/src/components/CricketMiniMatchStats.stories.tsx +++ b/dotcom-rendering/src/components/CricketMiniMatchStats.stories.tsx @@ -1,7 +1,7 @@ import { css } from '@emotion/react'; import { breakpoints, from } from '@guardian/source/foundations'; import preview from '../../.storybook/preview'; -import type { FECricketMatchStatsSummary } from '../frontend/feCricketMatchPage'; +import type { FECricketMatchStatsSummary } from '../frontend/feCricketMatchData'; import { palette } from '../palette'; import { CricketMiniMatchStats as CricketMiniMatchStatsComponent } from './CricketMiniMatchStats'; diff --git a/dotcom-rendering/src/components/CricketMiniMatchStats.tsx b/dotcom-rendering/src/components/CricketMiniMatchStats.tsx index fe70ee8438d..312b75822a3 100644 --- a/dotcom-rendering/src/components/CricketMiniMatchStats.tsx +++ b/dotcom-rendering/src/components/CricketMiniMatchStats.tsx @@ -8,9 +8,9 @@ import { import type { SWRConfiguration } from 'swr'; import useSWR from 'swr'; import { safeParse } from 'valibot'; -import type { CricketMatchStatsSummary } from '../cricketMatchV2'; -import { parseMatchStatsSummary } from '../cricketMatchV2'; -import { feCricketMatchStatsSummarySchema } from '../frontend/feCricketMatchPage'; +import type { CricketMatchStatsSummary } from '../cricketMatch'; +import { parseMatchStatsSummary } from '../cricketMatch'; +import { feCricketMatchStatsSummarySchema } from '../frontend/feCricketMatchData'; import type { Result } from '../lib/result'; import { error, fromValibot, ok } from '../lib/result'; import { palette } from '../palette'; diff --git a/dotcom-rendering/src/components/CricketScorecard.stories.tsx b/dotcom-rendering/src/components/CricketScorecard.stories.tsx index fcac3a3cd31..85909370878 100644 --- a/dotcom-rendering/src/components/CricketScorecard.stories.tsx +++ b/dotcom-rendering/src/components/CricketScorecard.stories.tsx @@ -1,34 +1,31 @@ -import { breakpoints } from '@guardian/source/foundations'; +import { centreColumnDecorator } from '../../.storybook/decorators/gridDecorators'; +import { allModes } from '../../.storybook/modes'; import preview from '../../.storybook/preview'; -import { CricketScorecard as CricketScorecardComponent } from './CricketScorecard'; +import { CricketScorecardNew as CricketScorecardNewComponent } from './CricketScorecard'; const meta = preview.meta({ - component: CricketScorecardComponent, - title: 'Components/CricketScorecard', - decorators: [ - // To make the story not shoved in the corner. - (Story) => ( - <> -
- -
- - ), - ], + component: CricketScorecardNewComponent, + title: 'Components/CricketScorecardNew', + decorators: [centreColumnDecorator], parameters: { chromatic: { - viewports: [breakpoints.mobileLandscape], + modes: { + 'light leftCol': allModes['light leftCol'], + }, }, }, }); -export const CricketScorecard = meta.story({ - name: 'Cricket Scorecard', +export const CricketScorecardNew = meta.story({ + name: 'Cricket Scorecard (New)', args: { allInnings: [ { description: 'India first innings', battingTeam: 'India', + order: 1, + declared: false, + forfeited: false, inningsTotals: { runs: 254, overs: '49.0', @@ -52,7 +49,7 @@ export const CricketScorecard = meta.story({ howOut: 'st Latham b Ravindra', out: true, onStrike: false, - nonStrike: false, + nonStrike: true, }, { name: 'Shubman Gill', @@ -63,7 +60,7 @@ export const CricketScorecard = meta.story({ howOut: 'c Phillips b Santner', out: true, onStrike: false, - nonStrike: false, + nonStrike: true, }, { name: 'Virat Kohli', @@ -74,7 +71,7 @@ export const CricketScorecard = meta.story({ howOut: 'lbw b Bracewell', out: true, onStrike: false, - nonStrike: false, + nonStrike: true, }, { name: 'Shreyas Iyer', @@ -85,7 +82,40 @@ export const CricketScorecard = meta.story({ howOut: 'c Ravindra b Santner', out: true, onStrike: false, - nonStrike: false, + nonStrike: true, + }, + { + name: 'Lokesh Rahul', + ballsFaced: 45, + runs: 39, + fours: 3, + sixes: 0, + howOut: 'run out (Williamson)', + out: true, + onStrike: false, + nonStrike: true, + }, + { + name: 'Hardik Pandya', + ballsFaced: 18, + runs: 22, + fours: 2, + sixes: 1, + howOut: 'c Bracewell b Smith', + out: true, + nonStrike: true, + onStrike: false, + }, + { + name: 'Ravindra Jadeja', + ballsFaced: 24, + runs: 30, + fours: 1, + sixes: 0, + howOut: 'not out', + out: false, + onStrike: true, + nonStrike: true, }, ], bowlers: [ @@ -157,6 +187,11 @@ export const CricketScorecard = meta.story({ ], }, { + description: 'New Zealand first innings', + battingTeam: 'New Zealand', + order: 2, + declared: false, + forfeited: false, inningsTotals: { runs: 254, overs: '49.0', @@ -170,8 +205,6 @@ export const CricketScorecard = meta.story({ penalties: 0, wides: 13, }, - description: 'New Zealand first innings', - battingTeam: 'New Zealand', batters: [ { name: 'Will Young', @@ -182,7 +215,7 @@ export const CricketScorecard = meta.story({ howOut: 'lbw b Vinod', out: true, onStrike: false, - nonStrike: false, + nonStrike: true, }, { name: 'Rachin Ravindra', @@ -193,7 +226,7 @@ export const CricketScorecard = meta.story({ howOut: 'b Yadav', out: true, onStrike: false, - nonStrike: false, + nonStrike: true, }, { name: 'Kane Williamson', @@ -204,7 +237,7 @@ export const CricketScorecard = meta.story({ howOut: 'c & b Yadav', out: true, onStrike: false, - nonStrike: false, + nonStrike: true, }, { name: 'Daryl Mitchell', @@ -215,7 +248,40 @@ export const CricketScorecard = meta.story({ howOut: 'c Sharma b Ahmed', out: true, onStrike: false, - nonStrike: false, + nonStrike: true, + }, + { + name: 'Tom Latham', + ballsFaced: 56, + runs: 44, + fours: 4, + sixes: 0, + howOut: 'c Kohli b Shami', + out: true, + onStrike: false, + nonStrike: true, + }, + { + name: 'Glenn Phillips', + ballsFaced: 34, + runs: 55, + fours: 3, + sixes: 3, + howOut: 'not out', + out: false, + onStrike: true, + nonStrike: true, + }, + { + name: 'Michael Bracewell', + ballsFaced: 8, + runs: 5, + fours: 0, + sixes: 0, + howOut: 'not out', + out: false, + onStrike: false, + nonStrike: true, }, ], bowlers: [ @@ -274,6 +340,7 @@ export const CricketScorecard = meta.story({ 'R S Madugalle', ], homeTeam: { + paID: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890', name: 'India', lineup: [ 'Rohit Sharma', @@ -290,6 +357,7 @@ export const CricketScorecard = meta.story({ ], }, awayTeam: { + paID: 'b2c3d4e5-f6a7-8901-bcde-f12345678901', name: 'New Zealand', lineup: [ 'Rachin Ravindra', diff --git a/dotcom-rendering/src/components/CricketScorecard.tsx b/dotcom-rendering/src/components/CricketScorecard.tsx index 767818247ab..f0b31c2bf56 100644 --- a/dotcom-rendering/src/components/CricketScorecard.tsx +++ b/dotcom-rendering/src/components/CricketScorecard.tsx @@ -1,19 +1,18 @@ import { css } from '@emotion/react'; import { - article15, - articleBold15, from, - headlineBold17, space, textSans14, + textSans15, textSansBold14, - until, + textSansBold15, + visuallyHidden, } from '@guardian/source/foundations'; -import { Stack } from '@guardian/source/react-components'; -import type { ReactNode } from 'react'; +import { type ReactNode } from 'react'; import type { Batter, Bowler, + CricketResult, CricketTeam, Extras, FallOfWicket, @@ -22,44 +21,139 @@ import type { } from '../cricketMatch'; import { palette } from '../palette'; -const borderStyle = css` - border-top: 0.0625rem solid ${palette('--sport-list-border')}; +const visuallyHiddenStyles = css` + ${visuallyHidden} `; -const tableStyles = css` - width: 100%; - background: ${palette('--table-block-background')}; - border-top: 0.0625rem solid ${palette('--sport-top-border')}; - color: ${palette('--table-block-text')}; - border-collapse: inherit; - ${textSans14}; - - caption { - background: ${palette('--table-block-background')}; - ${textSansBold14} +const responsiveTextSans = css` + ${textSans14} + ${from.desktop} { + ${textSans15} } +`; - tfoot, - thead > tr > th { - ${textSansBold14} +const responsiveTextSansBold = css` + ${textSansBold14} + ${from.desktop} { + ${textSansBold15} } +`; - caption, - th, - td { - padding: ${space[3]}px ${space[3]}px ${space[2]}px ${space[3]}px; - text-align: left; - } +const cardStyles = css` + border: 1px solid ${palette('--football-match-stat-border')}; + border-radius: 6px; + overflow: hidden; + color: ${palette('--football-match-stat-text')}; + background-color: ${palette('--football-match-info-background')}; +`; + +const cardSectionStyles = css` + padding: 0 ${space[3]}px ${space[3]}px; +`; + +const sectionHeadingStyles = css` + border-bottom: 1px solid ${palette('--football-match-stat-border')}; + color: ${palette('--football-match-stat-text')}; + padding: ${space[2]}px 0 ${space[1]}px; + ${responsiveTextSansBold} +`; + +const inningsHeadingStyles = css` + padding: 6px ${space[3]}px; + background: ${palette('--cricket-scorecard-first-team-color')}; + color: ${palette('--cricket-scorecard-innings-heading-text')}; + ${responsiveTextSansBold} +`; + +const secondTeamInningsHeadingStyles = css` + background: ${palette('--cricket-scorecard-second-team-color')}; +`; + +const batIconWrapperStyles = css` + display: flex; + width: 24px; + justify-content: center; + align-items: center; + align-self: flex-start; + padding-top: 2px; + flex-shrink: 0; +`; + +const batIconSvgStyles = css` + width: 28px; + height: 28px; + transform: rotate(-15deg); + flex-shrink: 0; +`; + +const firstTeamBatFillStyles = css` + fill: ${palette('--cricket-scorecard-first-team-color')}; +`; + +const secondTeamBatFillStyles = css` + fill: ${palette('--cricket-scorecard-second-team-color')}; +`; - tfoot > tr > *, - td { - ${borderStyle} +const tableStyles = css` + width: 100%; + border-collapse: collapse; + ${responsiveTextSans} +`; + +const cellBaseStyles = css` + padding: ${space[2]}px ${space[3]}px ${space[1]}px 0; + text-align: left; + vertical-align: middle; +`; + +const tableHeadCellStyles = css` + ${cellBaseStyles} + ${responsiveTextSansBold} + color: ${palette('--football-match-stat-text')}; +`; + +const tableCellStyles = css` + ${cellBaseStyles} + ${responsiveTextSans} +`; + +const tableRowHeaderStyles = css` + ${cellBaseStyles} + display: flex; + align-items: center; + ${responsiveTextSans} +`; + +const batterNameTextStyles = css` + display: flex; + flex-direction: column; +`; + +const tableRowStyles = css` + border-top: 1px solid ${palette('--football-match-stat-border')}; +`; + +const numericCellStyles = css` + white-space: nowrap; + text-align: left; +`; + +const dimmedCellStyles = css` + opacity: 0.7; +`; + +const footerRowStyles = css` + border-top: 1px solid ${palette('--football-match-stat-border')}; + td, + th { + ${responsiveTextSansBold} } `; const hideUntilTabletStyle = css` - ${until.tablet} { - display: none; + display: none; + ${from.tablet} { + display: table-cell; } `; @@ -69,40 +163,120 @@ const hideFromTabletStyle = css` } `; -const cricketScorecardGridStyles = css` - display: grid; - grid-template-columns: - [centre-column-start] repeat(4, 1fr) - [centre-column-end]; - column-gap: 10px; - ${from.mobileLandscape} { - column-gap: 20px; - } +const extrasDashedRowStyles = css` + border-top: 1px dashed ${palette('--football-match-stat-border')}; +`; - ${from.tablet} { - grid-template-columns: - [centre-column-start] repeat(12, 40px) - [centre-column-end]; - } +const fowOrderCellStyles = css` + width: ${space[5]}px; + text-align: right; +`; - ${from.desktop} { - grid-template-columns: - [centre-column-start] repeat(8, 60px) - [centre-column-end]; +const fowNameStyles = css` + min-width: 169px; +`; + +const howOutStyles = css` + color: ${palette('--football-match-info-team-number')}; +`; + +const fixedColStyles = css` + flex-shrink: 0; +`; + +const batColWidthStyles = css` + ${fixedColStyles} + width: 42px; +`; + +const bowlOColStyles = css` + ${fixedColStyles} + width: 34px; +`; + +const bowlStatColStyles = css` + ${fixedColStyles} + width: 28px; +`; + +const noFirstRowBorderStyles = css` + tbody tr:first-of-type { + border-top: none; } +`; + +const overallContainerStyles = css` + display: flex; + flex-direction: column; + gap: ${space[3]}px; +`; + +const inningsContainerStyles = css` + display: flex; + flex-direction: column; + gap: ${space[3]}px; +`; + +const lineupsHeadingStyles = css` + ${sectionHeadingStyles} + margin: 0 ${space[3]}px; +`; - ${from.leftCol} { - grid-template-columns: - [left-column-start] repeat(2, 60px) - [left-column-end centre-column-start] repeat(8, 60px) - [centre-column-end]; +const lineupsGridStyles = css` + ${cardSectionStyles} + display: grid; + grid-template-columns: 1fr 1fr; + column-gap: ${space[5]}px; +`; + +const teamContainerStyles = css` + padding: ${space[2]}px 0 2px 0; +`; + +const awayTeamDividerStyles = css` + position: relative; + padding-left: ${space[5]}px; + &::before { + content: ''; + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 1px; + background-color: ${palette('--football-match-stat-border')}; } +`; + +const teamNameStyles = css` + ${responsiveTextSansBold} + margin: 0 0 ${space[2]}px; +`; + +const homeTeamNameStyles = css` + color: ${palette('--cricket-scorecard-first-team-lineup-color')}; +`; + +const awayTeamNameStyles = css` + color: ${palette('--cricket-scorecard-second-team-lineup-color')}; +`; - ${from.wide} { - grid-template-columns: - [left-column-start] repeat(3, 60px) - [left-column-end centre-column-start] repeat(8, 60px) - [centre-column-end]; +const playerListStyles = css` + list-style: none; + margin: 0; + padding: 0; +`; + +const playerItemStyles = css` + ${responsiveTextSans} + color: ${palette('--football-match-stat-text')}; +`; + +const officialsListStyles = css` + list-style: none; + margin: 0; + padding: ${space[2]}px 0 2px 0; + li + li { + margin-top: ${space[2]}px; } `; @@ -113,7 +287,6 @@ const getExtrasDescription = ({ noBalls, penalties, }: Extras): ReactNode => ( - // Return HTML because we might be able to give these more accessible descriptions <> {byes > 0 && `${byes}b `} {legByes > 0 && `${legByes}lb `} @@ -123,227 +296,448 @@ const getExtrasDescription = ({ ); -const Bowling = ({ bowlers }: { bowlers: Bowler[] }) => ( - - - - - - - - - - - - {bowlers.map((bowler) => ( - - - - - - - - ))} - -
BowlerOMRW
- {bowler.name} - - {bowler.overs}.{bowler.balls % 6} - {bowler.maidens}{bowler.runs}{bowler.wickets}
+const SectionHeading = ({ children }: { children: ReactNode }) => ( +

{children}

+); + +const BatIcon = ({ isHomeTeam }: { isHomeTeam: boolean }) => ( +
+ +
); const Batting = ({ batters, extras, inningsTotals, + isHomeTeam, + showBatIcons, }: { batters: Batter[]; extras: Extras; inningsTotals: InningsTotals; + isHomeTeam: boolean; + showBatIcons: boolean; }) => ( - +
- - - - - - + + + + + + - {batters.map((batter) => ( - - + + - - - - - - ))} - - + + + + + ); + })} + + - - - + - - - - - + + + + + -
BatterRunsBalls4s6sBatter + How dismissed + + Runs + + Balls + + 4s + + 6s +
- { + const isAtCrease = showBatIcons && !batter.out; + return ( +
+ {isAtCrease && ( + <> + + + {batter.onStrike + ? '(on strike)' + : '(at crease)'} + + + )} +
+ {batter.name} +
+ {batter.howOut} +
+
+
- {batter.name} - -
{batter.howOut}
- -
{batter.howOut}{batter.runs}{batter.ballsFaced}{batter.fours}{batter.sixes}
- Extras + {batter.howOut} + + + {batter.runs} + + {batter.ballsFaced} + + {batter.fours} + + {batter.sixes} +
+
+ Extras +
+ {getExtrasDescription(extras)} +
+
+ {getExtrasDescription(extras)} - {inningsTotals.extras} - + {inningsTotals.extras}
Totalfor {inningsTotals.wickets}{inningsTotals.runs} +
+
+ Total +
+ for {inningsTotals.wickets} +
+
+
+ for {inningsTotals.wickets} + + {inningsTotals.runs} + {inningsTotals.overs} overs
); -const FallOfWickets = ({ - fallOfWickets, -}: { - fallOfWickets: FallOfWicket[]; -}) => ( +const Bowling = ({ bowlers }: { bowlers: Bowler[] }) => ( - + + + + + - {fallOfWickets.map((fallOfWicket) => ( - - - + - + + + + ))}
Fall of wicketsBowler + O + + M + + R + + W +
{fallOfWicket.order} - {fallOfWicket.name} + {bowlers.map((bowler) => ( +
+ {bowler.name} {fallOfWicket.runs} + {bowler.overs}.{bowler.balls % 6} + + {bowler.maidens} + + {bowler.runs} + + {bowler.wickets} +
); +const FallOfWickets = ({ + fallOfWickets, +}: { + fallOfWickets: FallOfWicket[]; +}) => ( + <> + Fall of wickets + + + {fallOfWickets.map((fow) => ( + + + + + + ))} + +
+ {fow.order} + + {fow.name} + {fow.runs}
+ +); + +const LineupTeam = ({ + team, + teamType, +}: { + team: CricketTeam; + teamType: 'homeTeam' | 'awayTeam'; +}) => ( +
+

+ {team.name} +

+ +
+); + type Props = { allInnings: Innings[]; officials: string[]; homeTeam: CricketTeam; awayTeam: CricketTeam; + matchResult?: CricketResult; }; -export const CricketScorecard = ({ +export const CricketScorecardNew = ({ allInnings, officials, homeTeam, awayTeam, + matchResult, }: Props) => ( - - {allInnings.map((innings) => ( -
-

- {innings.description} -

- - - - {' '} - -
- ))} -
-
-
Umpires
-
{officials.join(', ')}
- -
{homeTeam.name}
-
{homeTeam.lineup.join(', ')}
- -
{awayTeam.name}
-
{awayTeam.lineup.join(', ')}
-
-
-
+
+ {allInnings + .sort((a, b) => b.order - a.order) // reverse order so that the most recent innings is displayed first + .map((innings, index) => { + const isHomeTeam = innings.battingTeam === homeTeam.name; + const isCurrentInnings = + !matchResult && index === allInnings.length - 1; + return ( +
+
+

+ {innings.description} +

+
+
+ +
+
+ +
+
+ +
+
+ ); + })} + +
+

Lineups

+ +
+ + +
+ +
+ Umpires +
    + {officials.map((official) => ( +
  • + {official} +
  • + ))} +
+
+
+
); diff --git a/dotcom-rendering/src/components/CricketScorecardNew.stories.tsx b/dotcom-rendering/src/components/CricketScorecardNew.stories.tsx deleted file mode 100644 index ea3bdfde92d..00000000000 --- a/dotcom-rendering/src/components/CricketScorecardNew.stories.tsx +++ /dev/null @@ -1,377 +0,0 @@ -import { centreColumnDecorator } from '../../.storybook/decorators/gridDecorators'; -import { allModes } from '../../.storybook/modes'; -import preview from '../../.storybook/preview'; -import { CricketScorecardNew as CricketScorecardNewComponent } from './CricketScorecardNew'; - -const meta = preview.meta({ - component: CricketScorecardNewComponent, - title: 'Components/CricketScorecardNew', - decorators: [centreColumnDecorator], - parameters: { - chromatic: { - modes: { - 'light leftCol': allModes['light leftCol'], - }, - }, - }, -}); - -export const CricketScorecardNew = meta.story({ - name: 'Cricket Scorecard (New)', - args: { - allInnings: [ - { - description: 'India first innings', - battingTeam: 'India', - order: 1, - declared: false, - forfeited: false, - inningsTotals: { - runs: 254, - overs: '49.0', - extras: 8, - wickets: 7, - }, - extras: { - byes: 1, - legByes: 2, - wides: 5, - noBalls: 0, - penalties: 0, - }, - batters: [ - { - name: 'Rohit Sharma', - ballsFaced: 83, - runs: 76, - fours: 7, - sixes: 3, - howOut: 'st Latham b Ravindra', - out: true, - onStrike: false, - nonStrike: true, - }, - { - name: 'Shubman Gill', - ballsFaced: 50, - runs: 31, - fours: 0, - sixes: 1, - howOut: 'c Phillips b Santner', - out: true, - onStrike: false, - nonStrike: true, - }, - { - name: 'Virat Kohli', - ballsFaced: 2, - runs: 1, - fours: 0, - sixes: 0, - howOut: 'lbw b Bracewell', - out: true, - onStrike: false, - nonStrike: true, - }, - { - name: 'Shreyas Iyer', - ballsFaced: 62, - runs: 48, - fours: 2, - sixes: 2, - howOut: 'c Ravindra b Santner', - out: true, - onStrike: false, - nonStrike: true, - }, - { - name: 'Lokesh Rahul', - ballsFaced: 45, - runs: 39, - fours: 3, - sixes: 0, - howOut: 'run out (Williamson)', - out: true, - onStrike: false, - nonStrike: true, - }, - { - name: 'Hardik Pandya', - ballsFaced: 18, - runs: 22, - fours: 2, - sixes: 1, - howOut: 'c Bracewell b Smith', - out: true, - nonStrike: true, - onStrike: false, - }, - { - name: 'Ravindra Jadeja', - ballsFaced: 24, - runs: 30, - fours: 1, - sixes: 0, - howOut: 'not out', - out: false, - onStrike: true, - nonStrike: true, - }, - ], - bowlers: [ - { - name: 'Mohammed Shami', - overs: 9, - maidens: 0, - runs: 74, - wickets: 1, - balls: 55, - }, - { - name: 'Hardik Pandya', - overs: 3, - maidens: 0, - runs: 30, - wickets: 0, - balls: 18, - }, - { - name: 'Varun Chakaravarthy', - overs: 10, - maidens: 0, - runs: 45, - wickets: 2, - balls: 60, - }, - { - name: 'Kuldeep Yadav', - overs: 10, - maidens: 0, - runs: 40, - wickets: 2, - balls: 60, - }, - { - name: 'Axar Patel', - overs: 8, - maidens: 0, - runs: 29, - wickets: 0, - balls: 48, - }, - { - name: 'Ravindra Jadeja', - overs: 10, - maidens: 0, - runs: 30, - wickets: 1, - balls: 60, - }, - ], - fallOfWickets: [ - { - order: 1, - name: 'Shubman Gill', - runs: 105, - }, - { - order: 2, - name: 'Virat Kohli', - runs: 106, - }, - { - order: 3, - name: 'Rohit Sharma', - runs: 122, - }, - ], - }, - { - description: 'New Zealand first innings', - battingTeam: 'New Zealand', - order: 2, - declared: false, - forfeited: false, - inningsTotals: { - runs: 254, - overs: '49.0', - extras: 8, - wickets: 7, - }, - extras: { - byes: 0, - legByes: 3, - noBalls: 0, - penalties: 0, - wides: 13, - }, - batters: [ - { - name: 'Will Young', - ballsFaced: 23, - runs: 15, - fours: 2, - sixes: 0, - howOut: 'lbw b Vinod', - out: true, - onStrike: false, - nonStrike: true, - }, - { - name: 'Rachin Ravindra', - ballsFaced: 29, - runs: 37, - fours: 4, - sixes: 1, - howOut: 'b Yadav', - out: true, - onStrike: false, - nonStrike: true, - }, - { - name: 'Kane Williamson', - ballsFaced: 14, - runs: 11, - fours: 1, - sixes: 0, - howOut: 'c & b Yadav', - out: true, - onStrike: false, - nonStrike: true, - }, - { - name: 'Daryl Mitchell', - ballsFaced: 101, - runs: 63, - fours: 3, - sixes: 0, - howOut: 'c Sharma b Ahmed', - out: true, - onStrike: false, - nonStrike: true, - }, - { - name: 'Tom Latham', - ballsFaced: 56, - runs: 44, - fours: 4, - sixes: 0, - howOut: 'c Kohli b Shami', - out: true, - onStrike: false, - nonStrike: true, - }, - { - name: 'Glenn Phillips', - ballsFaced: 34, - runs: 55, - fours: 3, - sixes: 3, - howOut: 'not out', - out: false, - onStrike: true, - nonStrike: true, - }, - { - name: 'Michael Bracewell', - ballsFaced: 8, - runs: 5, - fours: 0, - sixes: 0, - howOut: 'not out', - out: false, - onStrike: false, - nonStrike: true, - }, - ], - bowlers: [ - { - name: 'Mohammed Shami', - overs: 9, - maidens: 0, - runs: 74, - wickets: 1, - balls: 54, - }, - { - name: 'Hardik Pandya', - overs: 3, - maidens: 0, - runs: 30, - wickets: 0, - balls: 18, - }, - { - name: 'Varun Chakaravarthy', - overs: 10, - maidens: 0, - runs: 45, - wickets: 2, - balls: 60, - }, - { - name: 'Kuldeep Yadav', - overs: 10, - maidens: 0, - runs: 40, - wickets: 2, - balls: 60, - }, - ], - fallOfWickets: [ - { - order: 1, - name: 'Will Young', - runs: 57, - }, - { - order: 2, - name: 'Rachin Ravindra', - runs: 69, - }, - ], - }, - ], - officials: [ - 'P R Reiffel', - 'R K Illingworth', - 'J S Wilson', - 'H D P K Dharmasena', - 'R S Madugalle', - ], - homeTeam: { - paID: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890', - name: 'India', - lineup: [ - 'Rohit Sharma', - 'Shubman Gill', - 'Virat Kohli', - 'Shreyas Iyer', - 'Axar Patel', - 'Lokesh Rahul', - 'Hardik Pandya', - 'Ravindra Jadeja', - 'Mohammed Shami', - 'Kuldeep Yadav', - 'Varun Chakaravarthy', - ], - }, - awayTeam: { - paID: 'b2c3d4e5-f6a7-8901-bcde-f12345678901', - name: 'New Zealand', - lineup: [ - 'Rachin Ravindra', - 'Will Young', - 'Kane Williamson', - 'Daryl Mitchell', - 'Tom Latham', - 'Glenn Phillips', - 'Michael Bracewell', - 'Mitchell Santner', - 'Nathan Smith', - 'Kyle Jamieson', - "Will O'Rourke", - ], - }, - }, -}); diff --git a/dotcom-rendering/src/components/CricketScorecardNew.tsx b/dotcom-rendering/src/components/CricketScorecardNew.tsx deleted file mode 100644 index eaf67389b46..00000000000 --- a/dotcom-rendering/src/components/CricketScorecardNew.tsx +++ /dev/null @@ -1,743 +0,0 @@ -import { css } from '@emotion/react'; -import { - from, - space, - textSans14, - textSans15, - textSansBold14, - textSansBold15, - visuallyHidden, -} from '@guardian/source/foundations'; -import { type ReactNode } from 'react'; -import type { - Batter, - Bowler, - CricketResult, - CricketTeam, - Extras, - FallOfWicket, - Innings, - InningsTotals, -} from '../cricketMatchV2'; -import { palette } from '../palette'; - -const visuallyHiddenStyles = css` - ${visuallyHidden} -`; - -const responsiveTextSans = css` - ${textSans14} - ${from.desktop} { - ${textSans15} - } -`; - -const responsiveTextSansBold = css` - ${textSansBold14} - ${from.desktop} { - ${textSansBold15} - } -`; - -const cardStyles = css` - border: 1px solid ${palette('--football-match-stat-border')}; - border-radius: 6px; - overflow: hidden; - color: ${palette('--football-match-stat-text')}; - background-color: ${palette('--football-match-info-background')}; -`; - -const cardSectionStyles = css` - padding: 0 ${space[3]}px ${space[3]}px; -`; - -const sectionHeadingStyles = css` - border-bottom: 1px solid ${palette('--football-match-stat-border')}; - color: ${palette('--football-match-stat-text')}; - padding: ${space[2]}px 0 ${space[1]}px; - ${responsiveTextSansBold} -`; - -const inningsHeadingStyles = css` - padding: 6px ${space[3]}px; - background: ${palette('--cricket-scorecard-first-team-color')}; - color: ${palette('--cricket-scorecard-innings-heading-text')}; - ${responsiveTextSansBold} -`; - -const secondTeamInningsHeadingStyles = css` - background: ${palette('--cricket-scorecard-second-team-color')}; -`; - -const batIconWrapperStyles = css` - display: flex; - width: 24px; - justify-content: center; - align-items: center; - align-self: flex-start; - padding-top: 2px; - flex-shrink: 0; -`; - -const batIconSvgStyles = css` - width: 28px; - height: 28px; - transform: rotate(-15deg); - flex-shrink: 0; -`; - -const firstTeamBatFillStyles = css` - fill: ${palette('--cricket-scorecard-first-team-color')}; -`; - -const secondTeamBatFillStyles = css` - fill: ${palette('--cricket-scorecard-second-team-color')}; -`; - -const tableStyles = css` - width: 100%; - border-collapse: collapse; - ${responsiveTextSans} -`; - -const cellBaseStyles = css` - padding: ${space[2]}px ${space[3]}px ${space[1]}px 0; - text-align: left; - vertical-align: middle; -`; - -const tableHeadCellStyles = css` - ${cellBaseStyles} - ${responsiveTextSansBold} - color: ${palette('--football-match-stat-text')}; -`; - -const tableCellStyles = css` - ${cellBaseStyles} - ${responsiveTextSans} -`; - -const tableRowHeaderStyles = css` - ${cellBaseStyles} - display: flex; - align-items: center; - ${responsiveTextSans} -`; - -const batterNameTextStyles = css` - display: flex; - flex-direction: column; -`; - -const tableRowStyles = css` - border-top: 1px solid ${palette('--football-match-stat-border')}; -`; - -const numericCellStyles = css` - white-space: nowrap; - text-align: left; -`; - -const dimmedCellStyles = css` - opacity: 0.7; -`; - -const footerRowStyles = css` - border-top: 1px solid ${palette('--football-match-stat-border')}; - td, - th { - ${responsiveTextSansBold} - } -`; - -const hideUntilTabletStyle = css` - display: none; - ${from.tablet} { - display: table-cell; - } -`; - -const hideFromTabletStyle = css` - ${from.tablet} { - display: none; - } -`; - -const extrasDashedRowStyles = css` - border-top: 1px dashed ${palette('--football-match-stat-border')}; -`; - -const fowOrderCellStyles = css` - width: ${space[5]}px; - text-align: right; -`; - -const fowNameStyles = css` - min-width: 169px; -`; - -const howOutStyles = css` - color: ${palette('--football-match-info-team-number')}; -`; - -const fixedColStyles = css` - flex-shrink: 0; -`; - -const batColWidthStyles = css` - ${fixedColStyles} - width: 42px; -`; - -const bowlOColStyles = css` - ${fixedColStyles} - width: 34px; -`; - -const bowlStatColStyles = css` - ${fixedColStyles} - width: 28px; -`; - -const noFirstRowBorderStyles = css` - tbody tr:first-of-type { - border-top: none; - } -`; - -const overallContainerStyles = css` - display: flex; - flex-direction: column; - gap: ${space[3]}px; -`; - -const inningsContainerStyles = css` - display: flex; - flex-direction: column; - gap: ${space[3]}px; -`; - -const lineupsHeadingStyles = css` - ${sectionHeadingStyles} - margin: 0 ${space[3]}px; -`; - -const lineupsGridStyles = css` - ${cardSectionStyles} - display: grid; - grid-template-columns: 1fr 1fr; - column-gap: ${space[5]}px; -`; - -const teamContainerStyles = css` - padding: ${space[2]}px 0 2px 0; -`; - -const awayTeamDividerStyles = css` - position: relative; - padding-left: ${space[5]}px; - &::before { - content: ''; - position: absolute; - left: 0; - top: 0; - bottom: 0; - width: 1px; - background-color: ${palette('--football-match-stat-border')}; - } -`; - -const teamNameStyles = css` - ${responsiveTextSansBold} - margin: 0 0 ${space[2]}px; -`; - -const homeTeamNameStyles = css` - color: ${palette('--cricket-scorecard-first-team-lineup-color')}; -`; - -const awayTeamNameStyles = css` - color: ${palette('--cricket-scorecard-second-team-lineup-color')}; -`; - -const playerListStyles = css` - list-style: none; - margin: 0; - padding: 0; -`; - -const playerItemStyles = css` - ${responsiveTextSans} - color: ${palette('--football-match-stat-text')}; -`; - -const officialsListStyles = css` - list-style: none; - margin: 0; - padding: ${space[2]}px 0 2px 0; - li + li { - margin-top: ${space[2]}px; - } -`; - -const getExtrasDescription = ({ - byes, - legByes, - wides, - noBalls, - penalties, -}: Extras): ReactNode => ( - <> - {byes > 0 && `${byes}b `} - {legByes > 0 && `${legByes}lb `} - {wides > 0 && `${wides}w `} - {noBalls > 0 && `${noBalls}nb `} - {penalties > 0 && `${penalties}p`} - -); - -const SectionHeading = ({ children }: { children: ReactNode }) => ( -

{children}

-); - -const BatIcon = ({ isHomeTeam }: { isHomeTeam: boolean }) => ( -
- -
-); - -const Batting = ({ - batters, - extras, - inningsTotals, - isHomeTeam, - showBatIcons, -}: { - batters: Batter[]; - extras: Extras; - inningsTotals: InningsTotals; - isHomeTeam: boolean; - showBatIcons: boolean; -}) => ( - - - - - - - - - - - - - {batters.map((batter) => { - const isAtCrease = showBatIcons && !batter.out; - return ( - - - - - - - - - ); - })} - - - - - - - - - - - - - - - - -
Batter - How dismissed - - Runs - - Balls - - 4s - - 6s -
- {isAtCrease && ( - <> - - - {batter.onStrike - ? '(on strike)' - : '(at crease)'} - - - )} -
- {batter.name} -
- {batter.howOut} -
-
-
- {batter.howOut} - - {batter.runs} - - {batter.ballsFaced} - - {batter.fours} - - {batter.sixes} -
-
- Extras -
- {getExtrasDescription(extras)} -
-
-
- {getExtrasDescription(extras)} - - {inningsTotals.extras} -
-
- Total -
- for {inningsTotals.wickets} -
-
-
- for {inningsTotals.wickets} - - {inningsTotals.runs} - - {inningsTotals.overs} overs -
-); - -const Bowling = ({ bowlers }: { bowlers: Bowler[] }) => ( - - - - - - - - - - - - {bowlers.map((bowler) => ( - - - - - - - - ))} - -
Bowler - O - - M - - R - - W -
- {bowler.name} - - {bowler.overs}.{bowler.balls % 6} - - {bowler.maidens} - - {bowler.runs} - - {bowler.wickets} -
-); - -const FallOfWickets = ({ - fallOfWickets, -}: { - fallOfWickets: FallOfWicket[]; -}) => ( - <> - Fall of wickets - - - {fallOfWickets.map((fow) => ( - - - - - - ))} - -
- {fow.order} - - {fow.name} - {fow.runs}
- -); - -const LineupTeam = ({ - team, - teamType, -}: { - team: CricketTeam; - teamType: 'homeTeam' | 'awayTeam'; -}) => ( -
-

- {team.name} -

- -
-); - -type Props = { - allInnings: Innings[]; - officials: string[]; - homeTeam: CricketTeam; - awayTeam: CricketTeam; - matchResult?: CricketResult; -}; - -export const CricketScorecardNew = ({ - allInnings, - officials, - homeTeam, - awayTeam, - matchResult, -}: Props) => ( -
- {allInnings - .sort((a, b) => b.order - a.order) // reverse order so that the most recent innings is displayed first - .map((innings, index) => { - const isHomeTeam = innings.battingTeam === homeTeam.name; - const isCurrentInnings = - !matchResult && index === allInnings.length - 1; - return ( -
-
-

- {innings.description} -

-
-
- -
-
- -
-
- -
-
- ); - })} - -
-

Lineups

- -
- - -
- -
- Umpires -
    - {officials.map((official) => ( -
  • - {official} -
  • - ))} -
-
-
-
-); diff --git a/dotcom-rendering/src/components/CricketScorecardPage.tsx b/dotcom-rendering/src/components/CricketScorecardPage.tsx deleted file mode 100644 index ac18a74e8f8..00000000000 --- a/dotcom-rendering/src/components/CricketScorecardPage.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { css } from '@emotion/react'; -import { from, headlineBold20, space } from '@guardian/source/foundations'; -import type { CricketMatch } from '../cricketMatch'; -import { grid } from '../grid'; -import { palette } from '../palette'; -import { CricketScorecard } from './CricketScorecard'; - -type Props = { - match: CricketMatch; - guardianBaseUrl: string; -}; - -export const CricketScorecardPage = ({ match, guardianBaseUrl }: Props) => ( -
-

- - Cricket - -

-
- -
-
-); diff --git a/dotcom-rendering/src/components/CricketScorecardTab.tsx b/dotcom-rendering/src/components/CricketScorecardTab.tsx index f59cab04306..65be443001f 100644 --- a/dotcom-rendering/src/components/CricketScorecardTab.tsx +++ b/dotcom-rendering/src/components/CricketScorecardTab.tsx @@ -1,10 +1,10 @@ import { css } from '@emotion/react'; import { from, space } from '@guardian/source/foundations'; import { useEffect, useRef } from 'react'; -import type { CricketMatch } from '../cricketMatchV2'; +import type { CricketMatch } from '../cricketMatch'; import { grid } from '../grid'; import { palette } from '../palette'; -import { CricketScorecardNew } from './CricketScorecardNew'; +import { CricketScorecardNew } from './CricketScorecard'; export type CricketScorecardTabProps = Pick< CricketMatch, diff --git a/dotcom-rendering/src/cricketMatch.test.ts b/dotcom-rendering/src/cricketMatch.test.ts index 1f92fe3f1bc..56c3e5eb0c6 100644 --- a/dotcom-rendering/src/cricketMatch.test.ts +++ b/dotcom-rendering/src/cricketMatch.test.ts @@ -1,86 +1,78 @@ -import { cricketMatchData } from '../fixtures/generated/cricket-match'; -import { parse } from './cricketMatch'; -import type { FECricketMatch } from './frontend/feCricketMatchPage'; +import { liveMatch } from '../fixtures/manual/cricketMatch'; +import { parseCricketMatch } from './cricketMatch'; -describe('parse', () => { - it('parses cricket match correctly', () => { - const parsedResult = parse(cricketMatchData.cricketMatch).getOrThrow( +describe('parseCricketMatchV2', () => { + it('parses a winner result cricket match correctly', () => { + const result = parseCricketMatch(liveMatch).getOrThrow( 'Expected parsing cricket match to succeed', ); - expect(parsedResult.awayTeam.lineup.length).toBe(11); - expect(parsedResult.innings.length).toBe( - cricketMatchData.cricketMatch.innings.length, - ); - }); - it('reverses the innings for display', () => { - const parsedResult = parse(cricketMatchData.cricketMatch).getOrThrow( - 'Expected parsing cricket match to succeed', - ); + expect(result.kind).toEqual('Result'); + expect(result.result).toEqual({ + type: 'home-win', + description: 'England win by 115 runs', + winner: { + type: 'runs', + team: 'England', + margin: 115, + }, + }); + expect(result.matchDate).toEqual(new Date('2026-06-04T10:00:00.000Z')); + }); - const secondToLastInning = - cricketMatchData.cricketMatch.innings[ - cricketMatchData.cricketMatch.innings.length - 2 - ]; - const parsedSecondInning = parsedResult.innings[1]; + it('parses a cricket match in pre-match status', () => { + const result = parseCricketMatch({ + ...liveMatch, + result: 'pre-match', + fullResult: undefined, + }).getOrThrow('Expected parsing cricket match to succeed'); - expect(parsedSecondInning?.description).toBe( - secondToLastInning?.description, - ); + expect(result.kind).toEqual('Fixture'); + expect(result.result).toEqual(undefined); + }); - const lastInning = - cricketMatchData.cricketMatch.innings[ - cricketMatchData.cricketMatch.innings.length - 1 - ]; - const parsedFirstInning = parsedResult.innings[0]; + it('parses a cricket match in in-play status', () => { + const result = parseCricketMatch({ + ...liveMatch, + result: 'in-play', + fullResult: undefined, + }).getOrThrow('Expected parsing cricket match to succeed'); - expect(parsedFirstInning?.fallOfWickets.length).toBe( - lastInning?.fallOfWicket.length, - ); - expect(parsedFirstInning?.inningsTotals.wickets).toBe( - lastInning?.fallOfWicket.length, - ); + expect(result.kind).toEqual('Live'); + expect(result.result).toEqual(undefined); }); - it('calculates the number of wickets fallen', () => { - const parsedResult = parse(cricketMatchData.cricketMatch).getOrThrow( - 'Expected parsing cricket match to succeed', - ); + it('parses an abandoned cricket match correctly', () => { + const result = parseCricketMatch({ + ...liveMatch, + fullResult: { + resultType: 'abandoned', + description: 'Match abandoned due to rain', + winner: undefined, + }, + }).getOrThrow('Expected parsing cricket match to succeed'); - const inning = - cricketMatchData.cricketMatch.innings[ - cricketMatchData.cricketMatch.innings.length - 1 - ]; - const parsedInning = parsedResult.innings[0]; - - expect(parsedInning?.fallOfWickets.length).toBe( - inning?.fallOfWicket.length, - ); - expect(parsedInning?.inningsTotals.wickets).toBe( - inning?.fallOfWicket.length, - ); + expect(result.result).toEqual({ + type: 'abandoned', + description: 'Match abandoned due to rain', + winner: undefined, + }); }); - it('returns an error if home and away team cannot be determined', () => { - const cricketMatch: FECricketMatch = { - ...cricketMatchData.cricketMatch, - teams: [ - { - name: '', - lineup: [], - home: true, - id: '', - }, - { - name: '', - lineup: [], - home: true, - id: '2', - }, - ], - }; + it('parses a cricket match with no winner', () => { + const result = parseCricketMatch({ + ...liveMatch, + fullResult: { + resultType: 'no-result', + description: 'No result', + winner: undefined, + }, + }).getOrThrow('Expected parsing cricket match to succeed'); - const parsedResult = parse(cricketMatch); - expect(parsedResult.ok).toBe(false); + expect(result.result).toEqual({ + type: 'no-result', + description: 'No result', + winner: undefined, + }); }); }); diff --git a/dotcom-rendering/src/cricketMatch.ts b/dotcom-rendering/src/cricketMatch.ts index e9868666c07..dbbd061bb21 100644 --- a/dotcom-rendering/src/cricketMatch.ts +++ b/dotcom-rendering/src/cricketMatch.ts @@ -1,9 +1,16 @@ import { isUndefined } from '@guardian/libs'; +import type { Output } from 'valibot'; +import { is, picklist } from 'valibot'; import type { - FECricketInnings, FECricketMatch, -} from './frontend/feCricketMatchPage'; -import { error, ok, type Result } from './lib/result'; + FECricketMatchResult, + FECricketMatchResultWinnerStatus, + FECricketMatchStatsSummary, + FECricketTeam, +} from './frontend/feCricketMatchData'; +import { parseDate, parseIntResult } from './lib/parse'; +import type { Result } from './lib/result'; +import { error, ok } from './lib/result'; export type Bowler = { name: string; @@ -49,97 +56,346 @@ export type FallOfWicket = { export type CricketTeam = { name: string; + paID: string; lineup: string[]; }; export type Innings = { description: string; battingTeam: string; + order: number; bowlers: Bowler[]; batters: Batter[]; extras: Extras; + declared: boolean; + forfeited: boolean; inningsTotals: InningsTotals; fallOfWickets: FallOfWicket[]; }; +const winnerTypesSchema = picklist([ + 'runs', + 'wickets', + 'innings', + 'forfeit', + 'run-rate', +] as const); +type WinnerType = Output; + +const parseWinnerType = (type: string): Result => { + if (is(winnerTypesSchema, type)) { + return ok(type); + } + + return error({ + kind: 'CricketMatchInvalidResult', + message: `Invalid winner type: ${type}`, + }); +}; + +type WinnerResult = { + type: 'home-win' | 'away-win'; + description?: string; + winner: { + type: WinnerType; + team: string; + margin?: number; + }; +}; + +type OtherResult = { + type: + | 'no-result' + | 'draw' + | 'abandoned' + | 'tied' + | 'level-scores-draw' + | 'none'; + description?: string; +}; + +export type CricketResult = WinnerResult | OtherResult; + +export type CricketMatchKind = 'Fixture' | 'Live' | 'Result'; + export type CricketMatch = { + kind: CricketMatchKind; + series: string; + competition: string; + venue: string; + day?: number; + matchDate: Date; homeTeam: CricketTeam; awayTeam: CricketTeam; - officials: string[]; innings: Innings[]; - competitionName: string; - venueName: string; - result: string; + result?: CricketResult; + officials: string[]; }; -const feInningsToDCARInnings = (feInnings: FECricketInnings): Innings => { - const inningsTotals = { - runs: feInnings.runsScored, - overs: feInnings.overs, - wickets: feInnings.fallOfWicket.length, - extras: feInnings.extras, - }; +export type CricketMatchStatsSummary = { + status: string; + currentBattingTeam?: string; + notOutBatters?: Batter[]; +}; - const extras = { - byes: feInnings.byes, - legByes: feInnings.legByes, - noBalls: feInnings.noBalls, - penalties: feInnings.penalties, - wides: feInnings.wides, - }; +const paCricketStatusToMatchKind: Record = { + // Fixtures + 'pre-match': 'Fixture', + 'start-delayed': 'Fixture', + cancelled: 'Fixture', + // Live + 'in-play': 'Live', + tea: 'Live', + lunch: 'Live', + 'between-innings': 'Live', + stumps: 'Live', + 'rain-delay': 'Live', + 'bad-light': 'Live', + 'crowd-trouble': 'Live', + 'pitch-condition': 'Live', + 'floodlight-failure': 'Live', + 'play-suspended-unknown': 'Live', + snow: 'Live', + 'medical-emergency': 'Live', + // Results + result: 'Result', + abandoned: 'Result', +}; - return { - description: feInnings.description, - battingTeam: feInnings.battingTeam, - inningsTotals, - extras, - fallOfWickets: feInnings.fallOfWicket, - batters: feInnings.batters.map((b) => ({ - name: b.name, - ballsFaced: b.ballsFaced, - runs: b.runs, - fours: b.fours, - sixes: b.sixes, - howOut: b.howOut, - out: b.out, - onStrike: b.onStrike, - nonStrike: b.nonStrike, - })), - bowlers: feInnings.bowlers, - }; +type CricketMatchInvalidDate = { + kind: 'CricketMatchInvalidDate'; + message: string; }; -type HomeAndAwayUndefined = { - kind: 'HomeAndAwayUndefined'; +type UnknownMatchStatus = { + kind: 'UnknownMatchStatus'; message: string; }; -type ParserError = HomeAndAwayUndefined; +type CricketMatchInvalidResult = { + kind: 'CricketMatchInvalidResult'; + message: string; +}; -export const parse = ( - feCricketMatch: FECricketMatch, -): Result => { - const homeTeam = feCricketMatch.teams.find((team) => team.home); - const awayTeam = feCricketMatch.teams.find((team) => !team.home); +type CricketWinnerInvalidMarginNumber = { + kind: 'CricketWinnerInvalidMarginNumber'; + message: string; +}; + +type CricketMatchInvalidTeams = { + kind: 'CricketMatchInvalidTeams'; + message: string; +}; + +type ParserError = + | CricketMatchInvalidDate + | UnknownMatchStatus + | CricketMatchInvalidResult + | CricketWinnerInvalidMarginNumber + | CricketMatchInvalidTeams; - if (isUndefined(homeTeam) || isUndefined(awayTeam)) { +type ParsedTeams = { + homeTeam: CricketTeam; + awayTeam: CricketTeam; +}; + +const parseTeams = ( + teams: FECricketTeam[], +): Result => { + const [firstTeam, secondTeam] = teams; + + if (!firstTeam || !secondTeam) { return error({ - kind: 'HomeAndAwayUndefined', - message: 'Could not determine home and away cricket teams', + kind: 'CricketMatchInvalidTeams', + message: `Expected two teams, received ${teams.length}`, }); } - const innings = feCricketMatch.innings - .map(feInningsToDCARInnings) - .reverse(); + const [homeSource, awaySource] = firstTeam.home + ? [firstTeam, secondTeam] + : [secondTeam, firstTeam]; return ok({ - homeTeam, - awayTeam, - officials: feCricketMatch.officials, - innings, - competitionName: feCricketMatch.stage, - venueName: feCricketMatch.venueName, - result: feCricketMatch.result, + homeTeam: { + name: homeSource.name, + paID: homeSource.id, + lineup: homeSource.lineup, + }, + awayTeam: { + name: awaySource.name, + paID: awaySource.id, + lineup: awaySource.lineup, + }, + }); +}; + +export const parseMatchStatsSummary = ( + feCricketMatchStatsSummary: FECricketMatchStatsSummary, +): Result => + ok({ + status: feCricketMatchStatsSummary.status, + currentBattingTeam: feCricketMatchStatsSummary.currentBattingTeam, + notOutBatters: feCricketMatchStatsSummary.notOutBatters, }); + +const parseWinnerResult = ( + winner: FECricketMatchResultWinnerStatus, + resultType: WinnerResult['type'], + description?: string, +): Result => { + return parseWinnerType(winner.winType).flatMap((winnerType) => { + if (winner.margin) { + return parseIntResult(winner.margin) + .mapError((message) => ({ + kind: 'CricketWinnerInvalidMarginNumber', + message, + })) + .map((margin) => ({ + type: resultType, + description, + winner: { + type: winnerType, + team: winner.team, + margin, + }, + })); + } + + return ok({ + type: resultType, + description, + winner: { + type: winnerType, + team: winner.team, + }, + }); + }); +}; + +const parseCricketResult = ( + fullResult?: FECricketMatchResult, +): Result => { + if (isUndefined(fullResult)) { + return ok(undefined); + } + + switch (fullResult.resultType) { + case 'home-win': + case 'away-win': + if (!fullResult.winner) { + return error({ + kind: 'CricketMatchInvalidResult', + message: `Missing winner information for result type ${fullResult.resultType}`, + }); + } + + return parseWinnerResult( + fullResult.winner, + fullResult.resultType, + fullResult.description, + ); + + case 'no-result': + case 'draw': + case 'abandoned': + case 'tied': + case 'level-scores-draw': + case 'none': + return ok({ + type: fullResult.resultType, + description: fullResult.description, + }); + + default: + return error({ + kind: 'CricketMatchInvalidResult', + message: `Invalid result type: ${fullResult.resultType}`, + }); + } +}; + +export const parseCricketMatch = ( + feMatch: FECricketMatch, +): Result => { + const matchKind = paCricketStatusToMatchKind[feMatch.result]; + + if (!matchKind) { + return error({ + kind: 'UnknownMatchStatus', + message: `Unknown match status: ${feMatch.result}`, + }); + } + + // The PA cricket date doesn't have time zone but it is a UTC date + const parsedDate = parseDate(`${feMatch.gameDate}Z`).mapError( + (message) => ({ + kind: 'CricketMatchInvalidDate', + message, + }), + ); + + if (!parsedDate.ok) { + return error(parsedDate.error); + } + + return parseTeams(feMatch.teams).flatMap(({ homeTeam, awayTeam }) => + parseCricketResult(feMatch.fullResult).map( + (parsedResult): CricketMatch => ({ + kind: matchKind, + series: feMatch.stage, + competition: feMatch.competitionName, + venue: feMatch.venueName, + matchDate: parsedDate.value, + day: feMatch.currentDay, + homeTeam, + awayTeam, + innings: feMatch.innings.map((innings) => ({ + description: innings.description, + battingTeam: innings.battingTeam, + order: innings.order, + bowlers: innings.bowlers.map((bowler) => ({ + name: bowler.name, + overs: bowler.overs, + maidens: bowler.maidens, + runs: bowler.runs, + wickets: bowler.wickets, + balls: bowler.balls, + })), + batters: innings.batters.map((batter) => ({ + name: batter.name, + ballsFaced: batter.ballsFaced, + runs: batter.runs, + fours: batter.fours, + sixes: batter.sixes, + howOut: batter.howOut, + out: batter.out, + onStrike: batter.onStrike, + nonStrike: batter.nonStrike, + })), + extras: { + byes: innings.byes, + legByes: innings.legByes, + noBalls: innings.noBalls, + penalties: innings.penalties, + wides: innings.wides, + }, + declared: innings.declared, + forfeited: innings.forfeited, + fallOfWickets: innings.fallOfWicket.map((fallOfWicket) => ({ + order: fallOfWicket.order, + name: fallOfWicket.name, + runs: fallOfWicket.runs, + })), + inningsTotals: { + extras: innings.extras, + runs: innings.runsScored, + overs: innings.overs, + wickets: innings.wickets, + }, + })), + result: parsedResult, + officials: feMatch.officials, + }), + ), + ); }; diff --git a/dotcom-rendering/src/cricketMatchV2.test.ts b/dotcom-rendering/src/cricketMatchV2.test.ts deleted file mode 100644 index 628d05517e6..00000000000 --- a/dotcom-rendering/src/cricketMatchV2.test.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { cricketMatchData } from '../fixtures/generated/cricket-match'; -import { parseCricketMatchV2 } from './cricketMatchV2'; - -describe('parseCricketMatchV2', () => { - it('parses a winner result cricket match correctly', () => { - const result = parseCricketMatchV2( - cricketMatchData.cricketMatch, - ).getOrThrow('Expected parsing cricket match to succeed'); - - expect(result.kind).toEqual('Result'); - expect(result.result).toEqual({ - type: 'home-win', - description: 'England win by 115 runs', - winner: { - type: 'runs', - team: 'England', - margin: 115, - }, - }); - expect(result.matchDate).toEqual(new Date('2026-06-04T10:00:00.000Z')); - }); - - it('parses a cricket match in pre-match status', () => { - const result = parseCricketMatchV2({ - ...cricketMatchData.cricketMatch, - result: 'pre-match', - fullResult: undefined, - }).getOrThrow('Expected parsing cricket match to succeed'); - - expect(result.kind).toEqual('Fixture'); - expect(result.result).toEqual(undefined); - }); - - it('parses a cricket match in in-play status', () => { - const result = parseCricketMatchV2({ - ...cricketMatchData.cricketMatch, - result: 'in-play', - fullResult: undefined, - }).getOrThrow('Expected parsing cricket match to succeed'); - - expect(result.kind).toEqual('Live'); - expect(result.result).toEqual(undefined); - }); - - it('parses an abandoned cricket match correctly', () => { - const result = parseCricketMatchV2({ - ...cricketMatchData.cricketMatch, - fullResult: { - resultType: 'abandoned', - description: 'Match abandoned due to rain', - winner: undefined, - }, - }).getOrThrow('Expected parsing cricket match to succeed'); - - expect(result.result).toEqual({ - type: 'abandoned', - description: 'Match abandoned due to rain', - winner: undefined, - }); - }); - - it('parses a cricket match with no winner', () => { - const result = parseCricketMatchV2({ - ...cricketMatchData.cricketMatch, - fullResult: { - resultType: 'no-result', - description: 'No result', - winner: undefined, - }, - }).getOrThrow('Expected parsing cricket match to succeed'); - - expect(result.result).toEqual({ - type: 'no-result', - description: 'No result', - winner: undefined, - }); - }); -}); diff --git a/dotcom-rendering/src/cricketMatchV2.ts b/dotcom-rendering/src/cricketMatchV2.ts deleted file mode 100644 index f8999e9ec03..00000000000 --- a/dotcom-rendering/src/cricketMatchV2.ts +++ /dev/null @@ -1,401 +0,0 @@ -import { isUndefined } from '@guardian/libs'; -import type { Output } from 'valibot'; -import { is, picklist } from 'valibot'; -import type { - FECricketMatch, - FECricketMatchResult, - FECricketMatchResultWinnerStatus, - FECricketMatchStatsSummary, - FECricketTeam, -} from './frontend/feCricketMatchPage'; -import { parseDate, parseIntResult } from './lib/parse'; -import type { Result } from './lib/result'; -import { error, ok } from './lib/result'; - -export type Bowler = { - name: string; - overs: number; - maidens: number; - runs: number; - wickets: number; - balls: number; -}; - -export type Batter = { - name: string; - ballsFaced: number; - runs: number; - fours: number; - sixes: number; - howOut: string; - out: boolean; - onStrike: boolean; - nonStrike: boolean; -}; - -export type Extras = { - byes: number; - legByes: number; - noBalls: number; - penalties: number; - wides: number; -}; - -export type InningsTotals = { - runs: number; - overs: string; - wickets: number; - extras: number; -}; - -export type FallOfWicket = { - order: number; - name: string; - runs: number; -}; - -export type CricketTeam = { - name: string; - paID: string; - lineup: string[]; -}; - -export type Innings = { - description: string; - battingTeam: string; - order: number; - bowlers: Bowler[]; - batters: Batter[]; - extras: Extras; - declared: boolean; - forfeited: boolean; - inningsTotals: InningsTotals; - fallOfWickets: FallOfWicket[]; -}; - -const winnerTypesSchema = picklist([ - 'runs', - 'wickets', - 'innings', - 'forfeit', - 'run-rate', -] as const); -type WinnerType = Output; - -const parseWinnerType = (type: string): Result => { - if (is(winnerTypesSchema, type)) { - return ok(type); - } - - return error({ - kind: 'CricketMatchInvalidResult', - message: `Invalid winner type: ${type}`, - }); -}; - -type WinnerResult = { - type: 'home-win' | 'away-win'; - description?: string; - winner: { - type: WinnerType; - team: string; - margin?: number; - }; -}; - -type OtherResult = { - type: - | 'no-result' - | 'draw' - | 'abandoned' - | 'tied' - | 'level-scores-draw' - | 'none'; - description?: string; -}; - -export type CricketResult = WinnerResult | OtherResult; - -export type CricketMatchKind = 'Fixture' | 'Live' | 'Result'; - -export type CricketMatch = { - kind: CricketMatchKind; - series: string; - competition: string; - venue: string; - day?: number; - matchDate: Date; - homeTeam: CricketTeam; - awayTeam: CricketTeam; - innings: Innings[]; - result?: CricketResult; - officials: string[]; -}; - -export type CricketMatchStatsSummary = { - status: string; - currentBattingTeam?: string; - notOutBatters?: Batter[]; -}; - -const paCricketStatusToMatchKind: Record = { - // Fixtures - 'pre-match': 'Fixture', - 'start-delayed': 'Fixture', - cancelled: 'Fixture', - // Live - 'in-play': 'Live', - tea: 'Live', - lunch: 'Live', - 'between-innings': 'Live', - stumps: 'Live', - 'rain-delay': 'Live', - 'bad-light': 'Live', - 'crowd-trouble': 'Live', - 'pitch-condition': 'Live', - 'floodlight-failure': 'Live', - 'play-suspended-unknown': 'Live', - snow: 'Live', - 'medical-emergency': 'Live', - // Results - result: 'Result', - abandoned: 'Result', -}; - -type CricketMatchInvalidDate = { - kind: 'CricketMatchInvalidDate'; - message: string; -}; - -type UnknownMatchStatus = { - kind: 'UnknownMatchStatus'; - message: string; -}; - -type CricketMatchInvalidResult = { - kind: 'CricketMatchInvalidResult'; - message: string; -}; - -type CricketWinnerInvalidMarginNumber = { - kind: 'CricketWinnerInvalidMarginNumber'; - message: string; -}; - -type CricketMatchInvalidTeams = { - kind: 'CricketMatchInvalidTeams'; - message: string; -}; - -type ParserError = - | CricketMatchInvalidDate - | UnknownMatchStatus - | CricketMatchInvalidResult - | CricketWinnerInvalidMarginNumber - | CricketMatchInvalidTeams; - -type ParsedTeams = { - homeTeam: CricketTeam; - awayTeam: CricketTeam; -}; - -const parseTeams = ( - teams: FECricketTeam[], -): Result => { - const [firstTeam, secondTeam] = teams; - - if (!firstTeam || !secondTeam) { - return error({ - kind: 'CricketMatchInvalidTeams', - message: `Expected two teams, received ${teams.length}`, - }); - } - - const [homeSource, awaySource] = firstTeam.home - ? [firstTeam, secondTeam] - : [secondTeam, firstTeam]; - - return ok({ - homeTeam: { - name: homeSource.name, - paID: homeSource.id, - lineup: homeSource.lineup, - }, - awayTeam: { - name: awaySource.name, - paID: awaySource.id, - lineup: awaySource.lineup, - }, - }); -}; - -export const parseMatchStatsSummary = ( - feCricketMatchStatsSummary: FECricketMatchStatsSummary, -): Result => - ok({ - status: feCricketMatchStatsSummary.status, - currentBattingTeam: feCricketMatchStatsSummary.currentBattingTeam, - notOutBatters: feCricketMatchStatsSummary.notOutBatters, - }); - -const parseWinnerResult = ( - winner: FECricketMatchResultWinnerStatus, - resultType: WinnerResult['type'], - description?: string, -): Result => { - return parseWinnerType(winner.winType).flatMap((winnerType) => { - if (winner.margin) { - return parseIntResult(winner.margin) - .mapError((message) => ({ - kind: 'CricketWinnerInvalidMarginNumber', - message, - })) - .map((margin) => ({ - type: resultType, - description, - winner: { - type: winnerType, - team: winner.team, - margin, - }, - })); - } - - return ok({ - type: resultType, - description, - winner: { - type: winnerType, - team: winner.team, - }, - }); - }); -}; - -const parseCricketResult = ( - fullResult?: FECricketMatchResult, -): Result => { - if (isUndefined(fullResult)) { - return ok(undefined); - } - - switch (fullResult.resultType) { - case 'home-win': - case 'away-win': - if (!fullResult.winner) { - return error({ - kind: 'CricketMatchInvalidResult', - message: `Missing winner information for result type ${fullResult.resultType}`, - }); - } - - return parseWinnerResult( - fullResult.winner, - fullResult.resultType, - fullResult.description, - ); - - case 'no-result': - case 'draw': - case 'abandoned': - case 'tied': - case 'level-scores-draw': - case 'none': - return ok({ - type: fullResult.resultType, - description: fullResult.description, - }); - - default: - return error({ - kind: 'CricketMatchInvalidResult', - message: `Invalid result type: ${fullResult.resultType}`, - }); - } -}; - -export const parseCricketMatchV2 = ( - feMatch: FECricketMatch, -): Result => { - const matchKind = paCricketStatusToMatchKind[feMatch.result]; - - if (!matchKind) { - return error({ - kind: 'UnknownMatchStatus', - message: `Unknown match status: ${feMatch.result}`, - }); - } - - // The PA cricket date doesn't have time zone but it is a UTC date - const parsedDate = parseDate(`${feMatch.gameDate}Z`).mapError( - (message) => ({ - kind: 'CricketMatchInvalidDate', - message, - }), - ); - - if (!parsedDate.ok) { - return error(parsedDate.error); - } - - return parseTeams(feMatch.teams).flatMap(({ homeTeam, awayTeam }) => - parseCricketResult(feMatch.fullResult).map( - (parsedResult): CricketMatch => ({ - kind: matchKind, - series: feMatch.stage, - competition: feMatch.competitionName, - venue: feMatch.venueName, - matchDate: parsedDate.value, - day: feMatch.currentDay, - homeTeam, - awayTeam, - innings: feMatch.innings.map((innings) => ({ - description: innings.description, - battingTeam: innings.battingTeam, - order: innings.order, - bowlers: innings.bowlers.map((bowler) => ({ - name: bowler.name, - overs: bowler.overs, - maidens: bowler.maidens, - runs: bowler.runs, - wickets: bowler.wickets, - balls: bowler.balls, - })), - batters: innings.batters.map((batter) => ({ - name: batter.name, - ballsFaced: batter.ballsFaced, - runs: batter.runs, - fours: batter.fours, - sixes: batter.sixes, - howOut: batter.howOut, - out: batter.out, - onStrike: batter.onStrike, - nonStrike: batter.nonStrike, - })), - extras: { - byes: innings.byes, - legByes: innings.legByes, - noBalls: innings.noBalls, - penalties: innings.penalties, - wides: innings.wides, - }, - declared: innings.declared, - forfeited: innings.forfeited, - fallOfWickets: innings.fallOfWicket.map((fallOfWicket) => ({ - order: fallOfWicket.order, - name: fallOfWicket.name, - runs: fallOfWicket.runs, - })), - inningsTotals: { - extras: innings.extras, - runs: innings.runsScored, - overs: innings.overs, - wickets: innings.wickets, - }, - })), - result: parsedResult, - officials: feMatch.officials, - }), - ), - ); -}; diff --git a/dotcom-rendering/src/devServer/docs/cricketScorecard.tsx b/dotcom-rendering/src/devServer/docs/cricketScorecard.tsx deleted file mode 100644 index 99a0aebbad5..00000000000 --- a/dotcom-rendering/src/devServer/docs/cricketScorecard.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { Available } from './available'; - -export const CricketScorecard = () => ( - <> - -

- These pages are summaries of cricket matches, and contain various - statistics about a match, including batters, bowlers, and the fall - of wickets. They are typically reached from a link at the top of - some recent{' '} - - cricket liveblogs - - , which are a kind of article. -

- -); diff --git a/dotcom-rendering/src/devServer/routers/dotcom.tsx b/dotcom-rendering/src/devServer/routers/dotcom.tsx index 04ecb6b3d91..284e7877205 100644 --- a/dotcom-rendering/src/devServer/routers/dotcom.tsx +++ b/dotcom-rendering/src/devServer/routers/dotcom.tsx @@ -1,6 +1,5 @@ import { Router } from 'express'; import { Article } from '../docs/article'; -import { CricketScorecard } from '../docs/cricketScorecard'; import { Dotcom } from '../docs/dotcom'; import { FootballFixtures } from '../docs/footballFixtures'; import { FootballLive } from '../docs/footballLive'; @@ -38,9 +37,5 @@ dotcom.get( '/football-match-summary', sendReact('Football Match Summary', ), ); -dotcom.get( - '/cricket-scorecard', - sendReact('Cricket Scorecard', ), -); export { dotcom }; diff --git a/dotcom-rendering/src/devServer/routers/pages.tsx b/dotcom-rendering/src/devServer/routers/pages.tsx index f4fb6c6a0da..0f3cf88b97d 100644 --- a/dotcom-rendering/src/devServer/routers/pages.tsx +++ b/dotcom-rendering/src/devServer/routers/pages.tsx @@ -1,6 +1,5 @@ import { Router } from 'express'; import { Article } from '../docs/article'; -import { CricketScorecard } from '../docs/cricketScorecard'; import { EditionsCrosswords } from '../docs/editionsCrosswords'; import { FootballFixtures } from '../docs/footballFixtures'; import { FootballLive } from '../docs/footballLive'; @@ -40,9 +39,5 @@ pages.get( '/football-match-summary', sendReact('Football Match Summary', ), ); -pages.get( - '/cricket-scorecard', - sendReact('Cricket Scorecard', ), -); export { pages }; diff --git a/dotcom-rendering/src/frontend/feCricketMatchData.ts b/dotcom-rendering/src/frontend/feCricketMatchData.ts new file mode 100644 index 00000000000..5c2c54ddfb4 --- /dev/null +++ b/dotcom-rendering/src/frontend/feCricketMatchData.ts @@ -0,0 +1,108 @@ +import type { Output } from 'valibot'; +import { array, boolean, number, object, optional, string } from 'valibot'; + +const feCricketTeamSchema = object({ + name: string(), + id: string(), + home: boolean(), + lineup: array(string()), + teamTagId: optional(string()), +}); + +export type FECricketTeam = Output; + +const feCricketBatterSchema = object({ + name: string(), + order: number(), + ballsFaced: number(), + runs: number(), + fours: number(), + sixes: number(), + out: boolean(), + howOut: string(), + onStrike: boolean(), + nonStrike: boolean(), +}); + +const feCricketBowlerSchema = object({ + name: string(), + order: number(), + overs: number(), + maidens: number(), + runs: number(), + wickets: number(), + balls: number(), +}); + +const feFallOfWicketSchema = object({ + order: number(), + name: string(), + runs: number(), +}); + +const feCricketInningsSchema = object({ + order: number(), + battingTeam: string(), + runsScored: number(), + wickets: number(), + overs: string(), + declared: boolean(), + forfeited: boolean(), + description: string(), + batters: array(feCricketBatterSchema), + bowlers: array(feCricketBowlerSchema), + fallOfWicket: array(feFallOfWicketSchema), + byes: number(), + legByes: number(), + noBalls: number(), + penalties: number(), + wides: number(), + extras: number(), +}); + +export type FECricketInnings = Output; + +const matchWinnerStatusSchema = object({ + winType: string(), + margin: optional(string()), + team: string(), +}); + +export type FECricketMatchResultWinnerStatus = Output< + typeof matchWinnerStatusSchema +>; + +const matchResultSchema = object({ + resultType: string(), + description: optional(string()), + winner: optional(matchWinnerStatusSchema), +}); + +export type FECricketMatchResult = Output; + +export const feCricketMatchSchema = object({ + teams: array(feCricketTeamSchema), + innings: array(feCricketInningsSchema), + stage: string(), + competitionName: string(), + venueName: string(), + result: string(), + currentDay: number(), + totalDays: number(), + gameDate: string(), + officials: array(string()), + matchId: string(), + fullResult: optional(matchResultSchema), +}); + +export type FECricketMatch = Output; + +export const feCricketMatchStatsSummarySchema = object({ + status: string(), + currentBattingTeam: optional(string()), + notOutBatters: optional(array(feCricketBatterSchema)), +}); + +export type FECricketMatchStatsSummary = Output< + typeof feCricketMatchStatsSummarySchema +>; diff --git a/dotcom-rendering/src/frontend/feCricketMatchHeader.ts b/dotcom-rendering/src/frontend/feCricketMatchHeader.ts index 5ff90225420..8d94f0d6201 100644 --- a/dotcom-rendering/src/frontend/feCricketMatchHeader.ts +++ b/dotcom-rendering/src/frontend/feCricketMatchHeader.ts @@ -1,5 +1,5 @@ import { object, optional, type Output, string } from 'valibot'; -import { feCricketMatchSchema } from './feCricketMatchPage'; +import { feCricketMatchSchema } from './feCricketMatchData'; export const feCricketMatchHeaderSchema = object({ cricketMatch: feCricketMatchSchema, diff --git a/dotcom-rendering/src/frontend/feCricketMatchPage.ts b/dotcom-rendering/src/frontend/feCricketMatchPage.ts index 45cc2adfbf5..5c2c54ddfb4 100644 --- a/dotcom-rendering/src/frontend/feCricketMatchPage.ts +++ b/dotcom-rendering/src/frontend/feCricketMatchPage.ts @@ -1,9 +1,5 @@ import type { Output } from 'valibot'; import { array, boolean, number, object, optional, string } from 'valibot'; -import type { EditionId } from '../lib/edition'; -import type { FooterType } from '../types/footer'; -import type { FENavType } from '../types/frontend'; -import type { FESportPageConfig } from './feFootballDataPage'; const feCricketTeamSchema = object({ name: string(), @@ -101,19 +97,6 @@ export const feCricketMatchSchema = object({ export type FECricketMatch = Output; -export type FECricketMatchPage = { - cricketMatch: FECricketMatch; - nav: FENavType; - editionId: EditionId; - guardianBaseURL: string; - config: FESportPageConfig; - pageFooter: FooterType; - isAdFreeUser: boolean; - canonicalUrl?: string; - contributionsServiceUrl: string; - pageId: string; -}; - export const feCricketMatchStatsSummarySchema = object({ status: string(), currentBattingTeam: optional(string()), diff --git a/dotcom-rendering/src/layouts/SportDataPageLayout.stories.tsx b/dotcom-rendering/src/layouts/SportDataPageLayout.stories.tsx index 197b61d725f..2e3e4960ef5 100644 --- a/dotcom-rendering/src/layouts/SportDataPageLayout.stories.tsx +++ b/dotcom-rendering/src/layouts/SportDataPageLayout.stories.tsx @@ -1,7 +1,6 @@ import type { StoryObj } from '@storybook/react-webpack5'; import { footballData } from '../../fixtures/generated/football-live'; import { initialDays, regions } from '../../fixtures/manual/footballData'; -import { CricketScorecard } from '../components/CricketScorecard.stories'; import { FootballTableList as TableListDefault } from '../components/FootballTableList.stories'; import { extractNAV } from '../model/extract-nav'; import { SportDataPageLayout } from './SportDataPageLayout'; @@ -63,20 +62,3 @@ export const Tables = { }, }, } satisfies Story; - -export const CricketMatch = { - args: { - ...Results.args, - sportData: { - ...Results.args.sportData, - kind: 'CricketMatch', - match: { - ...CricketScorecard.input.args, - innings: CricketScorecard.input.args.allInnings, - venueName: 'The Oval', - competitionName: 'World Cup', - result: 'result', - }, - }, - }, -} satisfies Story; diff --git a/dotcom-rendering/src/layouts/SportDataPageLayout.tsx b/dotcom-rendering/src/layouts/SportDataPageLayout.tsx index ce790ba45db..2bc39e43e3a 100644 --- a/dotcom-rendering/src/layouts/SportDataPageLayout.tsx +++ b/dotcom-rendering/src/layouts/SportDataPageLayout.tsx @@ -1,7 +1,6 @@ import { palette } from '@guardian/source/foundations'; import { AdSlot } from '../components/AdSlot.web'; import { AppsFooter } from '../components/AppsFooter.island'; -import { CricketScorecardPage } from '../components/CricketScorecardPage'; import { FootballMatchesPageWrapper } from '../components/FootballMatchesPageWrapper.island'; import { FootballMatchInfoPage } from '../components/FootballMatchInfoPage'; import { FootballTablesPage } from '../components/FootballTablesPage'; @@ -65,13 +64,6 @@ const SportsPage = ({ guardianBaseUrl={sportData.guardianBaseURL} /> ); - case 'CricketMatch': - return ( - - ); case 'FootballMatchSummary': return ( ( const validateFootballTablesPage = ajv.compile( footballTablesPageSchema, ); -const validateCricketMatchPage = ajv.compile( - cricketMatchPageSchema, -); + const validateFootballMatchInfoPage = ajv.compile( footballMatchInfoPageSchema, ); @@ -155,22 +151,6 @@ export const validateAsFootballTablesPage = ( ); }; -export const validateAsCricketMatchPageType = ( - data: unknown, -): FECricketMatchPage => { - if (validateCricketMatchPage(data)) return data; - - const url = - isObject(data) && isObject(data.config) && isString(data.config.pageId) - ? data.config.pageId - : 'unknown url'; - - throw new TypeError( - `Unable to validate request body for url ${url}.\n - ${JSON.stringify(validateCricketMatchPage.errors, null, 2)}`, - ); -}; - export const validateAsFootballMatchPageType = ( data: unknown, ): FEFootballMatchInfoPage => { diff --git a/dotcom-rendering/src/server/handler.sportDataPage.ts b/dotcom-rendering/src/server/handler.sportDataPage.ts index 59b68e90089..f92c90f54ab 100644 --- a/dotcom-rendering/src/server/handler.sportDataPage.ts +++ b/dotcom-rendering/src/server/handler.sportDataPage.ts @@ -1,5 +1,4 @@ import type { RequestHandler } from 'express'; -import { parse as parseCricketMatch } from '../cricketMatch'; import { parse as parseFootballMatch } from '../footballMatch'; import { getParserErrorMessage, @@ -11,7 +10,6 @@ import { parse as parseFootballTables, parseTableSummary, } from '../footballTables'; -import type { FECricketMatchPage } from '../frontend/feCricketMatchPage'; import type { FEFootballCompetition } from '../frontend/feFootballDataPage'; import type { FEFootballMatchInfoPage } from '../frontend/feFootballMatchInfoPage'; import type { FEFootballMatchListPage } from '../frontend/feFootballMatchListPage'; @@ -21,13 +19,11 @@ import { safeParseURL } from '../lib/parse'; import type { NavType } from '../model/extract-nav'; import { extractNAV } from '../model/extract-nav'; import { - validateAsCricketMatchPageType, validateAsFootballMatchListPage, validateAsFootballMatchPageType, validateAsFootballTablesPage, } from '../model/validate'; import type { - CricketMatchPage, FootballMatchInfoPage, FootballMatchListPage, FootballMatchListPageKind, @@ -183,43 +179,6 @@ export const handleFootballTablesPage: RequestHandler = ({ body }, res) => { res.status(200).set('Link', makePrefetchHeader(prefetchScripts)).send(html); }; -const parseFECricketMatch = (data: FECricketMatchPage): CricketMatchPage => { - const parsedCricketMatch = parseCricketMatch(data.cricketMatch); - - if (!parsedCricketMatch.ok) { - throw new Error( - `Failed to parse cricket match: ${parsedCricketMatch.error.kind} ${parsedCricketMatch.error.message}`, - ); - } - - return { - match: parsedCricketMatch.value, - kind: 'CricketMatch', - - editionId: data.editionId, - guardianBaseURL: data.guardianBaseURL, - config: data.config, - pageFooter: data.pageFooter, - isAdFreeUser: data.isAdFreeUser, - canonicalUrl: data.canonicalUrl, - contributionsServiceUrl: data.contributionsServiceUrl, - }; -}; - -export const handleCricketMatchPage: RequestHandler = ({ body }, res) => { - const cricketMatchPageValidated: FECricketMatchPage = - validateAsCricketMatchPageType(body); - - const parsedCricketMatchData = parseFECricketMatch( - cricketMatchPageValidated, - ); - const { html, prefetchScripts } = renderSportPage({ - sportData: parsedCricketMatchData, - nav: parseNav(cricketMatchPageValidated.nav), - }); - res.status(200).set('Link', makePrefetchHeader(prefetchScripts)).send(html); -}; - const parseFEFootballMatch = ( data: FEFootballMatchInfoPage, ): FootballMatchInfoPage => { diff --git a/dotcom-rendering/src/server/render.sportDataPage.web.tsx b/dotcom-rendering/src/server/render.sportDataPage.web.tsx index ca8beed7d47..3caa622b344 100644 --- a/dotcom-rendering/src/server/render.sportDataPage.web.tsx +++ b/dotcom-rendering/src/server/render.sportDataPage.web.tsx @@ -13,7 +13,6 @@ import { polyfillIO } from '../lib/polyfill.io'; import type { NavType } from '../model/extract-nav'; import { createGuardian } from '../model/guardian'; import type { - CricketMatchPage, FootballDataWithRegionsPage, SportDataPage, SportPageKind, @@ -34,8 +33,6 @@ export const decideDescription = (kind: SportPageKind) => { return `Football fixtures ${fromTheGuardian}`; case 'FootballTables': return `Football tables ${fromTheGuardian}`; - case 'CricketMatch': - return `Cricket scores ${fromTheGuardian}`; case 'FootballMatchSummary': return `Football matches ${fromTheGuardian}`; } @@ -48,8 +45,6 @@ export const decideTitle = (sportPage: SportDataPage) => { case 'FootballFixtures': case 'FootballTables': return decideFootballTitleWithCompetition(sportPage); - case 'CricketMatch': - return createCricketTitle(sportPage); case 'FootballMatchSummary': return createMatchSummaryTitle(sportPage.match); } @@ -94,10 +89,6 @@ const decideFootballTitleWithCompetition = ( } }; -const createCricketTitle = (sportPage: CricketMatchPage) => { - return `${sportPage.match.competitionName}, ${sportPage.match.venueName} | Cricket | The Guardian`; -}; - type Props = { sportData: SportDataPage; nav: NavType; diff --git a/dotcom-rendering/src/server/server.dev.ts b/dotcom-rendering/src/server/server.dev.ts index 0e7e9f5f440..250847b361b 100644 --- a/dotcom-rendering/src/server/server.dev.ts +++ b/dotcom-rendering/src/server/server.dev.ts @@ -20,7 +20,6 @@ import { handleFootballMatchDayEmbed } from './handler.footballMatchDayEmbed'; import { handleFront, handleTagPage } from './handler.front.web'; import { handleAppsFootballMatchPage, - handleCricketMatchPage, handleFootballMatchListPage, handleFootballMatchPage, handleFootballTablesPage, @@ -120,7 +119,6 @@ renderer.get('/AppsBlocks/*url', handleAppsBlocks); renderer.get('/EditionsCrossword/*url', handleEditionsCrossword); renderer.get('/FootballMatchListPage/*url', handleFootballMatchListPage); renderer.get('/FootballTablesPage/*url', handleFootballTablesPage); -renderer.get('/CricketMatchPage/*url', handleCricketMatchPage); renderer.get('/FootballMatchSummaryPage/*url', handleFootballMatchPage); renderer.get('/AppsFootballMatchSummaryPage/*url', handleAppsFootballMatchPage); renderer.get('/HostedContent/*url', handleHostedContent); @@ -141,7 +139,6 @@ renderer.post('/AppsBlocks', handleAppsBlocks); renderer.post('/EditionsCrossword', handleEditionsCrossword); renderer.post('/FootballMatchListPage', handleFootballMatchListPage); renderer.post('/FootballTablesPage', handleFootballTablesPage); -renderer.post('/CricketMatchPage', handleCricketMatchPage); renderer.post('/FootballMatchSummaryPage', handleFootballMatchPage); renderer.post('/AppsFootballMatchSummaryPage', handleAppsFootballMatchPage); renderer.post('/HostedContent', handleHostedContent); diff --git a/dotcom-rendering/src/server/server.prod.ts b/dotcom-rendering/src/server/server.prod.ts index 62b75a3b639..8f1bcdea678 100644 --- a/dotcom-rendering/src/server/server.prod.ts +++ b/dotcom-rendering/src/server/server.prod.ts @@ -21,7 +21,6 @@ import { handleFootballMatchDayEmbed } from './handler.footballMatchDayEmbed'; import { handleFront, handleTagPage } from './handler.front.web'; import { handleAppsFootballMatchPage, - handleCricketMatchPage, handleFootballMatchListPage, handleFootballMatchPage, handleFootballTablesPage, @@ -60,7 +59,6 @@ export const prodServer = (): void => { app.post('/Front', handleFront); app.post('/TagPage', handleTagPage); app.post('/FootballMatchListPage', handleFootballMatchListPage); - app.post('/CricketMatchPage', handleCricketMatchPage); app.post('/FootballTablesPage', handleFootballTablesPage); app.post('/FootballMatchSummaryPage', handleFootballMatchPage); app.post('/FootballMatchDayEmbed', handleFootballMatchDayEmbed); diff --git a/dotcom-rendering/src/sportDataPage.ts b/dotcom-rendering/src/sportDataPage.ts index 8bbb31b3126..26b8a74c767 100644 --- a/dotcom-rendering/src/sportDataPage.ts +++ b/dotcom-rendering/src/sportDataPage.ts @@ -1,4 +1,3 @@ -import type { CricketMatch } from './cricketMatch'; import type { FootballMatch } from './footballMatch'; import type { FootballMatches } from './footballMatches'; import { type FootballMatchStats } from './footballMatchStats'; @@ -46,11 +45,6 @@ export type FootballTablesPage = FootballData & { kind: 'FootballTables'; }; -export type CricketMatchPage = SportPageConfig & { - match: CricketMatch; - kind: 'CricketMatch'; -}; - export type FootballMatchInfoPage = SportPageConfig & { match: FootballMatch; matchStats: FootballMatchStats; @@ -70,10 +64,7 @@ export type FootballDataWithRegionsPage = export type FootballPageWithRegionsKind = FootballDataWithRegionsPage['kind']; -export type SportDataPage = - | FootballDataWithRegionsPage - | CricketMatchPage - | FootballMatchInfoPage; +export type SportDataPage = FootballDataWithRegionsPage | FootballMatchInfoPage; export type SportPageKind = SportDataPage['kind']; From e195ab81f073049b124532f4e36098642cd7fac3 Mon Sep 17 00:00:00 2001 From: Jake Lee Kennedy <1731150+Jakeii@users.noreply.github.com> Date: Mon, 10 Aug 2026 10:13:53 +0100 Subject: [PATCH 2/5] fix test --- dotcom-rendering/src/cricketMatch.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dotcom-rendering/src/cricketMatch.test.ts b/dotcom-rendering/src/cricketMatch.test.ts index 56c3e5eb0c6..3391ddd6ff8 100644 --- a/dotcom-rendering/src/cricketMatch.test.ts +++ b/dotcom-rendering/src/cricketMatch.test.ts @@ -1,9 +1,9 @@ -import { liveMatch } from '../fixtures/manual/cricketMatch'; +import { liveMatch, resultMatch } from '../fixtures/manual/cricketMatch'; import { parseCricketMatch } from './cricketMatch'; describe('parseCricketMatchV2', () => { it('parses a winner result cricket match correctly', () => { - const result = parseCricketMatch(liveMatch).getOrThrow( + const result = parseCricketMatch(resultMatch).getOrThrow( 'Expected parsing cricket match to succeed', ); @@ -17,7 +17,7 @@ describe('parseCricketMatchV2', () => { margin: 115, }, }); - expect(result.matchDate).toEqual(new Date('2026-06-04T10:00:00.000Z')); + expect(result.matchDate).toEqual(new Date('2026-06-17T10:00:00.000Z')); }); it('parses a cricket match in pre-match status', () => { From f39c595ead6a9099d557e7ecc631c38c835a46a8 Mon Sep 17 00:00:00 2001 From: Jake Lee Kennedy <1731150+Jakeii@users.noreply.github.com> Date: Mon, 10 Aug 2026 10:14:33 +0100 Subject: [PATCH 3/5] lint fix --- .../CricketMatchHeader/CricketMatchHeader.stories.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotcom-rendering/src/components/CricketMatchHeader/CricketMatchHeader.stories.tsx b/dotcom-rendering/src/components/CricketMatchHeader/CricketMatchHeader.stories.tsx index 28f183942b5..d5ab5caf1fd 100644 --- a/dotcom-rendering/src/components/CricketMatchHeader/CricketMatchHeader.stories.tsx +++ b/dotcom-rendering/src/components/CricketMatchHeader/CricketMatchHeader.stories.tsx @@ -1,11 +1,11 @@ import type { Meta, StoryObj } from '@storybook/react-webpack5'; import type { ComponentProps } from 'react'; import { expect, waitFor, within } from 'storybook/test'; -import type { FECricketMatchHeader } from '../../frontend/feCricketMatchHeader'; import type { FECricketInnings, FECricketMatch, } from '../../frontend/feCricketMatchData'; +import type { FECricketMatchHeader } from '../../frontend/feCricketMatchHeader'; import type { ArticleFormat } from '../../lib/articleFormat'; import type { ArticleDeprecated } from '../../types/article'; import { CricketMatchHeader } from './CricketMatchHeader'; From e6641e0f8e51c1347213140a7fbbf80d724bb518 Mon Sep 17 00:00:00 2001 From: Jake Lee Kennedy <1731150+Jakeii@users.noreply.github.com> Date: Mon, 10 Aug 2026 10:16:23 +0100 Subject: [PATCH 4/5] remove schema check --- dotcom-rendering/scripts/jsonSchema/schema.mjs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/dotcom-rendering/scripts/jsonSchema/schema.mjs b/dotcom-rendering/scripts/jsonSchema/schema.mjs index beec02248b4..69ffde599cd 100644 --- a/dotcom-rendering/scripts/jsonSchema/schema.mjs +++ b/dotcom-rendering/scripts/jsonSchema/schema.mjs @@ -69,10 +69,6 @@ const schemas = [ typeName: 'FEFootballTablesPage', file: `${root}/src/frontend/schemas/feFootballTablesPage.json`, }, - { - typeName: 'FECricketMatchPage', - file: `${root}/src/frontend/schemas/feCricketMatchPage.json`, - }, { typeName: 'FEFootballMatchInfoPage', file: `${root}/src/frontend/schemas/feFootballMatchInfoPage.json`, From 192c9c0eabef67c654a59e67595f73158c128ab1 Mon Sep 17 00:00:00 2001 From: Jake Lee Kennedy <1731150+Jakeii@users.noreply.github.com> Date: Tue, 11 Aug 2026 11:29:56 +0100 Subject: [PATCH 5/5] remove schema for cricket page --- .../scripts/jsonSchema/schema.mjs | 1 - .../src/frontend/feCricketMatchPage.ts | 108 ------------------ .../schemas/feFootballMatchInfoPage.json | 30 ++--- .../schemas/feFootballMatchListPage.json | 20 ++-- 4 files changed, 25 insertions(+), 134 deletions(-) delete mode 100644 dotcom-rendering/src/frontend/feCricketMatchPage.ts diff --git a/dotcom-rendering/scripts/jsonSchema/schema.mjs b/dotcom-rendering/scripts/jsonSchema/schema.mjs index 69ffde599cd..fe0eedb8541 100644 --- a/dotcom-rendering/scripts/jsonSchema/schema.mjs +++ b/dotcom-rendering/scripts/jsonSchema/schema.mjs @@ -15,7 +15,6 @@ const program = TJS.getProgramFromFiles( path.resolve(`${root}/src/frontend/feTagPage.ts`), path.resolve(`${root}/src/types/newslettersPage.ts`), path.resolve(`${root}/src/types/editionsCrossword.ts`), - path.resolve(`${root}/src/frontend/feCricketMatchPage.ts`), path.resolve(`${root}/src/frontend/feFootballMatchListPage.ts`), path.resolve(`${root}/src/frontend/feFootballTablesPage.ts`), path.resolve(`${root}/src/frontend/feFootballMatchInfoPage.ts`), diff --git a/dotcom-rendering/src/frontend/feCricketMatchPage.ts b/dotcom-rendering/src/frontend/feCricketMatchPage.ts deleted file mode 100644 index 5c2c54ddfb4..00000000000 --- a/dotcom-rendering/src/frontend/feCricketMatchPage.ts +++ /dev/null @@ -1,108 +0,0 @@ -import type { Output } from 'valibot'; -import { array, boolean, number, object, optional, string } from 'valibot'; - -const feCricketTeamSchema = object({ - name: string(), - id: string(), - home: boolean(), - lineup: array(string()), - teamTagId: optional(string()), -}); - -export type FECricketTeam = Output; - -const feCricketBatterSchema = object({ - name: string(), - order: number(), - ballsFaced: number(), - runs: number(), - fours: number(), - sixes: number(), - out: boolean(), - howOut: string(), - onStrike: boolean(), - nonStrike: boolean(), -}); - -const feCricketBowlerSchema = object({ - name: string(), - order: number(), - overs: number(), - maidens: number(), - runs: number(), - wickets: number(), - balls: number(), -}); - -const feFallOfWicketSchema = object({ - order: number(), - name: string(), - runs: number(), -}); - -const feCricketInningsSchema = object({ - order: number(), - battingTeam: string(), - runsScored: number(), - wickets: number(), - overs: string(), - declared: boolean(), - forfeited: boolean(), - description: string(), - batters: array(feCricketBatterSchema), - bowlers: array(feCricketBowlerSchema), - fallOfWicket: array(feFallOfWicketSchema), - byes: number(), - legByes: number(), - noBalls: number(), - penalties: number(), - wides: number(), - extras: number(), -}); - -export type FECricketInnings = Output; - -const matchWinnerStatusSchema = object({ - winType: string(), - margin: optional(string()), - team: string(), -}); - -export type FECricketMatchResultWinnerStatus = Output< - typeof matchWinnerStatusSchema ->; - -const matchResultSchema = object({ - resultType: string(), - description: optional(string()), - winner: optional(matchWinnerStatusSchema), -}); - -export type FECricketMatchResult = Output; - -export const feCricketMatchSchema = object({ - teams: array(feCricketTeamSchema), - innings: array(feCricketInningsSchema), - stage: string(), - competitionName: string(), - venueName: string(), - result: string(), - currentDay: number(), - totalDays: number(), - gameDate: string(), - officials: array(string()), - matchId: string(), - fullResult: optional(matchResultSchema), -}); - -export type FECricketMatch = Output; - -export const feCricketMatchStatsSummarySchema = object({ - status: string(), - currentBattingTeam: optional(string()), - notOutBatters: optional(array(feCricketBatterSchema)), -}); - -export type FECricketMatchStatsSummary = Output< - typeof feCricketMatchStatsSummarySchema ->; diff --git a/dotcom-rendering/src/frontend/schemas/feFootballMatchInfoPage.json b/dotcom-rendering/src/frontend/schemas/feFootballMatchInfoPage.json index e24bc5f08aa..55635c87abc 100644 --- a/dotcom-rendering/src/frontend/schemas/feFootballMatchInfoPage.json +++ b/dotcom-rendering/src/frontend/schemas/feFootballMatchInfoPage.json @@ -46,18 +46,18 @@ "type": "object", "properties": { "matchStats": { - "$ref": "#/definitions/{id:string;status:string;homeTeam:{name:string;id:string;scorers:string[];codename:string;players:{name:string;id:string;position:string;lastName:string;substitute:boolean;timeOnPitch:string;shirtNumber:string;events:{eventTime:string;eventType:string;}[];}[];possession:number;shotsOn:number;shotsOff:number;corners:number;fouls:number;colours:string;crest:string;score?:number;};awayTeam:{name:string;id:string;scorers:string[];codename:string;players:{name:string;id:string;position:string;lastName:string;substitute:boolean;timeOnPitch:string;shirtNumber:string;events:{eventTime:string;eventType:string;}[];}[];possession:number;shotsOn:number;shotsOff:number;corners:number;fouls:number;colours:string;crest:string;score?:number;};comments?:string;}" + "$ref": "#/definitions/{id:string;homeTeam:{name:string;id:string;scorers:string[];codename:string;players:{name:string;id:string;position:string;lastName:string;substitute:boolean;timeOnPitch:string;shirtNumber:string;events:{eventTime:string;eventType:string;}[];}[];possession:number;shotsOn:number;shotsOff:number;corners:number;fouls:number;colours:string;crest:string;score?:number;};awayTeam:{name:string;id:string;scorers:string[];codename:string;players:{name:string;id:string;position:string;lastName:string;substitute:boolean;timeOnPitch:string;shirtNumber:string;events:{eventTime:string;eventType:string;}[];}[];possession:number;shotsOn:number;shotsOff:number;corners:number;fouls:number;colours:string;crest:string;score?:number;};status:string;comments?:string;}" }, "matchInfo": { "anyOf": [ { - "$ref": "#/definitions/{stage:{stageNumber:string;};id:string;type:\"LiveMatch\";status:string;date:string;round:{roundNumber:string;name?:string;};leg:string;homeTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};awayTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};venue?:{name:string;id:string;};comments?:string;attendance?:string;referee?:{name:string;id:string;};}" + "$ref": "#/definitions/{stage:{stageNumber:string;};id:string;type:\"LiveMatch\";date:string;round:{roundNumber:string;name?:string;};leg:string;homeTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};awayTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};status:string;venue?:{name:string;id:string;};comments?:string;attendance?:string;referee?:{name:string;id:string;};}" }, { "$ref": "#/definitions/{stage:{stageNumber:string;};id:string;type:\"Fixture\";date:string;round:{roundNumber:string;name?:string;};leg:string;homeTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};awayTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};venue?:{name:string;id:string;};comments?:string;competition?:{name:string;id:string;};}" }, { - "$ref": "#/definitions/{stage:{stageNumber:string;};id:string;type:\"MatchDay\";result:boolean;date:string;round:{roundNumber:string;name?:string;};leg:string;homeTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};awayTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};liveMatch:boolean;previewAvailable:boolean;reportAvailable:boolean;lineupsAvailable:boolean;matchStatus:string;venue?:{name:string;id:string;};comments?:string;attendance?:string;referee?:{name:string;id:string;};competition?:{name:string;id:string;};}" + "$ref": "#/definitions/{stage:{stageNumber:string;};id:string;type:\"MatchDay\";date:string;round:{roundNumber:string;name?:string;};leg:string;homeTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};awayTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};liveMatch:boolean;result:boolean;previewAvailable:boolean;reportAvailable:boolean;lineupsAvailable:boolean;matchStatus:string;venue?:{name:string;id:string;};comments?:string;attendance?:string;referee?:{name:string;id:string;};competition?:{name:string;id:string;};}" }, { "$ref": "#/definitions/{stage:{stageNumber:string;};id:string;type:\"Result\";date:string;round:{roundNumber:string;name?:string;};leg:string;homeTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};awayTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};reportAvailable:boolean;venue?:{name:string;id:string;};comments?:string;attendance?:string;referee?:{name:string;id:string;};}" @@ -823,21 +823,21 @@ "url" ] }, - "{id:string;status:string;homeTeam:{name:string;id:string;scorers:string[];codename:string;players:{name:string;id:string;position:string;lastName:string;substitute:boolean;timeOnPitch:string;shirtNumber:string;events:{eventTime:string;eventType:string;}[];}[];possession:number;shotsOn:number;shotsOff:number;corners:number;fouls:number;colours:string;crest:string;score?:number;};awayTeam:{name:string;id:string;scorers:string[];codename:string;players:{name:string;id:string;position:string;lastName:string;substitute:boolean;timeOnPitch:string;shirtNumber:string;events:{eventTime:string;eventType:string;}[];}[];possession:number;shotsOn:number;shotsOff:number;corners:number;fouls:number;colours:string;crest:string;score?:number;};comments?:string;}": { + "{id:string;homeTeam:{name:string;id:string;scorers:string[];codename:string;players:{name:string;id:string;position:string;lastName:string;substitute:boolean;timeOnPitch:string;shirtNumber:string;events:{eventTime:string;eventType:string;}[];}[];possession:number;shotsOn:number;shotsOff:number;corners:number;fouls:number;colours:string;crest:string;score?:number;};awayTeam:{name:string;id:string;scorers:string[];codename:string;players:{name:string;id:string;position:string;lastName:string;substitute:boolean;timeOnPitch:string;shirtNumber:string;events:{eventTime:string;eventType:string;}[];}[];possession:number;shotsOn:number;shotsOff:number;corners:number;fouls:number;colours:string;crest:string;score?:number;};status:string;comments?:string;}": { "type": "object", "properties": { "id": { "type": "string" }, - "status": { - "type": "string" - }, "homeTeam": { "$ref": "#/definitions/{name:string;id:string;scorers:string[];codename:string;players:{name:string;id:string;position:string;lastName:string;substitute:boolean;timeOnPitch:string;shirtNumber:string;events:{eventTime:string;eventType:string;}[];}[];possession:number;shotsOn:number;shotsOff:number;corners:number;fouls:number;colours:string;crest:string;score?:number;}" }, "awayTeam": { "$ref": "#/definitions/{name:string;id:string;scorers:string[];codename:string;players:{name:string;id:string;position:string;lastName:string;substitute:boolean;timeOnPitch:string;shirtNumber:string;events:{eventTime:string;eventType:string;}[];}[];possession:number;shotsOn:number;shotsOff:number;corners:number;fouls:number;colours:string;crest:string;score?:number;}" }, + "status": { + "type": "string" + }, "comments": { "type": "string" } @@ -970,7 +970,7 @@ "eventType" ] }, - "{stage:{stageNumber:string;};id:string;type:\"LiveMatch\";status:string;date:string;round:{roundNumber:string;name?:string;};leg:string;homeTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};awayTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};venue?:{name:string;id:string;};comments?:string;attendance?:string;referee?:{name:string;id:string;};}": { + "{stage:{stageNumber:string;};id:string;type:\"LiveMatch\";date:string;round:{roundNumber:string;name?:string;};leg:string;homeTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};awayTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};status:string;venue?:{name:string;id:string;};comments?:string;attendance?:string;referee?:{name:string;id:string;};}": { "type": "object", "properties": { "stage": { @@ -983,9 +983,6 @@ "type": "string", "const": "LiveMatch" }, - "status": { - "type": "string" - }, "date": { "type": "string" }, @@ -1001,6 +998,9 @@ "awayTeam": { "$ref": "#/definitions/{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;}" }, + "status": { + "type": "string" + }, "venue": { "$ref": "#/definitions/{name:string;id:string;}" }, @@ -1175,7 +1175,7 @@ "name" ] }, - "{stage:{stageNumber:string;};id:string;type:\"MatchDay\";result:boolean;date:string;round:{roundNumber:string;name?:string;};leg:string;homeTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};awayTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};liveMatch:boolean;previewAvailable:boolean;reportAvailable:boolean;lineupsAvailable:boolean;matchStatus:string;venue?:{name:string;id:string;};comments?:string;attendance?:string;referee?:{name:string;id:string;};competition?:{name:string;id:string;};}": { + "{stage:{stageNumber:string;};id:string;type:\"MatchDay\";date:string;round:{roundNumber:string;name?:string;};leg:string;homeTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};awayTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};liveMatch:boolean;result:boolean;previewAvailable:boolean;reportAvailable:boolean;lineupsAvailable:boolean;matchStatus:string;venue?:{name:string;id:string;};comments?:string;attendance?:string;referee?:{name:string;id:string;};competition?:{name:string;id:string;};}": { "type": "object", "properties": { "stage": { @@ -1188,9 +1188,6 @@ "type": "string", "const": "MatchDay" }, - "result": { - "type": "boolean" - }, "date": { "type": "string" }, @@ -1209,6 +1206,9 @@ "liveMatch": { "type": "boolean" }, + "result": { + "type": "boolean" + }, "previewAvailable": { "type": "boolean" }, diff --git a/dotcom-rendering/src/frontend/schemas/feFootballMatchListPage.json b/dotcom-rendering/src/frontend/schemas/feFootballMatchListPage.json index ce6ccc9fbb9..5da9710c094 100644 --- a/dotcom-rendering/src/frontend/schemas/feFootballMatchListPage.json +++ b/dotcom-rendering/src/frontend/schemas/feFootballMatchListPage.json @@ -96,13 +96,13 @@ "items": { "anyOf": [ { - "$ref": "#/definitions/{stage:{stageNumber:string;};id:string;type:\"LiveMatch\";status:string;date:string;round:{roundNumber:string;name?:string;};leg:string;homeTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};awayTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};venue?:{name:string;id:string;};comments?:string;attendance?:string;referee?:{name:string;id:string;};}" + "$ref": "#/definitions/{stage:{stageNumber:string;};id:string;type:\"LiveMatch\";date:string;round:{roundNumber:string;name?:string;};leg:string;homeTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};awayTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};status:string;venue?:{name:string;id:string;};comments?:string;attendance?:string;referee?:{name:string;id:string;};}" }, { "$ref": "#/definitions/{stage:{stageNumber:string;};id:string;type:\"Fixture\";date:string;round:{roundNumber:string;name?:string;};leg:string;homeTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};awayTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};venue?:{name:string;id:string;};comments?:string;competition?:{name:string;id:string;};}" }, { - "$ref": "#/definitions/{stage:{stageNumber:string;};id:string;type:\"MatchDay\";result:boolean;date:string;round:{roundNumber:string;name?:string;};leg:string;homeTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};awayTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};liveMatch:boolean;previewAvailable:boolean;reportAvailable:boolean;lineupsAvailable:boolean;matchStatus:string;venue?:{name:string;id:string;};comments?:string;attendance?:string;referee?:{name:string;id:string;};competition?:{name:string;id:string;};}" + "$ref": "#/definitions/{stage:{stageNumber:string;};id:string;type:\"MatchDay\";date:string;round:{roundNumber:string;name?:string;};leg:string;homeTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};awayTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};liveMatch:boolean;result:boolean;previewAvailable:boolean;reportAvailable:boolean;lineupsAvailable:boolean;matchStatus:string;venue?:{name:string;id:string;};comments?:string;attendance?:string;referee?:{name:string;id:string;};competition?:{name:string;id:string;};}" }, { "$ref": "#/definitions/{stage:{stageNumber:string;};id:string;type:\"Result\";date:string;round:{roundNumber:string;name?:string;};leg:string;homeTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};awayTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};reportAvailable:boolean;venue?:{name:string;id:string;};comments?:string;attendance?:string;referee?:{name:string;id:string;};}" @@ -704,7 +704,7 @@ "Record": { "type": "object" }, - "{stage:{stageNumber:string;};id:string;type:\"LiveMatch\";status:string;date:string;round:{roundNumber:string;name?:string;};leg:string;homeTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};awayTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};venue?:{name:string;id:string;};comments?:string;attendance?:string;referee?:{name:string;id:string;};}": { + "{stage:{stageNumber:string;};id:string;type:\"LiveMatch\";date:string;round:{roundNumber:string;name?:string;};leg:string;homeTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};awayTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};status:string;venue?:{name:string;id:string;};comments?:string;attendance?:string;referee?:{name:string;id:string;};}": { "type": "object", "properties": { "stage": { @@ -717,9 +717,6 @@ "type": "string", "const": "LiveMatch" }, - "status": { - "type": "string" - }, "date": { "type": "string" }, @@ -735,6 +732,9 @@ "awayTeam": { "$ref": "#/definitions/{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;}" }, + "status": { + "type": "string" + }, "venue": { "$ref": "#/definitions/{name:string;id:string;}" }, @@ -909,7 +909,7 @@ "name" ] }, - "{stage:{stageNumber:string;};id:string;type:\"MatchDay\";result:boolean;date:string;round:{roundNumber:string;name?:string;};leg:string;homeTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};awayTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};liveMatch:boolean;previewAvailable:boolean;reportAvailable:boolean;lineupsAvailable:boolean;matchStatus:string;venue?:{name:string;id:string;};comments?:string;attendance?:string;referee?:{name:string;id:string;};competition?:{name:string;id:string;};}": { + "{stage:{stageNumber:string;};id:string;type:\"MatchDay\";date:string;round:{roundNumber:string;name?:string;};leg:string;homeTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};awayTeam:{name:string;id:string;score?:number;htScore?:number;aggregateScore?:number;scorers?:string;teamUrl?:string;};liveMatch:boolean;result:boolean;previewAvailable:boolean;reportAvailable:boolean;lineupsAvailable:boolean;matchStatus:string;venue?:{name:string;id:string;};comments?:string;attendance?:string;referee?:{name:string;id:string;};competition?:{name:string;id:string;};}": { "type": "object", "properties": { "stage": { @@ -922,9 +922,6 @@ "type": "string", "const": "MatchDay" }, - "result": { - "type": "boolean" - }, "date": { "type": "string" }, @@ -943,6 +940,9 @@ "liveMatch": { "type": "boolean" }, + "result": { + "type": "boolean" + }, "previewAvailable": { "type": "boolean" },