Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added support for image source object #1843

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion packages/docs/src/components/Image/Image.stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ An accessible and responsive image component.
| onLoad | ?Function | false |
| onLoadEnd | ?Function | false |
| onLoadStart | ?Function | false |
| onProgress | ?Function | false |
| resizeMode | ?ResizeMode | 'cover' |
| source | Source | false |
| style | ?Style | |
Expand Down Expand Up @@ -72,6 +73,16 @@ Called when load completes successfully.
</Story>
</Preview>

### onProgress

Invoked on download progress.

<Preview withSource='none'>
<Story name="onProgress">
<Stories.onProgress />
</Story>
</Preview>

### resizeMode

Determines how to resize the image when the frame doesn't match the raw image dimensions.
Expand All @@ -91,7 +102,7 @@ type ResizeMode = 'center' | 'contain' | 'cover' | 'none' | 'repeat' | 'stretch'
The source `uri` is a string representing the resource identifier for the image, which could be an http address or a base64 encoded image.

```js
type Source = { uri: string, width: number, height: number }
type Source = { uri: string, width: number, height: number, method?: string, headers?: object }
```

<Preview withSource='none'>
Expand Down
95 changes: 73 additions & 22 deletions packages/docs/src/components/Image/examples/NetworkImage.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { ActivityIndicator, Image, Text, View } from 'react-native';
class NetworkImageExample extends PureComponent {
state = {
error: false,
loading: false
loading: false,
messages: []
};

static defaultProps = {
Expand All @@ -29,44 +30,94 @@ class NetworkImageExample extends PureComponent {
onLoad={this._handleLoad}
onLoadEnd={this._handleLoadEnd}
onLoadStart={this._handleLoadStart}
onProgress={this._handleProgress}
source={this.props.source}
style={helpers.styles.base}
/>
{this.state.message && <Text style={helpers.styles.marginTop}>{this.state.message}</Text>}
{this.state.messages.map((message, index) => {
return (
<Text key={index} style={helpers.styles.marginTop}>
{message}
</Text>
);
})}
</View>
);
}

_handleError = e => {
const nextState = { loading: false };
if (this.props.logMethod === 'onError') {
nextState.message = `✘ onError ${JSON.stringify(e.nativeEvent)}`;
}
this.setState(() => nextState);
this.setState(state => {
const messages = [...state.messages];
if (this.props.logMethod === 'onError') {
messages.push(`✘ onError ${JSON.stringify(e.nativeEvent)}`);
}

return {
loading: false,
messages
};
});
};

_handleLoad = () => {
const nextState = { loading: false };
if (this.props.logMethod === 'onLoad') {
nextState.message = '✔ onLoad';
}
this.setState(() => nextState);
this.setState(state => {
const messages = [...state.messages];
if (this.props.logMethod === 'onLoad') {
messages.push('✔ onLoad');
}

return {
loading: false,
messages
};
});
};

_handleLoadEnd = () => {
const nextState = { loading: false };
if (this.props.logMethod === 'onLoadEnd') {
nextState.message = '✔ onLoadEnd';
}
this.setState(() => nextState);
this.setState(state => {
const messages = [...state.messages];
if (this.props.logMethod === 'onLoadEnd') {
messages.push('✔ onLoadEnd');
}

return {
loading: false,
messages
};
});
};

_handleLoadStart = () => {
const nextState = { loading: true };
if (this.props.logMethod === 'onLoadStart') {
nextState.message = '✔ onLoadStart';
}
this.setState(() => nextState);
this.setState(state => {
const messages = [...state.messages];
if (this.props.logMethod === 'onLoadStart') {
messages.push('✔ onLoadStart');
}

return {
loading: false,
messages
};
});
};

_handleProgress = e => {
this.setState(state => {
const messages = [...state.messages];
if (this.props.logMethod === 'onProgress') {
const { loaded, total } = e.nativeEvent;
messages.push(
`✔ onProgress ${JSON.stringify({
loaded,
total
})}`
);
}

return {
messages
};
});
};
}

Expand Down
8 changes: 8 additions & 0 deletions packages/docs/src/components/Image/examples/OnProgress.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { createUncachedURI } from '../helpers';
import NetworkImage from './NetworkImage';
import React from 'react';
import sources from '../sources';

