diff --git a/packages/rn-tester/e2e/__tests__/Button-test.js b/packages/rn-tester/e2e/__tests__/Button-test.js index c43fdb05d6b87f..53588b168a525a 100644 --- a/packages/rn-tester/e2e/__tests__/Button-test.js +++ b/packages/rn-tester/e2e/__tests__/Button-test.js @@ -18,8 +18,8 @@ describe('Button', () => { beforeAll(async () => { await device.reloadReactNative(); await openComponentWithLabel( - '<Button>', - '<Button> Simple React Native button component.', + 'Button', + 'Button Simple React Native button component.', ); }); diff --git a/packages/rn-tester/e2e/__tests__/DatePickerIOS-test.js b/packages/rn-tester/e2e/__tests__/DatePickerIOS-test.js index 11c73fcb2d5f4b..0656f30345421c 100644 --- a/packages/rn-tester/e2e/__tests__/DatePickerIOS-test.js +++ b/packages/rn-tester/e2e/__tests__/DatePickerIOS-test.js @@ -19,8 +19,8 @@ describe('DatePickerIOS', () => { beforeAll(async () => { await device.reloadReactNative(); await openComponentWithLabel( - '<DatePickerIOS>', - '<DatePickerIOS> Select dates and times using the native UIDatePicker.', + 'DatePickerIOS', + 'DatePickerIOS Select dates and times using the native UIDatePicker.', ); }); diff --git a/packages/rn-tester/e2e/__tests__/Picker-test.js b/packages/rn-tester/e2e/__tests__/Picker-test.js index f5ee3bcafc035b..822818ad9eb3a0 100644 --- a/packages/rn-tester/e2e/__tests__/Picker-test.js +++ b/packages/rn-tester/e2e/__tests__/Picker-test.js @@ -18,8 +18,8 @@ describe('Picker', () => { beforeAll(async () => { await device.reloadReactNative(); await openComponentWithLabel( - '<Picker>', - '<Picker> Provides multiple options to choose from, using either a dropdown menu or a dialog.', + 'Picker', + 'Picker Provides multiple options to choose from, using either a dropdown menu or a dialog.', ); }); diff --git a/packages/rn-tester/e2e/__tests__/TextInput-test.js b/packages/rn-tester/e2e/__tests__/TextInput-test.js index db1933ab6262f6..d262221f5fe23b 100644 --- a/packages/rn-tester/e2e/__tests__/TextInput-test.js +++ b/packages/rn-tester/e2e/__tests__/TextInput-test.js @@ -18,7 +18,7 @@ describe('TextInput', () => { beforeAll(async () => { await device.reloadReactNative(); await openComponentWithLabel( - '<TextInput>', + 'TextInput', 'Single and multi-line text inputs.', ); }); diff --git a/packages/rn-tester/e2e/__tests__/Touchable-test.js b/packages/rn-tester/e2e/__tests__/Touchable-test.js index a47fa76de5f6b8..2a241c8985e352 100644 --- a/packages/rn-tester/e2e/__tests__/Touchable-test.js +++ b/packages/rn-tester/e2e/__tests__/Touchable-test.js @@ -18,8 +18,8 @@ describe('Touchable', () => { beforeAll(async () => { await device.reloadReactNative(); await openComponentWithLabel( - '<Touchable*', - '<Touchable*> and onPress Touchable and onPress examples.', + 'Touchable*', + 'Touchable* and onPress Touchable and onPress examples.', ); }); diff --git a/packages/rn-tester/e2e/test-init.js b/packages/rn-tester/e2e/test-init.js index 4e95bbe7c3e8ab..5120acdf1b2e9e 100644 --- a/packages/rn-tester/e2e/test-init.js +++ b/packages/rn-tester/e2e/test-init.js @@ -11,7 +11,7 @@ /* global device */ const detox = require('detox'); -const config = require('../../package.json').detox; +const config = require('../../../package.json').detox; const adapter = require('detox/runners/jest/adapter'); jest.setTimeout(120000); jasmine.getEnv().addReporter(adapter); diff --git a/packages/rn-tester/js/RNTesterApp.android.js b/packages/rn-tester/js/RNTesterApp.android.js index fd3d45955dcb66..abf6121bc1c308 100644 --- a/packages/rn-tester/js/RNTesterApp.android.js +++ b/packages/rn-tester/js/RNTesterApp.android.js @@ -16,66 +16,66 @@ const RNTesterExampleList = require('./components/RNTesterExampleList'); const RNTesterList = require('./utils/RNTesterList'); const RNTesterNavigationReducer = require('./utils/RNTesterNavigationReducer'); const React = require('react'); -const URIActionMap = require('./utils/URIActionMap'); - -const nativeImageSource = require('../../../Libraries/Image/nativeImageSource'); +const RNTesterNavBar = require('./components/RNTesterNavbar'); const { AppRegistry, AsyncStorage, BackHandler, - Dimensions, - DrawerLayoutAndroid, - Image, - Linking, - StatusBar, StyleSheet, Text, - TouchableWithoutFeedback, UIManager, useColorScheme, View, + LogBox, } = require('react-native'); import type {RNTesterExample} from './types/RNTesterTypes'; import type {RNTesterNavigationState} from './utils/RNTesterNavigationReducer'; import {RNTesterThemeContext, themes} from './components/RNTesterTheme'; +import RNTesterDocumentationURL from './components/RNTesterDocumentationURL'; +import { + RNTesterBookmarkContext, + bookmarks, +} from './components/RNTesterBookmark'; +import type {RNTesterBookmark} from './components/RNTesterBookmark'; -UIManager.setLayoutAnimationEnabledExperimental(true); +import { + initializeAsyncStore, + addApi, + addComponent, + removeApi, + removeComponent, + checkBookmarks, + updateRecentlyViewedList, +} from './utils/RNTesterAsyncStorageAbstraction'; -const DRAWER_WIDTH_LEFT = 56; +UIManager.setLayoutAnimationEnabledExperimental && + UIManager.setLayoutAnimationEnabledExperimental(true); type Props = {exampleFromAppetizeParams?: ?string, ...}; const APP_STATE_KEY = 'RNTesterAppState.v2'; -const HEADER_NAV_ICON = nativeImageSource({ - android: 'ic_menu_black_24dp', - width: 48, - height: 48, -}); - const Header = ({ - onPressDrawer, title, + documentationURL, }: { - onPressDrawer?: () => mixed, title: string, + documentationURL?: string, ... }) => ( <RNTesterThemeContext.Consumer> {theme => { return ( - <View style={[styles.toolbar, {backgroundColor: theme.ToolbarColor}]}> + <View style={[styles.toolbar, {backgroundColor: '#F3F8FF'}]}> <View style={styles.toolbarCenter}> <Text style={[styles.title, {color: theme.LabelColor}]}> {title} </Text> - </View> - <View style={styles.toolbarLeft}> - <TouchableWithoutFeedback onPress={onPressDrawer}> - <Image source={HEADER_NAV_ICON} /> - </TouchableWithoutFeedback> + {documentationURL && ( + <RNTesterDocumentationURL documentationURL={documentationURL} /> + )} </View> </View> ); @@ -84,12 +84,10 @@ const Header = ({ ); const RNTesterExampleContainerViaHook = ({ - onPressDrawer, title, module, exampleRef, }: { - onPressDrawer?: () => mixed, title: string, module: RNTesterExample, exampleRef: () => void, @@ -100,102 +98,98 @@ const RNTesterExampleContainerViaHook = ({ return ( <RNTesterThemeContext.Provider value={theme}> <View style={styles.container}> - <Header title={title} onPressDrawer={onPressDrawer} /> + <Header title={title} documentationURL={module.documentationURL} /> <RNTesterExampleContainer module={module} ref={exampleRef} /> </View> </RNTesterThemeContext.Provider> ); }; -const RNTesterDrawerContentViaHook = ({ - onNavigate, - list, -}: { - onNavigate?: () => mixed, - list: { - ComponentExamples: Array<RNTesterExample>, - APIExamples: Array<RNTesterExample>, - ... - }, - ... -}) => { - const colorScheme = useColorScheme(); - const theme = colorScheme === 'dark' ? themes.dark : themes.light; - return ( - <RNTesterThemeContext.Provider value={theme}> - <View - style={[ - styles.drawerContentWrapper, - {backgroundColor: theme.SystemBackgroundColor}, - ]}> - <RNTesterExampleList - list={list} - displayTitleRow={true} - disableSearch={true} - onNavigate={onNavigate} - /> - </View> - </RNTesterThemeContext.Provider> - ); -}; - const RNTesterExampleListViaHook = ({ title, - onPressDrawer, onNavigate, + UpdateRecentlyViewedList, + recentComponents, + recentApis, + bookmark, list, + screen, }: { title: string, - onPressDrawer?: () => mixed, + screen: string, onNavigate?: () => mixed, + UpdateRecentlyViewedList?: (item: RNTesterExample, key: string) => mixed, + recentComponents: Array<RNTesterExample>, + recentApis: Array<RNTesterExample>, list: { ComponentExamples: Array<RNTesterExample>, APIExamples: Array<RNTesterExample>, ... }, + bookmark: RNTesterBookmark, ... }) => { const colorScheme = useColorScheme(); const theme = colorScheme === 'dark' ? themes.dark : themes.light; + const exampleTitle = + screen === 'component' + ? 'Component Store' + : screen === 'api' + ? 'API Store' + : 'Bookmarks'; return ( <RNTesterThemeContext.Provider value={theme}> - <View style={styles.container}> - <Header title={title} onPressDrawer={onPressDrawer} /> - <RNTesterExampleList onNavigate={onNavigate} list={list} /> - </View> + <RNTesterBookmarkContext.Provider value={bookmark}> + <View style={styles.container}> + <Header title={exampleTitle} /> + <RNTesterExampleList + onNavigate={onNavigate} + recentComponents={recentComponents} + recentApis={recentApis} + updateRecentlyViewedList={UpdateRecentlyViewedList} + list={list} + screen={screen} + /> + </View> + </RNTesterBookmarkContext.Provider> </RNTesterThemeContext.Provider> ); }; class RNTesterApp extends React.Component<Props, RNTesterNavigationState> { + constructor() { + super(); + + // RNTester App currently uses Async Storage from react-native for storing navigation state + // and bookmark items. + // TODO: Add Native Async Storage Module in RNTester + LogBox.ignoreLogs([new RegExp('has been extracted from react-native')]); + + this.state = { + openExample: null, + Components: bookmarks.Components, + Api: bookmarks.Api, + recentComponents: [], + recentApis: [], + screen: 'component', + AddApi: (apiName, api) => addApi(apiName, api, this), + AddComponent: (componentName, component) => + addComponent(componentName, component, this), + RemoveApi: apiName => removeApi(apiName, this), + RemoveComponent: componentName => removeComponent(componentName, this), + checkBookmark: (title, key) => checkBookmarks(title, key, this), + updateRecentlyViewedList: (item, key) => + updateRecentlyViewedList(item, key, this), + }; + } UNSAFE_componentWillMount() { - BackHandler.addEventListener( - 'hardwareBackPress', - this._handleBackButtonPress, + BackHandler.addEventListener('hardwareBackPress', () => + this._handleBackButtonPress(this.state.screen), ); } componentDidMount() { - Linking.getInitialURL().then(url => { - AsyncStorage.getItem(APP_STATE_KEY, (err, storedString) => { - const exampleAction = URIActionMap( - this.props.exampleFromAppetizeParams, - ); - const urlAction = URIActionMap(url); - const launchAction = exampleAction || urlAction; - if (err || !storedString) { - const initialAction = launchAction || {type: 'InitialAction'}; - this.setState(RNTesterNavigationReducer(null, initialAction)); - return; - } - const storedState = JSON.parse(storedString); - if (launchAction) { - this.setState(RNTesterNavigationReducer(storedState, launchAction)); - return; - } - this.setState(storedState); - }); - }); + initializeAsyncStore(this); } render(): React.Node { @@ -203,43 +197,28 @@ class RNTesterApp extends React.Component<Props, RNTesterNavigationState> { return null; } return ( - <DrawerLayoutAndroid - drawerPosition="left" - drawerWidth={Dimensions.get('window').width - DRAWER_WIDTH_LEFT} - keyboardDismissMode="on-drag" - onDrawerOpen={() => { - /* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue was - * found when making Flow check .android.js files. */ - this._overrideBackPressForDrawerLayout = true; - }} - onDrawerClose={() => { - /* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue was - * found when making Flow check .android.js files. */ - this._overrideBackPressForDrawerLayout = false; - }} - ref={drawer => { - /* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue was - * found when making Flow check .android.js files. */ - this.drawer = drawer; - }} - renderNavigationView={this._renderDrawerContent} - statusBarBackgroundColor="#589c90"> - {this._renderApp()} - </DrawerLayoutAndroid> + <View style={styles.container}> + {this._renderApp({ + Components: this.state.Components, + Api: this.state.Api, + AddApi: this.state.AddApi, + AddComponent: this.state.AddComponent, + RemoveApi: this.state.RemoveApi, + RemoveComponent: this.state.RemoveComponent, + checkBookmark: this.state.checkBookmark, + })} + <View style={styles.bottomNavbar}> + <RNTesterNavBar + screen={this.state.screen} + onNavigate={this._handleAction} + /> + </View> + </View> ); } - _renderDrawerContent = () => { - return ( - <RNTesterDrawerContentViaHook - onNavigate={this._handleAction} - list={RNTesterList} - /> - ); - }; - - _renderApp() { - const {openExample} = this.state; + _renderApp(bookmark) { + const {openExample, screen} = this.state; if (openExample) { const ExampleModule = RNTesterList.Modules[openExample]; @@ -247,7 +226,7 @@ class RNTesterApp extends React.Component<Props, RNTesterNavigationState> { return ( <ExampleModule onExampleExit={() => { - this._handleAction(RNTesterActions.Back()); + this._handleAction(RNTesterActions.Back(screen)); }} ref={example => { /* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue @@ -258,30 +237,36 @@ class RNTesterApp extends React.Component<Props, RNTesterNavigationState> { ); } else if (ExampleModule) { return ( - <RNTesterExampleContainerViaHook - /* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue was found - * when making Flow check .android.js files. */ - onPressDrawer={() => this.drawer.openDrawer()} - title={ExampleModule.title} - module={ExampleModule} - exampleRef={example => { - /* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue - * was found when making Flow check .android.js files. */ - this._exampleRef = example; - }} - /> + <> + <RNTesterExampleContainerViaHook + /* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue was found + * when making Flow check .android.js files. */ + title={ExampleModule.title} + module={ExampleModule} + exampleRef={example => { + /* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue + * was found when making Flow check .android.js files. */ + this._exampleRef = example; + }} + /> + </> ); } } return ( <RNTesterExampleListViaHook + key={screen} title={'RNTester'} /* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue was found * when making Flow check .android.js files. */ - onPressDrawer={() => this.drawer.openDrawer()} onNavigate={this._handleAction} + UpdateRecentlyViewedList={this.state.updateRecentlyViewedList} + recentComponents={this.state.recentComponents} + recentApis={this.state.recentApis} + bookmark={bookmark} list={RNTesterList} + screen={screen} /> ); } @@ -289,7 +274,6 @@ class RNTesterApp extends React.Component<Props, RNTesterNavigationState> { _handleAction = (action: Object): boolean => { /* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue was found * when making Flow check .android.js files. */ - this.drawer && this.drawer.closeDrawer(); const newState = RNTesterNavigationReducer(this.state, action); if (this.state !== newState) { this.setState(newState, () => @@ -300,18 +284,9 @@ class RNTesterApp extends React.Component<Props, RNTesterNavigationState> { return false; }; - _handleBackButtonPress = () => { + _handleBackButtonPress = screen => { /* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue was found * when making Flow check .android.js files. */ - if (this._overrideBackPressForDrawerLayout) { - // This hack is necessary because drawer layout provides an imperative API - // with open and close methods. This code would be cleaner if the drawer - // layout provided an `isOpen` prop and allowed us to pass a `onDrawerClose` handler. - /* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue was found - * when making Flow check .android.js files. */ - this.drawer && this.drawer.closeDrawer(); - return true; - } if ( /* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue was found * when making Flow check .android.js files. */ @@ -325,7 +300,7 @@ class RNTesterApp extends React.Component<Props, RNTesterNavigationState> { ) { return true; } - return this._handleAction(RNTesterActions.Back()); + return this._handleAction(RNTesterActions.Back(screen)); }; } @@ -352,9 +327,12 @@ const styles = StyleSheet.create({ fontWeight: '600', textAlign: 'center', }, - drawerContentWrapper: { - flex: 1, - paddingTop: StatusBar.currentHeight, + bottomNavbar: { + bottom: 0, + width: '100%', + display: 'flex', + flexDirection: 'column', + position: 'absolute', }, }); diff --git a/packages/rn-tester/js/RNTesterApp.ios.js b/packages/rn-tester/js/RNTesterApp.ios.js index 194892b50d60e3..96769429ce6eab 100644 --- a/packages/rn-tester/js/RNTesterApp.ios.js +++ b/packages/rn-tester/js/RNTesterApp.ios.js @@ -17,14 +17,13 @@ const RNTesterList = require('./utils/RNTesterList.ios'); const RNTesterNavigationReducer = require('./utils/RNTesterNavigationReducer'); const React = require('react'); const SnapshotViewIOS = require('./examples/Snapshot/SnapshotViewIOS.ios'); -const URIActionMap = require('./utils/URIActionMap'); +const RNTesterNavbar = require('./components/RNTesterNavbar'); const { AppRegistry, AsyncStorage, BackHandler, Button, - Linking, Platform, SafeAreaView, StyleSheet, @@ -35,23 +34,42 @@ const { } = require('react-native'); import type {RNTesterExample} from './types/RNTesterTypes'; -import type {RNTesterAction} from './utils/RNTesterActions'; +import type { + RNTesterAction, + RNTesterExampleAction, +} from './utils/RNTesterActions'; import type {RNTesterNavigationState} from './utils/RNTesterNavigationReducer'; import {RNTesterThemeContext, themes} from './components/RNTesterTheme'; +import RNTesterDocumentationURL from './components/RNTesterDocumentationURL'; import type {ColorSchemeName} from '../../../Libraries/Utilities/NativeAppearance'; +import { + RNTesterBookmarkContext, + bookmarks, +} from './components/RNTesterBookmark'; +import type {RNTesterBookmark} from './components/RNTesterBookmark'; type Props = {exampleFromAppetizeParams?: ?string, ...}; -LogBox.ignoreLogs(['Module RCTImagePickerManager requires main queue setup']); +import { + initializeAsyncStore, + addApi, + addComponent, + removeApi, + removeComponent, + checkBookmarks, + updateRecentlyViewedList, +} from './utils/RNTesterAsyncStorageAbstraction'; const APP_STATE_KEY = 'RNTesterAppState.v2'; const Header = ({ onBack, title, + documentationURL, }: { onBack?: () => mixed, title: string, + documentationURL?: string, ... }) => ( <RNTesterThemeContext.Consumer> @@ -67,9 +85,12 @@ const Header = ({ ]}> <View style={styles.header}> <View style={styles.headerCenter}> - <Text style={{...styles.title, ...{color: theme.LabelColor}}}> + <Text style={[styles.title, {color: theme.LabelColor}]}> {title} </Text> + {documentationURL && ( + <RNTesterDocumentationURL documentationURL={documentationURL} /> + )} </View> {onBack && ( <View> @@ -105,7 +126,11 @@ const RNTesterExampleContainerViaHook = ({ return ( <RNTesterThemeContext.Provider value={theme}> <View style={styles.exampleContainer}> - <Header onBack={onBack} title={title} /> + <Header + title={title} + onBack={onBack} + documentationURL={module.documentationURL} + /> <RNTesterExampleContainer module={module} /> </View> </RNTesterThemeContext.Provider> @@ -114,9 +139,19 @@ const RNTesterExampleContainerViaHook = ({ const RNTesterExampleListViaHook = ({ onNavigate, + UpdateRecentlyViewedList, + recentComponents, + recentApis, + bookmark, list, + screen, }: { - onNavigate?: () => mixed, + onNavigate?: (item: RNTesterExampleAction, key: string) => mixed, + UpdateRecentlyViewedList?: (item: RNTesterExample, key: string) => mixed, + recentComponents: Array<RNTesterExample>, + recentApis: Array<RNTesterExample>, + bookmark: RNTesterBookmark, + screen: string, list: { ComponentExamples: Array<RNTesterExample>, APIExamples: Array<RNTesterExample>, @@ -126,12 +161,27 @@ const RNTesterExampleListViaHook = ({ }) => { const colorScheme: ?ColorSchemeName = useColorScheme(); const theme = colorScheme === 'dark' ? themes.dark : themes.light; + const exampleTitle = + screen === 'component' + ? 'Component Store' + : screen === 'api' + ? 'API Store' + : 'Bookmarks'; return ( <RNTesterThemeContext.Provider value={theme}> - <View style={styles.exampleContainer}> - <Header title="RNTester" /> - <RNTesterExampleList onNavigate={onNavigate} list={list} /> - </View> + <RNTesterBookmarkContext.Provider value={bookmark}> + <View style={styles.exampleContainer}> + <Header title={exampleTitle} /> + <RNTesterExampleList + onNavigate={onNavigate} + recentComponents={recentComponents} + recentApis={recentApis} + updateRecentlyViewedList={UpdateRecentlyViewedList} + list={list} + screen={screen} + /> + </View> + </RNTesterBookmarkContext.Provider> </RNTesterThemeContext.Provider> ); }; @@ -139,38 +189,42 @@ const RNTesterExampleListViaHook = ({ class RNTesterApp extends React.Component<Props, RNTesterNavigationState> { _mounted: boolean; + constructor() { + super(); + + // RNTester App currently uses Async Storage from react-native for storing navigation state + // and bookmark items. + // TODO: Add Native Async Storage Module in RNTester + LogBox.ignoreLogs([new RegExp('has been extracted from react-native')]); + + this.state = { + openExample: null, + screen: 'component', + Components: bookmarks.Components, + Api: bookmarks.Api, + recentComponents: [], + recentApis: [], + AddApi: (apiName, api) => addApi(apiName, api, this), + AddComponent: (componentName, component) => + addComponent(componentName, component, this), + RemoveApi: apiName => removeApi(apiName, this), + RemoveComponent: componentName => removeComponent(componentName, this), + checkBookmark: (title, key) => checkBookmarks(title, key, this), + updateRecentlyViewedList: (item, key) => + updateRecentlyViewedList(item, key, this), + }; + } + UNSAFE_componentWillMount() { BackHandler.addEventListener('hardwareBackPress', this._handleBack); } componentDidMount() { - this._mounted = true; - Linking.getInitialURL().then(url => { - AsyncStorage.getItem(APP_STATE_KEY, (err, storedString) => { - if (!this._mounted) { - return; - } - const exampleAction = URIActionMap( - this.props.exampleFromAppetizeParams, - ); - const urlAction = URIActionMap(url); - const launchAction = exampleAction || urlAction; - const initialAction = launchAction || {type: 'InitialAction'}; - this.setState(RNTesterNavigationReducer(undefined, initialAction)); - }); - }); - - Linking.addEventListener('url', url => { - this._handleAction(URIActionMap(url)); - }); - } - - componentWillUnmount() { - this._mounted = false; + initializeAsyncStore(this); } _handleBack = () => { - this._handleAction(RNTesterActions.Back()); + this._handleAction(RNTesterActions.Back(this.state.screen)); }; _handleAction = (action: ?RNTesterAction) => { @@ -179,6 +233,7 @@ class RNTesterApp extends React.Component<Props, RNTesterNavigationState> { } const newState = RNTesterNavigationReducer(this.state, action); if (this.state !== newState) { + // syncing the app screens over async storage this.setState(newState, () => AsyncStorage.setItem(APP_STATE_KEY, JSON.stringify(this.state)), ); @@ -186,6 +241,16 @@ class RNTesterApp extends React.Component<Props, RNTesterNavigationState> { }; render(): React.Node | null { + const bookmark = { + Components: this.state.Components, + Api: this.state.Api, + AddApi: this.state.AddApi, + AddComponent: this.state.AddComponent, + RemoveApi: this.state.RemoveApi, + RemoveComponent: this.state.RemoveComponent, + checkBookmark: this.state.checkBookmark, + }; + if (!this.state) { return null; } @@ -195,24 +260,47 @@ class RNTesterApp extends React.Component<Props, RNTesterNavigationState> { return <Component onExampleExit={this._handleBack} />; } else { return ( - <RNTesterExampleContainerViaHook - onBack={this._handleBack} - title={Component.title} - module={Component} - /> + <> + <RNTesterExampleContainerViaHook + onBack={this._handleBack} + title={Component.title} + module={Component} + /> + <View style={styles.bottomNavbar}> + <RNTesterNavbar onNavigate={this._handleAction} /> + </View> + </> ); } } return ( - <RNTesterExampleListViaHook - onNavigate={this._handleAction} - list={RNTesterList} - /> + <> + <RNTesterExampleListViaHook + key={this.state.screen} + title={'RNTester'} + onNavigate={this._handleAction} + UpdateRecentlyViewedList={this.state.updateRecentlyViewedList} + recentComponents={this.state.recentComponents} + recentApis={this.state.recentApis} + bookmark={bookmark} + list={RNTesterList} + screen={this.state.screen} + /> + <View style={styles.bottomNavbar}> + <RNTesterNavbar + screen={this.state.screen} + onNavigate={this._handleAction} + /> + </View> + </> ); } } const styles = StyleSheet.create({ + container: { + flex: 1, + }, headerContainer: { borderBottomWidth: StyleSheet.hairlineWidth, }, @@ -236,6 +324,13 @@ const styles = StyleSheet.create({ exampleContainer: { flex: 1, }, + bottomNavbar: { + bottom: 0, + width: '100%', + display: 'flex', + flexDirection: 'column', + position: 'absolute', + }, }); AppRegistry.registerComponent('SetPropertiesExampleApp', () => diff --git a/packages/rn-tester/js/assets/bookmark-filled.png b/packages/rn-tester/js/assets/bookmark-filled.png new file mode 100755 index 00000000000000..a6e5075de537a8 Binary files /dev/null and b/packages/rn-tester/js/assets/bookmark-filled.png differ diff --git a/packages/rn-tester/js/assets/bookmark-outline-blue.png b/packages/rn-tester/js/assets/bookmark-outline-blue.png new file mode 100644 index 00000000000000..a17ef85991fba1 Binary files /dev/null and b/packages/rn-tester/js/assets/bookmark-outline-blue.png differ diff --git a/packages/rn-tester/js/assets/bookmark-outline-gray.png b/packages/rn-tester/js/assets/bookmark-outline-gray.png new file mode 100644 index 00000000000000..2b2281e5373db0 Binary files /dev/null and b/packages/rn-tester/js/assets/bookmark-outline-gray.png differ diff --git a/packages/rn-tester/js/assets/bookmark-outline.png b/packages/rn-tester/js/assets/bookmark-outline.png new file mode 100755 index 00000000000000..a939dac5740acc Binary files /dev/null and b/packages/rn-tester/js/assets/bookmark-outline.png differ diff --git a/packages/rn-tester/js/assets/bottom-nav-apis-icon-active.png b/packages/rn-tester/js/assets/bottom-nav-apis-icon-active.png new file mode 100644 index 00000000000000..772111340dc91a Binary files /dev/null and b/packages/rn-tester/js/assets/bottom-nav-apis-icon-active.png differ diff --git a/packages/rn-tester/js/assets/bottom-nav-apis-icon-inactive.png b/packages/rn-tester/js/assets/bottom-nav-apis-icon-inactive.png new file mode 100644 index 00000000000000..29de7b79b6086a Binary files /dev/null and b/packages/rn-tester/js/assets/bottom-nav-apis-icon-inactive.png differ diff --git a/packages/rn-tester/js/assets/bottom-nav-bookmark-fill.png b/packages/rn-tester/js/assets/bottom-nav-bookmark-fill.png new file mode 100644 index 00000000000000..29d29dff8f2ba1 Binary files /dev/null and b/packages/rn-tester/js/assets/bottom-nav-bookmark-fill.png differ diff --git a/packages/rn-tester/js/assets/bottom-nav-bookmark-outline.png b/packages/rn-tester/js/assets/bottom-nav-bookmark-outline.png new file mode 100644 index 00000000000000..dfe68645aa1ca6 Binary files /dev/null and b/packages/rn-tester/js/assets/bottom-nav-bookmark-outline.png differ diff --git a/packages/rn-tester/js/assets/bottom-nav-center-box.png b/packages/rn-tester/js/assets/bottom-nav-center-box.png new file mode 100644 index 00000000000000..31b0427bb776ab Binary files /dev/null and b/packages/rn-tester/js/assets/bottom-nav-center-box.png differ diff --git a/packages/rn-tester/js/assets/bottom-nav-components-icon-active.png b/packages/rn-tester/js/assets/bottom-nav-components-icon-active.png new file mode 100644 index 00000000000000..22c56d70603808 Binary files /dev/null and b/packages/rn-tester/js/assets/bottom-nav-components-icon-active.png differ diff --git a/packages/rn-tester/js/assets/bottom-nav-components-icon-inactive.png b/packages/rn-tester/js/assets/bottom-nav-components-icon-inactive.png new file mode 100644 index 00000000000000..aa051c06e07f06 Binary files /dev/null and b/packages/rn-tester/js/assets/bottom-nav-components-icon-inactive.png differ diff --git a/packages/rn-tester/js/assets/documentation.png b/packages/rn-tester/js/assets/documentation.png new file mode 100644 index 00000000000000..d43e2593969e66 Binary files /dev/null and b/packages/rn-tester/js/assets/documentation.png differ diff --git a/packages/rn-tester/js/assets/empty.png b/packages/rn-tester/js/assets/empty.png new file mode 100644 index 00000000000000..c659c6c057486e Binary files /dev/null and b/packages/rn-tester/js/assets/empty.png differ diff --git a/packages/rn-tester/js/assets/header-back-button.png b/packages/rn-tester/js/assets/header-back-button.png new file mode 100644 index 00000000000000..8fa30d327e6787 Binary files /dev/null and b/packages/rn-tester/js/assets/header-back-button.png differ diff --git a/packages/rn-tester/js/assets/search-icon.png b/packages/rn-tester/js/assets/search-icon.png new file mode 100644 index 00000000000000..ac8e5eb99a825f Binary files /dev/null and b/packages/rn-tester/js/assets/search-icon.png differ diff --git a/packages/rn-tester/js/components/ExamplePage.js b/packages/rn-tester/js/components/ExamplePage.js new file mode 100644 index 00000000000000..44661b2f11749f --- /dev/null +++ b/packages/rn-tester/js/components/ExamplePage.js @@ -0,0 +1,100 @@ +/** + * 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'; + +import * as React from 'react'; +import {StyleSheet, View, Text, Dimensions} from 'react-native'; + +type Props = $ReadOnly<{| + children?: React.Node, + title: string, + description?: ?string, + category?: ?string, + ios?: ?boolean, + android?: ?boolean, +|}>; + +const ScreenWidth = Dimensions.get('window').width; +import {RNTesterThemeContext} from './RNTesterTheme'; + +export default function ExamplePage(props: Props): React.Node { + const theme = React.useContext(RNTesterThemeContext); + + const description = props.description ?? ''; + const onAndroid = props.android; + const onIos = props.ios; + const category = props.category; + + return ( + <> + <View style={styles.titleView}> + <Text style={{marginVertical: 8, fontSize: 16}}>{description}</Text> + <View style={styles.rowStyle}> + <Text style={{color: theme.SecondaryLabelColor, width: 65}}> + {category || 'Other'} + </Text> + <View style={styles.platformLabelStyle}> + <Text + style={{ + color: onIos ? '#787878' : theme.SeparatorColor, + fontWeight: onIos ? '500' : '300', + }}> + iOS + </Text> + <Text + style={{ + color: onAndroid ? '#787878' : theme.SeparatorColor, + fontWeight: onAndroid ? '500' : '300', + }}> + Android + </Text> + </View> + </View> + </View> + <View style={styles.examplesContainer}>{props.children}</View> + </> + ); +} + +const styles = StyleSheet.create({ + titleView: { + backgroundColor: '#F3F8FF', + paddingHorizontal: 25, + paddingTop: 8, + overflow: 'hidden', + }, + iconContainer: { + flexDirection: 'row', + justifyContent: 'flex-end', + }, + examplesContainer: { + width: ScreenWidth, + flexGrow: 1, + }, + description: { + paddingVertical: 5, + flexDirection: 'row', + justifyContent: 'space-between', + }, + docsContainer: { + alignContent: 'center', + justifyContent: 'center', + }, + rowStyle: { + flexDirection: 'row', + justifyContent: 'space-between', + }, + platformLabelStyle: { + flexDirection: 'row', + width: 100, + justifyContent: 'space-between', + }, +}); diff --git a/packages/rn-tester/js/components/RNTesterBlock.js b/packages/rn-tester/js/components/RNTesterBlock.js index 1a0abba5eae33f..1f144d8360977e 100644 --- a/packages/rn-tester/js/components/RNTesterBlock.js +++ b/packages/rn-tester/js/components/RNTesterBlock.js @@ -10,10 +10,9 @@ 'use strict'; -const React = require('react'); - -const {StyleSheet, Text, View} = require('react-native'); +import * as React from 'react'; import {RNTesterThemeContext} from './RNTesterTheme'; +import {StyleSheet, Text, View} from 'react-native'; type Props = $ReadOnly<{| children?: React.Node, @@ -21,83 +20,48 @@ type Props = $ReadOnly<{| description?: ?string, |}>; -type State = {| - description: ?string, -|}; - -class RNTesterBlock extends React.Component<Props, State> { - state: State = {description: null}; - - render(): React.Node { - const description = this.props.description ? ( - <RNTesterThemeContext.Consumer> - {theme => { - return ( - <Text style={[styles.descriptionText, {color: theme.LabelColor}]}> - {this.props.description} - </Text> - ); - }} - </RNTesterThemeContext.Consumer> - ) : null; - - return ( - <RNTesterThemeContext.Consumer> - {theme => { - return ( - <View - style={[ - styles.container, - { - borderColor: theme.SeparatorColor, - backgroundColor: theme.SystemBackgroundColor, - }, - ]}> - <View - style={[ - styles.titleContainer, - { - borderBottomColor: theme.SeparatorColor, - backgroundColor: theme.QuaternarySystemFillColor, - }, - ]}> - <Text style={[styles.titleText, {color: theme.LabelColor}]}> - {this.props.title} - </Text> - {description} - </View> - <View style={styles.children}>{this.props.children}</View> - </View> - ); - }} - </RNTesterThemeContext.Consumer> - ); - } -} +/** functional component for generating example blocks */ +const RNTesterBlock = (props: Props): React.Node => { + const {description, title, children} = props; + const theme = React.useContext(RNTesterThemeContext); + return ( + <View style={[[styles.container], {borderColor: theme.SeparatorColor}]}> + <View style={[styles.titleContainer]}> + <Text style={[styles.titleText]}>{title}</Text> + <Text + style={[styles.descriptionText, {marginTop: description ? 10 : 0}]}> + {description} + </Text> + </View> + <View style={styles.children}>{children}</View> + </View> + ); +}; const styles = StyleSheet.create({ container: { - borderRadius: 3, - borderWidth: 0.5, - margin: 10, - marginVertical: 5, - overflow: 'hidden', + borderRadius: 0, + borderWidth: 1, + marginTop: 30, + marginHorizontal: 20, + backgroundColor: 'white', + }, + titleText: { + fontSize: 18, + fontWeight: '300', }, titleContainer: { - borderBottomWidth: 0.5, - borderTopLeftRadius: 3, - borderTopRightRadius: 2.5, paddingHorizontal: 10, paddingVertical: 5, }, - titleText: { - fontSize: 14, - fontWeight: '500', - }, descriptionText: { - fontSize: 14, + fontSize: 12, + opacity: 0.5, + color: 'black', }, children: { + paddingVertical: 10, + paddingHorizontal: 10, margin: 10, }, }); diff --git a/packages/rn-tester/js/components/RNTesterBookmark.js b/packages/rn-tester/js/components/RNTesterBookmark.js new file mode 100644 index 00000000000000..62142bd8ad7026 --- /dev/null +++ b/packages/rn-tester/js/components/RNTesterBookmark.js @@ -0,0 +1,38 @@ +/** + * 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. + * + * @flow strict-local + * @format + */ + +'use strict'; + +import * as React from 'react'; +import type {RNTesterExample} from '../types/RNTesterTypes'; + +export type RNTesterBookmark = { + Components: {...}, + Api: {...}, + AddApi: (apiName: string, api: RNTesterExample) => mixed, + AddComponent: (componentName: string, component: RNTesterExample) => mixed, + RemoveApi: (apiName: string) => mixed, + RemoveComponent: (componentName: string) => mixed, + checkBookmark: (title: string, key: string) => mixed, +}; + +export const bookmarks: RNTesterBookmark = { + Components: {}, + Api: {}, + AddComponent: () => {}, + RemoveComponent: () => {}, + AddApi: () => {}, + RemoveApi: () => {}, + checkBookmark: () => {}, +}; + +export const RNTesterBookmarkContext: React.Context<RNTesterBookmark> = React.createContext( + bookmarks, +); diff --git a/packages/rn-tester/js/components/RNTesterBookmarkButton.js b/packages/rn-tester/js/components/RNTesterBookmarkButton.js new file mode 100644 index 00000000000000..63de5a5b45270a --- /dev/null +++ b/packages/rn-tester/js/components/RNTesterBookmarkButton.js @@ -0,0 +1,62 @@ +/** + * 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'); + +import {TouchableOpacity, Image, StyleSheet} from 'react-native'; + +type Props = {| + isActive: boolean, + onPress: Function, + size: number, +|}; + +class RNTesterBookmarkButton extends React.Component<Props> { + constructor(props: Props) { + super(props); + } + + render(): React.Node { + const {size, onPress, isActive} = this.props; + return ( + <TouchableOpacity + style={[ + styles.imageViewStyle, + { + height: size + 5, + width: size + 5, + borderRadius: Math.floor(size + 5 / 2), + }, + ]} + onPress={onPress}> + <Image + style={{height: size, width: size}} + source={ + isActive + ? require('../assets/bookmark-filled.png') + : require('../assets/bookmark-outline-gray.png') + } + /> + </TouchableOpacity> + ); + } +} + +const styles = StyleSheet.create({ + imageViewStyle: { + backgroundColor: 'blue', + justifyContent: 'center', + alignItems: 'center', + }, +}); + +module.exports = RNTesterBookmarkButton; diff --git a/packages/rn-tester/js/components/RNTesterComponentTitle.js b/packages/rn-tester/js/components/RNTesterComponentTitle.js new file mode 100644 index 00000000000000..66b0c2899c5a15 --- /dev/null +++ b/packages/rn-tester/js/components/RNTesterComponentTitle.js @@ -0,0 +1,45 @@ +/** + * 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 + */ + +const React = require('react'); +import {RNTesterThemeContext} from './RNTesterTheme'; +const {StyleSheet, Text} = require('react-native'); + +type Props = $ReadOnly<{| + children: string, +|}>; + +class RNTesterComponentTitle extends React.Component<Props> { + constructor(props: Props) { + super(props); + } + + render(): React.Node { + return ( + <RNTesterThemeContext.Consumer> + {theme => ( + <Text style={[styles.titleText, {color: theme.LabelColor}]}> + {this.props.children} + </Text> + )} + </RNTesterThemeContext.Consumer> + ); + } +} + +const styles = StyleSheet.create({ + titleText: { + fontSize: 20, + fontWeight: '400', + marginBottom: 5, + }, +}); + +module.exports = RNTesterComponentTitle; diff --git a/packages/rn-tester/js/components/RNTesterDocumentationURL.js b/packages/rn-tester/js/components/RNTesterDocumentationURL.js new file mode 100644 index 00000000000000..8d6d8423591e81 --- /dev/null +++ b/packages/rn-tester/js/components/RNTesterDocumentationURL.js @@ -0,0 +1,39 @@ +/** + * 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 + */ + +'use strict'; + +import React from 'react'; +import {Image, StyleSheet, TouchableOpacity} from 'react-native'; +import openURLInBrowser from 'react-native/Libraries/Core/Devtools/openURLInBrowser'; + +const RNTesterDocumentationURL = ({documentationURL}) => ( + <TouchableOpacity + style={styles.container} + onPress={() => openURLInBrowser(documentationURL)}> + <Image + source={require('../assets/documentation.png')} + style={styles.icon} + /> + </TouchableOpacity> +); + +export default RNTesterDocumentationURL; + +const styles = StyleSheet.create({ + container: { + textDecorationLine: 'underline', + position: 'absolute', + bottom: 0, + right: -15, + }, + icon: { + height: 24, + }, +}); diff --git a/packages/rn-tester/js/components/RNTesterExampleContainer.js b/packages/rn-tester/js/components/RNTesterExampleContainer.js index 7eed43740dc45f..271a61859b10e2 100644 --- a/packages/rn-tester/js/components/RNTesterExampleContainer.js +++ b/packages/rn-tester/js/components/RNTesterExampleContainer.js @@ -13,9 +13,9 @@ const React = require('react'); const {Platform} = require('react-native'); const RNTesterBlock = require('./RNTesterBlock'); const RNTesterExampleFilter = require('./RNTesterExampleFilter'); -const RNTesterPage = require('./RNTesterPage'); const invariant = require('invariant'); +import ExamplePage from './ExamplePage'; class RNTesterExampleContainer extends React.Component { renderExample(example, i) { @@ -42,13 +42,29 @@ class RNTesterExampleContainer extends React.Component { module.examples.length === 1, 'If noExampleContainer is specified, only one example is allowed', ); - return module.examples[0].render(); + return ( + <ExamplePage + title={module.title} + description={module.description} + android={!module.platform || module.platform === 'android'} + ios={!module.platform || module.platform === 'ios'} + documentationURL={module.documentationURL} + category={module.category}> + {module.examples[0].render()} + </ExamplePage> + ); } if (module.examples.length === 1) { return ( - <RNTesterPage title={this.props.title}> - {this.renderExample(module.examples[0])} - </RNTesterPage> + <ExamplePage + title={module.title} + description={module.description} + android={!module.platform || module.platform === 'android'} + ios={!module.platform || module.platform === 'ios'} + documentationURL={module.documentationURL} + category={module.category}> + {module.examples[0].render()} + </ExamplePage> ); } @@ -63,16 +79,24 @@ class RNTesterExampleContainer extends React.Component { ]; return ( - <RNTesterPage title={this.props.title}> + <ExamplePage + title={module.title} + description={module.description} + android={!module.platform || module.platform === 'android'} + ios={!module.platform || module.platform === 'ios'} + documentationURL={module.documentationURL} + category={module.category}> <RNTesterExampleFilter testID="example_search" + page="examples_page" + hideFilterPills={true} sections={sections} filter={filter} render={({filteredSections}) => filteredSections[0].data.map(this.renderExample) } /> - </RNTesterPage> + </ExamplePage> ); } } diff --git a/packages/rn-tester/js/components/RNTesterExampleFilter.js b/packages/rn-tester/js/components/RNTesterExampleFilter.js index 992f30e6bc6b0c..403590e64df4d4 100644 --- a/packages/rn-tester/js/components/RNTesterExampleFilter.js +++ b/packages/rn-tester/js/components/RNTesterExampleFilter.js @@ -11,23 +11,36 @@ 'use strict'; const React = require('react'); - -const {StyleSheet, TextInput, View} = require('react-native'); +const RNTesterListFilters = require('./RNTesterListFilters'); +const { + StyleSheet, + TextInput, + View, + ScrollView, + Image, +} = require('react-native'); import {RNTesterThemeContext} from './RNTesterTheme'; +import type {RNTesterExample} from '../types/RNTesterTypes'; type Props = { filter: Function, render: Function, - sections: Object, disableSearch?: boolean, testID?: string, + hideFilterPills?: boolean, + page: string, // possible values -> examples_page, components_page, bookmarks_page + sections: Array<{ + data: Array<RNTesterExample>, + title: string, + key: string, + }>, ... }; -type State = {filter: string, ...}; +type State = {filter: string, category: string, ...}; class RNTesterExampleFilter extends React.Component<Props, State> { - state: State = {filter: ''}; + state: State = {filter: '', category: ''}; render(): React.Node { const filterText = this.state.filter; @@ -43,22 +56,52 @@ class RNTesterExampleFilter extends React.Component<Props, State> { ); } - const filter = example => - this.props.disableSearch || this.props.filter({example, filterRegex}); + const filter = example => { + const category = this.state.category; + return ( + this.props.disableSearch || + this.props.filter({example, filterRegex, category}) + ); + }; - const filteredSections = this.props.sections.map(section => ({ + let filteredSections = this.props.sections.map(section => ({ ...section, data: section.data.filter(filter), })); + if (this.state.filter.trim() !== '' || this.state.category.trim() !== '') { + filteredSections = filteredSections.filter( + section => section.title !== 'Recently viewed', + ); + } + return ( <View style={styles.container}> {this._renderTextInput()} - {this.props.render({filteredSections})} + {this._renderFilteredSections(filteredSections)} </View> ); } + _renderFilteredSections(filteredSections): ?React.Element<any> { + if (this.props.page === 'examples_page') { + return ( + <ScrollView + keyboardShouldPersistTaps="handled" + keyboardDismissMode="interactive"> + {this.props.render({filteredSections})} + {/** + * This is a fake list item. It is needed to provide the ScrollView some bottom padding. + * The height of this item is basically ScreenHeight - the height of (Header + bottom navbar) + * */} + <View style={{height: 350}} /> + </ScrollView> + ); + } else { + return this.props.render({filteredSections}); + } + } + _renderTextInput(): ?React.Element<any> { if (this.props.disableSearch) { return null; @@ -67,32 +110,41 @@ class RNTesterExampleFilter extends React.Component<Props, State> { <RNTesterThemeContext.Consumer> {theme => { return ( - <View - style={[ - styles.searchRow, - {backgroundColor: theme.GroupedBackgroundColor}, - ]}> - <TextInput - autoCapitalize="none" - autoCorrect={false} - clearButtonMode="always" - onChangeText={text => { - this.setState(() => ({filter: text})); - }} - placeholder="Search..." - placeholderTextColor={theme.PlaceholderTextColor} - underlineColorAndroid="transparent" - style={[ - styles.searchTextInput, - { - color: theme.LabelColor, - backgroundColor: theme.SecondaryGroupedBackgroundColor, - borderColor: theme.QuaternaryLabelColor, - }, - ]} - testID={this.props.testID} - value={this.state.filter} - /> + <View style={[styles.searchRow, {backgroundColor: '#F3F8FF'}]}> + <View style={styles.textInputStyle}> + <Image + source={require('../assets/search-icon.png')} + style={styles.searchIcon} + /> + <TextInput + autoCapitalize="none" + autoCorrect={false} + clearButtonMode="always" + onChangeText={text => { + this.setState(() => ({filter: text})); + }} + placeholder="Search..." + placeholderTextColor={theme.PlaceholderTextColor} + underlineColorAndroid="transparent" + style={[ + styles.searchTextInput, + { + color: theme.LabelColor, + backgroundColor: theme.SecondaryGroupedBackgroundColor, + borderColor: theme.QuaternaryLabelColor, + }, + ]} + testID={this.props.testID} + value={this.state.filter} + /> + </View> + {!this.props.hideFilterPills && ( + <RNTesterListFilters + onFilterButtonPress={filterLabel => + this.setState({category: filterLabel}) + } + /> + )} </View> ); }} @@ -102,18 +154,36 @@ class RNTesterExampleFilter extends React.Component<Props, State> { } const styles = StyleSheet.create({ + container: { + flex: 1, + }, searchRow: { - padding: 10, + paddingHorizontal: 20, + paddingVertical: 10, + alignItems: 'center', }, searchTextInput: { - borderRadius: 3, + borderRadius: 6, borderWidth: 1, - paddingLeft: 8, paddingVertical: 0, height: 35, - }, - container: { flex: 1, + alignSelf: 'center', + paddingLeft: 35, + }, + textInputStyle: { + flexDirection: 'row', + alignItems: 'center', + position: 'relative', + right: 10, + }, + searchIcon: { + width: 20, + height: 20, + position: 'relative', + top: 0, + left: 27, + zIndex: 2, }, }); diff --git a/packages/rn-tester/js/components/RNTesterExampleList.js b/packages/rn-tester/js/components/RNTesterExampleList.js index 0081c105e05bdb..456ffca9fdc5c0 100644 --- a/packages/rn-tester/js/components/RNTesterExampleList.js +++ b/packages/rn-tester/js/components/RNTesterExampleList.js @@ -9,9 +9,11 @@ */ 'use strict'; +import type {RNTesterBookmark} from './RNTesterBookmark.js'; const RNTesterActions = require('../utils/RNTesterActions'); const RNTesterExampleFilter = require('./RNTesterExampleFilter'); +const RNTesterComponentTitle = require('./RNTesterComponentTitle'); const React = require('react'); const { @@ -20,16 +22,21 @@ const { StyleSheet, Text, TouchableHighlight, + Image, View, } = require('react-native'); import type {ViewStyleProp} from '../../../../Libraries/StyleSheet/StyleSheet'; import type {RNTesterExample} from '../types/RNTesterTypes'; - import {RNTesterThemeContext} from './RNTesterTheme'; +import {RNTesterBookmarkContext} from './RNTesterBookmark'; type Props = { + screen: string, onNavigate: Function, + updateRecentlyViewedList: Function, + recentApis: Array<RNTesterExample>, + recentComponents: Array<RNTesterExample>, list: { ComponentExamples: Array<RNTesterExample>, APIExamples: Array<RNTesterExample>, @@ -39,15 +46,68 @@ type Props = { ... }; -class RowComponent extends React.PureComponent<{ +type State = { + components: Array<RNTesterExample>, + api: Array<RNTesterExample>, + recentComponents: Array<RNTesterExample>, + recentApis: Array<RNTesterExample>, + updateRecentlyViewedList: Function, +}; + +type ButtonState = {active: boolean, key: string, ...}; +type ButtonProps = { item: Object, + section: Object, + active: boolean, onNavigate: Function, onPress?: Function, onShowUnderlay?: Function, onHideUnderlay?: Function, + updateRecentlyViewedList: Function, ... -}> { +}; + +class RowComponent extends React.PureComponent<ButtonProps, ButtonState> { + static contextType = RNTesterBookmarkContext; + + constructor(props: ButtonProps) { + super(props); + this.state = { + active: props.active, + title: props.item.module.title, + key: props.section.key, + }; + } + + static getDerivedStateFromProps(nextProps, prevState) { + if (nextProps.active !== prevState.active) { + return {active: nextProps.active}; + } + return null; + } + + onButtonPress = () => { + let bookmark = this.context; + if (!this.state.active) { + if (this.state.key === 'APIS' || this.state.key === 'RECENT_APIS') { + bookmark.AddApi(this.props.item.module.title, this.props.item); + } else { + bookmark.AddComponent(this.props.item.module.title, this.props.item); + } + } else { + if (this.state.key === 'APIS' || this.state.key === 'RECENT_APIS') { + bookmark.RemoveApi(this.props.item.module.title); + } else { + bookmark.RemoveComponent(this.props.item.module.title); + } + } + this.setState({ + active: !this.state.active, + }); + }; + _onPress = () => { + this.props.updateRecentlyViewedList(); if (this.props.onPress) { this.props.onPress(); return; @@ -56,6 +116,9 @@ class RowComponent extends React.PureComponent<{ }; render() { const {item} = this.props; + const platform = item.module.platform; + const onIos = !platform || platform === 'ios'; + const onAndroid = !platform || platform === 'android'; return ( <RNTesterThemeContext.Consumer> {theme => { @@ -63,22 +126,62 @@ class RowComponent extends React.PureComponent<{ <TouchableHighlight onShowUnderlay={this.props.onShowUnderlay} onHideUnderlay={this.props.onHideUnderlay} + accessibilityLabel={ + item.module.title + ' ' + item.module.description + } + style={styles.listItem} + underlayColor={'rgb(242,242,242)'} onPress={this._onPress}> <View style={[ styles.row, {backgroundColor: theme.SystemBackgroundColor}, ]}> - <Text style={[styles.rowTitleText, {color: theme.LabelColor}]}> - {item.module.title} - </Text> + <View style={styles.topRowStyle}> + <RNTesterComponentTitle> + {item.module.title} + </RNTesterComponentTitle> + <TouchableHighlight + style={styles.imageViewStyle} + onPress={() => this.onButtonPress()}> + <Image + style={styles.imageStyle} + source={ + this.state.active + ? require('../assets/bookmark-outline-blue.png') + : require('../assets/bookmark-outline-gray.png') + } + /> + </TouchableHighlight> + </View> <Text style={[ styles.rowDetailText, - {color: theme.SecondaryLabelColor}, + {color: theme.SecondaryLabelColor, marginBottom: 5}, ]}> {item.module.description} </Text> + <View style={styles.bottomRowStyle}> + <Text style={{color: theme.SecondaryLabelColor, width: 65}}> + {item.module.category || 'Other'} + </Text> + <View style={styles.platformLabelStyle}> + <Text + style={{ + color: onIos ? '#787878' : theme.SeparatorColor, + fontWeight: onIos ? '500' : '300', + }}> + iOS + </Text> + <Text + style={{ + color: onAndroid ? '#787878' : theme.SeparatorColor, + fontWeight: onAndroid ? '500' : '300', + }}> + Android + </Text> + </View> + </View> </View> </TouchableHighlight> ); @@ -107,23 +210,117 @@ const renderSectionHeader = ({section}) => ( </RNTesterThemeContext.Consumer> ); -class RNTesterExampleList extends React.Component<Props, $FlowFixMeState> { +class RNTesterExampleList extends React.Component<Props, State> { + static contextType: React.Context<RNTesterBookmark> = RNTesterBookmarkContext; + + constructor(props: Props) { + super(props); + this.state = { + components: props.list.ComponentExamples, + api: props.list.APIExamples, + recentComponents: props.recentComponents, + recentApis: props.recentApis, + updateRecentlyViewedList: (item, key) => + props.updateRecentlyViewedList(item, key), + }; + } + + static getDerivedStateFromProps(nextProps: Props, prevState: State): State { + if ( + nextProps.recentComponents.every( + (component, index) => component !== prevState.recentComponents[index], + ) && + nextProps.recentApis.every( + (api, index) => api !== prevState.recentApis[index], + ) + ) { + return { + ...prevState, + recentComponents: nextProps.recentComponents, + recentApis: nextProps.recentApis, + }; + } + return prevState; + } + render(): React.Node { - const filter = ({example, filterRegex}) => - filterRegex.test(example.module.title) && !Platform.isTV; - - const sections = [ - { - data: this.props.list.ComponentExamples, - title: 'COMPONENTS', - key: 'c', - }, - { - data: this.props.list.APIExamples, - title: 'APIS', - key: 'a', - }, - ]; + const bookmark = this.context; + const filter = ({example, filterRegex, category}) => + filterRegex.test(example.module.title) && + (!category || example.module.category === category) && + (!Platform.isTV || example.supportsTVOS); + + const {screen} = this.props; + let sections = []; + + if (screen === 'component') { + if (this.state.recentComponents.length > 0) { + sections = [ + { + data: this.state.recentComponents, + key: 'RECENT_COMPONENTS', + title: 'Recently viewed', + }, + { + data: this.state.components, + key: 'COMPONENTS', + title: 'Components', + }, + ]; + } else { + sections = [ + { + data: this.state.components, + key: 'COMPONENTS', + title: 'Components', + }, + ]; + } + } else if (screen === 'api') { + if (this.state.recentApis.length > 0) { + sections = [ + { + data: this.state.recentApis, + key: 'RECENT_APIS', + title: 'Recently viewed', + }, + { + data: this.state.api, + key: 'APIS', + title: 'APIS', + }, + ]; + } else { + sections = [ + { + data: this.state.api, + key: 'APIS', + title: 'APIS', + }, + ]; + } + } else if (screen === 'bookmark') { + sections = [ + { + data: Object.values(bookmark.Components), + title: 'COMPONENTS', + key: 'COMPONENTS', + }, + { + data: Object.values(bookmark.Api), + title: 'APIS', + key: 'APIS', + }, + ]; + } else { + sections = []; + } + + const isEmpty = sections.filter(s => s.data.length).length === 0; + + if (isEmpty) { + return <EmptyState />; + } return ( <RNTesterThemeContext.Consumer> @@ -135,24 +332,23 @@ class RNTesterExampleList extends React.Component<Props, $FlowFixMeState> { this.props.style, {backgroundColor: theme.SecondaryGroupedBackgroundColor}, ]}> - {this._renderTitleRow()} <RNTesterExampleFilter testID="explorer_search" + page="components_page" + // $FlowFixMe sections={sections} filter={filter} render={({filteredSections}) => ( <SectionList - ItemSeparatorComponent={ItemSeparator} - contentContainerStyle={{ - backgroundColor: theme.SeparatorColor, - }} - style={{backgroundColor: theme.SystemBackgroundColor}} sections={filteredSections} + extraData={filteredSections} renderItem={this._renderItem} + ItemSeparatorComponent={ItemSeparator} keyboardShouldPersistTaps="handled" automaticallyAdjustContentInsets={false} keyboardDismissMode="on-drag" renderSectionHeader={renderSectionHeader} + ListFooterComponent={() => <View style={{height: 200}} />} /> )} /> @@ -163,37 +359,22 @@ class RNTesterExampleList extends React.Component<Props, $FlowFixMeState> { ); } - _renderItem = ({item, separators}) => ( - <RowComponent - item={item} - onNavigate={this.props.onNavigate} - onShowUnderlay={separators.highlight} - onHideUnderlay={separators.unhighlight} - /> - ); - - _renderTitleRow(): ?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.displayTitleRow) { - return null; - } + _renderItem = ({item, section, separators, index}) => { + let bookmark = this.context; return ( <RowComponent - item={{ - module: { - title: 'RNTester', - description: 'React Native Examples', - }, - }} + item={item} + section={section} + active={!bookmark.checkBookmark(item.module.title, section.key)} onNavigate={this.props.onNavigate} - onPress={() => { - this.props.onNavigate(RNTesterActions.ExampleList()); - }} + onShowUnderlay={separators.highlight} + onHideUnderlay={separators.unhighlight} + updateRecentlyViewedList={() => + this.state.updateRecentlyViewedList(item, section.key) + } /> ); - } + }; _handleRowPress(exampleKey: string): void { this.props.onNavigate(RNTesterActions.ExampleAction(exampleKey)); @@ -219,10 +400,37 @@ const ItemSeparator = ({highlighted}) => ( </RNTesterThemeContext.Consumer> ); +const EmptyState = () => ( + <View style={styles.emptyContainer}> + <View style={styles.emptyContainerInner}> + <Image + source={require('../assets/empty.png')} + resizeMode="contain" + style={styles.emptyImage} + /> + <View> + <Text style={styles.heading}>Bookmarks are empty</Text> + <Text style={styles.subheading}> + Please tap the{' '} + <Image + source={require('../assets/bookmark-outline-gray.png')} + resizeMode="contain" + style={styles.bookmarkIcon} + />{' '} + icon to bookmark examples. + </Text> + </View> + </View> + </View> +); + const styles = StyleSheet.create({ listContainer: { flex: 1, }, + listItem: { + backgroundColor: Platform.select({ios: '#FFFFFF', android: '#F3F8FF'}), + }, sectionHeader: { padding: 5, fontWeight: '500', @@ -231,23 +439,77 @@ const styles = StyleSheet.create({ row: { justifyContent: 'center', paddingHorizontal: 15, - paddingVertical: 8, + paddingVertical: 12, + marginVertical: Platform.select({ios: 4, android: 8}), + marginHorizontal: 15, + overflow: 'hidden', + elevation: 5, }, separator: { - height: StyleSheet.hairlineWidth, - marginLeft: 15, + height: Platform.select({ios: StyleSheet.hairlineWidth, android: 0}), + marginHorizontal: Platform.select({ios: 15, android: 0}), }, separatorHighlighted: { height: StyleSheet.hairlineWidth, }, - rowTitleText: { - fontSize: 17, - fontWeight: '500', + topRowStyle: { + flexDirection: 'row', + justifyContent: 'space-between', + flex: 1, + }, + bottomRowStyle: { + flexDirection: 'row', + justifyContent: 'space-between', }, rowDetailText: { - fontSize: 15, + fontSize: 12, lineHeight: 20, }, + imageViewStyle: { + height: 30, + width: 30, + borderRadius: 15, + justifyContent: 'center', + alignItems: 'center', + position: 'relative', + bottom: 5, + }, + imageStyle: { + height: 25, + width: 25, + }, + platformLabelStyle: { + flexDirection: 'row', + width: 100, + justifyContent: 'space-between', + }, + emptyContainer: { + flex: 1, + paddingHorizontal: 40, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: 'white', + }, + emptyContainerInner: { + marginTop: -150, + }, + emptyImage: { + maxWidth: '100%', + height: 300, + }, + heading: { + fontSize: 24, + textAlign: 'center', + }, + subheading: { + fontSize: 16, + textAlign: 'center', + }, + bookmarkIcon: { + width: 24, + height: 24, + transform: [{translateY: 4}], + }, }); module.exports = RNTesterExampleList; diff --git a/packages/rn-tester/js/components/RNTesterListFilters.js b/packages/rn-tester/js/components/RNTesterListFilters.js new file mode 100644 index 00000000000000..804f8298a56000 --- /dev/null +++ b/packages/rn-tester/js/components/RNTesterListFilters.js @@ -0,0 +1,94 @@ +/** + * 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, Text, TouchableOpacity, View} = require('react-native'); + +const filters = ['Basic', 'UI', 'ListView', 'iOS', 'Android']; + +type Props = { + onFilterButtonPress: (filter: string) => mixed, //optional only for testing + ... +}; + +class RNTesterListFilters extends React.Component< + Props, + {|currentFilter: string|}, +> { + constructor(props: Props) { + super(props); + this.state = { + currentFilter: '', + }; + } + + filterPressed: any = filterLabel => { + const newFilter = + this.state.currentFilter === filterLabel ? '' : filterLabel; + this.setState({ + currentFilter: newFilter, + }); + this.props.onFilterButtonPress(newFilter); + }; + + render(): React.Node { + return ( + <View style={styles.container}> + {filters.map(filterLabel => { + return ( + <TouchableOpacity + key={filterLabel} + style={[ + styles.pillStyle, + { + backgroundColor: + this.state.currentFilter === filterLabel + ? '#045cfc' + : 'white', + }, + ]} + onPress={() => this.filterPressed(filterLabel)}> + <Text + style={{ + color: + this.state.currentFilter === filterLabel + ? 'white' + : '#045cfc', + }}> + {filterLabel} + </Text> + </TouchableOpacity> + ); + })} + </View> + ); + } +} + +const styles = StyleSheet.create({ + container: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + }, + pillStyle: { + padding: 10, + marginHorizontal: 5, + marginVertical: 10, + borderRadius: 20, + borderWidth: 1, + borderColor: '#045cfc', + }, +}); + +module.exports = RNTesterListFilters; diff --git a/packages/rn-tester/js/components/RNTesterNavbar.js b/packages/rn-tester/js/components/RNTesterNavbar.js new file mode 100644 index 00000000000000..81cd701aa683cd --- /dev/null +++ b/packages/rn-tester/js/components/RNTesterNavbar.js @@ -0,0 +1,194 @@ +/** + * 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 + */ + +import React from 'react'; +import {Text, View, StyleSheet, Image, Pressable} from 'react-native'; + +import {RNTesterThemeContext} from './RNTesterTheme'; +const RNTesterActions = require('../utils/RNTesterActions'); + +const RNTesterNavbar = ({onNavigate, screen}) => { + const theme = React.useContext(RNTesterThemeContext); + + /** to be attached to navigation framework */ + const isAPIActive = screen === 'api'; + const isComponentActive = screen === 'component'; + const isBookmarkActive = screen === 'bookmark'; + + return ( + <View> + {/** Bottom Navbar code */} + {/** component and APIs tab */} + <View style={styles.buttonContainer}> + {/** left tab with Components */} + <Pressable + testID="components-tab" + onPress={() => onNavigate(RNTesterActions.OpenList('component'))} + style={[styles.navButton, {backgroundColor: theme.BackgroundColor}]}> + <View + style={[ + styles.pressableContent, + isComponentActive ? styles.activeBar : null, + ]} + collapsable={false}> + <Image + style={styles.componentIcon} + source={ + isComponentActive + ? require('./../assets/bottom-nav-components-icon-active.png') + : require('./../assets/bottom-nav-components-icon-inactive.png') + } + /> + <Text + style={ + isComponentActive ? styles.activeText : styles.inactiveText + }> + Components + </Text> + </View> + </Pressable> + + {/** central tab with bookmark icon */} + <View style={styles.centerBox}> + <Image + style={styles.centralBoxCutout} + source={require('./../assets/bottom-nav-center-box.png')} + /> + + {/** floating button in center */} + <View style={styles.floatContainer}> + <Pressable + testID="bookmarks-tab" + onPress={() => { + onNavigate(RNTesterActions.OpenList('bookmark')); + }}> + <View + style={[ + styles.floatingButton, + {backgroundColor: theme.BorderColor}, + ]}> + <Image + style={styles.bookmarkIcon} + source={ + isBookmarkActive + ? require('../assets/bottom-nav-bookmark-fill.png') + : require('../assets/bottom-nav-bookmark-outline.png') + } + /> + </View> + </Pressable> + </View> + </View> + + {/** right tab with Components */} + <Pressable + testID="apis-tab" + onPress={() => { + onNavigate(RNTesterActions.OpenList('api')); + }} + style={[styles.navButton, {backgroundColor: theme.BackgroundColor}]}> + <View + style={[ + styles.pressableContent, + isAPIActive ? styles.activeBar : null, + ]} + collapsable={false}> + <Image + style={styles.apiIcon} + source={ + isAPIActive + ? require('./../assets/bottom-nav-apis-icon-active.png') + : require('./../assets/bottom-nav-apis-icon-inactive.png') + } + /> + <Text style={isAPIActive ? styles.activeText : styles.inactiveText}> + APIs + </Text> + </View> + </Pressable> + </View> + </View> + ); +}; + +const styles = StyleSheet.create({ + floatContainer: { + flex: 1, + zIndex: 2, + alignItems: 'center', + }, + buttonContainer: { + flex: 1, + flexDirection: 'row', + }, + floatingButton: { + top: -20, + width: 50, + height: 50, + borderRadius: 500, + alignContent: 'center', + shadowColor: 'black', + shadowOffset: { + height: 5, + width: 0, + }, + shadowOpacity: 0.9, + shadowRadius: 10, + elevation: 5, + }, + bookmarkIcon: { + width: 30, + height: 30, + margin: 10, + }, + componentIcon: { + width: 20, + height: 20, + alignSelf: 'center', + }, + apiIcon: { + width: 30, + height: 20, + alignSelf: 'center', + }, + activeText: { + color: '#5E5F62', + }, + inactiveText: { + color: '#B1B4BA', + }, + centralBoxCutout: { + height: '100%', + width: '100%', + position: 'absolute', + top: 0, + }, + centerBox: { + flex: 1, + height: 65, + }, + navButton: { + flex: 1, + height: 65, + justifyContent: 'center', + alignItems: 'center', + }, + pressableContent: { + flex: 1, + alignSelf: 'stretch', + justifyContent: 'center', + alignItems: 'center', + }, + activeBar: { + borderTopWidth: 2, + borderColor: '#005DFF', + }, +}); + +module.exports = RNTesterNavbar; diff --git a/packages/rn-tester/js/components/RNTesterTheme.js b/packages/rn-tester/js/components/RNTesterTheme.js index 5a1244ef36280c..5727f64da5ce80 100644 --- a/packages/rn-tester/js/components/RNTesterTheme.js +++ b/packages/rn-tester/js/components/RNTesterTheme.js @@ -35,6 +35,8 @@ export type RNTesterTheme = { LinkColor: ColorValue, SystemPurpleColor: ColorValue, ToolbarColor: ColorValue, + BackgroundColor: ColorValue, + BorderColor: ColorValue, ... }; @@ -59,6 +61,8 @@ export const RNTesterLightTheme = { LinkColor: '#007affff', SystemPurpleColor: '#af52deff', ToolbarColor: '#e9eaedff', + BackgroundColor: '#f3f8ffff', + BorderColor: '#005dffff', }; export const RNTesterDarkTheme = { @@ -82,6 +86,8 @@ export const RNTesterDarkTheme = { LinkColor: '#0984ffff', SystemPurpleColor: '#bf5af2ff', ToolbarColor: '#3c3c43ff', + BackgroundColor: '#0c0700ff', + BorderColor: '#005dffff', }; export const themes = {light: RNTesterLightTheme, dark: RNTesterDarkTheme}; diff --git a/packages/rn-tester/js/components/TextLegend.js b/packages/rn-tester/js/components/TextLegend.js index dd4a1be526132e..2f513038390857 100644 --- a/packages/rn-tester/js/components/TextLegend.js +++ b/packages/rn-tester/js/components/TextLegend.js @@ -12,7 +12,8 @@ const React = require('react'); -const {Picker, Text, View} = require('react-native'); +const {Text, View} = require('react-native'); +import {Picker} from 'react-native'; class TextLegend extends React.Component<*, *> { state: {| diff --git a/packages/rn-tester/js/components/UseCase.js b/packages/rn-tester/js/components/UseCase.js new file mode 100644 index 00000000000000..f76b9a4882a3f7 --- /dev/null +++ b/packages/rn-tester/js/components/UseCase.js @@ -0,0 +1,40 @@ +/** + * 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 + */ + +import * as React from 'react'; +import {View, Text, StyleSheet} from 'react-native'; + +type Props = $ReadOnly<{| + children?: React.Node, + title?: ?string, + note?: ?string, + ios?: ?boolean, + android?: ?boolean, +|}>; + +export default function UseCase(props: Props): React.Node { + const children = React.Children.toArray(props.children).filter( + child => child !== ' ', + ); + return ( + <View style={styles.container}> + <Text>{props.title}</Text> + <Text>{props.note}</Text> + {children} + </View> + ); +} + +const styles = StyleSheet.create({ + container: { + justifyContent: 'center', + alignItems: 'center', + }, +}); diff --git a/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js b/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js index 38fa2b3cdb2409..f9e8e6c12e5089 100644 --- a/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js +++ b/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js @@ -751,6 +751,7 @@ class AnnounceForAccessibility extends React.Component<{}> { } exports.title = 'Accessibility'; +exports.documentationURL = 'https://reactnative.dev/docs/accessibilityinfo'; exports.description = 'Examples of using Accessibility APIs.'; exports.examples = [ { diff --git a/packages/rn-tester/js/examples/ActionSheetIOS/ActionSheetIOSExample.js b/packages/rn-tester/js/examples/ActionSheetIOS/ActionSheetIOSExample.js index c1130357829df5..786811a725d53f 100644 --- a/packages/rn-tester/js/examples/ActionSheetIOS/ActionSheetIOSExample.js +++ b/packages/rn-tester/js/examples/ActionSheetIOS/ActionSheetIOSExample.js @@ -295,6 +295,7 @@ const style = StyleSheet.create({ }); exports.title = 'ActionSheetIOS'; +exports.category = 'iOS'; exports.description = "Interface to show iOS' action sheets"; exports.examples = [ { diff --git a/packages/rn-tester/js/examples/ActivityIndicator/ActivityIndicatorExample.js b/packages/rn-tester/js/examples/ActivityIndicator/ActivityIndicatorExample.js index 19f4f4df21fc13..793fac7bfa09ca 100644 --- a/packages/rn-tester/js/examples/ActivityIndicator/ActivityIndicatorExample.js +++ b/packages/rn-tester/js/examples/ActivityIndicator/ActivityIndicatorExample.js @@ -70,8 +70,10 @@ const styles = StyleSheet.create({ }); exports.displayName = (undefined: ?string); +exports.category = 'UI'; exports.framework = 'React'; -exports.title = '<ActivityIndicator>'; +exports.title = 'ActivityIndicator'; +exports.documentationURL = 'https://reactnative.dev/docs/activityindicator'; exports.description = 'Animated loading indicators.'; exports.examples = [ diff --git a/packages/rn-tester/js/examples/Alert/AlertExample.js b/packages/rn-tester/js/examples/Alert/AlertExample.js index dfafd870cf324b..4ebb4708107d1f 100644 --- a/packages/rn-tester/js/examples/Alert/AlertExample.js +++ b/packages/rn-tester/js/examples/Alert/AlertExample.js @@ -135,6 +135,7 @@ const styles = StyleSheet.create({ }); exports.title = 'Alert'; +exports.category = 'UI'; exports.description = 'Alerts display a concise and informative message ' + 'and prompt the user to make a decision.'; diff --git a/packages/rn-tester/js/examples/Animated/AnimatedExample.js b/packages/rn-tester/js/examples/Animated/AnimatedExample.js index 76c546dc9e06b1..126d8317e8d111 100644 --- a/packages/rn-tester/js/examples/Animated/AnimatedExample.js +++ b/packages/rn-tester/js/examples/Animated/AnimatedExample.js @@ -33,6 +33,8 @@ const styles = StyleSheet.create({ exports.framework = 'React'; exports.title = 'Animated - Examples'; +exports.category = 'UI'; +exports.documentationURL = 'https://reactnative.dev/docs/animated'; exports.description = ('Animated provides a powerful ' + 'and easy-to-use API for building modern, ' + 'interactive user experiences.': string); diff --git a/packages/rn-tester/js/examples/AppState/AppStateExample.js b/packages/rn-tester/js/examples/AppState/AppStateExample.js index 9e7f17eefc1201..54d83415780212 100644 --- a/packages/rn-tester/js/examples/AppState/AppStateExample.js +++ b/packages/rn-tester/js/examples/AppState/AppStateExample.js @@ -71,6 +71,8 @@ class AppStateSubscription extends React.Component< } exports.title = 'AppState'; +exports.category = 'Basic'; +exports.documentationURL = 'https://reactnative.dev/docs/appstate'; exports.description = 'app background status'; exports.examples = [ { diff --git a/packages/rn-tester/js/examples/Appearance/AppearanceExample.js b/packages/rn-tester/js/examples/Appearance/AppearanceExample.js index 741c265d3c744f..f4e23a6871e3c2 100644 --- a/packages/rn-tester/js/examples/Appearance/AppearanceExample.js +++ b/packages/rn-tester/js/examples/Appearance/AppearanceExample.js @@ -77,6 +77,8 @@ const ThemedText = props => ( ); exports.title = 'Appearance'; +exports.category = 'UI'; +exports.documentationURL = 'https://reactnative.dev/docs/appearance'; exports.description = 'Light and dark user interface examples.'; exports.examples = [ { diff --git a/packages/rn-tester/js/examples/AsyncStorage/AsyncStorageExample.js b/packages/rn-tester/js/examples/AsyncStorage/AsyncStorageExample.js deleted file mode 100644 index cbbb13acdbd72e..00000000000000 --- a/packages/rn-tester/js/examples/AsyncStorage/AsyncStorageExample.js +++ /dev/null @@ -1,103 +0,0 @@ -/** - * 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 {AsyncStorage, PickerIOS, Text, View} = require('react-native'); -const PickerItemIOS = PickerIOS.Item; - -const STORAGE_KEY = '@AsyncStorageExample:key'; -const COLORS = ['red', 'orange', 'yellow', 'green', 'blue']; - -class BasicStorageExample extends React.Component<{...}, $FlowFixMeState> { - state = { - selectedValue: COLORS[0], - messages: [], - }; - - componentDidMount() { - this._loadInitialState().done(); - } - - _loadInitialState = async () => { - try { - const value = await AsyncStorage.getItem(STORAGE_KEY); - if (value !== null) { - this.setState({selectedValue: value}); - this._appendMessage('Recovered selection from disk: ' + value); - } else { - this._appendMessage('Initialized with no selection on disk.'); - } - } catch (error) { - this._appendMessage('AsyncStorage error: ' + error.message); - } - }; - - render() { - const color = this.state.selectedValue; - return ( - <View> - <PickerIOS selectedValue={color} onValueChange={this._onValueChange}> - {COLORS.map(value => ( - <PickerItemIOS key={value} value={value} label={value} /> - ))} - </PickerIOS> - <Text> - {'Selected: '} - <Text style={{color}}>{this.state.selectedValue}</Text> - </Text> - <Text /> - <Text onPress={this._removeStorage}> - Press here to remove from storage. - </Text> - <Text /> - <Text>Messages:</Text> - {this.state.messages.map(m => ( - <Text key={m}>{m}</Text> - ))} - </View> - ); - } - - _onValueChange = async selectedValue => { - this.setState({selectedValue}); - try { - await AsyncStorage.setItem(STORAGE_KEY, selectedValue); - this._appendMessage('Saved selection to disk: ' + selectedValue); - } catch (error) { - this._appendMessage('AsyncStorage error: ' + error.message); - } - }; - - _removeStorage = async () => { - try { - await AsyncStorage.removeItem(STORAGE_KEY); - this._appendMessage('Selection removed from disk.'); - } catch (error) { - this._appendMessage('AsyncStorage error: ' + error.message); - } - }; - - _appendMessage = message => { - this.setState({messages: this.state.messages.concat(message)}); - }; -} - -exports.title = 'AsyncStorage'; -exports.description = 'Asynchronous local disk storage.'; -exports.examples = [ - { - title: 'Basics - getItem, setItem, removeItem', - render(): React.Element<any> { - return <BasicStorageExample />; - }, - }, -]; diff --git a/packages/rn-tester/js/examples/Border/BorderExample.js b/packages/rn-tester/js/examples/Border/BorderExample.js index f43016ef60ed45..dbcb00b702841c 100644 --- a/packages/rn-tester/js/examples/Border/BorderExample.js +++ b/packages/rn-tester/js/examples/Border/BorderExample.js @@ -175,6 +175,7 @@ const styles = StyleSheet.create({ }); exports.title = 'Border'; +exports.category = 'UI'; exports.description = 'Demonstrates some of the border styles available to Views.'; exports.examples = [ diff --git a/packages/rn-tester/js/examples/BoxShadow/BoxShadowExample.js b/packages/rn-tester/js/examples/BoxShadow/BoxShadowExample.js index 429fb0873c504c..999ab089c5d82b 100644 --- a/packages/rn-tester/js/examples/BoxShadow/BoxShadowExample.js +++ b/packages/rn-tester/js/examples/BoxShadow/BoxShadowExample.js @@ -72,6 +72,7 @@ const styles = StyleSheet.create({ }); exports.title = 'Box Shadow'; +exports.category = 'UI'; exports.description = 'Demonstrates some of the shadow styles available to Views.'; exports.examples = [ diff --git a/packages/rn-tester/js/examples/Button/ButtonExample.js b/packages/rn-tester/js/examples/Button/ButtonExample.js index 981cdee0c56c17..00e8110f6dc2fe 100644 --- a/packages/rn-tester/js/examples/Button/ButtonExample.js +++ b/packages/rn-tester/js/examples/Button/ButtonExample.js @@ -21,7 +21,9 @@ function onButtonPress(buttonName) { exports.displayName = 'ButtonExample'; exports.framework = 'React'; -exports.title = '<Button>'; +exports.category = 'UI'; +exports.title = 'Button'; +exports.documentationURL = 'https://reactnative.dev/docs/button'; exports.description = 'Simple React Native button component.'; exports.examples = [ diff --git a/packages/rn-tester/js/examples/Clipboard/ClipboardExample.js b/packages/rn-tester/js/examples/Clipboard/ClipboardExample.js deleted file mode 100644 index 791f3d7eb5e671..00000000000000 --- a/packages/rn-tester/js/examples/Clipboard/ClipboardExample.js +++ /dev/null @@ -1,68 +0,0 @@ -/** - * 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 strict-local - */ - -'use strict'; - -const React = require('react'); - -const {Clipboard, View, Text, StyleSheet} = require('react-native'); - -type Props = $ReadOnly<{||}>; -type State = {| - content: string, -|}; - -class ClipboardExample extends React.Component<Props, State> { - state = { - content: 'Content will appear here', - }; - - _setClipboardContent = async () => { - Clipboard.setString('Hello World'); - try { - const content = await Clipboard.getString(); - this.setState({content}); - } catch (e) { - this.setState({content: e.message}); - } - }; - - render() { - return ( - <View> - <Text onPress={this._setClipboardContent} style={styles.label}> - Tap to put "Hello World" in the clipboard - </Text> - <Text style={styles.content}>{this.state.content}</Text> - </View> - ); - } -} - -const styles = StyleSheet.create({ - label: { - color: 'blue', - }, - content: { - color: 'red', - marginTop: 20, - }, -}); - -exports.title = 'Clipboard'; -exports.description = 'Show Clipboard contents.'; -exports.examples = [ - { - title: 'Clipboard.setString() and getString()', - render(): React.Node { - return <ClipboardExample />; - }, - }, -]; diff --git a/packages/rn-tester/js/examples/Crash/CrashExample.js b/packages/rn-tester/js/examples/Crash/CrashExample.js index 1232137de3e646..5d45b571510f43 100644 --- a/packages/rn-tester/js/examples/Crash/CrashExample.js +++ b/packages/rn-tester/js/examples/Crash/CrashExample.js @@ -18,6 +18,7 @@ const {CrashyCrash} = NativeModules; exports.displayName = (undefined: ?string); exports.framework = 'React'; exports.title = 'Crash'; +exports.category = 'Basic'; exports.description = 'Crash examples.'; exports.examples = [ diff --git a/packages/rn-tester/js/examples/DatePicker/DatePickerAndroidExample.js b/packages/rn-tester/js/examples/DatePicker/DatePickerAndroidExample.js deleted file mode 100644 index e52037f5aebcfa..00000000000000 --- a/packages/rn-tester/js/examples/DatePicker/DatePickerAndroidExample.js +++ /dev/null @@ -1,170 +0,0 @@ -/** - * 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 - */ - -'use strict'; - -const React = require('react'); -const { - DatePickerAndroid, - StyleSheet, - Text, - TouchableWithoutFeedback, -} = require('react-native'); - -const RNTesterBlock = require('../../components/RNTesterBlock'); -const RNTesterPage = require('../../components/RNTesterPage'); - -type Props = $ReadOnly<{||}>; -type State = {| - presetDate: Date, - simpleDate: Date, - spinnerDate: Date, - calendarDate: Date, - defaultDate: Date, - allDate: Date, - simpleText: string, - spinnerText: string, - calendarText: string, - defaultText: string, - minText: string, - maxText: string, - presetText: string, - allText: string, -|}; - -class DatePickerAndroidExample extends React.Component<Props, State> { - state = { - presetDate: new Date(2020, 4, 5), - simpleDate: new Date(2020, 4, 5), - spinnerDate: new Date(2020, 4, 5), - calendarDate: new Date(2020, 4, 5), - defaultDate: new Date(2020, 4, 5), - allDate: new Date(2020, 4, 5), - simpleText: 'pick a date', - spinnerText: 'pick a date', - calendarText: 'pick a date', - defaultText: 'pick a date', - minText: 'pick a date, no earlier than today', - maxText: 'pick a date, no later than today', - presetText: 'pick a date, preset to 2020/5/5', - allText: 'pick a date between 2020/5/1 and 2020/5/10', - }; - - showPicker = async (stateKey, options) => { - try { - const newState = {}; - const {action, year, month, day} = await DatePickerAndroid.open(options); - if (action === DatePickerAndroid.dismissedAction) { - newState[stateKey + 'Text'] = 'dismissed'; - } else { - const date = new Date(year, month, day); - newState[stateKey + 'Text'] = date.toLocaleDateString(); - newState[stateKey + 'Date'] = date; - } - this.setState(newState); - } catch ({code, message}) { - console.warn(`Error in example '${stateKey}': `, message); - } - }; - - render() { - return ( - <RNTesterPage title="DatePickerAndroid"> - <RNTesterBlock title="Simple date picker"> - <TouchableWithoutFeedback - onPress={this.showPicker.bind(this, 'simple', { - date: this.state.simpleDate, - })}> - <Text style={styles.text}>{this.state.simpleText}</Text> - </TouchableWithoutFeedback> - </RNTesterBlock> - <RNTesterBlock title="Simple spinner date picker"> - <TouchableWithoutFeedback - onPress={this.showPicker.bind(this, 'spinner', { - date: this.state.spinnerDate, - mode: 'spinner', - })}> - <Text style={styles.text}>{this.state.spinnerText}</Text> - </TouchableWithoutFeedback> - </RNTesterBlock> - <RNTesterBlock title="Simple calendar date picker"> - <TouchableWithoutFeedback - onPress={this.showPicker.bind(this, 'calendar', { - date: this.state.calendarDate, - mode: 'calendar', - })}> - <Text style={styles.text}>{this.state.calendarText}</Text> - </TouchableWithoutFeedback> - </RNTesterBlock> - <RNTesterBlock title="Simple default date picker"> - <TouchableWithoutFeedback - onPress={this.showPicker.bind(this, 'default', { - date: this.state.defaultDate, - mode: 'default', - })}> - <Text style={styles.text}>{this.state.defaultText}</Text> - </TouchableWithoutFeedback> - </RNTesterBlock> - <RNTesterBlock title="Date picker with pre-set date"> - <TouchableWithoutFeedback - onPress={this.showPicker.bind(this, 'preset', { - date: this.state.presetDate, - })}> - <Text style={styles.text}>{this.state.presetText}</Text> - </TouchableWithoutFeedback> - </RNTesterBlock> - <RNTesterBlock title="Date picker with minDate"> - <TouchableWithoutFeedback - onPress={this.showPicker.bind(this, 'min', { - date: this.state.minDate, - minDate: new Date(), - })}> - <Text style={styles.text}>{this.state.minText}</Text> - </TouchableWithoutFeedback> - </RNTesterBlock> - <RNTesterBlock title="Date picker with maxDate"> - <TouchableWithoutFeedback - onPress={this.showPicker.bind(this, 'max', { - date: this.state.maxDate, - maxDate: new Date(), - })}> - <Text style={styles.text}>{this.state.maxText}</Text> - </TouchableWithoutFeedback> - </RNTesterBlock> - <RNTesterBlock title="Date picker with all options"> - <TouchableWithoutFeedback - onPress={this.showPicker.bind(this, 'all', { - date: this.state.allDate, - minDate: new Date(2020, 4, 1), - maxDate: new Date(2020, 4, 10), - })}> - <Text style={styles.text}>{this.state.allText}</Text> - </TouchableWithoutFeedback> - </RNTesterBlock> - </RNTesterPage> - ); - } -} - -const styles = StyleSheet.create({ - text: { - color: 'black', - }, -}); - -exports.title = 'DatePickerAndroid'; -exports.description = 'Standard Android date picker dialog'; -exports.examples = [ - { - title: 'Simple date picker', - render: function(): React.Element<typeof DatePickerAndroidExample> { - return <DatePickerAndroidExample />; - }, - }, -]; diff --git a/packages/rn-tester/js/examples/DatePicker/DatePickerIOSExample.js b/packages/rn-tester/js/examples/DatePicker/DatePickerIOSExample.js deleted file mode 100644 index 6aef85f215ff33..00000000000000 --- a/packages/rn-tester/js/examples/DatePicker/DatePickerIOSExample.js +++ /dev/null @@ -1,150 +0,0 @@ -/** - * 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 {DatePickerIOS, StyleSheet, Text, View} = require('react-native'); - -type State = {| - date: Date, -|}; - -type Props = {| - children: (State, (Date) => void) => React.Node, -|}; - -class WithDatePickerData extends React.Component<Props, State> { - state = { - date: new Date(), - }; - - onDateChange = date => { - this.setState({date: date}); - }; - - render() { - return ( - <View> - <WithLabel label="Value:"> - <Text testID="date-indicator"> - {this.state.date.toLocaleDateString()} - </Text> - <Text> </Text> - <Text testID="time-indicator"> - {this.state.date.toLocaleTimeString([], { - hour: '2-digit', - minute: '2-digit', - })} - </Text> - </WithLabel> - {this.props.children(this.state, this.onDateChange)} - </View> - ); - } -} - -type LabelProps = {| - label: string, - children: React.Node, -|}; - -class WithLabel extends React.Component<LabelProps> { - render() { - return ( - <View style={styles.labelContainer}> - <View style={styles.labelView}> - <Text style={styles.label}>{this.props.label}</Text> - </View> - {this.props.children} - </View> - ); - } -} - -const styles = StyleSheet.create({ - textinput: { - height: 26, - width: 50, - borderWidth: 0.5, - borderColor: '#0f0f0f', - padding: 4, - fontSize: 13, - }, - labelContainer: { - flexDirection: 'row', - alignItems: 'center', - marginVertical: 2, - }, - labelView: { - marginRight: 10, - paddingVertical: 2, - }, - label: { - fontWeight: '500', - }, -}); - -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" - 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" - onDateChange={onDateChange} - /> - )} - </WithDatePickerData> - ); - }, - }, - { - title: 'Time only picker, 20-minute interval', - render: function(): React.Element<any> { - return ( - <WithDatePickerData> - {(state, onDateChange) => ( - <DatePickerIOS - testID="time-with-interval" - date={state.date} - minuteInterval={20} - mode="time" - onDateChange={onDateChange} - /> - )} - </WithDatePickerData> - ); - }, - }, -]; diff --git a/packages/rn-tester/js/examples/DevSettings/DevSettingsExample.js b/packages/rn-tester/js/examples/DevSettings/DevSettingsExample.js index 4428c8fa0e52eb..9b8d267f946c70 100644 --- a/packages/rn-tester/js/examples/DevSettings/DevSettingsExample.js +++ b/packages/rn-tester/js/examples/DevSettings/DevSettingsExample.js @@ -14,6 +14,8 @@ import * as React from 'react'; import {Alert, Button, DevSettings} from 'react-native'; exports.title = 'DevSettings'; +exports.category = 'Basic'; +exports.documentationURL = 'https://reactnative.dev/docs/devsettings'; exports.description = 'Customize the development settings'; exports.examples = [ { diff --git a/packages/rn-tester/js/examples/Dimensions/DimensionsExample.js b/packages/rn-tester/js/examples/Dimensions/DimensionsExample.js index b5d0e4fe630396..64167e6c2e5f6c 100644 --- a/packages/rn-tester/js/examples/Dimensions/DimensionsExample.js +++ b/packages/rn-tester/js/examples/Dimensions/DimensionsExample.js @@ -41,6 +41,8 @@ class DimensionsSubscription extends React.Component< } exports.title = 'Dimensions'; +exports.category = 'UI'; +exports.documentationURL = 'https://reactnative.dev/docs/dimensions'; exports.description = 'Dimensions of the viewport'; exports.examples = [ { diff --git a/packages/rn-tester/js/examples/FlatList/FlatListExample.js b/packages/rn-tester/js/examples/FlatList/FlatListExample.js index dfa097b6c6bc24..b62e95fb3020eb 100644 --- a/packages/rn-tester/js/examples/FlatList/FlatListExample.js +++ b/packages/rn-tester/js/examples/FlatList/FlatListExample.js @@ -272,7 +272,9 @@ const styles = StyleSheet.create({ }, }); -exports.title = '<FlatList>'; +exports.title = 'FlatList'; +exports.category = 'ListView'; +exports.documentationURL = 'https://reactnative.dev/docs/flatlist'; exports.description = 'Performant, scrollable list of data.'; exports.simpleExampleContainer = true; exports.examples = [ diff --git a/packages/rn-tester/js/examples/Image/ImageExample.js b/packages/rn-tester/js/examples/Image/ImageExample.js index 95a3e70e24d6b7..b0d665ed31c305 100644 --- a/packages/rn-tester/js/examples/Image/ImageExample.js +++ b/packages/rn-tester/js/examples/Image/ImageExample.js @@ -13,7 +13,6 @@ const React = require('react'); const { - ActivityIndicator, Image, StyleSheet, Text, @@ -402,7 +401,8 @@ const styles = StyleSheet.create({ exports.displayName = (undefined: ?string); exports.framework = 'React'; -exports.title = '<Image>'; +exports.title = 'Image'; +exports.category = 'Basic'; exports.description = 'Base component for displaying different types of images.'; diff --git a/packages/rn-tester/js/examples/InputAccessoryView/InputAccessoryViewExample.js b/packages/rn-tester/js/examples/InputAccessoryView/InputAccessoryViewExample.js index 80bddeb91735e5..76488422f9181b 100644 --- a/packages/rn-tester/js/examples/InputAccessoryView/InputAccessoryViewExample.js +++ b/packages/rn-tester/js/examples/InputAccessoryView/InputAccessoryViewExample.js @@ -108,7 +108,7 @@ const styles = StyleSheet.create({ }, }); -exports.title = '<InputAccessoryView>'; +exports.title = 'InputAccessoryView'; exports.description = 'Example showing how to use an InputAccessoryView to build an iMessage-like sticky text input'; exports.examples = [ diff --git a/packages/rn-tester/js/examples/JSResponderHandlerExample/JSResponderHandlerExample.js b/packages/rn-tester/js/examples/JSResponderHandlerExample/JSResponderHandlerExample.js index de7a78aaf782dc..cbfffbf266b800 100644 --- a/packages/rn-tester/js/examples/JSResponderHandlerExample/JSResponderHandlerExample.js +++ b/packages/rn-tester/js/examples/JSResponderHandlerExample/JSResponderHandlerExample.js @@ -18,7 +18,8 @@ import {PanResponder, ScrollView} from 'react-native'; exports.displayName = 'JSResponderHandlerExample'; exports.framework = 'React'; -exports.title = '<JSResponderHandler>'; +exports.title = 'JSResponderHandler'; +exports.category = 'Basic'; exports.description = 'Simple example to test JSResponderHandler.'; const _gesture = PanResponder.create({ diff --git a/packages/rn-tester/js/examples/KeyboardAvoidingView/KeyboardAvoidingViewExample.js b/packages/rn-tester/js/examples/KeyboardAvoidingView/KeyboardAvoidingViewExample.js index da9ab113835dcb..35f9cb59cbdd55 100644 --- a/packages/rn-tester/js/examples/KeyboardAvoidingView/KeyboardAvoidingViewExample.js +++ b/packages/rn-tester/js/examples/KeyboardAvoidingView/KeyboardAvoidingViewExample.js @@ -107,7 +107,9 @@ const styles = StyleSheet.create({ }, }); -exports.title = '<KeyboardAvoidingView>'; +exports.title = 'KeyboardAvoidingView'; +exports.category = 'Basic'; +exports.documentationURL = 'https://reactnative.dev/docs/keyboardavoidingview'; exports.description = 'Base component for views that automatically adjust their height or position to move out of the way of the keyboard.'; exports.examples = [ diff --git a/packages/rn-tester/js/examples/Layout/LayoutAnimationExample.js b/packages/rn-tester/js/examples/Layout/LayoutAnimationExample.js index 62c883246457b8..a69a961214fbc3 100644 --- a/packages/rn-tester/js/examples/Layout/LayoutAnimationExample.js +++ b/packages/rn-tester/js/examples/Layout/LayoutAnimationExample.js @@ -345,6 +345,8 @@ const styles = StyleSheet.create({ }); exports.title = 'Layout Animation'; +exports.category = 'UI'; +exports.documentationURL = 'https://reactnative.dev/docs/layoutanimation'; exports.description = 'Layout animation'; exports.examples = [ { diff --git a/packages/rn-tester/js/examples/Layout/LayoutEventsExample.js b/packages/rn-tester/js/examples/Layout/LayoutEventsExample.js index 3d299ae6a511fa..48c4cb5d718b13 100644 --- a/packages/rn-tester/js/examples/Layout/LayoutEventsExample.js +++ b/packages/rn-tester/js/examples/Layout/LayoutEventsExample.js @@ -153,6 +153,7 @@ const styles = StyleSheet.create({ }); exports.title = 'Layout Events'; +exports.category = 'UI'; exports.description = ('Examples that show how Layout events can be used to ' + 'measure view size and position.': string); exports.examples = [ diff --git a/packages/rn-tester/js/examples/Layout/LayoutExample.js b/packages/rn-tester/js/examples/Layout/LayoutExample.js index 9c69038f30f76f..6d9caf64c5c48b 100644 --- a/packages/rn-tester/js/examples/Layout/LayoutExample.js +++ b/packages/rn-tester/js/examples/Layout/LayoutExample.js @@ -194,6 +194,7 @@ const styles = StyleSheet.create({ }); exports.title = 'Layout - Flexbox'; +exports.category = 'UI'; exports.description = 'Examples of using the flexbox API to layout views.'; exports.displayName = 'LayoutExample'; exports.examples = [ diff --git a/packages/rn-tester/js/examples/Linking/LinkingExample.js b/packages/rn-tester/js/examples/Linking/LinkingExample.js index df63aadce14ca7..02f883a644dbd4 100644 --- a/packages/rn-tester/js/examples/Linking/LinkingExample.js +++ b/packages/rn-tester/js/examples/Linking/LinkingExample.js @@ -117,6 +117,8 @@ const styles = StyleSheet.create({ }); exports.title = 'Linking'; +exports.category = 'Basic'; +exports.documentationURL = 'https://reactnative.dev/docs/linking'; exports.description = 'Shows how to use Linking to open URLs.'; exports.examples = [ { diff --git a/packages/rn-tester/js/examples/MaskedView/MaskedViewExample.js b/packages/rn-tester/js/examples/MaskedView/MaskedViewExample.js deleted file mode 100644 index 7ad01c6e41cda1..00000000000000 --- a/packages/rn-tester/js/examples/MaskedView/MaskedViewExample.js +++ /dev/null @@ -1,236 +0,0 @@ -/** - * 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 strict-local - */ - -'use strict'; - -const React = require('react'); - -const { - Animated, - Image, - MaskedViewIOS, - StyleSheet, - Text, - View, -} = require('react-native'); - -type Props = $ReadOnly<{||}>; -type ChangingChildrenState = {| - alternateChildren: boolean, -|}; - -class AnimatedMaskExample extends React.Component<Props> { - _maskRotateAnimatedValue = new Animated.Value(0); - _maskScaleAnimatedValue = new Animated.Value(1); - - componentDidMount() { - Animated.loop( - Animated.sequence([ - Animated.timing(this._maskScaleAnimatedValue, { - toValue: 1.3, - duration: 750, - useNativeDriver: true, - }), - Animated.timing(this._maskScaleAnimatedValue, { - toValue: 1, - duration: 750, - useNativeDriver: true, - }), - ]), - ).start(); - - Animated.loop( - Animated.timing(this._maskRotateAnimatedValue, { - toValue: 360, - duration: 2000, - useNativeDriver: true, - }), - ).start(); - } - - render(): React.Node { - return ( - <View style={styles.exampleWrapperStyle}> - <MaskedViewIOS - style={styles.flexStyle} - maskElement={ - <Animated.View - style={[ - styles.maskContainerStyle, - {transform: [{scale: this._maskScaleAnimatedValue}]}, - ]}> - <Text style={styles.maskTextStyle}>Basic Mask</Text> - </Animated.View> - }> - <Animated.View - style={{ - flex: 1, - transform: [ - { - rotate: this._maskRotateAnimatedValue.interpolate({ - inputRange: [0, 360], - /* $FlowFixMe(>=0.38.0) - Flow error detected during the - * deployment of v0.38.0. To see the error, remove this - * comment and run flow */ - outputRange: ['0deg', '360deg'], - }), - }, - ], - }}> - <View style={styles.blueStyle} /> - <View style={styles.redStyle} /> - </Animated.View> - </MaskedViewIOS> - </View> - ); - } -} - -class ChangingChildrenMaskExample extends React.Component< - Props, - ChangingChildrenState, -> { - state: ChangingChildrenState = { - alternateChildren: true, - }; - - componentDidMount() { - setInterval(() => { - this.setState(state => ({ - alternateChildren: !state.alternateChildren, - })); - }, 1000); - } - - render(): React.Node { - return ( - <View style={styles.exampleWrapperStyle}> - <MaskedViewIOS - style={styles.flexStyle} - maskElement={ - <View style={styles.maskContainerStyle}> - <Text style={styles.maskTextStyle}>Basic Mask</Text> - </View> - }> - {this.state.alternateChildren - ? [ - <View key={1} style={styles.blueStyle} />, - <View key={2} style={styles.redStyle} />, - ] - : null} - </MaskedViewIOS> - </View> - ); - } -} - -const styles = StyleSheet.create({ - exampleWrapperStyle: { - width: 340, - height: 300, - alignSelf: 'center', - }, - imageStyle: { - height: 200, - width: 200, - }, - maskContainerStyle: { - flex: 1, - backgroundColor: 'transparent', - justifyContent: 'center', - alignItems: 'center', - }, - maskTextStyle: { - backgroundColor: 'transparent', - fontSize: 40, - fontWeight: 'bold', - }, - blueStyle: { - flex: 1, - backgroundColor: 'blue', - }, - redStyle: { - flex: 1, - backgroundColor: 'red', - }, - grayStyle: { - backgroundColor: '#eeeeee', - }, - flexStyle: { - flex: 1, - }, -}); - -exports.title = '<MaskedViewIOS>'; -exports.description = - 'Renders the child view with a mask specified in the `renderMask` prop.'; -exports.examples = [ - { - title: 'Basic Mask', - render: function(): React.Element<typeof View> { - return ( - <View style={styles.exampleWrapperStyle}> - <MaskedViewIOS - style={styles.flexStyle} - maskElement={ - <View style={styles.maskContainerStyle}> - <Text style={styles.maskTextStyle}>Basic Mask</Text> - </View> - }> - <View style={styles.blueStyle} /> - <View style={styles.redStyle} /> - </MaskedViewIOS> - </View> - ); - }, - }, - { - title: 'Image Mask', - render: function(): React.Element<typeof View> { - return ( - <View style={[styles.exampleWrapperStyle, styles.grayStyle]}> - <MaskedViewIOS - style={styles.flexStyle} - maskElement={ - <View style={styles.maskContainerStyle}> - <Image - style={styles.imageStyle} - source={require('../../assets/imageMask.png')} - /> - </View> - }> - <View style={styles.maskContainerStyle}> - <Image - resizeMode="cover" - style={styles.imageStyle} - source={{ - uri: - 'https://38.media.tumblr.com/9e9bd08c6e2d10561dd1fb4197df4c4e/tumblr_mfqekpMktw1rn90umo1_500.gif', - }} - /> - </View> - </MaskedViewIOS> - </View> - ); - }, - }, - { - title: 'Animated Mask', - render: function(): React.Element<typeof AnimatedMaskExample> { - return <AnimatedMaskExample />; - }, - }, - { - title: 'Mask w/ Changing Children', - render: function(): React.Element<typeof ChangingChildrenMaskExample> { - return <ChangingChildrenMaskExample />; - }, - }, -]; diff --git a/packages/rn-tester/js/examples/Modal/ModalExample.js b/packages/rn-tester/js/examples/Modal/ModalExample.js index d8418f496fe8c8..75dcc49b175ef6 100644 --- a/packages/rn-tester/js/examples/Modal/ModalExample.js +++ b/packages/rn-tester/js/examples/Modal/ModalExample.js @@ -27,7 +27,9 @@ const Item = Picker.Item; exports.displayName = (undefined: ?string); exports.framework = 'React'; -exports.title = '<Modal>'; +exports.title = 'Modal'; +exports.category = 'UI'; +exports.documentationURL = 'https://reactnative.dev/docs/modal'; exports.description = 'Component for presenting modal views.'; class Button extends React.Component<$FlowFixMeProps, $FlowFixMeState> { diff --git a/packages/rn-tester/js/examples/MultiColumn/MultiColumnExample.js b/packages/rn-tester/js/examples/MultiColumn/MultiColumnExample.js index 29eecb93c6ed7c..ca822b22c07d01 100644 --- a/packages/rn-tester/js/examples/MultiColumn/MultiColumnExample.js +++ b/packages/rn-tester/js/examples/MultiColumn/MultiColumnExample.js @@ -177,7 +177,8 @@ const styles = StyleSheet.create({ }, }); -exports.title = '<FlatList> - MultiColumn'; +exports.title = 'FlatList - MultiColumn'; +exports.category = 'ListView'; exports.description = 'Performant, scrollable grid of data.'; exports.examples = [ { diff --git a/packages/rn-tester/js/examples/NativeAnimation/NativeAnimationsExample.js b/packages/rn-tester/js/examples/NativeAnimation/NativeAnimationsExample.js index adcb40ff499457..7afe58e886bf4c 100644 --- a/packages/rn-tester/js/examples/NativeAnimation/NativeAnimationsExample.js +++ b/packages/rn-tester/js/examples/NativeAnimation/NativeAnimationsExample.js @@ -374,6 +374,7 @@ const styles = StyleSheet.create({ exports.framework = 'React'; exports.title = 'Native Animated Example'; +exports.category = 'UI'; exports.description = 'Test out Native Animations'; exports.examples = [ diff --git a/packages/rn-tester/js/examples/OrientationChange/OrientationChangeExample.js b/packages/rn-tester/js/examples/OrientationChange/OrientationChangeExample.js index 9233b93aca0bec..1cf3d45e20f3d6 100644 --- a/packages/rn-tester/js/examples/OrientationChange/OrientationChangeExample.js +++ b/packages/rn-tester/js/examples/OrientationChange/OrientationChangeExample.js @@ -54,6 +54,7 @@ class OrientationChangeExample extends React.Component<{...}, $FlowFixMeState> { } exports.title = 'OrientationChangeExample'; +exports.category = 'Basic'; exports.description = 'listening to orientation changes'; exports.examples = [ { diff --git a/packages/rn-tester/js/examples/PanResponder/PanResponderExample.js b/packages/rn-tester/js/examples/PanResponder/PanResponderExample.js index 00f966b585d944..33dcf037b430bc 100644 --- a/packages/rn-tester/js/examples/PanResponder/PanResponderExample.js +++ b/packages/rn-tester/js/examples/PanResponder/PanResponderExample.js @@ -141,6 +141,8 @@ const styles = StyleSheet.create({ }); exports.title = 'PanResponder Sample'; +exports.category = 'Basic'; +exports.documentationURL = 'https://reactnative.dev/docs/panresponder'; exports.description = 'Shows the Use of PanResponder to provide basic gesture handling'; exports.simpleExampleContainer = true; diff --git a/packages/rn-tester/js/examples/PermissionsAndroid/PermissionsExample.js b/packages/rn-tester/js/examples/PermissionsAndroid/PermissionsExample.js index efa09b59c52eb3..d1585d97105e99 100644 --- a/packages/rn-tester/js/examples/PermissionsAndroid/PermissionsExample.js +++ b/packages/rn-tester/js/examples/PermissionsAndroid/PermissionsExample.js @@ -103,6 +103,7 @@ class PermissionsExample extends React.Component<{...}, $FlowFixMeState> { exports.displayName = (undefined: ?string); exports.framework = 'React'; exports.title = 'PermissionsAndroid'; +exports.category = 'Android'; exports.description = 'Permissions example for API 23+.'; exports.examples = [ { diff --git a/packages/rn-tester/js/examples/Picker/PickerExample.js b/packages/rn-tester/js/examples/Picker/PickerExample.js deleted file mode 100644 index c4d1e2bfcf4902..00000000000000 --- a/packages/rn-tester/js/examples/Picker/PickerExample.js +++ /dev/null @@ -1,220 +0,0 @@ -/** - * 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 strict-local - */ - -'use strict'; - -const React = require('react'); - -const {Picker, StyleSheet, Text} = require('react-native'); - -const Item = Picker.Item; - -type State = {value: string | number, ...}; - -class BasicPickerExample extends React.Component<{...}, State> { - state: State = { - value: 'key1', - }; - - render(): React.Node { - return ( - <Picker - testID="basic-picker" - style={styles.picker} - selectedValue={this.state.value} - onValueChange={v => this.setState({value: v})}> - <Item label="hello" value="key0" /> - <Item label="world" value="key1" /> - </Picker> - ); - } -} - -class DisabledPickerExample extends React.Component<{...}, State> { - state: State = { - value: 'key1', - }; - - render(): React.Node { - return ( - <Picker - style={styles.picker} - enabled={false} - selectedValue={this.state.value}> - <Item label="hello" value="key0" /> - <Item label="world" value="key1" /> - </Picker> - ); - } -} - -class DropdownPickerExample extends React.Component<{...}, State> { - state: State = { - value: 'key1', - }; - - render(): React.Node { - return ( - <Picker - style={styles.picker} - selectedValue={this.state.value} - onValueChange={v => this.setState({value: v})} - mode="dropdown"> - <Item label="hello" value="key0" /> - <Item label="world" value="key1" /> - </Picker> - ); - } -} - -class PromptPickerExample extends React.Component<{...}, State> { - state: State = { - value: 'key1', - }; - - render(): React.Node { - return ( - <Picker - style={styles.picker} - selectedValue={this.state.value} - onValueChange={v => this.setState({value: v})} - prompt="Pick one, just one"> - <Item label="hello" value="key0" /> - <Item label="world" value="key1" /> - </Picker> - ); - } -} - -type ColorState = {color: string | number, ...}; - -class ColorPickerExample extends React.Component<{...}, ColorState> { - state: ColorState = { - color: 'red', - }; - - render(): React.Node { - return ( - <> - <Picker - style={[styles.picker, {color: 'white', backgroundColor: '#333'}]} - selectedValue={this.state.color} - onValueChange={v => this.setState({color: v})} - mode="dropdown"> - <Item label="red" color="red" value="red" /> - <Item label="green" color="green" value="green" /> - <Item label="blue" color="blue" value="blue" /> - </Picker> - <Picker - style={styles.picker} - selectedValue={this.state.color} - onValueChange={v => this.setState({color: v})} - mode="dialog"> - <Item label="red" color="red" value="red" /> - <Item label="green" color="green" value="green" /> - <Item label="blue" color="blue" value="blue" /> - </Picker> - </> - ); - } -} -class AccessibilityLabelPickerExample extends React.Component<{||}, State> { - state: State = { - value: '3', - }; - - render(): React.Node { - return ( - <Picker - accessibilityLabel={this.state.value + 'Hours'} - style={styles.picker} - selectedValue={this.state.value} - onValueChange={v => this.setState({value: v})}> - <Item label="1" value="1" /> - <Item label="2" value="2" /> - <Item label="3" value="3" /> - </Picker> - ); - } -} - -const styles = StyleSheet.create({ - picker: { - width: 100, - }, -}); - -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: 'Accessibility Label pickers', - render: function(): React.Element<typeof AccessibilityLabelPickerExample> { - return <AccessibilityLabelPickerExample />; - }, - }, - { - title: 'Picker with no listener', - render: function(): React.Element<typeof PromptPickerExample> { - return ( - /* $FlowFixMe(>=0.99.0 site=react_native_fb) This comment suppresses an - * error found when Flow v0.99 was deployed. To see the error, delete - * this comment and run Flow. */ - <> - <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 />; - }, - }, - { - title: 'AccessibilityLabel pickers', - render: function(): React.Element<typeof AccessibilityLabelPickerExample> { - return <AccessibilityLabelPickerExample />; - }, - }, -]; diff --git a/packages/rn-tester/js/examples/Picker/PickerIOSExample.js b/packages/rn-tester/js/examples/Picker/PickerIOSExample.js deleted file mode 100644 index 6993c7ad743e11..00000000000000 --- a/packages/rn-tester/js/examples/Picker/PickerIOSExample.js +++ /dev/null @@ -1,190 +0,0 @@ -/** - * 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 {PickerIOS, Text, View} = require('react-native'); - -const PickerItemIOS = PickerIOS.Item; - -const CAR_MAKES_AND_MODELS = { - amc: { - name: 'AMC', - models: ['AMX', 'Concord', 'Eagle', 'Gremlin', 'Matador', 'Pacer'], - }, - alfa: { - name: 'Alfa-Romeo', - models: [ - '159', - '4C', - 'Alfasud', - 'Brera', - 'GTV6', - 'Giulia', - 'MiTo', - 'Spider', - ], - }, - aston: { - name: 'Aston Martin', - models: ['DB5', 'DB9', 'DBS', 'Rapide', 'Vanquish', 'Vantage'], - }, - audi: { - name: 'Audi', - models: [ - '90', - '4000', - '5000', - 'A3', - 'A4', - 'A5', - 'A6', - 'A7', - 'A8', - 'Q5', - 'Q7', - ], - }, - austin: { - name: 'Austin', - models: ['America', 'Maestro', 'Maxi', 'Mini', 'Montego', 'Princess'], - }, - borgward: { - name: 'Borgward', - models: ['Hansa', 'Isabella', 'P100'], - }, - buick: { - name: 'Buick', - models: [ - 'Electra', - 'LaCrosse', - 'LeSabre', - 'Park Avenue', - 'Regal', - 'Roadmaster', - 'Skylark', - ], - }, - cadillac: { - name: 'Cadillac', - models: ['Catera', 'Cimarron', 'Eldorado', 'Fleetwood', 'Sedan de Ville'], - }, - chevrolet: { - name: 'Chevrolet', - models: [ - 'Astro', - 'Aveo', - 'Bel Air', - 'Captiva', - 'Cavalier', - 'Chevelle', - 'Corvair', - 'Corvette', - 'Cruze', - 'Nova', - 'SS', - 'Vega', - 'Volt', - ], - }, -}; - -class PickerExample extends React.Component<{...}, $FlowFixMeState> { - state = { - carMake: 'cadillac', - modelIndex: 3, - }; - - render() { - const make = CAR_MAKES_AND_MODELS[this.state.carMake]; - const selectionString = - make.name + ' ' + make.models[this.state.modelIndex]; - return ( - <View> - <Text>Please choose a make for your car:</Text> - <PickerIOS - selectedValue={this.state.carMake} - onValueChange={carMake => this.setState({carMake, modelIndex: 0})}> - {Object.keys(CAR_MAKES_AND_MODELS).map(carMake => ( - <PickerItemIOS - key={carMake} - value={carMake} - label={CAR_MAKES_AND_MODELS[carMake].name} - /> - ))} - </PickerIOS> - <Text>Please choose a model of {make.name}:</Text> - <PickerIOS - selectedValue={this.state.modelIndex} - key={this.state.carMake} - onValueChange={modelIndex => this.setState({modelIndex})}> - {CAR_MAKES_AND_MODELS[this.state.carMake].models.map( - (modelName, modelIndex) => ( - <PickerItemIOS - key={this.state.carMake + '_' + modelIndex} - value={modelIndex} - label={modelName} - /> - ), - )} - </PickerIOS> - <Text>You selected: {selectionString}</Text> - </View> - ); - } -} - -class PickerStyleExample extends React.Component<{...}, $FlowFixMeState> { - state = { - carMake: 'cadillac', - modelIndex: 0, - }; - - render() { - return ( - <PickerIOS - itemStyle={{ - fontSize: 25, - color: 'red', - textAlign: 'left', - fontWeight: 'bold', - }} - selectedValue={this.state.carMake} - onValueChange={carMake => this.setState({carMake, modelIndex: 0})}> - {Object.keys(CAR_MAKES_AND_MODELS).map(carMake => ( - <PickerItemIOS - key={carMake} - value={carMake} - label={CAR_MAKES_AND_MODELS[carMake].name} - /> - ))} - </PickerIOS> - ); - } -} - -exports.displayName = (undefined: ?string); -exports.title = '<PickerIOS>'; -exports.description = 'Render lists of selectable options with UIPickerView.'; -exports.examples = [ - { - title: '<PickerIOS>', - render: function(): React.Element<any> { - return <PickerExample />; - }, - }, - { - title: '<PickerIOS> with custom styling', - render: function(): React.Element<any> { - return <PickerStyleExample />; - }, - }, -]; diff --git a/packages/rn-tester/js/examples/PlatformColor/PlatformColorExample.js b/packages/rn-tester/js/examples/PlatformColor/PlatformColorExample.js index a7fc2accbaa2cf..cda369e62e1e76 100644 --- a/packages/rn-tester/js/examples/PlatformColor/PlatformColorExample.js +++ b/packages/rn-tester/js/examples/PlatformColor/PlatformColorExample.js @@ -301,6 +301,8 @@ const styles = StyleSheet.create({ }); exports.title = 'PlatformColor'; +exports.category = 'Basic'; +exports.documentationURL = 'https://reactnative.dev/docs/platformcolor'; exports.description = 'Examples that show how PlatformColors may be used in an app.'; exports.examples = [ diff --git a/packages/rn-tester/js/examples/PointerEvents/PointerEventsExample.js b/packages/rn-tester/js/examples/PointerEvents/PointerEventsExample.js index fcca307e9eac05..569ae494ddda48 100644 --- a/packages/rn-tester/js/examples/PointerEvents/PointerEventsExample.js +++ b/packages/rn-tester/js/examples/PointerEvents/PointerEventsExample.js @@ -235,6 +235,7 @@ const styles = StyleSheet.create({ exports.framework = 'React'; exports.title = 'Pointer Events'; +exports.category = 'Basic'; exports.description = ('Demonstrates the use of the pointerEvents prop of a ' + 'View to control how touches should be handled.': string); exports.examples = (exampleClasses.map(infoToExample): Array<any>); diff --git a/packages/rn-tester/js/examples/Pressable/PressableExample.js b/packages/rn-tester/js/examples/Pressable/PressableExample.js index 9a67af10f6c4ed..f7a1e4dc33dd2c 100644 --- a/packages/rn-tester/js/examples/Pressable/PressableExample.js +++ b/packages/rn-tester/js/examples/Pressable/PressableExample.js @@ -316,7 +316,9 @@ const styles = StyleSheet.create({ exports.displayName = (undefined: ?string); exports.description = 'Component for making views pressable.'; -exports.title = '<Pressable>'; +exports.title = 'Pressable'; +exports.category = 'UI'; +exports.documentationURL = 'https://reactnative.dev/docs/pressable'; exports.examples = [ { title: 'Change content based on Press', diff --git a/packages/rn-tester/js/examples/ProgressBarAndroid/ProgressBarAndroidExample.android.js b/packages/rn-tester/js/examples/ProgressBarAndroid/ProgressBarAndroidExample.android.js deleted file mode 100644 index ca78dea22c1022..00000000000000 --- a/packages/rn-tester/js/examples/ProgressBarAndroid/ProgressBarAndroidExample.android.js +++ /dev/null @@ -1,94 +0,0 @@ -/** - * 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 strict-local - */ - -'use strict'; - -const RNTesterBlock = require('../../components/RNTesterBlock'); -const RNTesterPage = require('../../components/RNTesterPage'); -const React = require('react'); - -const {ProgressBarAndroid: ProgressBar} = require('react-native'); - -import type {ProgressBarAndroidProps} from '../../../../../Libraries/Components/ProgressBarAndroid/ProgressBarAndroid'; - -type MovingBarProps = $ReadOnly<{| - ...$Diff<ProgressBarAndroidProps, {progress: ?number, ...}>, - indeterminate: false, -|}>; - -type MovingBarState = {progress: number, ...}; - -class MovingBar extends React.Component<MovingBarProps, MovingBarState> { - _intervalID: ?IntervalID = null; - - state = { - progress: 0, - }; - - componentDidMount() { - this._intervalID = setInterval(() => { - const progress = (this.state.progress + 0.02) % 1; - this.setState({progress}); - }, 50); - } - - componentWillUnmount() { - if (this._intervalID != null) { - clearInterval(this._intervalID); - } - } - - render() { - return <ProgressBar progress={this.state.progress} {...this.props} />; - } -} - -class ProgressBarAndroidExample extends React.Component<{...}> { - render(): React.Node { - return ( - <RNTesterPage title="ProgressBar Examples"> - <RNTesterBlock title="Horizontal Indeterminate ProgressBar"> - {/* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue was - * found when making Flow check .android.js files. */} - <ProgressBar styleAttr="Horizontal" /> - </RNTesterBlock> - - <RNTesterBlock title="Horizontal ProgressBar"> - <MovingBar styleAttr="Horizontal" indeterminate={false} /> - </RNTesterBlock> - - <RNTesterBlock title="Horizontal Black Indeterminate ProgressBar"> - {/* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue was - * found when making Flow check .android.js files. */} - <ProgressBar styleAttr="Horizontal" color="black" /> - </RNTesterBlock> - - <RNTesterBlock title="Horizontal Blue ProgressBar"> - <MovingBar - styleAttr="Horizontal" - indeterminate={false} - color="blue" - /> - </RNTesterBlock> - </RNTesterPage> - ); - } -} - -exports.title = '<ProgressBarAndroid>'; -exports.description = 'Horizontal bar to show the progress of some operation.'; -exports.examples = [ - { - title: 'Simple progress bar', - render: function(): React.Element<typeof ProgressBarAndroidExample> { - return <ProgressBarAndroidExample />; - }, - }, -]; diff --git a/packages/rn-tester/js/examples/ProgressViewIOS/ProgressViewIOSExample.js b/packages/rn-tester/js/examples/ProgressViewIOS/ProgressViewIOSExample.js deleted file mode 100644 index 95d6885e1a753a..00000000000000 --- a/packages/rn-tester/js/examples/ProgressViewIOS/ProgressViewIOSExample.js +++ /dev/null @@ -1,103 +0,0 @@ -/** - * 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 strict-local - */ - -'use strict'; - -const React = require('react'); - -const {ProgressViewIOS, StyleSheet, View} = require('react-native'); - -type Props = {||}; -type State = {| - progress: number, -|}; - -class ProgressViewExample extends React.Component<Props, State> { - _rafId: ?AnimationFrameID = null; - - state = { - progress: 0, - }; - - componentDidMount() { - this.updateProgress(); - } - - componentWillUnmount() { - if (this._rafId != null) { - cancelAnimationFrame(this._rafId); - } - } - - updateProgress = () => { - const progress = this.state.progress + 0.01; - this.setState({progress}); - this._rafId = requestAnimationFrame(() => this.updateProgress()); - }; - - getProgress = offset => { - const progress = this.state.progress + offset; - return Math.sin(progress % Math.PI) % 1; - }; - - render() { - return ( - <View style={styles.container}> - <ProgressViewIOS - style={styles.progressView} - progress={this.getProgress(0)} - /> - <ProgressViewIOS - style={styles.progressView} - progressTintColor="purple" - progress={this.getProgress(0.2)} - /> - <ProgressViewIOS - style={styles.progressView} - progressTintColor="red" - progress={this.getProgress(0.4)} - /> - <ProgressViewIOS - style={styles.progressView} - progressTintColor="orange" - progress={this.getProgress(0.6)} - /> - <ProgressViewIOS - style={styles.progressView} - progressTintColor="yellow" - progress={this.getProgress(0.8)} - /> - </View> - ); - } -} - -const styles = StyleSheet.create({ - container: { - marginTop: -20, - backgroundColor: 'transparent', - }, - progressView: { - marginTop: 20, - }, -}); - -exports.displayName = (undefined: ?string); -exports.framework = 'React'; -exports.title = 'ProgressViewIOS'; -exports.description = 'ProgressViewIOS'; -exports.examples = [ - { - title: 'ProgressViewIOS', - render(): React.Node { - return <ProgressViewExample />; - }, - }, -]; diff --git a/packages/rn-tester/js/examples/PushNotificationIOS/PushNotificationIOSExample.js b/packages/rn-tester/js/examples/PushNotificationIOS/PushNotificationIOSExample.js deleted file mode 100644 index c32bacefef2cc8..00000000000000 --- a/packages/rn-tester/js/examples/PushNotificationIOS/PushNotificationIOSExample.js +++ /dev/null @@ -1,267 +0,0 @@ -/** - * 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 { - Alert, - DeviceEventEmitter, - PushNotificationIOS, - StyleSheet, - Text, - TouchableHighlight, - View, -} = require('react-native'); - -class Button extends React.Component<$FlowFixMeProps> { - render() { - return ( - <TouchableHighlight - underlayColor={'white'} - style={styles.button} - onPress={this.props.onPress}> - <Text style={styles.buttonLabel}>{this.props.label}</Text> - </TouchableHighlight> - ); - } -} - -class NotificationExample extends React.Component<{...}> { - UNSAFE_componentWillMount() { - PushNotificationIOS.addEventListener('register', this._onRegistered); - PushNotificationIOS.addEventListener( - 'registrationError', - this._onRegistrationError, - ); - PushNotificationIOS.addEventListener( - 'notification', - this._onRemoteNotification, - ); - PushNotificationIOS.addEventListener( - 'localNotification', - this._onLocalNotification, - ); - } - - componentWillUnmount() { - PushNotificationIOS.removeEventListener('register', this._onRegistered); - PushNotificationIOS.removeEventListener( - 'registrationError', - this._onRegistrationError, - ); - PushNotificationIOS.removeEventListener( - 'notification', - this._onRemoteNotification, - ); - PushNotificationIOS.removeEventListener( - 'localNotification', - this._onLocalNotification, - ); - } - - render() { - return ( - <View> - <Button - onPress={this._sendNotification} - label="Send fake notification" - /> - - <Button - onPress={this._sendLocalNotification} - label="Send fake local notification" - /> - </View> - ); - } - - _sendNotification() { - DeviceEventEmitter.emit('remoteNotificationReceived', { - remote: true, - aps: { - alert: 'Sample notification', - badge: '+1', - sound: 'default', - category: 'REACT_NATIVE', - 'content-available': 1, - }, - }); - } - - _sendLocalNotification() { - DeviceEventEmitter.emit('localNotificationReceived', { - aps: { - alert: 'Sample local notification', - badge: '+1', - sound: 'default', - category: 'REACT_NATIVE', - }, - }); - } - - _onRegistered(deviceToken) { - Alert.alert('Registered For Remote Push', `Device Token: ${deviceToken}`, [ - { - text: 'Dismiss', - onPress: null, - }, - ]); - } - - _onRegistrationError(error) { - Alert.alert( - 'Failed To Register For Remote Push', - `Error (${error.code}): ${error.message}`, - [ - { - text: 'Dismiss', - onPress: null, - }, - ], - ); - } - - _onRemoteNotification(notification) { - const result = `Message: ${notification.getMessage()};\n - badge: ${notification.getBadgeCount()};\n - sound: ${notification.getSound()};\n - category: ${notification.getCategory()};\n - content-available: ${notification.getContentAvailable()}.`; - - Alert.alert('Push Notification Received', result, [ - { - text: 'Dismiss', - onPress: null, - }, - ]); - } - - _onLocalNotification(notification) { - Alert.alert( - 'Local Notification Received', - 'Alert message: ' + notification.getMessage(), - [ - { - text: 'Dismiss', - onPress: null, - }, - ], - ); - } -} - -class NotificationPermissionExample extends React.Component< - $FlowFixMeProps, - any, -> { - constructor(props) { - super(props); - this.state = {permissions: null}; - } - - render() { - return ( - <View> - <Button - onPress={this._requestPermissions} - label="Request Notifications (Should Display Alert)" - /> - <Button onPress={this._checkPermissions} label="Check permissions" /> - <Text style={{textAlign: 'center'}}> - {JSON.stringify(this.state.permissions)} - </Text> - </View> - ); - } - - _requestPermissions = () => { - PushNotificationIOS.requestPermissions().then( - onFulfill => { - this._showAlert( - 'Successfully requested permissions -- ' + - 'Alert: ' + - onFulfill.alert.toString() + - ', Badge: ' + - onFulfill.badge.toString() + - ', Sound: ' + - onFulfill.sound.toString(), - ); - this._checkPermissions(); - }, - () => { - this._showAlert('Error requesting permissions'); - this._checkPermissions(); - }, - ); - }; - - _checkPermissions = () => { - PushNotificationIOS.checkPermissions(permissions => { - this.setState({permissions}); - }); - }; - - _showAlert(text) { - Alert.alert('Notification Permission', text, [ - { - text: 'Dismiss', - onPress: null, - }, - ]); - } -} - -const styles = StyleSheet.create({ - button: { - padding: 10, - alignItems: 'center', - justifyContent: 'center', - }, - buttonLabel: { - color: 'blue', - }, -}); - -exports.title = 'PushNotificationIOS'; -exports.description = 'Apple PushNotification and badge value'; -exports.examples = [ - { - title: 'Notifications Permissions', - render(): React.Element<any> { - return <NotificationPermissionExample />; - }, - }, - { - title: 'Push Notifications', - render(): React.Element<any> { - return <NotificationExample />; - }, - }, - { - title: 'Badge Number', - render(): React.Element<any> { - return ( - <View> - <Button - onPress={() => - PushNotificationIOS.setApplicationIconBadgeNumber(42) - } - label="Set app's icon badge to 42" - /> - <Button - onPress={() => PushNotificationIOS.setApplicationIconBadgeNumber(0)} - label="Clear app's icon badge" - /> - </View> - ); - }, - }, -]; diff --git a/packages/rn-tester/js/examples/RCTRootView/RCTRootViewIOSExample.js b/packages/rn-tester/js/examples/RCTRootView/RCTRootViewIOSExample.js index 0ed34bcac5d8ef..180c56844bd729 100644 --- a/packages/rn-tester/js/examples/RCTRootView/RCTRootViewIOSExample.js +++ b/packages/rn-tester/js/examples/RCTRootView/RCTRootViewIOSExample.js @@ -77,6 +77,7 @@ const styles = StyleSheet.create({ }); exports.title = 'RCTRootView'; +exports.category = 'Basic'; exports.description = 'Examples that show useful methods when embedding React Native in a native application'; exports.examples = [ diff --git a/packages/rn-tester/js/examples/RTL/RTLExample.js b/packages/rn-tester/js/examples/RTL/RTLExample.js index e19c1bac3167c0..d6b1c1827edbc3 100644 --- a/packages/rn-tester/js/examples/RTL/RTLExample.js +++ b/packages/rn-tester/js/examples/RTL/RTLExample.js @@ -27,14 +27,6 @@ const { Button, } = require('react-native'); -type State = { - toggleStatus: any, - pan: Object, - linear: Object, - isRTL: boolean, - ... -}; - type RTLToggleState = {isRTL: boolean, ...}; type AnimationState = { @@ -643,6 +635,7 @@ const styles = StyleSheet.create({ }); exports.title = 'RTLExample'; +exports.category = 'UI'; exports.description = 'Examples to show how to apply components to RTL layout.'; exports.examples = [ { diff --git a/packages/rn-tester/js/examples/RefreshControl/RefreshControlExample.js b/packages/rn-tester/js/examples/RefreshControl/RefreshControlExample.js index 06493b0971b82a..fc582cb7d5a35b 100644 --- a/packages/rn-tester/js/examples/RefreshControl/RefreshControlExample.js +++ b/packages/rn-tester/js/examples/RefreshControl/RefreshControlExample.js @@ -114,7 +114,9 @@ class RefreshControlExample extends React.Component { }; } -exports.title = '<RefreshControl>'; +exports.title = 'RefreshControl'; +exports.category = 'Basic'; +exports.documentationURL = 'https://reactnative.dev/docs/refreshcontrol'; exports.description = 'Adds pull-to-refresh support to a scrollview.'; exports.simpleExampleContainer = true; exports.examples = [ diff --git a/packages/rn-tester/js/examples/SafeAreaView/SafeAreaViewExample.js b/packages/rn-tester/js/examples/SafeAreaView/SafeAreaViewExample.js index a80d717b9726dc..0b9064bd143b99 100644 --- a/packages/rn-tester/js/examples/SafeAreaView/SafeAreaViewExample.js +++ b/packages/rn-tester/js/examples/SafeAreaView/SafeAreaViewExample.js @@ -116,7 +116,9 @@ const styles = StyleSheet.create({ exports.displayName = (undefined: ?string); exports.framework = 'React'; -exports.title = '<SafeAreaView>'; +exports.title = 'SafeAreaView'; +exports.category = 'UI'; +exports.documentationURL = 'https://reactnative.dev/docs/safeareaview'; exports.description = 'SafeAreaView automatically applies paddings reflect the portion of the view that is not covered by other (special) ancestor views.'; exports.examples = [ diff --git a/packages/rn-tester/js/examples/ScrollView/ScrollViewAnimatedExample.js b/packages/rn-tester/js/examples/ScrollView/ScrollViewAnimatedExample.js index a4a5ffea33979b..656c51276f6285 100644 --- a/packages/rn-tester/js/examples/ScrollView/ScrollViewAnimatedExample.js +++ b/packages/rn-tester/js/examples/ScrollView/ScrollViewAnimatedExample.js @@ -94,7 +94,8 @@ const styles = StyleSheet.create({ }, }); -exports.title = '<ScrollViewAnimated>'; +exports.title = 'ScrollViewAnimated'; +exports.category = 'Basic'; exports.description = 'Component that is animated when ScrollView is offset.'; exports.examples = [ diff --git a/packages/rn-tester/js/examples/ScrollView/ScrollViewExample.js b/packages/rn-tester/js/examples/ScrollView/ScrollViewExample.js index 0fcc681fc44b4a..76e9147080e810 100644 --- a/packages/rn-tester/js/examples/ScrollView/ScrollViewExample.js +++ b/packages/rn-tester/js/examples/ScrollView/ScrollViewExample.js @@ -26,7 +26,9 @@ const nullthrows = require('nullthrows'); import type {ViewStyleProp} from '../../../../../Libraries/StyleSheet/StyleSheet'; exports.displayName = 'ScrollViewExample'; -exports.title = '<ScrollView>'; +exports.title = 'ScrollView'; +exports.documentationURL = 'https://reactnative.dev/docs/scrollview'; +exports.category = 'Basic'; exports.description = 'Component that enables scrolling through child components'; exports.examples = [ diff --git a/packages/rn-tester/js/examples/ScrollView/ScrollViewSimpleExample.js b/packages/rn-tester/js/examples/ScrollView/ScrollViewSimpleExample.js index 3a5488428c01d2..b4960f93ee4b15 100644 --- a/packages/rn-tester/js/examples/ScrollView/ScrollViewSimpleExample.js +++ b/packages/rn-tester/js/examples/ScrollView/ScrollViewSimpleExample.js @@ -91,7 +91,8 @@ const styles = StyleSheet.create({ }, }); -exports.title = '<ScrollViewSimpleExample>'; +exports.title = 'ScrollViewSimpleExample'; +exports.category = 'Basic'; exports.description = 'Component that enables scrolling through child components.'; diff --git a/packages/rn-tester/js/examples/SectionList/SectionListExample.js b/packages/rn-tester/js/examples/SectionList/SectionListExample.js index 87b8e7b72d6e68..f65cc8196f2d95 100644 --- a/packages/rn-tester/js/examples/SectionList/SectionListExample.js +++ b/packages/rn-tester/js/examples/SectionList/SectionListExample.js @@ -290,7 +290,9 @@ const styles = StyleSheet.create({ }, }); -exports.title = '<SectionList>'; +exports.title = 'SectionList'; +exports.category = 'ListView'; +exports.documentationURL = 'https://reactnative.dev/docs/sectionlist'; exports.description = 'Performant, scrollable list of data.'; exports.examples = [ { diff --git a/packages/rn-tester/js/examples/SegmentedControlIOS/SegmentedControlExampleComponents.js b/packages/rn-tester/js/examples/SegmentedControlIOS/SegmentedControlExampleComponents.js deleted file mode 100644 index 8b06f064fda39f..00000000000000 --- a/packages/rn-tester/js/examples/SegmentedControlIOS/SegmentedControlExampleComponents.js +++ /dev/null @@ -1,114 +0,0 @@ -/** - * 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 strict-local - */ - -'use strict'; - -import * as React from 'react'; -import {SegmentedControlIOS, Text, View, StyleSheet} from 'react-native'; - -export function BasicSegmentedControlExample(): React.Node { - return ( - <View> - <View style={{marginBottom: 10}}> - <SegmentedControlIOS values={['One', 'Two']} /> - </View> - <View> - <SegmentedControlIOS values={['One', 'Two', 'Three', 'Four', 'Five']} /> - </View> - </View> - ); -} - -export function PreSelectedSegmentedControlExample(): React.Node { - return ( - <View> - <View> - <SegmentedControlIOS values={['One', 'Two']} selectedIndex={0} /> - </View> - </View> - ); -} - -export function MomentarySegmentedControlExample(): React.Node { - return ( - <View> - <View> - <SegmentedControlIOS values={['One', 'Two']} momentary={true} /> - </View> - </View> - ); -} - -export function DisabledSegmentedControlExample(): React.Node { - return ( - <View> - <View> - <SegmentedControlIOS - enabled={false} - values={['One', 'Two']} - selectedIndex={1} - /> - </View> - </View> - ); -} - -export function ColorSegmentedControlExample(): React.Node { - return ( - <View> - <View style={{marginBottom: 10}}> - <SegmentedControlIOS - tintColor="#ff0000" - values={['One', 'Two', 'Three', 'Four']} - selectedIndex={0} - /> - </View> - <View> - <SegmentedControlIOS - tintColor="#00ff00" - values={['One', 'Two', 'Three']} - selectedIndex={1} - /> - </View> - </View> - ); -} - -export function EventSegmentedControlExample(): React.Node { - const [selectedIndex, setSelectedIndex] = React.useState(undefined); - const [value, setValue] = React.useState('Not selected'); - const values = ['One', 'Two', 'Three']; - - return ( - <View> - <Text style={styles.text}>Value: {value}</Text> - <Text style={styles.text}>Index: {selectedIndex}</Text> - <SegmentedControlIOS - values={values} - selectedIndex={selectedIndex} - onChange={event => { - setSelectedIndex(event.nativeEvent.selectedSegmentIndex); - }} - onValueChange={changedValue => { - setValue(changedValue); - }} - /> - </View> - ); -} - -const styles = StyleSheet.create({ - text: { - fontSize: 14, - textAlign: 'center', - fontWeight: '500', - margin: 10, - }, -}); diff --git a/packages/rn-tester/js/examples/SegmentedControlIOS/SegmentedControlIOSExample.js b/packages/rn-tester/js/examples/SegmentedControlIOS/SegmentedControlIOSExample.js deleted file mode 100644 index 2824475b838f6a..00000000000000 --- a/packages/rn-tester/js/examples/SegmentedControlIOS/SegmentedControlIOSExample.js +++ /dev/null @@ -1,63 +0,0 @@ -/** - * 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 { - BasicSegmentedControlExample, - PreSelectedSegmentedControlExample, - MomentarySegmentedControlExample, - DisabledSegmentedControlExample, - ColorSegmentedControlExample, - EventSegmentedControlExample, -} = require('./SegmentedControlExampleComponents'); - -exports.title = '<SegmentedControlIOS>'; -exports.displayName = 'SegmentedControlExample'; -exports.description = 'Native segmented control'; -exports.examples = [ - { - title: 'Segmented controls can have values', - render(): React.Element<any> { - return <BasicSegmentedControlExample />; - }, - }, - { - title: 'Segmented controls can have a pre-selected value', - render(): React.Element<any> { - return <PreSelectedSegmentedControlExample />; - }, - }, - { - title: 'Segmented controls can be momentary', - render(): React.Element<any> { - return <MomentarySegmentedControlExample />; - }, - }, - { - title: 'Segmented controls can be disabled', - render(): React.Element<any> { - return <DisabledSegmentedControlExample />; - }, - }, - { - title: 'Custom colors can be provided', - render(): React.Element<any> { - return <ColorSegmentedControlExample />; - }, - }, - { - title: 'Change events can be detected', - render(): React.Element<any> { - return <EventSegmentedControlExample />; - }, - }, -]; diff --git a/packages/rn-tester/js/examples/Share/ShareExample.js b/packages/rn-tester/js/examples/Share/ShareExample.js index bf33e7670ae0d6..ed3268af252fef 100644 --- a/packages/rn-tester/js/examples/Share/ShareExample.js +++ b/packages/rn-tester/js/examples/Share/ShareExample.js @@ -113,6 +113,8 @@ const styles = StyleSheet.create({ exports.framework = 'React'; exports.title = 'Share'; +exports.category = 'Basic'; +exports.documentationURL = 'https://reactnative.dev/docs/share'; exports.description = 'Share data with other Apps.'; exports.examples = [ { diff --git a/packages/rn-tester/js/examples/Slider/SliderExample.js b/packages/rn-tester/js/examples/Slider/SliderExample.js deleted file mode 100644 index 7a1fca6c1ff66c..00000000000000 --- a/packages/rn-tester/js/examples/Slider/SliderExample.js +++ /dev/null @@ -1,136 +0,0 @@ -/** - * 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 {Slider, Text, StyleSheet, View} = require('react-native'); - -function SliderExample(props: React.ElementConfig<typeof Slider>) { - const [value, setValue] = React.useState(0); - - return ( - <View> - <Text style={styles.text}>{value.toFixed(3)}</Text> - <Slider {...props} onValueChange={newValue => setValue(newValue)} /> - </View> - ); -} - -function SlidingCompleteExample(props: React.ElementConfig<typeof Slider>) { - const [slideCompletionValue, setSlideCompletionValue] = React.useState(0); - const [slideCompletionCount, setSlideCompletionCount] = React.useState(0); - - return ( - <View> - <SliderExample - {...props} - onSlidingComplete={value => { - setSlideCompletionValue(value); - setSlideCompletionCount(count => count + 1); - }} - /> - <Text> - Completions: {slideCompletionCount} Value: {slideCompletionValue} - </Text> - </View> - ); -} - -const styles = StyleSheet.create({ - text: { - fontSize: 14, - textAlign: 'center', - fontWeight: '500', - margin: 10, - }, -}); - -exports.title = '<Slider>'; -exports.displayName = 'SliderExample'; -exports.description = 'Slider input for numeric values'; -exports.examples = [ - { - title: 'Default settings', - render(): React.Element<any> { - return <SliderExample />; - }, - }, - { - title: 'Initial value: 0.5', - render(): React.Element<any> { - return <SliderExample value={0.5} />; - }, - }, - { - title: 'minimumValue: -1, maximumValue: 2', - render(): React.Element<any> { - return <SliderExample minimumValue={-1} maximumValue={2} />; - }, - }, - { - title: 'step: 0.25', - render(): React.Element<any> { - return <SliderExample step={0.25} />; - }, - }, - { - title: 'onSlidingComplete', - render(): React.Element<any> { - return <SlidingCompleteExample />; - }, - }, - { - title: 'Custom min/max track tint color', - render(): React.Element<any> { - return ( - <SliderExample - minimumTrackTintColor={'blue'} - maximumTrackTintColor={'red'} - value={0.5} - /> - ); - }, - }, - { - title: 'Custom thumb tint color', - render(): React.Element<any> { - return <SliderExample thumbTintColor={'blue'} />; - }, - }, - { - title: 'Custom thumb image', - platform: 'ios', - render(): React.Element<any> { - return ( - <SliderExample thumbImage={require('../../assets/uie_thumb_big.png')} /> - ); - }, - }, - { - title: 'Custom track image', - platform: 'ios', - render(): React.Element<any> { - return <SliderExample trackImage={require('../../assets/slider.png')} />; - }, - }, - { - title: 'Custom min/max track image', - platform: 'ios', - render(): React.Element<any> { - return ( - <SliderExample - minimumTrackImage={require('../../assets/slider-left.png')} - maximumTrackImage={require('../../assets/slider-right.png')} - /> - ); - }, - }, -]; diff --git a/packages/rn-tester/js/examples/Snapshot/SnapshotExample.js b/packages/rn-tester/js/examples/Snapshot/SnapshotExample.js index 98378fc7a704a1..ac9a8b2fedfbb5 100644 --- a/packages/rn-tester/js/examples/Snapshot/SnapshotExample.js +++ b/packages/rn-tester/js/examples/Snapshot/SnapshotExample.js @@ -58,6 +58,7 @@ const style = StyleSheet.create({ }); exports.title = 'Snapshot / Screenshot'; +exports.category = 'Basic'; exports.description = 'API to capture images from the screen.'; exports.examples = [ { diff --git a/packages/rn-tester/js/examples/StatusBar/StatusBarExample.js b/packages/rn-tester/js/examples/StatusBar/StatusBarExample.js index 60510df9db0e9a..6a5ef7c0eb49c9 100644 --- a/packages/rn-tester/js/examples/StatusBar/StatusBarExample.js +++ b/packages/rn-tester/js/examples/StatusBar/StatusBarExample.js @@ -432,7 +432,9 @@ class ModalExample extends React.Component<{...}, $FlowFixMeState> { } exports.framework = 'React'; -exports.title = '<StatusBar>'; +exports.title = 'StatusBar'; +exports.category = 'Basic'; +exports.documentationURL = 'https://reactnative.dev/docs/statusbar'; exports.description = 'Component for controlling the status bar'; exports.examples = [ { diff --git a/packages/rn-tester/js/examples/Switch/SwitchExample.js b/packages/rn-tester/js/examples/Switch/SwitchExample.js index ce825594e1150a..1acbb65c765608 100644 --- a/packages/rn-tester/js/examples/Switch/SwitchExample.js +++ b/packages/rn-tester/js/examples/Switch/SwitchExample.js @@ -202,7 +202,9 @@ class EventSwitchExample extends React.Component<{...}, $FlowFixMeState> { } } -exports.title = '<Switch>'; +exports.title = 'Switch'; +exports.documentationURL = 'https://reactnative.dev/docs/switch'; +exports.category = 'UI'; exports.displayName = 'SwitchExample'; exports.description = 'Native boolean input'; exports.examples = [ diff --git a/packages/rn-tester/js/examples/TVEventHandler/TVEventHandlerExample.js b/packages/rn-tester/js/examples/TVEventHandler/TVEventHandlerExample.js index 2980bc12745e08..26aff1bfc30ef2 100644 --- a/packages/rn-tester/js/examples/TVEventHandler/TVEventHandlerExample.js +++ b/packages/rn-tester/js/examples/TVEventHandler/TVEventHandlerExample.js @@ -77,6 +77,7 @@ class TVEventHandlerView extends React.Component<Props, State> { exports.framework = 'React'; exports.title = 'TVEventHandler example'; +exports.category = 'iOS'; exports.description = 'iOS alerts and action sheets'; exports.examples = [ { diff --git a/packages/rn-tester/js/examples/Text/TextExample.android.js b/packages/rn-tester/js/examples/Text/TextExample.android.js index cec4b348d19fe4..9a53a9f553169a 100644 --- a/packages/rn-tester/js/examples/Text/TextExample.android.js +++ b/packages/rn-tester/js/examples/Text/TextExample.android.js @@ -878,7 +878,9 @@ const styles = StyleSheet.create({ alignSelf: 'center', }, }); -exports.title = '<Text>'; +exports.title = 'Text'; +exports.documentationURL = 'https://reactnative.dev/docs/text'; +exports.category = 'Basic'; exports.description = 'Base component for rendering styled text.'; exports.examples = [ { diff --git a/packages/rn-tester/js/examples/Text/TextExample.ios.js b/packages/rn-tester/js/examples/Text/TextExample.ios.js index 334daff88c0ff8..9562f1c45b4940 100644 --- a/packages/rn-tester/js/examples/Text/TextExample.ios.js +++ b/packages/rn-tester/js/examples/Text/TextExample.ios.js @@ -479,7 +479,9 @@ class TextWithCapBaseBox extends React.Component<*, *> { } } -exports.title = '<Text>'; +exports.title = 'Text'; +exports.documentationURL = 'https://reactnative.dev/docs/text'; +exports.category = 'Basic'; exports.description = 'Base component for rendering styled text.'; exports.displayName = 'TextExample'; exports.examples = [ diff --git a/packages/rn-tester/js/examples/TextInput/TextInputExample.android.js b/packages/rn-tester/js/examples/TextInput/TextInputExample.android.js index 8c7020f02ccc74..9551a9a96fca89 100644 --- a/packages/rn-tester/js/examples/TextInput/TextInputExample.android.js +++ b/packages/rn-tester/js/examples/TextInput/TextInputExample.android.js @@ -144,7 +144,9 @@ const styles = StyleSheet.create({ }, }); -exports.title = '<TextInput>'; +exports.title = 'TextInput'; +exports.documentationURL = 'https://reactnative.dev/docs/textinput'; +exports.category = 'Basic'; exports.description = 'Single and multi-line text inputs.'; exports.examples = ([ ...TextInputSharedExamples, diff --git a/packages/rn-tester/js/examples/TextInput/TextInputExample.ios.js b/packages/rn-tester/js/examples/TextInput/TextInputExample.ios.js index 311efa1179b066..218ad62d9f4e45 100644 --- a/packages/rn-tester/js/examples/TextInput/TextInputExample.ios.js +++ b/packages/rn-tester/js/examples/TextInput/TextInputExample.ios.js @@ -255,7 +255,9 @@ const styles = StyleSheet.create({ }); exports.displayName = (undefined: ?string); -exports.title = '<TextInput>'; +exports.title = 'TextInput'; +exports.documentationURL = 'https://reactnative.dev/docs/textinput'; +exports.category = 'Basic'; exports.description = 'Single and multi-line text inputs.'; exports.examples = ([ ...TextInputSharedExamples, diff --git a/packages/rn-tester/js/examples/Timer/TimerExample.js b/packages/rn-tester/js/examples/Timer/TimerExample.js index e9c9b74c8cf4de..596020e12705f9 100644 --- a/packages/rn-tester/js/examples/Timer/TimerExample.js +++ b/packages/rn-tester/js/examples/Timer/TimerExample.js @@ -262,6 +262,7 @@ class TimerTester extends React.Component<TimerTesterProps> { exports.framework = 'React'; exports.title = 'Timers'; +exports.category = 'UI'; exports.description = 'A demonstration of Timers in React Native.'; exports.examples = [ diff --git a/packages/rn-tester/js/examples/ToastAndroid/ToastAndroidExample.android.js b/packages/rn-tester/js/examples/ToastAndroid/ToastAndroidExample.android.js index 27ac0e49cffe7e..f83eabada87302 100644 --- a/packages/rn-tester/js/examples/ToastAndroid/ToastAndroidExample.android.js +++ b/packages/rn-tester/js/examples/ToastAndroid/ToastAndroidExample.android.js @@ -124,6 +124,7 @@ const styles = StyleSheet.create({ }); exports.title = 'Toast Example'; +exports.category = 'Android'; exports.description = 'Example that demonstrates the use of an Android Toast to provide feedback.'; exports.examples = [ diff --git a/packages/rn-tester/js/examples/Touchable/TouchableExample.js b/packages/rn-tester/js/examples/Touchable/TouchableExample.js index 3d1c4008ac731c..7c313cbf185604 100644 --- a/packages/rn-tester/js/examples/Touchable/TouchableExample.js +++ b/packages/rn-tester/js/examples/Touchable/TouchableExample.js @@ -553,7 +553,9 @@ const styles = StyleSheet.create({ exports.displayName = (undefined: ?string); exports.description = 'Touchable and onPress examples.'; -exports.title = '<Touchable*> and onPress'; +exports.title = 'Touchable* and onPress'; +exports.category = 'UI'; +exports.documentationURL = 'https://reactnative.dev/docs/touchablehighlight'; exports.examples = [ { title: '<TouchableHighlight>', diff --git a/packages/rn-tester/js/examples/Transform/TransformExample.js b/packages/rn-tester/js/examples/Transform/TransformExample.js index a42a00c105742f..350f1c6c3fd265 100644 --- a/packages/rn-tester/js/examples/Transform/TransformExample.js +++ b/packages/rn-tester/js/examples/Transform/TransformExample.js @@ -237,6 +237,8 @@ const styles = StyleSheet.create({ }); exports.title = 'Transforms'; +exports.category = 'UI'; +exports.documentationURL = 'https://reactnative.dev/docs/transforms'; exports.description = 'View transforms'; exports.examples = [ { diff --git a/packages/rn-tester/js/examples/TransparentHitTest/TransparentHitTestExample.js b/packages/rn-tester/js/examples/TransparentHitTest/TransparentHitTestExample.js index 5c1f50cae40296..edf8bbc9db063e 100644 --- a/packages/rn-tester/js/examples/TransparentHitTest/TransparentHitTestExample.js +++ b/packages/rn-tester/js/examples/TransparentHitTest/TransparentHitTestExample.js @@ -37,7 +37,8 @@ class TransparentHitTestExample extends React.Component<{...}> { } } -exports.title = '<TransparentHitTestExample>'; +exports.title = 'TransparentHitTestExample'; +exports.category = 'UI'; exports.displayName = 'TransparentHitTestExample'; exports.description = 'Transparent view receiving touch events'; exports.examples = [ diff --git a/packages/rn-tester/js/examples/TurboModule/TurboModuleExample.js b/packages/rn-tester/js/examples/TurboModule/TurboModuleExample.js index a22d3c703546c3..7539b6733ad738 100644 --- a/packages/rn-tester/js/examples/TurboModule/TurboModuleExample.js +++ b/packages/rn-tester/js/examples/TurboModule/TurboModuleExample.js @@ -15,6 +15,7 @@ const SampleTurboModuleExample = require('./SampleTurboModuleExample'); exports.displayName = (undefined: ?string); exports.title = 'TurboModule'; +exports.category = 'Basic'; exports.description = 'Usage of TurboModule'; exports.examples = [ { diff --git a/packages/rn-tester/js/examples/Vibration/VibrationExample.js b/packages/rn-tester/js/examples/Vibration/VibrationExample.js index b328d80f538db8..721a9da4ec8dff 100644 --- a/packages/rn-tester/js/examples/Vibration/VibrationExample.js +++ b/packages/rn-tester/js/examples/Vibration/VibrationExample.js @@ -23,6 +23,8 @@ const { exports.framework = 'React'; exports.title = 'Vibration'; +exports.category = 'Basic'; +exports.documentationURL = 'https://reactnative.dev/docs/vibration'; exports.description = 'Vibration API'; let pattern, patternLiteral, patternDescription; diff --git a/packages/rn-tester/js/examples/View/ViewExample.js b/packages/rn-tester/js/examples/View/ViewExample.js index 96636eb2a417e6..cea8eddf7ac750 100644 --- a/packages/rn-tester/js/examples/View/ViewExample.js +++ b/packages/rn-tester/js/examples/View/ViewExample.js @@ -19,7 +19,9 @@ const { View, } = require('react-native'); -exports.title = '<View>'; +exports.title = 'View'; +exports.documentationURL = 'https://reactnative.dev/docs/view'; +exports.category = 'Basic'; exports.description = ('Basic building block of all UI, examples that ' + 'demonstrate some of the many styles available.': string); diff --git a/packages/rn-tester/js/examples/WebSocket/WebSocketExample.js b/packages/rn-tester/js/examples/WebSocket/WebSocketExample.js index 169678443df9af..53aec1a76ac723 100644 --- a/packages/rn-tester/js/examples/WebSocket/WebSocketExample.js +++ b/packages/rn-tester/js/examples/WebSocket/WebSocketExample.js @@ -345,6 +345,7 @@ const styles = StyleSheet.create({ }); exports.title = 'WebSocket'; +exports.category = 'Basic'; exports.description = 'WebSocket API'; exports.examples = [ { diff --git a/packages/rn-tester/js/examples/XHR/XHRExample.js b/packages/rn-tester/js/examples/XHR/XHRExample.js index c8871a01276f22..4418e58f605cbb 100644 --- a/packages/rn-tester/js/examples/XHR/XHRExample.js +++ b/packages/rn-tester/js/examples/XHR/XHRExample.js @@ -20,6 +20,7 @@ const XHRExampleOnTimeOut = require('./XHRExampleOnTimeOut'); exports.framework = 'React'; exports.title = 'XMLHttpRequest'; +exports.category = 'Basic'; exports.description = ('Example that demonstrates upload and download ' + 'requests using XMLHttpRequest.': string); exports.examples = [ diff --git a/packages/rn-tester/js/types/RNTesterTypes.js b/packages/rn-tester/js/types/RNTesterTypes.js index 7abf2f875b1390..73206557af27a2 100644 --- a/packages/rn-tester/js/types/RNTesterTypes.js +++ b/packages/rn-tester/js/types/RNTesterTypes.js @@ -35,6 +35,8 @@ export type RNTesterExampleModule = $ReadOnly<{| title: string, description: string, displayName?: ?string, + documentationURL?: ?string, + category?: ?string, framework?: string, examples: Array<RNTesterExampleModuleItem>, simpleExampleContainer?: ?boolean, @@ -43,4 +45,7 @@ export type RNTesterExampleModule = $ReadOnly<{| export type RNTesterExample = $ReadOnly<{| key: string, module: RNTesterExampleModule, + category?: string, + supportsTVOS?: boolean, + documentationURL?: string, |}>; diff --git a/packages/rn-tester/js/utils/RNTesterActions.js b/packages/rn-tester/js/utils/RNTesterActions.js index 10b6ee1cd83e6f..e9ec1080d604f2 100644 --- a/packages/rn-tester/js/utils/RNTesterActions.js +++ b/packages/rn-tester/js/utils/RNTesterActions.js @@ -25,15 +25,17 @@ export type RNTesterAction = | RNTesterListAction | RNTesterExampleAction; -function Back(): RNTesterBackAction { +function Back(screen: string): RNTesterBackAction { return { type: 'RNTesterBackAction', + screen, }; } -function ExampleList(): RNTesterListAction { +function OpenList(screen: string): RNTesterListAction { return { type: 'RNTesterListAction', + screen, }; } @@ -46,7 +48,7 @@ function ExampleAction(openExample: string): RNTesterExampleAction { const RNTesterActions = { Back, - ExampleList, + OpenList, ExampleAction, }; diff --git a/packages/rn-tester/js/utils/RNTesterAsyncStorageAbstraction.js b/packages/rn-tester/js/utils/RNTesterAsyncStorageAbstraction.js new file mode 100644 index 00000000000000..084bac82307633 --- /dev/null +++ b/packages/rn-tester/js/utils/RNTesterAsyncStorageAbstraction.js @@ -0,0 +1,190 @@ +/** + * 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'; + +import {Platform, Linking} from 'react-native'; +import {AsyncStorage} from 'react-native'; +const RNTesterNavigationReducer = require('./RNTesterNavigationReducer'); +const URIActionMap = require('./URIActionMap'); +import type {RNTesterExample} from '../types/RNTesterTypes'; + +const APP_STATE_KEY = 'RNTesterAppState.v2'; + +type Context = $FlowFixMe; + +export const initializeAsyncStore = (context: Context) => { + Linking.getInitialURL().then(url => { + AsyncStorage.getItem(APP_STATE_KEY, (err, storedString) => { + const exampleAction = URIActionMap( + context.props.exampleFromAppetizeParams, + ); + const urlAction = URIActionMap(url); + const launchAction = exampleAction || urlAction; + if (err || !storedString) { + const initialAction = launchAction || {type: 'RNTesterListAction'}; + context.setState( + RNTesterNavigationReducer(context.state, initialAction), + ); + return; + } + + const storedState = JSON.parse(storedString); + if (launchAction) { + context.setState(RNTesterNavigationReducer(storedState, launchAction)); + return; + } + context.setState({ + openExample: storedState.openExample, + }); + }); + }); + + if (Platform.OS === 'ios') { + Linking.addEventListener('url', url => { + context._handleAction(URIActionMap(url)); + }); + } + + AsyncStorage.getItem('Components', (err, storedString) => { + if (err || !storedString) { + return; + } + const components = JSON.parse(storedString); + context.setState({ + Components: components, + }); + }); + AsyncStorage.getItem('Api', (err, storedString) => { + if (err || !storedString) { + return; + } + const api = JSON.parse(storedString); + context.setState({ + Api: api, + }); + }); + AsyncStorage.getItem('RecentComponents', (err, storedString) => { + if (err || !storedString) { + return; + } + const recentComponents = JSON.parse(storedString); + context.setState({ + recentComponents: recentComponents, + }); + }); + AsyncStorage.getItem('RecentApi', (err, storedString) => { + if (err || !storedString) { + return; + } + const recentApis = JSON.parse(storedString); + context.setState({ + recentApis: recentApis, + }); + }); +}; + +export const addApi = ( + apiName: string, + api: RNTesterExample, + context: $FlowFixMe, +) => { + const stateApi = Object.assign({}, context.state.Api); + stateApi[apiName] = api; + context.setState({ + Api: stateApi, + }); + // Syncing the bookmarks over async storage + AsyncStorage.setItem('Api', JSON.stringify(stateApi)); +}; + +export const addComponent = ( + componentName: string, + component: RNTesterExample, + context: Context, +) => { + const stateComponent = Object.assign({}, context.state.Components); + stateComponent[componentName] = component; + context.setState({ + Components: stateComponent, + }); + // Syncing the bookmarks over async storage + AsyncStorage.setItem('Components', JSON.stringify(stateComponent)); +}; + +export const removeApi = (apiName: string, context: Context) => { + const stateApi = Object.assign({}, context.state.Api); + delete stateApi[apiName]; + context.setState({ + Api: stateApi, + }); + AsyncStorage.setItem('Api', JSON.stringify(stateApi)); +}; + +export const removeComponent = (componentName: string, context: Context) => { + const stateComponent = Object.assign({}, context.state.Components); + delete stateComponent[componentName]; + context.setState({ + Components: stateComponent, + }); + AsyncStorage.setItem('Components', JSON.stringify(stateComponent)); +}; + +export const checkBookmarks = ( + title: string, + key: string, + context: Context, +): boolean => { + if (key === 'APIS' || key === 'RECENT_APIS') { + return context.state.Api[title] === undefined; + } + return context.state.Components[title] === undefined; +}; + +export const updateRecentlyViewedList = ( + item: RNTesterExample, + key: string, + context: Context, +) => { + const openedItem = item; + if (key === 'COMPONENTS' || key === 'RECENT_COMPONENTS') { + let componentsCopy = [...context.state.recentComponents]; + const ind = componentsCopy.findIndex( + component => component.key === openedItem.key, + ); + if (ind !== -1) { + componentsCopy.splice(ind, 1); + } + if (context.state.recentComponents.length >= 5) { + componentsCopy.pop(); + } + componentsCopy.unshift(openedItem); + context.setState({ + recentComponents: componentsCopy, + }); + // Syncing the recently viewed components over async storage + AsyncStorage.setItem('RecentComponents', JSON.stringify(componentsCopy)); + } else { + let apisCopy = [...context.state.recentApis]; + const ind = apisCopy.findIndex(api => api.key === openedItem.key); + if (ind !== -1) { + apisCopy.splice(ind, 1); + } + if (context.state.recentApis.length >= 5) { + apisCopy.pop(); + } + apisCopy.unshift(openedItem); + context.setState({ + recentApis: apisCopy, + }); + // Syncing the recently viewed apis over async storage + AsyncStorage.setItem('RecentApi', JSON.stringify(apisCopy)); + } +}; diff --git a/packages/rn-tester/js/utils/RNTesterList.android.js b/packages/rn-tester/js/utils/RNTesterList.android.js index 2b1b921efe75f0..bb509d0f3b0103 100644 --- a/packages/rn-tester/js/utils/RNTesterList.android.js +++ b/packages/rn-tester/js/utils/RNTesterList.android.js @@ -15,18 +15,22 @@ import type {RNTesterExample} from '../types/RNTesterTypes'; const ComponentExamples: Array<RNTesterExample> = [ { key: 'ActivityIndicatorExample', + category: 'UI', module: require('../examples/ActivityIndicator/ActivityIndicatorExample'), }, { key: 'ButtonExample', + category: 'UI', module: require('../examples/Button/ButtonExample'), }, { key: 'FlatListExample', + category: 'ListView', module: require('../examples/FlatList/FlatListExample'), }, { key: 'ImageExample', + category: 'Basic', module: require('../examples/Image/ImageExample'), }, { @@ -39,74 +43,75 @@ const ComponentExamples: Array<RNTesterExample> = [ }, { key: 'ModalExample', + category: 'UI', module: require('../examples/Modal/ModalExample'), }, { key: 'MultiColumnExample', + category: 'ListView', module: require('../examples/MultiColumn/MultiColumnExample'), }, { key: 'NewAppScreenExample', module: require('../examples/NewAppScreen/NewAppScreenExample'), }, - { - key: 'PickerExample', - module: require('../examples/Picker/PickerExample'), - }, { key: 'PressableExample', + category: 'UI', module: require('../examples/Pressable/PressableExample'), }, - { - key: 'ProgressBarAndroidExample', - module: require('../examples/ProgressBarAndroid/ProgressBarAndroidExample'), - }, { key: 'RefreshControlExample', module: require('../examples/RefreshControl/RefreshControlExample'), }, { key: 'ScrollViewExample', + category: 'Basic', module: require('../examples/ScrollView/ScrollViewExample'), }, { key: 'ScrollViewSimpleExample', + category: 'Basic', module: require('../examples/ScrollView/ScrollViewSimpleExample'), }, { key: 'ScrollViewAnimatedExample', + category: 'Basic', module: require('../examples/ScrollView/ScrollViewAnimatedExample'), }, { key: 'SectionListExample', + category: 'ListView', module: require('../examples/SectionList/SectionListExample'), }, - { - key: 'SliderExample', - module: require('../examples/Slider/SliderExample'), - }, { key: 'StatusBarExample', + category: 'UI', module: require('../examples/StatusBar/StatusBarExample'), }, { key: 'SwitchExample', + category: 'UI', module: require('../examples/Switch/SwitchExample'), }, { key: 'TextExample', + category: 'Basic', module: require('../examples/Text/TextExample'), }, { key: 'TextInputExample', + category: 'Basic', module: require('../examples/TextInput/TextInputExample'), }, { key: 'TouchableExample', + category: 'UI', module: require('../examples/Touchable/TouchableExample'), }, { key: 'ViewExample', + category: 'Basic', module: require('../examples/View/ViewExample'), }, ]; @@ -114,130 +119,147 @@ const ComponentExamples: Array<RNTesterExample> = [ const APIExamples: Array<RNTesterExample> = [ { key: 'AccessibilityExample', + category: 'Basic', module: require('../examples/Accessibility/AccessibilityExample'), }, { key: 'AccessibilityAndroidExample', + category: 'Android', module: require('../examples/Accessibility/AccessibilityAndroidExample'), }, { key: 'AlertExample', + category: 'UI', module: require('../examples/Alert/AlertExample'), }, { key: 'AnimatedExample', + category: 'UI', module: require('../examples/Animated/AnimatedExample'), }, { key: 'Animation - GratuitousAnimation', + category: 'UI', module: require('../examples/Animated/AnimatedGratuitousApp/AnExApp'), }, { key: 'AppearanceExample', + category: 'UI', module: require('../examples/Appearance/AppearanceExample'), }, { key: 'AppStateExample', + category: 'Basic', module: require('../examples/AppState/AppStateExample'), }, { key: 'BorderExample', + category: 'UI', module: require('../examples/Border/BorderExample'), }, - { - key: 'BoxShadowExample', - module: require('../examples/BoxShadow/BoxShadowExample'), - }, - { - key: 'ClipboardExample', - module: require('../examples/Clipboard/ClipboardExample'), - }, { key: 'CrashExample', + category: 'Basic', module: require('../examples/Crash/CrashExample'), }, - { - key: 'DatePickerAndroidExample', - module: require('../examples/DatePicker/DatePickerAndroidExample'), - }, { key: 'DevSettings', + category: 'Basic', module: require('../examples/DevSettings/DevSettingsExample'), }, { key: 'Dimensions', + category: 'UI', module: require('../examples/Dimensions/DimensionsExample'), }, { key: 'LayoutEventsExample', + category: 'UI', module: require('../examples/Layout/LayoutEventsExample'), }, { key: 'LinkingExample', + category: 'Basic', module: require('../examples/Linking/LinkingExample'), }, { key: 'LayoutAnimationExample', + category: 'UI', module: require('../examples/Layout/LayoutAnimationExample'), }, { key: 'LayoutExample', + category: 'UI', module: require('../examples/Layout/LayoutExample'), }, { key: 'NativeAnimationsExample', + category: 'UI', module: require('../examples/NativeAnimation/NativeAnimationsExample'), }, { key: 'OrientationChangeExample', + category: 'UI', module: require('../examples/OrientationChange/OrientationChangeExample'), }, { key: 'PanResponderExample', + category: 'Basic', module: require('../examples/PanResponder/PanResponderExample'), }, { key: 'PermissionsExampleAndroid', + category: 'Android', module: require('../examples/PermissionsAndroid/PermissionsExample'), }, { key: 'PlatformColorExample', + category: 'UI', module: require('../examples/PlatformColor/PlatformColorExample'), }, { key: 'PointerEventsExample', + category: 'Basic', module: require('../examples/PointerEvents/PointerEventsExample'), }, { key: 'RTLExample', + category: 'Basic', module: require('../examples/RTL/RTLExample'), }, { key: 'ShareExample', + category: 'Basic', module: require('../examples/Share/ShareExample'), }, { key: 'TimerExample', + category: 'UI', module: require('../examples/Timer/TimerExample'), }, { key: 'ToastAndroidExample', + category: 'Android', module: require('../examples/ToastAndroid/ToastAndroidExample'), }, { key: 'TransformExample', + category: 'UI', module: require('../examples/Transform/TransformExample'), }, { key: 'VibrationExample', + category: 'Basic', module: require('../examples/Vibration/VibrationExample'), }, { key: 'WebSocketExample', + category: 'Basic', module: require('../examples/WebSocket/WebSocketExample'), }, { key: 'XHRExample', + category: 'Basic', module: require('../examples/XHR/XHRExample'), }, ]; diff --git a/packages/rn-tester/js/utils/RNTesterList.ios.js b/packages/rn-tester/js/utils/RNTesterList.ios.js index 8e43dacf5415ac..f0e96e4b005734 100644 --- a/packages/rn-tester/js/utils/RNTesterList.ios.js +++ b/packages/rn-tester/js/utils/RNTesterList.ios.js @@ -15,23 +15,27 @@ import type {RNTesterExample} from '../types/RNTesterTypes'; const ComponentExamples: Array<RNTesterExample> = [ { key: 'ActivityIndicatorExample', + category: 'UI', module: require('../examples/ActivityIndicator/ActivityIndicatorExample'), + supportsTVOS: true, }, { key: 'ButtonExample', module: require('../examples/Button/ButtonExample'), - }, - { - key: 'DatePickerIOSExample', - module: require('../examples/DatePicker/DatePickerIOSExample'), + category: 'UI', + supportsTVOS: true, }, { key: 'FlatListExample', module: require('../examples/FlatList/FlatListExample'), + category: 'ListView', + supportsTVOS: true, }, { key: 'ImageExample', module: require('../examples/Image/ImageExample'), + category: 'Basic', + supportsTVOS: true, }, { key: 'JSResponderHandlerExample', @@ -40,106 +44,109 @@ const ComponentExamples: Array<RNTesterExample> = [ { key: 'InputAccessoryViewExample', module: require('../examples/InputAccessoryView/InputAccessoryViewExample'), + supportsTVOS: true, }, { key: 'KeyboardAvoidingViewExample', module: require('../examples/KeyboardAvoidingView/KeyboardAvoidingViewExample'), + supportsTVOS: false, }, { key: 'LayoutEventsExample', module: require('../examples/Layout/LayoutEventsExample'), - }, - { - key: 'MaskedViewExample', - module: require('../examples/MaskedView/MaskedViewExample'), + supportsTVOS: true, }, { key: 'ModalExample', module: require('../examples/Modal/ModalExample'), + supportsTVOS: true, }, { key: 'MultiColumnExample', module: require('../examples/MultiColumn/MultiColumnExample'), + supportsTVOS: true, }, { key: 'NewAppScreenExample', module: require('../examples/NewAppScreen/NewAppScreenExample'), - }, - { - key: 'PickerExample', - module: require('../examples/Picker/PickerExample'), - }, - { - key: 'PickerIOSExample', - module: require('../examples/Picker/PickerIOSExample'), + supportsTVOS: false, }, { key: 'PressableExample', module: require('../examples/Pressable/PressableExample'), - }, - { - key: 'ProgressViewIOSExample', - module: require('../examples/ProgressViewIOS/ProgressViewIOSExample'), + supportsTVOS: true, }, { key: 'RefreshControlExample', module: require('../examples/RefreshControl/RefreshControlExample'), + supportsTVOS: false, }, { key: 'ScrollViewSimpleExample', module: require('../examples/ScrollView/ScrollViewSimpleExample'), + category: 'Basic', + supportsTVOS: true, }, { key: 'SafeAreaViewExample', module: require('../examples/SafeAreaView/SafeAreaViewExample'), + supportsTVOS: true, }, { key: 'ScrollViewExample', module: require('../examples/ScrollView/ScrollViewExample'), + category: 'Basic', + supportsTVOS: true, }, { key: 'ScrollViewAnimatedExample', module: require('../examples/ScrollView/ScrollViewAnimatedExample'), + supportsTVOS: true, }, { key: 'SectionListExample', module: require('../examples/SectionList/SectionListExample'), - }, - { - key: 'SegmentedControlIOSExample', - module: require('../examples/SegmentedControlIOS/SegmentedControlIOSExample'), - }, - { - key: 'SliderExample', - module: require('../examples/Slider/SliderExample'), + category: 'ListView', + supportsTVOS: true, }, { key: 'StatusBarExample', module: require('../examples/StatusBar/StatusBarExample'), + supportsTVOS: false, }, { key: 'SwitchExample', module: require('../examples/Switch/SwitchExample'), + category: 'UI', + supportsTVOS: false, }, { key: 'TextExample', module: require('../examples/Text/TextExample.ios'), + category: 'Basic', + supportsTVOS: true, }, { key: 'TextInputExample', - module: require('../examples/TextInput/TextInputExample.ios'), + module: require('../examples/TextInput/TextInputExample'), + category: 'Basic', + supportsTVOS: true, }, { key: 'TouchableExample', module: require('../examples/Touchable/TouchableExample'), + supportsTVOS: true, }, { key: 'TransparentHitTestExample', module: require('../examples/TransparentHitTest/TransparentHitTestExample'), + supportsTVOS: false, }, { key: 'ViewExample', module: require('../examples/View/ViewExample'), + category: 'Basic', + supportsTVOS: true, }, ]; @@ -147,54 +154,60 @@ const APIExamples: Array<RNTesterExample> = [ { key: 'AccessibilityExample', module: require('../examples/Accessibility/AccessibilityExample'), + supportsTVOS: false, }, { key: 'AccessibilityIOSExample', module: require('../examples/Accessibility/AccessibilityIOSExample'), + category: 'iOS', + supportsTVOS: false, }, { key: 'ActionSheetIOSExample', module: require('../examples/ActionSheetIOS/ActionSheetIOSExample'), + category: 'iOS', + supportsTVOS: true, }, { key: 'AlertIOSExample', module: require('../examples/Alert/AlertIOSExample'), + category: 'iOS', + supportsTVOS: true, }, { key: 'AnimatedExample', module: require('../examples/Animated/AnimatedExample'), + supportsTVOS: true, }, { key: 'AnExApp', module: require('../examples/Animated/AnimatedGratuitousApp/AnExApp'), + supportsTVOS: true, }, { key: 'AppearanceExample', module: require('../examples/Appearance/AppearanceExample'), + supportsTVOS: false, }, { key: 'AppStateExample', module: require('../examples/AppState/AppStateExample'), - }, - { - key: 'AsyncStorageExample', - module: require('../examples/AsyncStorage/AsyncStorageExample'), + supportsTVOS: true, }, { key: 'BorderExample', module: require('../examples/Border/BorderExample'), + supportsTVOS: true, }, { key: 'BoxShadowExample', module: require('../examples/BoxShadow/BoxShadowExample'), - }, - { - key: 'ClipboardExample', - module: require('../examples/Clipboard/ClipboardExample'), + supportsTVOS: true, }, { key: 'CrashExample', module: require('../examples/Crash/CrashExample'), + supportsTVOS: false, }, { key: 'DevSettings', @@ -203,86 +216,102 @@ const APIExamples: Array<RNTesterExample> = [ { key: 'Dimensions', module: require('../examples/Dimensions/DimensionsExample'), + supportsTVOS: true, }, { key: 'LayoutAnimationExample', module: require('../examples/Layout/LayoutAnimationExample'), + supportsTVOS: true, }, { key: 'LayoutExample', module: require('../examples/Layout/LayoutExample'), + supportsTVOS: true, }, { key: 'LinkingExample', module: require('../examples/Linking/LinkingExample'), + supportsTVOS: true, }, { key: 'NativeAnimationsExample', module: require('../examples/NativeAnimation/NativeAnimationsExample'), + supportsTVOS: true, }, { key: 'OrientationChangeExample', module: require('../examples/OrientationChange/OrientationChangeExample'), + supportsTVOS: false, }, { key: 'PanResponderExample', module: require('../examples/PanResponder/PanResponderExample'), + supportsTVOS: false, }, { key: 'PlatformColorExample', module: require('../examples/PlatformColor/PlatformColorExample'), + supportsTVOS: true, }, { key: 'PointerEventsExample', module: require('../examples/PointerEvents/PointerEventsExample'), - }, - { - key: 'PushNotificationIOSExample', - module: require('../examples/PushNotificationIOS/PushNotificationIOSExample'), + supportsTVOS: false, }, { key: 'RCTRootViewIOSExample', module: require('../examples/RCTRootView/RCTRootViewIOSExample'), + supportsTVOS: true, }, { key: 'RTLExample', module: require('../examples/RTL/RTLExample'), + supportsTVOS: true, }, { key: 'ShareExample', module: require('../examples/Share/ShareExample'), + supportsTVOS: true, }, { key: 'SnapshotExample', module: require('../examples/Snapshot/SnapshotExample'), + supportsTVOS: true, }, { key: 'TimerExample', module: require('../examples/Timer/TimerExample'), + supportsTVOS: true, }, { key: 'TransformExample', module: require('../examples/Transform/TransformExample'), + supportsTVOS: true, }, { key: 'TurboModuleExample', module: require('../examples/TurboModule/TurboModuleExample'), + supportsTVOS: false, }, { key: 'TVEventHandlerExample', module: require('../examples/TVEventHandler/TVEventHandlerExample'), + supportsTVOS: true, }, { key: 'VibrationExample', module: require('../examples/Vibration/VibrationExample'), + supportsTVOS: false, }, { key: 'WebSocketExample', module: require('../examples/WebSocket/WebSocketExample'), + supportsTVOS: true, }, { key: 'XHRExample', module: require('../examples/XHR/XHRExample'), + supportsTVOS: true, }, ]; diff --git a/packages/rn-tester/js/utils/RNTesterNavigationReducer.js b/packages/rn-tester/js/utils/RNTesterNavigationReducer.js index ee8d9e273e407a..0f8efa4455906b 100644 --- a/packages/rn-tester/js/utils/RNTesterNavigationReducer.js +++ b/packages/rn-tester/js/utils/RNTesterNavigationReducer.js @@ -11,11 +11,26 @@ 'use strict'; const RNTesterList = require('./RNTesterList'); +import type {RNTesterExample} from '../types/RNTesterTypes'; -export type RNTesterNavigationState = {openExample: ?string, ...}; +export type RNTesterNavigationState = { + openExample: ?string, + screen: string, + Components: {...}, + Api: {...}, + recentComponents: Array<RNTesterExample>, + recentApis: Array<RNTesterExample>, + AddApi: (apiName: string, api: RNTesterExample) => mixed, + AddComponent: (componentName: string, component: RNTesterExample) => mixed, + RemoveApi: (apiName: string) => mixed, + RemoveComponent: (componentName: string) => mixed, + checkBookmark: (title: string, key: string) => mixed, + updateRecentlyViewedList: (item: RNTesterExample, key: string) => mixed, + ... +}; function RNTesterNavigationReducer( - state: ?RNTesterNavigationState, + state: RNTesterNavigationState, action: any, ): RNTesterNavigationState { if ( @@ -27,17 +42,27 @@ function RNTesterNavigationReducer( (state.openExample && action.type === 'RNTesterBackAction') ) { return { + ...state, + screen: action.screen ?? 'component', // A null openExample will cause the views to display the RNTester example list openExample: null, }; } + if (action.screen === 'bookmark' && action.type === 'RNTesterBackAction') { + return { + ...state, + screen: 'component', + openExample: null, + }; + } + if (action.type === 'RNTesterExampleAction') { // Make sure we see the module before returning the new state const ExampleModule = RNTesterList.Modules[action.openExample]; - if (ExampleModule) { return { + ...state, openExample: action.openExample, }; } diff --git a/packages/rn-tester/js/utils/RNTesterStatePersister.js b/packages/rn-tester/js/utils/RNTesterStatePersister.js index 751070395d129a..8130a69c38bba9 100644 --- a/packages/rn-tester/js/utils/RNTesterStatePersister.js +++ b/packages/rn-tester/js/utils/RNTesterStatePersister.js @@ -11,7 +11,7 @@ 'use strict'; const React = require('react'); -const {AsyncStorage} = require('react-native'); +import {AsyncStorage} from 'react-native'; export type PassProps<State> = { state: State,