diff --git a/src/index.js b/src/index.js index 8f792c4..5e381ab 100644 --- a/src/index.js +++ b/src/index.js @@ -15,11 +15,13 @@ import edit from './edit'; import save from './save'; import { PRESETS, DEFAULT_PRESET, isValidPresetSlug } from './presets'; import { FilterPanel } from './inspector'; +import { transforms } from './transforms'; registerBlockType( metadata.name, { ...metadata, edit, save, + transforms, } ); /** diff --git a/src/transforms.js b/src/transforms.js new file mode 100644 index 0000000..97a8de5 --- /dev/null +++ b/src/transforms.js @@ -0,0 +1,84 @@ +/** + * Block transforms for the Filtered Image block. + * + * - "To" core Image: copies image attributes, drops filter and intensity. + * - "From" core Image: copies image attributes, sets filter="normal", + * intensity=100. + * + * @package ImageFilters + */ + +import { DEFAULT_PRESET } from './presets'; + +const IMAGE_ATTR_MAP = { + id: 'imageId', + url: 'imageUrl', + alt: 'imageAlt', + caption: 'caption', + width: 'width', + height: 'height', + href: 'linkUrl', +}; + +/** + * Maps a core/image-shaped attribute object to a Filtered Image + * attribute object. + * + * @param {Object} source + * @return {Object} + */ +function fromImageAttrs( source ) { + const out = { + filter: DEFAULT_PRESET, + intensity: 100, + imageId: 0, + imageUrl: '', + imageAlt: '', + width: undefined, + height: undefined, + linkUrl: '', + caption: '', + }; + for ( const [ coreKey, ourKey ] of Object.entries( IMAGE_ATTR_MAP ) ) { + if ( source[ coreKey ] !== undefined ) { + out[ ourKey ] = source[ coreKey ]; + } + } + return out; +} + +/** + * Maps a Filtered Image attribute object back to a core/image-shaped + * attribute object. + * + * @param {Object} source + * @return {Object} + */ +function toImageAttrs( source ) { + const out = {}; + for ( const [ coreKey, ourKey ] of Object.entries( IMAGE_ATTR_MAP ) ) { + if ( source[ ourKey ] !== undefined ) { + out[ coreKey ] = source[ ourKey ]; + } + } + return out; +} + +export const transforms = { + to: [ + { + type: 'block', + blocks: [ 'core/image' ], + transform: ( attributes ) => toImageAttrs( attributes ), + }, + ], + from: [ + { + type: 'block', + blocks: [ 'core/image' ], + transform: ( attributes ) => fromImageAttrs( attributes ), + }, + ], +}; + +export default transforms; \ No newline at end of file diff --git a/tests/jest/transforms.test.js b/tests/jest/transforms.test.js new file mode 100644 index 0000000..f8bef66 --- /dev/null +++ b/tests/jest/transforms.test.js @@ -0,0 +1,66 @@ +import { transforms } from '../../src/transforms'; + +describe( 'transforms', () => { + test( 'exposes a "to" transform targeting core/image', () => { + const to = transforms.to; + expect( Array.isArray( to ) ? to : [ to ] ).toEqual( + expect.arrayContaining( [ + expect.objectContaining( { + type: 'block', + blocks: expect.arrayContaining( [ 'core/image' ] ), + } ), + ] ) + ); + } ); + + test( 'exposes a "from" transform sourced from core/image', () => { + const from = transforms.from; + expect( Array.isArray( from ) ? from : [ from ] ).toEqual( + expect.arrayContaining( [ + expect.objectContaining( { + type: 'block', + blocks: expect.arrayContaining( [ 'core/image' ] ), + } ), + ] ) + ); + } ); + + test( 'to-transform drops filter and intensity', () => { + const to = Array.isArray( transforms.to ) ? transforms.to[ 0 ] : transforms.to; + const mapped = to.transform( { + filter: 'dramatic', + intensity: 80, + imageId: 12, + imageUrl: 'https://example.com/a.jpg', + imageAlt: 'A', + width: 100, + height: 100, + linkUrl: '', + caption: '', + } ); + expect( mapped ).not.toHaveProperty( 'filter' ); + expect( mapped ).not.toHaveProperty( 'intensity' ); + expect( mapped.id ).toBe( 12 ); + expect( mapped.url ).toBe( 'https://example.com/a.jpg' ); + } ); + + test( 'from-transform normalises filter and intensity to defaults', () => { + const from = Array.isArray( transforms.from ) ? transforms.from[ 0 ] : transforms.from; + const mapped = from.transform( { + id: 12, + url: 'https://example.com/a.jpg', + alt: 'A', + caption: 'cap', + width: 100, + height: 100, + href: 'https://example.com', + } ); + expect( mapped.filter ).toBe( 'normal' ); + expect( mapped.intensity ).toBe( 100 ); + expect( mapped.imageId ).toBe( 12 ); + expect( mapped.imageUrl ).toBe( 'https://example.com/a.jpg' ); + expect( mapped.imageAlt ).toBe( 'A' ); + expect( mapped.caption ).toBe( 'cap' ); + expect( mapped.linkUrl ).toBe( 'https://example.com' ); + } ); +} ); \ No newline at end of file