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

Commit

Permalink
feat(sources): Allow PowerPicture to take a single source as a string
Browse files Browse the repository at this point in the history
  • Loading branch information
tvthatsme committed Jun 6, 2018
1 parent 672d776 commit 6653efe
Show file tree
Hide file tree
Showing 5 changed files with 215 additions and 40 deletions.
62 changes: 53 additions & 9 deletions example/src/App.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import React, { Component } from 'react';
import PowerPicture from 'react-power-picture';
import ToggleButton from './ToggleButton';

export default class App extends Component {
state = {
useSrcMap: true
};

sources = [
{
size: 400,
Expand All @@ -16,20 +21,59 @@ export default class App extends Component {
src: 'https://source.unsplash.com/random/400x300'
}
];

useSrcMap = toggle => {
this.setState({
useSrcMap: toggle
});
};

renderPicture = (image, loading) => {
return (
<div className="text-center">
<p>Loading state is tracked: {loading.toString()}</p>
<img alt="A p!cture is worth a thousand words" src={image} />
</div>
);
};

useSources = () => {
return (
<PowerPicture sources={this.sources}>
{(image, loading) => this.renderPicture(image, loading)}
</PowerPicture>
);
};

useSource = () => {
return (
<PowerPicture source={this.sources[2].src}>
{(image, loading) => this.renderPicture(image, loading)}
</PowerPicture>
);
};

render() {
const picture = this.state.useSrcMap ? this.useSources() : this.useSource();
return (
<div>
<h1 className="header">React Power Picture Example</h1>
<div className="center">
<PowerPicture sources={this.sources}>
{(image, loading) => (
<div className="text-center">
<p>Loading state: {loading.toString()}</p>
<img alt="A p!cture is worth a thousand words" src={image} />
</div>
)}
</PowerPicture>
<p className="text-center">
Source info can come from an array of srcset info or from a single url
</p>
<div className="toggle-container">
<ToggleButton
select={() => this.useSrcMap(true)}
selected={this.state.useSrcMap}
text="Use Source Map"
/>
<ToggleButton
select={() => this.useSrcMap(false)}
selected={!this.state.useSrcMap}
text="Use Single Source"
/>
</div>
<div className="center">{picture}</div>
</div>
);
}
Expand Down
14 changes: 14 additions & 0 deletions example/src/ToggleButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';

const ToggleButton = props => {
return (
<button
onClick={() => props.select()}
className={'btn ' + (props.selected ? 'btn--selected' : '')}
>
{props.text}
</button>
);
};

export default ToggleButton;
35 changes: 34 additions & 1 deletion example/src/index.css
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
body {
background-color: #ecf0f1;
background-color: #fff;
font-family: 'Open Sans', sans-serif;
}

.header {
font-size: 2.5rem;
color: #34495e;
text-align: center;
font-weight: 800;
}

.center {
Expand All @@ -19,3 +20,35 @@ body {
.text-center {
text-align: center;
}

.toggle-container {
display: flex;
flex-direction: row;
justify-content: center;
}

.btn {
border: 2px solid #34495e;
border-radius: 50px;
padding: 5px 12px;
margin: 0 5px;
font-size: 16px;
font-weight: 600;
background: transparent;
-webkit-font-smoothing: inherit;
-moz-osx-font-smoothing: inherit;
-webkit-appearance: none;
}

.btn--selected {
color: #fff;
background-color: #2ecc71;
border: 2px solid #27ae60;
}

.btn:hover {
cursor: pointer;
}
.btn:focus {
outline: none;
}
98 changes: 79 additions & 19 deletions src/__tests__/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,33 +26,37 @@ describe('PowerPicture Component', () => {
expect(comp).toMatchSnapshot();
});

it('Starts with loading equals true', () => {
let result = null;
const comp = renderer.create(
<PowerPicture sources={defaultSrcs}>
{(image, loading) => {
if (result === null) {
result = loading;
}
return null;
}}
</PowerPicture>
);

expect(result).toBe(true);
it('Accepts a single source as a parameter', () => {
const _error = console.error;
console.error = jest.fn(() => {});
try {
expect(() => {
renderer.create(
<PowerPicture sources={defaultSrcs}>
{image => <img alt="description" src={image} />}
</PowerPicture>
);
}).not.toThrow(
`PowerPicture requires either \'source\' or \'sources\' as a prop`
);
} finally {
console.error = _error;
}
});

it('Throws if not provided a function as a child', () => {
it('Accepts an array of sources as a parameter', () => {
const _error = console.error;
console.error = jest.fn(() => {});
try {
expect(() => {
renderer.create(
<PowerPicture sources={defaultSrcs}>
<p>Not a render method</p>
<PowerPicture source="some-image.png">
{image => <img alt="description" src={image} />}
</PowerPicture>
);
}).toThrow(`PowerPicture requires a function as its only child`);
}).not.toThrow(
`PowerPicture requires either \'source\' or \'sources\' as a prop`
);
} finally {
console.error = _error;
}
Expand All @@ -72,7 +76,63 @@ describe('PowerPicture Component', () => {
)}
</PowerPicture>
);
}).toThrow(`PowerPicture requires sources as a prop`);
}).toThrow(
`PowerPicture requires either \'source\' or \'sources\' as a prop`
);
} finally {
console.error = _error;
}
});

