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

Commit

Permalink
Merge pull request #2 from FormidableLabs/master
Browse files Browse the repository at this point in the history
Sync with upstream master
  • Loading branch information
brybrophy authored Sep 24, 2018
2 parents 6146776 + 6f0b6b4 commit d19a7f2
Show file tree
Hide file tree
Showing 9 changed files with 153 additions and 85 deletions.
70 changes: 43 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@

[`react-progressive-image`](https://www.npmjs.com/package/react-progressive-image) React component for progressive image loading

## Installation

Using [npm](https://www.npmjs.com/):

$ npm install --save react-progressive-image
### Install

```bash
$ yarn add react-progressive-image
```

The UMD build is also available on [unpkg](https://unpkg.com):

Expand All @@ -20,50 +19,67 @@ The UMD build is also available on [unpkg](https://unpkg.com):

If you use the UMD build you can find the library on `window.ReactProgressiveImage`.

## Usage
### Examples

`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.
#### Simple

`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.

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`.
```jsx
<ProgressiveImage src="large-image.jpg" placeholder="tiny-image.jpg">
{src => <img src={src} alt="an image" />}
</ProgressiveImage>
```

`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.
#### With Delay

```jsx
<ProgressiveImage src='large-image.jpg' placeholder='tiny-image.jpg'>
{(src) => <img src={src} alt='an image'/>}
<ProgressiveImage
delay={3000}
src="large-image.jpg"
placeholder="tiny-image.jpg"
>
{src => <img src={src} alt="an image" />}
</ProgressiveImage>
```

It will also call the render callback with a second argument, `loading`, which you can use to quickly determine what image is being rendered. `loading` will be `true` when the placeholder is rendered, and `false` when the full image is rendered.
#### With loading argment

```jsx
<ProgressiveImage src='large-image.jpg' placeholder='tiny-image.jpg'>
<ProgressiveImage src="large-image.jpg" placeholder="tiny-image.jpg">
{(src, loading) => (
<img style={{ opacity: loading ? 0.5 : 1 }} src={src} alt='an image'/>
<img style={{ opacity: loading ? 0.5 : 1 }} src={src} alt="an image" />
)}
</ProgressiveImage>
```

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.
#### With srcSet

```jsx
<ProgressiveImage
src='medium.jpg'
<ProgressiveImage
src="medium.jpg"
srcSetData={{
srcSet: 'small.jpg 320w, medium.jpg 700w, large.jpg 2000w',
sizes: "(max-width: 2000px) 100vw, 2000px"
sizes: '(max-width: 2000px) 100vw, 2000px'
}}
placeholder='tiny-image.jpg'
placeholder="tiny-image.jpg"
>
{(src, _loading, srcSetData) => (
<img
src={src}
srcSet={srcSetData.srcSet}
sizes={srcSetData.sizes}
alt='an image'
<img
src={src}
srcSet={srcSetData.srcSet}
sizes={srcSetData.sizes}
alt="an image"
/>
)}
</ProgressiveImage>
```
```

### Props

| Name | Type | Required | Description |
| ----------- | -------------------------------------- | -------- | ----------------------------------------------- |
| children | `function` | `true` | returns `src`, `loading`, and `srcSetData` |
| delay | `number` | `false` | time in milliseconds before src image is loaded |
| onError | `function` | `false` | returns error event |
| placeholder | `string` | `true` | the src of the placeholder image |
| src | `string` | `true` | the src of the main image |
| srcSetData | `{srcSet: "string", sizes: "string" }` | `false` | srcset and sizes to be applied to the image |
65 changes: 42 additions & 23 deletions __tests__/index.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
import "raf/polyfill";
import React from "react";
import { configure, mount } from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import ProgressiveImage from "../src";
import 'raf/polyfill';
import React from 'react';
import { configure, mount } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import ProgressiveImage from '../src';

configure({ adapter: new Adapter() });

const src = "https://image.xyz/source";
const placeholder = "https://image.xyz/placeholder";
const src = 'https://image.xyz/source';
const placeholder = 'https://image.xyz/placeholder';
const srcSetData = {
srcSet: "srcSet",
sizes: "sizes"
srcSet: 'srcSet',
sizes: 'sizes'
};

const mountProgressiveImage = renderFn => {
const mountProgressiveImage = (renderFn, delay) => {
const defaultRender = image => {
return <img src={image} />;
};
const render = renderFn || defaultRender;
return mount(
<ProgressiveImage
delay={delay}
src={src}
placeholder={placeholder}
srcSetData={srcSetData}
Expand All @@ -29,14 +30,14 @@ const mountProgressiveImage = renderFn => {
);
};

describe("react-progressive-image", () => {
describe('react-progressive-image', () => {
beforeEach(() => {
global.Image = Image;
});
it("exports a React component", () => {
expect(typeof ProgressiveImage).toBe("function");
it('exports a React component', () => {
expect(typeof ProgressiveImage).toBe('function');
});
it("throws if not provided a function as a child", () => {
it('throws if not provided a function as a child', () => {
/* eslint-disable no-console */
const _error = console.error;
console.error = jest.fn(() => {});
Expand All @@ -53,54 +54,72 @@ describe("react-progressive-image", () => {
}
/* eslint-enable no-console */
});
it("creates an instance of Image when mounted", () => {
it('creates an instance of Image when mounted', () => {
const wrapper = mountProgressiveImage();
const instance = wrapper.instance();
expect(instance.image.constructor).toBe(HTMLImageElement);
});
it("sets the onload property on the Image instance", () => {
it('sets the onload property on the Image instance', () => {
const wrapper = mountProgressiveImage();
const instance = wrapper.instance();
expect(instance.image.onload).toEqual(instance.onLoad);
});
it("sets the onerror property on the Image instance", () => {
it('sets the onerror property on the Image instance', () => {
const wrapper = mountProgressiveImage();
const instance = wrapper.instance();
expect(instance.image.onerror).toEqual(instance.onError);
});
it("sets the src property on the Image instance", () => {
it('sets the src property on the Image instance', () => {
const wrapper = mountProgressiveImage();
const instance = wrapper.instance();
expect(instance.image.src).toEqual(src);
});
it("sets the srcSet property on the Image instance", () => {
it('sets the srcSet property on the Image instance', () => {
const wrapper = mountProgressiveImage();
const instance = wrapper.instance();
expect(instance.image.srcset).toEqual(srcSetData.srcSet);
});
it("sets the sizes property on the Image instance", () => {
it('sets the sizes property on the Image instance', () => {
const wrapper = mountProgressiveImage();
const instance = wrapper.instance();
expect(instance.image.sizes).toEqual(srcSetData.sizes);
});
it("renders placeholder image on initial render", () => {
it('renders placeholder image on initial render', () => {
const render = jest.fn(src => <img src={src} alt="an image" />);
const wrapper = mountProgressiveImage(render);
expect(render.mock.calls[0][0]).toEqual(placeholder);
});
it("renders src image on second render", () => {
it('renders src image on second render', () => {
const render = jest.fn(src => <img src={src} alt="an image" />);
const wrapper = mountProgressiveImage(render);
wrapper.instance().loadImage(src);
wrapper.instance().onLoad();
expect(render.mock.calls[1][0]).toEqual(src);
});
it("sets loading to false after src image is loaded", () => {
it('sets loading to false after src image is loaded', () => {
const render = jest.fn(src => <img src={src} alt="an image" />);
const wrapper = mountProgressiveImage(render);
expect(render.mock.calls[0][1]).toEqual(true);
wrapper.instance().loadImage(src);
wrapper.instance().onLoad();
expect(render.mock.calls[1][1]).toEqual(false);
});
it('does not immediately set image if delay prop exists', () => {
const delay = 3000;
const render = jest.fn(src => <img src={src} alt="an image" />);
const wrapper = mountProgressiveImage(render, delay);
wrapper.instance().loadImage(src);
wrapper.instance().onLoad();
expect(wrapper.instance().state.image).toEqual(placeholder);
});
it('sets image after delay passes if delay prop exists', () => {
const delay = 3000;
const render = jest.fn(src => <img src={src} alt="an image" />);
const wrapper = mountProgressiveImage(render, delay);
wrapper.instance().loadImage(src);
wrapper.instance().onLoad();
setTimeout(() => {
expect(wrapper.instance().state.image).toEqual(src);
}, delay + 1);
});
});
20 changes: 10 additions & 10 deletions demo/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ const textContainerStyle = {
position: 'absolute',
right: 0,
top: 0
}
};

const textStyle = {
color: '#fff',
fontFamily: 'sans-serif',
fontSize: '2.5em',
textTransform: 'uppercase',
textTransform: 'uppercase'
};

class App extends React.Component {
Expand All @@ -51,20 +51,20 @@ class App extends React.Component {
Image
</h1>
</div>
<ProgressiveImage
src={MD}
placeholder={inline}
<ProgressiveImage
src={MD}
placeholder={inline}
srcSetData={{
srcSet: `${SM} 320w, ${MD} 700w, ${LG} 2000w`,
sizes: "(max-width: 2000px) 100vw, 2000px"
sizes: '(max-width: 2000px) 100vw, 2000px'
}}
>
{(image, loading, srcSetData) => {
return (
<img
style={imageStyle}
src={image}
srcSet={srcSetData.srcSet}
<img
style={imageStyle}
src={image}
srcSet={srcSetData.srcSet}
sizes={srcSetData.sizes}
/>
);
Expand Down
2 changes: 1 addition & 1 deletion demo/inline.js

Large diffs are not rendered by default.

16 changes: 8 additions & 8 deletions demo/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
module.exports = {
mode: 'development',
context: __dirname,
entry: [
'./app.js'
],
entry: ['./app.js'],
output: {
path: __dirname,
filename: 'bundle.js'
},
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
}]
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
}
]
}
};
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-progressive-image",
"version": "0.5.0",
"version": "0.6.0",
"description": "Progressive image loading for React",
"main": "dist.js",
"typings": "src/index.d.ts",
Expand Down
4 changes: 4 additions & 0 deletions prettier.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
singleQuote: true,
trailingComma: "none"
};
33 changes: 23 additions & 10 deletions src/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
declare module "react-progressive-image" {
export interface ProgressiveImageProps {
onError?: (errorEvent: Event) => void;
placeholder: string;
src: string;
}
declare module 'react-progressive-image' {
export interface ProgressiveImageProps {
delay?: number;
onError?: (errorEvent: Event) => void;
placeholder: string;
src: string;
srcSetData?: {
srcSet: string;
sizes: string;
};
}

export interface ProgressiveImageState {
image: string;
}
export interface ProgressiveImageState {
image: string;
loading: boolean;
srcSetData?: {
srcSet: string;
sizes: string;
};
}

export default class ProgressiveImage extends React.Component<ProgressiveImageProps, ProgressiveImageState>{}
export default class ProgressiveImage extends React.Component<
ProgressiveImageProps,
ProgressiveImageState
> {}
}
Loading

0 comments on commit d19a7f2

Please sign in to comment.