diff --git a/package-lock.json b/package-lock.json index 7d04b97..ee6e6c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@apmg/mimas", - "version": "0.2.0", + "version": "0.3.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 6f2174e..12ae76c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@apmg/mimas", "description": "A React component that takes an image endpoint from APM's APIs and returns a proper image with srcset.", - "version": "0.2.0", + "version": "0.3.0", "main": "dist/index.js", "module": "dist/index.js", "license": "MIT", diff --git a/src/Image/Image.js b/src/Image/Image.js index a36b1a6..d248564 100644 --- a/src/Image/Image.js +++ b/src/Image/Image.js @@ -3,152 +3,106 @@ import PropTypes from 'prop-types'; // Ideally, this component will take in an image object formatted by our images API and spit out an image with a proper srcset. However, I also thought I should provide a couple of fallback options, in case you want to use an image from somewhere else entirely: fallbackSrcSet and fallbackSrc. The last one will just create a normal img tag, so I really don't recommend it. -function generateSrcSet(imageProps, props) { - let aspectRatio = 'uncropped'; - - if (props.image.aspect_ratios) { - if ( - props.aspectRatio in props.image.aspect_ratios && - props.image.aspect_ratios[props.aspectRatio] !== null - ) { - aspectRatio = props.aspectRatio; +const Image = (props) => { + const determineAspectRatio = () => { + if (props.aspectRatio) { + return props.aspectRatio; + } else if (props.image.preferredAspectRatio) { + // forces getSrcSet() to use props.image.preferredAspectRatio if it exists, i.e. the function moves on to the next condition, + // this means that the aspectRatio prop acts as an override if there is a preferred value in the data + return false; + } else if (props.image && props.image.preferred_aspect_ratio_slug) { + return props.image.preferred_aspect_ratio_slug; + } else { + return 'uncropped'; } - - props.image.aspect_ratios[aspectRatio].instances.forEach( - (image, i, dataSet) => { - let set = `${image.url} ${image.width}w`; - if (i !== dataSet.length - 1) { - set = set.concat(','); - } - - imageProps.srcSet = imageProps.srcSet.concat(set); - return; - } - ); - } else { - imageProps.srcSet = props.image.srcset; - return; - } -} - -function generateAttrs(props) { - let imageProps = { - srcSet: '', - src: '', - alt: '' }; - if (props.image) { - imageProps.src = props.image.fallback; - if (props.alt) { - imageProps.alt = props.alt; + const getSrcSet = () => { + if (props.image) { + if ( + props.image.aspect_ratios && + determineAspectRatio() in props.image.aspect_ratios && + props.image.aspect_ratios[props.aspectRatio] !== null + ) { + return generateSrcSet( + props.image.aspect_ratios[determineAspectRatio()].instances + ); + } else if (props.image.preferredAspectRatio) { + return generateSrcSet(props.image.preferredAspectRatio.instances); + } else { + return props.image.srcset; + } + } else if (props.fallbackSrcSet) { + return props.fallbackSrcSet; } else { - imageProps.alt = props.image.short_caption; + return null; } - if (props.aspectRatio) { - generateSrcSet(imageProps, props); + }; + + const getSrc = () => { + if (props.image && props.image.fallback) { + return props.image.fallback; } else { - imageProps.srcSet = props.image.srcset; + return props.fallbackSrc; } - } else if (props.fallbackSrcSet) { - imageProps.srcSet = props.fallbackSrcSet; - imageProps.src = props.fallbackSrc; - imageProps.alt = props.alt; - } else { - imageProps.src = props.fallbackSrc; - imageProps.alt = props.alt; - } + }; - return imageProps; -} + const generateSrcSet = (instances) => { + return instances + .map((instance) => `${instance.url} ${instance.width}w`) + .join(','); + }; -const Image = (props) => { - const imageProps = generateAttrs(props); + const getAlt = () => { + if (props.alt) { + return props.alt; + } else if (props.image && props.image.short_caption) { + return props.image.short_caption; + } else { + return ''; + } + }; return ( {imageProps.alt} ); }; +const aspectRatioType = PropTypes.shape({ + instances: PropTypes.arrayOf( + PropTypes.shape({ + url: PropTypes.string, + width: PropTypes.number, + height: PropTypes.number + }) + ), + slug: PropTypes.string +}); + Image.propTypes = { image: PropTypes.shape({ + preferredAspectRatio: aspectRatioType, aspect_ratios: PropTypes.shape({ - normal: PropTypes.shape({ - instances: PropTypes.arrayOf( - PropTypes.shape({ - width: PropTypes.number, - url: PropTypes.string, - height: PropTypes.number - }) - ), - slug: PropTypes.string - }), - square: PropTypes.shape({ - instances: PropTypes.arrayOf( - PropTypes.shape({ - width: PropTypes.number, - url: PropTypes.string, - height: PropTypes.number - }) - ), - slug: PropTypes.string - }), - thumbnail: PropTypes.shape({ - instances: PropTypes.arrayOf( - PropTypes.shape({ - width: PropTypes.number, - url: PropTypes.string, - height: PropTypes.number - }) - ), - slug: PropTypes.string - }), - widescreen: PropTypes.shape({ - instances: PropTypes.arrayOf( - PropTypes.shape({ - width: PropTypes.number, - url: PropTypes.string, - height: PropTypes.number - }) - ), - slug: PropTypes.string - }), - portrait: PropTypes.shape({ - instances: PropTypes.arrayOf( - PropTypes.shape({ - width: PropTypes.number, - url: PropTypes.string, - height: PropTypes.number - }) - ), - slug: PropTypes.string - }), - uncropped: PropTypes.shape({ - instances: PropTypes.arrayOf( - PropTypes.shape({ - width: PropTypes.number, - url: PropTypes.string, - height: PropTypes.number - }) - ), - slug: PropTypes.string - }) + normal: aspectRatioType, + square: aspectRatioType, + thumbnail: aspectRatioType, + widescreen: aspectRatioType, + portrait: aspectRatioType, + uncropped: aspectRatioType }), + fallback: PropTypes.string, long_caption: PropTypes.string, short_caption: PropTypes.string, width: PropTypes.string, preferred_aspect_ratio_slug: PropTypes.string, id: PropTypes.string, - credit_url: PropTypes.string, - type: PropTypes.string, - float: PropTypes.string, - credit: PropTypes.string, url: PropTypes.string, srcset: PropTypes.string }), diff --git a/src/Image/Image.test.js b/src/Image/Image.test.js index 0d89ddc..22a5b0d 100644 --- a/src/Image/Image.test.js +++ b/src/Image/Image.test.js @@ -1,20 +1,24 @@ import React from 'react'; import { render, cleanup } from 'react-testing-library'; import Image from './Image'; -import { image } from './testdata/image'; +import { + image, + imageWithPreferred, + imageWithoutPreferredSlug +} from './testdata/image'; import 'jest-prop-type-error'; afterEach(cleanup); // SUCCESSES -test('Creates an img with the correct alt, src, and srcSet when aspectRatio prop is provided', () => { +test('Creates an img with the correct alt, src, and srcSet when aspectRatio prop is provided, prioritizes aspectRatio prop over preferred', () => { const props = { aspectRatio: 'widescreen' }; const { container } = render( - + ); expect(container.firstChild.getAttribute('alt')).toBe( @@ -52,6 +56,20 @@ test('Takes in an optional sizes string, specifying specific image behavior with ); }); +test('Creates an img with the preferred aspect ratio when image.preferredAspectRatio is included and no aspectRatio prop is provided', () => { + const { container } = render(); + + expect(container.firstChild.getAttribute('alt')).toBe( + 'Serena Brook opens our show at The Town Hall' + ); + expect(container.firstChild.getAttribute('src')).toBe( + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/uncropped/f5db37-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg' + ); + expect(container.firstChild.getAttribute('srcset')).toBe( + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/square/5ecd52-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg 400w,https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/square/de193e-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg 600w,https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/square/7cb7e2-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg 1000w,https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/square/822d4e-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg 1400w,https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/square/f977a8-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg 2000w' + ); +}); + test('Creates an img with the correct alt, src, and srcSet and className when the elementClass property is provided', () => { const props = { aspectRatio: 'widescreen', @@ -78,8 +96,8 @@ test('Creates an img with the correct alt, src, and srcSet and className when th ); }); -test('Creates an img with the correct alt, src, and defaut srcSet when no aspectRatio prop is provided', () => { - const { container } = render(); +test('Creates an img with the correct alt, src, and "uncropped" srcSet when no aspectRatio prop or image.preferred_aspect_ratio_slug is provided', () => { + const { container } = render(); expect(container.firstChild.getAttribute('alt')).toBe( 'Serena Brook opens our show at The Town Hall' @@ -87,7 +105,9 @@ test('Creates an img with the correct alt, src, and defaut srcSet when no aspect expect(container.firstChild.getAttribute('src')).toBe( 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/uncropped/f5db37-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg' ); - expect(container.firstChild.getAttribute('srcset')).toBe(image.srcset); + expect(container.firstChild.getAttribute('srcset')).toBe( + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/uncropped/35bd3b-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg 400w,https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/uncropped/f5db37-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg 600w,https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/uncropped/04a63f-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg 1000w,https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/uncropped/72bc48-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg 1400w,https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/uncropped/f20034-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg 2000w' + ); }); test('Creates an img when an image object is not provided, but a fallbackSrc, fallbackSrcSet and alt are provided', () => { @@ -142,7 +162,9 @@ test('Creates the correct img when an image prop and all of the fallbacks are pr expect(container.firstChild.getAttribute('src')).toBe( 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/uncropped/f5db37-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg' ); - expect(container.firstChild.getAttribute('srcset')).toBe(image.srcset); + expect(container.firstChild.getAttribute('srcset')).toBe( + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/widescreen/e428bc-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg 400w,https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/widescreen/58b2ba-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg 600w,https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/widescreen/95c885-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg 1000w,https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/widescreen/b3a373-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg 1400w,https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/widescreen/6ceb83-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg 2000w' + ); }); test('Creates the a basic img with an empty srcset if only a alt and fallbackSrc are provided', () => { @@ -162,7 +184,7 @@ test('Creates the a basic img with an empty srcset if only a alt and fallbackSrc expect(container.firstChild.getAttribute('src')).toBe( 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/298/wolf_20131015_003_1400.jpg' ); - expect(container.firstChild.getAttribute('srcset')).toBe(''); + expect(container.firstChild.getAttribute('srcset')).toBe(null); }); // FAILURES diff --git a/src/Image/testdata/image.js b/src/Image/testdata/image.js index 8789935..dfd4753 100644 --- a/src/Image/testdata/image.js +++ b/src/Image/testdata/image.js @@ -207,3 +207,335 @@ export const image = { srcset: 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/widescreen/e428bc-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg 400w, https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/widescreen/58b2ba-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg 600w, https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/widescreen/95c885-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg 1000w, https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/widescreen/b3a373-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg 1400w, https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/widescreen/6ceb83-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg 2000w' }; + +export const imageWithoutPreferredSlug = { + aspect_ratios: { + widescreen: { + instances: [ + { + width: 400, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/widescreen/e428bc-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 225 + }, + { + width: 600, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/widescreen/58b2ba-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 337 + }, + { + width: 1000, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/widescreen/95c885-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 562 + }, + { + width: 1400, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/widescreen/b3a373-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 787 + }, + { + width: 2000, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/widescreen/6ceb83-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 1124 + } + ], + slug: 'widescreen' + }, + uncropped: { + instances: [ + { + width: 400, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/uncropped/35bd3b-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 320 + }, + { + width: 600, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/uncropped/f5db37-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 480 + }, + { + width: 1000, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/uncropped/04a63f-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 800 + }, + { + width: 1400, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/uncropped/72bc48-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 1120 + }, + { + width: 2000, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/uncropped/f20034-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 1600 + } + ], + slug: 'uncropped' + } + }, + long_caption: 'Serena Brook opens our show at The Town Hall', + short_caption: 'Serena Brook opens our show at The Town Hall', + width: 'full', + id: 'c2c452354fbff94d720ba8f86e2c71ba7427b306', + credit_url: '', + type: 'apmImage', + float: 'none', + credit: 'American Public Media', + fallback: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/uncropped/f5db37-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + srcset: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/widescreen/e428bc-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg 400w, https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/widescreen/58b2ba-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg 600w, https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/widescreen/95c885-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg 1000w, https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/widescreen/b3a373-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg 1400w, https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/widescreen/6ceb83-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg 2000w' +}; + +export const imageWithPreferred = { + preferredAspectRatio: { + instances: [ + { + width: 400, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/square/5ecd52-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 400 + }, + { + width: 600, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/square/de193e-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 600 + }, + { + width: 1000, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/square/7cb7e2-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 1000 + }, + { + width: 1400, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/square/822d4e-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 1400 + }, + { + width: 2000, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/square/f977a8-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 2000 + } + ], + slug: 'square' + }, + aspect_ratios: { + normal: { + instances: [ + { + width: 400, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/normal/fa6aa0-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 301 + }, + { + width: 600, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/normal/00b407-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 451 + }, + { + width: 1000, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/normal/10ac72-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 752 + }, + { + width: 1400, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/normal/6e721c-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 1053 + }, + { + width: 2000, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/normal/f49c92-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 1504 + } + ], + slug: 'normal' + }, + square: { + instances: [ + { + width: 400, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/square/5ecd52-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 400 + }, + { + width: 600, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/square/de193e-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 600 + }, + { + width: 1000, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/square/7cb7e2-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 1000 + }, + { + width: 1400, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/square/822d4e-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 1400 + }, + { + width: 2000, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/square/f977a8-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 2000 + } + ], + slug: 'square' + }, + thumbnail: { + instances: [ + { + width: 120, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/thumbnail/e8796f-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 90 + }, + { + width: 300, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/thumbnail/dfad0f-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 226 + } + ], + slug: 'thumbnail' + }, + widescreen: { + instances: [ + { + width: 400, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/widescreen/e428bc-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 225 + }, + { + width: 600, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/widescreen/58b2ba-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 337 + }, + { + width: 1000, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/widescreen/95c885-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 562 + }, + { + width: 1400, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/widescreen/b3a373-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 787 + }, + { + width: 2000, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/widescreen/6ceb83-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 1124 + } + ], + slug: 'widescreen' + }, + portrait: { + instances: [ + { + width: 400, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/portrait/e6415b-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 500 + }, + { + width: 600, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/portrait/0ebc89-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 750 + }, + { + width: 1000, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/portrait/fed99c-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 1250 + }, + { + width: 1400, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/portrait/523b4b-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 1750 + }, + { + width: 1883, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/portrait/766692-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 2354 + } + ], + slug: 'portrait' + }, + uncropped: { + instances: [ + { + width: 400, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/uncropped/35bd3b-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 320 + }, + { + width: 600, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/uncropped/f5db37-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 480 + }, + { + width: 1000, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/uncropped/04a63f-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 800 + }, + { + width: 1400, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/uncropped/72bc48-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 1120 + }, + { + width: 2000, + url: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/uncropped/f20034-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + height: 1600 + } + ], + slug: 'uncropped' + } + }, + long_caption: 'Serena Brook opens our show at The Town Hall', + short_caption: 'Serena Brook opens our show at The Town Hall', + width: 'full', + preferred_aspect_ratio_slug: 'widescreen', + id: 'c2c452354fbff94d720ba8f86e2c71ba7427b306', + credit_url: '', + type: 'apmImage', + float: 'none', + credit: 'American Public Media', + fallback: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/uncropped/f5db37-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg', + srcset: + 'https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/widescreen/e428bc-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg 400w, https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/widescreen/58b2ba-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg 600w, https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/widescreen/95c885-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg 1000w, https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/widescreen/b3a373-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg 1400w, https://img.apmcdn.org/c2c452354fbff94d720ba8f86e2c71ba7427b306/widescreen/6ceb83-20181220-serena-brook-opens-our-show-at-the-town-hall.jpg 2000w' +};