Skip to content

Commit 8991705

Browse files
richardbarrell-calviumbenvium
authored andcommitted
Render passed-in components. (#17)
* Render passed-in components, passing a `closeThisTest` prop. Makes working around #14 possible. * Add vertical space at the bottom of component tests, so components don't get covered by the 'Close' button. * Bump version 0.4.0
1 parent 0041552 commit 8991705

File tree

4 files changed

+82
-20
lines changed

4 files changed

+82
-20
lines changed

README.md

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# react-native-component-viewer
22
A searchable list of components or scenes in your app. Handy for tweaking layout or design without needing to navigate your
3-
app to get there.
3+
app to get there.
44

55
Especially useful for accessing screens that are hard to get to, or for testing the design of screen in specific difficult-to-test situations (e.g. server failure).
66

@@ -85,26 +85,78 @@ addSceneTest(<MySceneComponent items={[]}/>, {name: 'MySceneComponent', title: '
8585
addSceneTest(<MySceneComponent items={['more','test','data']}/>, {name: 'MySceneComponent', title: 'Three items'});
8686
```
8787

88+
# The component parameter
89+
90+
When you add a scene or component test, you can either pass in a rendered component, like either of the following:
91+
92+
```js
93+
addSceneTest(<MySceneComponent />, {name: 'MySceneComponent'});
94+
addComponentTest(<MyComponent />, {name: 'MyComponent'});
95+
```
96+
97+
or you can pass in an unrendered component, which Component Viewer will render for you. For example, with functional components:
98+
99+
```js
100+
addSceneTest(({closeThisTest}) => <MySceneComponent onClose={closeThisTest} />, {name: 'MySceneComponent'});
101+
addComponentTest(({closeThisTest}) => <MyComponent onClose={closeThisTest} />, {name: 'MyComponent'});
102+
```
103+
104+
If you pass in an anonymous functional component, you should always supply an explicit `name` option as above, because Component Viewer will not be able to reliably find out the name of your component.
105+
106+
If you pass in an unrendered component, Component Viewer will render it with the following props:
107+
* closeThisTest: a 0-argument function; when called it closes the current test.
108+
109+
Note that the list of props that Component Viewer will supply to components may be added to in future, so it's best to use a functional component that explicitly picks out the ones you want to use.
110+
88111
## Making space for header and footer bars
89112

90113
In real app, your screens will normally have a header (and perhaps a footer). This means your actual component has a smaller space to render in.
91114

92115
To support this there's an optional property `wrapperStyle` on the second parameter to `addSceneTest`. This is a standard React Native View style.
93116

94117
Use the `padding` properties to adjust the rendering inset to your scene. You may want to store the style somewhere central in your app so
95-
you don't have to type it out each time you use `addTestScene`.
118+
you don't have to type it out each time you use `addTestScene`.
96119

97120
Example:
98121

99122
```js
100-
addSceneTest(<MySceneComponent items={['more','test','data']}/>,
101-
{
123+
addSceneTest(<MySceneComponent items={['more','test','data']}/>,
124+
{
102125
name: 'MySceneComponent',
103-
title: 'Three items',
126+
title: 'Three items',
104127
wrapperStyle: {paddingTop: 44, backgroundColor: 'black'}),
105128
}
106129
```
107130
131+
## Testing modals
132+
133+
If you have are testing a component with a <Modal> on it, the modal may cover the 'Close' button and make it impossible to go back to the list. If your modal has its own close button then you can work around this by:
134+
* registering your modal as a scene using addSceneTest, and
135+
* calling the `closeThisTest` callback from your modal's close button.
136+
137+
For example, if you write:
138+
139+
```js
140+
import React from 'react';
141+
import {addSceneTest} from 'react-native-component-viewer';
142+
import {Modal, Text, View, Button} from 'react-native';
143+
144+
const MyModal = ({onRequestClose, visible}) => ```
145+
<Modal visible={visible} onRequestClose={onRequestClose}>
146+
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#fff'}}>
147+
<Text>This is a modal pop-up!</Text>
148+
<Button title="Close this modal" onPress={onRequestClose} />
149+
</View>
150+
</Modal>
151+
);
152+
153+
addSceneTest(({closeThisTest}) => <MyModal onRequestClose={closeThisTest} visible />, {
154+
name: 'MyModal',
155+
});
156+
```
157+
158+
then the 'Close this modal' button inside the modal will take you back to the menu.
159+
108160
# Registering Test Components
109161

110162
When testing a component such as a Button, you'll most likely want to view all the possible states of the button on a single screen
@@ -133,14 +185,15 @@ addComponentTest(
133185
```
134186
135187
Multiple tests for a single component appear in the ComponentViewer list as a single entry. Tapping the entry displays a ScrollView containing all your tests.
136-
188+
189+
137190
# Usage with Redux
138191
139192
If you're using the `react-redux` `connect` method, make sure you pass the 'unconnected' version of the component to `addTestScene`, e.g.:
140193

141194
```js
142195
class MyComponent extends React.Component {
143-
//...
196+
//...
144197
}
145198
146199
export default connect(
@@ -150,7 +203,7 @@ export default connect(
150203
dispatch => ({
151204
//.. mapDispatchToProps
152205
}))(MyComponent);
153-
206+
154207
// we export the connected component, but pass the
155208
// unconnected component to addTestScene
156209
addTestScene(<MyComponent {...testData}/>);
@@ -160,13 +213,6 @@ This way you can make sure your test scenes are completely independent of the Re
160213

161214
# Other options
162215

163-
By default the list will save and restore your last search term via `AsyncStorage`. This is useful if you have many registered tests, as it saves you from having to type the search term every time you reload.
216+
By default the list will save and restore your last search term via `AsyncStorage`. This is useful if you have many registered tests, as it saves you from having to type the search term every time you reload.
164217

165218
Saving your last search can be disabled by setting the optional `saveSearch` prop to `false` on `ComponentViewer`. The default is `true`.
166-
167-
168-
169-
170-
171-
172-

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-native-component-viewer",
3-
"version": "0.3.0",
3+
"version": "0.4.0",
44
"description": "A searchable list of components or scenes in your app. Handy for tweaking layout or design without using the app",
55
"main": "index.js",
66
"scripts": {},

src/DebugSceneList.js

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ const styles = StyleSheet.create({
2222
componentModalScrollView: {
2323
alignItems: 'center',
2424
},
25+
componentModalSpacer: {
26+
height: 30,
27+
},
2528
componentWrapper: {
2629
alignSelf: 'stretch',
2730
},
@@ -119,13 +122,25 @@ class DebugSceneList extends Component {
119122
removeUpdateListener(this.handleTestsUpdated);
120123
}
121124

125+
renderWithProps(Component) {
126+
// If we're passed a rendered component, use it as-is.
127+
if (!(Component instanceof Function)) {
128+
return Component;
129+
}
130+
// If we're passed an unrendered component, render it.
131+
// This list of props might grow in future.
132+
return (
133+
<Component closeThisTest={this.onHideScene} />
134+
);
135+
}
136+
122137
renderSceneModal() {
123138
return (
124139
<View
125140
key={'component-viewer-modal'}
126141
style={[styles.selectedComponentWrapper, this.state.selectedItem && this.state.selectedItem.wrapperStyle]}
127142
>
128-
{this.state.selectedItem.component}
143+
{this.renderWithProps(this.state.selectedItem.component)}
129144
</View>
130145
);
131146
}
@@ -141,9 +156,10 @@ class DebugSceneList extends Component {
141156
{selectedItem.states.map((i: RegisteredItemType) => [
142157
<Text key={`${i.name}_${i.title}_title`} style={styles.componentTitle}>{i.title}</Text>,
143158
<View key={`${i.name}_${i.title}_component`} style={[styles.componentWrapper, i.wrapperStyle]}>
144-
{i.component}
159+
{this.renderWithProps(i.component)}
145160
</View>,
146161
])}
162+
<View style={styles.componentModalSpacer} />
147163
</ScrollView>
148164
</View>
149165
);

src/TestRegistry.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ function getName(component: React.Element<any>) {
4747
* @param options - for the test
4848
*/
4949
function addTest(component: React.Element<any>, type: 'scene' | 'component', options: TestType = {}) {
50-
if (!component || !component.type) {
50+
if (!component || (!component.type && !component instanceof Function)) {
5151
return;
5252
}
5353
// Use provided name, or extract from component if not given

0 commit comments

Comments
 (0)