Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions dotcom-rendering/src/components/FootballMatchInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type {
FootballMatchStats,
FootballMatchTeamWithStats,
} from '../footballMatchStats';
import type { FootballTable as FootballTableData } from '../footballTables';
import type { FootballTableSummary } from '../footballTables';
import {
FootballMatchGoalAttempts,
FootballMatchStat,
Expand All @@ -13,7 +13,7 @@ import { Lineups } from './Lineups';

type Props = {
match: FootballMatchStats;
table?: FootballTableData;
table?: FootballTableSummary;
};

const layoutCss = css`
Expand Down
11 changes: 4 additions & 7 deletions dotcom-rendering/src/components/LeagueTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,11 @@ import {
textSansBold14,
textSansBold15,
} from '@guardian/source/foundations';
import type {
Entry,
FootballTable as FootballTableData,
} from '../footballTables';
import type { EntrySummary, FootballTableSummary } from '../footballTables';
import { palette } from '../palette';

type Props = {
table: FootballTableData;
table: FootballTableSummary;
};

export const LeagueTable = ({ table }: Props) => {
Expand Down Expand Up @@ -43,7 +40,7 @@ const Title = ({ text }: { text: string }) => (
</h3>
);

const Table = ({ table }: { table: FootballTableData }) => {
const Table = ({ table }: { table: FootballTableSummary }) => {
return (
<table
css={css`
Expand Down Expand Up @@ -78,7 +75,7 @@ const Table = ({ table }: { table: FootballTableData }) => {
);
};

