diff --git a/examples/components/Image/ImageExample.js b/examples/components/Image/ImageExample.js index 75a610fb0..9521759a8 100644 --- a/examples/components/Image/ImageExample.js +++ b/examples/components/Image/ImageExample.js @@ -218,7 +218,7 @@ const examples = [ render: function() { return ( ); diff --git a/src/components/Image/__tests__/index-test.js b/src/components/Image/__tests__/index-test.js index fec17b07e..cde80b6e5 100644 --- a/src/components/Image/__tests__/index-test.js +++ b/src/components/Image/__tests__/index-test.js @@ -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(); - 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', () => { @@ -45,13 +45,30 @@ 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(); + const html = image.html(); + 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(); + 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(); + const image = mount(); function onError(e) { - assert.equal(e.nativeEvent.type, 'error'); + assert.ok(e.nativeEvent.error); + image.unmount(); done(); } }); @@ -63,6 +80,7 @@ suite('components/Image', () => { assert.equal(e.nativeEvent.type, 'load'); const hasBackgroundImage = (image.html()).indexOf('url("https://google.com/favicon.ico")') > -1; assert.equal(hasBackgroundImage, true); + image.unmount(); done(); } }); @@ -74,6 +92,7 @@ suite('components/Image', () => { assert.ok(true); const hasBackgroundImage = (image.html()).indexOf('url("https://google.com/favicon.ico")') > -1; assert.equal(hasBackgroundImage, true); + image.unmount(); done(); } }); @@ -121,10 +140,11 @@ suite('components/Image', () => { test('sets background image when value is an object', (done) => { const source = { uri: 'https://google.com/favicon.ico' }; - mount(); + const image = mount(); function onLoad(e) { const src = e.nativeEvent.target.src; assert.equal(src, source.uri); + image.unmount(); done(); } }); @@ -132,10 +152,11 @@ suite('components/Image', () => { test('sets background image when value is a string', (done) => { // emulate require-ed asset const source = 'https://google.com/favicon.ico'; - mount(); + const image = mount(); function onLoad(e) { const src = e.nativeEvent.target.src; assert.equal(src, source); + image.unmount(); done(); } }); diff --git a/src/components/Image/index.js b/src/components/Image/index.js index 85cec69db..a9ab17adf 100644 --- a/src/components/Image/index.js +++ b/src/components/Image/index.js @@ -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; }; @@ -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] ]); @@ -147,14 +159,18 @@ 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); } + this._updateImageState(STATUS_ERRORED); + if (onError) { + onError({ + nativeEvent: { + error: `Failed to load resource ${resolveAssetSource(source)} (404)` + } + }); + } } _onLoad = (e) => { @@ -189,7 +205,6 @@ class Image extends Component { const styles = StyleSheet.create({ initial: { - alignSelf: 'flex-start', backgroundColor: 'transparent', backgroundPosition: 'center', backgroundRepeat: 'no-repeat',