Add tests to help with maintenance of theme color tokens

pull/10525/head
Thibaud Colas 2023-05-25 13:45:29 +01:00
rodzic fb793f2b4b
commit 40335ba9d1
4 zmienionych plików z 211 dodań i 33 usunięć

Wyświetl plik

@ -1,11 +1,15 @@
const colors = require('./colors');
const { generateColorVariables } = require('./colorVariables');
const { staticColors, transparencies } = require('./colors');
const colorThemes = require('./colorThemes');
const {
generateColorVariables,
generateThemeColorVariables,
} = require('./colorVariables');
describe('generateColorVariables', () => {
it('generates all variables', () => {
const colorVariables = generateColorVariables(colors);
const colorVariables = generateColorVariables(staticColors);
const generatedVariables = Object.keys(colorVariables);
Object.values(colors).forEach((hues) => {
Object.values(staticColors).forEach((hues) => {
Object.values(hues).forEach((shade) => {
expect(generatedVariables).toContain(shade.cssVariable);
});
@ -20,7 +24,7 @@ describe('generateColorVariables', () => {
* - Leave the copied content exactly as-is when pasting, to avoid any Markdown formatting issues.
*/
it('is stable (update custom_user_interface_colours documentation when this changes)', () => {
const colorVariables = generateColorVariables(colors);
const colorVariables = generateColorVariables(staticColors);
expect(colorVariables).toMatchInlineSnapshot(`
Object {
"--w-color-black": "hsl(var(--w-color-black-hue) var(--w-color-black-saturation) var(--w-color-black-lightness))",
@ -131,3 +135,125 @@ describe('generateColorVariables', () => {
`);
});
});
describe('transparencies', () => {
it('is stable (update custom_user_interface_colours documentation when this changes)', () => {
expect(transparencies).toMatchInlineSnapshot(`
Object {
"--w-color-black-10": "rgba(0, 0, 0, 0.10)",
"--w-color-black-20": "rgba(0, 0, 0, 0.20)",
"--w-color-black-25": "rgba(0, 0, 0, 0.25)",
"--w-color-black-35": "rgba(0, 0, 0, 0.35)",
"--w-color-black-5": "rgba(0, 0, 0, 0.05)",
"--w-color-black-50": "rgba(0, 0, 0, 0.50)",
"--w-color-white-10": "rgba(255, 255, 255, 0.10)",
"--w-color-white-15": "rgba(255, 255, 255, 0.15)",
"--w-color-white-50": "rgba(255, 255, 255, 0.50)",
"--w-color-white-80": "rgba(255, 255, 255, 0.80)",
}
`);
});
});
describe('generateThemeColorVariables', () => {
it('uses the same variables in both themes', () => {
const light = Object.keys(generateThemeColorVariables(colorThemes.light));
const dark = Object.keys(generateThemeColorVariables(colorThemes.dark));
expect(light).toEqual(dark);
});
it('uses color variables for all values (except focus)', () => {
const values = [
...Object.values(generateThemeColorVariables(colorThemes.light)),
...Object.values(generateThemeColorVariables(colorThemes.dark)),
];
expect(values.filter((val) => !val.startsWith('var('))).toEqual([
'#00A885',
'#00A885',
]);
});
it('light theme is stable (update custom_user_interface_colours documentation when this changes)', () => {
expect(generateThemeColorVariables(colorThemes.light))
.toMatchInlineSnapshot(`
Object {
"--w-color-border-button-outline-default": "var(--w-color-secondary)",
"--w-color-border-button-small-outline-default": "var(--w-color-grey-150)",
"--w-color-border-field-default": "var(--w-color-grey-150)",
"--w-color-border-field-hover": "var(--w-color-grey-200)",
"--w-color-border-field-inactive": "var(--w-color-grey-150)",
"--w-color-border-furniture": "var(--w-color-grey-100)",
"--w-color-focus": "#00A885",
"--w-color-icon-primary": "var(--w-color-primary)",
"--w-color-icon-primary-hover": "var(--w-color-primary-200)",
"--w-color-icon-secondary": "var(--w-color-grey-400)",
"--w-color-icon-secondary-hover": "var(--w-color-primary-200)",
"--w-color-surface-button-default": "var(--w-color-secondary)",
"--w-color-surface-button-hover": "var(--w-color-secondary-400)",
"--w-color-surface-button-inactive": "var(--w-color-grey-400)",
"--w-color-surface-button-outline-hover": "var(--w-color-secondary-50)",
"--w-color-surface-field": "var(--w-color-white)",
"--w-color-surface-field-inactive": "var(--w-color-grey-50)",
"--w-color-surface-header": "var(--w-color-grey-50)",
"--w-color-surface-menu-item-active": "var(--w-color-primary-200)",
"--w-color-surface-menus": "var(--w-color-primary)",
"--w-color-surface-page": "var(--w-color-white)",
"--w-color-surface-tooltip": "var(--w-color-primary-200)",
"--w-color-text-button": "var(--w-color-white)",
"--w-color-text-button-outline-default": "var(--w-color-secondary)",
"--w-color-text-context": "var(--w-color-grey-600)",
"--w-color-text-error": "var(--w-color-critical-200)",
"--w-color-text-highlight": "var(--w-color-secondary-75)",
"--w-color-text-label": "var(--w-color-primary)",
"--w-color-text-label-menus-active": "var(--w-color-white)",
"--w-color-text-label-menus-default": "var(--w-color-white-80)",
"--w-color-text-link-default": "var(--w-color-secondary)",
"--w-color-text-link-hover": "var(--w-color-secondary-400)",
"--w-color-text-meta": "var(--w-color-grey-400)",
"--w-color-text-placeholder": "var(--w-color-grey-400)",
}
`);
});
it('dark theme is stable (update custom_user_interface_colours documentation when this changes)', () => {
expect(generateThemeColorVariables(colorThemes.dark))
.toMatchInlineSnapshot(`
Object {
"--w-color-border-button-outline-default": "var(--w-color-secondary-100)",
"--w-color-border-button-small-outline-default": "var(--w-color-grey-400)",
"--w-color-border-field-default": "var(--w-color-grey-400)",
"--w-color-border-field-hover": "var(--w-color-grey-200)",
"--w-color-border-field-inactive": "var(--w-color-grey-500)",
"--w-color-border-furniture": "var(--w-color-grey-500)",
"--w-color-focus": "#00A885",
"--w-color-icon-primary": "var(--w-color-grey-150)",
"--w-color-icon-primary-hover": "var(--w-color-grey-50)",
"--w-color-icon-secondary": "var(--w-color-grey-150)",
"--w-color-icon-secondary-hover": "var(--w-color-grey-50)",
"--w-color-surface-button-default": "var(--w-color-secondary)",
"--w-color-surface-button-hover": "var(--w-color-secondary-400)",
"--w-color-surface-button-inactive": "var(--w-color-grey-400)",
"--w-color-surface-button-outline-hover": "var(--w-color-grey-500)",
"--w-color-surface-field": "var(--w-color-grey-600)",
"--w-color-surface-field-inactive": "var(--w-color-grey-500)",
"--w-color-surface-header": "var(--w-color-grey-600)",
"--w-color-surface-menu-item-active": "var(--w-color-grey-600)",
"--w-color-surface-menus": "var(--w-color-grey-500)",
"--w-color-surface-page": "var(--w-color-grey-600)",
"--w-color-surface-tooltip": "var(--w-color-grey-500)",
"--w-color-text-button": "var(--w-color-white)",
"--w-color-text-button-outline-default": "var(--w-color-secondary-100)",
"--w-color-text-context": "var(--w-color-grey-50)",
"--w-color-text-error": "var(--w-color-critical-100)",
"--w-color-text-highlight": "var(--w-color-secondary-400)",
"--w-color-text-label": "var(--w-color-grey-150)",
"--w-color-text-label-menus-active": "var(--w-color-white)",
"--w-color-text-label-menus-default": "var(--w-color-white-80)",
"--w-color-text-link-default": "var(--w-color-secondary-100)",
"--w-color-text-link-hover": "var(--w-color-secondary-75)",
"--w-color-text-meta": "var(--w-color-grey-150)",
"--w-color-text-placeholder": "var(--w-color-grey-200)",
}
`);
});
});

Wyświetl plik

@ -17,7 +17,7 @@
}} Colors */
/** @type {Colors} */
const colors = {
const staticColors = {
black: {
DEFAULT: {
hex: '#000000',
@ -273,4 +273,20 @@ const colors = {
},
};
module.exports = colors;
const transparencies = {
'--w-color-white-10': 'rgba(255, 255, 255, 0.10)',
'--w-color-white-15': 'rgba(255, 255, 255, 0.15)',
'--w-color-white-50': 'rgba(255, 255, 255, 0.50)',
'--w-color-white-80': 'rgba(255, 255, 255, 0.80)',
'--w-color-black-5': 'rgba(0, 0, 0, 0.05)',
'--w-color-black-10': 'rgba(0, 0, 0, 0.10)',
'--w-color-black-20': 'rgba(0, 0, 0, 0.20)',
'--w-color-black-25': 'rgba(0, 0, 0, 0.25)',
'--w-color-black-35': 'rgba(0, 0, 0, 0.35)',
'--w-color-black-50': 'rgba(0, 0, 0, 0.50)',
};
module.exports = {
staticColors,
transparencies,
};

Wyświetl plik

@ -1,7 +1,10 @@
import React from 'react';
import colors, { Hues, Shade } from './colors';
import colorThemes, { Token, ThemeCategory } from './colorThemes';
import { generateColorVariables } from './colorVariables';
import React, { Fragment } from 'react';
import { staticColors, Hues, Shade } from './colors';
import colorThemes, { ThemeCategory } from './colorThemes';
import {
generateColorVariables,
generateThemeColorVariables,
} from './colorVariables';
const description = `
Wagtails typographic styles are made available as separate design tokens, but in most scenarios its better to use one of the predefined text styles.
@ -21,7 +24,7 @@ const getContrastGridLink = () => {
'?version=1.1.0&es-color-form__tile-size=compact&es-color-form__show-contrast=aaa&es-color-form__show-contrast=aa&es-color-form__show-contrast=aa18';
const bg: string[] = [];
const fg: string[] = [];
Object.values(colors).forEach((hues: Hues) => {
Object.values(staticColors).forEach((hues: Hues) => {
Object.values(hues).forEach((shade: Shade) => {
const color = `${shade.hex}, ${shade.textUtility.replace('w-text-', '')}`;
bg.push(color);
@ -77,7 +80,7 @@ export const ColorPalette = () => (
color palette, with contrasting text chosen for readability of this
example only.
</p>
{Object.entries(colors).map(([color, hues]) => (
{Object.entries(staticColors).map(([color, hues]) => (
<div key={color}>
<h2 className="w-sr-only">{color}</h2>
<Palette color={color} hues={hues} />
@ -130,17 +133,25 @@ export const ColorThemes = () => (
</>
);
const variablesMap = Object.entries(generateColorVariables(colors))
const rootVariablesMap = [
...Object.entries(generateColorVariables(staticColors)),
...Object.entries(generateThemeColorVariables(colorThemes.light)),
]
.map(([cssVar, val]) => `${cssVar}: ${val};`)
.join('');
const secondaryHSL = colors.secondary.DEFAULT.hsl.match(
const darkVariablesMap = Object.entries(
generateThemeColorVariables(colorThemes.dark),
)
.map(([cssVar, val]) => `${cssVar}: ${val};`)
.join('');
const secondaryHSL = staticColors.secondary.DEFAULT.hsl.match(
/\d+(\.\d+)?/g,
) as string[];
// Make sure this contains no empty lines, otherwise Sphinx docs will treat this as paragraphs.
const liveEditorCustomisations = `:root {
--w-color-primary: ${colors.primary.DEFAULT.hex};
--w-color-primary: ${staticColors.primary.DEFAULT.hex};
/* Any valid CSS format is supported. */
--w-color-primary-200: ${colors.primary[200].hsl};
--w-color-primary-200: ${staticColors.primary[200].hsl};
/* Set each HSL component separately to change all hues at once. */
--w-color-secondary-hue: ${secondaryHSL[0]};
--w-color-secondary-saturation: ${secondaryHSL[1]}%;
@ -148,13 +159,15 @@ const liveEditorCustomisations = `:root {
}`;
// Story using inline styles only so it can be copy-pasted into the Wagtail documentation for color customisations.
const demoStyles = `
:root {${variablesMap}}
:root {${rootVariablesMap}}
.w-theme-dark {${darkVariablesMap}}
.wagtail-color-swatch {
border-collapse: separate;
border-spacing: 4px;
}
.wagtail-color-swatch td:first-child {
.wagtail-color-swatch td:first-child,
.wagtail-color-swatch .w-theme-dark {
height: 1.5rem;
width: 1.5rem;
border: 1px solid #333;
@ -190,6 +203,7 @@ const colorCustomisationsDemo = (
{liveEditorCustomisations}
</style>
</pre>
<h3>Static colours</h3>
<table className="wagtail-color-swatch">
<thead>
<tr>
@ -199,7 +213,7 @@ const colorCustomisationsDemo = (
</tr>
</thead>
<tbody>
{Object.values(colors).map((hues) =>
{Object.values(staticColors).map((hues) =>
Object.entries(hues)
// Show DEFAULT shades first, then in numerical order.
.sort(([nameA], [nameB]) =>
@ -217,6 +231,37 @@ const colorCustomisationsDemo = (
)}
</tbody>
</table>
<h3>Light & dark theme colours</h3>
<table className="wagtail-color-swatch">
<thead>
<tr>
<th>Light</th>
<th>Dark</th>
<th>Variable</th>
</tr>
</thead>
{colorThemes.light.map((category) => (
<tbody key={category.label}>
<tr>
<th scope="rowgroup" colSpan={3}>
{category.label}
</th>
</tr>
{Object.values(category.tokens).map((token) => (
<tr key={token.cssVariable}>
<td style={{ backgroundColor: `var(${token.cssVariable})` }} />
<td
className="w-theme-dark"
style={{ backgroundColor: `var(${token.cssVariable})` }}
/>
<td>
<code>{token.cssVariable}</code>
</td>
</tr>
))}
</tbody>
))}
</table>
</section>
);

Wyświetl plik

@ -3,7 +3,7 @@ const vanillaRTL = require('tailwindcss-vanilla-rtl');
/**
* Design Tokens
*/
const colors = require('./src/tokens/colors');
const { staticColors, transparencies } = require('./src/tokens/colors');
const {
generateColorVariables,
generateThemeColorVariables,
@ -36,7 +36,7 @@ const scrollbarThin = require('./src/plugins/scrollbarThin');
* themeColors: For converting our design tokens into a format that tailwind accepts
*/
const themeColors = Object.fromEntries(
Object.entries(colors).map(([key, hues]) => {
Object.entries(staticColors).map(([key, hues]) => {
const shades = Object.fromEntries(
Object.entries(hues).map(([k, shade]) => [
k,
@ -165,17 +165,8 @@ module.exports = {
':root, :host': {
'--w-font-sans': fontFamily.sans.join(', '),
'--w-font-mono': fontFamily.mono.join(', '),
'--w-color-white-10': 'rgba(255, 255, 255, 0.10)',
'--w-color-white-15': 'rgba(255, 255, 255, 0.15)',
'--w-color-white-50': 'rgba(255, 255, 255, 0.50)',
'--w-color-white-80': 'rgba(255, 255, 255, 0.80)',
'--w-color-black-5': 'rgba(0, 0, 0, 0.05)',
'--w-color-black-10': 'rgba(0, 0, 0, 0.10)',
'--w-color-black-20': 'rgba(0, 0, 0, 0.20)',
'--w-color-black-25': 'rgba(0, 0, 0, 0.25)',
'--w-color-black-35': 'rgba(0, 0, 0, 0.35)',
'--w-color-black-50': 'rgba(0, 0, 0, 0.50)',
...generateColorVariables(colors),
...transparencies,
...generateColorVariables(staticColors),
...generateThemeColorVariables(colorThemes.light),
},
'.w-theme-system': {