Skip to content

Commit

Permalink
Merge pull request #12 from APMG/preferred-aspect-ratio-object-prop
Browse files Browse the repository at this point in the history
Adds preferredAspectRatio instances object support
  • Loading branch information
Matt Aho committed Jul 15, 2019
2 parents 99e0340 + 3a6049e commit 2d96e3d
Show file tree
Hide file tree
Showing 5 changed files with 437 additions and 129 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
192 changes: 73 additions & 119 deletions src/Image/Image.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<img
className={props.elementClass}
src={imageProps.src}
alt={imageProps.alt}
srcSet={imageProps.srcSet}
src={getSrc()}
alt={getAlt()}
srcSet={getSrcSet()}
sizes={props.sizes}
/>
);
};

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
}),
Expand Down
38 changes: 30 additions & 8 deletions src/Image/Image.test.js
Original file line number Diff line number Diff line change
@@ -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(
<Image image={image} aspectRatio={props.aspectRatio} />
<Image image={imageWithPreferred} aspectRatio={props.aspectRatio} />
);

expect(container.firstChild.getAttribute('alt')).toBe(
Expand Down Expand Up @@ -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(<Image image={imageWithPreferred} />);

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',
Expand All @@ -78,16 +96,18 @@ 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(<Image image={image} />);
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(<Image image={imageWithoutPreferredSlug} />);

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(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', () => {
Expand Down Expand Up @@ -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', () => {
Expand All @@ -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
Expand Down
Loading

0 comments on commit 2d96e3d

Please sign in to comment.