Skip to content

Commit

Permalink
Merge pull request #4806 from elucaswork/feat/rn-add-filter-bar
Browse files Browse the repository at this point in the history
feat(react-native): Add ability to filter story list from React-Native
  • Loading branch information
Gongreg committed Nov 23, 2018
2 parents 9bdd2fb + d0d0eba commit a8b2649
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 73 deletions.
9 changes: 5 additions & 4 deletions app/react-native/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,16 @@ The next thing you need to do is make Storybook UI visible in your app.
The easiest way to use Storybook is to simply replace your App with the Storybook UI, which is possible by replacing `App.js` with a single line of code:

```js
export default from "./storybook";
export default from './storybook';
```

This will get you up and running quickly, but then you lose your app!
There are multiple options here. for example, you can export conditionally:

```js
import StorybookUI from "./storybook";
import StorybookUI from './storybook';

import App from "./app";
import App from './app';

module.exports = __DEV__ ? StorybookUI : App;
```
Expand Down Expand Up @@ -125,7 +125,8 @@ You can pass these parameters to getStorybookUI call in your storybook entry poi
-- initialize storybook with a specific story. In case a valid object is passed, it will take precedence over `shouldPersistSelection. ex: `{ kind: 'Knobs', story: 'with knobs' }`
shouldPersistSelection: Boolean (true)
-- initialize storybook with the last selected story.`
)
shouldDisableKeyboardAvoidingView: Boolean (false)
-- Disable KeyboardAvoidingView wrapping Storybook's view
}
```

Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,21 @@
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { View, ScrollView, KeyboardAvoidingView, Platform } from 'react-native';
import { View, ScrollView } from 'react-native';

import style from '../style';

export default class Wrapper extends PureComponent {
render() {
const { panels, addonSelected } = this.props;

const keyboardVerticalOffset = Platform.OS === 'ios' ? 60 : 0;

const addonKeys = Object.keys(panels);

return addonKeys.map(id => {
const selected = addonSelected === id;

return (
<View key={id} style={selected ? style.flex : style.invisible}>
<KeyboardAvoidingView
behavior={Platform.OS === 'android' ? null : 'padding'}
keyboardVerticalOffset={keyboardVerticalOffset}
style={style.flex}
>
<ScrollView style={style.flex}>{panels[id].render({ active: selected })}</ScrollView>
</KeyboardAvoidingView>
<ScrollView style={style.flex}>{panels[id].render({ active: selected })}</ScrollView>
</View>
);
});
Expand Down
89 changes: 55 additions & 34 deletions app/react-native/src/preview/components/OnDeviceUI/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { SafeAreaView, Animated, TouchableOpacity } from 'react-native';
import {
Keyboard,
KeyboardAvoidingView,
Platform,
SafeAreaView,
Animated,
TouchableOpacity,
} from 'react-native';
import Events from '@storybook/core-events';

import StoryListView from '../StoryListView';
Expand All @@ -22,6 +29,7 @@ import {
import style from './style';

const ANIMATION_DURATION = 300;
const IS_IOS = Platform.OS === 'ios';

export default class OnDeviceUI extends PureComponent {
constructor(props) {
Expand Down Expand Up @@ -95,10 +103,15 @@ export default class OnDeviceUI extends PureComponent {
// True if swiping between navigator and addons
slideBetweenAnimation: tabOpen + newTabOpen === PREVIEW,
});

// close the keyboard opened from a TextInput from story list or knobs
if (newTabOpen === PREVIEW) {
Keyboard.dismiss();
}
};

render() {
const { stories, events, url, isUIHidden } = this.props;
const { stories, events, url, isUIHidden, shouldDisableKeyboardAvoidingView } = this.props;
const {
tabOpen,
slideBetweenAnimation,
Expand All @@ -121,40 +134,46 @@ export default class OnDeviceUI extends PureComponent {

return (
<SafeAreaView style={style.flex}>
<AbsolutePositionedKeyboardAwareView
onLayout={this.onLayout}
previewHeight={previewHeight}
previewWidth={previewWidth}
<KeyboardAvoidingView
enabled={!shouldDisableKeyboardAvoidingView || tabOpen !== PREVIEW}
behavior={IS_IOS ? 'padding' : null}
style={style.flex}
>
<Animated.View style={previewWrapperStyles}>
<Animated.View style={previewStyles}>
<TouchableOpacity
accessible={false}
style={style.flex}
disabled={tabOpen === PREVIEW}
onPress={this.handleOpenPreview}
>
<StoryView url={url} events={events} selection={selection} storyFn={storyFn} />
</TouchableOpacity>
<AbsolutePositionedKeyboardAwareView
onLayout={this.onLayout}
previewHeight={previewHeight}
previewWidth={previewWidth}
>
<Animated.View style={previewWrapperStyles}>
<Animated.View style={previewStyles}>
<TouchableOpacity
accessible={false}
style={style.flex}
disabled={tabOpen === PREVIEW}
onPress={this.handleOpenPreview}
>
<StoryView url={url} events={events} selection={selection} storyFn={storyFn} />
</TouchableOpacity>
</Animated.View>
</Animated.View>
</Animated.View>
<Panel style={getNavigatorPanelPosition(this.animatedValue, previewWidth)}>
<StoryListView
stories={stories}
events={events}
selectedKind={selection.kind}
selectedStory={selection.story}
/>
</Panel>
<Panel style={getAddonPanelPosition(this.animatedValue, previewWidth)}>
<Addons />
</Panel>
</AbsolutePositionedKeyboardAwareView>
<Navigation
tabOpen={tabOpen}
onChangeTab={this.handleToggleTab}
initialUiVisible={!isUIHidden}
/>
<Panel style={getNavigatorPanelPosition(this.animatedValue, previewWidth)}>
<StoryListView
stories={stories}
events={events}
selectedKind={selection.kind}
selectedStory={selection.story}
/>
</Panel>
<Panel style={getAddonPanelPosition(this.animatedValue, previewWidth)}>
<Addons />
</Panel>
</AbsolutePositionedKeyboardAwareView>
<Navigation
tabOpen={tabOpen}
onChangeTab={this.handleToggleTab}
initialUiVisible={!isUIHidden}
/>
</KeyboardAvoidingView>
</SafeAreaView>
);
}
Expand All @@ -180,11 +199,13 @@ OnDeviceUI.propTypes = {
kind: PropTypes.string.isRequired,
storyFn: PropTypes.func.isRequired,
}),
shouldDisableKeyboardAvoidingView: PropTypes.bool,
};

OnDeviceUI.defaultProps = {
url: '',
tabOpen: 0,
isUIHidden: false,
initialStory: null,
shouldDisableKeyboardAvoidingView: false,
};
82 changes: 60 additions & 22 deletions app/react-native/src/preview/components/StoryListView/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { SectionList, View, Text, TouchableOpacity } from 'react-native';
import { SectionList, Text, TextInput, TouchableOpacity, View } from 'react-native';
import Events from '@storybook/core-events';
import style from './style';

Expand Down Expand Up @@ -40,6 +40,7 @@ export default class StoryListView extends Component {

this.state = {
data: [],
originalData: [],
};

this.storyAddedHandler = this.handleStoryAdded.bind(this);
Expand Down Expand Up @@ -71,10 +72,37 @@ export default class StoryListView extends Component {
}),
{}
);
this.setState({
data,
});

this.setState({ data, originalData: data });
}
};

handleChangeSearchText = text => {
const query = text.trim();
const { originalData: data } = this.state;

if (!query) {
this.setState({ data });
return;
}

const checkValue = value => value.toLowerCase().includes(query.toLowerCase());
const filteredData = data.reduce((acc, story) => {
const hasTitle = checkValue(story.title);
const hasKind = story.data.some(kind => checkValue(kind.name));

if (hasTitle || hasKind) {
acc.push({
...story,
// in case the query matches component's title, all of its stories will be shown
data: !hasTitle ? story.data.filter(kind => checkValue(kind.name)) : story.data,
});
}

return acc;
}, []);

this.setState({ data: filteredData });
};

changeStory(kind, story) {
Expand All @@ -88,24 +116,34 @@ export default class StoryListView extends Component {
const { data } = this.state;

return (
<SectionList
testID="Storybook.ListView"
style={[style.list]}
renderItem={({ item }) => (
<ListItem
title={item.name}
kind={item.kind}
selected={item.kind === selectedKind && item.name === selectedStory}
onPress={() => this.changeStory(item.kind, item.name)}
/>
)}
renderSectionHeader={({ section: { title } }) => (
<SectionHeader title={title} selected={title === selectedKind} />
)}
keyExtractor={(item, index) => item + index}
sections={data}
stickySectionHeadersEnabled={false}
/>
<View style={style.flex}>
<TextInput
clearButtonMode="while-editing"
disableFullscreenUI
onChangeText={this.handleChangeSearchText}
placeholder="Filter"
returnKeyType="search"
style={style.searchBar}
/>
<SectionList
testID="Storybook.ListView"
style={style.flex}
renderItem={({ item }) => (
<ListItem
title={item.name}
kind={item.kind}
selected={item.kind === selectedKind && item.name === selectedStory}
onPress={() => this.changeStory(item.kind, item.name)}
/>
)}
renderSectionHeader={({ section: { title } }) => (
<SectionHeader title={title} selected={title === selectedKind} />
)}
keyExtractor={(item, index) => item + index}
sections={data}
stickySectionHeadersEnabled={false}
/>
</View>
);
}
}
Expand Down
13 changes: 10 additions & 3 deletions app/react-native/src/preview/components/StoryListView/style.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
export default {
list: {
searchBar: {
backgroundColor: '#eee',
borderRadius: 5,
fontSize: 16,
marginHorizontal: 5,
marginVertical: 5,
padding: 5,
},
flex: {
flex: 1,
},
header: {
paddingTop: 5,
paddingBottom: 5,
paddingVertical: 5,
},
headerText: {
fontSize: 20,
Expand Down
1 change: 1 addition & 0 deletions app/react-native/src/preview/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ export default class Preview {
isUIOpen={params.isUIOpen}
tabOpen={params.tabOpen}
initialStory={setInitialStory ? preview._getInitialStory() : null}
shouldDisableKeyboardAvoidingView={params.shouldDisableKeyboardAvoidingView}
/>
);
}
Expand Down

0 comments on commit a8b2649

Please sign in to comment.