Skip to content

Commit 0af6dc0

Browse files
committed
[change] Image 'source' dimensions and RN layout
Adds support for 'width' and 'height' set via the 'source' property. Emulates RN image layout (i.e., no dimensions by default). Fix #10
1 parent c9d401f commit 0af6dc0

File tree

3 files changed

+54
-18
lines changed

3 files changed

+54
-18
lines changed

examples/components/Image/ImageExample.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ const examples = [
218218
render: function() {
219219
return (
220220
<Image
221-
source={{uri: 'http://facebook.github.io/react/img/logo_og.png'}}
221+
source={{ uri: 'http://facebook.github.io/react/img/logo_og.png', width: 1200, height: 630 }}
222222
style={styles.base}
223223
/>
224224
);

src/components/Image/__tests__/index-test.js

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ suite('components/Image', () => {
3434
test('sets background image when value is an object', () => {
3535
const defaultSource = { uri: 'https://google.com/favicon.ico' };
3636
const image = shallow(<Image defaultSource={defaultSource} />);
37-
const backgroundImage = StyleSheet.flatten(image.prop('style')).backgroundImage;
38-
assert(backgroundImage.indexOf(defaultSource.uri) > -1);
37+
const style = StyleSheet.flatten(image.prop('style'));
38+
assert(style.backgroundImage.indexOf(defaultSource.uri) > -1);
3939
});
4040

4141
test('sets background image when value is a string', () => {
@@ -45,13 +45,30 @@ suite('components/Image', () => {
4545
const backgroundImage = StyleSheet.flatten(image.prop('style')).backgroundImage;
4646
assert(backgroundImage.indexOf(defaultSource) > -1);
4747
});
48+
49+
test('sets "height" and "width" styles if missing', () => {
50+
const defaultSource = { uri: 'https://google.com/favicon.ico', height: 10, width: 20 };
51+
const image = mount(<Image defaultSource={defaultSource} />);
52+
const html = image.html();
53+
assert(html.indexOf('height: 10px') > -1);
54+
assert(html.indexOf('width: 20px') > -1);
55+
});
56+
57+
test('does not override "height" and "width" styles', () => {
58+
const defaultSource = { uri: 'https://google.com/favicon.ico', height: 10, width: 20 };
59+
const image = mount(<Image defaultSource={defaultSource} style={{ height: 20, width: 40 }} />);
60+
const html = image.html();
61+
assert(html.indexOf('height: 20px') > -1);
62+
assert(html.indexOf('width: 40px') > -1);
63+
});
4864
});
4965

5066
test('prop "onError"', function (done) {
5167
this.timeout(5000);
52-
mount(<Image onError={onError} source={{ uri: 'https://google.com/favicon.icox' }} />);
68+
const image = mount(<Image onError={onError} source={{ uri: 'https://google.com/favicon.icox' }} />);
5369
function onError(e) {
54-
assert.equal(e.nativeEvent.type, 'error');
70+
assert.ok(e.nativeEvent.error);
71+
image.unmount();
5572
done();
5673
}
5774
});
@@ -63,6 +80,7 @@ suite('components/Image', () => {
6380
assert.equal(e.nativeEvent.type, 'load');
6481
const hasBackgroundImage = (image.html()).indexOf('url(&quot;https://google.com/favicon.ico&quot;)') > -1;
6582
assert.equal(hasBackgroundImage, true);
83+
image.unmount();
6684
done();
6785
}
6886
});
@@ -74,6 +92,7 @@ suite('components/Image', () => {
7492
assert.ok(true);
7593
const hasBackgroundImage = (image.html()).indexOf('url(&quot;https://google.com/favicon.ico&quot;)') > -1;
7694
assert.equal(hasBackgroundImage, true);
95+
image.unmount();
7796
done();
7897
}
7998
});
@@ -121,21 +140,23 @@ suite('components/Image', () => {
121140

122141
test('sets background image when value is an object', (done) => {
123142
const source = { uri: 'https://google.com/favicon.ico' };
124-
mount(<Image onLoad={onLoad} source={source} />);
143+
const image = mount(<Image onLoad={onLoad} source={source} />);
125144
function onLoad(e) {
126145
const src = e.nativeEvent.target.src;
127146
assert.equal(src, source.uri);
147+
image.unmount();
128148
done();
129149
}
130150
});
131151

132152
test('sets background image when value is a string', (done) => {
133153
// emulate require-ed asset
134154
const source = 'https://google.com/favicon.ico';
135-
mount(<Image onLoad={onLoad} source={source} />);
155+
const image = mount(<Image onLoad={onLoad} source={source} />);
136156
function onLoad(e) {
137157
const src = e.nativeEvent.target.src;
138158
assert.equal(src, source);
159+
image.unmount();
139160
done();
140161
}
141162
});

src/components/Image/index.js

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,20 @@ const STATUS_IDLE = 'IDLE';
1717

1818
const ImageSourcePropType = PropTypes.oneOfType([
1919
PropTypes.shape({
20-
uri: PropTypes.string.isRequired
20+
height: PropTypes.number,
21+
uri: PropTypes.string.isRequired,
22+
width: PropTypes.number
2123
}),
2224
PropTypes.string
2325
]);
2426

27+
const resolveAssetDimensions = (source) => {
28+
if (typeof source === 'object') {
29+
const { height, width } = source;
30+
return { height, width };
31+
}
32+
};
33+
2534
const resolveAssetSource = (source) => {
2635
return ((typeof source === 'object') ? source.uri : source) || null;
2736
};
@@ -93,12 +102,15 @@ class Image extends Component {
93102
} = this.props;
94103

95104
const displayImage = resolveAssetSource(!isLoaded ? defaultSource : source);
105+
const imageSizeStyle = resolveAssetDimensions(!isLoaded ? defaultSource : source);
96106
const backgroundImage = displayImage ? `url("${displayImage}")` : null;
97-
const flatStyle = StyleSheet.flatten(this.props.style);
98-
const resizeMode = this.props.resizeMode || flatStyle.resizeMode || ImageResizeMode.cover;
107+
const originalStyle = StyleSheet.flatten(this.props.style);
108+
const resizeMode = this.props.resizeMode || originalStyle.resizeMode || ImageResizeMode.cover;
109+
99110
const style = StyleSheet.flatten([
100111
styles.initial,
101-
flatStyle,
112+
imageSizeStyle,
113+
originalStyle,
102114
backgroundImage && { backgroundImage },
103115
resizeModeStyles[resizeMode]
104116
]);
@@ -147,14 +159,18 @@ class Image extends Component {
147159
}
148160
}
149161

150-
_onError = (e) => {
151-
const { onError } = this.props;
152-
const event = { nativeEvent: e };
153-
162+
_onError = () => {
163+
const { onError, source } = this.props;
154164
this._destroyImageLoader();
155-
this._updateImageState(STATUS_ERRORED);
156165
this._onLoadEnd();
157-
if (onError) { onError(event); }
166+
this._updateImageState(STATUS_ERRORED);
167+
if (onError) {
168+
onError({
169+
nativeEvent: {
170+
error: `Failed to load resource ${resolveAssetSource(source)} (404)`
171+
}
172+
});
173+
}
158174
}
159175

160176
_onLoad = (e) => {
@@ -189,7 +205,6 @@ class Image extends Component {
189205

190206
const styles = StyleSheet.create({
191207
initial: {
192-
alignSelf: 'flex-start',
193208
backgroundColor: 'transparent',
194209
backgroundPosition: 'center',
195210
backgroundRepeat: 'no-repeat',

0 commit comments

Comments
 (0)