it('Throws if provided too many types of sources', () => {
const _error = console.error;
console.error = jest.fn(() => {});
try {
expect(() => {
renderer.create(
<PowerPicture source="some-src.png" sources={defaultSrcs}>
{image => (
<div className="text-center">
<img alt="description" src={image} />
</div>
)}
</PowerPicture>
);
}).toThrow(
`PowerPicture requires ONE prop of either \'source\' or \'sources\' but cannot accept both`
);
} finally {
console.error = _error;
}
});

it('Starts with loading equals true', () => {
let result = null;
const comp = renderer.create(
<PowerPicture sources={defaultSrcs}>
{(image, loading) => {
if (result === null) {
result = loading;
}
return null;
}}
</PowerPicture>
);

expect(result).toBe(true);
});

it('Throws if not provided a function as a child', () => {
const _error = console.error;
console.error = jest.fn(() => {});
try {
expect(() => {
renderer.create(
<PowerPicture sources={defaultSrcs}>
<p>Not a render method</p>
</PowerPicture>
);
}).toThrow(`PowerPicture requires a function as its only child`);
} finally {
console.error = _error;
}
Expand Down
46 changes: 35 additions & 11 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import PropTypes from 'prop-types';

class PowerPicture extends Component {
static propTypes = {
source: PropTypes.string,
sources: PropTypes.arrayOf(
PropTypes.shape({
src: PropTypes.string,
size: PropTypes.number
})
).isRequired,
),
onError: PropTypes.func,
children: PropTypes.func
};
Expand All @@ -19,6 +20,10 @@ class PowerPicture extends Component {
// define an internal variable for the image that will be loaded
this.image = null;

// define error messages
this.ERROR_TOO_MANY_SOURCES = `PowerPicture requires ONE prop of either \'source\' or \'sources\' but cannot accept both`;
this.ERROR_MISSING_SOURCE = `PowerPicture requires either \'source\' or \'sources\' as a prop`;

// define the component state
this.state = {
image: null,
Expand All @@ -30,13 +35,20 @@ class PowerPicture extends Component {
* Start loading the right image once the component mounts
*/
componentDidMount() {
const { sources } = this.props;

if (sources === undefined) {
throw new Error(`PowerPicture requires sources as a prop`);
const { source, sources } = this.props;
let imgToLoad;

if (sources) {
if (source) {
throw new Error(this.ERROR_TOO_MANY_SOURCES);
}
imgToLoad = this.getIdealSize(sources, window.innerWidth);
} else if (source) {
imgToLoad = source;
} else {
throw new Error(this.ERROR_MISSING_SOURCE);
}

const imgToLoad = this.getIdealSize(sources, window.innerWidth);
this.loadImage(imgToLoad);
}

Expand All @@ -57,13 +69,25 @@ class PowerPicture extends Component {
* @param {Object} prevState state of component before update
*/
componentDidUpdate(prevProps, prevState) {
const { sources } = this.props;
const imgToLoad = this.getIdealSize(sources, window.innerWidth);
const { source, sources } = this.props;

// default to source (which may be undefined)
let imgToLoad = source;

// get the image to load from sources (if defined)
if (sources) {
imgToLoad = this.getIdealSize(sources, window.innerWidth);
}

// If the img source changed, reset PowerPicture back to the default state
if (prevState.image !== null && imgToLoad !== prevState.image) {
this.setState({ loading: true }, () => {
this.loadImage(imgToLoad);
});
this.setState(
{
image: null,
loading: true
},
() => this.loadImage(imgToLoad)
);
}
}

Expand Down

0 comments on commit 6653efe

Please sign in to comment.