feat: preset stili contenuto con whitelist
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
export const STYLE_GROUPS = {
|
||||
size: ['s', 'm', 'l'],
|
||||
color: ['accent', 'accent-dark', 'dark', 'heading', 'text', 'text-light', 'white'],
|
||||
weight: ['normal', 'bold'],
|
||||
style: ['normal', 'italic'],
|
||||
align: ['left', 'center', 'right'],
|
||||
} as const;
|
||||
|
||||
export type StyleGroup = keyof typeof STYLE_GROUPS;
|
||||
|
||||
export function validateStyles(input: unknown):
|
||||
| { ok: true; styles: Record<string, string> }
|
||||
| { ok: false; error: string } {
|
||||
if (typeof input !== 'object' || input === null || Array.isArray(input)) {
|
||||
return { ok: false, error: 'Stili non validi.' };
|
||||
}
|
||||
const out: Record<string, string> = {};
|
||||
for (const [k, v] of Object.entries(input)) {
|
||||
const allowed = (STYLE_GROUPS as Record<string, readonly string[]>)[k];
|
||||
if (!allowed) return { ok: false, error: `Gruppo stile sconosciuto: ${k}` };
|
||||
if (typeof v !== 'string' || !allowed.includes(v)) {
|
||||
return { ok: false, error: `Valore non ammesso per ${k}: ${String(v)}` };
|
||||
}
|
||||
out[k] = v;
|
||||
}
|
||||
return { ok: true, styles: out };
|
||||
}
|
||||
|
||||
export function stylesToClasses(styles: Record<string, string>): string {
|
||||
return Object.entries(styles).map(([k, v]) => `cs-${k}-${v}`).join(' ');
|
||||
}
|
||||
@@ -68,3 +68,22 @@ html.js [data-reveal].is-inview, html.js .section-heading.is-inview { opacity: 1
|
||||
html.js [data-reveal], html.js .section-heading { opacity: 1; transform: none; transition: none; }
|
||||
*, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
|
||||
}
|
||||
|
||||
/* === Stili contenuto (preset pannello /admin/content) === */
|
||||
.cs-size-s { font-size: .85em; }
|
||||
.cs-size-m { font-size: 1em; }
|
||||
.cs-size-l { font-size: 1.25em; }
|
||||
.cs-color-accent { color: var(--c-accent); }
|
||||
.cs-color-accent-dark { color: var(--c-accent-dark); }
|
||||
.cs-color-dark { color: var(--c-dark); }
|
||||
.cs-color-heading { color: var(--c-heading); }
|
||||
.cs-color-text { color: var(--c-text); }
|
||||
.cs-color-text-light { color: var(--c-text-light); }
|
||||
.cs-color-white { color: #fff; }
|
||||
.cs-weight-normal { font-weight: 400; }
|
||||
.cs-weight-bold { font-weight: 700; }
|
||||
.cs-style-normal { font-style: normal; }
|
||||
.cs-style-italic { font-style: italic; }
|
||||
.cs-align-left { text-align: left; }
|
||||
.cs-align-center { text-align: center; }
|
||||
.cs-align-right { text-align: right; }
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { validateStyles, stylesToClasses, STYLE_GROUPS } from '../src/lib/style-presets';
|
||||
|
||||
describe('style presets', () => {
|
||||
it('accetta chiavi e valori in whitelist', () => {
|
||||
const r = validateStyles({ size: 'l', color: 'accent', align: 'center' });
|
||||
expect(r).toEqual({ ok: true, styles: { size: 'l', color: 'accent', align: 'center' } });
|
||||
});
|
||||
|
||||
it('rifiuta gruppo sconosciuto e valore fuori lista', () => {
|
||||
expect(validateStyles({ font: 'comic' }).ok).toBe(false);
|
||||
expect(validateStyles({ size: 'xxl' }).ok).toBe(false);
|
||||
expect(validateStyles('non-oggetto').ok).toBe(false);
|
||||
});
|
||||
|
||||
it('mappa stili in classi cs-*', () => {
|
||||
expect(stylesToClasses({ size: 's', weight: 'bold' })).toBe('cs-size-s cs-weight-bold');
|
||||
expect(stylesToClasses({})).toBe('');
|
||||
});
|
||||
|
||||
it('espone i gruppi attesi', () => {
|
||||
expect(Object.keys(STYLE_GROUPS)).toEqual(['size', 'color', 'weight', 'style', 'align']);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user