Skip to content

Commit

Permalink
Merge pull request #12879 from guardian/dark-mode-tables
Browse files Browse the repository at this point in the history
Dark mode for table block
  • Loading branch information
frederickobrien authored Dec 12, 2024
2 parents f4a35f7 + 587de7b commit 9895a0d
Show file tree
Hide file tree
Showing 3 changed files with 344 additions and 33 deletions.
215 changes: 215 additions & 0 deletions dotcom-rendering/src/components/TableBlockComponent.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
import type { Meta } from '@storybook/react';
import { TableBlockComponent } from './TableBlockComponent';

const tableHtml = `<table data-embed-type="table" class="table table--football table--striped">
<thead>
<tr>
<th class="football-stat--postition table-column--sub"><abbr title="Position">Pos</abbr></th>
<th class="football-stat--team table-column--main">Team</th>
<th class="football-stat--played"><abbr title="Played">P</abbr></th>
<th class="football-stat--difference"><abbr title="Goal difference">GD</abbr></th>
<th class="football-stat--points"><abbr title="Points">Pts</abbr></th>
</tr>
</thead>
<tbody>
<tr class="
">
<td class="football-stat--postition table-column--sub">1</td>
<td class="football-stat--team table-column--main">Liverpool</td>
<td class="football-stat--played">11</td>
<td class="football-stat--difference">15</td>
<td class="football-stat--points">28</td>
</tr>
<tr class="
">
<td class="football-stat--postition table-column--sub">2</td>
<td class="football-stat--team table-column--main">Man City</td>
<td class="football-stat--played">11</td>
<td class="football-stat--difference">9</td>
<td class="football-stat--points">23</td>
</tr>
<tr class="
">
<td class="football-stat--postition table-column--sub">3</td>
<td class="football-stat--team table-column--main">Nottm Forest</td>
<td class="football-stat--played">11</td>
<td class="football-stat--difference">5</td>
<td class="football-stat--points">19</td>
</tr>
<tr class="
">
<td class="football-stat--postition table-column--sub">4</td>
<td class="football-stat--team table-column--main">Brighton</td>
<td class="football-stat--played">11</td>
<td class="football-stat--difference">4</td>
<td class="football-stat--points">19</td>
</tr>
<tr class="
">
<td class="football-stat--postition table-column--sub">5</td>
<td class="football-stat--team table-column--main">Chelsea</td>
<td class="football-stat--played">10</td>
<td class="football-stat--difference">8</td>
<td class="football-stat--points">18</td>
</tr>
<tr class="
">
<td class="football-stat--postition table-column--sub">6</td>
<td class="football-stat--team table-column--main">Arsenal</td>
<td class="football-stat--played">10</td>
<td class="football-stat--difference">6</td>
<td class="football-stat--points">18</td>
</tr>
<tr class="
">
<td class="football-stat--postition table-column--sub">7</td>
<td class="football-stat--team table-column--main">Fulham</td>
<td class="football-stat--played">11</td>
<td class="football-stat--difference">3</td>
<td class="football-stat--points">18</td>
</tr>
<tr class="
">
<td class="football-stat--postition table-column--sub">8</td>
<td class="football-stat--team table-column--main">Newcastle</td>
<td class="football-stat--played">11</td>
<td class="football-stat--difference">2</td>
<td class="football-stat--points">18</td>
</tr>
<tr class="
">
<td class="football-stat--postition table-column--sub">9</td>
<td class="football-stat--team table-column--main">Aston Villa</td>
<td class="football-stat--played">11</td>
<td class="football-stat--difference">0</td>
<td class="football-stat--points">18</td>
</tr>
<tr class="
">
<td class="football-stat--postition table-column--sub">10</td>
<td class="football-stat--team table-column--main">Tottenham Hotspur</td>
<td class="football-stat--played">11</td>
<td class="football-stat--difference">10</td>
<td class="football-stat--points">16</td>
</tr>
<tr class="
">
<td class="football-stat--postition table-column--sub">11</td>
<td class="football-stat--team table-column--main">Brentford</td>
<td class="football-stat--played">11</td>
<td class="football-stat--difference">0</td>
<td class="football-stat--points">16</td>
</tr>
<tr class="
">
<td class="football-stat--postition table-column--sub">12</td>
<td class="football-stat--team table-column--main">AFC Bournemouth</td>
<td class="football-stat--played">11</td>
<td class="football-stat--difference">0</td>
<td class="football-stat--points">15</td>
</tr>
<tr class="
">
<td class="football-stat--postition table-column--sub">13</td>
<td class="football-stat--team table-column--main">Man Utd</td>
<td class="football-stat--played">11</td>
<td class="football-stat--difference">0</td>
<td class="football-stat--points">15</td>
</tr>
<tr class="
">
<td class="football-stat--postition table-column--sub">14</td>
<td class="football-stat--team table-column--main">West Ham</td>
<td class="football-stat--played">11</td>
<td class="football-stat--difference">-6</td>
<td class="football-stat--points">12</td>
</tr>
<tr class="
">
<td class="football-stat--postition table-column--sub">15</td>
<td class="football-stat--team table-column--main">Leicester</td>
<td class="football-stat--played">11</td>
<td class="football-stat--difference">-7</td>
<td class="football-stat--points">10</td>
</tr>
<tr class="
">
<td class="football-stat--postition table-column--sub">16</td>
<td class="football-stat--team table-column--main">Everton</td>
<td class="football-stat--played">11</td>
<td class="football-stat--difference">-7</td>
<td class="football-stat--points">10</td>
</tr>
<tr class="
">
<td class="football-stat--postition table-column--sub">17</td>
<td class="football-stat--team table-column--main">Ipswich</td>
<td class="football-stat--played">11</td>
<td class="football-stat--difference">-10</td>
<td class="football-stat--points">8</td>
</tr>
<tr class="
">
<td class="football-stat--postition table-column--sub">18</td>
<td class="football-stat--team table-column--main">Crystal Palace</td>
<td class="football-stat--played">11</td>
<td class="football-stat--difference">-7</td>
<td class="football-stat--points">7</td>
</tr>
<tr class="
">
<td class="football-stat--postition table-column--sub">19</td>
<td class="football-stat--team table-column--main">Wolverhampton</td>
<td class="football-stat--played">11</td>
<td class="football-stat--difference">-11</td>
<td class="football-stat--points">6</td>
</tr>
<tr class="
">
<td class="football-stat--postition table-column--sub">20</td>
<td class="football-stat--team table-column--main">Southampton</td>
<td class="football-stat--played">11</td>
<td class="football-stat--difference">-14</td>
<td class="football-stat--points">4</td>
</tr>
</tbody>
</table>`;

