Add filtering to e2e tests (#22828)
Summary: This PR adds filtering for e2e test examples using the new examples filter introduced in https://github.com/facebook/react-native/pull/22777 To do that we: - Add a `testID` to `RNTesterExampleFilter` to select an example - Refactor a few examples to export multiple examples for filtering - Update all tests to filter by example title Pull Request resolved: https://github.com/facebook/react-native/pull/22828 Reviewed By: TheSavior Differential Revision: D13562664 Pulled By: rickhanlonii fbshipit-source-id: efb0ca8050c1ca5c10d96bd77d35dd1143c3a3b3
This commit is contained in:
parent
9db0050635
commit
34ee8250b5
|
@ -9,34 +9,36 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* global device, element, by, expect */
|
/* global device, element, by, expect */
|
||||||
|
const {
|
||||||
|
openComponentWithLabel,
|
||||||
|
openExampleWithTitle,
|
||||||
|
} = require('../e2e-helpers');
|
||||||
|
|
||||||
describe('Button', () => {
|
describe('Button', () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await device.reloadReactNative();
|
await device.reloadReactNative();
|
||||||
await element(by.id('explorer_search')).replaceText('<Button>');
|
await openComponentWithLabel(
|
||||||
await element(
|
'<Button>',
|
||||||
by.label('<Button> Simple React Native button component.'),
|
'<Button> Simple React Native button component.',
|
||||||
).tap();
|
);
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(async () => {
|
|
||||||
//TODO - remove app state persistency, till then, we must go back to main screen,
|
|
||||||
await element(by.label('Back')).tap();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Simple button should be tappable', async () => {
|
it('Simple button should be tappable', async () => {
|
||||||
|
await openExampleWithTitle('Simple Button');
|
||||||
await element(by.id('simple_button')).tap();
|
await element(by.id('simple_button')).tap();
|
||||||
await expect(element(by.text('Simple has been pressed!'))).toBeVisible();
|
await expect(element(by.text('Simple has been pressed!'))).toBeVisible();
|
||||||
await element(by.text('OK')).tap();
|
await element(by.text('OK')).tap();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Adjusted color button should be tappable', async () => {
|
it('Adjusted color button should be tappable', async () => {
|
||||||
|
await openExampleWithTitle('Adjusted color');
|
||||||
await element(by.id('purple_button')).tap();
|
await element(by.id('purple_button')).tap();
|
||||||
await expect(element(by.text('Purple has been pressed!'))).toBeVisible();
|
await expect(element(by.text('Purple has been pressed!'))).toBeVisible();
|
||||||
await element(by.text('OK')).tap();
|
await element(by.text('OK')).tap();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Two buttons with JustifyContent:'space-between' should be tappable", async () => {
|
it("Two buttons with JustifyContent:'space-between' should be tappable", async () => {
|
||||||
|
await openExampleWithTitle('Fit to text layout');
|
||||||
await element(by.id('left_button')).tap();
|
await element(by.id('left_button')).tap();
|
||||||
await expect(element(by.text('Left has been pressed!'))).toBeVisible();
|
await expect(element(by.text('Left has been pressed!'))).toBeVisible();
|
||||||
await element(by.text('OK')).tap();
|
await element(by.text('OK')).tap();
|
||||||
|
@ -47,6 +49,7 @@ describe('Button', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Disabled button should not interact', async () => {
|
it('Disabled button should not interact', async () => {
|
||||||
|
await openExampleWithTitle('Disabled Button');
|
||||||
await element(by.id('disabled_button')).tap();
|
await element(by.id('disabled_button')).tap();
|
||||||
await expect(
|
await expect(
|
||||||
element(by.text('Disabled has been pressed!')),
|
element(by.text('Disabled has been pressed!')),
|
||||||
|
|
|
@ -8,23 +8,24 @@
|
||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* global element, by, expect */
|
/* global element, by, expect, device */
|
||||||
|
|
||||||
|
const {
|
||||||
|
openComponentWithLabel,
|
||||||
|
openExampleWithTitle,
|
||||||
|
} = require('../e2e-helpers');
|
||||||
|
|
||||||
describe('DatePickerIOS', () => {
|
describe('DatePickerIOS', () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await element(by.id('explorer_search')).replaceText('<DatePickerIOS>');
|
await device.reloadReactNative();
|
||||||
await element(
|
await openComponentWithLabel(
|
||||||
by.label(
|
'<DatePickerIOS>',
|
||||||
'<DatePickerIOS> Select dates and times using the native UIDatePicker.',
|
'<DatePickerIOS> Select dates and times using the native UIDatePicker.',
|
||||||
),
|
);
|
||||||
).tap();
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(async () => {
|
|
||||||
await element(by.label('Back')).tap();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should change indicator with datetime picker', async () => {
|
it('Should change indicator with datetime picker', async () => {
|
||||||
|
await openExampleWithTitle('Date and time picker');
|
||||||
const testID = 'date-and-time';
|
const testID = 'date-and-time';
|
||||||
const indicatorID = 'date-and-time-indicator';
|
const indicatorID = 'date-and-time-indicator';
|
||||||
|
|
||||||
|
@ -45,6 +46,7 @@ describe('DatePickerIOS', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should change indicator with date-only picker', async () => {
|
it('Should change indicator with date-only picker', async () => {
|
||||||
|
await openExampleWithTitle('Date only');
|
||||||
const testID = 'date-only';
|
const testID = 'date-only';
|
||||||
const indicatorID = 'date-and-time-indicator';
|
const indicatorID = 'date-and-time-indicator';
|
||||||
|
|
||||||
|
|
|
@ -8,23 +8,23 @@
|
||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* global element, by, expect */
|
/* global device, element, by, expect */
|
||||||
|
const {
|
||||||
|
openComponentWithLabel,
|
||||||
|
openExampleWithTitle,
|
||||||
|
} = require('../e2e-helpers');
|
||||||
|
|
||||||
describe('Picker', () => {
|
describe('Picker', () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await element(by.id('explorer_search')).replaceText('<Picker>');
|
await device.reloadReactNative();
|
||||||
await element(
|
await openComponentWithLabel(
|
||||||
by.label(
|
'<Picker>',
|
||||||
'<Picker> Provides multiple options to choose from, using either a dropdown menu or a dialog.',
|
'<Picker> Provides multiple options to choose from, using either a dropdown menu or a dialog.',
|
||||||
),
|
);
|
||||||
).tap();
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(async () => {
|
|
||||||
await element(by.label('Back')).tap();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be selectable by ID', async () => {
|
it('should be selectable by ID', async () => {
|
||||||
|
await openExampleWithTitle('Basic picker');
|
||||||
await expect(element(by.id('basic-picker'))).toBeVisible();
|
await expect(element(by.id('basic-picker'))).toBeVisible();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,12 +11,20 @@
|
||||||
/* global device, element, by, expect */
|
/* global device, element, by, expect */
|
||||||
|
|
||||||
const jestExpect = require('expect');
|
const jestExpect = require('expect');
|
||||||
|
const {
|
||||||
|
openComponentWithLabel,
|
||||||
|
openExampleWithTitle,
|
||||||
|
} = require('../e2e-helpers');
|
||||||
|
|
||||||
describe('Switch', () => {
|
describe('Switch', () => {
|
||||||
beforeEach(async () => {
|
beforeAll(async () => {
|
||||||
await device.reloadReactNative();
|
await device.reloadReactNative();
|
||||||
await element(by.id('explorer_search')).replaceText('<Switch>');
|
await openComponentWithLabel('<Switch>', '<Switch> Native boolean input');
|
||||||
await element(by.label('<Switch> Native boolean input')).tap();
|
});
|
||||||
|
|
||||||
|
describe('Switches can be set to true or false', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
await openExampleWithTitle('Switches can be set to true or false');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Switch that starts off should switch', async () => {
|
it('Switch that starts off should switch', async () => {
|
||||||
|
@ -40,6 +48,12 @@ describe('Switch', () => {
|
||||||
await expect(element(by.id(testID))).toHaveValue('0');
|
await expect(element(by.id(testID))).toHaveValue('0');
|
||||||
await expect(element(by.id(indicatorID))).toHaveText('Off');
|
await expect(element(by.id(indicatorID))).toHaveText('Off');
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Switches can be disabled', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
await openExampleWithTitle('Switches can be disabled');
|
||||||
|
});
|
||||||
|
|
||||||
it('disabled switch should not toggle', async () => {
|
it('disabled switch should not toggle', async () => {
|
||||||
const onTestID = 'disabled-initial-on';
|
const onTestID = 'disabled-initial-on';
|
||||||
|
@ -78,4 +92,5 @@ describe('Switch', () => {
|
||||||
await expect(element(by.id(offTestID))).toHaveValue('0');
|
await expect(element(by.id(offTestID))).toHaveValue('0');
|
||||||
await expect(element(by.id(offIndicatorID))).toHaveText('Off');
|
await expect(element(by.id(offIndicatorID))).toHaveText('Off');
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,22 +8,23 @@
|
||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* global element, by, expect */
|
/* global device, element, by, expect */
|
||||||
|
const {
|
||||||
|
openComponentWithLabel,
|
||||||
|
openExampleWithTitle,
|
||||||
|
} = require('../e2e-helpers');
|
||||||
|
|
||||||
describe('Touchable', () => {
|
describe('Touchable', () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await element(by.id('explorer_search')).replaceText('<Touchable*');
|
await device.reloadReactNative();
|
||||||
await element(
|
await openComponentWithLabel(
|
||||||
by.label('<Touchable*> and onPress Touchable and onPress examples.'),
|
'<Touchable*',
|
||||||
).tap();
|
'<Touchable*> and onPress Touchable and onPress examples.',
|
||||||
});
|
);
|
||||||
|
|
||||||
afterAll(async () => {
|
|
||||||
//TODO - remove app state persistency, till then, we must go back to main screen,
|
|
||||||
await element(by.label('Back')).tap();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Touchable Highlight should be tappable', async () => {
|
it('Touchable Highlight should be tappable', async () => {
|
||||||
|
await openExampleWithTitle('<TouchableHighlight>');
|
||||||
const buttonID = 'touchable_highlight_image_button';
|
const buttonID = 'touchable_highlight_image_button';
|
||||||
const button2ID = 'touchable_highlight_text_button';
|
const button2ID = 'touchable_highlight_text_button';
|
||||||
const consoleID = 'touchable_highlight_console';
|
const consoleID = 'touchable_highlight_console';
|
||||||
|
@ -45,6 +46,8 @@ describe('Touchable', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Touchable Without Feedback should be tappable', async () => {
|
it('Touchable Without Feedback should be tappable', async () => {
|
||||||
|
await openExampleWithTitle('<TouchableWithoutFeedback>');
|
||||||
|
|
||||||
const buttonID = 'touchable_without_feedback_button';
|
const buttonID = 'touchable_without_feedback_button';
|
||||||
const consoleID = 'touchable_without_feedback_console';
|
const consoleID = 'touchable_without_feedback_console';
|
||||||
|
|
||||||
|
@ -60,6 +63,8 @@ describe('Touchable', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Text should be tappable', async () => {
|
it('Text should be tappable', async () => {
|
||||||
|
await openExampleWithTitle('<Text onPress={fn}> with highlight');
|
||||||
|
|
||||||
const buttonID = 'tappable_text';
|
const buttonID = 'tappable_text';
|
||||||
const consoleID = 'tappable_text_console';
|
const consoleID = 'tappable_text_console';
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @emails oncall+react_native
|
||||||
|
* @format
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* global element, by, expect */
|
||||||
|
|
||||||
|
// Will open a component example from the root list
|
||||||
|
// by filtering by component and then tapping on the label
|
||||||
|
exports.openComponentWithLabel = async (component, label) => {
|
||||||
|
await element(by.id('explorer_search')).replaceText(component);
|
||||||
|
await element(by.label(label)).tap();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Will open an individual example for a component
|
||||||
|
// by filtering on the example title
|
||||||
|
exports.openExampleWithTitle = async title => {
|
||||||
|
await element(by.id('example_search')).replaceText(title);
|
||||||
|
};
|
|
@ -14,20 +14,21 @@ const React = require('react');
|
||||||
const ReactNative = require('react-native');
|
const ReactNative = require('react-native');
|
||||||
const {DatePickerIOS, StyleSheet, Text, TextInput, View} = ReactNative;
|
const {DatePickerIOS, StyleSheet, Text, TextInput, View} = ReactNative;
|
||||||
|
|
||||||
class DatePickerExample extends React.Component<
|
type State = {|
|
||||||
$FlowFixMeProps,
|
date: Date,
|
||||||
$FlowFixMeState,
|
timeZoneOffsetInHours: number,
|
||||||
> {
|
|};
|
||||||
static defaultProps = {
|
|
||||||
|
type Props = {|
|
||||||
|
children: (State, (Date) => void) => React.Node,
|
||||||
|
|};
|
||||||
|
|
||||||
|
class WithDatePickerData extends React.Component<Props, State> {
|
||||||
|
state = {
|
||||||
date: new Date(),
|
date: new Date(),
|
||||||
timeZoneOffsetInHours: (-1 * new Date().getTimezoneOffset()) / 60,
|
timeZoneOffsetInHours: (-1 * new Date().getTimezoneOffset()) / 60,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
|
||||||
date: this.props.date,
|
|
||||||
timeZoneOffsetInHours: this.props.timeZoneOffsetInHours,
|
|
||||||
};
|
|
||||||
|
|
||||||
onDateChange = date => {
|
onDateChange = date => {
|
||||||
this.setState({date: date});
|
this.setState({date: date});
|
||||||
};
|
};
|
||||||
|
@ -63,37 +64,18 @@ class DatePickerExample extends React.Component<
|
||||||
/>
|
/>
|
||||||
<Text> hours from UTC</Text>
|
<Text> hours from UTC</Text>
|
||||||
</WithLabel>
|
</WithLabel>
|
||||||
<Heading label="Date + time picker" />
|
{this.props.children(this.state, this.onDateChange)}
|
||||||
<DatePickerIOS
|
|
||||||
testID="date-and-time"
|
|
||||||
date={this.state.date}
|
|
||||||
mode="datetime"
|
|
||||||
timeZoneOffsetInMinutes={this.state.timeZoneOffsetInHours * 60}
|
|
||||||
onDateChange={this.onDateChange}
|
|
||||||
/>
|
|
||||||
<Heading label="Date picker" />
|
|
||||||
<DatePickerIOS
|
|
||||||
testID="date-only"
|
|
||||||
date={this.state.date}
|
|
||||||
mode="date"
|
|
||||||
timeZoneOffsetInMinutes={this.state.timeZoneOffsetInHours * 60}
|
|
||||||
onDateChange={this.onDateChange}
|
|
||||||
/>
|
|
||||||
<Heading label="Time picker, 10-minute interval" />
|
|
||||||
<DatePickerIOS
|
|
||||||
testID="time-only"
|
|
||||||
date={this.state.date}
|
|
||||||
mode="time"
|
|
||||||
timeZoneOffsetInMinutes={this.state.timeZoneOffsetInHours * 60}
|
|
||||||
onDateChange={this.onDateChange}
|
|
||||||
minuteInterval={10}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class WithLabel extends React.Component<$FlowFixMeProps> {
|
type LabelProps = {|
|
||||||
|
label: string,
|
||||||
|
children: React.Node,
|
||||||
|
|};
|
||||||
|
|
||||||
|
class WithLabel extends React.Component<LabelProps> {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<View style={styles.labelContainer}>
|
<View style={styles.labelContainer}>
|
||||||
|
@ -106,28 +88,6 @@ class WithLabel extends React.Component<$FlowFixMeProps> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Heading extends React.Component<$FlowFixMeProps> {
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<View style={styles.headingContainer}>
|
|
||||||
<Text style={styles.heading}>{this.props.label}</Text>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.displayName = (undefined: ?string);
|
|
||||||
exports.title = '<DatePickerIOS>';
|
|
||||||
exports.description = 'Select dates and times using the native UIDatePicker.';
|
|
||||||
exports.examples = [
|
|
||||||
{
|
|
||||||
title: '<DatePickerIOS>',
|
|
||||||
render: function(): React.Element<any> {
|
|
||||||
return <DatePickerExample />;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
textinput: {
|
textinput: {
|
||||||
height: 26,
|
height: 26,
|
||||||
|
@ -149,12 +109,63 @@ const styles = StyleSheet.create({
|
||||||
label: {
|
label: {
|
||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
},
|
},
|
||||||
headingContainer: {
|
|
||||||
padding: 4,
|
|
||||||
backgroundColor: '#f6f7f8',
|
|
||||||
},
|
|
||||||
heading: {
|
|
||||||
fontWeight: '500',
|
|
||||||
fontSize: 14,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
exports.title = '<DatePickerIOS>';
|
||||||
|
exports.description = 'Select dates and times using the native UIDatePicker.';
|
||||||
|
exports.examples = [
|
||||||
|
{
|
||||||
|
title: 'Date and time picker',
|
||||||
|
render: function(): React.Element<any> {
|
||||||
|
return (
|
||||||
|
<WithDatePickerData>
|
||||||
|
{(state, onDateChange) => (
|
||||||
|
<DatePickerIOS
|
||||||
|
testID="date-and-time"
|
||||||
|
date={state.date}
|
||||||
|
mode="datetime"
|
||||||
|
timeZoneOffsetInMinutes={state.timeZoneOffsetInHours * 60}
|
||||||
|
onDateChange={onDateChange}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</WithDatePickerData>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Date only picker',
|
||||||
|
render: function(): React.Element<any> {
|
||||||
|
return (
|
||||||
|
<WithDatePickerData>
|
||||||
|
{(state, onDateChange) => (
|
||||||
|
<DatePickerIOS
|
||||||
|
testID="date-only"
|
||||||
|
date={state.date}
|
||||||
|
mode="date"
|
||||||
|
timeZoneOffsetInMinutes={state.timeZoneOffsetInHours * 60}
|
||||||
|
onDateChange={onDateChange}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</WithDatePickerData>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Time only picker, 10-minute interval',
|
||||||
|
render: function(): React.Element<any> {
|
||||||
|
return (
|
||||||
|
<WithDatePickerData>
|
||||||
|
{(state, onDateChange) => (
|
||||||
|
<DatePickerIOS
|
||||||
|
testID="time-only"
|
||||||
|
date={state.date}
|
||||||
|
mode="time"
|
||||||
|
timeZoneOffsetInMinutes={state.timeZoneOffsetInHours * 60}
|
||||||
|
onDateChange={onDateChange}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</WithDatePickerData>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
|
@ -13,83 +13,106 @@
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const ReactNative = require('react-native');
|
const ReactNative = require('react-native');
|
||||||
const StyleSheet = require('StyleSheet');
|
const StyleSheet = require('StyleSheet');
|
||||||
const RNTesterBlock = require('RNTesterBlock');
|
|
||||||
const RNTesterPage = require('RNTesterPage');
|
|
||||||
|
|
||||||
const {Picker, Text} = ReactNative;
|
const {Picker, Text} = ReactNative;
|
||||||
|
|
||||||
const Item = Picker.Item;
|
const Item = Picker.Item;
|
||||||
|
|
||||||
class PickerExample extends React.Component<{}, $FlowFixMeState> {
|
type State = {
|
||||||
static title = '<Picker>';
|
value: string | number,
|
||||||
static description =
|
};
|
||||||
'Provides multiple options to choose from, using either a dropdown menu or a dialog.';
|
|
||||||
|
|
||||||
|
class BasicPickerExample extends React.Component<{}, State> {
|
||||||
state = {
|
state = {
|
||||||
selected1: 'key1',
|
value: 'key1',
|
||||||
selected2: 'key1',
|
|
||||||
selected3: 'key1',
|
|
||||||
color: 'red',
|
|
||||||
mode: Picker.MODE_DIALOG,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<RNTesterPage title="<Picker>">
|
|
||||||
<RNTesterBlock title="Basic Picker">
|
|
||||||
<Picker
|
<Picker
|
||||||
testID="basic-picker"
|
testID="basic-picker"
|
||||||
style={styles.picker}
|
style={styles.picker}
|
||||||
selectedValue={this.state.selected1}
|
selectedValue={this.state.value}
|
||||||
onValueChange={this.onValueChange.bind(this, 'selected1')}>
|
onValueChange={v => this.setState({value: v})}>
|
||||||
<Item label="hello" value="key0" />
|
<Item label="hello" value="key0" />
|
||||||
<Item label="world" value="key1" />
|
<Item label="world" value="key1" />
|
||||||
</Picker>
|
</Picker>
|
||||||
</RNTesterBlock>
|
);
|
||||||
<RNTesterBlock title="Disabled picker">
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DisabledPickerExample extends React.Component<{}, State> {
|
||||||
|
state = {
|
||||||
|
value: 'key1',
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
<Picker
|
<Picker
|
||||||
style={styles.picker}
|
style={styles.picker}
|
||||||
enabled={false}
|
enabled={false}
|
||||||
selectedValue={this.state.selected1}>
|
selectedValue={this.state.value}>
|
||||||
<Item label="hello" value="key0" />
|
<Item label="hello" value="key0" />
|
||||||
<Item label="world" value="key1" />
|
<Item label="world" value="key1" />
|
||||||
</Picker>
|
</Picker>
|
||||||
</RNTesterBlock>
|
);
|
||||||
<RNTesterBlock title="Dropdown Picker">
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DropdownPickerExample extends React.Component<{}, State> {
|
||||||
|
state = {
|
||||||
|
value: 'key1',
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
<Picker
|
<Picker
|
||||||
style={styles.picker}
|
style={styles.picker}
|
||||||
selectedValue={this.state.selected2}
|
selectedValue={this.state.value}
|
||||||
onValueChange={this.onValueChange.bind(this, 'selected2')}
|
onValueChange={v => this.setState({value: v})}
|
||||||
mode="dropdown">
|
mode="dropdown">
|
||||||
<Item label="hello" value="key0" />
|
<Item label="hello" value="key0" />
|
||||||
<Item label="world" value="key1" />
|
<Item label="world" value="key1" />
|
||||||
</Picker>
|
</Picker>
|
||||||
</RNTesterBlock>
|
);
|
||||||
<RNTesterBlock title="Picker with prompt message">
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PromptPickerExample extends React.Component<{}, State> {
|
||||||
|
state = {
|
||||||
|
value: 'key1',
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
<Picker
|
<Picker
|
||||||
style={styles.picker}
|
style={styles.picker}
|
||||||
selectedValue={this.state.selected3}
|
selectedValue={this.state.value}
|
||||||
onValueChange={this.onValueChange.bind(this, 'selected3')}
|
onValueChange={v => this.setState({value: v})}
|
||||||
prompt="Pick one, just one">
|
prompt="Pick one, just one">
|
||||||
<Item label="hello" value="key0" />
|
<Item label="hello" value="key0" />
|
||||||
<Item label="world" value="key1" />
|
<Item label="world" value="key1" />
|
||||||
</Picker>
|
</Picker>
|
||||||
</RNTesterBlock>
|
);
|
||||||
<RNTesterBlock title="Picker with no listener">
|
}
|
||||||
<Picker style={styles.picker}>
|
}
|
||||||
<Item label="hello" value="key0" />
|
|
||||||
<Item label="world" value="key1" />
|
type ColorState = {
|
||||||
</Picker>
|
color: string | number,
|
||||||
<Text>
|
};
|
||||||
Cannot change the value of this picker because it doesn't update
|
|
||||||
selectedValue.
|
class ColorPickerExample extends React.Component<{}, ColorState> {
|
||||||
</Text>
|
state = {
|
||||||
</RNTesterBlock>
|
color: 'red',
|
||||||
<RNTesterBlock title="Colorful pickers">
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
<Picker
|
<Picker
|
||||||
style={[styles.picker, {color: 'white', backgroundColor: '#333'}]}
|
style={[styles.picker, {color: 'white', backgroundColor: '#333'}]}
|
||||||
selectedValue={this.state.color}
|
selectedValue={this.state.color}
|
||||||
onValueChange={this.onValueChange.bind(this, 'color')}
|
onValueChange={v => this.setState({color: v})}
|
||||||
mode="dropdown">
|
mode="dropdown">
|
||||||
<Item label="red" color="red" value="red" />
|
<Item label="red" color="red" value="red" />
|
||||||
<Item label="green" color="green" value="green" />
|
<Item label="green" color="green" value="green" />
|
||||||
|
@ -98,30 +121,15 @@ class PickerExample extends React.Component<{}, $FlowFixMeState> {
|
||||||
<Picker
|
<Picker
|
||||||
style={styles.picker}
|
style={styles.picker}
|
||||||
selectedValue={this.state.color}
|
selectedValue={this.state.color}
|
||||||
onValueChange={this.onValueChange.bind(this, 'color')}
|
onValueChange={v => this.setState({color: v})}
|
||||||
mode="dialog">
|
mode="dialog">
|
||||||
<Item label="red" color="red" value="red" />
|
<Item label="red" color="red" value="red" />
|
||||||
<Item label="green" color="green" value="green" />
|
<Item label="green" color="green" value="green" />
|
||||||
<Item label="blue" color="blue" value="blue" />
|
<Item label="blue" color="blue" value="blue" />
|
||||||
</Picker>
|
</Picker>
|
||||||
</RNTesterBlock>
|
</>
|
||||||
</RNTesterPage>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
changeMode = () => {
|
|
||||||
const newMode =
|
|
||||||
this.state.mode === Picker.MODE_DIALOG
|
|
||||||
? Picker.MODE_DROPDOWN
|
|
||||||
: Picker.MODE_DIALOG;
|
|
||||||
this.setState({mode: newMode});
|
|
||||||
};
|
|
||||||
|
|
||||||
onValueChange = (key: string, value: string | number) => {
|
|
||||||
const newState = {};
|
|
||||||
newState[key] = value;
|
|
||||||
this.setState(newState);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
@ -130,4 +138,55 @@ const styles = StyleSheet.create({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = PickerExample;
|
exports.title = '<Picker>';
|
||||||
|
exports.description =
|
||||||
|
'Provides multiple options to choose from, using either a dropdown menu or a dialog.';
|
||||||
|
exports.examples = [
|
||||||
|
{
|
||||||
|
title: 'Basic Picker',
|
||||||
|
render: function(): React.Element<typeof BasicPickerExample> {
|
||||||
|
return <BasicPickerExample />;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Disabled Picker',
|
||||||
|
render: function(): React.Element<typeof DisabledPickerExample> {
|
||||||
|
return <DisabledPickerExample />;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Dropdown Picker',
|
||||||
|
render: function(): React.Element<typeof DropdownPickerExample> {
|
||||||
|
return <DropdownPickerExample />;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Picker with prompt message',
|
||||||
|
render: function(): React.Element<typeof PromptPickerExample> {
|
||||||
|
return <PromptPickerExample />;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Picker with no listener',
|
||||||
|
render: function(): React.Element<typeof PromptPickerExample> {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Picker style={styles.picker}>
|
||||||
|
<Item label="hello" value="key0" />
|
||||||
|
<Item label="world" value="key1" />
|
||||||
|
</Picker>
|
||||||
|
<Text>
|
||||||
|
Cannot change the value of this picker because it doesn't update
|
||||||
|
selectedValue.
|
||||||
|
</Text>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Colorful pickers',
|
||||||
|
render: function(): React.Element<typeof ColorPickerExample> {
|
||||||
|
return <ColorPickerExample />;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
|
@ -56,6 +56,7 @@ class RNTesterExampleContainer extends React.Component {
|
||||||
return (
|
return (
|
||||||
<RNTesterPage title={this.props.title}>
|
<RNTesterPage title={this.props.title}>
|
||||||
<RNTesterExampleFilter
|
<RNTesterExampleFilter
|
||||||
|
testID="example_search"
|
||||||
sections={sections}
|
sections={sections}
|
||||||
filter={filter}
|
filter={filter}
|
||||||
render={({filteredSections}) =>
|
render={({filteredSections}) =>
|
||||||
|
|
|
@ -20,6 +20,7 @@ type Props = {
|
||||||
render: Function,
|
render: Function,
|
||||||
sections: Object,
|
sections: Object,
|
||||||
disableSearch?: boolean,
|
disableSearch?: boolean,
|
||||||
|
testID?: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
|
@ -75,7 +76,7 @@ class RNTesterExampleFilter extends React.Component<Props, State> {
|
||||||
placeholder="Search..."
|
placeholder="Search..."
|
||||||
underlineColorAndroid="transparent"
|
underlineColorAndroid="transparent"
|
||||||
style={styles.searchTextInput}
|
style={styles.searchTextInput}
|
||||||
testID="explorer_search"
|
testID={this.props.testID}
|
||||||
value={this.state.filter}
|
value={this.state.filter}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -94,6 +94,7 @@ class RNTesterExampleList extends React.Component<Props, $FlowFixMeState> {
|
||||||
<View style={[styles.listContainer, this.props.style]}>
|
<View style={[styles.listContainer, this.props.style]}>
|
||||||
{this._renderTitleRow()}
|
{this._renderTitleRow()}
|
||||||
<RNTesterExampleFilter
|
<RNTesterExampleFilter
|
||||||
|
testID="explorer_search"
|
||||||
sections={sections}
|
sections={sections}
|
||||||
filter={filter}
|
filter={filter}
|
||||||
render={({filteredSections}) => (
|
render={({filteredSections}) => (
|
||||||
|
|
|
@ -198,7 +198,10 @@ class EventSwitchExample extends React.Component<{}, $FlowFixMeState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const examples = [
|
exports.title = '<Switch>';
|
||||||
|
exports.displayName = 'SwitchExample';
|
||||||
|
exports.description = 'Native boolean input';
|
||||||
|
exports.examples = [
|
||||||
{
|
{
|
||||||
title: 'Switches can be set to true or false',
|
title: 'Switches can be set to true or false',
|
||||||
render(): React.Element<any> {
|
render(): React.Element<any> {
|
||||||
|
@ -230,8 +233,3 @@ const examples = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
exports.title = '<Switch>';
|
|
||||||
exports.displayName = 'SwitchExample';
|
|
||||||
exports.description = 'Native boolean input';
|
|
||||||
exports.examples = examples;
|
|
||||||
|
|
|
@ -32,108 +32,6 @@ const forceTouchAvailable =
|
||||||
NativeModules.PlatformConstants.forceTouchAvailable) ||
|
NativeModules.PlatformConstants.forceTouchAvailable) ||
|
||||||
false;
|
false;
|
||||||
|
|
||||||
exports.displayName = (undefined: ?string);
|
|
||||||
exports.description = 'Touchable and onPress examples.';
|
|
||||||
exports.title = '<Touchable*> and onPress';
|
|
||||||
exports.examples = [
|
|
||||||
{
|
|
||||||
title: '<TouchableHighlight>',
|
|
||||||
description:
|
|
||||||
'TouchableHighlight works by adding an extra view with a ' +
|
|
||||||
'black background under the single child view. This works best when the ' +
|
|
||||||
'child view is fully opaque, although it can be made to work as a simple ' +
|
|
||||||
'background color change as well with the activeOpacity and ' +
|
|
||||||
'underlayColor props.',
|
|
||||||
render: function() {
|
|
||||||
return <TouchableHighlightBox />;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '<TouchableWithoutFeedback>',
|
|
||||||
render: function() {
|
|
||||||
return <TouchableWithoutFeedbackBox />;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'TouchableNativeFeedback with Animated child',
|
|
||||||
description:
|
|
||||||
'TouchableNativeFeedback can have an AnimatedComponent as a' +
|
|
||||||
'direct child.',
|
|
||||||
platform: 'android',
|
|
||||||
render: function() {
|
|
||||||
const mScale = new Animated.Value(1);
|
|
||||||
Animated.timing(mScale, {toValue: 0.3, duration: 1000}).start();
|
|
||||||
const style = {
|
|
||||||
backgroundColor: 'rgb(180, 64, 119)',
|
|
||||||
width: 200,
|
|
||||||
height: 100,
|
|
||||||
transform: [{scale: mScale}],
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<View>
|
|
||||||
<View style={styles.row}>
|
|
||||||
<TouchableNativeFeedback>
|
|
||||||
<Animated.View style={style} />
|
|
||||||
</TouchableNativeFeedback>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '<Text onPress={fn}> with highlight',
|
|
||||||
render: function(): React.Element<any> {
|
|
||||||
return <TextOnPressBox />;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Touchable feedback events',
|
|
||||||
description:
|
|
||||||
'<Touchable*> components accept onPress, onPressIn, ' +
|
|
||||||
'onPressOut, and onLongPress as props.',
|
|
||||||
render: function(): React.Element<any> {
|
|
||||||
return <TouchableFeedbackEvents />;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Touchable delay for events',
|
|
||||||
description:
|
|
||||||
'<Touchable*> components also accept delayPressIn, ' +
|
|
||||||
'delayPressOut, and delayLongPress as props. These props impact the ' +
|
|
||||||
'timing of feedback events.',
|
|
||||||
render: function(): React.Element<any> {
|
|
||||||
return <TouchableDelayEvents />;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '3D Touch / Force Touch',
|
|
||||||
description:
|
|
||||||
'iPhone 6s and 6s plus support 3D touch, which adds a force property to touches',
|
|
||||||
render: function(): React.Element<any> {
|
|
||||||
return <ForceTouchExample />;
|
|
||||||
},
|
|
||||||
platform: 'ios',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Touchable Hit Slop',
|
|
||||||
description:
|
|
||||||
'<Touchable*> components accept hitSlop prop which extends the touch area ' +
|
|
||||||
'without changing the view bounds.',
|
|
||||||
render: function(): React.Element<any> {
|
|
||||||
return <TouchableHitSlop />;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Disabled Touchable*',
|
|
||||||
description:
|
|
||||||
'<Touchable*> components accept disabled prop which prevents ' +
|
|
||||||
'any interaction with component',
|
|
||||||
render: function(): React.Element<any> {
|
|
||||||
return <TouchableDisabled />;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
class TouchableHighlightBox extends React.Component<{}, $FlowFixMeState> {
|
class TouchableHighlightBox extends React.Component<{}, $FlowFixMeState> {
|
||||||
state = {
|
state = {
|
||||||
timesPressed: 0,
|
timesPressed: 0,
|
||||||
|
@ -566,3 +464,105 @@ const styles = StyleSheet.create({
|
||||||
color: 'blue',
|
color: 'blue',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
exports.displayName = (undefined: ?string);
|
||||||
|
exports.description = 'Touchable and onPress examples.';
|
||||||
|
exports.title = '<Touchable*> and onPress';
|
||||||
|
exports.examples = [
|
||||||
|
{
|
||||||
|
title: '<TouchableHighlight>',
|
||||||
|
description:
|
||||||
|
'TouchableHighlight works by adding an extra view with a ' +
|
||||||
|
'black background under the single child view. This works best when the ' +
|
||||||
|
'child view is fully opaque, although it can be made to work as a simple ' +
|
||||||
|
'background color change as well with the activeOpacity and ' +
|
||||||
|
'underlayColor props.',
|
||||||
|
render: function() {
|
||||||
|
return <TouchableHighlightBox />;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '<TouchableWithoutFeedback>',
|
||||||
|
render: function() {
|
||||||
|
return <TouchableWithoutFeedbackBox />;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'TouchableNativeFeedback with Animated child',
|
||||||
|
description:
|
||||||
|
'TouchableNativeFeedback can have an AnimatedComponent as a' +
|
||||||
|
'direct child.',
|
||||||
|
platform: 'android',
|
||||||
|
render: function() {
|
||||||
|
const mScale = new Animated.Value(1);
|
||||||
|
Animated.timing(mScale, {toValue: 0.3, duration: 1000}).start();
|
||||||
|
const style = {
|
||||||
|
backgroundColor: 'rgb(180, 64, 119)',
|
||||||
|
width: 200,
|
||||||
|
height: 100,
|
||||||
|
transform: [{scale: mScale}],
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<View style={styles.row}>
|
||||||
|
<TouchableNativeFeedback>
|
||||||
|
<Animated.View style={style} />
|
||||||
|
</TouchableNativeFeedback>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '<Text onPress={fn}> with highlight',
|
||||||
|
render: function(): React.Element<any> {
|
||||||
|
return <TextOnPressBox />;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Touchable feedback events',
|
||||||
|
description:
|
||||||
|
'<Touchable*> components accept onPress, onPressIn, ' +
|
||||||
|
'onPressOut, and onLongPress as props.',
|
||||||
|
render: function(): React.Element<any> {
|
||||||
|
return <TouchableFeedbackEvents />;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Touchable delay for events',
|
||||||
|
description:
|
||||||
|
'<Touchable*> components also accept delayPressIn, ' +
|
||||||
|
'delayPressOut, and delayLongPress as props. These props impact the ' +
|
||||||
|
'timing of feedback events.',
|
||||||
|
render: function(): React.Element<any> {
|
||||||
|
return <TouchableDelayEvents />;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '3D Touch / Force Touch',
|
||||||
|
description:
|
||||||
|
'iPhone 6s and 6s plus support 3D touch, which adds a force property to touches',
|
||||||
|
render: function(): React.Element<any> {
|
||||||
|
return <ForceTouchExample />;
|
||||||
|
},
|
||||||
|
platform: 'ios',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Touchable Hit Slop',
|
||||||
|
description:
|
||||||
|
'<Touchable*> components accept hitSlop prop which extends the touch area ' +
|
||||||
|
'without changing the view bounds.',
|
||||||
|
render: function(): React.Element<any> {
|
||||||
|
return <TouchableHitSlop />;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Disabled Touchable*',
|
||||||
|
description:
|
||||||
|
'<Touchable*> components accept disabled prop which prevents ' +
|
||||||
|
'any interaction with component',
|
||||||
|
render: function(): React.Element<any> {
|
||||||
|
return <TouchableDisabled />;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
Loading…
Reference in New Issue