Chart theming: custom named themes + Theme Builder

This commit is contained in:
2026-06-12 18:15:54 +03:00
parent 44a601affd
commit b193464f55
32 changed files with 2220 additions and 70 deletions
+91
View File
@@ -0,0 +1,91 @@
import { describe, expect, it } from 'vitest';
import {
CURRENT_THEME_VERSION,
THEME_FONT_OPTIONS,
applyFontToConfig,
createCustomTheme,
} from './custom-theme';
import { THEME_PREVIEW_SPECS } from './theme-preview-specs';
describe('createCustomTheme', () => {
it('stamps version, timestamps, and carries the config through', () => {
const now = new Date('2026-06-12T10:00:00Z');
const theme = createCustomTheme({ name: 'Brand', config: { font: 'Georgia' }, now });
expect(theme.version).toBe(CURRENT_THEME_VERSION);
expect(theme.name).toBe('Brand');
expect(theme.config).toEqual({ font: 'Georgia' });
expect(theme.created).toBe(now.toISOString());
expect(theme.modified).toBe(now.toISOString());
});
});
describe('applyFontToConfig', () => {
it('sets the top-level font on an empty config', () => {
expect(applyFontToConfig({}, 'Georgia, serif')).toEqual({ font: 'Georgia, serif' });
});
it('rewrites every explicit font slot at any depth', () => {
const config = {
font: 'Helvetica',
title: { font: 'Helvetica', subtitleFont: 'Helvetica', fontSize: 16 },
axis: { labelFont: 'Helvetica', titleFont: 'Helvetica', labelFontSize: 11 },
axisX: { labelFont: 'Helvetica' },
legend: { labelFont: 'Helvetica', titleFont: 'Helvetica' },
header: { labelFont: 'Helvetica', titleFont: 'Helvetica' },
text: { font: 'Helvetica' },
};
const out = applyFontToConfig(config, 'Georgia');
expect(out).toEqual({
font: 'Georgia',
title: { font: 'Georgia', subtitleFont: 'Georgia', fontSize: 16 },
axis: { labelFont: 'Georgia', titleFont: 'Georgia', labelFontSize: 11 },
axisX: { labelFont: 'Georgia' },
legend: { labelFont: 'Georgia', titleFont: 'Georgia' },
header: { labelFont: 'Georgia', titleFont: 'Georgia' },
text: { font: 'Georgia' },
});
});
it('leaves non-font properties (including fontSize/fontWeight) untouched', () => {
const config = {
background: '#fff',
title: { fontSize: 16, fontWeight: 600 },
range: { category: ['#111', '#222'] },
};
expect(applyFontToConfig(config, 'Georgia')).toEqual({ ...config, font: 'Georgia' });
});
it('does not mutate the input', () => {
const config = { title: { font: 'Helvetica' } };
applyFontToConfig(config, 'Georgia');
expect(config).toEqual({ title: { font: 'Helvetica' } });
});
});
describe('THEME_FONT_OPTIONS', () => {
it('offers distinct, non-empty CSS stacks', () => {
expect(THEME_FONT_OPTIONS.length).toBeGreaterThanOrEqual(4);
const values = THEME_FONT_OPTIONS.map((f) => f.value);
expect(new Set(values).size).toBe(values.length);
expect(values.every((v) => v.trim().length > 0)).toBe(true);
});
});
describe('THEME_PREVIEW_SPECS', () => {
it('every gallery card is a self-contained inline-data spec', () => {
expect(THEME_PREVIEW_SPECS.length).toBeGreaterThanOrEqual(6);
for (const card of THEME_PREVIEW_SPECS) {
expect(card.id).toBeTruthy();
expect(card.caption).toBeTruthy();
const data = card.spec.data as { values?: unknown[] };
expect(Array.isArray(data.values)).toBe(true);
expect(data.values!.length).toBeGreaterThan(0);
expect(card.spec.$schema).toContain('vega-lite');
}
});
it('card ids are unique', () => {
const ids = THEME_PREVIEW_SPECS.map((c) => c.id);
expect(new Set(ids).size).toBe(ids.length);
});
});