const meta = {
title: 'Components/TableBlockComponent',
component: TableBlockComponent,
render: (args) => <TableBlockComponent {...args} />,
args: {
element: {
isMandatory: true,
elementId: 'table',
_type: 'model.dotcomrendering.pageElements.TableBlockElement',
html: tableHtml,
},
},
} satisfies Meta<typeof TableBlockComponent>;

export default meta;

export const Default = {};
125 changes: 92 additions & 33 deletions dotcom-rendering/src/components/TableBlockComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,54 +1,113 @@
import { css } from '@emotion/react';
import { palette, text, textSans12 } from '@guardian/source/foundations';
import { unescapeData } from '../lib/escapeData';
import { textSans12 } from '@guardian/source/foundations';
import { isElement, parseHtml } from '../lib/domUtils';
import { palette } from '../palette';
import { logger } from '../server/lib/logging';
import type { TableBlockElement } from '../types/content';

const tableStyles = css`
width: 100%;
background: ${palette('--table-block-background')};
border-top: 0.0625rem solid ${palette('--table-block-border-top')};
color: ${palette('--table-block-text')};
border-collapse: inherit;
`;

const headStyles = css`
tr {
font-weight: 800;
text-align: left;
}
`;

const rowStyles = css`
${textSans12};
:nth-child(odd) > td {
background-color: ${palette('--table-block-stripe')};
}
`;

const cellPadding = css`
padding: 0.5rem;
`;

const tableEmbed = css`
.table--football {
width: 100%;
background: ${palette.neutral[97]};
border-top: 0.0625rem solid ${palette.focus[400]};
color: ${palette.neutral[7]};
border-collapse: inherit;
tr:nth-child(odd) > td {
background-color: ${palette.neutral[93]};
tr > th:first-child,
td:first-child {
color: ${palette('--table-block-text-first-column')};
}
`;

type Props = {
element: TableBlockElement;
};

const buildElementTree = (node: Node) => {
const children = Array.from(node.childNodes).map(buildElementTree);
switch (node.nodeName) {
case 'TABLE': {
return (
<table className="table--football" css={tableStyles}>
{children}
</table>
);
}
th {
padding: 0.5rem;
case 'THEAD': {
return <thead css={headStyles}>{children}</thead>;
}
td {
padding: 0.5rem;
case 'TBODY': {
return <tbody>{children}</tbody>;
}
tr {
${textSans12};
case 'ABBR': {
return (
<abbr title={(node as HTMLElement).getAttribute('title') ?? ''}>
{children}
</abbr>
);
}
thead {
tr {
font-weight: 800;
text-align: left;
}
case 'TR': {
return <tr css={rowStyles}>{children}</tr>;
}
tr > th:first-child,
td:first-child {
color: ${text.supporting};
case 'TH': {
return <th css={cellPadding}>{children}</th>;
}
.table-column--main {
width: 100%;
case 'TD': {
const isMainColumn = (node as HTMLElement).className.includes(
'table-column--main',
);
return (
<td
style={isMainColumn ? { width: '100%' } : undefined}
css={cellPadding}
>
{children}
</td>
);
}
case '#text': {
return node.textContent;
}
default:
logger.warn('TableBlockComponent: Unknown element received', {
isDev: process.env.NODE_ENV !== 'production',
element: {
name: node.nodeName,
html: isElement(node) ? node.outerHTML : undefined,
},
});
return null;
}
margin-bottom: 16px;
`;

type Props = {
element: TableBlockElement;
};

export const TableBlockComponent = ({ element }: Props) => {
const fragment = parseHtml(element.html);
return (
<div
css={tableEmbed}
style={{ marginBottom: '16px' }}
data-testid="football-table-embed"
dangerouslySetInnerHTML={{ __html: unescapeData(element.html) }}
/>
>
{Array.from(fragment.childNodes).map(buildElementTree)}
</div>
);
};
Loading

0 comments on commit 9895a0d

Please sign in to comment.