export default function OnProgress() {
return <NetworkImage logMethod="onProgress" source={createUncachedURI(sources.dynamic)} />;
}
10 changes: 10 additions & 0 deletions packages/docs/src/components/Image/examples/Source.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@ export default function Source() {
<Image source={sources.dataSvg} style={styles.image} />
</View>
</View>
<View style={styles.row}>
<View style={styles.column}>
<Text style={styles.text}>WebP</Text>
<Image source={sources.webP} style={styles.image} />
</View>
<View style={styles.column}>
<Text style={styles.text}>Dynamic (POST)</Text>
<Image source={sources.dynamic} style={styles.image} />
</View>
</View>
</React.Fragment>
);
}
Expand Down
1 change: 1 addition & 0 deletions packages/docs/src/components/Image/examples/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export { default as onError } from './OnError';
export { default as onLoad } from './OnLoad';
export { default as onLoadEnd } from './OnLoadEnd';
export { default as onLoadStart } from './OnLoadStart';
export { default as onProgress } from './OnProgress';
export { default as resizeMode } from './ResizeMode';
export { default as source } from './Source';
export { default as styleBoxShadow } from './StyleBoxShadow';
Expand Down
10 changes: 9 additions & 1 deletion packages/docs/src/components/Image/sources/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -328,14 +328,14 @@ exports[`components/Image prop "style" removes other unsupported View styles 1`]
>
<div
class="css-view-1dbjc4n r-backgroundColor-1niwhzg r-backgroundPosition-vvn4in r-backgroundRepeat-u6sd8q r-backgroundSize-4gszlv r-bottom-1p0dtai r-height-1pi2tsx r-left-1d2f490 r-position-u8s1d r-right-zchlnj r-top-ipm5af r-width-13qz1uu r-zIndex-1wyyakw"
style="filter: url(#tint-57);"
style="filter: url(#tint-55);"
/>
<svg
style="position: absolute; height: 0px; visibility: hidden; width: 0px;"
>
<defs>
<filter
id="tint-57"
id="tint-55"
>
<feflood
flood-color="blue"
Expand Down Expand Up @@ -377,7 +377,7 @@ exports[`components/Image prop "style" supports "tintcolor" property (convert to
>
<div
class="css-view-1dbjc4n r-backgroundColor-1niwhzg r-backgroundPosition-vvn4in r-backgroundRepeat-u6sd8q r-backgroundSize-4gszlv r-bottom-1p0dtai r-height-1pi2tsx r-left-1d2f490 r-position-u8s1d r-right-zchlnj r-top-ipm5af r-width-13qz1uu r-zIndex-1wyyakw"
style="background-image: url(https://google.com/favicon.ico); filter: url(#tint-56);"
style="background-image: url(https://google.com/favicon.ico); filter: url(#tint-54);"
/>
<img
alt=""
Expand All @@ -390,7 +390,7 @@ exports[`components/Image prop "style" supports "tintcolor" property (convert to
>
<defs>
<filter
id="tint-56"
id="tint-54"
>
<feflood
flood-color="red"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import PixelRatio from '../../PixelRatio';
import React from 'react';
import { render } from '@testing-library/react';

const originalImage = window.Image;
const OriginalImage = window.Image;

describe('components/Image', () => {
beforeEach(() => {
Expand All @@ -18,7 +18,7 @@ describe('components/Image', () => {
});

afterEach(() => {
window.Image = originalImage;
window.Image = OriginalImage;
});

test('prop "accessibilityLabel"', () => {
Expand Down Expand Up @@ -91,8 +91,9 @@ describe('components/Image', () => {
describe('prop "onLoad"', () => {
test('is called after image is loaded from network', () => {
jest.useFakeTimers();
const uri = 'https://test.com/img.jpg';
ImageLoader.load = jest.fn().mockImplementation((_, onLoad, onError) => {
onLoad();
onLoad(uri);
});
const onLoadStartStub = jest.fn();
const onLoadStub = jest.fn();
Expand All @@ -102,7 +103,7 @@ describe('components/Image', () => {
onLoad={onLoadStub}
onLoadEnd={onLoadEndStub}
onLoadStart={onLoadStartStub}
source="https://test.com/img.jpg"
source={uri}
/>
);
jest.runOnlyPendingTimers();
Expand All @@ -111,13 +112,13 @@ describe('components/Image', () => {

test('is called after image is loaded from cache', () => {
jest.useFakeTimers();
const uri = 'https://test.com/img.jpg';
ImageLoader.load = jest.fn().mockImplementation((_, onLoad, onError) => {
onLoad();
onLoad(uri);
});
const onLoadStartStub = jest.fn();
const onLoadStub = jest.fn();
const onLoadEndStub = jest.fn();
const uri = 'https://test.com/img.jpg';
ImageUriCache.add(uri);
render(
<Image
Expand Down Expand Up @@ -238,7 +239,7 @@ describe('components/Image', () => {
test('is set immediately if the image was preloaded', () => {
const uri = 'https://yahoo.com/favicon.ico';
ImageLoader.load = jest.fn().mockImplementationOnce((_, onLoad, onError) => {
onLoad();
onLoad(uri);
});
return Image.prefetch(uri).then(() => {
const source = { uri };
Expand Down Expand Up @@ -285,7 +286,7 @@ describe('components/Image', () => {
const { container } = render(<Image defaultSource={{ uri: defaultUri }} source={{ uri }} />);
expect(container.firstChild).toMatchSnapshot();
act(() => {
loadCallback();
loadCallback(uri);
});
expect(container.firstChild).toMatchSnapshot();
});
Expand Down
Loading