const TableRow = ({ entry }: { entry: Entry }) => {
const TableRow = ({ entry }: { entry: EntrySummary }) => {
return (
<tr css={[gridContainer, tableRowStyles]}>
<th scope="row" css={[between('pos', 'team'), position]}>
Expand Down
48 changes: 42 additions & 6 deletions dotcom-rendering/src/footballTables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { listParse } from './footballMatches';
import type {
FEFootballTable,
FEGroup,
FEGroupSummary,
FELeagueTableEntry,
FELeagueTableEntrySummary,
FETeamResult,
} from './frontend/feFootballTablesPage';
import { error, ok, type Result } from './lib/result';
Expand All @@ -26,7 +28,7 @@ type Team = {
url?: string;
};

export type Entry = {
export type EntrySummary = {
position: number;
team: Team;
gamesPlayed: number;
Expand All @@ -37,6 +39,9 @@ export type Entry = {
goalsAgainst: number;
goalDifference: number;
points: number;
};

type Entry = EntrySummary & {
results: TeamResult[];
};

Expand All @@ -53,6 +58,11 @@ export type FootballTable = {
entries: Entry[];
};

export type FootballTableSummary = {
groupName?: string;
entries: EntrySummary[];
};

export type FootballTableCompetitions = FootballTableCompetition[];

type MissingScore = {
Expand All @@ -62,12 +72,22 @@ type MissingScore = {

type ParserError = MissingScore;

const parseTable = (feGroup: FEGroup): Result<ParserError, FootballTable> =>
export const parseTable = (
feGroup: FEGroup,
): Result<ParserError, FootballTable> =>
parseEntries(feGroup.entries).map((entries) => ({
groupName: feGroup.round.name,
entries: entries.sort((a, b) => a.position - b.position),
}));

export const parseTableSummary = (
feGroup: FEGroupSummary,
): Result<ParserError, FootballTableSummary> =>
parseEntriesSummaries(feGroup.entries).map((entries) => ({
groupName: feGroup.round.name,
entries: entries.sort((a, b) => a.position - b.position),
}));

const parseTables = listParse(parseTable);

const parseResult = (result: FETeamResult): Result<ParserError, TeamResult> => {
Expand Down Expand Up @@ -102,12 +122,12 @@ const parseResult = (result: FETeamResult): Result<ParserError, TeamResult> => {

const parseResults = listParse(parseResult);

const parseEntry = (
feEntry: FELeagueTableEntry,
): Result<ParserError, Entry> => {
const mapBaseEntryFields = (
feEntry: FELeagueTableEntrySummary,
): EntrySummary => {
const { team, teamUrl } = feEntry;

return parseResults(feEntry.results).map((results) => ({
return {
position: team.rank,
team: {
name: cleanTeamName(team.name),
Expand All @@ -122,12 +142,28 @@ const parseEntry = (
goalsAgainst: team.total.goalsAgainst,
goalDifference: team.goalDifference,
points: team.points,
};
};

const parseEntry = (
feEntry: FELeagueTableEntry,
): Result<ParserError, Entry> => {
return parseResults(feEntry.results).map((results) => ({
...mapBaseEntryFields(feEntry),
results,
}));
};

const parseEntries = listParse(parseEntry);

const parseEntrySummary = (
feEntry: FELeagueTableEntrySummary,
): Result<ParserError, EntrySummary> => {
return ok(mapBaseEntryFields(feEntry));
};

const parseEntriesSummaries = listParse(parseEntrySummary);

const parseFootballTableCompetition = (
table: FEFootballTable,
): Result<ParserError, FootballTableCompetition> =>
Expand Down
2 changes: 2 additions & 0 deletions dotcom-rendering/src/frontend/feFootballMatchPage.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { FEFootballDataPage } from './feFootballDataPage';
import { type FEGroupSummary } from './feFootballTablesPage';

export type FEFootballPlayerEvent = {
eventTime: string;
Expand Down Expand Up @@ -42,4 +43,5 @@ export type FEFootballMatch = {

export type FEFootballMatchPage = FEFootballDataPage & {
footballMatch: FEFootballMatch;
group?: FEGroupSummary;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there occasions when we don't have a group for a particular match?

Copy link
Contributor Author

@marjisound marjisound Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question, I think we expect to have league table for all our competitions, but in our data, we are considering the possibility of the league table to be empty and use hasLeagueTable to indicate if the competition has a league table.

Now I'm thinking what should we do in a case like the match summary (match info) page? Do you think it might make more sense to make the group a mandatory field?

For now I merge this, but lets talk about again. I created this issue so we won't forget this #15204

};
10 changes: 9 additions & 1 deletion dotcom-rendering/src/frontend/feFootballTablesPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,14 @@ export type FERecentResultsPerTeam = {
results: FETeamResult[];
};

export type FELeagueTableEntry = {
export type FELeagueTableEntrySummary = {
stageNumber: string;
round: FERound;
team: FELeagueTeam;
teamUrl?: string;
};

export type FELeagueTableEntry = FELeagueTableEntrySummary & {
results: FETeamResult[];
};

Expand All @@ -55,6 +58,11 @@ export type FEGroup = {
entries: FELeagueTableEntry[];
};

export type FEGroupSummary = {
round: FERound;
entries: FELeagueTableEntrySummary[];
};

export type FEFootballTable = {
competition: FECompetitionSummary;
groups: FEGroup[];
Expand Down
179 changes: 179 additions & 0 deletions dotcom-rendering/src/frontend/schemas/feFootballMatchPage.json
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,185 @@
"id",
"status"
]
},
"group": {
"type": "object",
"properties": {
"round": {
"type": "object",
"properties": {
"roundNumber": {
"type": "string"
},
"name": {
"type": "string"
}
},
"required": [
"roundNumber"
]
},
"entries": {
"type": "array",
"items": {
"type": "object",
"properties": {
"stageNumber": {
"type": "string"
},
"round": {
"type": "object",
"properties": {
"roundNumber": {
"type": "string"
},
"name": {
"type": "string"
}
},
"required": [
"roundNumber"
]
},
"team": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"rank": {
"type": "number"
},
"total": {
"type": "object",
"properties": {
"played": {
"type": "number"
},
"won": {
"type": "number"
},
"drawn": {
"type": "number"
},
"lost": {
"type": "number"
},
"goalsFor": {
"type": "number"
},
"goalsAgainst": {
"type": "number"
}
},
"required": [
"drawn",
"goalsAgainst",
"goalsFor",
"lost",
"played",
"won"
]
},
"home": {
"type": "object",
"properties": {
"played": {
"type": "number"
},
"won": {
"type": "number"
},
"drawn": {
"type": "number"
},
"lost": {
"type": "number"
},
"goalsFor": {
"type": "number"
},
"goalsAgainst": {
"type": "number"
}
},
"required": [
"drawn",
"goalsAgainst",
"goalsFor",
"lost",
"played",
"won"
]
},
"away": {
"type": "object",
"properties": {
"played": {
"type": "number"
},
"won": {
"type": "number"
},
"drawn": {
"type": "number"
},
"lost": {
"type": "number"
},
"goalsFor": {
"type": "number"
},
"goalsAgainst": {
"type": "number"
}
},
"required": [
"drawn",
"goalsAgainst",
"goalsFor",
"lost",
"played",
"won"
]
},
"goalDifference": {
"type": "number"
},
"points": {
"type": "number"
}
},
"required": [
"away",
"goalDifference",
"home",
"id",
"name",
"points",
"rank",
"total"
]
},
"teamUrl": {
"type": "string"
}
},
"required": [
"round",
"stageNumber",
"team"
]
}
}
},
"required": [
"entries",
"round"
]
}
},
"required": [
Expand Down
Loading
Loading