Skip to content
This repository was archived by the owner on Aug 19, 2022. It is now read-only.

Commit a40263e

Browse files
authored
Merge pull request #24 from brybrophy/feature/support-srcset
Feature/support srcset
2 parents 1ced372 + 295c4e3 commit a40263e

File tree

6 files changed

+135
-31
lines changed

6 files changed

+135
-31
lines changed

README.md

+25-1
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@ If you use the UMD build you can find the library on `window.ReactProgressiveIma
2222

2323
## Usage
2424

25-
`react-progressive-image` exports a single React component, `ProgressiveImage`, which takes a `src` and `placeholder` prop, as well as an optional `onError` function.
25+
`react-progressive-image` exports a single React component, `ProgressiveImage`, which takes a `src` and `placeholder` prop, as well as optional props `srcSetData`, and `onError` function.
2626

2727
`src` should be the final image you want to load, and `placeholder` is the image you want to display until `src` is loaded. `placeholder` can be anything you want. A typical use case might involve using a smaller version of the image, an inlined version (data URI), or a loading graphic.
2828

29+
If you would like to supply a srcSet for the image, you can use the `srcSetData` prop. The prop should be and object containing two properties, `srcSet`, and `sizes`.
30+
2931
`ProgressiveImage` accepts a render callback as a child, which will be called with the `placeholder` first, and then `src` once the image has been loaded.
3032

3133
```jsx
@@ -42,4 +44,26 @@ It will also call the render callback with a second argument, `loading`, which y
4244
<img style={{ opacity: loading ? 0.5 : 1 }} src={src} alt='an image'/>
4345
)}
4446
</ProgressiveImage>
47+
```
48+
49+
If the `srcSetData` prop is supplied, it will be returned as the third argument to the render callback. This is helpful if you are sharing a render function between multiple different `ProgressiveImage` components.
50+
51+
```jsx
52+
<ProgressiveImage
53+
src='medium.jpg'
54+
srcSetData={{
55+
srcSet: 'small.jpg 320w, medium.jpg 700w, large.jpg 2000w',
56+
sizes: "(max-width: 2000px) 100vw, 2000px"
57+
}}
58+
placeholder='tiny-image.jpg'
59+
>
60+
{(src, _loading, srcSetData) => (
61+
<img
62+
src={src}
63+
srcSet={srcSetData.srcSet}
64+
sizes={srcSetData.sizes}
65+
alt='an image'
66+
/>
67+
)}
68+
</ProgressiveImage>
4569
```

__tests__/index.js

+19-9
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,22 @@ configure({ adapter: new Adapter() });
88

99
const src = "https://image.xyz/source";
1010
const placeholder = "https://image.xyz/placeholder";
11+
const srcSetData = {
12+
srcSet: "srcSet",
13+
sizes: "sizes"
14+
};
1115

1216
const mountProgressiveImage = renderFn => {
1317
const defaultRender = image => {
1418
return <img src={image} />;
1519
};
1620
const render = renderFn || defaultRender;
1721
return mount(
18-
<ProgressiveImage src={src} placeholder={placeholder}>
22+
<ProgressiveImage
23+
src={src}
24+
placeholder={placeholder}
25+
srcSetData={srcSetData}
26+
>
1927
{render}
2028
</ProgressiveImage>
2129
);
@@ -25,11 +33,9 @@ describe("react-progressive-image", () => {
2533
beforeEach(() => {
2634
global.Image = Image;
2735
});
28-
2936
it("exports a React component", () => {
3037
expect(typeof ProgressiveImage).toBe("function");
3138
});
32-
3339
it("throws if not provided a function as a child", () => {
3440
/* eslint-disable no-console */
3541
const _error = console.error;
@@ -47,31 +53,36 @@ describe("react-progressive-image", () => {
4753
}
4854
/* eslint-enable no-console */
4955
});
50-
5156
it("creates an instance of Image when mounted", () => {
5257
const wrapper = mountProgressiveImage();
5358
const instance = wrapper.instance();
5459
expect(instance.image.constructor).toBe(HTMLImageElement);
5560
});
56-
5761
it("sets the onload property on the Image instance", () => {
5862
const wrapper = mountProgressiveImage();
5963
const instance = wrapper.instance();
6064
expect(instance.image.onload).toEqual(instance.onLoad);
6165
});
62-
6366
it("sets the onerror property on the Image instance", () => {
6467
const wrapper = mountProgressiveImage();
6568
const instance = wrapper.instance();
6669
expect(instance.image.onerror).toEqual(instance.onError);
6770
});
68-
6971
it("sets the src property on the Image instance", () => {
7072
const wrapper = mountProgressiveImage();
7173
const instance = wrapper.instance();
7274
expect(instance.image.src).toEqual(src);
7375
});
74-
76+
it("sets the srcSet property on the Image instance", () => {
77+
const wrapper = mountProgressiveImage();
78+
const instance = wrapper.instance();
79+
expect(instance.image.srcset).toEqual(srcSetData.srcSet);
80+
});
81+
it("sets the sizes property on the Image instance", () => {
82+
const wrapper = mountProgressiveImage();
83+
const instance = wrapper.instance();
84+
expect(instance.image.sizes).toEqual(srcSetData.sizes);
85+
});
7586
it("renders placeholder image on initial render", () => {
7687
const render = jest.fn(src => <img src={src} alt="an image" />);
7788
const wrapper = mountProgressiveImage(render);
@@ -84,7 +95,6 @@ describe("react-progressive-image", () => {
8495
wrapper.instance().onLoad();
8596
expect(render.mock.calls[1][0]).toEqual(src);
8697
});
87-
8898
it("sets loading to false after src image is loaded", () => {
8999
const render = jest.fn(src => <img src={src} alt="an image" />);
90100
const wrapper = mountProgressiveImage(render);

demo/app.js

+60-7
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,73 @@ import React from 'react';
22
import ReactDOM from 'react-dom';
33
import ProgressiveImage from '../src/index.js';
44
import inline from './inline';
5-
const src = 'http://i.imgur.com/XhGsjTN.jpg';
5+
const SM = 'https://farm2.staticflickr.com/1853/42944460370_e749cd18eb_b.jpg';
6+
const MD = 'https://farm2.staticflickr.com/1867/30884025408_7e6907d2e4_b.jpg';
7+
const LG = 'https://farm2.staticflickr.com/1875/42944459590_170ddf9fc8_b.jpg';
8+
9+
const centerAlign = {
10+
alignItems: 'center',
11+
display: 'flex',
12+
flexDirection: 'column',
13+
justifyContent: 'center'
14+
};
15+
16+
const containerStyle = {
17+
...centerAlign,
18+
overflow: 'hidden',
19+
position: 'relative'
20+
};
621

722
const imageStyle = {
8-
width: 800,
9-
margin: '0 auto'
23+
minHeight: '100vh',
24+
minWidth: '100vw'
25+
};
26+
27+
const textContainerStyle = {
28+
...centerAlign,
29+
bottom: 0,
30+
left: 0,
31+
position: 'absolute',
32+
right: 0,
33+
top: 0
34+
}
35+
36+
const textStyle = {
37+
color: '#fff',
38+
fontFamily: 'sans-serif',
39+
fontSize: '2.5em',
40+
textTransform: 'uppercase',
1041
};
1142

1243
class App extends React.Component {
1344
render() {
1445
return (
15-
<div>
16-
<h1>Progressive!</h1>
17-
<ProgressiveImage src={src} placeholder={inline}>
18-
{image => <img style={imageStyle} src={image} />}
46+
<div style={containerStyle}>
47+
<div style={textContainerStyle}>
48+
<h1 style={textStyle}>
49+
React <br />
50+
Progressive <br />
51+
Image
52+
</h1>
53+
</div>
54+
<ProgressiveImage
55+
src={MD}
56+
placeholder={inline}
57+
srcSetData={{
58+
srcSet: `${SM} 320w, ${MD} 700w, ${LG} 2000w`,
59+
sizes: "(max-width: 2000px) 100vw, 2000px"
60+
}}
61+
>
62+
{(image, loading, srcSetData) => {
63+
return (
64+
<img
65+
style={imageStyle}
66+
src={image}
67+
srcSet={srcSetData.srcSet}
68+
sizes={srcSetData.sizes}
69+
/>
70+
);
71+
}}
1972
</ProgressiveImage>
2073
</div>
2174
);

demo/index.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<meta name="description" content="">
1111
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
1212
</head>
13-
<body>
13+
<body style="margin: 0">
1414
<!--[if lt IE 8]>
1515
<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
1616
<![endif]-->

demo/inline.js

+2-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/index.js

+28-12
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,23 @@
22

33
import * as React from "react";
44

5+
type SrcSetData = {
6+
srcSet: string,
7+
sizes: string
8+
};
9+
510
type Props = {
6-
children: (string, boolean) => React.Node,
11+
children: (string, boolean, SrcSetData) => React.Node,
712
onError?: (errorEvent: Event) => void,
813
placeholder: string,
9-
src: string
14+
src: string,
15+
srcSetData?: SrcSetData
1016
};
1117

1218
type State = {
1319
image: string,
14-
loading: boolean
20+
loading: boolean,
21+
srcSetData: SrcSetData
1522
};
1623

1724
export default class ProgressiveImage extends React.Component<Props, State> {
@@ -20,21 +27,22 @@ export default class ProgressiveImage extends React.Component<Props, State> {
2027
super(props);
2128
this.state = {
2229
image: props.placeholder,
23-
loading: true
30+
loading: true,
31+
srcSetData: { srcSet: "", sizes: "" }
2432
};
2533
}
2634

2735
componentDidMount() {
28-
const { src } = this.props;
29-
this.loadImage(src);
36+
const { src, srcSetData } = this.props;
37+
this.loadImage(src, srcSetData);
3038
}
3139

3240
componentDidUpdate(prevProps: Props) {
33-
const { src, placeholder } = this.props;
41+
const { src, placeholder, srcSetData } = this.props;
3442
// We only invalidate the current image if the src has changed.
3543
if (src !== prevProps.src) {
3644
this.setState({ image: placeholder, loading: true }, () => {
37-
this.loadImage(src);
45+
this.loadImage(src, srcSetData);
3846
});
3947
}
4048
}
@@ -46,7 +54,7 @@ export default class ProgressiveImage extends React.Component<Props, State> {
4654
}
4755
}
4856

49-
loadImage = (src: string) => {
57+
loadImage = (src: string, srcSetData?: SrcSetData) => {
5058
// If there is already an image we nullify the onload
5159
// and onerror props so it does not incorrectly set state
5260
// when it resolves
@@ -59,6 +67,10 @@ export default class ProgressiveImage extends React.Component<Props, State> {
5967
image.onload = this.onLoad;
6068
image.onerror = this.onError;
6169
image.src = src;
70+
if (srcSetData) {
71+
image.srcset = srcSetData.srcSet;
72+
image.sizes = srcSetData.sizes;
73+
}
6274
};
6375

6476
onLoad = () => {
@@ -68,7 +80,11 @@ export default class ProgressiveImage extends React.Component<Props, State> {
6880
// this.props.
6981
this.setState({
7082
image: this.image.src,
71-
loading: false
83+
loading: false,
84+
srcSetData: {
85+
srcSet: this.image.srcset || "",
86+
sizes: this.image.sizes || ""
87+
}
7288
});
7389
};
7490

@@ -80,13 +96,13 @@ export default class ProgressiveImage extends React.Component<Props, State> {
8096
};
8197

8298
render() {
83-
const { image, loading } = this.state;
99+
const { image, loading, srcSetData } = this.state;
84100
const { children } = this.props;
85101

86102
if (!children || typeof children !== "function") {
87103
throw new Error(`ProgressiveImage requires a function as its only child`);
88104
}
89105

90-
return children(image, loading);
106+
return children(image, loading, srcSetData);
91107
}
92108
}

0 commit comments

Comments
 (0)