Skip to content

Commit

Permalink
Added filtering to RNTester example screens (#22777)
Browse files Browse the repository at this point in the history
Summary:
This PR adds filtering functionality to individual example screens of RNTester. This is useful for Detox testing of RNTester, since Detox requires elements to be visible on the screen before they can be interacted with. Instead of needing to scroll an arbitrary amount, the test can enter the name of the example to be tested, just as is done on the main screen. This will lead to simpler and more reliable E2E tests for long example screens. This PR doesn't add any automated tests using the filter; those will be added in a separate PR.

This is implemented by extracting the existing filtering functionality out of `RNTesterExampleList` into a shared `RNTesterExampleFilter` component that can be used both within `RNTesterExampleList` (the main screen) and `RNTesterExampleContainer` (the example screen).

![simulator screen shot - iphone 8 - 2018-12-24 at 08 22 46](https://user-images.githubusercontent.com/15832198/50401564-4273a300-0755-11e9-9120-9bf8fbb70261.png)

![simulator screen shot - iphone 8 - 2018-12-24 at 08 22 51](https://user-images.githubusercontent.com/15832198/50401566-44d5fd00-0755-11e9-9637-6e5ddce1c476.png)

Changelog:
----------

[General] [Added] - Added filtering to RNTester example screens
Pull Request resolved: #22777

Reviewed By: TheSavior

Differential Revision: D13561744

Pulled By: rickhanlonii

fbshipit-source-id: cb120626a8e2b8440f88b871557c0b92fbef5edc
  • Loading branch information
CodingItWrong authored and grabbou committed Jan 3, 2019
1 parent 68f6325 commit bd79303
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 77 deletions.
24 changes: 23 additions & 1 deletion RNTester/js/RNTesterExampleContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
const React = require('react');
const {Platform} = require('react-native');
const RNTesterBlock = require('./RNTesterBlock');
const RNTesterExampleFilter = require('./RNTesterExampleFilter');
const RNTesterPage = require('./RNTesterPage');

class RNTesterExampleContainer extends React.Component {
Expand All @@ -37,9 +38,30 @@ class RNTesterExampleContainer extends React.Component {
return <this.props.module />;
}

if (this.props.module.examples.length === 1) {
const Example = this.props.module.examples[0].render;
return <Example />;
}

const filter = ({example, filterRegex}) => filterRegex.test(example.title);

const sections = [
{
data: this.props.module.examples,
title: 'EXAMPLES',
key: 'e',
},
];

return (
<RNTesterPage title={this.props.title}>
{this.props.module.examples.map(this.renderExample)}
<RNTesterExampleFilter
sections={sections}
filter={filter}
render={({filteredSections}) =>
filteredSections[0].data.map(this.renderExample)
}
/>
</RNTesterPage>
);
}
Expand Down
102 changes: 102 additions & 0 deletions RNTester/js/RNTesterExampleFilter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow
*/

'use strict';

const React = require('react');
const StyleSheet = require('StyleSheet');
const TextInput = require('TextInput');
const View = require('View');

type Props = {
filter: Function,
render: Function,
sections: Object,
disableSearch?: boolean,
};

type State = {
filter: string,
};

class RNTesterExampleFilter extends React.Component<Props, State> {
state = {filter: ''};

render() {
const filterText = this.state.filter;
let filterRegex = /.*/;

try {
filterRegex = new RegExp(String(filterText), 'i');
} catch (error) {
console.warn(
'Failed to create RegExp: %s\n%s',
filterText,
error.message,
);
}

const filter = example =>
this.props.disableSearch || this.props.filter({example, filterRegex});

const filteredSections = this.props.sections.map(section => ({
...section,
data: section.data.filter(filter),
}));

return (
<View>
{this._renderTextInput()}
{this.props.render({filteredSections})}
</View>
);
}

_renderTextInput(): ?React.Element<any> {
if (this.props.disableSearch) {
return null;
}
return (
<View style={styles.searchRow}>
<TextInput
autoCapitalize="none"
autoCorrect={false}
clearButtonMode="always"
onChangeText={text => {
this.setState(() => ({filter: text}));
}}
placeholder="Search..."
underlineColorAndroid="transparent"
style={styles.searchTextInput}
testID="explorer_search"
value={this.state.filter}
/>
</View>
);
}
}

const styles = StyleSheet.create({
searchRow: {
backgroundColor: '#eeeeee',
padding: 10,
},
searchTextInput: {
backgroundColor: 'white',
borderColor: '#cccccc',
borderRadius: 3,
borderWidth: 1,
paddingLeft: 8,
paddingVertical: 0,
height: 35,
},
});

module.exports = RNTesterExampleFilter;
106 changes: 30 additions & 76 deletions RNTester/js/RNTesterExampleList.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ const React = require('react');
const SectionList = require('SectionList');
const StyleSheet = require('StyleSheet');
const Text = require('Text');
const TextInput = require('TextInput');
const TouchableHighlight = require('TouchableHighlight');
const RNTesterActions = require('./RNTesterActions');
const RNTesterExampleFilter = require('./RNTesterExampleFilter');
const View = require('View');

/* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue was found when
* making Flow check .android.js files. */
import type {RNTesterExample} from './RNTesterList.ios';
import type {TextStyleProp, ViewStyleProp} from 'StyleSheet';
import type {ViewStyleProp} from 'StyleSheet';

type Props = {
onNavigate: Function,
Expand Down Expand Up @@ -69,58 +69,48 @@ const renderSectionHeader = ({section}) => (
);

class RNTesterExampleList extends React.Component<Props, $FlowFixMeState> {
state = {filter: ''};

render() {
const filterText = this.state.filter;
let filterRegex = /.*/;

try {
filterRegex = new RegExp(String(filterText), 'i');
} catch (error) {
console.warn(
'Failed to create RegExp: %s\n%s',
filterText,
error.message,
);
}

const filter = example =>
const filter = ({example, filterRegex}) =>
/* $FlowFixMe(>=0.68.0 site=react_native_fb) This comment suppresses an
* error found when Flow v0.68 was deployed. To see the error delete this
* comment and run Flow. */
this.props.disableSearch ||
(filterRegex.test(example.module.title) &&
(!Platform.isTV || example.supportsTVOS));
* error found when Flow v0.68 was deployed. To see the error delete this
* comment and run Flow. */
filterRegex.test(example.module.title) &&
(!Platform.isTV || example.supportsTVOS);

const sections = [
{
data: this.props.list.ComponentExamples.filter(filter),
data: this.props.list.ComponentExamples,
title: 'COMPONENTS',
key: 'c',
},
{
data: this.props.list.APIExamples.filter(filter),
data: this.props.list.APIExamples,
title: 'APIS',
key: 'a',
},
];

return (
<View style={[styles.listContainer, this.props.style]}>
{this._renderTitleRow()}
{this._renderTextInput()}
<SectionList
ItemSeparatorComponent={ItemSeparator}
contentContainerStyle={{backgroundColor: 'white'}}
style={styles.list}
<RNTesterExampleFilter
sections={sections}
renderItem={this._renderItem}
enableEmptySections={true}
itemShouldUpdate={this._itemShouldUpdate}
keyboardShouldPersistTaps="handled"
automaticallyAdjustContentInsets={false}
keyboardDismissMode="on-drag"
renderSectionHeader={renderSectionHeader}
filter={filter}
render={({filteredSections}) => (
<SectionList
ItemSeparatorComponent={ItemSeparator}
contentContainerStyle={styles.sectionListContentContainer}
style={styles.list}
sections={filteredSections}
renderItem={this._renderItem}
enableEmptySections={true}
itemShouldUpdate={this._itemShouldUpdate}
keyboardShouldPersistTaps="handled"
automaticallyAdjustContentInsets={false}
keyboardDismissMode="on-drag"
renderSectionHeader={renderSectionHeader}
/>
)}
/>
</View>
);
Expand Down Expand Up @@ -162,32 +152,6 @@ class RNTesterExampleList extends React.Component<Props, $FlowFixMeState> {
);
}

_renderTextInput(): ?React.Element<any> {
/* $FlowFixMe(>=0.68.0 site=react_native_fb) This comment suppresses an
* error found when Flow v0.68 was deployed. To see the error delete this
* comment and run Flow. */
if (this.props.disableSearch) {
return null;
}
return (
<View style={styles.searchRow}>
<TextInput
autoCapitalize="none"
autoCorrect={false}
clearButtonMode="always"
onChangeText={text => {
this.setState(() => ({filter: text}));
}}
placeholder="Search..."
underlineColorAndroid="transparent"
style={styles.searchTextInput}
testID="explorer_search"
value={this.state.filter}
/>
</View>
);
}

_handleRowPress(exampleKey: string): void {
this.props.onNavigate(RNTesterActions.ExampleAction(exampleKey));
}
Expand Down Expand Up @@ -225,6 +189,9 @@ const styles = StyleSheet.create({
height: StyleSheet.hairlineWidth,
backgroundColor: 'rgb(217, 217, 217)',
},
sectionListContentContainer: {
backgroundColor: 'white',
},
rowTitleText: {
fontSize: 17,
fontWeight: '500',
Expand All @@ -234,19 +201,6 @@ const styles = StyleSheet.create({
color: '#888888',
lineHeight: 20,
},
searchRow: {
backgroundColor: '#eeeeee',
padding: 10,
},
searchTextInput: {
backgroundColor: 'white',
borderColor: '#cccccc',
borderRadius: 3,
borderWidth: 1,
paddingLeft: 8,
paddingVertical: 0,
height: 35,
},
});

module.exports = RNTesterExampleList;

0 comments on commit bd79303

Please sign in to comment.