From c485816135a3c0810599ff0ef185ca25f503c0a3 Mon Sep 17 00:00:00 2001 From: Keith Solomon Date: Wed, 5 Aug 2026 16:05:13 -0500 Subject: [PATCH] Add presets.js as single source of truth for filter definitions --- src/presets.js | 74 ++++++++++++++++++++++++++++++++++++++ tests/jest/presets.test.js | 45 +++++++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 src/presets.js create mode 100644 tests/jest/presets.test.js diff --git a/src/presets.js b/src/presets.js new file mode 100644 index 0000000..87b88ec --- /dev/null +++ b/src/presets.js @@ -0,0 +1,74 @@ +/** + * Single source of truth for filter presets. + * + * The CSS rules in src/styles/style.scss MUST match this list one-to-one. + * Adding a preset = add an entry here + add a matching .has-filter- + * rule in style.scss. Do not duplicate filter values elsewhere. + * + * @package ImageFilters + */ + +export const PRESETS = [ + { + slug: 'normal', + label: 'Normal', + color: '#f0f0f0', + cssFilter: 'none', + }, + { + slug: 'warm', + label: 'Warm', + color: '#f4a261', + cssFilter: 'saturate(1.25) sepia(0.18) brightness(1.05) contrast(1.05)', + }, + { + slug: 'cool', + label: 'Cool', + color: '#a8dadc', + cssFilter: 'saturate(0.95) hue-rotate(-10deg) brightness(1.02) contrast(1.05)', + }, + { + slug: 'vivid', + label: 'Vivid', + color: '#e63946', + cssFilter: 'saturate(1.6) contrast(1.15) brightness(1.03)', + }, + { + slug: 'fade', + label: 'Fade', + color: '#cdb4db', + cssFilter: 'saturate(0.85) contrast(0.9) brightness(1.08) sepia(0.08)', + }, + { + slug: 'mono', + label: 'Mono', + color: '#6c757d', + cssFilter: 'grayscale(1) contrast(1.05)', + }, + { + slug: 'dramatic', + label: 'Dramatic', + color: '#1d3557', + cssFilter: 'contrast(1.35) saturate(1.15) brightness(0.92)', + }, + { + slug: 'sepia', + label: 'Sepia', + color: '#d4a373', + cssFilter: 'sepia(0.85) saturate(1.1) contrast(1.05)', + }, +]; + +export const DEFAULT_PRESET = 'normal'; + +const VALID_SLUGS = new Set( PRESETS.map( ( p ) => p.slug ) ); + +/** + * Returns true if the given slug is a known preset. + * + * @param {string} slug + * @return {boolean} + */ +export function isValidPresetSlug( slug ) { + return VALID_SLUGS.has( slug ); +} diff --git a/tests/jest/presets.test.js b/tests/jest/presets.test.js new file mode 100644 index 0000000..43873b4 --- /dev/null +++ b/tests/jest/presets.test.js @@ -0,0 +1,45 @@ +import { PRESETS, DEFAULT_PRESET, isValidPresetSlug } from '../../src/presets'; + +describe( 'presets', () => { + test( 'exports 8 presets', () => { + expect( PRESETS ).toHaveLength( 8 ); + } ); + + test( 'each preset has the required shape', () => { + for ( const preset of PRESETS ) { + expect( preset ).toEqual( + expect.objectContaining( { + slug: expect.stringMatching( /^[a-z-]+$/ ), + label: expect.any( String ), + color: expect.stringMatching( /^#[0-9a-f]{3,6}$/i ), + cssFilter: expect.any( String ), + } ) + ); + } + } ); + + test( 'slugs are unique', () => { + const slugs = PRESETS.map( ( p ) => p.slug ); + expect( new Set( slugs ).size ).toBe( slugs.length ); + } ); + + test( 'normal preset has filter: none', () => { + const normal = PRESETS.find( ( p ) => p.slug === 'normal' ); + expect( normal.cssFilter ).toBe( 'none' ); + } ); + + test( 'DEFAULT_PRESET is "normal" and exists in PRESETS', () => { + expect( DEFAULT_PRESET ).toBe( 'normal' ); + expect( PRESETS.map( ( p ) => p.slug ) ).toContain( DEFAULT_PRESET ); + } ); + + test( 'isValidPresetSlug returns true for known slugs', () => { + expect( isValidPresetSlug( 'normal' ) ).toBe( true ); + expect( isValidPresetSlug( 'dramatic' ) ).toBe( true ); + } ); + + test( 'isValidPresetSlug returns false for unknown slugs', () => { + expect( isValidPresetSlug( 'foobar' ) ).toBe( false ); + expect( isValidPresetSlug( '' ) ).toBe( false ); + } ); +} );