Skip to content

Commit

Permalink
[change] Image 'source' dimensions and RN layout
Browse files Browse the repository at this point in the history
Adds support for 'width' and 'height' set via the 'source' property.
Emulates RN image layout (i.e., no dimensions by default).

Fix #10
  • Loading branch information
necolas committed Oct 24, 2016
1 parent c9d401f commit efeac8f
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 19 deletions.
2 changes: 1 addition & 1 deletion examples/components/Image/ImageExample.js
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ const examples = [
render: function() {
return (
<Image
source={{uri: 'http://facebook.github.io/react/img/logo_og.png'}}
source={{ uri: 'http://facebook.github.io/react/img/logo_og.png', width: 1200, height: 630 }}
style={styles.base}
/>
);
Expand Down
36 changes: 29 additions & 7 deletions src/components/Image/__tests__/index-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ suite('components/Image', () => {
test('sets background image when value is an object', () => {
const defaultSource = { uri: 'https://google.com/favicon.ico' };
const image = shallow(<Image defaultSource={defaultSource} />);
const backgroundImage = StyleSheet.flatten(image.prop('style')).backgroundImage;
assert(backgroundImage.indexOf(defaultSource.uri) > -1);
const style = StyleSheet.flatten(image.prop('style'));
assert(style.backgroundImage.indexOf(defaultSource.uri) > -1);
});

test('sets background image when value is a string', () => {
Expand All @@ -45,13 +45,31 @@ suite('components/Image', () => {
const backgroundImage = StyleSheet.flatten(image.prop('style')).backgroundImage;
assert(backgroundImage.indexOf(defaultSource) > -1);
});

test('sets "height" and "width" styles if missing', () => {
const defaultSource = { uri: 'https://google.com/favicon.ico', height: 10, width: 20 };
const image = mount(<Image defaultSource={defaultSource} />);
const html = image.html();
const style = image.prop('style');
assert(html.indexOf('height: 10px') > -1);
assert(html.indexOf('width: 20px') > -1);
});

test('does not override "height" and "width" styles', () => {
const defaultSource = { uri: 'https://google.com/favicon.ico', height: 10, width: 20 };
const image = mount(<Image defaultSource={defaultSource} style={{ height: 20, width: 40 }} />);
const html = image.html();
assert(html.indexOf('height: 20px') > -1);
assert(html.indexOf('width: 40px') > -1);
});
});

test('prop "onError"', function (done) {
this.timeout(5000);
mount(<Image onError={onError} source={{ uri: 'https://google.com/favicon.icox' }} />);
const image = mount(<Image onError={onError} source={{ uri: 'https://google.com/favicon.icox' }} />);
function onError(e) {
assert.equal(e.nativeEvent.type, 'error');
assert.ok(e.nativeEvent.error);
image.unmount();
done();
}
});
Expand All @@ -63,6 +81,7 @@ suite('components/Image', () => {
assert.equal(e.nativeEvent.type, 'load');
const hasBackgroundImage = (image.html()).indexOf('url(&quot;https://google.com/favicon.ico&quot;)') > -1;
assert.equal(hasBackgroundImage, true);
image.unmount();
done();
}
});
Expand All @@ -74,13 +93,14 @@ suite('components/Image', () => {
assert.ok(true);
const hasBackgroundImage = (image.html()).indexOf('url(&quot;https://google.com/favicon.ico&quot;)') > -1;
assert.equal(hasBackgroundImage, true);
image.unmount();
done();
}
});

test('prop "onLoadStart"', function (done) {
this.timeout(5000);
mount(<Image onLoadStart={onLoadStart} source={{ uri: 'https://google.com/favicon.ico' }} />);
const image = mount(<Image onLoadStart={onLoadStart} source={{ uri: 'https://google.com/favicon.ico' }} />);
function onLoadStart() {
assert.ok(true);
done();
Expand Down Expand Up @@ -121,21 +141,23 @@ suite('components/Image', () => {

test('sets background image when value is an object', (done) => {
const source = { uri: 'https://google.com/favicon.ico' };
mount(<Image onLoad={onLoad} source={source} />);
const image = mount(<Image onLoad={onLoad} source={source} />);
function onLoad(e) {
const src = e.nativeEvent.target.src;
assert.equal(src, source.uri);
image.unmount();
done();
}
});

test('sets background image when value is a string', (done) => {
// emulate require-ed asset
const source = 'https://google.com/favicon.ico';
mount(<Image onLoad={onLoad} source={source} />);
const image = mount(<Image onLoad={onLoad} source={source} />);
function onLoad(e) {
const src = e.nativeEvent.target.src;
assert.equal(src, source);
image.unmount();
done();
}
});
Expand Down
36 changes: 25 additions & 11 deletions src/components/Image/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,20 @@ const STATUS_IDLE = 'IDLE';

const ImageSourcePropType = PropTypes.oneOfType([
PropTypes.shape({
uri: PropTypes.string.isRequired
height: PropTypes.number,
uri: PropTypes.string.isRequired,
width: PropTypes.number
}),
PropTypes.string
]);

const resolveAssetDimensions = (source) => {
if (typeof source === 'object') {
const { height, width } = source;
return { height, width };
}
};

const resolveAssetSource = (source) => {
return ((typeof source === 'object') ? source.uri : source) || null;
};
Expand Down Expand Up @@ -93,12 +102,15 @@ class Image extends Component {
} = this.props;

const displayImage = resolveAssetSource(!isLoaded ? defaultSource : source);
const imageSizeStyle = resolveAssetDimensions(!isLoaded ? defaultSource : source);
const backgroundImage = displayImage ? `url("${displayImage}")` : null;
const flatStyle = StyleSheet.flatten(this.props.style);
const resizeMode = this.props.resizeMode || flatStyle.resizeMode || ImageResizeMode.cover;
const originalStyle = StyleSheet.flatten(this.props.style);
const resizeMode = this.props.resizeMode || originalStyle.resizeMode || ImageResizeMode.cover;

const style = StyleSheet.flatten([
styles.initial,
flatStyle,
imageSizeStyle,
originalStyle,
backgroundImage && { backgroundImage },
resizeModeStyles[resizeMode]
]);
Expand Down Expand Up @@ -147,14 +159,17 @@ class Image extends Component {
}
}

_onError = (e) => {
const { onError } = this.props;
const event = { nativeEvent: e };

_onError = () => {
const { onError, source } = this.props;
this._destroyImageLoader();
this._updateImageState(STATUS_ERRORED);
this._onLoadEnd();
if (onError) { onError(event); }
if (onError) {
onError({
nativeEvent: {
error: `Failed to load resource ${resolveAssetSource(source)} (404)`
}
});
}
}

_onLoad = (e) => {
Expand Down Expand Up @@ -189,7 +204,6 @@ class Image extends Component {

const styles = StyleSheet.create({
initial: {
alignSelf: 'flex-start',
backgroundColor: 'transparent',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
Expand Down

0 comments on commit efeac8f

Please sign in to comment.