Skip to content

Commit

Permalink
Add demo mocking requests made with Fetch API
Browse files Browse the repository at this point in the history
  • Loading branch information
phelipetls committed Feb 26, 2022
1 parent 456b50e commit 49c7ffd
Show file tree
Hide file tree
Showing 8 changed files with 188 additions and 3 deletions.
28 changes: 28 additions & 0 deletions src/components/FilmCard/FilmCard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';
import {View, Text, StyleSheet} from 'react-native';

export const FilmCard = ({film}) => {
return (
<View style={styles.filmCard}>
<Text style={styles.filmTitle}>{film.title}</Text>
<Text>{film.opening_crawl}</Text>
</View>
);
};

const styles = StyleSheet.create({
filmCard: {
borderRadius: 8,
borderWidth: 2,
borderColor: '#c7d2fe',
backgroundColor: '#e0e7ff',
paddingHorizontal: 16,
paddingVertical: 24,
marginBottom: 16,
},
filmTitle: {
color: '#1e40af',
fontSize: 24,
marginBottom: 8,
},
});
1 change: 1 addition & 0 deletions src/components/FilmCard/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './FilmCard';
1 change: 1 addition & 0 deletions src/components/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './FilmCard';
61 changes: 61 additions & 0 deletions src/demos/fetch/App.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React, {useState, useEffect} from 'react';
import {ScrollView, Text, StyleSheet} from 'react-native';

import {FilmCard} from '../../components';

function useFetchFilms() {
const [status, setStatus] = useState('idle');
const [data, setData] = useState([]);

useEffect(() => {
setStatus('loading');

fetch('https://swapi.dev/api/films/')
.then(res => {
if (!res.ok) {
throw new Error(res.statusText);
}
return res;
})
.then(res => res.json())
.then(({results}) => {
setStatus('success');
setData(results);
})
.catch(() => {
setStatus('error');
});
}, []);

return {
status,
data,
};
}

export const App = () => {
const {status, data: films} = useFetchFilms();

if (status === 'loading') {
return <Text>Fetching Star Wars data...</Text>;
}

if (status === 'error') {
return <Text>Could not fetch Star Wars data</Text>;
}

return (
<ScrollView contentContainerStyle={styles.container}>
{films.map(film => (
<FilmCard key={film.episode_id} film={film} />
))}
</ScrollView>
);
};

const styles = StyleSheet.create({
container: {
flex: 1,
gap: 30,
},
});
58 changes: 58 additions & 0 deletions src/demos/fetch/App.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React from 'react';
import {rest} from 'msw';
import {App} from './App';
import {storiesOf} from '@storybook/react-native';

export default {
title: 'Demos/Fetch',
component: App,
};

export const DefaultBehavior = () => <App />;

const MockTemplate = () => <App />;

const films = [
{
title: 'A New Hope',
episode_id: 4,
opening_crawl:
'(Mocked) Rebel spaceships have won their first victory against the evil Galactic Empire.',
},
{
title: 'Empire Strikes Back',
episode_id: 5,
opening_crawl:
'(Mocked) Imperial troops are pursuing the Rebel forces across the galaxy.',
},
{
title: 'Return of the Jedi',
episode_id: 6,
opening_crawl:
'(Mocked) Luke Skywalker has returned to his home planet of Tatooine to rescue Han Solo.',
},
];

storiesOf('Demos/Fetch', module)
.add('MockedSuccess', MockTemplate, {
msw: {
handlers: [
rest.get('https://swapi.dev/api/films/', (_, res, ctx) => {
return res(
ctx.json({
results: films,
}),
);
}),
],
},
})
.add('MockedError', MockTemplate, {
msw: {
handlers: [
rest.get('https://swapi.dev/api/films/', (_, res, ctx) => {
return res(ctx.delay(800), ctx.status(403));
}),
],
},
});
6 changes: 5 additions & 1 deletion storybook/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@ import AsyncStorage from '@react-native-async-storage/async-storage';

import {getStorybookUI, configure, addDecorator} from '@storybook/react-native';
import {withKnobs} from '@storybook/addon-knobs';
import {initialize, withMsw} from './mswDecorator';

import './rn-addons';

// enables knobs for all stories
addDecorator(withKnobs);

initialize();
addDecorator(withMsw);

// import stories
configure(() => {
require('./stories');
require('../src/demos/fetch/App.stories');
}, module);

// Refer to https://github.com/storybookjs/react-native/tree/master/app/react-native#getstorybookui-options
Expand Down
34 changes: 34 additions & 0 deletions storybook/mswDecorator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import 'react-native-url-polyfill/auto';
import {setupServer} from 'msw/native';

const server = setupServer();

export const initialize = () => {
// Do not warn or error out if a non-mocked request happens.
// If we don't use this, Storybook will be spammy about requests made to
// fetch the JS bundle etc.
server.listen({onUnhandledRequest: 'bypass'});
};

export const withMsw = (storyFn, {parameters: {msw}}) => {
server.resetHandlers();

if (msw) {
if (Array.isArray(msw) && msw.length > 0) {
// Support an Array of request handlers (backwards compatibility).
server.use(...msw);
} else if ('handlers' in msw && msw.handlers) {
// Support an Array named request handlers handlers
// or an Object of named request handlers with named arrays of handlers
const handlers = Object.values(msw.handlers)
.filter(Boolean)
.reduce((handlers, handlersList) => handlers.concat(handlersList), []);

if (handlers.length > 0) {
server.use(...handlers);
}
}
}

return storyFn();
};
2 changes: 0 additions & 2 deletions storybook/stories/index.js

This file was deleted.

0 comments on commit 49c7ffd

Please sign in to comment.