diff --git a/.buckconfig b/.buckconfig new file mode 100644 index 000000000..cc1b65568 --- /dev/null +++ b/.buckconfig @@ -0,0 +1,8 @@ + +[android] + target = Google Inc.:Google APIs:23 + +[maven_repositories] + central = https://repo1.maven.org/maven2 + + diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..3b281f2f7 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 diff --git a/.eslintrc b/.eslintrc index e1ec93029..5a7692b80 100644 --- a/.eslintrc +++ b/.eslintrc @@ -55,7 +55,6 @@ "no-debugger": 1, // disallow use of debugger "no-dupe-keys": 1, // disallow duplicate keys when creating object literals "no-empty": 0, // disallow empty statements - "no-empty-class": 1, // disallow the use of empty character classes in regular expressions "no-ex-assign": 1, // disallow assigning to the exception in a catch block "no-extra-boolean-cast": 1, // disallow double-negation boolean casts in a boolean context "no-extra-parens": 0, // disallow unnecessary parentheses (off by default) @@ -126,9 +125,7 @@ // Strict Mode // These rules relate to using strict mode. - "global-strict": [2, "always"], // require or disallow the "use strict" pragma in the global scope (off by default in the node environment) - "no-extra-strict": 1, // disallow unnecessary use of "use strict"; when already in strict mode - "strict": 0, // require that all functions are run in strict mode + "strict": [2, "global"], // require or disallow the "use strict" pragma in the global scope (off by default in the node environment) // Variables // These rules have to do with variable declarations. @@ -174,11 +171,9 @@ "no-lonely-if": 0, // disallow if as the only statement in an else block (off by default) "no-new-object": 1, // disallow use of the Object constructor "no-spaced-func": 1, // disallow space between function identifier and application - "no-space-before-semi": 1, // disallow space before semicolon "no-ternary": 0, // disallow the use of ternary operators (off by default) "no-trailing-spaces": 1, // disallow trailing whitespace at the end of lines "no-underscore-dangle": 0, // disallow dangling underscores in identifiers - "no-wrap-func": 1, // disallow wrapping of non-IIFE statements in parens "no-mixed-spaces-and-tabs": 1, // disallow mixed spaces and tabs for indentation "quotes": [1, "single", "avoid-escape"], // specify whether double or single quotes should be used "quote-props": 0, // require quotes around object literal property names (off by default) diff --git a/.flowconfig b/.flowconfig index 65df8bc75..53bfd2520 100644 --- a/.flowconfig +++ b/.flowconfig @@ -7,12 +7,26 @@ # Some modules have their own node_modules with overlap .*/node_modules/node-haste/.* -# Ignore react-tools where there are overlaps, but don't ignore anything that -# react-native relies on -.*/node_modules/react-tools/src/React.js -.*/node_modules/react-tools/src/renderers/shared/event/EventPropagators.js -.*/node_modules/react-tools/src/renderers/shared/event/eventPlugins/ResponderEventPlugin.js -.*/node_modules/react-tools/src/shared/vendor/core/ExecutionEnvironment.js +# Ugh +.*/node_modules/babel.* +.*/node_modules/babylon.* +.*/node_modules/invariant.* + +# Ignore react and fbjs where there are overlaps, but don't ignore +# anything that react-native relies on +.*/node_modules/fbjs/lib/Map.js +.*/node_modules/fbjs/lib/Promise.js +.*/node_modules/fbjs/lib/fetch.js +.*/node_modules/fbjs/lib/ExecutionEnvironment.js +.*/node_modules/fbjs/lib/isEmpty.js +.*/node_modules/fbjs/lib/crc32.js +.*/node_modules/fbjs/lib/ErrorUtils.js + +# Flow has a built-in definition for the 'react' module which we prefer to use +# over the currently-untyped source +.*/node_modules/react/react.js +.*/node_modules/react/lib/React.js +.*/node_modules/react/lib/ReactDOM.js # Ignore commoner tests .*/node_modules/commoner/test/.* @@ -43,9 +57,9 @@ suppress_type=$FlowIssue suppress_type=$FlowFixMe suppress_type=$FixMe -suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(1[0-4]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) -suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(1[0-4]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)? #[0-9]+ +suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(2[0-0]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) +suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(2[0-0]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy [version] -0.14.0 +0.20.1 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..8f9372e00 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +# Force LF line endings for Bash scripts. On Windows the rest of the source +# files will typically have CR+LF endings (Git default on Windows), but Bash +# scripts need to have LF endings to work (under Cygwin), thus override to force +# that. +gradlew text eol=lf +*.sh text eol=lf diff --git a/.gitignore b/.gitignore index 5326cd9ca..74ab1dc77 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,6 @@ !**/*.xcworkspacedata !**/*.xcsettings !**/*.xcscheme -build/ *.pbxuser !default.pbxuser *.mode1v3 @@ -22,10 +21,21 @@ DerivedData *.xcuserstate project.xcworkspace -# OS X -.DS_Store +# Gradle +/build/ +/Examples/**/android/app/build/ +/ReactAndroid/build/ + +# Android +.idea +.gradle +local.properties +*.iml # Node node_modules *.log .nvm + +# OS X +.DS_Store diff --git a/.travis.yml b/.travis.yml index 9319fb00a..67ac1cc42 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,24 +1,21 @@ language: objective-c -osx_image: xcode6.4 +osx_image: xcode7.2 cache: directories: - node_modules - .nvm -before_install: - # Update brew twice because the first run can fail: https://github.com/Homebrew/homebrew/issues/42553 - - brew update; brew update install: - - brew reinstall xctool nvm + - brew reinstall nvm - mkdir -p .nvm - export NVM_DIR="$PWD/.nvm" - source $(brew --prefix nvm)/nvm.sh - - nvm install iojs-v3 - - rm -Rf `node -p "require('os').tmpDir()"`/jest_preprocess_cache - - npm install -g flow-bin@`node -p "require('fs').readFileSync('.flowconfig', 'utf8').split('[version]')[1].trim()"` + - nvm install 5 + - rm -Rf "${TMPDIR}/jest_preprocess_cache" - npm config set spin=false + - npm install -g flow-bin@`node -p "require('fs').readFileSync('.flowconfig', 'utf8').split('[version]')[1].trim()"` - npm install script: @@ -31,7 +28,19 @@ script: elif [ "$TEST_TYPE" = js ] then - flow check && npm test + npm install github@0.2.4 + cat <(echo eslint; npm run lint --silent -- --format=json; echo flow; flow --json) | GITHUB_TOKEN="af6ef0d15709bc91d""06a6217a5a826a226fb57b7" node bots/code-analysis-bot.js + flow check && npm test -- '\/Libraries\/' + + elif [ "$TEST_TYPE" = packager ] + then + + npm test -- '\/packager\/' + + elif [ "$TEST_TYPE" = cli ] + then + + npm test -- '\/(local|private|react-native)-cli\/' elif [ "$TEST_TYPE" = build_website ] then @@ -60,11 +69,19 @@ env: matrix: - TEST_TYPE=objc - TEST_TYPE=js + - TEST_TYPE=packager + - TEST_TYPE=cli - TEST_TYPE=build_website - TEST_TYPE=e2e global: + # $GITHUB_TOKEN - secure: "HlmG8M2DmBUSBh6KH1yVIe/8gR4iibg4WfcHq1x/xYQxGbvleq7NOo04V6eFHnl9cvZCu+PKH0841WLnGR7c4BBf47GVu/o16nXzggPumHKy++lDzxFPlJ1faMDfjg/5vjbAxRUe7D3y98hQSeGHH4tedc8LvTaFLVu7iiGqvjU=" + # $APPETIZE_TOKEN + - secure: "egsvVSpszTzrNd6bN62DsVAzMiSZI/OHgdizfPryqvqWBf655ztE6XFQSEFNpuIAzSKDDF25ioT8iPfVsbC1iK6HDWHfmqYxML0L+OoU0gi+hV2oKUBFZDZ1fwSnFoWuBdNdMDpLlUxvJp6N1WyfNOB2dxuZUt8eTt48Hi3+Hpc=" + # $S3_TOKEN + - secure: "lY8JZPA0A7zT7L5KF9BBg34XYWIeR/RJiEvE7l7oVr88KnEPtyd//79eHhhVKnUnav7zsk5QJwkcX0MxKTp/dp4G0Am+zOX+sfA8kQrJ+2/+FzFW7AEsW/kHByfaIEIly9DQvUFt4I4oMm8nQZysJLahDgNWglyI3RTuJp//hcY=" branches: only: - master + - /^.*-stable$/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 295ef36c0..3171208c8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,10 +19,28 @@ The core team will be monitoring for pull requests. When we get one, we'll run s 1. Fork the repo and create your branch from `master`. 2. If you've added code that should be tested, add tests! 3. If you've changed APIs, update the documentation. -4. Ensure tests pass on Travis. -5. Make sure your code lints (`node linter.js `). -6. Squash your commits (`git rebase -i`). -7. If you haven't already, complete the CLA. +4. Add the copyright notice to the top of any new files you've added. +5. Ensure tests pass on Travis and Circle CI. +6. Make sure your code lints (`node linter.js `). +7. Squash your commits (`git rebase -i`). +8. If you haven't already, sign the [CLA](https://code.facebook.com/cla). + +#### Copyright Notice for files + +Copy and paste this to the top of your new file(s): + +```JS +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +``` + +If you've added a new module, add a `@providesModule ` at the end of the comment. This will allow the haste package manager to find it. ### Contributor License Agreement (CLA) @@ -46,8 +64,8 @@ Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe ## How to Get in Touch -* IRC - [#reactnative on freenode](http://webchat.freenode.net/?channels=reactnative) * [Facebook group](https://www.facebook.com/groups/react.native.community/) +* Reactiflux — [#react-native](http://join.reactiflux.com/) ## Style Guide @@ -67,6 +85,14 @@ Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe * Do not use the optional parameters of `setTimeout` and `setInterval` * 80 character line length +#### JSX + +* Prefer `'` over `"` for string literal props +* When wrapping opening tags over multiple lines, place one prop per line +* `{}` of props should hug their values (no spaces) +* Place the closing `>` of opening tags on the same line as the last prop +* Place the closing `/>` of self-closing tags on their own line and left-align them with the opening `<` + #### Objective-C * Space after `@property` declarations @@ -75,6 +101,12 @@ Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe * *Try* to keep it around 80 characters line length (sometimes it's just not possible...) * `*` operator goes with the variable name (e.g. `NSObject *variableName;`) +#### Java + +* If a method call spans multiple lines closing bracket is on the same line as the last argument. +* If a method header doesn't fit on one line each argument goes on a separate line. +* 100 character line length + ### Documentation * Do not wrap lines at 80 characters - configure your editor to soft-wrap when editing documentation. diff --git a/Examples/2048/2048/AppDelegate.m b/Examples/2048/2048/AppDelegate.m index 2413b3552..9975d7df3 100644 --- a/Examples/2048/2048/AppDelegate.m +++ b/Examples/2048/2048/AppDelegate.m @@ -36,7 +36,7 @@ * on the same Wi-Fi network. */ - jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/2048/Game2048.bundle?platform=ios"]; + jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/2048/Game2048.bundle?platform=ios&dev=true"]; /** * OPTION 2 @@ -56,7 +56,7 @@ launchOptions:launchOptions]; self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; - UIViewController *rootViewController = [[UIViewController alloc] init]; + UIViewController *rootViewController = [UIViewController new]; rootViewController.view = rootView; self.window.rootViewController = rootViewController; [self.window makeKeyAndVisible]; diff --git a/Examples/2048/Game2048.js b/Examples/2048/Game2048.js index 90111d38b..239d1dade 100644 --- a/Examples/2048/Game2048.js +++ b/Examples/2048/Game2048.js @@ -237,7 +237,7 @@ var styles = StyleSheet.create({ marginBottom: 20, }, tryAgain: { - backgroundColor: '#887766', + backgroundColor: '#887761', padding: 20, borderRadius: 5, }, @@ -279,7 +279,7 @@ var styles = StyleSheet.create({ backgroundColor: '#eeeecc', }, tile8: { - backgroundColor: '#ffbb88', + backgroundColor: '#ffbb87', }, tile16: { backgroundColor: '#ff9966', diff --git a/Examples/2048/GameBoard.js b/Examples/2048/GameBoard.js index 1d7f34085..6ed92e6b5 100644 --- a/Examples/2048/GameBoard.js +++ b/Examples/2048/GameBoard.js @@ -17,7 +17,7 @@ 'use strict'; // NB: Taken straight from: https://github.com/IvanVergiliev/2048-react/blob/master/src/board.js -// with no modificiation except to format it for CommonJS and fix lint/flow errors +// with no modification except to format it for CommonJS and fix lint/flow errors var rotateLeft = function (matrix) { var rows = matrix.length; diff --git a/Examples/Movies/MovieCell.js b/Examples/Movies/MovieCell.js index 62062cb2a..c90931c08 100644 --- a/Examples/Movies/MovieCell.js +++ b/Examples/Movies/MovieCell.js @@ -18,7 +18,6 @@ var React = require('react-native'); var { Image, - PixelRatio, Platform, StyleSheet, Text, @@ -99,8 +98,7 @@ var styles = StyleSheet.create({ }, cellBorder: { backgroundColor: 'rgba(0, 0, 0, 0.1)', - // Trick to get the thinest line the device can display - height: 1 / PixelRatio.get(), + height: StyleSheet.hairlineWidth, marginLeft: 4, }, }); diff --git a/Examples/Movies/MovieScreen.js b/Examples/Movies/MovieScreen.js index 09d6544df..ff228a8a3 100644 --- a/Examples/Movies/MovieScreen.js +++ b/Examples/Movies/MovieScreen.js @@ -18,7 +18,6 @@ var React = require('react-native'); var { Image, - PixelRatio, ScrollView, StyleSheet, Text, @@ -152,7 +151,7 @@ var styles = StyleSheet.create({ }, separator: { backgroundColor: 'rgba(0, 0, 0, 0.1)', - height: 1 / PixelRatio.get(), + height: StyleSheet.hairlineWidth, marginVertical: 10, }, castTitle: { diff --git a/Examples/Movies/Movies.xcodeproj/project.pbxproj b/Examples/Movies/Movies.xcodeproj/project.pbxproj index 21fc0cb88..f3937e428 100644 --- a/Examples/Movies/Movies.xcodeproj/project.pbxproj +++ b/Examples/Movies/Movies.xcodeproj/project.pbxproj @@ -216,7 +216,7 @@ 83CBB9F71A601CBA00E9B192 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0610; + LastUpgradeCheck = 0700; ORGANIZATIONNAME = Facebook; }; buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "Movies" */; @@ -358,6 +358,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "-ObjC"; + PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = Movies; USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../Libraries/**"; }; @@ -376,6 +377,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "-ObjC"; + PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = Movies; USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../Libraries/**"; }; @@ -401,6 +403,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; diff --git a/Examples/Movies/Movies.xcodeproj/xcshareddata/xcschemes/Movies.xcscheme b/Examples/Movies/Movies.xcodeproj/xcshareddata/xcschemes/Movies.xcscheme index 18b45cad0..4cb2fa20a 100644 --- a/Examples/Movies/Movies.xcodeproj/xcshareddata/xcschemes/Movies.xcscheme +++ b/Examples/Movies/Movies.xcodeproj/xcshareddata/xcschemes/Movies.xcscheme @@ -1,6 +1,6 @@ + + - + - + CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - com.facebook.$(PRODUCT_NAME:rfc1034identifier) + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName @@ -33,6 +33,11 @@ 1 LSRequiresIPhoneOS + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + UILaunchStoryboardName LaunchScreen UIRequiredDeviceCapabilities @@ -47,11 +52,5 @@ UIViewControllerBasedStatusBarAppearance - NSAppTransportSecurity - - - NSAllowsArbitraryLoads - - diff --git a/Examples/Movies/MoviesApp.android.js b/Examples/Movies/MoviesApp.android.js new file mode 100644 index 000000000..5dfb57fd2 --- /dev/null +++ b/Examples/Movies/MoviesApp.android.js @@ -0,0 +1,94 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * @providesModule MoviesApp + * @flow + */ +'use strict'; + +var React = require('react-native'); +var { + AppRegistry, + BackAndroid, + Navigator, + StyleSheet, + ToolbarAndroid, + View, +} = React; + +var MovieScreen = require('./MovieScreen'); +var SearchScreen = require('./SearchScreen'); + +var _navigator; +BackAndroid.addEventListener('hardwareBackPress', () => { + if (_navigator && _navigator.getCurrentRoutes().length > 1) { + _navigator.pop(); + return true; + } + return false; +}); + +var RouteMapper = function(route, navigationOperations, onComponentRef) { + _navigator = navigationOperations; + if (route.name === 'search') { + return ( + + ); + } else if (route.name === 'movie') { + return ( + + + + + ); + } +}; + +var MoviesApp = React.createClass({ + render: function() { + var initialRoute = {name: 'search'}; + return ( + Navigator.SceneConfigs.FadeAndroid} + renderScene={RouteMapper} + /> + ); + } +}); + +var styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: 'white', + }, + toolbar: { + backgroundColor: '#a9a9a9', + height: 56, + }, +}); + +AppRegistry.registerComponent('MoviesApp', () => MoviesApp); + +module.exports = MoviesApp; diff --git a/Examples/Movies/README.md b/Examples/Movies/README.md new file mode 100644 index 000000000..e80a81fee --- /dev/null +++ b/Examples/Movies/README.md @@ -0,0 +1,42 @@ +# Movies app + +The Movies app is a demonstration of basic concepts, such as fetching data, rendering a list of data including images, and navigating between different screens. + +## Running this app + +Before running the app, make sure you ran: + + git clone https://github.com/facebook/react-native.git + cd react-native + npm install + +### Running on iOS + +Mac OS and Xcode are required. + +- Open `Examples/Movies/Movies.xcodeproj` in Xcode +- Hit the Run button + +See [Running on device](https://facebook.github.io/react-native/docs/running-on-device-ios.html) if you want to use a physical device. + +### Running on Android + +You'll need to have all the [prerequisites](https://github.com/facebook/react-native/tree/master/ReactAndroid#prerequisites) (SDK, NDK) for Building React Native installed. + +Start an Android emulator ([Genymotion](https://www.genymotion.com) is recommended). + + cd react-native + ./gradlew :Examples:Movies:android:app:installDebug + ./packager/packager.sh + +_Note: Building for the first time can take a while._ + +Open the Movies app in your emulator. + +See [Running on Device](https://facebook.github.io/react-native/docs/running-on-device-android.html) in case you want to use a physical device. + +## Built from source + +Building the app on both iOS and Android means building the React Native framework from source. This way you're running the latest native and JS code the way you see it in your clone of the github repo. + +This is different from apps created using `react-native init` which have a dependency on a specific version of React Native JS and native code, declared in a `package.json` file (and `build.gradle` for Android apps). diff --git a/Examples/Movies/SearchBar.android.js b/Examples/Movies/SearchBar.android.js new file mode 100644 index 000000000..2e12ff488 --- /dev/null +++ b/Examples/Movies/SearchBar.android.js @@ -0,0 +1,104 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * @providesModule SearchBar + * @flow + */ +'use strict'; + +var React = require('react-native'); +var { + Image, + Platform, + ProgressBarAndroid, + TextInput, + StyleSheet, + TouchableNativeFeedback, + View, +} = React; + +var IS_RIPPLE_EFFECT_SUPPORTED = Platform.Version >= 21; + +var SearchBar = React.createClass({ + render: function() { + var loadingView; + if (this.props.isLoading) { + loadingView = ( + + ); + } else { + loadingView = ; + } + var background = IS_RIPPLE_EFFECT_SUPPORTED ? + TouchableNativeFeedback.SelectableBackgroundBorderless() : + TouchableNativeFeedback.SelectableBackground(); + return ( + + this.refs.input && this.refs.input.focus()}> + + + + + + {loadingView} + + ); + } +}); + +var styles = StyleSheet.create({ + searchBar: { + flexDirection: 'row', + alignItems: 'center', + backgroundColor: '#a9a9a9', + height: 56, + }, + searchBarInput: { + flex: 1, + fontSize: 20, + fontWeight: 'bold', + color: 'white', + height: 50, + padding: 0, + backgroundColor: 'transparent' + }, + spinner: { + width: 30, + height: 30, + }, + icon: { + width: 24, + height: 24, + marginHorizontal: 8, + }, +}); + +module.exports = SearchBar; diff --git a/Examples/Movies/SearchScreen.js b/Examples/Movies/SearchScreen.js index 4fd36790b..1bc6bb906 100644 --- a/Examples/Movies/SearchScreen.js +++ b/Examples/Movies/SearchScreen.js @@ -23,7 +23,6 @@ var { ProgressBarAndroid, StyleSheet, Text, - TextInput, View, } = React; var TimerMixin = require('react-timer-mixin'); @@ -271,7 +270,7 @@ var SearchScreen = React.createClass({ style = [style, styles.rowSeparatorHide]; } return ( - + ); }, diff --git a/Examples/Movies/android/app/build.gradle b/Examples/Movies/android/app/build.gradle new file mode 100644 index 000000000..6bc71ffda --- /dev/null +++ b/Examples/Movies/android/app/build.gradle @@ -0,0 +1,31 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 23 + buildToolsVersion "23.0.1" + + defaultConfig { + applicationId "com.facebook.react.movies" + minSdkVersion 16 + targetSdkVersion 22 + versionCode 1 + versionName "1.0" + ndk { + abiFilters "armeabi-v7a", "x86" + } + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + compile 'com.android.support:appcompat-v7:23.0.1' + + // Build React Native from source + compile project(':ReactAndroid') +} diff --git a/Examples/Movies/android/app/gradle.properties b/Examples/Movies/android/app/gradle.properties new file mode 100644 index 000000000..dfbe47874 --- /dev/null +++ b/Examples/Movies/android/app/gradle.properties @@ -0,0 +1 @@ +android.useDeprecatedNdk=true diff --git a/Examples/Movies/android/app/proguard-rules.pro b/Examples/Movies/android/app/proguard-rules.pro new file mode 100644 index 000000000..a92fa177e --- /dev/null +++ b/Examples/Movies/android/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/Examples/Movies/android/app/src/main/AndroidManifest.xml b/Examples/Movies/android/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..185ffa506 --- /dev/null +++ b/Examples/Movies/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + diff --git a/Examples/Movies/android/app/src/main/java/com/facebook/react/movies/MoviesActivity.java b/Examples/Movies/android/app/src/main/java/com/facebook/react/movies/MoviesActivity.java new file mode 100644 index 000000000..3b2626601 --- /dev/null +++ b/Examples/Movies/android/app/src/main/java/com/facebook/react/movies/MoviesActivity.java @@ -0,0 +1,89 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.facebook.react.movies; + +import android.app.Activity; +import android.os.Bundle; +import android.view.KeyEvent; + +import com.facebook.react.LifecycleState; +import com.facebook.react.ReactInstanceManager; +import com.facebook.react.ReactRootView; +import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler; +import com.facebook.react.shell.MainReactPackage; + +public class MoviesActivity extends Activity implements DefaultHardwareBackBtnHandler { + + private ReactInstanceManager mReactInstanceManager; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + mReactInstanceManager = ReactInstanceManager.builder() + .setApplication(getApplication()) + .setBundleAssetName("MoviesApp.android.bundle") + .setJSMainModuleName("Examples/Movies/MoviesApp.android") + .addPackage(new MainReactPackage()) + .setUseDeveloperSupport(true) + .setInitialLifecycleState(LifecycleState.RESUMED) + .build(); + + ((ReactRootView) findViewById(R.id.react_root_view)) + .startReactApplication(mReactInstanceManager, "MoviesApp", null); + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) { + mReactInstanceManager.showDevOptionsDialog(); + return true; + } + return super.onKeyUp(keyCode, event); + } + + @Override + protected void onPause() { + super.onPause(); + + if (mReactInstanceManager != null) { + mReactInstanceManager.onPause(); + } + } + + @Override + protected void onResume() { + super.onResume(); + + if (mReactInstanceManager != null) { + mReactInstanceManager.onResume(this, this); + } + } + + @Override + public void onBackPressed() { + if (mReactInstanceManager != null) { + mReactInstanceManager.onBackPressed(); + } else { + super.onBackPressed(); + } + } + + @Override + public void invokeDefaultOnBackPressed() { + super.onBackPressed(); + } +} diff --git a/Examples/Movies/android/app/src/main/res/drawable-hdpi/android_back_white.png b/Examples/Movies/android/app/src/main/res/drawable-hdpi/android_back_white.png new file mode 100644 index 000000000..a34f0dbb8 Binary files /dev/null and b/Examples/Movies/android/app/src/main/res/drawable-hdpi/android_back_white.png differ diff --git a/Examples/Movies/android/app/src/main/res/drawable-hdpi/android_search_white.png b/Examples/Movies/android/app/src/main/res/drawable-hdpi/android_search_white.png new file mode 100755 index 000000000..861b40db0 Binary files /dev/null and b/Examples/Movies/android/app/src/main/res/drawable-hdpi/android_search_white.png differ diff --git a/Examples/Movies/android/app/src/main/res/drawable-mdpi/android_back_white.png b/Examples/Movies/android/app/src/main/res/drawable-mdpi/android_back_white.png new file mode 100644 index 000000000..63111599a Binary files /dev/null and b/Examples/Movies/android/app/src/main/res/drawable-mdpi/android_back_white.png differ diff --git a/Examples/Movies/android/app/src/main/res/drawable-mdpi/android_search_white.png b/Examples/Movies/android/app/src/main/res/drawable-mdpi/android_search_white.png new file mode 100755 index 000000000..fedd0c623 Binary files /dev/null and b/Examples/Movies/android/app/src/main/res/drawable-mdpi/android_search_white.png differ diff --git a/Examples/Movies/android/app/src/main/res/drawable-xhdpi/android_back_white.png b/Examples/Movies/android/app/src/main/res/drawable-xhdpi/android_back_white.png new file mode 100644 index 000000000..500892e10 Binary files /dev/null and b/Examples/Movies/android/app/src/main/res/drawable-xhdpi/android_back_white.png differ diff --git a/Examples/Movies/android/app/src/main/res/drawable-xhdpi/android_search_white.png b/Examples/Movies/android/app/src/main/res/drawable-xhdpi/android_search_white.png new file mode 100755 index 000000000..5713a86f6 Binary files /dev/null and b/Examples/Movies/android/app/src/main/res/drawable-xhdpi/android_search_white.png differ diff --git a/Examples/Movies/android/app/src/main/res/drawable-xxhdpi/android_back_white.png b/Examples/Movies/android/app/src/main/res/drawable-xxhdpi/android_back_white.png new file mode 100644 index 000000000..03620979e Binary files /dev/null and b/Examples/Movies/android/app/src/main/res/drawable-xxhdpi/android_back_white.png differ diff --git a/Examples/Movies/android/app/src/main/res/drawable-xxhdpi/android_search_white.png b/Examples/Movies/android/app/src/main/res/drawable-xxhdpi/android_search_white.png new file mode 100755 index 000000000..d9f75bc79 Binary files /dev/null and b/Examples/Movies/android/app/src/main/res/drawable-xxhdpi/android_search_white.png differ diff --git a/Examples/Movies/android/app/src/main/res/drawable/rotten_tomatoes_icon.png b/Examples/Movies/android/app/src/main/res/drawable/rotten_tomatoes_icon.png new file mode 100644 index 000000000..395d70431 Binary files /dev/null and b/Examples/Movies/android/app/src/main/res/drawable/rotten_tomatoes_icon.png differ diff --git a/Examples/Movies/android/app/src/main/res/layout/activity_main.xml b/Examples/Movies/android/app/src/main/res/layout/activity_main.xml new file mode 100644 index 000000000..d42c3b844 --- /dev/null +++ b/Examples/Movies/android/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,12 @@ + + + + + diff --git a/Examples/Movies/android/app/src/main/res/values/strings.xml b/Examples/Movies/android/app/src/main/res/values/strings.xml new file mode 100644 index 000000000..7c4632e0d --- /dev/null +++ b/Examples/Movies/android/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + MoviesApp + diff --git a/Examples/Movies/android/app/src/main/res/values/styles.xml b/Examples/Movies/android/app/src/main/res/values/styles.xml new file mode 100644 index 000000000..319eb0ca1 --- /dev/null +++ b/Examples/Movies/android/app/src/main/res/values/styles.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/Examples/Movies/getStyleFromScore.js b/Examples/Movies/getStyleFromScore.js index 98feaa50b..1d5b599b6 100644 --- a/Examples/Movies/getStyleFromScore.js +++ b/Examples/Movies/getStyleFromScore.js @@ -22,7 +22,9 @@ var { var MAX_VALUE = 200; -function getStyleFromScore(score: number): {color: string} { +import type { StyleObj } from 'StyleSheetTypes'; + +function getStyleFromScore(score: number): StyleObj { if (score < 0) { return styles.noScore; } diff --git a/Examples/SampleApp/.gitignore b/Examples/SampleApp/.gitignore deleted file mode 100644 index c39012e9e..000000000 --- a/Examples/SampleApp/.gitignore +++ /dev/null @@ -1,27 +0,0 @@ -# OSX -# -.DS_Store - -# Xcode -# -build/ -*.pbxuser -!default.pbxuser -*.mode1v3 -!default.mode1v3 -*.mode2v3 -!default.mode2v3 -*.perspectivev3 -!default.perspectivev3 -xcuserdata -*.xccheckout -*.moved-aside -DerivedData -*.hmap -*.ipa -*.xcuserstate - -# node.js -# -node_modules/ -npm-debug.log diff --git a/Examples/SampleApp/_flowconfig b/Examples/SampleApp/_flowconfig deleted file mode 100644 index 9ca5deb8f..000000000 --- a/Examples/SampleApp/_flowconfig +++ /dev/null @@ -1,49 +0,0 @@ -[ignore] - -# We fork some components by platform. -.*/*.web.js -.*/*.android.js - -# Some modules have their own node_modules with overlap -.*/node_modules/node-haste/.* - -# Ignore react-tools where there are overlaps, but don't ignore anything that -# react-native relies on -.*/node_modules/react-tools/src/React.js -.*/node_modules/react-tools/src/renderers/shared/event/EventPropagators.js -.*/node_modules/react-tools/src/renderers/shared/event/eventPlugins/ResponderEventPlugin.js -.*/node_modules/react-tools/src/shared/vendor/core/ExecutionEnvironment.js - - -# Ignore commoner tests -.*/node_modules/commoner/test/.* - -# See https://github.com/facebook/flow/issues/442 -.*/react-tools/node_modules/commoner/lib/reader.js - -# Ignore jest -.*/react-native/node_modules/jest-cli/.* - -[include] - -[libs] -node_modules/react-native/Libraries/react-native/react-native-interface.js - -[options] -module.system=haste - -munge_underscores=true - -module.name_mapper='^image![a-zA-Z0-9$_-]+$' -> 'GlobalImageStub' -module.name_mapper='^[./a-zA-Z0-9$_-]+\.png$' -> 'RelativeImageStub' - -suppress_type=$FlowIssue -suppress_type=$FlowFixMe -suppress_type=$FixMe - -suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(1[0-4]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) -suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(1[0-4]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)? #[0-9]+ -suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy - -[version] -0.14.0 diff --git a/Examples/SampleApp/_gitignore b/Examples/SampleApp/_gitignore deleted file mode 100644 index b927355df..000000000 --- a/Examples/SampleApp/_gitignore +++ /dev/null @@ -1,28 +0,0 @@ -# OSX -# -.DS_Store - -# Xcode -# -build/ -*.pbxuser -!default.pbxuser -*.mode1v3 -!default.mode1v3 -*.mode2v3 -!default.mode2v3 -*.perspectivev3 -!default.perspectivev3 -xcuserdata -*.xccheckout -*.moved-aside -DerivedData -*.hmap -*.ipa -*.xcuserstate -project.xcworkspace - -# node.js -# -node_modules/ -npm-debug.log diff --git a/Examples/SampleApp/_watchmanconfig b/Examples/SampleApp/_watchmanconfig deleted file mode 100644 index 0967ef424..000000000 --- a/Examples/SampleApp/_watchmanconfig +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/Examples/SampleApp/iOS/SampleApp.xcodeproj/project.pbxproj b/Examples/SampleApp/iOS/SampleApp.xcodeproj/project.pbxproj deleted file mode 100644 index e0225c7c7..000000000 --- a/Examples/SampleApp/iOS/SampleApp.xcodeproj/project.pbxproj +++ /dev/null @@ -1,754 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 008F07F31AC5B25A0029DE68 /* main.jsbundle in Resources */ = {isa = PBXBuildFile; fileRef = 008F07F21AC5B25A0029DE68 /* main.jsbundle */; }; - 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; }; - 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; }; - 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */; }; - 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; }; - 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; }; - 00E356F31AD99517003FC87E /* SampleAppTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* SampleAppTests.m */; }; - 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; }; - 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; }; - 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */; }; - 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; - 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; }; - 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; - 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; - 146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; - 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 134814201AA4EA6300B7C361; - remoteInfo = RCTActionSheet; - }; - 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 134814201AA4EA6300B7C361; - remoteInfo = RCTGeolocation; - }; - 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 58B5115D1A9E6B3D00147676; - remoteInfo = RCTImage; - }; - 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 58B511DB1A9E6C8500147676; - remoteInfo = RCTNetwork; - }; - 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 832C81801AAF6DEF007FA2F7; - remoteInfo = RCTVibration; - }; - 00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 13B07F861A680F5B00A75B9A; - remoteInfo = SampleApp; - }; - 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 134814201AA4EA6300B7C361; - remoteInfo = RCTSettings; - }; - 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 3C86DF461ADF2C930047B81A; - remoteInfo = RCTWebSocket; - }; - 146834031AC3E56700842450 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192; - remoteInfo = React; - }; - 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 134814201AA4EA6300B7C361; - remoteInfo = RCTLinking; - }; - 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 58B5119B1A9E6C1200147676; - remoteInfo = RCTText; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - 008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = ""; }; - 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = ../../../Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj; sourceTree = ""; }; - 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = ../../../Libraries/Geolocation/RCTGeolocation.xcodeproj; sourceTree = ""; }; - 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = ../../../Libraries/Image/RCTImage.xcodeproj; sourceTree = ""; }; - 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = ../../../Libraries/Network/RCTNetwork.xcodeproj; sourceTree = ""; }; - 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = ../../../Libraries/Vibration/RCTVibration.xcodeproj; sourceTree = ""; }; - 00E356EE1AD99517003FC87E /* SampleAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SampleAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 00E356F21AD99517003FC87E /* SampleAppTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SampleAppTests.m; sourceTree = ""; }; - 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = ../../../Libraries/Settings/RCTSettings.xcodeproj; sourceTree = ""; }; - 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = ../../../Libraries/WebSocket/RCTWebSocket.xcodeproj; sourceTree = ""; }; - 13B07F961A680F5B00A75B9A /* SampleApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SampleApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; - 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; - 13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; - 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; - 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - 146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = ../../../React/React.xcodeproj; sourceTree = ""; }; - 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = ../../../Libraries/LinkingIOS/RCTLinking.xcodeproj; sourceTree = ""; }; - 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = ../../../Libraries/Text/RCTText.xcodeproj; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 00E356EB1AD99517003FC87E /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 146834051AC3E58100842450 /* libReact.a in Frameworks */, - 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */, - 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */, - 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */, - 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */, - 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */, - 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */, - 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */, - 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */, - 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 00C302A81ABCB8CE00DB3ED1 /* Products */ = { - isa = PBXGroup; - children = ( - 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */, - ); - name = Products; - sourceTree = ""; - }; - 00C302B61ABCB90400DB3ED1 /* Products */ = { - isa = PBXGroup; - children = ( - 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */, - ); - name = Products; - sourceTree = ""; - }; - 00C302BC1ABCB91800DB3ED1 /* Products */ = { - isa = PBXGroup; - children = ( - 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */, - ); - name = Products; - sourceTree = ""; - }; - 00C302D41ABCB9D200DB3ED1 /* Products */ = { - isa = PBXGroup; - children = ( - 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */, - ); - name = Products; - sourceTree = ""; - }; - 00C302E01ABCB9EE00DB3ED1 /* Products */ = { - isa = PBXGroup; - children = ( - 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */, - ); - name = Products; - sourceTree = ""; - }; - 00E356EF1AD99517003FC87E /* SampleAppTests */ = { - isa = PBXGroup; - children = ( - 00E356F21AD99517003FC87E /* SampleAppTests.m */, - 00E356F01AD99517003FC87E /* Supporting Files */, - ); - path = SampleAppTests; - sourceTree = ""; - }; - 00E356F01AD99517003FC87E /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 00E356F11AD99517003FC87E /* Info.plist */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - 139105B71AF99BAD00B5F7CC /* Products */ = { - isa = PBXGroup; - children = ( - 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */, - ); - name = Products; - sourceTree = ""; - }; - 139FDEE71B06529A00C62182 /* Products */ = { - isa = PBXGroup; - children = ( - 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */, - ); - name = Products; - sourceTree = ""; - }; - 13B07FAE1A68108700A75B9A /* SampleApp */ = { - isa = PBXGroup; - children = ( - 13B07FAF1A68108700A75B9A /* AppDelegate.h */, - 13B07FB01A68108700A75B9A /* AppDelegate.m */, - 13B07FB51A68108700A75B9A /* Images.xcassets */, - 13B07FB61A68108700A75B9A /* Info.plist */, - 13B07FB11A68108700A75B9A /* LaunchScreen.xib */, - 008F07F21AC5B25A0029DE68 /* main.jsbundle */, - 13B07FB71A68108700A75B9A /* main.m */, - ); - path = SampleApp; - sourceTree = ""; - }; - 146834001AC3E56700842450 /* Products */ = { - isa = PBXGroup; - children = ( - 146834041AC3E56700842450 /* libReact.a */, - ); - name = Products; - sourceTree = ""; - }; - 78C398B11ACF4ADC00677621 /* Products */ = { - isa = PBXGroup; - children = ( - 78C398B91ACF4ADC00677621 /* libRCTLinking.a */, - ); - name = Products; - sourceTree = ""; - }; - 832341AE1AAA6A7D00B99B32 /* Libraries */ = { - isa = PBXGroup; - children = ( - 146833FF1AC3E56700842450 /* React.xcodeproj */, - 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */, - 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */, - 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */, - 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */, - 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */, - 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */, - 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */, - 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */, - 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */, - ); - name = Libraries; - sourceTree = ""; - }; - 832341B11AAA6A8300B99B32 /* Products */ = { - isa = PBXGroup; - children = ( - 832341B51AAA6A8300B99B32 /* libRCTText.a */, - ); - name = Products; - sourceTree = ""; - }; - 83CBB9F61A601CBA00E9B192 = { - isa = PBXGroup; - children = ( - 13B07FAE1A68108700A75B9A /* SampleApp */, - 00E356EF1AD99517003FC87E /* SampleAppTests */, - 832341AE1AAA6A7D00B99B32 /* Libraries */, - 83CBBA001A601CBA00E9B192 /* Products */, - ); - indentWidth = 2; - sourceTree = ""; - tabWidth = 2; - }; - 83CBBA001A601CBA00E9B192 /* Products */ = { - isa = PBXGroup; - children = ( - 13B07F961A680F5B00A75B9A /* SampleApp.app */, - 00E356EE1AD99517003FC87E /* SampleAppTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 00E356ED1AD99517003FC87E /* SampleAppTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "SampleAppTests" */; - buildPhases = ( - 00E356EA1AD99517003FC87E /* Sources */, - 00E356EB1AD99517003FC87E /* Frameworks */, - 00E356EC1AD99517003FC87E /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 00E356F51AD99517003FC87E /* PBXTargetDependency */, - ); - name = SampleAppTests; - productName = SampleAppTests; - productReference = 00E356EE1AD99517003FC87E /* SampleAppTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - 13B07F861A680F5B00A75B9A /* SampleApp */ = { - isa = PBXNativeTarget; - buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "SampleApp" */; - buildPhases = ( - 13B07F871A680F5B00A75B9A /* Sources */, - 13B07F8C1A680F5B00A75B9A /* Frameworks */, - 13B07F8E1A680F5B00A75B9A /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = SampleApp; - productName = "Hello World"; - productReference = 13B07F961A680F5B00A75B9A /* SampleApp.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 83CBB9F71A601CBA00E9B192 /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 0610; - ORGANIZATIONNAME = Facebook; - TargetAttributes = { - 00E356ED1AD99517003FC87E = { - CreatedOnToolsVersion = 6.2; - TestTargetID = 13B07F861A680F5B00A75B9A; - }; - }; - }; - buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "SampleApp" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 83CBB9F61A601CBA00E9B192; - productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; - projectDirPath = ""; - projectReferences = ( - { - ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */; - ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; - }, - { - ProductGroup = 00C302B61ABCB90400DB3ED1 /* Products */; - ProjectRef = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; - }, - { - ProductGroup = 00C302BC1ABCB91800DB3ED1 /* Products */; - ProjectRef = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; - }, - { - ProductGroup = 78C398B11ACF4ADC00677621 /* Products */; - ProjectRef = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; - }, - { - ProductGroup = 00C302D41ABCB9D200DB3ED1 /* Products */; - ProjectRef = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; - }, - { - ProductGroup = 139105B71AF99BAD00B5F7CC /* Products */; - ProjectRef = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; - }, - { - ProductGroup = 832341B11AAA6A8300B99B32 /* Products */; - ProjectRef = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; - }, - { - ProductGroup = 00C302E01ABCB9EE00DB3ED1 /* Products */; - ProjectRef = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; - }, - { - ProductGroup = 139FDEE71B06529A00C62182 /* Products */; - ProjectRef = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; - }, - { - ProductGroup = 146834001AC3E56700842450 /* Products */; - ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */; - }, - ); - projectRoot = ""; - targets = ( - 13B07F861A680F5B00A75B9A /* SampleApp */, - 00E356ED1AD99517003FC87E /* SampleAppTests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXReferenceProxy section */ - 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTActionSheet.a; - remoteRef = 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTGeolocation.a; - remoteRef = 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTImage.a; - remoteRef = 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTNetwork.a; - remoteRef = 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTVibration.a; - remoteRef = 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTSettings.a; - remoteRef = 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTWebSocket.a; - remoteRef = 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 146834041AC3E56700842450 /* libReact.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libReact.a; - remoteRef = 146834031AC3E56700842450 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 78C398B91ACF4ADC00677621 /* libRCTLinking.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTLinking.a; - remoteRef = 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 832341B51AAA6A8300B99B32 /* libRCTText.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTText.a; - remoteRef = 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; -/* End PBXReferenceProxy section */ - -/* Begin PBXResourcesBuildPhase section */ - 00E356EC1AD99517003FC87E /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 13B07F8E1A680F5B00A75B9A /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 008F07F31AC5B25A0029DE68 /* main.jsbundle in Resources */, - 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, - 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 00E356EA1AD99517003FC87E /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 00E356F31AD99517003FC87E /* SampleAppTests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 13B07F871A680F5B00A75B9A /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */, - 13B07FC11A68108700A75B9A /* main.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 00E356F51AD99517003FC87E /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 13B07F861A680F5B00A75B9A /* SampleApp */; - targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = { - isa = PBXVariantGroup; - children = ( - 13B07FB21A68108700A75B9A /* Base */, - ); - name = LaunchScreen.xib; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 00E356F61AD99517003FC87E /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - HEADER_SEARCH_PATHS = ( - "$(inherited)", - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - "$(SRCROOT)/../../../React/**", - ); - INFOPLIST_FILE = SampleAppTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.2; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SampleApp.app/SampleApp"; - }; - name = Debug; - }; - 00E356F71AD99517003FC87E /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - COPY_PHASE_STRIP = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); - HEADER_SEARCH_PATHS = ( - "$(inherited)", - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - "$(SRCROOT)/../../../React/**", - ); - INFOPLIST_FILE = SampleAppTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.2; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SampleApp.app/SampleApp"; - }; - name = Release; - }; - 13B07F941A680F5B00A75B9A /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - "$(SRCROOT)/../../../React/**", - ); - INFOPLIST_FILE = "$(SRCROOT)/SampleApp/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - OTHER_LDFLAGS = "-ObjC"; - PRODUCT_NAME = SampleApp; - }; - name = Debug; - }; - 13B07F951A680F5B00A75B9A /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - "$(SRCROOT)/../../../React/**", - ); - INFOPLIST_FILE = "$(SRCROOT)/SampleApp/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - OTHER_LDFLAGS = "-ObjC"; - PRODUCT_NAME = SampleApp; - }; - name = Release; - }; - 83CBBA201A601CBA00E9B192 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - "$(SRCROOT)/../../React/**", - ); - IPHONEOS_DEPLOYMENT_TARGET = 7.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - }; - name = Debug; - }; - 83CBBA211A601CBA00E9B192 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = YES; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - "$(SRCROOT)/../../React/**", - ); - IPHONEOS_DEPLOYMENT_TARGET = 7.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "SampleAppTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 00E356F61AD99517003FC87E /* Debug */, - 00E356F71AD99517003FC87E /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "SampleApp" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 13B07F941A680F5B00A75B9A /* Debug */, - 13B07F951A680F5B00A75B9A /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "SampleApp" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 83CBBA201A601CBA00E9B192 /* Debug */, - 83CBBA211A601CBA00E9B192 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; -} diff --git a/Examples/SampleApp/iOS/SampleApp.xcodeproj/xcshareddata/xcschemes/SampleApp.xcscheme b/Examples/SampleApp/iOS/SampleApp.xcodeproj/xcshareddata/xcschemes/SampleApp.xcscheme deleted file mode 100644 index 6a3c2997a..000000000 --- a/Examples/SampleApp/iOS/SampleApp.xcodeproj/xcshareddata/xcschemes/SampleApp.xcscheme +++ /dev/null @@ -1,112 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Examples/SampleApp/iOS/SampleApp/AppDelegate.m b/Examples/SampleApp/iOS/SampleApp/AppDelegate.m deleted file mode 100644 index b1c018ca5..000000000 --- a/Examples/SampleApp/iOS/SampleApp/AppDelegate.m +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -#import "AppDelegate.h" - -#import "RCTRootView.h" - -@implementation AppDelegate - -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions -{ - NSURL *jsCodeLocation; - - /** - * Loading JavaScript code - uncomment the one you want. - * - * OPTION 1 - * Load from development server. Start the server from the repository root: - * - * $ npm start - * - * To run on device, change `localhost` to the IP address of your computer - * (you can get this by typing `ifconfig` into the terminal and selecting the - * `inet` value under `en0:`) and make sure your computer and iOS device are - * on the same Wi-Fi network. - */ - - jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/SampleApp/index.ios.bundle"]; - - /** - * OPTION 2 - * Load from pre-bundled file on disk. To re-generate the static bundle - * from the root of your project directory, run - * - * $ react-native bundle --minify - * - * see http://facebook.github.io/react-native/docs/runningondevice.html - */ - -// jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; - - RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation - moduleName:@"SampleApp" - initialProperties:nil - launchOptions:launchOptions]; - - self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; - UIViewController *rootViewController = [UIViewController new]; - rootViewController.view = rootView; - self.window.rootViewController = rootViewController; - [self.window makeKeyAndVisible]; - return YES; -} - -@end diff --git a/Examples/SampleApp/iOS/SampleApp/Base.lproj/LaunchScreen.xib b/Examples/SampleApp/iOS/SampleApp/Base.lproj/LaunchScreen.xib deleted file mode 100644 index 73cc9d07c..000000000 --- a/Examples/SampleApp/iOS/SampleApp/Base.lproj/LaunchScreen.xib +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Examples/SampleApp/iOS/SampleApp/Images.xcassets/AppIcon.appiconset/Contents.json b/Examples/SampleApp/iOS/SampleApp/Images.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 118c98f74..000000000 --- a/Examples/SampleApp/iOS/SampleApp/Images.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "images" : [ - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Examples/SampleApp/iOS/SampleApp/Info.plist b/Examples/SampleApp/iOS/SampleApp/Info.plist deleted file mode 100644 index cddf0766c..000000000 --- a/Examples/SampleApp/iOS/SampleApp/Info.plist +++ /dev/null @@ -1,48 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - NSLocationWhenInUseUsageDescription - - NSAppTransportSecurity - - - NSAllowsArbitraryLoads - - - - diff --git a/Examples/SampleApp/iOS/SampleApp/main.jsbundle b/Examples/SampleApp/iOS/SampleApp/main.jsbundle deleted file mode 100644 index b702b30c6..000000000 --- a/Examples/SampleApp/iOS/SampleApp/main.jsbundle +++ /dev/null @@ -1,8 +0,0 @@ -// Offline JS -// To re-generate the offline bundle, run this from the root of your project: -// -// $ react-native bundle --minify -// -// See http://facebook.github.io/react-native/docs/runningondevice.html for more details. - -throw new Error('Offline JS file is empty. See iOS/main.jsbundle for instructions'); diff --git a/Examples/SampleApp/iOS/SampleAppTests/Info.plist b/Examples/SampleApp/iOS/SampleAppTests/Info.plist deleted file mode 100644 index 886825ccc..000000000 --- a/Examples/SampleApp/iOS/SampleAppTests/Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - - diff --git a/Examples/SampleApp/iOS/SampleAppTests/SampleAppTests.m b/Examples/SampleApp/iOS/SampleAppTests/SampleAppTests.m deleted file mode 100644 index 14b9f0fbf..000000000 --- a/Examples/SampleApp/iOS/SampleAppTests/SampleAppTests.m +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -#import -#import - -#import "RCTLog.h" -#import "RCTRootView.h" - -#define TIMEOUT_SECONDS 240 -#define TEXT_TO_LOOK_FOR @"Welcome to React Native!" - -@interface SampleAppTests : XCTestCase - -@end - -@implementation SampleAppTests - -- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test -{ - if (test(view)) { - return YES; - } - for (UIView *subview in [view subviews]) { - if ([self findSubviewInView:subview matching:test]) { - return YES; - } - } - return NO; -} - -- (void)testRendersWelcomeScreen -{ - UIViewController *vc = [[[[UIApplication sharedApplication] delegate] window] rootViewController]; - NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; - BOOL foundElement = NO; - - __block NSString *redboxError = nil; - RCTSetLogFunction(^(RCTLogLevel level, NSString *fileName, NSNumber *lineNumber, NSString *message) { - if (level >= RCTLogLevelError) { - redboxError = message; - } - }); - - while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { - [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; - [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; - - foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { - if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { - return YES; - } - return NO; - }]; - } - - RCTSetLogFunction(RCTDefaultLogFunction); - - XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); - XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); -} - - -@end diff --git a/Examples/TicTacToe/TicTacToe/AppDelegate.m b/Examples/TicTacToe/TicTacToe/AppDelegate.m index aa746ba69..4cca2217d 100644 --- a/Examples/TicTacToe/TicTacToe/AppDelegate.m +++ b/Examples/TicTacToe/TicTacToe/AppDelegate.m @@ -36,7 +36,7 @@ * on the same Wi-Fi network. */ - jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/TicTacToe/TicTacToeApp.bundle?platform=ios"]; + jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/TicTacToe/TicTacToeApp.bundle?platform=ios&dev=true"]; /** * OPTION 2 @@ -56,7 +56,7 @@ launchOptions:launchOptions]; self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; - UIViewController *rootViewController = [[UIViewController alloc] init]; + UIViewController *rootViewController = [UIViewController new]; rootViewController.view = rootView; self.window.rootViewController = rootViewController; [self.window makeKeyAndVisible]; diff --git a/Examples/TicTacToe/TicTacToeApp.js b/Examples/TicTacToe/TicTacToeApp.js index 15652ee4d..6d5c460cb 100755 --- a/Examples/TicTacToe/TicTacToeApp.js +++ b/Examples/TicTacToe/TicTacToeApp.js @@ -89,7 +89,6 @@ class Board { } } } - return this.winner() === null; } } diff --git a/Examples/UIExplorer/AccessibilityAndroidExample.js b/Examples/UIExplorer/AccessibilityAndroidExample.android.js similarity index 81% rename from Examples/UIExplorer/AccessibilityAndroidExample.js rename to Examples/UIExplorer/AccessibilityAndroidExample.android.js index d75907a86..3df94c603 100644 --- a/Examples/UIExplorer/AccessibilityAndroidExample.js +++ b/Examples/UIExplorer/AccessibilityAndroidExample.android.js @@ -19,10 +19,9 @@ var { StyleSheet, Text, View, + ToastAndroid, TouchableWithoutFeedback, } = React; -var ToastAndroid = require('ToastAndroid'); -var AccessibilityInfo = require('AccessibilityInfo'); var UIExplorerBlock = require('./UIExplorerBlock'); var UIExplorerPage = require('./UIExplorerPage'); @@ -39,47 +38,14 @@ var AccessibilityAndroidExample = React.createClass({ getInitialState: function() { return { count: 0, - talkbackEnabled: false, backgroundImportantForAcc: 0, forgroundImportantForAcc: 0, }; }, - componentDidMount: function() { - AccessibilityInfo.addEventListener( - 'change', - this._handleTouchExplorationChange - ); - AccessibilityInfo.fetch().done((enabled) => { - this.setState({ - count: this.state.count, - talkbackEnabled: enabled}); } - ); - }, - - componentWillUnmount: function() { - AccessibilityInfo.removeEventListener( - 'change', - this._handleTouchExplorationChange - ); - }, - - _handleTouchExplorationChange: function(isEnabled) { - this.setState({ - count: this.state.count, - talkbackEnabled: isEnabled, - }); - }, - - _showAccessibilityToast: function() { - var text = 'TouchExploration is ' + (this.state.talkbackEnabled ? 'enabled' : 'disabled'); - ToastAndroid.show(text, ToastAndroid.SHORT); - }, - _addOne: function() { this.setState({ count: ++this.state.count, - talkbackEnabled: this.state.talkbackEnabled, }); }, @@ -135,8 +101,9 @@ var AccessibilityAndroidExample = React.createClass({ ToastAndroid.show('Toasts work by default', ToastAndroid.SHORT)}> - + onPress={() => ToastAndroid.show('Toasts work by default', ToastAndroid.SHORT)} + accessibilityComponentType="button"> + Click me Or not @@ -145,7 +112,7 @@ var AccessibilityAndroidExample = React.createClass({ - + Click me @@ -154,16 +121,6 @@ var AccessibilityAndroidExample = React.createClass({ - - - - - Click to check TouchExploration - - - - - - Accessibility traits example diff --git a/Examples/UIExplorer/ActionSheetIOSExample.js b/Examples/UIExplorer/ActionSheetIOSExample.js index 5c6ba8f96..ccc86fba4 100644 --- a/Examples/UIExplorer/ActionSheetIOSExample.js +++ b/Examples/UIExplorer/ActionSheetIOSExample.js @@ -24,10 +24,10 @@ var { } = React; var BUTTONS = [ - 'Button Index: 0', - 'Button Index: 1', - 'Button Index: 2', - 'Destruct', + 'Option 0', + 'Option 1', + 'Option 2', + 'Delete', 'Cancel', ]; var DESTRUCTIVE_INDEX = 3; @@ -47,7 +47,7 @@ var ActionSheetExample = React.createClass({ Click to show the ActionSheet - Clicked button at index: "{this.state.clicked}" + Clicked button: {this.state.clicked} ); @@ -65,6 +65,40 @@ var ActionSheetExample = React.createClass({ } }); +var ActionSheetTintExample = React.createClass({ + getInitialState() { + return { + clicked: 'none', + }; + }, + + render() { + return ( + + + Click to show the ActionSheet + + + Clicked button: {this.state.clicked} + + + ); + }, + + showActionSheet() { + ActionSheetIOS.showActionSheetWithOptions({ + options: BUTTONS, + cancelButtonIndex: CANCEL_INDEX, + destructiveButtonIndex: DESTRUCTIVE_INDEX, + tintColor: 'green', + }, + (buttonIndex) => { + this.setState({ clicked: BUTTONS[buttonIndex] }); + }); + } +}); + + var ShareActionSheetExample = React.createClass({ getInitialState() { return { @@ -88,6 +122,11 @@ var ShareActionSheetExample = React.createClass({ showShareActionSheet() { ActionSheetIOS.showShareActionSheetWithOptions({ url: 'https://code.facebook.com', + message: 'message to go with the shared url', + subject: 'a subject to go in the email heading', + excludedActivityTypes: [ + 'com.apple.UIKit.activity.PostToTwitter' + ] }, (error) => { console.error(error); @@ -99,7 +138,7 @@ var ShareActionSheetExample = React.createClass({ } else { text = 'You didn\'t share'; } - this.setState({text}) + this.setState({text}); }); } }); @@ -118,6 +157,10 @@ exports.examples = [ title: 'Show Action Sheet', render(): ReactElement { return ; } }, + { + title: 'Show Action Sheet with tinted buttons', + render(): ReactElement { return ; } + }, { title: 'Show Share Action Sheet', render(): ReactElement { return ; } diff --git a/Examples/UIExplorer/AlertExample.js b/Examples/UIExplorer/AlertExample.js new file mode 100644 index 000000000..37f47dd9b --- /dev/null +++ b/Examples/UIExplorer/AlertExample.js @@ -0,0 +1,136 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +'use strict'; + +var React = require('react-native'); +var { + Alert, + StyleSheet, + Text, + TouchableHighlight, + View, +} = React; + +var UIExplorerBlock = require('./UIExplorerBlock'); + +// corporate ipsum > lorem ipsum +var alertMessage = 'Credibly reintermediate next-generation potentialities after goal-oriented ' + + 'catalysts for change. Dynamically revolutionize.'; + +/** + * Simple alert examples. + */ +var SimpleAlertExampleBlock = React.createClass({ + + render: function() { + return ( + + Alert.alert( + 'Alert Title', + alertMessage, + )}> + + Alert with message and default button + + + Alert.alert( + 'Alert Title', + alertMessage, + [ + {text: 'OK', onPress: () => console.log('OK Pressed!')}, + ] + )}> + + Alert with one button + + + Alert.alert( + 'Alert Title', + alertMessage, + [ + {text: 'Cancel', onPress: () => console.log('Cancel Pressed!')}, + {text: 'OK', onPress: () => console.log('OK Pressed!')}, + ] + )}> + + Alert with two buttons + + + Alert.alert( + 'Alert Title', + null, + [ + {text: 'Foo', onPress: () => console.log('Foo Pressed!')}, + {text: 'Bar', onPress: () => console.log('Bar Pressed!')}, + {text: 'Baz', onPress: () => console.log('Baz Pressed!')}, + ] + )}> + + Alert with three buttons + + + Alert.alert( + 'Foo Title', + alertMessage, + '..............'.split('').map((dot, index) => ({ + text: 'Button ' + index, + onPress: () => console.log('Pressed ' + index) + })) + )}> + + Alert with too many buttons + + + + ); + }, +}); + +var AlertExample = React.createClass({ + statics: { + title: 'Alert', + description: 'Alerts display a concise and informative message ' + + 'and prompt the user to make a decision.', + }, + render: function() { + return ( + + + + ); + } +}); + +var styles = StyleSheet.create({ + wrapper: { + borderRadius: 5, + marginBottom: 5, + }, + button: { + backgroundColor: '#eeeeee', + padding: 10, + }, +}); + +module.exports = { + AlertExample, + SimpleAlertExampleBlock, +}; diff --git a/Examples/UIExplorer/AlertIOSExample.js b/Examples/UIExplorer/AlertIOSExample.js index 2d57fd5fe..a52c1ab79 100644 --- a/Examples/UIExplorer/AlertIOSExample.js +++ b/Examples/UIExplorer/AlertIOSExample.js @@ -24,103 +24,83 @@ var { AlertIOS, } = React; +var { SimpleAlertExampleBlock } = require('./AlertExample'); + exports.framework = 'React'; exports.title = 'AlertIOS'; exports.description = 'iOS alerts and action sheets'; exports.examples = [{ title: 'Alerts', + render() { + return ; + } +}, +{ + title: 'Prompt Options', + render(): React.Component { + return ; + } +}, +{ + title: 'Prompt Types', render() { return ( - AlertIOS.alert( - 'Foo Title', - 'My Alert Msg' - )}> + AlertIOS.prompt('Plain Text Entry')}> + - Alert with message and default button + + plain-text + + - AlertIOS.alert( - null, - null, - [ - {text: 'Button', onPress: () => console.log('Button Pressed!')}, - ] - )}> + AlertIOS.prompt('Secure Text', null, null, 'secure-text')}> + - Alert with only one button + + secure-text + + - AlertIOS.alert( - 'Foo Title', - 'My Alert Msg', - [ - {text: 'Foo', onPress: () => console.log('Foo Pressed!')}, - {text: 'Bar', onPress: () => console.log('Bar Pressed!')}, - ] - )}> + AlertIOS.prompt('Login & Password', null, null, 'login-password')}> + - Alert with two buttons - - - AlertIOS.alert( - 'Foo Title', - null, - [ - {text: 'Foo', onPress: () => console.log('Foo Pressed!')}, - {text: 'Bar', onPress: () => console.log('Bar Pressed!')}, - {text: 'Baz', onPress: () => console.log('Baz Pressed!')}, - ] - )}> - - Alert with 3 buttons - - - AlertIOS.alert( - 'Foo Title', - 'My Alert Msg', - '..............'.split('').map((dot, index) => ({ - text: 'Button ' + index, - onPress: () => console.log('Pressed ' + index) - })) - )}> - - Alert with too many buttons + + login-password + + ); } -}, -{ - title: 'Prompt', - render(): React.Component { - return - } }]; -class PromptExample extends React.Component { +class PromptOptions extends React.Component { constructor(props) { super(props); - this.promptResponse = this.promptResponse.bind(this); + this.saveResponse = this.saveResponse.bind(this); + + this.customButtons = [{ + text: 'Custom OK', + onPress: this.saveResponse + }, { + text: 'Custom Cancel', + style: 'cancel', + }]; + this.state = { promptValue: undefined, }; - - this.title = 'Type a value'; - this.defaultValue = 'Default value'; - this.buttons = [{ - text: 'Custom cancel', - }, { - text: 'Custom OK', - onPress: this.promptResponse - }]; } render() { @@ -132,7 +112,7 @@ class PromptExample extends React.Component { + onPress={() => AlertIOS.prompt('Type a value', null, this.saveResponse)}> @@ -143,7 +123,7 @@ class PromptExample extends React.Component { + onPress={() => AlertIOS.prompt('Type a value', null, this.customButtons)}> @@ -154,22 +134,22 @@ class PromptExample extends React.Component { + onPress={() => AlertIOS.prompt('Type a value', null, this.saveResponse, undefined, 'Default value')}> - prompt with title, default value & callback + prompt with title, callback & default value + onPress={() => AlertIOS.prompt('Type a value', null, this.customButtons, 'login-password', 'admin@site.com')}> - prompt with title, default value & custom buttons + prompt with title, custom buttons, login/password & default value @@ -177,13 +157,8 @@ class PromptExample extends React.Component { ); } - prompt() { - // Flow's apply support is broken: #7035621 - ((AlertIOS.prompt: any).apply: any)(AlertIOS, arguments); - } - - promptResponse(promptValue) { - this.setState({ promptValue }); + saveResponse(promptValue) { + this.setState({ promptValue: JSON.stringify(promptValue) }); } } diff --git a/Examples/UIExplorer/AnimatedExample.js b/Examples/UIExplorer/AnimatedExample.js new file mode 100644 index 000000000..5f6096338 --- /dev/null +++ b/Examples/UIExplorer/AnimatedExample.js @@ -0,0 +1,231 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * @flow + */ +'use strict'; + +var React = require('react-native'); +var { + Animated, + Easing, + StyleSheet, + Text, + View, +} = React; +var UIExplorerButton = require('./UIExplorerButton'); + +exports.framework = 'React'; +exports.title = 'Animated - Examples'; +exports.description = 'Animated provides a powerful ' + + 'and easy-to-use API for building modern, ' + + 'interactive user experiences.'; + +exports.examples = [ + { + title: 'FadeInView', + description: 'Uses a simple timing animation to ' + + 'bring opacity from 0 to 1 when the component ' + + 'mounts.', + render: function() { + class FadeInView extends React.Component { + constructor(props) { + super(props); + this.state = { + fadeAnim: new Animated.Value(0), // opacity 0 + }; + } + componentDidMount() { + Animated.timing( // Uses easing functions + this.state.fadeAnim, // The value to drive + { + toValue: 1, // Target + duration: 2000, // Configuration + }, + ).start(); // Don't forget start! + } + render() { + return ( + + {this.props.children} + + ); + } + } + class FadeInExample extends React.Component { + constructor(props) { + super(props); + this.state = { + show: true, + }; + } + render() { + return ( + + { + this.setState((state) => ( + {show: !state.show} + )); + }}> + Press to {this.state.show ? + 'Hide' : 'Show'} + + {this.state.show && + + FadeInView + + } + + ); + } + } + return ; + }, + }, + { + title: 'Transform Bounce', + description: 'One `Animated.Value` is driven by a ' + + 'spring with custom constants and mapped to an ' + + 'ordered set of transforms. Each transform has ' + + 'an interpolation to convert the value into the ' + + 'right range and units.', + render: function() { + this.anim = this.anim || new Animated.Value(0); + return ( + + { + Animated.spring(this.anim, { + toValue: 0, // Returns to the start + velocity: 3, // Velocity makes it move + tension: -10, // Slow + friction: 1, // Oscillate a lot + }).start(); }}> + Press to Fling it! + + + Transforms! + + + ); + }, + }, + { + title: 'Composite Animations with Easing', + description: 'Sequence, parallel, delay, and ' + + 'stagger with different easing functions.', + render: function() { + this.anims = this.anims || [1,2,3].map( + () => new Animated.Value(0) + ); + return ( + + { + var timing = Animated.timing; + Animated.sequence([ // One after the other + timing(this.anims[0], { + toValue: 200, + easing: Easing.linear, + }), + Animated.delay(400), // Use with sequence + timing(this.anims[0], { + toValue: 0, + easing: Easing.elastic(2), // Springy + }), + Animated.delay(400), + Animated.stagger(200, + this.anims.map((anim) => timing( + anim, {toValue: 200} + )).concat( + this.anims.map((anim) => timing( + anim, {toValue: 0} + ))), + ), + Animated.delay(400), + Animated.parallel([ + Easing.inOut(Easing.quad), // Symmetric + Easing.back(1.5), // Goes backwards first + Easing.ease // Default bezier + ].map((easing, ii) => ( + timing(this.anims[ii], { + toValue: 320, easing, duration: 3000, + }) + ))), + Animated.delay(400), + Animated.stagger(200, + this.anims.map((anim) => timing(anim, { + toValue: 0, + easing: Easing.bounce, // Like a ball + duration: 2000, + })), + ), + ]).start(); }}> + Press to Animate + + {['Composite', 'Easing', 'Animations!'].map( + (text, ii) => ( + + {text} + + ) + )} + + ); + }, + }, + { + title: 'Continuous Interactions', + description: 'Gesture events, chaining, 2D ' + + 'values, interrupting and transitioning ' + + 'animations, etc.', + render: () => ( + Checkout the Gratuitous Animation App! + ), + } +]; + +var styles = StyleSheet.create({ + content: { + backgroundColor: 'deepskyblue', + borderWidth: 1, + borderColor: 'dodgerblue', + padding: 20, + margin: 20, + borderRadius: 10, + alignItems: 'center', + }, +}); diff --git a/Examples/UIExplorer/AnimationExample/AnExApp.js b/Examples/UIExplorer/AnimatedGratuitousApp/AnExApp.js similarity index 100% rename from Examples/UIExplorer/AnimationExample/AnExApp.js rename to Examples/UIExplorer/AnimatedGratuitousApp/AnExApp.js diff --git a/Examples/UIExplorer/AnimationExample/AnExBobble.js b/Examples/UIExplorer/AnimatedGratuitousApp/AnExBobble.js similarity index 99% rename from Examples/UIExplorer/AnimationExample/AnExBobble.js rename to Examples/UIExplorer/AnimatedGratuitousApp/AnExBobble.js index bebad819d..d3e603738 100644 --- a/Examples/UIExplorer/AnimationExample/AnExBobble.js +++ b/Examples/UIExplorer/AnimatedGratuitousApp/AnExBobble.js @@ -95,6 +95,7 @@ class AnExBobble extends React.Component { return ( + {this.state.appState} + + ); + } + return ( + + {JSON.stringify(this.state.previousAppStates)} + + ); + } +}); + +exports.title = 'AppState'; +exports.description = 'app background status'; +exports.examples = [ + { + title: 'AppState.currentState', + description: 'Can be null on app initialization', + render() { return {AppState.currentState}; } + }, + { + title: 'Subscribed AppState:', + description: 'This changes according to the current state, so you can only ever see it rendered as "active"', + render(): ReactElement { return ; } + }, + { + title: 'Previous states:', + render(): ReactElement { return ; } + }, +]; diff --git a/Examples/UIExplorer/AppStateIOSExample.js b/Examples/UIExplorer/AppStateIOSExample.js index c2a011ceb..9cf15fad1 100644 --- a/Examples/UIExplorer/AppStateIOSExample.js +++ b/Examples/UIExplorer/AppStateIOSExample.js @@ -40,7 +40,7 @@ var AppStateSubscription = React.createClass({ AppStateIOS.removeEventListener('memoryWarning', this._handleMemoryWarning); }, _handleMemoryWarning: function() { - this.setState({memoryWarnings: this.state.memoryWarnings + 1}) + this.setState({memoryWarnings: this.state.memoryWarnings + 1}); }, _handleAppStateChange: function(appState) { var previousAppStates = this.state.previousAppStates.slice(); @@ -92,7 +92,7 @@ exports.examples = [ }, { title: 'Memory Warnings', - description: "In the simulator, hit Shift+Command+M to simulate a memory warning.", + description: 'In the simulator, hit Shift+Command+M to simulate a memory warning.', render(): ReactElement { return ; } }, ]; diff --git a/Examples/UIExplorer/AsyncStorageExample.js b/Examples/UIExplorer/AsyncStorageExample.js index a091c0caa..a3ca76437 100644 --- a/Examples/UIExplorer/AsyncStorageExample.js +++ b/Examples/UIExplorer/AsyncStorageExample.js @@ -80,7 +80,7 @@ var BasicStorageExample = React.createClass({ {' '} Messages: - {this.state.messages.map((m) => {m})} + {this.state.messages.map((m) => {m})} ); }, diff --git a/Examples/UIExplorer/BorderExample.js b/Examples/UIExplorer/BorderExample.js index 0370e69b2..c6c22f553 100644 --- a/Examples/UIExplorer/BorderExample.js +++ b/Examples/UIExplorer/BorderExample.js @@ -26,12 +26,12 @@ var styles = StyleSheet.create({ }, border1: { borderWidth: 10, - borderColor: '#a52a2a', + borderColor: 'brown', }, borderRadius: { borderWidth: 10, borderRadius: 10, - borderColor: '#00ffff', + borderColor: 'cyan', }, border2: { borderWidth: 10, @@ -82,7 +82,7 @@ var styles = StyleSheet.create({ }, border7: { borderWidth: 10, - borderColor: 'rgba(255,0,0,0.5)', + borderColor: '#f007', borderRadius: 30, overflow: 'hidden', }, @@ -91,10 +91,17 @@ var styles = StyleSheet.create({ width: 100, height: 100 }, + border8: { + width: 60, + height: 60, + borderColor: 'black', + marginRight: 10, + backgroundColor: 'lightgrey', + }, }); exports.title = 'Border'; -exports.description = 'View borders'; +exports.description = 'Demonstrates some of the border styles available to Views.'; exports.examples = [ { title: 'Equal-Width / Same-Color', @@ -159,4 +166,18 @@ exports.examples = [ ); } }, + { + title: 'Single Borders', + description: 'top, left, bottom right', + render() { + return ( + + + + + + + ); + } + }, ]; diff --git a/Examples/UIExplorer/BoxShadowExample.js b/Examples/UIExplorer/BoxShadowExample.js new file mode 100644 index 000000000..12568c1b6 --- /dev/null +++ b/Examples/UIExplorer/BoxShadowExample.js @@ -0,0 +1,85 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +'use strict'; + +var React = require('react-native'); +var { + Image, + StyleSheet, + View +} = React; + +var styles = StyleSheet.create({ + box: { + width: 100, + height: 100, + borderWidth: 2, + }, + shadow1: { + shadowOpacity: 0.5, + shadowRadius: 3, + shadowOffset: {width: 2, height: 2}, + }, + shadow2: { + shadowOpacity: 1.0, + shadowColor: 'red', + shadowRadius: 0, + shadowOffset: {width: 3, height: 3}, + }, +}); + +exports.title = 'Box Shadow'; +exports.description = 'Demonstrates some of the shadow styles available to Views.'; +exports.examples = [ + { + title: 'Basic shadow', + description: 'shadowOpacity: 0.5, shadowOffset: {2, 2}', + render() { + return ; + } + }, + { + title: 'Colored shadow', + description: 'shadowColor: \'red\', shadowRadius: 0', + render() { + return ; + } + }, + { + title: 'Shaped shadow', + description: 'borderRadius: 50', + render() { + return ; + } + }, + { + title: 'Image shadow', + description: 'Image shadows are derived exactly from the pixels.', + render() { + return ; + } + }, + { + title: 'Child shadow', + description: 'For views without an opaque background color, shadow will be derived from the subviews.', + render() { + return + + ; + } + }, +]; diff --git a/Examples/UIExplorer/CameraRollExample.ios.js b/Examples/UIExplorer/CameraRollExample.js similarity index 72% rename from Examples/UIExplorer/CameraRollExample.ios.js rename to Examples/UIExplorer/CameraRollExample.js index d783d9d8e..d43156072 100644 --- a/Examples/UIExplorer/CameraRollExample.ios.js +++ b/Examples/UIExplorer/CameraRollExample.js @@ -15,24 +15,25 @@ */ 'use strict'; -var React = require('react-native'); -var { +const React = require('react-native'); +const { CameraRoll, Image, SliderIOS, StyleSheet, - SwitchIOS, + Switch, Text, View, TouchableOpacity } = React; -var CameraRollView = require('./CameraRollView.ios'); -var AssetScaledImageExampleView = require('./AssetScaledImageExample'); +const CameraRollView = require('./CameraRollView'); -var CAMERA_ROLL_VIEW = 'camera_roll_view'; +const AssetScaledImageExampleView = require('./AssetScaledImageExample'); -var CameraRollExample = React.createClass({ +const CAMERA_ROLL_VIEW = 'camera_roll_view'; + +const CameraRollExample = React.createClass({ getInitialState() { return { @@ -45,7 +46,7 @@ var CameraRollExample = React.createClass({ render() { return ( - {(this.state.bigImages ? 'Big' : 'Small') + ' Images'} @@ -65,22 +66,24 @@ var CameraRollExample = React.createClass({ }, loadAsset(asset){ - this.props.navigator.push({ - title: 'Camera Roll Image', - component: AssetScaledImageExampleView, - backButtonTitle: 'Back', - passProps: { asset: asset }, - }); + if (this.props.navigator) { + this.props.navigator.push({ + title: 'Camera Roll Image', + component: AssetScaledImageExampleView, + backButtonTitle: 'Back', + passProps: { asset: asset }, + }); + } }, _renderImage(asset) { - var imageSize = this.state.bigImages ? 150 : 75; - var imageStyle = [styles.image, {width: imageSize, height: imageSize}]; - var location = asset.node.location.longitude ? + const imageSize = this.state.bigImages ? 150 : 75; + const imageStyle = [styles.image, {width: imageSize, height: imageSize}]; + const location = asset.node.location.longitude ? JSON.stringify(asset.node.location) : 'Unknown location'; return ( - - + + + + Tap to put "Hello World" in the clipboard + + + {this.state.content} + + + ); + } +}); + +exports.title = 'Clipboard'; +exports.description = 'Show Clipboard contents.'; +exports.examples = [ + { + title: 'Clipboard.setString() and getString()', + render() { + return ; + } + } +]; diff --git a/Examples/UIExplorer/ExampleTypes.js b/Examples/UIExplorer/ExampleTypes.js index ac9deaadf..4513f19e2 100644 --- a/Examples/UIExplorer/ExampleTypes.js +++ b/Examples/UIExplorer/ExampleTypes.js @@ -18,6 +18,7 @@ export type Example = { title: string, + /* $FlowFixMe(>=0.16.0) */ render: () => ?ReactElement, description?: string, platform?: string; diff --git a/Examples/UIExplorer/GeolocationExample.js b/Examples/UIExplorer/GeolocationExample.js index d9dd4e842..61d7d3778 100644 --- a/Examples/UIExplorer/GeolocationExample.js +++ b/Examples/UIExplorer/GeolocationExample.js @@ -49,11 +49,15 @@ var GeolocationExample = React.createClass({ componentDidMount: function() { navigator.geolocation.getCurrentPosition( - (initialPosition) => this.setState({initialPosition}), + (position) => { + var initialPosition = JSON.stringify(position); + this.setState({initialPosition}); + }, (error) => alert(error.message), {enableHighAccuracy: true, timeout: 20000, maximumAge: 1000} ); - this.watchID = navigator.geolocation.watchPosition((lastPosition) => { + this.watchID = navigator.geolocation.watchPosition((position) => { + var lastPosition = JSON.stringify(position); this.setState({lastPosition}); }); }, @@ -67,11 +71,11 @@ var GeolocationExample = React.createClass({ Initial position: - {JSON.stringify(this.state.initialPosition)} + {this.state.initialPosition} Current position: - {JSON.stringify(this.state.lastPosition)} + {this.state.lastPosition} ); diff --git a/Examples/UIExplorer/ImageCapInsetsExample.js b/Examples/UIExplorer/ImageCapInsetsExample.js index 8e17ac928..f75f7da98 100644 --- a/Examples/UIExplorer/ImageCapInsetsExample.js +++ b/Examples/UIExplorer/ImageCapInsetsExample.js @@ -35,6 +35,7 @@ var ImageCapInsetsExample = React.createClass({ @@ -45,6 +46,7 @@ var ImageCapInsetsExample = React.createClass({ @@ -66,7 +68,6 @@ var styles = StyleSheet.create({ width: 250, height: 150, borderWidth: 1, - resizeMode: Image.resizeMode.stretch, }, text: { fontSize: 13.5, diff --git a/Examples/UIExplorer/ImageEditingExample.js b/Examples/UIExplorer/ImageEditingExample.js index affd36796..29b2dfd84 100644 --- a/Examples/UIExplorer/ImageEditingExample.js +++ b/Examples/UIExplorer/ImageEditingExample.js @@ -20,15 +20,17 @@ var React = require('react-native'); var { CameraRoll, Image, + ImageEditor, NativeModules, ScrollView, StyleSheet, Text, TouchableHighlight, + UIManager, View, } = React; -var ImageEditingManager = NativeModules.ImageEditingManager; -var RCTScrollViewConsts = NativeModules.UIManager.RCTScrollView.Constants; + +var RCTScrollViewConsts = UIManager.RCTScrollView.Constants; var PAGE_SIZE = 20; @@ -42,14 +44,16 @@ type ImageSize = { height: number; }; -type TransformData = { +type ImageCropData = { offset: ImageOffset; size: ImageSize; -} + displaySize?: ?ImageSize; + resizeMode?: ?any; +}; class SquareImageCropper extends React.Component { _isMounted: boolean; - _transformData: TransformData; + _transformData: ImageCropData; constructor(props) { super(props); @@ -166,7 +170,7 @@ class SquareImageCropper extends React.Component { } _crop() { - ImageEditingManager.cropImage( + ImageEditor.cropImage( this.state.randomPhoto.uri, this._transformData, (croppedImageURI) => this.setState({croppedImageURI}), @@ -230,7 +234,7 @@ class ImageCropper extends React.Component { var sizeRatioX = croppedImageSize.width / scaledImageSize.width; var sizeRatioY = croppedImageSize.height / scaledImageSize.height; - this.props.onTransformDataChange && this.props.onTransformDataChange({ + var cropData: ImageCropData = { offset: { x: this.props.image.width * offsetRatioX, y: this.props.image.height * offsetRatioY, @@ -239,7 +243,8 @@ class ImageCropper extends React.Component { width: this.props.image.width * sizeRatioX, height: this.props.image.height * sizeRatioY, }, - }); + }; + this.props.onTransformDataChange && this.props.onTransformDataChange(cropData); } render() { @@ -270,8 +275,8 @@ class ImageCropper extends React.Component { } exports.framework = 'React'; -exports.title = 'ImageEditingManager'; -exports.description = 'Cropping and scaling with ImageEditingManager'; +exports.title = 'ImageEditor'; +exports.description = 'Cropping and scaling with ImageEditor'; exports.examples = [{ title: 'Image Cropping', render() { diff --git a/Examples/UIExplorer/ImageExample.js b/Examples/UIExplorer/ImageExample.js index cea7b5511..60fc38dab 100644 --- a/Examples/UIExplorer/ImageExample.js +++ b/Examples/UIExplorer/ImageExample.js @@ -24,11 +24,50 @@ var { ActivityIndicatorIOS } = React; +var base64Icon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEsAAABLCAQAAACSR7JhAAADtUlEQVR4Ac3YA2Bj6QLH0XPT1Fzbtm29tW3btm3bfLZtv7e2ObZnms7d8Uw098tuetPzrxv8wiISrtVudrG2JXQZ4VOv+qUfmqCGGl1mqLhoA52oZlb0mrjsnhKpgeUNEs91Z0pd1kvihA3ULGVHiQO2narKSHKkEMulm9VgUyE60s1aWoMQUbpZOWE+kaqs4eLEjdIlZTcFZB0ndc1+lhB1lZrIuk5P2aib1NBpZaL+JaOGIt0ls47SKzLC7CqrlGF6RZ09HGoNy1lYl2aRSWL5GuzqWU1KafRdoRp0iOQEiDzgZPnG6DbldcomadViflnl/cL93tOoVbsOLVM2jylvdWjXolWX1hmfZbGR/wjypDjFLSZIRov09BgYmtUqPQPlQrPapecLgTIy0jMgPKtTeob2zWtrGH3xvjUkPCtNg/tm1rjwrMa+mdUkPd3hWbH0jArPGiU9ufCsNNWFZ40wpwn+62/66R2RUtoso1OB34tnLOcy7YB1fUdc9e0q3yru8PGM773vXsuZ5YIZX+5xmHwHGVvlrGPN6ZSiP1smOsMMde40wKv2VmwPPVXNut4sVpUreZiLBHi0qln/VQeI/LTMYXpsJtFiclUN+5HVZazim+Ky+7sAvxWnvjXrJFneVtLWLyPJu9K3cXLWeOlbMTlrIelbMDlrLenrjEQOtIF+fuI9xRp9ZBFp6+b6WT8RrxEpdK64BuvHgDk+vUy+b5hYk6zfyfs051gRoNO1usU12WWRWL73/MMEy9pMi9qIrR4ZpV16Rrvduxazmy1FSvuFXRkqTnE7m2kdb5U8xGjLw/spRr1uTov4uOgQE+0N/DvFrG/Jt7i/FzwxbA9kDanhf2w+t4V97G8lrT7wc08aA2QNUkuTfW/KimT01wdlfK4yEw030VfT0RtZbzjeMprNq8m8tnSTASrTLti64oBNdpmMQm0eEwvfPwRbUBywG5TzjPCsdwk3IeAXjQblLCoXnDVeoAz6SfJNk5TTzytCNZk/POtTSV40NwOFWzw86wNJRpubpXsn60NJFlHeqlYRbslqZm2jnEZ3qcSKgm0kTli3zZVS7y/iivZTweYXJ26Y+RTbV1zh3hYkgyFGSTKPfRVbRqWWVReaxYeSLarYv1Qqsmh1s95S7G+eEWK0f3jYKTbV6bOwepjfhtafsvUsqrQvrGC8YhmnO9cSCk3yuY984F1vesdHYhWJ5FvASlacshUsajFt2mUM9pqzvKGcyNJW0arTKN1GGGzQlH0tXwLDgQTurS8eIQAAAABJRU5ErkJggg=='; + var ImageCapInsetsExample = require('./ImageCapInsetsExample'); -var NetworkImageExample = React.createClass({ - watchID: (null: ?number), +var NetworkImageCallbackExample = React.createClass({ + getInitialState: function() { + return { + events: [], + mountTime: new Date(), + }; + }, + componentWillMount() { + this.setState({mountTime: new Date()}); + }, + + render: function() { + var { mountTime } = this.state; + + return ( + + this._loadEventFired(`✔ onLoadStart (+${new Date() - mountTime}ms)`)} + onLoad={() => this._loadEventFired(`✔ onLoad (+${new Date() - mountTime}ms)`)} + onLoadEnd={() => this._loadEventFired(`✔ onLoadEnd (+${new Date() - mountTime}ms)`)} + /> + + + {this.state.events.join('\n')} + + + ); + }, + + _loadEventFired(event) { + this.setState((state) => { + return state.events = [...state.events, event]; + }); + } +}); + +var NetworkImageExample = React.createClass({ getInitialState: function() { return { error: false, @@ -56,6 +95,38 @@ var NetworkImageExample = React.createClass({ } }); +var ImageSizeExample = React.createClass({ + getInitialState: function() { + return { + width: 0, + height: 0, + }; + }, + componentDidMount: function() { + Image.getSize(this.props.source.uri, (width, height) => { + this.setState({width, height}); + }); + }, + render: function() { + return ( + + + + Actual dimensions:{'\n'} + Width: {this.state.width}, Height: {this.state.height} + + + ); + }, +}); + exports.displayName = (undefined: ?string); exports.framework = 'React'; exports.title = ''; @@ -90,6 +161,14 @@ exports.examples = [ ); }, }, + { + title: 'Image Loading Events', + render: function() { + return ( + + ); + }, + }, { title: 'Error Handler', render: function() { @@ -124,7 +203,6 @@ exports.examples = [ ); }, - platform: 'ios', }, { title: 'Border Width', @@ -142,7 +220,6 @@ exports.examples = [ ); }, - platform: 'ios', }, { title: 'Border Radius', @@ -325,6 +402,30 @@ exports.examples = [ ); }, }, + { + title: 'Animated GIF', + render: function() { + return ( + + ); + }, + platform: 'ios', + }, + { + title: 'Base64 image', + render: function() { + return ( + + ); + }, + platform: 'ios', + }, { title: 'Cap Insets', description: @@ -337,6 +438,13 @@ exports.examples = [ }, platform: 'ios', }, + { + title: 'Image Size', + render: function() { + return ; + }, + platform: 'ios', + }, ]; var fullImage = {uri: 'http://facebook.github.io/react/img/logo_og.png'}; @@ -384,5 +492,14 @@ var styles = StyleSheet.create({ }, horizontal: { flexDirection: 'row', - } + }, + gif: { + flex: 1, + height: 200, + }, + base64: { + flex: 1, + height: 50, + resizeMode: 'contain', + }, }); diff --git a/Examples/UIExplorer/IntentAndroidExample.android.js b/Examples/UIExplorer/IntentAndroidExample.android.js new file mode 100644 index 000000000..e655bd965 --- /dev/null +++ b/Examples/UIExplorer/IntentAndroidExample.android.js @@ -0,0 +1,90 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +'use strict'; + +var React = require('react-native'); +var { + IntentAndroid, + StyleSheet, + Text, + TouchableNativeFeedback, + View, +} = React; +var UIExplorerBlock = require('./UIExplorerBlock'); + +var OpenURLButton = React.createClass({ + + propTypes: { + url: React.PropTypes.string, + }, + + handleClick: function() { + IntentAndroid.canOpenURL(this.props.url, (supported) => { + if (supported) { + IntentAndroid.openURL(this.props.url); + } else { + console.log('Don\'t know how to open URI: ' + this.props.url); + } + }); + }, + + render: function() { + return ( + + + Open {this.props.url} + + + ); + } +}); + +var IntentAndroidExample = React.createClass({ + + statics: { + title: 'IntentAndroid', + description: 'Shows how to use Android Intents to open URLs.', + }, + + render: function() { + return ( + + + + + + + ); + }, +}); + +var styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: 'white', + padding: 10, + paddingTop: 30, + }, + button: { + padding: 10, + backgroundColor: '#3B5998', + marginBottom: 10, + }, + text: { + color: 'white', + }, +}); + +module.exports = IntentAndroidExample; diff --git a/Examples/UIExplorer/LayoutEventsExample.js b/Examples/UIExplorer/LayoutEventsExample.js index 4dec90f8f..a6b0cea89 100644 --- a/Examples/UIExplorer/LayoutEventsExample.js +++ b/Examples/UIExplorer/LayoutEventsExample.js @@ -24,19 +24,30 @@ var { View, } = React; +type Layout = { + x: number; + y: number; + width: number; + height: number; +}; + type LayoutEvent = { nativeEvent: { - layout: { - x: number; - y: number; - width: number; - height: number; - }; + layout: Layout, }; }; +type State = { + containerStyle?: { width: number }, + extraText?: string, + imageLayout?: Layout, + textLayout?: Layout, + viewLayout?: Layout, + viewStyle: { margin: number }, +}; + var LayoutEventExample = React.createClass({ - getInitialState: function() { + getInitialState(): State { return { viewStyle: { margin: 20, @@ -141,7 +152,8 @@ var styles = StyleSheet.create({ }); exports.title = 'Layout Events'; -exports.description = 'Layout events can be used to measure view size and position.'; +exports.description = 'Examples that show how Layout events can be used to ' + + 'measure view size and position.'; exports.examples = [ { title: 'LayoutEventExample', diff --git a/Examples/UIExplorer/ListViewExample.js b/Examples/UIExplorer/ListViewExample.js index 4b711a0dc..c4df7c7a9 100644 --- a/Examples/UIExplorer/ListViewExample.js +++ b/Examples/UIExplorer/ListViewExample.js @@ -21,6 +21,7 @@ var { ListView, TouchableHighlight, StyleSheet, + RecyclerViewBackedScrollView, Text, View, } = React; @@ -55,6 +56,8 @@ var ListViewSimpleExample = React.createClass({ } + renderSeparator={(sectionID, rowID) => } /> ); @@ -62,9 +65,7 @@ var ListViewSimpleExample = React.createClass({ _renderRow: function(rowData: string, sectionID: number, rowID: number) { var rowHash = Math.abs(hashCode(rowData)); - var imgSource = { - uri: THUMB_URLS[rowHash % THUMB_URLS.length], - }; + var imgSource = THUMB_URLS[rowHash % THUMB_URLS.length]; return ( this._pressRow(rowID)}> @@ -74,7 +75,6 @@ var ListViewSimpleExample = React.createClass({ {rowData + ' - ' + LOREM_IPSUM.substr(0, rowHash % 301 + 10)} - ); @@ -97,7 +97,20 @@ var ListViewSimpleExample = React.createClass({ }, }); -var THUMB_URLS = ['https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-ash3/t39.1997/p128x128/851549_767334479959628_274486868_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851561_767334496626293_1958532586_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-ash3/t39.1997/p128x128/851579_767334503292959_179092627_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851589_767334513292958_1747022277_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851563_767334559959620_1193692107_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851593_767334566626286_1953955109_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851591_767334523292957_797560749_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851567_767334529959623_843148472_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851548_767334489959627_794462220_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851575_767334539959622_441598241_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-ash3/t39.1997/p128x128/851573_767334549959621_534583464_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851583_767334573292952_1519550680_n.png']; +var THUMB_URLS = [ + require('./Thumbnails/like.png'), + require('./Thumbnails/dislike.png'), + require('./Thumbnails/call.png'), + require('./Thumbnails/fist.png'), + require('./Thumbnails/bandaged.png'), + require('./Thumbnails/flowers.png'), + require('./Thumbnails/heart.png'), + require('./Thumbnails/liking.png'), + require('./Thumbnails/party.png'), + require('./Thumbnails/poke.png'), + require('./Thumbnails/superlike.png'), + require('./Thumbnails/victory.png'), + ]; var LOREM_IPSUM = 'Lorem ipsum dolor sit amet, ius ad pertinax oportere accommodare, an vix civibus corrumpit referrentur. Te nam case ludus inciderint, te mea facilisi adipiscing. Sea id integre luptatum. In tota sale consequuntur nec. Erat ocurreret mei ei. Eu paulo sapientem vulputate est, vel an accusam intellegam interesset. Nam eu stet pericula reprimique, ea vim illud modus, putant invidunt reprehendunt ne qui.'; /* eslint no-bitwise: 0 */ diff --git a/Examples/UIExplorer/ListViewGridLayoutExample.js b/Examples/UIExplorer/ListViewGridLayoutExample.js index 879b24387..ac7d56b86 100644 --- a/Examples/UIExplorer/ListViewGridLayoutExample.js +++ b/Examples/UIExplorer/ListViewGridLayoutExample.js @@ -26,18 +26,18 @@ var { } = React; var THUMB_URLS = [ - 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-ash3/t39.1997/p128x128/851549_767334479959628_274486868_n.png', - 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851561_767334496626293_1958532586_n.png', - 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-ash3/t39.1997/p128x128/851579_767334503292959_179092627_n.png', - 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851589_767334513292958_1747022277_n.png', - 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851563_767334559959620_1193692107_n.png', - 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851593_767334566626286_1953955109_n.png', - 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851591_767334523292957_797560749_n.png', - 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851567_767334529959623_843148472_n.png', - 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851548_767334489959627_794462220_n.png', - 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851575_767334539959622_441598241_n.png', - 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-ash3/t39.1997/p128x128/851573_767334549959621_534583464_n.png', - 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851583_767334573292952_1519550680_n.png', + require('./Thumbnails/like.png'), + require('./Thumbnails/dislike.png'), + require('./Thumbnails/call.png'), + require('./Thumbnails/fist.png'), + require('./Thumbnails/bandaged.png'), + require('./Thumbnails/flowers.png'), + require('./Thumbnails/heart.png'), + require('./Thumbnails/liking.png'), + require('./Thumbnails/party.png'), + require('./Thumbnails/poke.png'), + require('./Thumbnails/superlike.png'), + require('./Thumbnails/victory.png'), ]; var ListViewGridLayoutExample = React.createClass({ @@ -74,9 +74,7 @@ var ListViewGridLayoutExample = React.createClass({ _renderRow: function(rowData: string, sectionID: number, rowID: number) { var rowHash = Math.abs(hashCode(rowData)); - var imgSource = { - uri: THUMB_URLS[rowHash % THUMB_URLS.length], - }; + var imgSource = THUMB_URLS[rowHash % THUMB_URLS.length]; return ( this._pressRow(rowID)} underlayColor="transparent"> diff --git a/Examples/UIExplorer/ListViewPagingExample.js b/Examples/UIExplorer/ListViewPagingExample.js index 7167f3eea..f6cdcae62 100644 --- a/Examples/UIExplorer/ListViewPagingExample.js +++ b/Examples/UIExplorer/ListViewPagingExample.js @@ -11,6 +11,7 @@ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * @provides ListViewPagingExample * @flow */ 'use strict'; @@ -26,8 +27,26 @@ var { View, } = React; +var NativeModules = require('NativeModules'); +var { + UIManager, +} = NativeModules; + var PAGE_SIZE = 4; -var THUMB_URLS = ['https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-ash3/t39.1997/p128x128/851549_767334479959628_274486868_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851561_767334496626293_1958532586_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-ash3/t39.1997/p128x128/851579_767334503292959_179092627_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851589_767334513292958_1747022277_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851563_767334559959620_1193692107_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851593_767334566626286_1953955109_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851591_767334523292957_797560749_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851567_767334529959623_843148472_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851548_767334489959627_794462220_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851575_767334539959622_441598241_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-ash3/t39.1997/p128x128/851573_767334549959621_534583464_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851583_767334573292952_1519550680_n.png']; +var THUMB_URLS = [ + require('./Thumbnails/like.png'), + require('./Thumbnails/dislike.png'), + require('./Thumbnails/call.png'), + require('./Thumbnails/fist.png'), + require('./Thumbnails/bandaged.png'), + require('./Thumbnails/flowers.png'), + require('./Thumbnails/heart.png'), + require('./Thumbnails/liking.png'), + require('./Thumbnails/party.png'), + require('./Thumbnails/poke.png'), + require('./Thumbnails/superlike.png'), + require('./Thumbnails/victory.png'), +]; var NUM_SECTIONS = 100; var NUM_ROWS_PER_SECTION = 10; @@ -35,6 +54,10 @@ var Thumb = React.createClass({ getInitialState: function() { return {thumbIndex: this._getThumbIdx(), dir: 'row'}; }, + componentWillMount: function() { + UIManager.setLayoutAnimationEnabledExperimental && + UIManager.setLayoutAnimationEnabledExperimental(true); + }, _getThumbIdx: function() { return Math.floor(Math.random() * THUMB_URLS.length); }, @@ -51,9 +74,9 @@ var Thumb = React.createClass({ - - - + + + {this.state.dir === 'column' ? Oooo, look at this new text! So awesome it may just be crazy. diff --git a/Examples/UIExplorer/MapViewExample.js b/Examples/UIExplorer/MapViewExample.js index 572017574..bd1173a83 100644 --- a/Examples/UIExplorer/MapViewExample.js +++ b/Examples/UIExplorer/MapViewExample.js @@ -17,10 +17,13 @@ var React = require('react-native'); var { + Image, MapView, + PropTypes, StyleSheet, Text, TextInput, + TouchableOpacity, View, } = React; @@ -34,22 +37,20 @@ var regionText = { var MapRegionInput = React.createClass({ propTypes: { - region: React.PropTypes.shape({ - latitude: React.PropTypes.number.isRequired, - longitude: React.PropTypes.number.isRequired, - latitudeDelta: React.PropTypes.number.isRequired, - longitudeDelta: React.PropTypes.number.isRequired, + region: PropTypes.shape({ + latitude: PropTypes.number.isRequired, + longitude: PropTypes.number.isRequired, + latitudeDelta: PropTypes.number, + longitudeDelta: PropTypes.number, }), - onChange: React.PropTypes.func.isRequired, + onChange: PropTypes.func.isRequired, }, - getInitialState: function() { + getInitialState() { return { region: { latitude: 0, longitude: 0, - latitudeDelta: 0, - longitudeDelta: 0, } }; }, @@ -91,7 +92,9 @@ var MapRegionInput = React.createClass({ {'Latitude delta'} ); @@ -208,6 +215,42 @@ var MapViewExample = React.createClass({ }); +var AnnotationExample = React.createClass({ + + getInitialState() { + return { + isFirstLoad: true, + annotations: [], + mapRegion: undefined, + }; + }, + + render() { + if (this.state.isFirstLoad) { + var onRegionChangeComplete = (region) => { + this.setState({ + isFirstLoad: false, + annotations: [{ + longitude: region.longitude, + latitude: region.latitude, + ...this.props.annotation, + }], + }); + }; + } + + return ( + + ); + }, + +}); + var styles = StyleSheet.create({ map: { height: 150, @@ -242,12 +285,109 @@ exports.description = 'Base component to display maps'; exports.examples = [ { title: 'Map', - render(): ReactElement { return ; } + render() { + return ; + } }, { - title: 'Map shows user location', + title: 'showsUserLocation + followUserLocation', render() { - return ; + return ( + + ); } - } + }, + { + title: 'Callout example', + render() { + return { + alert('You Are Here'); + }}> + + + ), + }}/>; + } + }, + { + title: 'Draggable pin', + render() { + return { + console.log('Drag state: ' + event.state); + }, + }}/>; + } + }, + { + title: 'Custom pin color', + render() { + return ; + } + }, + { + title: 'Custom pin image', + render() { + return ; + } + }, + { + title: 'Custom pin view', + render() { + return + + Thumbs Up! + + + , + }}/>; + } + }, + { + title: 'Custom overlay', + render() { + return ; + } + }, ]; diff --git a/Examples/UIExplorer/ModalExample.js b/Examples/UIExplorer/ModalExample.js index 3202cb622..eb925b5eb 100644 --- a/Examples/UIExplorer/ModalExample.js +++ b/Examples/UIExplorer/ModalExample.js @@ -143,6 +143,7 @@ var styles = StyleSheet.create({ }, innerContainer: { borderRadius: 10, + alignItems: 'center', }, row: { alignItems: 'center', @@ -158,6 +159,7 @@ var styles = StyleSheet.create({ borderRadius: 5, flex: 1, height: 44, + alignSelf: 'stretch', justifyContent: 'center', overflow: 'hidden', }, diff --git a/Examples/UIExplorer/Navigator/BreadcrumbNavSample.js b/Examples/UIExplorer/Navigator/BreadcrumbNavSample.js index 08b8571ae..e05a8c39e 100644 --- a/Examples/UIExplorer/Navigator/BreadcrumbNavSample.js +++ b/Examples/UIExplorer/Navigator/BreadcrumbNavSample.js @@ -15,14 +15,12 @@ var React = require('react-native'); var { - PixelRatio, Navigator, StyleSheet, ScrollView, Text, TouchableHighlight, - TouchableOpacity, - View, + TouchableOpacity } = React; var _getRandomRoute = function() { @@ -82,19 +80,19 @@ var BreadcrumbNavSample = React.createClass({ return ( { navigator.push(_getRandomRoute()) }} + onPress={() => { navigator.push(_getRandomRoute()); }} text="Push" /> { navigator.immediatelyResetRouteStack([_getRandomRoute(), _getRandomRoute()]) }} + onPress={() => { navigator.immediatelyResetRouteStack([_getRandomRoute(), _getRandomRoute()]); }} text="Reset w/ 2 scenes" /> { navigator.popToTop() }} + onPress={() => { navigator.popToTop(); }} text="Pop to top" /> { navigator.replace(_getRandomRoute()) }} + onPress={() => { navigator.replace(_getRandomRoute()); }} text="Replace" /> { if (route.sceneConfig) { @@ -190,7 +189,7 @@ var styles = StyleSheet.create({ button: { backgroundColor: 'white', padding: 15, - borderBottomWidth: 1 / PixelRatio.get(), + borderBottomWidth: StyleSheet.hairlineWidth, borderBottomColor: '#CDCDCD', }, buttonText: { diff --git a/Examples/UIExplorer/NavigatorIOSColorsExample.js b/Examples/UIExplorer/NavigatorIOSColorsExample.js index 741186902..4078cac6d 100644 --- a/Examples/UIExplorer/NavigatorIOSColorsExample.js +++ b/Examples/UIExplorer/NavigatorIOSColorsExample.js @@ -66,7 +66,7 @@ var NavigatorIOSColors = React.createClass({ tintColor="#FFFFFF" barTintColor="#183E63" titleTextColor="#FFFFFF" - translucent="true" + translucent={true} /> ); }, diff --git a/Examples/UIExplorer/NavigatorIOSExample.js b/Examples/UIExplorer/NavigatorIOSExample.js index 4a2011a65..08d227c3f 100644 --- a/Examples/UIExplorer/NavigatorIOSExample.js +++ b/Examples/UIExplorer/NavigatorIOSExample.js @@ -20,7 +20,6 @@ var ViewExample = require('./ViewExample'); var createExamplePage = require('./createExamplePage'); var { AlertIOS, - PixelRatio, ScrollView, StyleSheet, Text, @@ -227,7 +226,7 @@ var styles = StyleSheet.create({ }, line: { backgroundColor: '#bbbbbb', - height: 1 / PixelRatio.get(), + height: StyleSheet.hairlineWidth, }, row: { backgroundColor: 'white', @@ -236,7 +235,7 @@ var styles = StyleSheet.create({ paddingVertical: 15, }, separator: { - height: 1 / PixelRatio.get(), + height: StyleSheet.hairlineWidth, backgroundColor: '#bbbbbb', marginLeft: 15, }, diff --git a/Examples/UIExplorer/NetInfoExample.js b/Examples/UIExplorer/NetInfoExample.js index 6ab1805df..a79ed67e2 100644 --- a/Examples/UIExplorer/NetInfoExample.js +++ b/Examples/UIExplorer/NetInfoExample.js @@ -15,83 +15,84 @@ */ 'use strict'; -var React = require('react-native'); -var { +const React = require('react-native'); +const { NetInfo, Text, - View + View, + TouchableWithoutFeedback, } = React; -var ReachabilitySubscription = React.createClass({ +const ConnectionInfoSubscription = React.createClass({ getInitialState() { return { - reachabilityHistory: [], + connectionInfoHistory: [], }; }, componentDidMount: function() { NetInfo.addEventListener( - 'change', - this._handleReachabilityChange + 'change', + this._handleConnectionInfoChange ); }, componentWillUnmount: function() { NetInfo.removeEventListener( - 'change', - this._handleReachabilityChange + 'change', + this._handleConnectionInfoChange ); }, - _handleReachabilityChange: function(reachability) { - var reachabilityHistory = this.state.reachabilityHistory.slice(); - reachabilityHistory.push(reachability); + _handleConnectionInfoChange: function(connectionInfo) { + const connectionInfoHistory = this.state.connectionInfoHistory.slice(); + connectionInfoHistory.push(connectionInfo); this.setState({ - reachabilityHistory, + connectionInfoHistory, }); }, render() { return ( - - {JSON.stringify(this.state.reachabilityHistory)} - + + {JSON.stringify(this.state.connectionInfoHistory)} + ); } }); -var ReachabilityCurrent = React.createClass({ +const ConnectionInfoCurrent = React.createClass({ getInitialState() { return { - reachability: null, + connectionInfo: null, }; }, componentDidMount: function() { NetInfo.addEventListener( - 'change', - this._handleReachabilityChange + 'change', + this._handleConnectionInfoChange ); NetInfo.fetch().done( - (reachability) => { this.setState({reachability}); } + (connectionInfo) => { this.setState({connectionInfo}); } ); }, componentWillUnmount: function() { NetInfo.removeEventListener( - 'change', - this._handleReachabilityChange + 'change', + this._handleConnectionInfoChange ); }, - _handleReachabilityChange: function(reachability) { + _handleConnectionInfoChange: function(connectionInfo) { this.setState({ - reachability, + connectionInfo, }); }, render() { return ( - - {this.state.reachability} - + + {this.state.connectionInfo} + ); } }); -var IsConnected = React.createClass({ +const IsConnected = React.createClass({ getInitialState() { return { isConnected: null, @@ -99,17 +100,17 @@ var IsConnected = React.createClass({ }, componentDidMount: function() { NetInfo.isConnected.addEventListener( - 'change', - this._handleConnectivityChange + 'change', + this._handleConnectivityChange ); NetInfo.isConnected.fetch().done( - (isConnected) => { this.setState({isConnected}); } + (isConnected) => { this.setState({isConnected}); } ); }, componentWillUnmount: function() { NetInfo.isConnected.removeEventListener( - 'change', - this._handleConnectivityChange + 'change', + this._handleConnectivityChange ); }, _handleConnectivityChange: function(isConnected) { @@ -119,9 +120,37 @@ var IsConnected = React.createClass({ }, render() { return ( - - {this.state.isConnected ? 'Online' : 'Offline'} - + + {this.state.isConnected ? 'Online' : 'Offline'} + + ); + } +}); + +const IsConnectionExpensive = React.createClass({ + getInitialState() { + return { + isConnectionExpensive: (null : ?boolean), + }; + }, + _checkIfExpensive() { + NetInfo.isConnectionExpensive( + (isConnectionExpensive) => { this.setState({isConnectionExpensive}); } + ); + }, + render() { + return ( + + + + Click to see if connection is expensive: + {this.state.isConnectionExpensive === true ? 'Expensive' : + this.state.isConnectionExpensive === false ? 'Not expensive' + : 'Unknown'} + + + + ); } }); @@ -135,13 +164,19 @@ exports.examples = [ render(): ReactElement { return ; } }, { - title: 'NetInfo.reachabilityIOS', - description: 'Asynchronously load and observe iOS reachability', - render(): ReactElement { return ; } + title: 'NetInfo.update', + description: 'Asynchronously load and observe connectionInfo', + render(): ReactElement { return ; } }, { - title: 'NetInfo.reachabilityIOS', - description: 'Observed updates to iOS reachability', - render(): ReactElement { return ; } + title: 'NetInfo.updateHistory', + description: 'Observed updates to connectionInfo', + render(): ReactElement { return ; } + }, + { + platform: 'android', + title: 'NetInfo.isConnectionExpensive (Android)', + description: 'Asynchronously check isConnectionExpensive', + render(): ReactElement { return ; } }, ]; diff --git a/Examples/UIExplorer/PanResponderExample.js b/Examples/UIExplorer/PanResponderExample.js index 649ea21cb..47833af3f 100644 --- a/Examples/UIExplorer/PanResponderExample.js +++ b/Examples/UIExplorer/PanResponderExample.js @@ -11,27 +11,27 @@ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - * @flow + * @flow weak */ 'use strict'; var React = require('react-native'); var { - StyleSheet, PanResponder, + StyleSheet, View, + processColor, } = React; var CIRCLE_SIZE = 80; var CIRCLE_COLOR = 'blue'; var CIRCLE_HIGHLIGHT_COLOR = 'green'; - var PanResponderExample = React.createClass({ statics: { title: 'PanResponder Sample', - description: 'Basic gesture handling example', + description: 'Shows the use of PanResponder to provide basic gesture handling.', }, _panResponder: {}, @@ -52,8 +52,10 @@ var PanResponderExample = React.createClass({ this._previousLeft = 20; this._previousTop = 84; this._circleStyles = { - left: this._previousLeft, - top: this._previousTop, + style: { + left: this._previousLeft, + top: this._previousTop + } }; }, @@ -77,14 +79,20 @@ var PanResponderExample = React.createClass({ }, _highlight: function() { - this.circle && this.circle.setNativeProps({ - backgroundColor: CIRCLE_HIGHLIGHT_COLOR + const circle = this.circle; + circle && circle.setNativeProps({ + style: { + backgroundColor: processColor(CIRCLE_HIGHLIGHT_COLOR) + } }); }, _unHighlight: function() { - this.circle && this.circle.setNativeProps({ - backgroundColor: CIRCLE_COLOR + const circle = this.circle; + circle && circle.setNativeProps({ + style: { + backgroundColor: processColor(CIRCLE_COLOR) + } }); }, @@ -106,8 +114,8 @@ var PanResponderExample = React.createClass({ this._highlight(); }, _handlePanResponderMove: function(e: Object, gestureState: Object) { - this._circleStyles.left = this._previousLeft + gestureState.dx; - this._circleStyles.top = this._previousTop + gestureState.dy; + this._circleStyles.style.left = this._previousLeft + gestureState.dx; + this._circleStyles.style.top = this._previousTop + gestureState.dy; this._updatePosition(); }, _handlePanResponderEnd: function(e: Object, gestureState: Object) { diff --git a/Examples/UIExplorer/PickerAndroidExample.js b/Examples/UIExplorer/PickerAndroidExample.js new file mode 100644 index 000000000..91a2bd5a4 --- /dev/null +++ b/Examples/UIExplorer/PickerAndroidExample.js @@ -0,0 +1,142 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * @flow + */ +'use strict'; + +const React = require('react-native'); +const UIExplorerBlock = require('UIExplorerBlock'); +const UIExplorerPage = require('UIExplorerPage'); + +const { + PickerAndroid, + Text, + TouchableWithoutFeedback, +} = React; +const Item = PickerAndroid.Item; + +const PickerAndroidExample = React.createClass({ + getInitialState: function() { + return { + selected1: 'key1', + selected2: 'key1', + selected3: 'key1', + selected4: 'key1', + color: 'red', + mode: PickerAndroid.MODE_DIALOG, + }; + }, + + displayName: 'Android Picker', + + render: function() { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + Tap here to switch between dialog/dropdown. + + + + + + + + + + + + + + + You can not change the value of this picker because it doesn't set a selected prop on + its items. + + + + + + + + + + + + + + + + ); + }, + + changeMode: function() { + const newMode = this.state.mode === PickerAndroid.MODE_DIALOG + ? PickerAndroid.MODE_DROPDOWN + : PickerAndroid.MODE_DIALOG; + this.setState({mode: newMode}); + }, + + onSelect: function(key, value) { + const newState = {}; + newState[key] = value; + this.setState(newState); + }, +}); + +exports.title = ''; +exports.displayName = 'PickerAndroidExample'; +exports.description = 'The Android Picker component provides multiple options to choose from'; +exports.examples = [ + { + title: 'PickerAndroidExample', + render(): ReactElement { return ; } + }, +]; diff --git a/Examples/UIExplorer/PickerIOSExample.js b/Examples/UIExplorer/PickerIOSExample.js index 31c81cccc..51b8f9f30 100644 --- a/Examples/UIExplorer/PickerIOSExample.js +++ b/Examples/UIExplorer/PickerIOSExample.js @@ -87,24 +87,21 @@ var PickerExample = React.createClass({ key={carMake} value={carMake} label={CAR_MAKES_AND_MODELS[carMake].name} - /> - ) - )} + /> + ))} Please choose a model of {make.name}: this.setState({modelIndex})}> - {CAR_MAKES_AND_MODELS[this.state.carMake].models.map( - (modelName, modelIndex) => ( - - )) - } + {CAR_MAKES_AND_MODELS[this.state.carMake].models.map((modelName, modelIndex) => ( + + ))} You selected: {selectionString} @@ -112,6 +109,34 @@ var PickerExample = React.createClass({ }, }); +var PickerStyleExample = React.createClass({ + getInitialState: function() { + return { + carMake: 'cadillac', + modelIndex: 0, + }; + }, + + render: function() { + var make = CAR_MAKES_AND_MODELS[this.state.carMake]; + var selectionString = make.name + ' ' + make.models[this.state.modelIndex]; + return ( + this.setState({carMake, modelIndex: 0})}> + {Object.keys(CAR_MAKES_AND_MODELS).map((carMake) => ( + + ))} + + ); + }, +}); + exports.displayName = (undefined: ?string); exports.title = ''; exports.description = 'Render lists of selectable options with UIPickerView.'; @@ -121,4 +146,10 @@ exports.examples = [ render: function(): ReactElement { return ; }, +}, +{ + title: ' with custom styling', + render: function(): ReactElement { + return ; + }, }]; diff --git a/Examples/UIExplorer/PointerEventsExample.js b/Examples/UIExplorer/PointerEventsExample.js index cd402c471..735a5952d 100644 --- a/Examples/UIExplorer/PointerEventsExample.js +++ b/Examples/UIExplorer/PointerEventsExample.js @@ -250,6 +250,6 @@ var styles = StyleSheet.create({ exports.framework = 'React'; exports.title = 'Pointer Events'; -exports.description = '`pointerEvents` is a prop of View that gives control ' + - 'of how touches should be handled.'; +exports.description = 'Demonstrates the use of the pointerEvents prop of a ' + + 'View to control how touches should be handled.'; exports.examples = exampleClasses.map(infoToExample); diff --git a/Examples/UIExplorer/ProgressBarAndroidExample.android.js b/Examples/UIExplorer/ProgressBarAndroidExample.android.js new file mode 100644 index 000000000..19b182bc7 --- /dev/null +++ b/Examples/UIExplorer/ProgressBarAndroidExample.android.js @@ -0,0 +1,111 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * @flow + */ +'use strict'; + +var ProgressBar = require('ProgressBarAndroid'); +var React = require('React'); +var UIExplorerBlock = require('UIExplorerBlock'); +var UIExplorerPage = require('UIExplorerPage'); + +var TimerMixin = require('react-timer-mixin'); + +var MovingBar = React.createClass({ + mixins: [TimerMixin], + + getInitialState: function() { + return { + progress: 0 + }; + }, + + componentDidMount: function() { + this.setInterval( + () => { + var progress = (this.state.progress + 0.02) % 1; + this.setState({progress: progress}); + }, 50 + ); + }, + + render: function() { + return ; + }, +}); + +var ProgressBarAndroidExample = React.createClass({ + + statics: { + title: '', + description: 'Visual indicator of progress of some operation. ' + + 'Shows either a cyclic animation or a horizontal bar.', + }, + + render: function() { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); + }, +}); + +module.exports = ProgressBarAndroidExample; diff --git a/Examples/UIExplorer/PullToRefreshViewAndroidExample.android.js b/Examples/UIExplorer/PullToRefreshViewAndroidExample.android.js new file mode 100644 index 000000000..3d22f7608 --- /dev/null +++ b/Examples/UIExplorer/PullToRefreshViewAndroidExample.android.js @@ -0,0 +1,128 @@ +/** +* The examples provided by Facebook are for non-commercial testing and +* evaluation purposes only. +* +* Facebook reserves all rights not expressly granted. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL +* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*/ +'use strict'; + +const React = require('react-native'); +const { + ScrollView, + StyleSheet, + PullToRefreshViewAndroid, + Text, + TouchableWithoutFeedback, + View, +} = React; + +const styles = StyleSheet.create({ + row: { + borderColor: 'grey', + borderWidth: 1, + padding: 20, + backgroundColor: '#3a5795', + margin: 5, + }, + text: { + alignSelf: 'center', + color: '#fff', + + }, + layout: { + flex: 1, + }, + scrollview: { + flex: 1, + }, +}); + +const Row = React.createClass({ + _onClick: function() { + this.props.onClick(this.props.data); + }, + render: function() { + return ( + + + + {this.props.data.text + ' (' + this.props.data.clicks + ' clicks)'} + + + + ); + }, +}); +const PullToRefreshViewAndroidExample = React.createClass({ + statics: { + title: '', + description: 'Container that adds pull-to-refresh support to its child view.' + }, + + getInitialState() { + return { + isRefreshing: false, + loaded: 0, + rowData: Array.from(new Array(20)).map( + (val, i) => ({text: 'Initial row' + i, clicks: 0}) + ), + }; + }, + + _onClick(row) { + row.clicks++; + this.setState({ + rowData: this.state.rowData, + }); + }, + + render() { + const rows = this.state.rowData.map((row, ii) => { + return ; + }); + return ( + + + {rows} + + + ); + }, + + _onRefresh() { + this.setState({isRefreshing: true}); + setTimeout(() => { + // prepend 10 items + const rowData = Array.from(new Array(10)) + .map((val, i) => ({ + text: 'Loaded row' + (+this.state.loaded + i), + clicks: 0, + })) + .concat(this.state.rowData); + + this.setState({ + loaded: this.state.loaded + 10, + isRefreshing: false, + rowData: rowData, + }); + }, 5000); + }, + +}); + + +module.exports = PullToRefreshViewAndroidExample; diff --git a/Examples/UIExplorer/RCTRootViewIOSExample.js b/Examples/UIExplorer/RCTRootViewIOSExample.js new file mode 100644 index 000000000..69ecab0a7 --- /dev/null +++ b/Examples/UIExplorer/RCTRootViewIOSExample.js @@ -0,0 +1,98 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * @flow + */ + +'use strict'; + +const React = require('react-native'); +const { + StyleSheet, + Text, + View, +} = React; + +const requireNativeComponent = require('requireNativeComponent'); + +class AppPropertiesUpdateExample extends React.Component { + render() { + // Do not require this unless we are actually rendering. + const UpdatePropertiesExampleView = requireNativeComponent('UpdatePropertiesExampleView'); + return ( + + + Press the button to update the field below by passing new properties to the RN app. + + + + Error: This demo is accessible only from UIExplorer app + + + + ); + } +} + +class RootViewSizeFlexibilityExample extends React.Component { + render() { + // Do not require this unless we are actually rendering. + const FlexibleSizeExampleView = requireNativeComponent('FlexibleSizeExampleView'); + return ( + + + Press the button to resize it. On resize, RCTRootViewDelegate is notified. You can use it to handle content size updates. + + + + Error: This demo is accessible only from UIExplorer app + + + + ); + } +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: '#F5FCFF', + }, + text: { + marginBottom: 20 + }, + nativeView: { + height: 140, + width: 280 + } +}); + +exports.title = 'RCTRootView'; +exports.description = 'Examples that show useful methods when embedding React Native in a native application'; +exports.examples = [ +{ + title: 'Updating app properties in runtime', + render(): React.Component { + return ( + + ); + }, +}, +{ + title: 'RCTRootView\'s size flexibility', + render(): React.Component { + return ( + + ); + }, +}]; diff --git a/Examples/UIExplorer/README.md b/Examples/UIExplorer/README.md new file mode 100644 index 000000000..f7668a821 --- /dev/null +++ b/Examples/UIExplorer/README.md @@ -0,0 +1,42 @@ +# UIExplorer + +The UIExplorer is a sample app that showcases React Native views and modules. + +## Running this app + +Before running the app, make sure you ran: + + git clone https://github.com/facebook/react-native.git + cd react-native + npm install + +### Running on iOS + +Mac OS and Xcode are required. + +- Open `Examples/UIExplorer/UIExplorer.xcodeproj` in Xcode +- Hit the Run button + +See [Running on device](https://facebook.github.io/react-native/docs/running-on-device-ios.html) if you want to use a physical device. + +### Running on Android + +You'll need to have all the [prerequisites](https://github.com/facebook/react-native/tree/master/ReactAndroid#prerequisites) (SDK, NDK) for Building React Native installed. + +Start an Android emulator ([Genymotion](https://www.genymotion.com) is recommended). + + cd react-native + ./gradlew :Examples:UIExplorer:android:app:installDebug + ./packager/packager.sh + +_Note: Building for the first time can take a while._ + +Open the UIExplorer app in your emulator. + +See [Running on Device](https://facebook.github.io/react-native/docs/running-on-device-android.html) in case you want to use a physical device. + +## Built from source + +Building the app on both iOS and Android means building the React Native framework from source. This way you're running the latest native and JS code the way you see it in your clone of the github repo. + +This is different from apps created using `react-native init` which have a dependency on a specific version of React Native JS and native code, declared in a `package.json` file (and `build.gradle` for Android apps). diff --git a/Examples/UIExplorer/RefreshControlExample.js b/Examples/UIExplorer/RefreshControlExample.js new file mode 100644 index 000000000..b8feefb66 --- /dev/null +++ b/Examples/UIExplorer/RefreshControlExample.js @@ -0,0 +1,125 @@ +/** +* The examples provided by Facebook are for non-commercial testing and +* evaluation purposes only. +* +* Facebook reserves all rights not expressly granted. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL +* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*/ +'use strict'; + +const React = require('react-native'); +const { + ScrollView, + StyleSheet, + RefreshControl, + Text, + TouchableWithoutFeedback, + View, +} = React; + +const styles = StyleSheet.create({ + row: { + borderColor: 'grey', + borderWidth: 1, + padding: 20, + backgroundColor: '#3a5795', + margin: 5, + }, + text: { + alignSelf: 'center', + color: '#fff', + }, + scrollview: { + flex: 1, + }, +}); + +const Row = React.createClass({ + _onClick: function() { + this.props.onClick(this.props.data); + }, + render: function() { + return ( + + + + {this.props.data.text + ' (' + this.props.data.clicks + ' clicks)'} + + + + ); + }, +}); + +const RefreshControlExample = React.createClass({ + statics: { + title: '', + description: 'Adds pull-to-refresh support to a scrollview.' + }, + + getInitialState() { + return { + isRefreshing: false, + loaded: 0, + rowData: Array.from(new Array(20)).map( + (val, i) => ({text: 'Initial row' + i, clicks: 0})), + }; + }, + + _onClick(row) { + row.clicks++; + this.setState({ + rowData: this.state.rowData, + }); + }, + + render() { + const rows = this.state.rowData.map((row, ii) => { + return ; + }); + return ( + + }> + {rows} + + ); + }, + + _onRefresh() { + this.setState({isRefreshing: true}); + setTimeout(() => { + // prepend 10 items + const rowData = Array.from(new Array(10)) + .map((val, i) => ({ + text: 'Loaded row' + (+this.state.loaded + i), + clicks: 0, + })) + .concat(this.state.rowData); + + this.setState({ + loaded: this.state.loaded + 10, + isRefreshing: false, + rowData: rowData, + }); + }, 5000); + }, +}); + +module.exports = RefreshControlExample; diff --git a/Examples/UIExplorer/ResponderExample.js b/Examples/UIExplorer/ResponderExample.js deleted file mode 100644 index 05ba27cd2..000000000 --- a/Examples/UIExplorer/ResponderExample.js +++ /dev/null @@ -1,136 +0,0 @@ -/** - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * @flow - */ -'use strict'; - -var React = require('react-native'); -var { - StyleSheet, - PanResponder, - View, -} = React; - -var CIRCLE_SIZE = 80; -var CIRCLE_COLOR = 'blue'; -var CIRCLE_HIGHLIGHT_COLOR = 'green'; - - -var NavigatorIOSExample = React.createClass({ - - statics: { - title: 'PanResponder Sample', - description: 'Basic gesture handling example', - }, - - _panResponder: {}, - _previousLeft: 0, - _previousTop: 0, - _circleStyles: {}, - circle: (null : ?{ setNativeProps(props: Object): void }), - - componentWillMount: function() { - this._panResponder = PanResponder.create({ - onStartShouldSetPanResponder: this._handleStartShouldSetPanResponder, - onMoveShouldSetPanResponder: this._handleMoveShouldSetPanResponder, - onPanResponderGrant: this._handlePanResponderGrant, - onPanResponderMove: this._handlePanResponderMove, - onPanResponderRelease: this._handlePanResponderEnd, - onPanResponderTerminate: this._handlePanResponderEnd, - }); - this._previousLeft = 20; - this._previousTop = 84; - this._circleStyles = { - left: this._previousLeft, - top: this._previousTop, - }; - }, - - componentDidMount: function() { - this._updatePosition(); - }, - - render: function() { - return ( - - { - this.circle = circle; - }} - style={styles.circle} - {...this._panResponder.panHandlers} - /> - - ); - }, - - _highlight: function() { - this.circle && this.circle.setNativeProps({ - backgroundColor: CIRCLE_HIGHLIGHT_COLOR - }); - }, - - _unHighlight: function() { - this.circle && this.circle.setNativeProps({ - backgroundColor: CIRCLE_COLOR - }); - }, - - _updatePosition: function() { - this.circle && this.circle.setNativeProps(this._circleStyles); - }, - - _handleStartShouldSetPanResponder: function(e: Object, gestureState: Object): boolean { - // Should we become active when the user presses down on the circle? - return true; - }, - - _handleMoveShouldSetPanResponder: function(e: Object, gestureState: Object): boolean { - // Should we become active when the user moves a touch over the circle? - return true; - }, - - _handlePanResponderGrant: function(e: Object, gestureState: Object) { - this._highlight(); - }, - _handlePanResponderMove: function(e: Object, gestureState: Object) { - this._circleStyles.left = this._previousLeft + gestureState.dx; - this._circleStyles.top = this._previousTop + gestureState.dy; - this._updatePosition(); - }, - _handlePanResponderEnd: function(e: Object, gestureState: Object) { - this._unHighlight(); - this._previousLeft += gestureState.dx; - this._previousTop += gestureState.dy; - }, -}); - -var styles = StyleSheet.create({ - circle: { - width: CIRCLE_SIZE, - height: CIRCLE_SIZE, - borderRadius: CIRCLE_SIZE / 2, - backgroundColor: CIRCLE_COLOR, - position: 'absolute', - left: 0, - top: 0, - }, - container: { - flex: 1, - paddingTop: 64, - }, -}); - -module.exports = NavigatorIOSExample; diff --git a/Examples/UIExplorer/ScrollViewExample.js b/Examples/UIExplorer/ScrollViewExample.js index 1ca8baf9a..53d19148b 100644 --- a/Examples/UIExplorer/ScrollViewExample.js +++ b/Examples/UIExplorer/ScrollViewExample.js @@ -33,9 +33,9 @@ exports.examples = [ render: function() { return ( { console.log('onScroll!'); }} scrollEventThrottle={200} - contentInset={{top: -50}} style={styles.scrollView}> {THUMBS.map(createThumbRow)} @@ -47,8 +47,8 @@ exports.examples = [ render: function() { return ( {THUMBS.map(createThumbRow)} diff --git a/Examples/UIExplorer/ScrollViewSimpleExample.js b/Examples/UIExplorer/ScrollViewSimpleExample.js new file mode 100644 index 000000000..c9bbe7407 --- /dev/null +++ b/Examples/UIExplorer/ScrollViewSimpleExample.js @@ -0,0 +1,82 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * @flow + */ +'use strict'; + +var React = require('react-native'); +var { + ScrollView, + StyleSheet, + Text, + TouchableOpacity +} = React; + +var NUM_ITEMS = 20; + +var ScrollViewSimpleExample = React.createClass({ + statics: { + title: '', + description: 'Component that enables scrolling through child components.' + }, + makeItems: function(nItems: number, styles): Array { + var items = []; + for (var i = 0; i < nItems; i++) { + items[i] = ( + + {'Item ' + i} + + ); + } + return items; + }, + + render: function() { + // One of the items is a horizontal scroll view + var items = this.makeItems(NUM_ITEMS, styles.itemWrapper); + items[4] = ( + + {this.makeItems(NUM_ITEMS, [styles.itemWrapper, styles.horizontalItemWrapper])} + + ); + + var verticalScrollView = ( + + {items} + + ); + + return verticalScrollView; + } +}); + +var styles = StyleSheet.create({ + verticalScrollView: { + margin: 10, + }, + itemWrapper: { + backgroundColor: '#dddddd', + alignItems: 'center', + borderRadius: 5, + borderWidth: 5, + borderColor: '#a52a2a', + padding: 30, + margin: 5, + }, + horizontalItemWrapper: { + padding: 50 + } +}); + +module.exports = ScrollViewSimpleExample; diff --git a/Examples/UIExplorer/SetPropertiesExampleApp.js b/Examples/UIExplorer/SetPropertiesExampleApp.js new file mode 100644 index 000000000..748bbc3cc --- /dev/null +++ b/Examples/UIExplorer/SetPropertiesExampleApp.js @@ -0,0 +1,43 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * @flow + */ + +'use strict'; + +var React = require('React'); +var Text = require('Text'); +var View = require('View'); + +var SetPropertiesExampleApp = React.createClass({ + + render: function() { + var wrapperStyle = { + backgroundColor: this.props.color, + flex: 1, + alignItems: 'center', + justifyContent: 'center' + }; + + return ( + + + Embedded React Native view + + + ); + }, +}); + +module.exports = SetPropertiesExampleApp; diff --git a/Examples/UIExplorer/SliderIOSExample.js b/Examples/UIExplorer/SliderIOSExample.js index 2dfda6667..baee69ae8 100644 --- a/Examples/UIExplorer/SliderIOSExample.js +++ b/Examples/UIExplorer/SliderIOSExample.js @@ -37,7 +37,7 @@ var SliderExample = React.createClass({ {this.state.value} this.setState({value: value})} /> ); @@ -62,7 +62,60 @@ exports.displayName = 'SliderExample'; exports.description = 'Slider input for numeric values'; exports.examples = [ { - title: 'SliderIOS', - render(): ReactElement { return ; } - } + title: 'Default settings', + render(): ReactElement { + return ; + } + }, + { + title: 'minimumValue: -1, maximumValue: 2', + render(): ReactElement { + return ( + + ); + } + }, + { + title: 'step: 0.25', + render(): ReactElement { + return ; + } + }, + { + title: 'Custom min/max track tint color', + render(): ReactElement { + return ( + + ); + } + }, + { + title: 'Custom thumb image', + render(): ReactElement { + return ; + } + }, + { + title: 'Custom track image', + render(): ReactElement { + return ; + } + }, + { + title: 'Custom min/max track image', + render(): ReactElement { + return ( + + ); + } + }, ]; diff --git a/Examples/UIExplorer/StatusBarIOSExample.js b/Examples/UIExplorer/StatusBarIOSExample.js index 28d5e2a7c..a7765d856 100644 --- a/Examples/UIExplorer/StatusBarIOSExample.js +++ b/Examples/UIExplorer/StatusBarIOSExample.js @@ -33,7 +33,7 @@ exports.examples = [{ return ( {['default', 'light-content'].map((style) => - StatusBarIOS.setStyle(style)}> setStyle('{style}') @@ -49,7 +49,7 @@ exports.examples = [{ return ( {['default', 'light-content'].map((style) => - StatusBarIOS.setStyle(style, true)}> setStyle('{style}', true) @@ -65,7 +65,7 @@ exports.examples = [{ return ( {['none', 'fade', 'slide'].map((animation) => - + StatusBarIOS.setHidden(true, animation)}> diff --git a/Examples/UIExplorer/SwitchIOSExample.js b/Examples/UIExplorer/SwitchExample.js similarity index 88% rename from Examples/UIExplorer/SwitchIOSExample.js rename to Examples/UIExplorer/SwitchExample.js index 3a70e2b5a..5ab41fe15 100644 --- a/Examples/UIExplorer/SwitchIOSExample.js +++ b/Examples/UIExplorer/SwitchExample.js @@ -17,7 +17,7 @@ var React = require('react-native'); var { - SwitchIOS, + Switch, Text, View } = React; @@ -32,11 +32,11 @@ var BasicSwitchExample = React.createClass({ render() { return ( - this.setState({falseSwitchIsOn: value})} style={{marginBottom: 10}} value={this.state.falseSwitchIsOn} /> - this.setState({trueSwitchIsOn: value})} value={this.state.trueSwitchIsOn} /> @@ -48,11 +48,11 @@ var DisabledSwitchExample = React.createClass({ render() { return ( - - @@ -70,14 +70,14 @@ var ColorSwitchExample = React.createClass({ render() { return ( - this.setState({colorFalseSwitchIsOn: value})} onTintColor="#00ff00" style={{marginBottom: 10}} thumbTintColor="#0000ff" tintColor="#ff0000" value={this.state.colorFalseSwitchIsOn} /> - this.setState({colorTrueSwitchIsOn: value})} onTintColor="#00ff00" thumbTintColor="#0000ff" @@ -99,36 +99,33 @@ var EventSwitchExample = React.createClass({ return ( - this.setState({eventSwitchIsOn: value})} style={{marginBottom: 10}} value={this.state.eventSwitchIsOn} /> - this.setState({eventSwitchIsOn: value})} style={{marginBottom: 10}} value={this.state.eventSwitchIsOn} /> - {this.state.eventSwitchIsOn ? "On" : "Off"} + {this.state.eventSwitchIsOn ? 'On' : 'Off'} - this.setState({eventSwitchRegressionIsOn: value})} style={{marginBottom: 10}} value={this.state.eventSwitchRegressionIsOn} /> - this.setState({eventSwitchRegressionIsOn: value})} style={{marginBottom: 10}} value={this.state.eventSwitchRegressionIsOn} /> - {this.state.eventSwitchRegressionIsOn ? "On" : "Off"} + {this.state.eventSwitchRegressionIsOn ? 'On' : 'Off'} ); } }); -exports.title = ''; -exports.displayName = 'SwitchExample'; -exports.description = 'Native boolean input'; -exports.examples = [ +var examples = [ { title: 'Switches can be set to true or false', render(): ReactElement { return ; } @@ -137,16 +134,24 @@ exports.examples = [ title: 'Switches can be disabled', render(): ReactElement { return ; } }, - { - title: 'Custom colors can be provided', - render(): ReactElement { return ; } - }, { title: 'Change events can be detected', render(): ReactElement { return ; } }, { title: 'Switches are controlled components', - render(): ReactElement { return ; } + render(): ReactElement { return ; } } ]; + +if (React.Platform.OS === 'ios') { + examples.push({ + title: 'Custom colors can be provided', + render(): ReactElement { return ; } + }); +} + +exports.title = ''; +exports.displayName = 'SwitchExample'; +exports.description = 'Native boolean input'; +exports.examples = examples; diff --git a/Examples/UIExplorer/TabBarIOSExample.js b/Examples/UIExplorer/TabBarIOSExample.js index 5ba3f0099..eccc84cbe 100644 --- a/Examples/UIExplorer/TabBarIOSExample.js +++ b/Examples/UIExplorer/TabBarIOSExample.js @@ -79,7 +79,8 @@ var TabBarExample = React.createClass({ {this._renderContent('#783E33', 'Red Tab', this.state.notifCount)} { this.setState({ diff --git a/Examples/UIExplorer/TextExample.android.js b/Examples/UIExplorer/TextExample.android.js new file mode 100644 index 000000000..7c0c7868f --- /dev/null +++ b/Examples/UIExplorer/TextExample.android.js @@ -0,0 +1,385 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * @flow + */ +'use strict'; + +var React = require('react-native'); +var { + Image, + StyleSheet, + Text, + View, +} = React; +var UIExplorerBlock = require('./UIExplorerBlock'); +var UIExplorerPage = require('./UIExplorerPage'); + +var Entity = React.createClass({ + render: function() { + return ( + + {this.props.children} + + ); + } +}); + +var AttributeToggler = React.createClass({ + getInitialState: function() { + return {fontWeight: 'bold', fontSize: 15}; + }, + toggleWeight: function() { + this.setState({ + fontWeight: this.state.fontWeight === 'bold' ? 'normal' : 'bold' + }); + }, + increaseSize: function() { + this.setState({ + fontSize: this.state.fontSize + 1 + }); + }, + render: function() { + var curStyle = {fontWeight: this.state.fontWeight, fontSize: this.state.fontSize}; + return ( + + + Tap the controls below to change attributes. + + + See how it will even work on this nested text + + + Toggle Weight + {' (with highlight onPress)'} + + + Increase Size (suppressHighlighting true) + + + ); + } +}); + +var TextExample = React.createClass({ + statics: { + title: '', + description: 'Base component for rendering styled text.', + }, + render: function() { + return ( + + + + The text should wrap if it goes on multiple lines. + See, this is going to the next line. + + + + + This text is indented by 10px padding on all sides. + + + + + Sans-Serif + + + Sans-Serif Bold + + + Serif + + + Serif Bold + + + Monospace + + + Monospace Bold (After 5.0) + + + + + + + Roboto Regular + + + Roboto Italic + + + Roboto Bold + + + Roboto Bold Italic + + + Roboto Light + + + Roboto Light Italic + + + Roboto Thin (After 4.2) + + + Roboto Thin Italic (After 4.2) + + + Roboto Condensed + + + Roboto Condensed Italic + + + Roboto Condensed Bold + + + Roboto Condensed Bold Italic + + + Roboto Medium (After 5.0) + + + Roboto Medium Italic (After 5.0) + + + + + + + + + NotoSerif Regular + + + NotoSerif Bold Italic + + + NotoSerif Italic (Missing Font file) + + + + + + + + Size 23 + + + Size 8 + + + + + Red color + + + Blue color + + + + + Move fast and be bold + + + Move fast and be bold + + + + + Move fast and be bold + + + Move fast and be bold + + + + + Move fast and be bold + + + + console.log('1st')}> + (Normal text, + console.log('2nd')}> + (and bold + console.log('3rd')}> + (and tiny bold italic blue + console.log('4th')}> + (and tiny normal blue) + + ) + + ) + + ) + + console.log('1st')}> + (Serif + console.log('2nd')}> + (Serif Bold Italic + console.log('3rd')}> + (Monospace Normal + console.log('4th')}> + (Sans-Serif Bold + console.log('5th')}> + (and Sans-Serif Normal) + + ) + + ) + + ) + + ) + + + Entity Name + + + + + auto (default) - english LTR + + + أحب اللغة العربية auto (default) - arabic RTL + + + left left left left left left left left left left left left left left left + + + center center center center center center center center center center center + + + right right right right right right right right right right right right right + + + + + + + 星际争霸是世界上最好的游戏。 + + + + + 星际争霸是世界上最好的游戏。 + + + + + 星际争霸是世界上最好的游戏。 + + + + + 星际争霸是世界上最好的游戏。星际争霸是世界上最好的游戏。星际争霸是世界上最好的游戏。星际争霸是世界上最好的游戏。 + + + + + + + A {'generated'} {' '} {'string'} and some     spaces + + + + + Holisticly formulate inexpensive ideas before best-of-breed benefits. Continually expedite magnetic potentialities rather than client-focused interfaces. + + + + + + + + + + + Red background, + + {' '}blue background, + + {' '}inherited blue background, + + {' '}nested green background. + + + + + + Same alpha as background, + + Inherited alpha from background, + + Reapply alpha + + + + + + + + + + + Default containerBackgroundColor (inherited) + backgroundColor wash + + + {"containerBackgroundColor: 'transparent' + backgroundColor wash"} + + + + + Maximum of one line no matter now much I write here. If I keep writing it{"'"}ll just truncate after one line + + + Maximum of two lines no matter now much I write here. If I keep writing it{"'"}ll just truncate after two lines + + + No maximum lines specified no matter now much I write here. If I keep writing it{"'"}ll just keep going and going + + + + + This text contains an inline image . Neat, huh? + + + + + Demo text shadow + + + + ); + } +}); + +var styles = StyleSheet.create({ + backgroundColorText: { + left: 5, + backgroundColor: 'rgba(100, 100, 100, 0.3)' + }, +}); + +module.exports = TextExample; diff --git a/Examples/UIExplorer/TextExample.ios.js b/Examples/UIExplorer/TextExample.ios.js index cceca74e6..3de37c423 100644 --- a/Examples/UIExplorer/TextExample.ios.js +++ b/Examples/UIExplorer/TextExample.ios.js @@ -17,6 +17,7 @@ var React = require('react-native'); var { + Image, StyleSheet, Text, View, @@ -242,6 +243,21 @@ exports.examples = [ ) + + (opacity + + (is inherited + + (and accumulated + + (and also applies to the background) + + ) + + ) + + ) + Entity Name @@ -397,6 +413,28 @@ exports.examples = [ ); }, +}, { + title: 'Inline images', + render: function() { + return ( + + + This text contains an inline image . Neat, huh? + + + ); + }, +}, { + title: 'Text shadow', + render: function() { + return ( + + + Demo text shadow + + + ); + }, }]; var styles = StyleSheet.create({ diff --git a/Examples/UIExplorer/TextInputExample.android.js b/Examples/UIExplorer/TextInputExample.android.js new file mode 100644 index 000000000..6e628f350 --- /dev/null +++ b/Examples/UIExplorer/TextInputExample.android.js @@ -0,0 +1,431 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * @flow + */ +'use strict'; + +var React = require('react-native'); +var { + Text, + TextInput, + View, + StyleSheet, +} = React; + +var TextEventsExample = React.createClass({ + getInitialState: function() { + return { + curText: '', + prevText: '', + prev2Text: '', + }; + }, + + updateText: function(text) { + this.setState((state) => { + return { + curText: text, + prevText: state.curText, + prev2Text: state.prevText, + }; + }); + }, + + render: function() { + return ( + + this.updateText('onFocus')} + onBlur={() => this.updateText('onBlur')} + onChange={(event) => this.updateText( + 'onChange text: ' + event.nativeEvent.text + )} + onEndEditing={(event) => this.updateText( + 'onEndEditing text: ' + event.nativeEvent.text + )} + onSubmitEditing={(event) => this.updateText( + 'onSubmitEditing text: ' + event.nativeEvent.text + )} + style={styles.singleLine} + /> + + {this.state.curText}{'\n'} + (prev: {this.state.prevText}){'\n'} + (prev2: {this.state.prev2Text}) + + + ); + } +}); + +class AutoExpandingTextInput extends React.Component { + constructor(props) { + super(props); + this.state = {text: '', height: 0}; + } + render() { + return ( + { + this.setState({ + text: event.nativeEvent.text, + height: event.nativeEvent.contentSize.height, + }); + }} + style={[styles.default, {height: Math.max(35, this.state.height)}]} + value={this.state.text} + /> + ); + } +} + +class RewriteExample extends React.Component { + constructor(props) { + super(props); + this.state = {text: ''}; + } + render() { + var limit = 20; + var remainder = limit - this.state.text.length; + var remainderColor = remainder > 5 ? 'blue' : 'red'; + return ( + + { + text = text.replace(/ /g, '_'); + this.setState({text}); + }} + style={styles.default} + value={this.state.text} + /> + + {remainder} + + + ); + } +} + +class TokenizedTextExample extends React.Component { + constructor(props) { + super(props); + this.state = {text: 'Hello #World'}; + } + render() { + + //define delimiter + let delimiter = /\s+/; + + //split string + let _text = this.state.text; + let token, index, parts = []; + while (_text) { + delimiter.lastIndex = 0; + token = delimiter.exec(_text); + if (token === null) { + break; + } + index = token.index; + if (token[0].length === 0) { + index = 1; + } + parts.push(_text.substr(0, index)); + parts.push(token[0]); + index = index + token[0].length; + _text = _text.slice(index); + } + parts.push(_text); + + //highlight hashtags + parts = parts.map((text) => { + if (/^#/.test(text)) { + return {text}; + } else { + return text; + } + }); + + return ( + + { + this.setState({text}); + }}> + {parts} + + + ); + } +} + +var styles = StyleSheet.create({ + multiline: { + height: 60, + fontSize: 16, + padding: 4, + marginBottom: 10, + }, + eventLabel: { + margin: 3, + fontSize: 12, + }, + singleLine: { + fontSize: 16, + padding: 4, + }, + singleLineWithHeightTextInput: { + height: 30, + }, + hashtag: { + color: 'blue', + fontWeight: 'bold', + }, +}); + +exports.title = ''; +exports.description = 'Single and multi-line text inputs.'; +exports.examples = [ + { + title: 'Auto-focus', + render: function() { + return ; + } + }, + { + title: "Live Re-Write ( -> '_')", + render: function() { + return ; + } + }, + { + title: 'Auto-capitalize', + render: function() { + var autoCapitalizeTypes = [ + 'none', + 'sentences', + 'words', + 'characters', + ]; + var examples = autoCapitalizeTypes.map((type) => { + return ( + + ); + }); + return {examples}; + } + }, + { + title: 'Auto-correct', + render: function() { + return ( + + + + + ); + } + }, + { + title: 'Keyboard types', + render: function() { + var keyboardTypes = [ + 'default', + 'email-address', + 'numeric', + 'phone-pad', + ]; + var examples = keyboardTypes.map((type) => { + return ( + + ); + }); + return {examples}; + } + }, + { + title: 'Event handling', + render: function(): ReactElement { return ; }, + }, + { + title: 'Colors and text inputs', + render: function() { + return ( + + + + + + + + + + Darker backgroundColor + + + + ); + } + }, + { + title: 'Text input, themes and heights', + render: function() { + return ( + + ); + } + }, + { + title: 'Passwords', + render: function() { + return ( + + ); + } + }, + { + title: 'Editable', + render: function() { + return ( + + ); + } + }, + { + title: 'Multiline', + render: function() { + return ( + + + + + multiline with children, aligned bottom-right + + + ); + } + }, + { + title: 'Fixed number of lines', + platform: 'android', + render: function() { + return ( + + + + + ); + } + }, + { + title: 'Auto-expanding', + render: function() { + return ( + + + + ); + } + }, + { + title: 'Attributed text', + render: function() { + return ; + } + }, +]; diff --git a/Examples/UIExplorer/TextInputExample.ios.js b/Examples/UIExplorer/TextInputExample.ios.js index d51a95e33..49079ff8b 100644 --- a/Examples/UIExplorer/TextInputExample.ios.js +++ b/Examples/UIExplorer/TextInputExample.ios.js @@ -42,6 +42,7 @@ var TextEventsExample = React.createClass({ curText: '', prevText: '', prev2Text: '', + prev3Text: '', }; }, @@ -51,6 +52,7 @@ var TextEventsExample = React.createClass({ curText: text, prevText: state.curText, prev2Text: state.prevText, + prev3Text: state.prev2Text, }; }); }, @@ -73,18 +75,50 @@ var TextEventsExample = React.createClass({ onSubmitEditing={(event) => this.updateText( 'onSubmitEditing text: ' + event.nativeEvent.text )} + onSelectionChange={(event) => this.updateText( + 'onSelectionChange range: ' + + event.nativeEvent.selection.start + ',' + + event.nativeEvent.selection.end + )} + onKeyPress={(event) => { + this.updateText('onKeyPress key: ' + event.nativeEvent.key); + }} style={styles.default} /> {this.state.curText}{'\n'} (prev: {this.state.prevText}){'\n'} - (prev2: {this.state.prev2Text}) + (prev2: {this.state.prev2Text}){'\n'} + (prev3: {this.state.prev3Text}) ); } }); +class AutoExpandingTextInput extends React.Component { + constructor(props) { + super(props); + this.state = {text: '', height: 0}; + } + render() { + return ( + { + this.setState({ + text: event.nativeEvent.text, + height: event.nativeEvent.contentSize.height, + }); + }} + style={[styles.default, {height: Math.max(35, this.state.height)}]} + value={this.state.text} + /> + ); + } +} + class RewriteExample extends React.Component { constructor(props) { super(props); @@ -114,6 +148,135 @@ class RewriteExample extends React.Component { } } +class RewriteExampleInvalidCharacters extends React.Component { + constructor(props) { + super(props); + this.state = {text: ''}; + } + render() { + return ( + + { + this.setState({text: text.replace(/\s/g, '')}); + }} + style={styles.default} + value={this.state.text} + /> + + ); + } +} + +class TokenizedTextExample extends React.Component { + constructor(props) { + super(props); + this.state = {text: 'Hello #World'}; + } + render() { + + //define delimiter + let delimiter = /\s+/; + + //split string + let _text = this.state.text; + let token, index, parts = []; + while (_text) { + delimiter.lastIndex = 0; + token = delimiter.exec(_text); + if (token === null) { + break; + } + index = token.index; + if (token[0].length === 0) { + index = 1; + } + parts.push(_text.substr(0, index)); + parts.push(token[0]); + index = index + token[0].length; + _text = _text.slice(index); + } + parts.push(_text); + + //highlight hashtags + parts = parts.map((text) => { + if (/^#/.test(text)) { + return {text}; + } else { + return text; + } + }); + + return ( + + { + this.setState({text}); + }}> + {parts} + + + ); + } +} + +var BlurOnSubmitExample = React.createClass({ + focusNextField(nextField) { + this.refs[nextField].focus(); + }, + + render: function() { + return ( + + this.focusNextField('2')} + /> + this.focusNextField('3')} + /> + this.focusNextField('4')} + /> + this.focusNextField('5')} + /> + + + ); + } +}); + var styles = StyleSheet.create({ page: { paddingBottom: 300, @@ -172,6 +335,10 @@ var styles = StyleSheet.create({ textAlign: 'right', width: 24, }, + hashtag: { + color: 'blue', + fontWeight: 'bold', + }, }); exports.displayName = (undefined: ?string); @@ -190,6 +357,12 @@ exports.examples = [ return ; } }, + { + title: 'Live Re-Write (no spaces allowed)', + render: function() { + return ; + } + }, { title: 'Auto-capitalize', render: function() { @@ -268,6 +441,27 @@ exports.examples = [ return {examples}; } }, + { + title: 'Keyboard appearance', + render: function() { + var keyboardAppearance = [ + 'default', + 'light', + 'dark', + ]; + var examples = keyboardAppearance.map((type) => { + return ( + + + + ); + }); + return {examples}; + } + }, { title: 'Return key types', render: function() { @@ -400,6 +594,27 @@ exports.examples = [ ); } }, + { + title: 'Blur on submit', + render: function(): ReactElement { return ; }, + }, + { + title: 'Multiline blur on submit', + render: function() { + return ( + + alert(event.nativeEvent.text)} + /> + + ); + } + }, { title: 'Multiline', render: function() { @@ -437,5 +652,25 @@ exports.examples = [ ); } - } + }, + { + title: 'Auto-expanding', + render: function() { + return ( + + + + ); + } + }, + { + title: 'Attributed text', + render: function() { + return ; + } + }, ]; diff --git a/Examples/UIExplorer/Thumbnails/bandaged.png b/Examples/UIExplorer/Thumbnails/bandaged.png new file mode 100644 index 000000000..1ba2e0b26 Binary files /dev/null and b/Examples/UIExplorer/Thumbnails/bandaged.png differ diff --git a/Examples/UIExplorer/Thumbnails/call.png b/Examples/UIExplorer/Thumbnails/call.png new file mode 100644 index 000000000..e1de13b3f Binary files /dev/null and b/Examples/UIExplorer/Thumbnails/call.png differ diff --git a/Examples/UIExplorer/Thumbnails/dislike.png b/Examples/UIExplorer/Thumbnails/dislike.png new file mode 100644 index 000000000..c7d241f30 Binary files /dev/null and b/Examples/UIExplorer/Thumbnails/dislike.png differ diff --git a/Examples/UIExplorer/Thumbnails/fist.png b/Examples/UIExplorer/Thumbnails/fist.png new file mode 100644 index 000000000..5338b924c Binary files /dev/null and b/Examples/UIExplorer/Thumbnails/fist.png differ diff --git a/Examples/UIExplorer/Thumbnails/flowers.png b/Examples/UIExplorer/Thumbnails/flowers.png new file mode 100644 index 000000000..94e93ca42 Binary files /dev/null and b/Examples/UIExplorer/Thumbnails/flowers.png differ diff --git a/Examples/UIExplorer/Thumbnails/heart.png b/Examples/UIExplorer/Thumbnails/heart.png new file mode 100644 index 000000000..28322cfa5 Binary files /dev/null and b/Examples/UIExplorer/Thumbnails/heart.png differ diff --git a/Examples/UIExplorer/Thumbnails/like.png b/Examples/UIExplorer/Thumbnails/like.png new file mode 100644 index 000000000..9c7347a8b Binary files /dev/null and b/Examples/UIExplorer/Thumbnails/like.png differ diff --git a/Examples/UIExplorer/UIExplorer/Images.xcassets/uie_thumb_big.imageset/uie_thumb_big.png b/Examples/UIExplorer/Thumbnails/liking.png similarity index 100% rename from Examples/UIExplorer/UIExplorer/Images.xcassets/uie_thumb_big.imageset/uie_thumb_big.png rename to Examples/UIExplorer/Thumbnails/liking.png diff --git a/Examples/UIExplorer/Thumbnails/party.png b/Examples/UIExplorer/Thumbnails/party.png new file mode 100644 index 000000000..4b02aaddd Binary files /dev/null and b/Examples/UIExplorer/Thumbnails/party.png differ diff --git a/Examples/UIExplorer/Thumbnails/poke.png b/Examples/UIExplorer/Thumbnails/poke.png new file mode 100644 index 000000000..a5c138ee9 Binary files /dev/null and b/Examples/UIExplorer/Thumbnails/poke.png differ diff --git a/Examples/UIExplorer/Thumbnails/superlike.png b/Examples/UIExplorer/Thumbnails/superlike.png new file mode 100644 index 000000000..b0f9c8541 Binary files /dev/null and b/Examples/UIExplorer/Thumbnails/superlike.png differ diff --git a/Examples/UIExplorer/Thumbnails/victory.png b/Examples/UIExplorer/Thumbnails/victory.png new file mode 100644 index 000000000..e13ed5c47 Binary files /dev/null and b/Examples/UIExplorer/Thumbnails/victory.png differ diff --git a/Examples/UIExplorer/TimerExample.js b/Examples/UIExplorer/TimerExample.js index b5923b350..51a8f1af5 100644 --- a/Examples/UIExplorer/TimerExample.js +++ b/Examples/UIExplorer/TimerExample.js @@ -18,27 +18,12 @@ var React = require('react-native'); var { AlertIOS, - StyleSheet, - Text, - TouchableHighlight, + Platform, + ToastAndroid, View, } = React; var TimerMixin = require('react-timer-mixin'); - -var Button = React.createClass({ - render: function() { - return ( - - - {this.props.children} - - - ); - }, -}); +var UIExplorerButton = require('./UIExplorerButton'); var TimerTester = React.createClass({ mixins: [TimerMixin], @@ -52,9 +37,9 @@ var TimerTester = React.createClass({ render: function() { var args = 'fn' + (this.props.dt !== undefined ? ', ' + this.props.dt : ''); return ( - + ); }, @@ -88,7 +73,11 @@ var TimerTester = React.createClass({ var msg = 'Finished ' + this._ii + ' ' + this.props.type + ' calls.\n' + 'Elapsed time: ' + e + ' ms\n' + (e / this._ii) + ' ms / iter'; console.log(msg); - AlertIOS.alert(msg); + if (Platform.OS === 'ios') { + AlertIOS.alert(msg); + } else if (Platform.OS === 'android') { + ToastAndroid.show(msg, ToastAndroid.SHORT); + } this._start = 0; this.forceUpdate(() => { this._ii = 0; }); return; @@ -112,18 +101,6 @@ var TimerTester = React.createClass({ }, }); -var styles = StyleSheet.create({ - button: { - borderColor: 'gray', - borderRadius: 8, - borderWidth: 1, - padding: 10, - margin: 5, - alignItems: 'center', - justifyContent: 'center', - }, -}); - exports.framework = 'React'; exports.title = 'Timers, TimerMixin'; exports.description = 'The TimerMixin provides timer functions for executing ' + @@ -183,9 +160,9 @@ exports.examples = [ if (this.state.showTimer) { var timer = [ , - + ]; var toggleText = 'Unmount timer'; } else { @@ -194,10 +171,21 @@ exports.examples = [ } return ( - {timer} - + {this.state.showTimer && this._renderTimer()} + + {this.state.showTimer ? 'Unmount timer' : 'Mount new timer'} + + + ); + }, + + _renderTimer: function() { + return ( + + + this.refs.interval.clear() }> + Clear interval + ); }, diff --git a/Examples/UIExplorer/ToastAndroidExample.android.js b/Examples/UIExplorer/ToastAndroidExample.android.js new file mode 100644 index 000000000..7f9cedf07 --- /dev/null +++ b/Examples/UIExplorer/ToastAndroidExample.android.js @@ -0,0 +1,69 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * @flow + */ + +'use strict'; + +var React = require('react-native'); +var { + StyleSheet, + Text, + ToastAndroid, + TouchableWithoutFeedback, +} = React; + +var UIExplorerBlock = require('UIExplorerBlock'); +var UIExplorerPage = require('UIExplorerPage'); + +var ToastExample = React.createClass({ + + statics: { + title: 'Toast Example', + description: 'Example that demonstrates the use of an Android Toast to provide feedback.', + }, + + getInitialState: function() { + return {}; + }, + + render: function() { + return ( + + + + ToastAndroid.show('This is a toast with short duration', ToastAndroid.SHORT)}> + Click me. + + + + + ToastAndroid.show('This is a toast with long duration', ToastAndroid.LONG)}> + Click me too. + + + + ); + }, +}); + +var styles = StyleSheet.create({ + text: { + color: 'black', + }, +}); + +module.exports = ToastExample; diff --git a/Examples/UIExplorer/ToolbarAndroidExample.android.js b/Examples/UIExplorer/ToolbarAndroidExample.android.js new file mode 100644 index 000000000..769737a33 --- /dev/null +++ b/Examples/UIExplorer/ToolbarAndroidExample.android.js @@ -0,0 +1,133 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * @flow + */ +'use strict'; + +var React = require('react-native'); +var { + StyleSheet, + Text, + View, +} = React; +var UIExplorerBlock = require('./UIExplorerBlock'); +var UIExplorerPage = require('./UIExplorerPage'); + +var SwitchAndroid = require('SwitchAndroid'); +var ToolbarAndroid = require('ToolbarAndroid'); + +var ToolbarAndroidExample = React.createClass({ + statics: { + title: '', + description: 'Examples of using the Android toolbar.' + }, + getInitialState: function() { + return { + actionText: 'Example app with toolbar component', + toolbarSwitch: false, + colorProps: { + titleColor: '#3b5998', + subtitleColor: '#6a7180', + }, + }; + }, + render: function() { + return ( + + + this.setState({actionText: 'Icon clicked'})} + style={styles.toolbar} + subtitle={this.state.actionText} + title="Toolbar" /> + {this.state.actionText} + + + + + this.setState({'toolbarSwitch': value})} /> + {'\'Tis but a switch'} + + + + + + + + + + + this.setState({colorProps: {}})} + title="Wow, such toolbar" + style={styles.toolbar} + subtitle="Much native" + {...this.state.colorProps} /> + + Touch the icon to reset the custom colors to the default (theme-provided) ones. + + + + + + + + + + ); + }, + _onActionSelected: function(position) { + this.setState({ + actionText: 'Selected ' + toolbarActions[position].title, + }); + }, +}); + +var toolbarActions = [ + {title: 'Create', icon: require('image!ic_create_black_48dp'), show: 'always'}, + {title: 'Filter'}, + {title: 'Settings', icon: require('image!ic_settings_black_48dp'), show: 'always'}, +]; + +var styles = StyleSheet.create({ + toolbar: { + backgroundColor: '#e9eaed', + height: 56, + }, +}); + +module.exports = ToolbarAndroidExample; diff --git a/Examples/UIExplorer/TouchableExample.js b/Examples/UIExplorer/TouchableExample.js index ba5294412..7b0bf2c0b 100644 --- a/Examples/UIExplorer/TouchableExample.js +++ b/Examples/UIExplorer/TouchableExample.js @@ -27,7 +27,7 @@ var { } = React; exports.displayName = (undefined: ?string); -exports.description = 'Touchable and onPress examples'; +exports.description = 'Touchable and onPress examples.'; exports.title = ' and onPress'; exports.examples = [ { @@ -229,7 +229,7 @@ var styles = StyleSheet.create({ logBox: { padding: 20, margin: 10, - borderWidth: 1 / PixelRatio.get(), + borderWidth: StyleSheet.hairlineWidth, borderColor: '#f0f0f0', backgroundColor: '#f9f9f9', }, @@ -237,7 +237,7 @@ var styles = StyleSheet.create({ padding: 10, margin: 10, height: 120, - borderWidth: 1 / PixelRatio.get(), + borderWidth: StyleSheet.hairlineWidth, borderColor: '#f0f0f0', backgroundColor: '#f9f9f9', }, diff --git a/Examples/UIExplorer/TransformExample.js b/Examples/UIExplorer/TransformExample.js index 7da9eb5ac..01a2813d4 100644 --- a/Examples/UIExplorer/TransformExample.js +++ b/Examples/UIExplorer/TransformExample.js @@ -10,6 +10,7 @@ * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * @flow */ 'use strict'; diff --git a/Examples/UIExplorer/TransparentHitTestExample.js b/Examples/UIExplorer/TransparentHitTestExample.js new file mode 100644 index 000000000..755582da3 --- /dev/null +++ b/Examples/UIExplorer/TransparentHitTestExample.js @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @flow + */ + +'use strict'; + +var React = require('react-native'); +var { + Text, + View, + TouchableOpacity, +} = React; + +var TransparentHitTestExample = React.createClass({ + render: function() { + return ( + + alert('Hi!')}> + HELLO! + + + + + ); + }, +}); + +exports.title = ''; +exports.displayName = 'TransparentHitTestExample'; +exports.description = 'Transparent view receiving touch events'; +exports.examples = [ + { + title: 'TransparentHitTestExample', + render(): ReactElement { return ; } + } +]; diff --git a/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj b/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj index 9ec430af5..e6dfc3658 100644 --- a/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj +++ b/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj @@ -8,32 +8,36 @@ /* Begin PBXBuildFile section */ 1300627F1B59179B0043FE5A /* RCTGzipTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1300627E1B59179B0043FE5A /* RCTGzipTests.m */; }; + 1323F1891C04AB9F0091BED0 /* bunny.png in Resources */ = {isa = PBXBuildFile; fileRef = 1323F1851C04AB9F0091BED0 /* bunny.png */; }; + 1323F18A1C04AB9F0091BED0 /* flux@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 1323F1861C04AB9F0091BED0 /* flux@3x.png */; }; + 1323F18B1C04AB9F0091BED0 /* hawk.png in Resources */ = {isa = PBXBuildFile; fileRef = 1323F1871C04AB9F0091BED0 /* hawk.png */; }; + 1323F18C1C04AB9F0091BED0 /* uie_thumb_big.png in Resources */ = {isa = PBXBuildFile; fileRef = 1323F1881C04AB9F0091BED0 /* uie_thumb_big.png */; }; + 1323F18F1C04ABEB0091BED0 /* Thumbnails in Resources */ = {isa = PBXBuildFile; fileRef = 1323F18E1C04ABEB0091BED0 /* Thumbnails */; }; 13417FE91AA91432003F314A /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 13417FE81AA91428003F314A /* libRCTImage.a */; }; 134180011AA9153C003F314A /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 13417FEF1AA914B8003F314A /* libRCTText.a */; }; 1341802C1AA9178B003F314A /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1341802B1AA91779003F314A /* libRCTNetwork.a */; }; 134454601AAFCABD003F0779 /* libRCTAdSupport.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1344545A1AAFCAAE003F0779 /* libRCTAdSupport.a */; }; 134A8A2A1AACED7A00945AAE /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 134A8A251AACED6A00945AAE /* libRCTGeolocation.a */; }; - 138D6A171B53CD440074A87E /* RCTCacheTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 138D6A151B53CD440074A87E /* RCTCacheTests.m */; }; 138D6A181B53CD440074A87E /* RCTShadowViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 138D6A161B53CD440074A87E /* RCTShadowViewTests.m */; }; + 138DEE241B9EDFB6007F4EA5 /* libRCTCameraRoll.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 138DEE091B9EDDDB007F4EA5 /* libRCTCameraRoll.a */; }; 1393D0381B68CD1300E1B601 /* RCTModuleMethodTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1393D0371B68CD1300E1B601 /* RCTModuleMethodTests.m */; }; 139FDEDB1B0651FB00C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDED91B0651EA00C62182 /* libRCTWebSocket.a */; }; 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; + 13B6C1A31C34225900D3FAF5 /* RCTURLUtilsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B6C1A21C34225900D3FAF5 /* RCTURLUtilsTests.m */; }; 13DB03481B5D2ED500C27245 /* RCTJSONTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 13DB03471B5D2ED500C27245 /* RCTJSONTests.m */; }; 13DF61B61B67A45000EDB188 /* RCTMethodArgumentTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 13DF61B51B67A45000EDB188 /* RCTMethodArgumentTests.m */; }; - 141FC1211B222EBB004D5FFB /* IntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 141FC1201B222EBB004D5FFB /* IntegrationTests.m */; }; 143BC5A11B21E45C00462512 /* UIExplorerSnapshotTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 143BC5A01B21E45C00462512 /* UIExplorerSnapshotTests.m */; }; 144D21241B2204C5006DB32B /* RCTImageUtilTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 144D21231B2204C5006DB32B /* RCTImageUtilTests.m */; }; 147CED4C1AB3532B00DA3E4C /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 147CED4B1AB34F8C00DA3E4C /* libRCTActionSheet.a */; }; 1497CFAC1B21F5E400C1F8F2 /* RCTAllocationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1497CFA41B21F5E400C1F8F2 /* RCTAllocationTests.m */; }; 1497CFAD1B21F5E400C1F8F2 /* RCTBridgeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1497CFA51B21F5E400C1F8F2 /* RCTBridgeTests.m */; }; - 1497CFAE1B21F5E400C1F8F2 /* RCTContextExecutorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1497CFA61B21F5E400C1F8F2 /* RCTContextExecutorTests.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 1497CFAE1B21F5E400C1F8F2 /* RCTJSCExecutorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1497CFA61B21F5E400C1F8F2 /* RCTJSCExecutorTests.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; 1497CFAF1B21F5E400C1F8F2 /* RCTConvert_NSURLTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1497CFA71B21F5E400C1F8F2 /* RCTConvert_NSURLTests.m */; }; 1497CFB01B21F5E400C1F8F2 /* RCTConvert_UIFontTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1497CFA81B21F5E400C1F8F2 /* RCTConvert_UIFontTests.m */; }; 1497CFB11B21F5E400C1F8F2 /* RCTEventDispatcherTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1497CFA91B21F5E400C1F8F2 /* RCTEventDispatcherTests.m */; }; - 1497CFB21B21F5E400C1F8F2 /* RCTSparseArrayTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1497CFAA1B21F5E400C1F8F2 /* RCTSparseArrayTests.m */; }; 1497CFB31B21F5E400C1F8F2 /* RCTUIManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1497CFAB1B21F5E400C1F8F2 /* RCTUIManagerTests.m */; }; 14AADF051AC3DBB1002390C9 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14AADF041AC3DB95002390C9 /* libReact.a */; }; 14B6DA821B276C5900BF4DD1 /* libRCTTest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58005BEE1ABA80530062E044 /* libRCTTest.a */; }; @@ -51,12 +55,15 @@ 14D6D7281B2222EF001FB087 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDED91B0651EA00C62182 /* libRCTWebSocket.a */; }; 14D6D7291B2222EF001FB087 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14AADF041AC3DB95002390C9 /* libReact.a */; }; 14DC67F41AB71881001358AB /* libRCTPushNotification.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14DC67F11AB71876001358AB /* libRCTPushNotification.a */; }; + 272E6B3F1BEA849E001FCF37 /* UpdatePropertiesExampleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 272E6B3C1BEA849E001FCF37 /* UpdatePropertiesExampleView.m */; }; + 27B885561BED29AF00008352 /* RCTRootViewIntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 27B885551BED29AF00008352 /* RCTRootViewIntegrationTests.m */; }; + 27F441EC1BEBE5030039B79C /* FlexibleSizeExampleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 27F441E81BEBE5030039B79C /* FlexibleSizeExampleView.m */; }; 3578590A1B28D2CF00341EDB /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 357859011B28D2C500341EDB /* libRCTLinking.a */; }; + 3DB99D0C1BA0340600302749 /* UIExplorerIntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3DB99D0B1BA0340600302749 /* UIExplorerIntegrationTests.m */; }; 834C36EC1AF8DED70019C93C /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 834C36D21AF8DA610019C93C /* libRCTSettings.a */; }; 83636F8F1B53F22C009F943E /* RCTUIManagerScenarioTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 83636F8E1B53F22C009F943E /* RCTUIManagerScenarioTests.m */; }; 8385CEF51B873B5C00C6273E /* RCTImageLoaderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8385CEF41B873B5C00C6273E /* RCTImageLoaderTests.m */; }; 8385CF041B87479200C6273E /* RCTImageLoaderHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 8385CF031B87479200C6273E /* RCTImageLoaderHelpers.m */; }; - 83A936C81B7E0F08005B9C36 /* RCTConvert_UIColorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 83A936C71B7E0F08005B9C36 /* RCTConvert_UIColorTests.m */; }; D85B829E1AB6D5D7003F4FE2 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D85B829C1AB6D5CE003F4FE2 /* libRCTVibration.a */; }; /* End PBXBuildFile section */ @@ -96,6 +103,13 @@ remoteGlobalIDString = 134814201AA4EA6300B7C361; remoteInfo = RCTGeolocation; }; + 138DEE081B9EDDDB007F4EA5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 138DEE021B9EDDDB007F4EA5 /* RCTCameraRoll.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 58B5115D1A9E6B3D00147676; + remoteInfo = RCTImage; + }; 139FDED81B0651EA00C62182 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 139FDECA1B0651EA00C62182 /* RCTWebSocket.xcodeproj */; @@ -164,13 +178,18 @@ /* Begin PBXFileReference section */ 004D289E1AAF61C70097A701 /* UIExplorerUnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UIExplorerUnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 1300627E1B59179B0043FE5A /* RCTGzipTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTGzipTests.m; sourceTree = ""; }; + 1323F1851C04AB9F0091BED0 /* bunny.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = bunny.png; sourceTree = ""; }; + 1323F1861C04AB9F0091BED0 /* flux@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "flux@3x.png"; sourceTree = ""; }; + 1323F1871C04AB9F0091BED0 /* hawk.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = hawk.png; sourceTree = ""; }; + 1323F1881C04AB9F0091BED0 /* uie_thumb_big.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = uie_thumb_big.png; sourceTree = ""; }; + 1323F18E1C04ABEB0091BED0 /* Thumbnails */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Thumbnails; sourceTree = ""; }; 13417FE31AA91428003F314A /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = ../../Libraries/Image/RCTImage.xcodeproj; sourceTree = ""; }; 13417FEA1AA914B8003F314A /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = ../../Libraries/Text/RCTText.xcodeproj; sourceTree = ""; }; 134180261AA91779003F314A /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = ../../Libraries/Network/RCTNetwork.xcodeproj; sourceTree = ""; }; 134454551AAFCAAE003F0779 /* RCTAdSupport.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAdSupport.xcodeproj; path = ../../Libraries/AdSupport/RCTAdSupport.xcodeproj; sourceTree = ""; }; 134A8A201AACED6A00945AAE /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = ../../Libraries/Geolocation/RCTGeolocation.xcodeproj; sourceTree = ""; }; - 138D6A151B53CD440074A87E /* RCTCacheTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTCacheTests.m; sourceTree = ""; }; 138D6A161B53CD440074A87E /* RCTShadowViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTShadowViewTests.m; sourceTree = ""; }; + 138DEE021B9EDDDB007F4EA5 /* RCTCameraRoll.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTCameraRoll.xcodeproj; path = ../../Libraries/CameraRoll/RCTCameraRoll.xcodeproj; sourceTree = ""; }; 1393D0371B68CD1300E1B601 /* RCTModuleMethodTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTModuleMethodTests.m; sourceTree = ""; }; 139FDECA1B0651EA00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = ../../Libraries/WebSocket/RCTWebSocket.xcodeproj; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* UIExplorer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = UIExplorer.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -180,10 +199,10 @@ 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = UIExplorer/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = UIExplorer/Info.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = UIExplorer/main.m; sourceTree = ""; }; + 13B6C1A21C34225900D3FAF5 /* RCTURLUtilsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTURLUtilsTests.m; sourceTree = ""; }; 13CC9D481AEED2B90020D1C2 /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = ../../Libraries/Settings/RCTSettings.xcodeproj; sourceTree = ""; }; 13DB03471B5D2ED500C27245 /* RCTJSONTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTJSONTests.m; sourceTree = ""; }; 13DF61B51B67A45000EDB188 /* RCTMethodArgumentTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMethodArgumentTests.m; sourceTree = ""; }; - 141FC1201B222EBB004D5FFB /* IntegrationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IntegrationTests.m; sourceTree = ""; }; 143BC57E1B21E18100462512 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 143BC5811B21E18100462512 /* testLayoutExampleSnapshot_1@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "testLayoutExampleSnapshot_1@2x.png"; sourceTree = ""; }; 143BC5821B21E18100462512 /* testSliderExampleSnapshot_1@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "testSliderExampleSnapshot_1@2x.png"; sourceTree = ""; }; @@ -197,11 +216,10 @@ 144D21231B2204C5006DB32B /* RCTImageUtilTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageUtilTests.m; sourceTree = ""; }; 1497CFA41B21F5E400C1F8F2 /* RCTAllocationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAllocationTests.m; sourceTree = ""; }; 1497CFA51B21F5E400C1F8F2 /* RCTBridgeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBridgeTests.m; sourceTree = ""; }; - 1497CFA61B21F5E400C1F8F2 /* RCTContextExecutorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTContextExecutorTests.m; sourceTree = ""; }; + 1497CFA61B21F5E400C1F8F2 /* RCTJSCExecutorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTJSCExecutorTests.m; sourceTree = ""; }; 1497CFA71B21F5E400C1F8F2 /* RCTConvert_NSURLTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTConvert_NSURLTests.m; sourceTree = ""; }; 1497CFA81B21F5E400C1F8F2 /* RCTConvert_UIFontTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTConvert_UIFontTests.m; sourceTree = ""; }; 1497CFA91B21F5E400C1F8F2 /* RCTEventDispatcherTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTEventDispatcherTests.m; sourceTree = ""; }; - 1497CFAA1B21F5E400C1F8F2 /* RCTSparseArrayTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSparseArrayTests.m; sourceTree = ""; }; 1497CFAB1B21F5E400C1F8F2 /* RCTUIManagerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTUIManagerTests.m; sourceTree = ""; }; 14AADEFF1AC3DB95002390C9 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = ../../React/React.xcodeproj; sourceTree = ""; }; 14D6D7021B220AE3001FB087 /* NSNotificationCenter+OCMAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNotificationCenter+OCMAdditions.h"; sourceTree = ""; }; @@ -216,13 +234,18 @@ 14D6D7101B220EB3001FB087 /* libOCMock.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libOCMock.a; sourceTree = ""; }; 14DC67E71AB71876001358AB /* RCTPushNotification.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTPushNotification.xcodeproj; path = ../../Libraries/PushNotificationIOS/RCTPushNotification.xcodeproj; sourceTree = ""; }; 14E0EEC81AB118F7000DECC3 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = ../../Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj; sourceTree = ""; }; + 272E6B3B1BEA849E001FCF37 /* UpdatePropertiesExampleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UpdatePropertiesExampleView.h; path = UIExplorer/NativeExampleViews/UpdatePropertiesExampleView.h; sourceTree = ""; }; + 272E6B3C1BEA849E001FCF37 /* UpdatePropertiesExampleView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = UpdatePropertiesExampleView.m; path = UIExplorer/NativeExampleViews/UpdatePropertiesExampleView.m; sourceTree = ""; }; + 27B885551BED29AF00008352 /* RCTRootViewIntegrationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTRootViewIntegrationTests.m; sourceTree = ""; }; + 27F441E81BEBE5030039B79C /* FlexibleSizeExampleView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FlexibleSizeExampleView.m; path = UIExplorer/NativeExampleViews/FlexibleSizeExampleView.m; sourceTree = ""; }; + 27F441EA1BEBE5030039B79C /* FlexibleSizeExampleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FlexibleSizeExampleView.h; path = UIExplorer/NativeExampleViews/FlexibleSizeExampleView.h; sourceTree = ""; }; 357858F81B28D2C400341EDB /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = ../../Libraries/LinkingIOS/RCTLinking.xcodeproj; sourceTree = ""; }; + 3DB99D0B1BA0340600302749 /* UIExplorerIntegrationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIExplorerIntegrationTests.m; sourceTree = ""; }; 58005BE41ABA80530062E044 /* RCTTest.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTTest.xcodeproj; path = ../../Libraries/RCTTest/RCTTest.xcodeproj; sourceTree = ""; }; 83636F8E1B53F22C009F943E /* RCTUIManagerScenarioTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTUIManagerScenarioTests.m; sourceTree = ""; }; 8385CEF41B873B5C00C6273E /* RCTImageLoaderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageLoaderTests.m; sourceTree = ""; }; 8385CF031B87479200C6273E /* RCTImageLoaderHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageLoaderHelpers.m; sourceTree = ""; }; 8385CF051B8747A000C6273E /* RCTImageLoaderHelpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTImageLoaderHelpers.h; sourceTree = ""; }; - 83A936C71B7E0F08005B9C36 /* RCTConvert_UIColorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTConvert_UIColorTests.m; sourceTree = ""; }; D85B82911AB6D5CE003F4FE2 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = ../../Libraries/Vibration/RCTVibration.xcodeproj; sourceTree = ""; }; /* End PBXFileReference section */ @@ -254,6 +277,7 @@ 14AADF051AC3DBB1002390C9 /* libReact.a in Frameworks */, 147CED4C1AB3532B00DA3E4C /* libRCTActionSheet.a in Frameworks */, 134454601AAFCABD003F0779 /* libRCTAdSupport.a in Frameworks */, + 138DEE241B9EDFB6007F4EA5 /* libRCTCameraRoll.a in Frameworks */, 134A8A2A1AACED7A00945AAE /* libRCTGeolocation.a in Frameworks */, 13417FE91AA91432003F314A /* libRCTImage.a in Frameworks */, 3578590A1B28D2CF00341EDB /* libRCTLinking.a in Frameworks */, @@ -281,13 +305,14 @@ isa = PBXGroup; children = ( 14AADEFF1AC3DB95002390C9 /* React.xcodeproj */, - 14DC67E71AB71876001358AB /* RCTPushNotification.xcodeproj */, 14E0EEC81AB118F7000DECC3 /* RCTActionSheet.xcodeproj */, 134454551AAFCAAE003F0779 /* RCTAdSupport.xcodeproj */, + 138DEE021B9EDDDB007F4EA5 /* RCTCameraRoll.xcodeproj */, 134A8A201AACED6A00945AAE /* RCTGeolocation.xcodeproj */, 13417FE31AA91428003F314A /* RCTImage.xcodeproj */, 357858F81B28D2C400341EDB /* RCTLinking.xcodeproj */, 134180261AA91779003F314A /* RCTNetwork.xcodeproj */, + 14DC67E71AB71876001358AB /* RCTPushNotification.xcodeproj */, 13CC9D481AEED2B90020D1C2 /* RCTSettings.xcodeproj */, 58005BE41ABA80530062E044 /* RCTTest.xcodeproj */, 13417FEA1AA914B8003F314A /* RCTText.xcodeproj */, @@ -297,6 +322,18 @@ name = Libraries; sourceTree = ""; }; + 1323F18D1C04ABAC0091BED0 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 13B07FB61A68108700A75B9A /* Info.plist */, + 1323F1851C04AB9F0091BED0 /* bunny.png */, + 1323F1861C04AB9F0091BED0 /* flux@3x.png */, + 1323F1871C04AB9F0091BED0 /* hawk.png */, + 1323F1881C04AB9F0091BED0 /* uie_thumb_big.png */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; 13417FE41AA91428003F314A /* Products */ = { isa = PBXGroup; children = ( @@ -337,6 +374,14 @@ name = Products; sourceTree = ""; }; + 138DEE031B9EDDDB007F4EA5 /* Products */ = { + isa = PBXGroup; + children = ( + 138DEE091B9EDDDB007F4EA5 /* libRCTCameraRoll.a */, + ); + name = Products; + sourceTree = ""; + }; 139FDECB1B0651EA00C62182 /* Products */ = { isa = PBXGroup; children = ( @@ -348,12 +393,14 @@ 13B07FAE1A68108700A75B9A /* UIExplorer */ = { isa = PBXGroup; children = ( + 272E6B3A1BEA846C001FCF37 /* NativeExampleViews */, 13B07FAF1A68108700A75B9A /* AppDelegate.h */, 13B07FB01A68108700A75B9A /* AppDelegate.m */, 13B07FB51A68108700A75B9A /* Images.xcassets */, - 13B07FB61A68108700A75B9A /* Info.plist */, 13B07FB11A68108700A75B9A /* LaunchScreen.xib */, 13B07FB71A68108700A75B9A /* main.m */, + 1323F18E1C04ABEB0091BED0 /* Thumbnails */, + 1323F18D1C04ABAC0091BED0 /* Supporting Files */, ); name = UIExplorer; sourceTree = ""; @@ -361,12 +408,11 @@ 143BC57C1B21E18100462512 /* UIExplorerUnitTests */ = { isa = PBXGroup; children = ( + 13B6C1A21C34225900D3FAF5 /* RCTURLUtilsTests.m */, 1497CFA41B21F5E400C1F8F2 /* RCTAllocationTests.m */, 1497CFA51B21F5E400C1F8F2 /* RCTBridgeTests.m */, - 138D6A151B53CD440074A87E /* RCTCacheTests.m */, - 1497CFA61B21F5E400C1F8F2 /* RCTContextExecutorTests.m */, + 1497CFA61B21F5E400C1F8F2 /* RCTJSCExecutorTests.m */, 1497CFA71B21F5E400C1F8F2 /* RCTConvert_NSURLTests.m */, - 83A936C71B7E0F08005B9C36 /* RCTConvert_UIColorTests.m */, 1497CFA81B21F5E400C1F8F2 /* RCTConvert_UIFontTests.m */, 1497CFA91B21F5E400C1F8F2 /* RCTEventDispatcherTests.m */, 1300627E1B59179B0043FE5A /* RCTGzipTests.m */, @@ -378,7 +424,6 @@ 13DF61B51B67A45000EDB188 /* RCTMethodArgumentTests.m */, 1393D0371B68CD1300E1B601 /* RCTModuleMethodTests.m */, 138D6A161B53CD440074A87E /* RCTShadowViewTests.m */, - 1497CFAA1B21F5E400C1F8F2 /* RCTSparseArrayTests.m */, 1497CFAB1B21F5E400C1F8F2 /* RCTUIManagerTests.m */, 143BC57E1B21E18100462512 /* Info.plist */, 14D6D7101B220EB3001FB087 /* libOCMock.a */, @@ -412,7 +457,8 @@ 143BC5961B21E3E100462512 /* UIExplorerIntegrationTests */ = { isa = PBXGroup; children = ( - 141FC1201B222EBB004D5FFB /* IntegrationTests.m */, + 27B885551BED29AF00008352 /* RCTRootViewIntegrationTests.m */, + 3DB99D0B1BA0340600302749 /* UIExplorerIntegrationTests.m */, 143BC5A01B21E45C00462512 /* UIExplorerSnapshotTests.m */, 83636F8E1B53F22C009F943E /* RCTUIManagerScenarioTests.m */, 143BC5971B21E3E100462512 /* Supporting Files */, @@ -475,6 +521,17 @@ name = Products; sourceTree = ""; }; + 272E6B3A1BEA846C001FCF37 /* NativeExampleViews */ = { + isa = PBXGroup; + children = ( + 27F441E81BEBE5030039B79C /* FlexibleSizeExampleView.m */, + 27F441EA1BEBE5030039B79C /* FlexibleSizeExampleView.h */, + 272E6B3B1BEA849E001FCF37 /* UpdatePropertiesExampleView.h */, + 272E6B3C1BEA849E001FCF37 /* UpdatePropertiesExampleView.m */, + ); + name = NativeExampleViews; + sourceTree = ""; + }; 357858F91B28D2C400341EDB /* Products */ = { isa = PBXGroup; children = ( @@ -624,6 +681,10 @@ ProductGroup = 134454561AAFCAAE003F0779 /* Products */; ProjectRef = 134454551AAFCAAE003F0779 /* RCTAdSupport.xcodeproj */; }, + { + ProductGroup = 138DEE031B9EDDDB007F4EA5 /* Products */; + ProjectRef = 138DEE021B9EDDDB007F4EA5 /* RCTCameraRoll.xcodeproj */; + }, { ProductGroup = 134A8A211AACED6A00945AAE /* Products */; ProjectRef = 134A8A201AACED6A00945AAE /* RCTGeolocation.xcodeproj */; @@ -714,6 +775,13 @@ remoteRef = 134A8A241AACED6A00945AAE /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 138DEE091B9EDDDB007F4EA5 /* libRCTCameraRoll.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTCameraRoll.a; + remoteRef = 138DEE081B9EDDDB007F4EA5 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 139FDED91B0651EA00C62182 /* libRCTWebSocket.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; @@ -784,8 +852,13 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 1323F18A1C04AB9F0091BED0 /* flux@3x.png in Resources */, + 1323F18F1C04ABEB0091BED0 /* Thumbnails in Resources */, + 1323F18B1C04AB9F0091BED0 /* hawk.png in Resources */, 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */, + 1323F1891C04AB9F0091BED0 /* bunny.png in Resources */, + 1323F18C1C04AB9F0091BED0 /* uie_thumb_big.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -806,19 +879,17 @@ 1497CFB01B21F5E400C1F8F2 /* RCTConvert_UIFontTests.m in Sources */, 144D21241B2204C5006DB32B /* RCTImageUtilTests.m in Sources */, 1393D0381B68CD1300E1B601 /* RCTModuleMethodTests.m in Sources */, - 1497CFB21B21F5E400C1F8F2 /* RCTSparseArrayTests.m in Sources */, 1300627F1B59179B0043FE5A /* RCTGzipTests.m in Sources */, 1497CFAF1B21F5E400C1F8F2 /* RCTConvert_NSURLTests.m in Sources */, - 1497CFAE1B21F5E400C1F8F2 /* RCTContextExecutorTests.m in Sources */, + 1497CFAE1B21F5E400C1F8F2 /* RCTJSCExecutorTests.m in Sources */, 1497CFAD1B21F5E400C1F8F2 /* RCTBridgeTests.m in Sources */, 1497CFB11B21F5E400C1F8F2 /* RCTEventDispatcherTests.m in Sources */, 1497CFB31B21F5E400C1F8F2 /* RCTUIManagerTests.m in Sources */, - 138D6A171B53CD440074A87E /* RCTCacheTests.m in Sources */, 13DB03481B5D2ED500C27245 /* RCTJSONTests.m in Sources */, 1497CFAC1B21F5E400C1F8F2 /* RCTAllocationTests.m in Sources */, - 83A936C81B7E0F08005B9C36 /* RCTConvert_UIColorTests.m in Sources */, 13DF61B61B67A45000EDB188 /* RCTMethodArgumentTests.m in Sources */, 138D6A181B53CD440074A87E /* RCTShadowViewTests.m in Sources */, + 13B6C1A31C34225900D3FAF5 /* RCTURLUtilsTests.m in Sources */, 8385CF041B87479200C6273E /* RCTImageLoaderHelpers.m in Sources */, 8385CEF51B873B5C00C6273E /* RCTImageLoaderTests.m in Sources */, ); @@ -828,7 +899,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 272E6B3F1BEA849E001FCF37 /* UpdatePropertiesExampleView.m in Sources */, 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */, + 27F441EC1BEBE5030039B79C /* FlexibleSizeExampleView.m in Sources */, 13B07FC11A68108700A75B9A /* main.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -837,9 +910,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 141FC1211B222EBB004D5FFB /* IntegrationTests.m in Sources */, + 3DB99D0C1BA0340600302749 /* UIExplorerIntegrationTests.m in Sources */, 83636F8F1B53F22C009F943E /* RCTUIManagerScenarioTests.m in Sources */, 143BC5A11B21E45C00462512 /* UIExplorerSnapshotTests.m in Sources */, + 27B885561BED29AF00008352 /* RCTRootViewIntegrationTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -944,6 +1018,13 @@ OTHER_LDFLAGS = "-ObjC"; PRODUCT_BUNDLE_IDENTIFIER = com.facebook.internal.uiexplorer.local; PRODUCT_NAME = UIExplorer; + TARGETED_DEVICE_FAMILY = "1,2"; + WARNING_CFLAGS = ( + "-Wextra", + "-Wall", + "-Wincompatible-pointer-types", + "-Wincompatible-pointer-types-discards-qualifiers", + ); }; name = Debug; }; @@ -963,6 +1044,13 @@ OTHER_LDFLAGS = "-ObjC"; PRODUCT_BUNDLE_IDENTIFIER = com.facebook.internal.uiexplorer.local; PRODUCT_NAME = UIExplorer; + TARGETED_DEVICE_FAMILY = "1,2"; + WARNING_CFLAGS = ( + "-Wextra", + "-Wall", + "-Wincompatible-pointer-types", + "-Wincompatible-pointer-types-discards-qualifiers", + ); }; name = Release; }; @@ -1054,6 +1142,7 @@ "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_MISSING_NEWLINE = YES; GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; @@ -1081,6 +1170,7 @@ WARNING_CFLAGS = ( "-Wextra", "-Wall", + "-Wincompatible-pointer-types", ); }; name = Debug; @@ -1111,6 +1201,7 @@ ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_MISSING_NEWLINE = YES; GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; @@ -1138,6 +1229,7 @@ WARNING_CFLAGS = ( "-Wextra", "-Wall", + "-Wincompatible-pointer-types", ); }; name = Release; diff --git a/Examples/UIExplorer/UIExplorer.xcodeproj/xcshareddata/xcschemes/UIExplorer.xcscheme b/Examples/UIExplorer/UIExplorer.xcodeproj/xcshareddata/xcschemes/UIExplorer.xcscheme index e2f84182e..52560f0d6 100644 --- a/Examples/UIExplorer/UIExplorer.xcodeproj/xcshareddata/xcschemes/UIExplorer.xcscheme +++ b/Examples/UIExplorer/UIExplorer.xcodeproj/xcshareddata/xcschemes/UIExplorer.xcscheme @@ -1,7 +1,7 @@ + version = "1.3"> @@ -51,10 +51,10 @@ + shouldUseLaunchSchemeArgsEnv = "YES"> @@ -90,11 +90,11 @@ diff --git a/Examples/UIExplorer/UIExplorer/AppDelegate.h b/Examples/UIExplorer/UIExplorer/AppDelegate.h index 55c38cc0b..f3e75417c 100644 --- a/Examples/UIExplorer/UIExplorer/AppDelegate.h +++ b/Examples/UIExplorer/UIExplorer/AppDelegate.h @@ -14,9 +14,11 @@ #import +@class RCTBridge; + @interface AppDelegate : UIResponder @property (nonatomic, strong) UIWindow *window; +@property (nonatomic, readonly) RCTBridge *bridge; @end - diff --git a/Examples/UIExplorer/UIExplorer/AppDelegate.m b/Examples/UIExplorer/UIExplorer/AppDelegate.m index 24ada2b56..577e6dd94 100644 --- a/Examples/UIExplorer/UIExplorer/AppDelegate.m +++ b/Examples/UIExplorer/UIExplorer/AppDelegate.m @@ -26,10 +26,10 @@ - (BOOL)application:(__unused UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self - launchOptions:launchOptions]; + _bridge = [[RCTBridge alloc] initWithDelegate:self + launchOptions:launchOptions]; - RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge + RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:_bridge moduleName:@"UIExplorerApp" initialProperties:nil]; @@ -59,14 +59,14 @@ * on the same Wi-Fi network. */ - sourceURL = [NSURL URLWithString:@"http://localhost:8081/Examples/UIExplorer/UIExplorerApp.ios.bundle?dev=true"]; + sourceURL = [NSURL URLWithString:@"http://localhost:8081/Examples/UIExplorer/UIExplorerApp.ios.bundle?platform=ios&dev=true"]; /** * OPTION 2 * Load from pre-bundled file on disk. To re-generate the static bundle, `cd` * to your Xcode project folder and run * - * $ curl 'http://localhost:8081/Examples/UIExplorer/UIExplorerApp.ios.bundle' -o main.jsbundle + * $ curl 'http://localhost:8081/Examples/UIExplorer/UIExplorerApp.ios.bundle?platform=ios' -o main.jsbundle * * then add the `main.jsbundle` file to your project and uncomment this line: */ diff --git a/Examples/UIExplorer/UIExplorer/Base.lproj/LaunchScreen.xib b/Examples/UIExplorer/UIExplorer/Base.lproj/LaunchScreen.xib index 3b7dcb4a0..0556e6641 100644 --- a/Examples/UIExplorer/UIExplorer/Base.lproj/LaunchScreen.xib +++ b/Examples/UIExplorer/UIExplorer/Base.lproj/LaunchScreen.xib @@ -1,7 +1,8 @@ - + - + + @@ -13,17 +14,20 @@ + diff --git a/Examples/UIExplorer/UIExplorer/Images.xcassets/Contents.json b/Examples/UIExplorer/UIExplorer/Images.xcassets/Contents.json new file mode 100644 index 000000000..da4a164c9 --- /dev/null +++ b/Examples/UIExplorer/UIExplorer/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Examples/UIExplorer/UIExplorer/Images.xcassets/blue_square.imageset/Contents.json b/Examples/UIExplorer/UIExplorer/Images.xcassets/blue_square.imageset/Contents.json new file mode 100644 index 000000000..5de78da51 --- /dev/null +++ b/Examples/UIExplorer/UIExplorer/Images.xcassets/blue_square.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "blue_square.png" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Examples/UIExplorer/UIExplorer/Images.xcassets/blue_square.imageset/blue_square.png b/Examples/UIExplorer/UIExplorer/Images.xcassets/blue_square.imageset/blue_square.png new file mode 100644 index 000000000..4974e084c Binary files /dev/null and b/Examples/UIExplorer/UIExplorer/Images.xcassets/blue_square.imageset/blue_square.png differ diff --git a/Examples/UIExplorer/UIExplorer/Images.xcassets/red_square.imageset/Contents.json b/Examples/UIExplorer/UIExplorer/Images.xcassets/red_square.imageset/Contents.json new file mode 100644 index 000000000..808156987 --- /dev/null +++ b/Examples/UIExplorer/UIExplorer/Images.xcassets/red_square.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "red_square.png" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Examples/UIExplorer/UIExplorer/Images.xcassets/red_square.imageset/red_square.png b/Examples/UIExplorer/UIExplorer/Images.xcassets/red_square.imageset/red_square.png new file mode 100644 index 000000000..0977a3e8c Binary files /dev/null and b/Examples/UIExplorer/UIExplorer/Images.xcassets/red_square.imageset/red_square.png differ diff --git a/Examples/UIExplorer/UIExplorer/Images.xcassets/uie_thumb_big.imageset/Contents.json b/Examples/UIExplorer/UIExplorer/Images.xcassets/uie_thumb_big.imageset/Contents.json deleted file mode 100644 index 589a2f493..000000000 --- a/Examples/UIExplorer/UIExplorer/Images.xcassets/uie_thumb_big.imageset/Contents.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x", - "filename" : "uie_thumb_big.png" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Examples/UIExplorer/UIExplorer/NativeExampleViews/FlexibleSizeExampleView.h b/Examples/UIExplorer/UIExplorer/NativeExampleViews/FlexibleSizeExampleView.h new file mode 100644 index 000000000..e4427fec5 --- /dev/null +++ b/Examples/UIExplorer/UIExplorer/NativeExampleViews/FlexibleSizeExampleView.h @@ -0,0 +1,20 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import +#import "RCTView.h" + +@interface FlexibleSizeExampleView : RCTView + +@end diff --git a/Examples/UIExplorer/UIExplorer/NativeExampleViews/FlexibleSizeExampleView.m b/Examples/UIExplorer/UIExplorer/NativeExampleViews/FlexibleSizeExampleView.m new file mode 100644 index 000000000..ce8992070 --- /dev/null +++ b/Examples/UIExplorer/UIExplorer/NativeExampleViews/FlexibleSizeExampleView.m @@ -0,0 +1,118 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import "AppDelegate.h" + +#import "FlexibleSizeExampleView.h" +#import "RCTJavaScriptLoader.h" +#import "RCTBridge.h" +#import "RCTRootView.h" +#import "RCTRootViewDelegate.h" + +#import "RCTViewManager.h" + +@interface FlexibleSizeExampleViewManager : RCTViewManager + +@end + +@implementation FlexibleSizeExampleViewManager + +RCT_EXPORT_MODULE(); + +- (UIView *)view +{ + return [FlexibleSizeExampleView new]; +} + +@end + + +@interface FlexibleSizeExampleView () + +@end + + +@implementation FlexibleSizeExampleView +{ + RCTRootView *_resizableRootView; + UITextView *_currentSizeTextView; + BOOL _sizeUpdated; +} + +- (instancetype)initWithFrame:(CGRect)frame +{ + if ((self = [super initWithFrame:frame])) { + _sizeUpdated = NO; + + AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate]; + + _resizableRootView = [[RCTRootView alloc] initWithBridge:appDelegate.bridge + moduleName:@"RootViewSizeFlexibilityExampleApp" + initialProperties:@{}]; + + [_resizableRootView setSizeFlexibility:RCTRootViewSizeFlexibilityHeight]; + + _currentSizeTextView = [UITextView new]; + _currentSizeTextView.editable = NO; + _currentSizeTextView.text = @"Resizable view has not been resized yet"; + _currentSizeTextView.textColor = [UIColor blackColor]; + _currentSizeTextView.backgroundColor = [UIColor whiteColor]; + _currentSizeTextView.font = [UIFont boldSystemFontOfSize:10]; + + _resizableRootView.delegate = self; + + [self addSubview:_currentSizeTextView]; + [self addSubview:_resizableRootView]; + } + return self; +} + +- (void)layoutSubviews +{ + float textViewHeight = 60; + float spacingHeight = 10; + [_resizableRootView setFrame:CGRectMake(0, textViewHeight + spacingHeight, self.frame.size.width, _resizableRootView.frame.size.height)]; + [_currentSizeTextView setFrame:CGRectMake(0, 0, self.frame.size.width, textViewHeight)]; +} + + +- (NSArray *> *)reactSubviews +{ + // this is to avoid unregistering our RCTRootView when the component is removed from RN hierarchy + return @[]; +} + + +#pragma mark - RCTRootViewDelegate + +- (void)rootViewDidChangeIntrinsicSize:(RCTRootView *)rootView +{ + CGRect newFrame = rootView.frame; + newFrame.size = rootView.intrinsicSize; + + if (!_sizeUpdated) { + _sizeUpdated = TRUE; + _currentSizeTextView.text = [NSString stringWithFormat:@"RCTRootViewDelegate: content with initially unknown size has appeared, updating root view's size so the content fits."]; + + } else { + _currentSizeTextView.text = [NSString stringWithFormat:@"RCTRootViewDelegate: content size has been changed to (%ld, %ld), updating root view's size.", + (long)newFrame.size.width, + (long)newFrame.size.height]; + + } + + rootView.frame = newFrame; +} + +@end diff --git a/Examples/UIExplorer/UIExplorer/NativeExampleViews/UpdatePropertiesExampleView.h b/Examples/UIExplorer/UIExplorer/NativeExampleViews/UpdatePropertiesExampleView.h new file mode 100644 index 000000000..6f769f249 --- /dev/null +++ b/Examples/UIExplorer/UIExplorer/NativeExampleViews/UpdatePropertiesExampleView.h @@ -0,0 +1,20 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import +#import "RCTView.h" + +@interface UpdatePropertiesExampleView : RCTView + +@end diff --git a/Examples/UIExplorer/UIExplorer/NativeExampleViews/UpdatePropertiesExampleView.m b/Examples/UIExplorer/UIExplorer/NativeExampleViews/UpdatePropertiesExampleView.m new file mode 100644 index 000000000..cc78f6cff --- /dev/null +++ b/Examples/UIExplorer/UIExplorer/NativeExampleViews/UpdatePropertiesExampleView.m @@ -0,0 +1,95 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import "AppDelegate.h" + +#import "UpdatePropertiesExampleView.h" +#import "RCTJavaScriptLoader.h" +#import "RCTBridge.h" +#import "RCTRootView.h" +#import "RCTViewManager.h" + +@interface UpdatePropertiesExampleViewManager : RCTViewManager + +@end + +@implementation UpdatePropertiesExampleViewManager + +RCT_EXPORT_MODULE(); + +- (UIView *)view +{ + return [UpdatePropertiesExampleView new]; +} + +@end + +@implementation UpdatePropertiesExampleView +{ + RCTRootView *_rootView; + UIButton *_button; + BOOL _beige; +} + +- (instancetype)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + _beige = YES; + + AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate]; + + _rootView = [[RCTRootView alloc] initWithBridge:appDelegate.bridge + moduleName:@"SetPropertiesExampleApp" + initialProperties:@{@"color":@"beige"}]; + + _button = [UIButton buttonWithType:UIButtonTypeRoundedRect]; + [_button setTitle:@"Native Button" forState:UIControlStateNormal]; + [_button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; + [_button setBackgroundColor:[UIColor grayColor]]; + + [_button addTarget:self + action:@selector(changeColor) + forControlEvents:UIControlEventTouchUpInside]; + + [self addSubview:_button]; + [self addSubview:_rootView]; + } + return self; +} + +- (void)layoutSubviews +{ + float spaceHeight = 20; + float buttonHeight = 40; + float rootViewWidth = self.bounds.size.width; + float rootViewHeight = self.bounds.size.height - spaceHeight - buttonHeight; + + [_rootView setFrame:CGRectMake(0, 0, rootViewWidth, rootViewHeight)]; + [_button setFrame:CGRectMake(0, rootViewHeight + spaceHeight, rootViewWidth, buttonHeight)]; +} + +- (void)changeColor +{ + _beige = !_beige; + [_rootView setAppProperties:@{@"color":_beige ? @"beige" : @"purple"}]; +} + +- (NSArray *> *)reactSubviews +{ + // this is to avoid unregistering our RCTRootView when the component is removed from RN hierarchy + return @[]; +} + +@end diff --git a/Examples/UIExplorer/UIExplorerApp.android.js b/Examples/UIExplorer/UIExplorerApp.android.js index 154796cef..c9bd2418f 100644 --- a/Examples/UIExplorer/UIExplorerApp.android.js +++ b/Examples/UIExplorer/UIExplorerApp.android.js @@ -18,34 +18,42 @@ var React = require('react-native'); var { + AppRegistry, + BackAndroid, Dimensions, + DrawerLayoutAndroid, StyleSheet, + ToolbarAndroid, View, } = React; -var UIExplorerList = require('./UIExplorerList'); - -// TODO: these should be exposed by the 'react-native' module. -var DrawerLayoutAndroid = require('DrawerLayoutAndroid'); -var ToolbarAndroid = require('ToolbarAndroid'); +var UIExplorerList = require('./UIExplorerList.android'); var DRAWER_WIDTH_LEFT = 56; var UIExplorerApp = React.createClass({ - getInitialState: function() { return { - example: { - title: 'UIExplorer', - component: this._renderHome(), - }, + example: this._getUIExplorerHome(), }; }, + _getUIExplorerHome: function() { + return { + title: 'UIExplorer', + component: this._renderHome(), + }; + }, + + componentWillMount: function() { + BackAndroid.addEventListener('hardwareBackPress', this._handleBackButtonPress); + }, + render: function() { return ( { this.drawer = drawer; }} renderNavigationView={this._renderNavigationView}> {this._renderNavigation()} @@ -64,14 +72,11 @@ var UIExplorerApp = React.createClass({ onSelectExample: function(example) { this.drawer.closeDrawer(); - if (example.title === 'UIExplorer') { - example.component = this._renderHome(); + if (example.title === this._getUIExplorerHome().title) { + example = this._getUIExplorerHome(); } this.setState({ - example: { - title: example.title, - component: example.component, - }, + example: example, }); }, @@ -105,16 +110,16 @@ var UIExplorerApp = React.createClass({ ); }, + _handleBackButtonPress: function() { + if (this.state.example.title !== this._getUIExplorerHome().title) { + this.onSelectExample(this._getUIExplorerHome()); + return true; + } + return false; + }, }); var styles = StyleSheet.create({ - messageText: { - fontSize: 17, - fontWeight: '500', - padding: 15, - marginTop: 50, - marginLeft: 15, - }, container: { flex: 1, }, @@ -124,4 +129,6 @@ var styles = StyleSheet.create({ }, }); +AppRegistry.registerComponent('UIExplorerApp', () => UIExplorerApp); + module.exports = UIExplorerApp; diff --git a/Examples/UIExplorer/UIExplorerApp.ios.js b/Examples/UIExplorer/UIExplorerApp.ios.js index c2f4734e8..c8c4a834f 100644 --- a/Examples/UIExplorer/UIExplorerApp.ios.js +++ b/Examples/UIExplorer/UIExplorerApp.ios.js @@ -22,6 +22,9 @@ var { AppRegistry, NavigatorIOS, StyleSheet, + Text, + TouchableHighlight, + View } = React; var UIExplorerApp = React.createClass({ @@ -70,8 +73,76 @@ var styles = StyleSheet.create({ itemWrapper: { backgroundColor: '#eaeaea', }, + bigContainer: { + flex: 1, + height: 60, + backgroundColor: 'gray', + }, + smallContainer: { + flex: 1, + height: 40, + backgroundColor: 'gray', + }, + center: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + }, + whiteText: { + color: 'white', + } }); +var SetPropertiesExampleApp = React.createClass({ + + render: function() { + var wrapperStyle = { + backgroundColor: this.props.color, + flex: 1, + alignItems: 'center', + justifyContent: 'center' + }; + + return ( + + + Embedded React Native view + + + ); + }, +}); + +var RootViewSizeFlexibilityExampleApp = React.createClass({ + + getInitialState: function () { + return { toggled: false }; + }, + + _onPressButton: function() { + this.setState({ toggled: !this.state.toggled }); + }, + + render: function() { + var viewStyle = this.state.toggled ? styles.bigContainer : styles.smallContainer; + + return ( + + + + + React Native Button + + + + + ); + }, +}); + +AppRegistry.registerComponent('SetPropertiesExampleApp', () => SetPropertiesExampleApp); +AppRegistry.registerComponent('RootViewSizeFlexibilityExampleApp', () => RootViewSizeFlexibilityExampleApp); AppRegistry.registerComponent('UIExplorerApp', () => UIExplorerApp); +UIExplorerList.registerComponents(); module.exports = UIExplorerApp; diff --git a/Examples/UIExplorer/UIExplorerButton.js b/Examples/UIExplorer/UIExplorerButton.js new file mode 100644 index 000000000..21f8efc8c --- /dev/null +++ b/Examples/UIExplorer/UIExplorerButton.js @@ -0,0 +1,56 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * @flow + */ +'use strict'; + +var React = require('react-native'); +var { + StyleSheet, + Text, + TouchableHighlight, +} = React; + +var UIExplorerButton = React.createClass({ + propTypes: { + onPress: React.PropTypes.func, + }, + render: function() { + return ( + + + {this.props.children} + + + ); + }, +}); + +var styles = StyleSheet.create({ + button: { + borderColor: '#696969', + borderRadius: 8, + borderWidth: 1, + padding: 10, + margin: 5, + alignItems: 'center', + justifyContent: 'center', + backgroundColor: '#d3d3d3', + }, +}); + +module.exports = UIExplorerButton; diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/IntegrationTests.m b/Examples/UIExplorer/UIExplorerIntegrationTests/IntegrationTests.m deleted file mode 100644 index 267ed1409..000000000 --- a/Examples/UIExplorer/UIExplorerIntegrationTests/IntegrationTests.m +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -#import -#import - -#import - -#import "RCTAssert.h" - -@interface IntegrationTests : XCTestCase - -@end - -@implementation IntegrationTests -{ - RCTTestRunner *_runner; -} - -- (void)setUp -{ -#if __LP64__ - RCTAssert(NO, @"Tests should be run on 32-bit device simulators (e.g. iPhone 5)"); -#endif - - NSOperatingSystemVersion version = [NSProcessInfo processInfo].operatingSystemVersion; - RCTAssert(version.majorVersion == 8 || version.minorVersion >= 3, @"Tests should be run on iOS 8.3+, found %zd.%zd.%zd", version.majorVersion, version.minorVersion, version.patchVersion); - _runner = RCTInitRunnerForApp(@"Examples/UIExplorer/UIExplorerIntegrationTests/js/IntegrationTestsApp", nil); -} - -#pragma mark Logic Tests - -- (void)testTheTester -{ - [_runner runTest:_cmd module:@"IntegrationTestHarnessTest"]; -} - -- (void)testTheTester_waitOneFrame -{ - [_runner runTest:_cmd - module:@"IntegrationTestHarnessTest" - initialProps:@{@"waitOneFrame": @YES} - expectErrorBlock:nil]; -} - -- (void)testTheTester_ExpectError -{ - [_runner runTest:_cmd - module:@"IntegrationTestHarnessTest" - initialProps:@{@"shouldThrow": @YES} - expectErrorRegex:@"because shouldThrow"]; -} - -- (void)testTimers -{ - [_runner runTest:_cmd module:@"TimersTest"]; -} - -- (void)testAsyncStorage -{ - [_runner runTest:_cmd module:@"AsyncStorageTest"]; -} - -- (void)DISABLED_testLayoutEvents // #7149037 -{ - [_runner runTest:_cmd module:@"LayoutEventsTest"]; -} - -- (void)testAppEvents -{ - [_runner runTest:_cmd module:@"AppEventsTest"]; -} - -- (void)testPromises -{ - [_runner runTest:_cmd module:@"PromiseTest"]; -} - -#pragma mark Snapshot Tests - -- (void)testSimpleSnapshot -{ - // If tests have changes, set recordMode = YES below and re-run - _runner.recordMode = NO; - [_runner runTest:_cmd module:@"SimpleSnapshotTest"]; -} - -@end diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/RCTLoggingTests.m b/Examples/UIExplorer/UIExplorerIntegrationTests/RCTLoggingTests.m new file mode 100644 index 000000000..0f34cfb26 --- /dev/null +++ b/Examples/UIExplorer/UIExplorerIntegrationTests/RCTLoggingTests.m @@ -0,0 +1,109 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import + +#import "RCTAssert.h" +#import "RCTLog.h" +#import "RCTBridge.h" + +@interface RCTLoggingTests : XCTestCase + +@end + +@implementation RCTLoggingTests +{ + RCTBridge *_bridge; + + dispatch_semaphore_t _logSem; + RCTLogLevel _lastLogLevel; + RCTLogSource _lastLogSource; + NSString *_lastLogMessage; +} + +- (void)setUp +{ +#if RUNNING_ON_CI + NSURL *scriptURL = [[NSBundle bundleForClass:[RCTBridge class]] URLForResource:@"main" withExtension:@"jsbundle"]; + RCTAssert(scriptURL != nil, @"Could not locate main.jsBundle"); +#else + NSString *app = @"Examples/UIExplorer/UIExplorerIntegrationTests/js/IntegrationTestsApp"; + NSURL *scriptURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:8081/%@.bundle?platform=ios&dev=true", app]]; +#endif + + _bridge = [[RCTBridge alloc] initWithBundleURL:scriptURL moduleProvider:NULL launchOptions:nil]; + NSDate *date = [NSDate dateWithTimeIntervalSinceNow:5]; + while (date.timeIntervalSinceNow > 0 && _bridge.loading) { + [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + } + XCTAssertFalse(_bridge.loading); + + _logSem = dispatch_semaphore_create(0); + RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { + if (source == RCTLogSourceJavaScript) { + _lastLogLevel = level; + _lastLogSource = source; + _lastLogMessage = message; + dispatch_semaphore_signal(_logSem); + } + }); +} + +- (void)tearDown +{ + [_bridge invalidate]; + _bridge = nil; + + RCTSetLogFunction(RCTDefaultLogFunction); +} + +- (void)testLogging +{ + [_bridge enqueueJSCall:@"LoggingTestModule.logToConsole" args:@[@"Invoking console.log"]]; + dispatch_semaphore_wait(_logSem, DISPATCH_TIME_FOREVER); + + XCTAssertEqual(_lastLogLevel, RCTLogLevelInfo); + XCTAssertEqual(_lastLogSource, RCTLogSourceJavaScript); + XCTAssertEqualObjects(_lastLogMessage, @"Invoking console.log"); + + [_bridge enqueueJSCall:@"LoggingTestModule.warning" args:@[@"Generating warning"]]; + dispatch_semaphore_wait(_logSem, DISPATCH_TIME_FOREVER); + + XCTAssertEqual(_lastLogLevel, RCTLogLevelWarning); + XCTAssertEqual(_lastLogSource, RCTLogSourceJavaScript); + XCTAssertEqualObjects(_lastLogMessage, @"Warning: Generating warning"); + + [_bridge enqueueJSCall:@"LoggingTestModule.invariant" args:@[@"Invariant failed"]]; + dispatch_semaphore_wait(_logSem, DISPATCH_TIME_FOREVER); + + XCTAssertEqual(_lastLogLevel, RCTLogLevelError); + XCTAssertEqual(_lastLogSource, RCTLogSourceJavaScript); + XCTAssertEqualObjects(_lastLogMessage, @"Invariant failed"); + + [_bridge enqueueJSCall:@"LoggingTestModule.logErrorToConsole" args:@[@"Invoking console.error"]]; + dispatch_semaphore_wait(_logSem, DISPATCH_TIME_FOREVER); + + XCTAssertEqual(_lastLogLevel, RCTLogLevelError); + XCTAssertEqual(_lastLogSource, RCTLogSourceJavaScript); + XCTAssertEqualObjects(_lastLogMessage, @"Invoking console.error"); + + [_bridge enqueueJSCall:@"LoggingTestModule.throwError" args:@[@"Throwing an error"]]; + dispatch_semaphore_wait(_logSem, DISPATCH_TIME_FOREVER); + + XCTAssertEqual(_lastLogLevel, RCTLogLevelError); + XCTAssertEqual(_lastLogSource, RCTLogSourceJavaScript); + XCTAssertEqualObjects(_lastLogMessage, @"Throwing an error"); +} + +@end diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/RCTRootViewIntegrationTests.m b/Examples/UIExplorer/UIExplorerIntegrationTests/RCTRootViewIntegrationTests.m new file mode 100644 index 000000000..9e665b4e1 --- /dev/null +++ b/Examples/UIExplorer/UIExplorerIntegrationTests/RCTRootViewIntegrationTests.m @@ -0,0 +1,172 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +//vs + +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import +#import + +#import "RCTAssert.h" + +#import "RCTEventDispatcher.h" +#import "RCTRootView.h" +#import "RCTRootViewDelegate.h" + +#import + +#define RCT_TEST_DATA_CONFIGURATION_BLOCK(appName, testType, input, block) \ +- (void)test##appName##_##testType##_##input \ +{ \ + [_runner runTest:_cmd \ + module:@#appName \ + initialProps:@{@#input:@YES} \ +configurationBlock:block]; \ +} + +#define RCT_TEST_CONFIGURATION_BLOCK(appName, block) \ +- (void)test##appName \ +{ \ + [_runner runTest:_cmd \ + module:@#appName \ + initialProps:nil \ +configurationBlock:block]; \ +} + +#define RCTNone RCTRootViewSizeFlexibilityNone +#define RCTHeight RCTRootViewSizeFlexibilityHeight +#define RCTWidth RCTRootViewSizeFlexibilityWidth +#define RCTBoth RCTRootViewSizeFlexibilityWidthAndHeight + +typedef void (^ControlBlock)(RCTRootView*); + +@interface SizeFlexibilityTestDelegate : NSObject +@end + +@implementation SizeFlexibilityTestDelegate + +- (void)rootViewDidChangeIntrinsicSize:(RCTRootView *)rootView +{ + [rootView.bridge.eventDispatcher sendAppEventWithName:@"rootViewDidChangeIntrinsicSize" + body:@{@"width": @(rootView.intrinsicSize.width), + @"height": @(rootView.intrinsicSize.height)}]; +} + +@end + +static SizeFlexibilityTestDelegate *sizeFlexibilityDelegate() +{ + static SizeFlexibilityTestDelegate *delegate; + if (delegate == nil) { + delegate = [SizeFlexibilityTestDelegate new]; + } + + return delegate; +} + +static ControlBlock simpleSizeFlexibilityBlock(RCTRootViewSizeFlexibility sizeFlexibility) +{ + return ^(RCTRootView *rootView){ + rootView.delegate = sizeFlexibilityDelegate(); + rootView.sizeFlexibility = sizeFlexibility; + }; +} + +static ControlBlock multipleSizeFlexibilityUpdatesBlock(RCTRootViewSizeFlexibility finalSizeFlexibility) +{ + return ^(RCTRootView *rootView){ + + NSInteger arr[4] = {RCTNone, + RCTHeight, + RCTWidth, + RCTBoth}; + + rootView.delegate = sizeFlexibilityDelegate(); + + for (int i = 0; i < 4; ++i) { + if (arr[i] != finalSizeFlexibility) { + rootView.sizeFlexibility = arr[i]; + } + } + + rootView.sizeFlexibility = finalSizeFlexibility; + }; +} + +static ControlBlock reactContentSizeUpdateBlock(RCTRootViewSizeFlexibility sizeFlexibility) +{ + return ^(RCTRootView *rootView){ + rootView.delegate = sizeFlexibilityDelegate(); + rootView.sizeFlexibility = sizeFlexibility; + }; +} + +static ControlBlock propertiesUpdateBlock() +{ + return ^(RCTRootView *rootView){ + rootView.appProperties = @{@"markTestPassed":@YES}; + }; +} + +@interface RCTRootViewIntegrationTests : XCTestCase + +@end + +@implementation RCTRootViewIntegrationTests +{ + RCTTestRunner *_runner; +} + +- (void)setUp +{ +#if __LP64__ + RCTAssert(NO, @"Tests should be run on 32-bit device simulators (e.g. iPhone 5)"); +#endif + + NSOperatingSystemVersion version = [NSProcessInfo processInfo].operatingSystemVersion; + RCTAssert((version.majorVersion == 8 && version.minorVersion >= 3) || version.majorVersion >= 9, @"Tests should be run on iOS 8.3+, found %zd.%zd.%zd", version.majorVersion, version.minorVersion, version.patchVersion); + _runner = RCTInitRunnerForApp(@"IntegrationTests/RCTRootViewIntegrationTestApp", nil); +} + +#pragma mark Logic Tests + +// This list should be kept in sync with RCTRootViewIntegrationTestApp.js + +// Simple size flexibility tests - test if the content is measured properly +RCT_TEST_DATA_CONFIGURATION_BLOCK(SizeFlexibilityUpdateTest, SingleUpdate, none, simpleSizeFlexibilityBlock(RCTNone)); +RCT_TEST_DATA_CONFIGURATION_BLOCK(SizeFlexibilityUpdateTest, SingleUpdate, height, simpleSizeFlexibilityBlock(RCTHeight)); +RCT_TEST_DATA_CONFIGURATION_BLOCK(SizeFlexibilityUpdateTest, SingleUpdate, width, simpleSizeFlexibilityBlock(RCTWidth)); +RCT_TEST_DATA_CONFIGURATION_BLOCK(SizeFlexibilityUpdateTest, SingleUpdate, both, simpleSizeFlexibilityBlock(RCTBoth)); + +// Consider multiple size flexibility updates in a row. Test if the view's flexibility mode eventually is set to the expected value +RCT_TEST_DATA_CONFIGURATION_BLOCK(SizeFlexibilityUpdateTest, MultipleUpdates, none, multipleSizeFlexibilityUpdatesBlock(RCTNone)); +RCT_TEST_DATA_CONFIGURATION_BLOCK(SizeFlexibilityUpdateTest, MultipleUpdates, height, multipleSizeFlexibilityUpdatesBlock(RCTHeight)); +RCT_TEST_DATA_CONFIGURATION_BLOCK(SizeFlexibilityUpdateTest, MultipleUpdates, width, multipleSizeFlexibilityUpdatesBlock(RCTWidth)); +RCT_TEST_DATA_CONFIGURATION_BLOCK(SizeFlexibilityUpdateTest, MultipleUpdates, both, multipleSizeFlexibilityUpdatesBlock(RCTBoth)); + +// Test if the 'rootViewDidChangeIntrinsicSize' delegate method is called after the RN app decides internally to resize +RCT_TEST_CONFIGURATION_BLOCK(ReactContentSizeUpdateTest, reactContentSizeUpdateBlock(RCTBoth)) + +// Test if setting 'appProperties' property updates the RN app +RCT_TEST_CONFIGURATION_BLOCK(PropertiesUpdateTest, propertiesUpdateBlock()) + +@end diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/RCTUIManagerScenarioTests.m b/Examples/UIExplorer/UIExplorerIntegrationTests/RCTUIManagerScenarioTests.m index c30b8d1dc..520c9beda 100644 --- a/Examples/UIExplorer/UIExplorerIntegrationTests/RCTUIManagerScenarioTests.m +++ b/Examples/UIExplorer/UIExplorerIntegrationTests/RCTUIManagerScenarioTests.m @@ -12,10 +12,8 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - #import #import -#import "RCTSparseArray.h" #import "RCTUIManager.h" #import "UIView+React.h" @@ -27,9 +25,9 @@ addChildReactTags:(NSArray *)addChildReactTags addAtIndices:(NSArray *)addAtIndices removeAtIndices:(NSArray *)removeAtIndices - registry:(RCTSparseArray *)registry; + registry:(NSMutableDictionary> *)registry; -@property (nonatomic, readonly) RCTSparseArray *viewRegistry; +@property (nonatomic, readonly) NSMutableDictionary *viewRegistry; @end @@ -51,13 +49,13 @@ for (NSInteger i = 1; i <= 20; i++) { UIView *registeredView = [UIView new]; registeredView.reactTag = @(i); - _uiManager.viewRegistry[i] = registeredView; + _uiManager.viewRegistry[@(i)] = registeredView; } } - (void)testManagingChildrenToAddViews { - UIView *containerView = _uiManager.viewRegistry[20]; + UIView *containerView = _uiManager.viewRegistry[@20]; NSMutableArray *addedViews = [NSMutableArray array]; NSArray *tagsToAdd = @[@1, @2, @3, @4, @5]; @@ -73,7 +71,7 @@ addChildReactTags:tagsToAdd addAtIndices:addAtIndices removeAtIndices:nil - registry:_uiManager.viewRegistry]; + registry:(NSMutableDictionary> *)_uiManager.viewRegistry]; XCTAssertTrue([[containerView reactSubviews] count] == 5, @"Expect to have 5 react subviews after calling manage children \ @@ -87,7 +85,7 @@ - (void)testManagingChildrenToRemoveViews { - UIView *containerView = _uiManager.viewRegistry[20]; + UIView *containerView = _uiManager.viewRegistry[@20]; NSMutableArray *removedViews = [NSMutableArray array]; NSArray *removeAtIndices = @[@0, @4, @8, @12, @16]; @@ -96,7 +94,7 @@ [removedViews addObject:_uiManager.viewRegistry[reactTag]]; } for (NSInteger i = 2; i < 20; i++) { - UIView *view = _uiManager.viewRegistry[i]; + UIView *view = _uiManager.viewRegistry[@(i)]; [containerView addSubview:view]; } @@ -107,7 +105,7 @@ addChildReactTags:nil addAtIndices:nil removeAtIndices:removeAtIndices - registry:_uiManager.viewRegistry]; + registry:(NSMutableDictionary> *)_uiManager.viewRegistry]; XCTAssertEqual(containerView.reactSubviews.count, (NSUInteger)13, @"Expect to have 13 react subviews after calling manage children\ @@ -120,7 +118,7 @@ _uiManager.viewRegistry[view.reactTag] = view; } for (NSInteger i = 2; i < 20; i++) { - UIView *view = _uiManager.viewRegistry[i]; + UIView *view = _uiManager.viewRegistry[@(i)]; if (![removedViews containsObject:view]) { XCTAssertTrue([view superview] == containerView, @"Should not have removed view with react tag %ld during delete but did", (long)i); @@ -139,7 +137,7 @@ // [11,5,1,2,7,8,12,10] - (void)testManagingChildrenToAddRemoveAndMove { - UIView *containerView = _uiManager.viewRegistry[20]; + UIView *containerView = _uiManager.viewRegistry[@20]; NSArray *removeAtIndices = @[@2, @3, @5, @8]; NSArray *addAtIndices = @[@0, @6]; @@ -156,7 +154,7 @@ } for (NSInteger i = 1; i < 11; i++) { - UIView *view = _uiManager.viewRegistry[i]; + UIView *view = _uiManager.viewRegistry[@(i)]; [containerView addSubview:view]; } @@ -166,7 +164,7 @@ addChildReactTags:tagsToAdd addAtIndices:addAtIndices removeAtIndices:removeAtIndices - registry:_uiManager.viewRegistry]; + registry:(NSMutableDictionary> *)_uiManager.viewRegistry]; XCTAssertTrue([[containerView reactSubviews] count] == 8, @"Expect to have 8 react subviews after calling manage children,\ @@ -181,7 +179,7 @@ // Clean up after ourselves for (NSInteger i = 1; i < 13; i++) { - UIView *view = _uiManager.viewRegistry[i]; + UIView *view = _uiManager.viewRegistry[@(i)]; [view removeFromSuperview]; } for (UIView *view in viewsToRemove) { diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testLayoutExample_1@2x.png b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testLayoutExample_1@2x.png index ff9eaeae1..2c6573453 100644 Binary files a/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testLayoutExample_1@2x.png and b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testLayoutExample_1@2x.png differ diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testSliderExample_1@2x.png b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testSliderExample_1@2x.png index b08e761b6..45b9547a8 100644 Binary files a/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testSliderExample_1@2x.png and b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testSliderExample_1@2x.png differ diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testSwitchExample_1@2x.png b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testSwitchExample_1@2x.png index 7297dde2d..f57db959a 100644 Binary files a/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testSwitchExample_1@2x.png and b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testSwitchExample_1@2x.png differ diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testTabBarExample_1@2x.png b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testTabBarExample_1@2x.png index 62425fde9..cfde34a4a 100644 Binary files a/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testTabBarExample_1@2x.png and b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testTabBarExample_1@2x.png differ diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testTextExample_1@2x.png b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testTextExample_1@2x.png index 32449556d..71d984921 100644 Binary files a/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testTextExample_1@2x.png and b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testTextExample_1@2x.png differ diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testViewExample_1@2x.png b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testViewExample_1@2x.png index fb2ab9feb..bf417eb6f 100644 Binary files a/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testViewExample_1@2x.png and b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testViewExample_1@2x.png differ diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerIntegrationTests-js-IntegrationTestsApp/testSimpleSnapshot_1@2x.png b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerIntegrationTests-js-IntegrationTestsApp/testSimpleSnapshot_1@2x.png deleted file mode 100644 index fd91abf42..000000000 Binary files a/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerIntegrationTests-js-IntegrationTestsApp/testSimpleSnapshot_1@2x.png and /dev/null differ diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/IntegrationTests-IntegrationTestsApp/testImageSnapshotTest_1@2x.png b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/IntegrationTests-IntegrationTestsApp/testImageSnapshotTest_1@2x.png new file mode 100644 index 000000000..41492574c Binary files /dev/null and b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/IntegrationTests-IntegrationTestsApp/testImageSnapshotTest_1@2x.png differ diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/IntegrationTests-IntegrationTestsApp/testSimpleSnapshotTest_1@2x.png b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/IntegrationTests-IntegrationTestsApp/testSimpleSnapshotTest_1@2x.png new file mode 100644 index 000000000..ea37dc900 Binary files /dev/null and b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/IntegrationTests-IntegrationTestsApp/testSimpleSnapshotTest_1@2x.png differ diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/UIExplorerIntegrationTests.m b/Examples/UIExplorer/UIExplorerIntegrationTests/UIExplorerIntegrationTests.m new file mode 100644 index 000000000..54efba917 --- /dev/null +++ b/Examples/UIExplorer/UIExplorerIntegrationTests/UIExplorerIntegrationTests.m @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import +#import + +#import + +#import "RCTAssert.h" + +#define RCT_TEST(name) \ +- (void)test##name \ +{ \ + [_runner runTest:_cmd module:@#name]; \ +} + +@interface UIExplorerIntegrationTests : XCTestCase + +@end + +@implementation UIExplorerIntegrationTests +{ + RCTTestRunner *_runner; +} + +- (void)setUp +{ +#if __LP64__ + RCTAssert(NO, @"Tests should be run on 32-bit device simulators (e.g. iPhone 5)"); +#endif + + NSOperatingSystemVersion version = [NSProcessInfo processInfo].operatingSystemVersion; + RCTAssert((version.majorVersion == 8 && version.minorVersion >= 3) || version.majorVersion >= 9, @"Tests should be run on iOS 8.3+, found %zd.%zd.%zd", version.majorVersion, version.minorVersion, version.patchVersion); + _runner = RCTInitRunnerForApp(@"IntegrationTests/IntegrationTestsApp", nil); +} + +#pragma mark - Test harness + +- (void)testTheTester_waitOneFrame +{ + [_runner runTest:_cmd + module:@"IntegrationTestHarnessTest" + initialProps:@{@"waitOneFrame": @YES} +configurationBlock:nil]; +} + +- (void)testTheTester_ExpectError +{ + [_runner runTest:_cmd + module:@"IntegrationTestHarnessTest" + initialProps:@{@"shouldThrow": @YES} +configurationBlock:nil + expectErrorRegex:@"because shouldThrow"]; +} + +#pragma mark - JS tests + +// This list should be kept in sync with IntegrationTestsApp.js +RCT_TEST(IntegrationTestHarnessTest) +RCT_TEST(TimersTest) +RCT_TEST(AsyncStorageTest) +RCT_TEST(AppEventsTest) +//RCT_TEST(ImageSnapshotTest) // Disabled: #8985988 +//RCT_TEST(LayoutEventsTest) // Disabled due to flakiness: #8686784 +RCT_TEST(SimpleSnapshotTest) +RCT_TEST(PromiseTest) + + +@end diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/UIExplorerSnapshotTests.m b/Examples/UIExplorer/UIExplorerIntegrationTests/UIExplorerSnapshotTests.m index 1281a4796..81c11503c 100644 --- a/Examples/UIExplorer/UIExplorerIntegrationTests/UIExplorerSnapshotTests.m +++ b/Examples/UIExplorer/UIExplorerIntegrationTests/UIExplorerSnapshotTests.m @@ -37,7 +37,7 @@ #endif NSOperatingSystemVersion version = [NSProcessInfo processInfo].operatingSystemVersion; - RCTAssert(version.majorVersion == 8 || version.minorVersion >= 3, @"Snapshot tests should be run on iOS 8.3+, found %zd.%zd.%zd", version.majorVersion, version.minorVersion, version.patchVersion); + RCTAssert((version.majorVersion == 8 && version.minorVersion >= 3) || version.majorVersion >= 9, @"Tests should be run on iOS 8.3+, found %zd.%zd.%zd", version.majorVersion, version.minorVersion, version.patchVersion); _runner = RCTInitRunnerForApp(@"Examples/UIExplorer/UIExplorerApp.ios", nil); _runner.recordMode = NO; } @@ -52,8 +52,8 @@ RCT_TEST(ViewExample) RCT_TEST(LayoutExample) RCT_TEST(TextExample) RCT_TEST(SwitchExample) -RCT_TEST(SliderExample) -RCT_TEST(TabBarExample) +//RCT_TEST(SliderExample) // Disabled: #8985988 +//RCT_TEST(TabBarExample) // Disabled: #8985988 - (void)testZZZNotInRecordMode { diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/blue_square.png b/Examples/UIExplorer/UIExplorerIntegrationTests/blue_square.png new file mode 100644 index 000000000..4974e084c Binary files /dev/null and b/Examples/UIExplorer/UIExplorerIntegrationTests/blue_square.png differ diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/red_square.png b/Examples/UIExplorer/UIExplorerIntegrationTests/red_square.png new file mode 100644 index 000000000..0977a3e8c Binary files /dev/null and b/Examples/UIExplorer/UIExplorerIntegrationTests/red_square.png differ diff --git a/Examples/UIExplorer/UIExplorerList.android.js b/Examples/UIExplorer/UIExplorerList.android.js new file mode 100644 index 000000000..01e8a331e --- /dev/null +++ b/Examples/UIExplorer/UIExplorerList.android.js @@ -0,0 +1,112 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * @flow + */ +'use strict'; + +var React = require('react-native'); +var { + StyleSheet, + View, +} = React; +var UIExplorerListBase = require('./UIExplorerListBase'); + +var COMPONENTS = [ + require('./ImageExample'), + require('./ListViewExample'), + require('./ProgressBarAndroidExample'), + require('./ScrollViewSimpleExample'), + require('./SwitchExample'), + require('./RefreshControlExample'), + require('./PickerAndroidExample'), + require('./PullToRefreshViewAndroidExample.android'), + require('./TextExample.android'), + require('./TextInputExample.android'), + require('./ToolbarAndroidExample'), + require('./TouchableExample'), + require('./ViewExample'), + require('./ViewPagerAndroidExample.android'), + require('./WebViewExample'), +]; + +var APIS = [ + require('./AccessibilityAndroidExample.android'), + require('./AlertExample').AlertExample, + require('./AppStateExample'), + require('./BorderExample'), + require('./CameraRollExample'), + require('./ClipboardExample'), + require('./GeolocationExample'), + require('./IntentAndroidExample.android'), + require('./LayoutEventsExample'), + require('./LayoutExample'), + require('./NetInfoExample'), + require('./PanResponderExample'), + require('./PointerEventsExample'), + require('./TimerExample'), + require('./ToastAndroidExample.android'), + require('./XHRExample'), +]; + +type Props = { + onSelectExample: Function, + isInDrawer: bool, +}; + +class UIExplorerList extends React.Component { + props: Props; + + render() { + return ( + + ); + } + + renderAdditionalView(renderRow, renderTextInput): React.Component { + if (this.props.isInDrawer) { + var homePage = renderRow({ + title: 'UIExplorer', + description: 'List of examples', + }, -1); + return ( + + {homePage} + + ); + } + return renderTextInput(styles.searchTextInput); + } + + onPressRow(example: any) { + var Component = UIExplorerListBase.makeRenderable(example); + this.props.onSelectExample({ + title: Component.title, + component: Component, + }); + } +} + +var styles = StyleSheet.create({ + searchTextInput: { + padding: 2, + }, +}); + +module.exports = UIExplorerList; diff --git a/Examples/UIExplorer/UIExplorerList.ios.js b/Examples/UIExplorer/UIExplorerList.ios.js index d45f4ab70..f33d97c86 100644 --- a/Examples/UIExplorer/UIExplorerList.ios.js +++ b/Examples/UIExplorer/UIExplorerList.ios.js @@ -19,11 +19,10 @@ var React = require('react-native'); var { AppRegistry, Settings, + SnapshotViewIOS, StyleSheet, } = React; -var { TestModule } = React.addons; - import type { NavigationContext } from 'NavigationContext'; var UIExplorerListBase = require('./UIExplorerListBase'); @@ -43,14 +42,16 @@ var COMPONENTS = [ require('./NavigatorIOSExample'), require('./PickerIOSExample'), require('./ProgressViewIOSExample'), + require('./RefreshControlExample'), require('./ScrollViewExample'), require('./SegmentedControlIOSExample'), require('./SliderIOSExample'), - require('./SwitchIOSExample'), + require('./SwitchExample'), require('./TabBarIOSExample'), require('./TextExample.ios'), require('./TextInputExample.ios'), require('./TouchableExample'), + require('./TransparentHitTestExample'), require('./ViewExample'), require('./WebViewExample'), ]; @@ -60,45 +61,30 @@ var APIS = [ require('./ActionSheetIOSExample'), require('./AdSupportIOSExample'), require('./AlertIOSExample'), - require('./AnimationExample/AnExApp'), + require('./AnimatedExample'), + require('./AnimatedGratuitousApp/AnExApp'), require('./AppStateIOSExample'), + require('./AppStateExample'), require('./AsyncStorageExample'), require('./BorderExample'), - require('./CameraRollExample.ios'), + require('./BoxShadowExample'), + require('./CameraRollExample'), + require('./ClipboardExample'), require('./GeolocationExample'), require('./LayoutExample'), require('./NetInfoExample'), require('./PanResponderExample'), require('./PointerEventsExample'), require('./PushNotificationIOSExample'), + require('./RCTRootViewIOSExample'), require('./StatusBarIOSExample'), require('./TimerExample'), + require('./TransformExample'), require('./VibrationIOSExample'), require('./XHRExample.ios'), require('./ImageEditingExample'), ]; -// Register suitable examples for snapshot tests -COMPONENTS.concat(APIS).forEach((Example) => { - if (Example.displayName) { - var Snapshotter = React.createClass({ - componentDidMount: function() { - // View is still blank after first RAF :\ - global.requestAnimationFrame(() => - global.requestAnimationFrame(() => TestModule.verifySnapshot( - TestModule.markTestPassed - ) - )); - }, - render: function() { - var Renderable = UIExplorerListBase.makeRenderable(Example); - return ; - }, - }); - AppRegistry.registerComponent(Example.displayName, () => Snapshotter); - } -}); - type Props = { navigator: { navigationContext: NavigationContext, @@ -147,6 +133,25 @@ class UIExplorerList extends React.Component { onPressRow(example: any) { this._openExample(example); } + + // Register suitable examples for snapshot tests + static registerComponents() { + COMPONENTS.concat(APIS).forEach((Example) => { + if (Example.displayName) { + var Snapshotter = React.createClass({ + render: function() { + var Renderable = UIExplorerListBase.makeRenderable(Example); + return ( + + + + ); + }, + }); + AppRegistry.registerComponent(Example.displayName, () => Snapshotter); + } + }); + } } var styles = StyleSheet.create({ diff --git a/Examples/UIExplorer/UIExplorerListBase.js b/Examples/UIExplorer/UIExplorerListBase.js index 4b26a6976..17ecd8dc2 100644 --- a/Examples/UIExplorer/UIExplorerListBase.js +++ b/Examples/UIExplorer/UIExplorerListBase.js @@ -18,7 +18,6 @@ var React = require('react-native'); var { ListView, - PixelRatio, StyleSheet, Text, TextInput, @@ -40,7 +39,7 @@ class UIExplorerListBase extends React.Component { components: [], apis: [], }), - searchText: this.props.searchText, + searchText: this.props.searchText || '', }; } @@ -87,11 +86,9 @@ class UIExplorerListBase extends React.Component { _renderSectionHeader(data: any, section: string) { return ( - - - {section.toUpperCase()} - - + + {section.toUpperCase()} + ); } @@ -116,7 +113,7 @@ class UIExplorerListBase extends React.Component { search(text: mixed): void { this.props.search && this.props.search(text); - var regex = new RegExp(text, 'i'); + var regex = new RegExp(String(text), 'i'); var filter = (component) => regex.test(component.title); this.setState({ @@ -148,14 +145,12 @@ var styles = StyleSheet.create({ }, sectionHeader: { padding: 5, + fontWeight: '500', + fontSize: 11, }, group: { backgroundColor: 'white', }, - sectionHeaderTitle: { - fontWeight: '500', - fontSize: 11, - }, row: { backgroundColor: 'white', justifyContent: 'center', @@ -163,7 +158,7 @@ var styles = StyleSheet.create({ paddingVertical: 8, }, separator: { - height: 1 / PixelRatio.get(), + height: StyleSheet.hairlineWidth, backgroundColor: '#bbbbbb', marginLeft: 15, }, diff --git a/Examples/UIExplorer/UIExplorerUnitTests/LayoutSubviewsOrderingTest.m b/Examples/UIExplorer/UIExplorerUnitTests/LayoutSubviewsOrderingTest.m index c00eedcfd..39c0404e0 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/LayoutSubviewsOrderingTest.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/LayoutSubviewsOrderingTest.m @@ -14,16 +14,16 @@ /** * This test exists to insure that didLayoutSubviews is always called immediately after layoutSubviews for a VC:View * pair. In Catalyst we have multiple levels of ViewController containment, and we rely on this ordering - * to insure that layoutGuides are set on RKViewControllers before Views further down in the heirarchy have + * to insure that layoutGuides are set on RKViewControllers before Views further down in the hierarchy have * their layoutSubviews called (and need to use the aforementioned layoutGuides) */ - (void)testLayoutSubviewsOrdering { // create some Views and ViewControllers - UIViewController *parentVC = [[UIViewController alloc] init]; - UIView *parentView = [[UIView alloc] init]; - UIViewController *childVC = [[UIViewController alloc] init]; - UIView *childView = [[UIView alloc] init]; + UIViewController *parentVC = [UIViewController new]; + UIView *parentView = [UIView new]; + UIViewController *childVC = [UIViewController new]; + UIView *childView = [UIView new]; // The ordering we expect is: // parentView::layoutSubviews @@ -63,7 +63,7 @@ } }] viewDidLayoutSubviews]; - // setup View heirarchy and force layout + // setup View hierarchy and force layout parentVC.view = parentView; childVC.view = childView; [parentVC addChildViewController:childVC]; diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTAllocationTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTAllocationTests.m index 4fe7294fb..a52400ffc 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTAllocationTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTAllocationTests.m @@ -16,24 +16,18 @@ #import #import "RCTBridge.h" -#import "RCTContextExecutor.h" +#import "RCTBridge+Private.h" +#import "RCTJSCExecutor.h" #import "RCTModuleMethod.h" #import "RCTRootView.h" #define RUN_RUNLOOP_WHILE(CONDITION) \ -_Pragma("clang diagnostic push") \ -_Pragma("clang diagnostic ignored \"-Wshadow\"") \ -NSDate *timeout = [[NSDate date] dateByAddingTimeInterval:5]; \ -while ((CONDITION) && [timeout timeIntervalSinceNow] > 0) { \ - [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:timeout]; \ -} \ -_Pragma("clang diagnostic pop") - -@interface RCTBridge (RCTAllocationTests) - -@property (nonatomic, weak) RCTBridge *batchedBridge; - -@end +{ \ + NSDate *timeout = [NSDate dateWithTimeIntervalSinceNow:5]; \ + while ((CONDITION) && [timeout timeIntervalSinceNow] > 0) { \ + [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; \ + } \ +} @interface RCTJavaScriptContext : NSObject @@ -74,13 +68,40 @@ RCT_EXPORT_METHOD(test:(__unused NSString *)a @interface RCTAllocationTests : XCTestCase @end -@implementation RCTAllocationTests +@implementation RCTAllocationTests { + NSURL *_bundleURL; +} + +- (void)setUp +{ + [super setUp]; + + NSString *bundleContents = + @"var __fbBatchedBridge = {" + " callFunctionReturnFlushedQueue: function() {}," + " invokeCallbackAndReturnFlushedQueue: function() {}," + " flushedQueue: function() {}," + "};"; + + NSURL *tempDir = [NSURL fileURLWithPath:NSTemporaryDirectory() isDirectory:YES]; + [[NSFileManager defaultManager] createDirectoryAtURL:tempDir withIntermediateDirectories:YES attributes:nil error:NULL]; + + _bundleURL = [tempDir URLByAppendingPathComponent:@"rctallocationtests-bundle.js"]; + [bundleContents writeToURL:_bundleURL atomically:YES encoding:NSUTF8StringEncoding error:NULL]; +} + +- (void)tearDown +{ + [super tearDown]; + + [[NSFileManager defaultManager] removeItemAtURL:_bundleURL error:NULL]; +} - (void)testBridgeIsDeallocated { __weak RCTBridge *weakBridge; @autoreleasepool { - RCTRootView *view = [[RCTRootView alloc] initWithBundleURL:nil + RCTRootView *view = [[RCTRootView alloc] initWithBundleURL:_bundleURL moduleName:@"" initialProperties:nil launchOptions:nil]; @@ -96,7 +117,7 @@ RCT_EXPORT_METHOD(test:(__unused NSString *)a { AllocationTestModule *module = [AllocationTestModule new]; @autoreleasepool { - RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil + RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:_bundleURL moduleProvider:^{ return @[module]; } @@ -114,11 +135,11 @@ RCT_EXPORT_METHOD(test:(__unused NSString *)a __weak AllocationTestModule *weakModule; @autoreleasepool { AllocationTestModule *module = [AllocationTestModule new]; - RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil - moduleProvider:^{ - return @[module]; - } - launchOptions:nil]; + RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:_bundleURL + moduleProvider:^{ + return @[module]; + } + launchOptions:nil]; weakModule = module; XCTAssertNotNil(weakModule, @"AllocationTestModule should have been created"); (void)bridge; @@ -132,7 +153,7 @@ RCT_EXPORT_METHOD(test:(__unused NSString *)a { __weak RCTModuleMethod *weakMethod; @autoreleasepool { - __autoreleasing RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithObjCMethodName:@"test:(NSString *)a :(nonnull NSNumber *)b :(RCTResponseSenderBlock)c :(RCTResponseErrorBlock)d" JSMethodName:@"" moduleClass:[AllocationTestModule class]]; + __autoreleasing RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithMethodSignature:@"test:(NSString *)a :(nonnull NSNumber *)b :(RCTResponseSenderBlock)c :(RCTResponseErrorBlock)d" JSMethodName:@"" moduleClass:[AllocationTestModule class]]; weakMethod = method; XCTAssertNotNil(method, @"RCTModuleMethod should have been created"); } @@ -145,7 +166,7 @@ RCT_EXPORT_METHOD(test:(__unused NSString *)a { __weak id weakExecutor; @autoreleasepool { - RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil + RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:_bundleURL moduleProvider:nil launchOptions:nil]; weakExecutor = [bridge.batchedBridge valueForKey:@"javaScriptExecutor"]; @@ -161,11 +182,11 @@ RCT_EXPORT_METHOD(test:(__unused NSString *)a { __weak id weakContext; @autoreleasepool { - RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil + RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:_bundleURL moduleProvider:nil launchOptions:nil]; id executor = [bridge.batchedBridge valueForKey:@"javaScriptExecutor"]; - RUN_RUNLOOP_WHILE(!(weakContext = [executor valueForKey:@"context"])); + RUN_RUNLOOP_WHILE(!(weakContext = [executor valueForKey:@"_context"])); XCTAssertNotNil(weakContext, @"RCTJavaScriptContext should have been created"); (void)bridge; } @@ -176,7 +197,7 @@ RCT_EXPORT_METHOD(test:(__unused NSString *)a - (void)testContentViewIsInvalidated { - RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil + RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:_bundleURL moduleProvider:nil launchOptions:nil]; __weak UIView *rootContentView; @@ -195,7 +216,7 @@ RCT_EXPORT_METHOD(test:(__unused NSString *)a RCTBridge *bridge; __weak id batchedBridge; @autoreleasepool { - bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:nil launchOptions:nil]; + bridge = [[RCTBridge alloc] initWithBundleURL:_bundleURL moduleProvider:nil launchOptions:nil]; batchedBridge = bridge.batchedBridge; XCTAssertTrue([batchedBridge isValid], @"RCTBatchedBridge should be valid"); [bridge reload]; @@ -205,6 +226,15 @@ RCT_EXPORT_METHOD(test:(__unused NSString *)a XCTAssertNotNil(bridge, @"RCTBridge should not have been deallocated"); XCTAssertNil(batchedBridge, @"RCTBatchedBridge should have been deallocated"); + + // Wait to complete the test until the new batchedbridge is also deallocated + @autoreleasepool { + batchedBridge = bridge.batchedBridge; + bridge = nil; + } + + RUN_RUNLOOP_WHILE(batchedBridge != nil); + XCTAssertNil(batchedBridge); } @end diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTBridgeTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTBridgeTests.m index 24967be4a..31fc8f74e 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTBridgeTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTBridgeTests.m @@ -16,22 +16,22 @@ #import #import "RCTBridge.h" +#import "RCTBridge+Private.h" #import "RCTBridgeModule.h" #import "RCTJavaScriptExecutor.h" #import "RCTUtils.h" -@interface RCTBridge (Testing) - -@property (nonatomic, strong, readonly) RCTBridge *batchedBridge; - -- (void)_handleBuffer:(id)buffer; -- (void)setUp; - -@end +#define RUN_RUNLOOP_WHILE(CONDITION) \ +{ \ + NSDate *timeout = [NSDate dateWithTimeIntervalSinceNow:5]; \ + while ((CONDITION) && [timeout timeIntervalSinceNow] > 0) { \ + [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; \ + } \ +} @interface TestExecutor : NSObject -@property (nonatomic, readonly, copy) NSMutableDictionary *injectedStuff; +@property (nonatomic, readonly, copy) NSMutableDictionary *injectedStuff; @end @@ -54,10 +54,22 @@ RCT_EXPORT_MODULE() return YES; } -- (void)executeJSCall:(__unused NSString *)name - method:(__unused NSString *)method - arguments:(__unused NSArray *)arguments - callback:(RCTJavaScriptCallback)onComplete +- (void)flushedQueue:(RCTJavaScriptCallback)onComplete +{ + onComplete(nil, nil); +} + +- (void)callFunctionOnModule:(__unused NSString *)module + method:(__unused NSString *)method + arguments:(__unused NSArray *)args + callback:(RCTJavaScriptCallback)onComplete +{ + onComplete(nil, nil); +} + +- (void)invokeCallbackID:(__unused NSNumber *)cbID + arguments:(__unused NSArray *)args + callback:(RCTJavaScriptCallback)onComplete { onComplete(nil, nil); } @@ -89,6 +101,8 @@ RCT_EXPORT_MODULE() @interface RCTBridgeTests : XCTestCase { RCTBridge *_bridge; + __weak TestExecutor *_jsExecutor; + BOOL _testMethodCalled; } @end @@ -108,68 +122,81 @@ RCT_EXPORT_MODULE(TestModule) launchOptions:nil]; _bridge.executorClass = [TestExecutor class]; + // Force to recreate the executor with the new class // - reload: doesn't work here since bridge hasn't loaded yet. [_bridge invalidate]; [_bridge setUp]; + + _jsExecutor = [_bridge.batchedBridge valueForKey:@"javaScriptExecutor"]; + XCTAssertNotNil(_jsExecutor); } - (void)tearDown { [super tearDown]; - [_bridge invalidate]; -} -#define RUN_RUNLOOP_WHILE(CONDITION) \ -_Pragma("clang diagnostic push") \ -_Pragma("clang diagnostic ignored \"-Wshadow\"") \ -NSDate *timeout = [[NSDate date] dateByAddingTimeInterval:0.1]; \ -while ((CONDITION) && [timeout timeIntervalSinceNow] > 0) { \ - [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:timeout]; \ -} \ -_Pragma("clang diagnostic pop") + _testMethodCalled = NO; + + [_bridge invalidate]; + _bridge = nil; + + RUN_RUNLOOP_WHILE(_jsExecutor != nil); + XCTAssertNotNil(_jsExecutor); +} - (void)testHookRegistration { - TestExecutor *executor = [_bridge.batchedBridge valueForKey:@"_javaScriptExecutor"]; - NSString *injectedStuff; - RUN_RUNLOOP_WHILE(!(injectedStuff = executor.injectedStuff[@"__fbBatchedBridgeConfig"])); + RUN_RUNLOOP_WHILE(!(injectedStuff = _jsExecutor.injectedStuff[@"__fbBatchedBridgeConfig"])); + XCTAssertNotNil(injectedStuff); - NSDictionary *moduleConfig = RCTJSONParse(injectedStuff, NULL); - NSDictionary *remoteModuleConfig = moduleConfig[@"remoteModuleConfig"]; - NSDictionary *testModuleConfig = remoteModuleConfig[@"TestModule"]; - NSDictionary *constants = testModuleConfig[@"constants"]; - NSDictionary *methods = testModuleConfig[@"methods"]; + __block NSNumber *testModuleID = nil; + __block NSDictionary *testConstants = nil; + __block NSNumber *testMethodID = nil; + + NSArray *remoteModuleConfig = RCTJSONParse(injectedStuff, NULL)[@"remoteModuleConfig"]; + [remoteModuleConfig enumerateObjectsUsingBlock:^(id moduleConfig, NSUInteger i, BOOL *stop) { + if ([moduleConfig isKindOfClass:[NSArray class]] && [moduleConfig[0] isEqualToString:@"TestModule"]) { + testModuleID = @(i); + testConstants = moduleConfig[1]; + testMethodID = @([moduleConfig[2] indexOfObject:@"testMethod"]); + *stop = YES; + } + }]; - XCTAssertNotNil(moduleConfig); XCTAssertNotNil(remoteModuleConfig); - XCTAssertNotNil(testModuleConfig); - XCTAssertNotNil(constants); - XCTAssertEqualObjects(constants[@"eleventyMillion"], @42); - XCTAssertNotNil(methods); - XCTAssertNotNil(methods[@"testMethod"]); + XCTAssertNotNil(testModuleID); + XCTAssertNotNil(testConstants); + XCTAssertEqualObjects(testConstants[@"eleventyMillion"], @42); + XCTAssertNotNil(testMethodID); } - (void)testCallNativeMethod { - TestExecutor *executor = [_bridge.batchedBridge valueForKey:@"_javaScriptExecutor"]; - NSString *injectedStuff; - RUN_RUNLOOP_WHILE(!(injectedStuff = executor.injectedStuff[@"__fbBatchedBridgeConfig"])); + RUN_RUNLOOP_WHILE(!(injectedStuff = _jsExecutor.injectedStuff[@"__fbBatchedBridgeConfig"])); + XCTAssertNotNil(injectedStuff); - NSDictionary *moduleConfig = RCTJSONParse(injectedStuff, NULL); - NSDictionary *remoteModuleConfig = moduleConfig[@"remoteModuleConfig"]; - NSDictionary *testModuleConfig = remoteModuleConfig[@"TestModule"]; - NSNumber *testModuleID = testModuleConfig[@"moduleID"]; - NSDictionary *methods = testModuleConfig[@"methods"]; - NSDictionary *testMethod = methods[@"testMethod"]; - NSNumber *testMethodID = testMethod[@"methodID"]; + __block NSNumber *testModuleID = nil; + __block NSNumber *testMethodID = nil; + + NSArray *remoteModuleConfig = RCTJSONParse(injectedStuff, NULL)[@"remoteModuleConfig"]; + [remoteModuleConfig enumerateObjectsUsingBlock:^(id moduleConfig, NSUInteger i, __unused BOOL *stop) { + if ([moduleConfig isKindOfClass:[NSArray class]] && [moduleConfig[0] isEqualToString:@"TestModule"]) { + testModuleID = @(i); + testMethodID = @([moduleConfig[2] indexOfObject:@"testMethod"]); + *stop = YES; + } + }]; + + XCTAssertNotNil(testModuleID); + XCTAssertNotNil(testMethodID); NSArray *args = @[@1234, @5678, @"stringy", @{@"a": @1}, @42]; - NSArray *buffer = @[@[testModuleID], @[testMethodID], @[args], @[], @1234567]; + NSArray *buffer = @[@[testModuleID], @[testMethodID], @[args]]; - [_bridge.batchedBridge _handleBuffer:buffer]; + [_bridge.batchedBridge handleBuffer:buffer]; dispatch_sync(_methodQueue, ^{ // clear the queue @@ -180,7 +207,7 @@ _Pragma("clang diagnostic pop") - (void)DISABLED_testBadArgumentsCount { //NSArray *bufferWithMissingArgument = @[@[@1], @[@0], @[@[@1234, @5678, @"stringy", @{@"a": @1}/*, @42*/]], @[], @1234567]; - //[_bridge _handleBuffer:bufferWithMissingArgument]; + //[_bridge handleBuffer:bufferWithMissingArgument]; NSLog(@"WARNING: testBadArgumentsCount is temporarily disabled until we have a better way to test cases that we expect to trigger redbox errors"); } @@ -199,7 +226,7 @@ RCT_EXPORT_METHOD(testMethod:(NSInteger)integer XCTAssertNotNil(callback); } -- (NSDictionary *)constantsToExport +- (NSDictionary *)constantsToExport { return @{@"eleventyMillion": @42}; } diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTCacheTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTCacheTests.m deleted file mode 100644 index b6bc22188..000000000 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTCacheTests.m +++ /dev/null @@ -1,168 +0,0 @@ -/** - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#import -#import "RCTCache.h" - -// Silence silly sign warnings when using int literals -#pragma clang diagnostic ignored "-Wsign-compare" - -@interface RCTCache (Private) - -- (void)cleanUpAllObjects; -- (void)resequence; -- (NSDictionary *)cache; -- (void)setSequenceNumber:(NSInteger)number; - -@end - -@interface RCTCacheTests : XCTestCase - -@property (nonatomic, strong) RCTCache *cache; - -@end - -@implementation RCTCacheTests - -- (void)setUp -{ - self.cache = [RCTCache new]; - self.cache.countLimit = 3; - self.cache.totalCostLimit = 100; -} - -- (void)tearDown -{ - self.cache = nil; -} - -- (void)testInsertion -{ - [self.cache setObject:@1 forKey:@"foo" cost:1]; - [self.cache setObject:@2 forKey:@"bar" cost:2]; - [self.cache setObject:@3 forKey:@"baz" cost:3]; - - XCTAssertEqual([self.cache count], 3); - XCTAssertEqual([self.cache totalCost], 6); -} - -- (void)testRemoval -{ - [self.cache setObject:@1 forKey:@"foo" cost:1]; - [self.cache setObject:@2 forKey:@"bar" cost:2]; - [self.cache setObject:@3 forKey:@"baz" cost:3]; - - [self.cache removeObjectForKey:@"bar"]; - - XCTAssertEqual([self.cache count], 2); - XCTAssertNil([self.cache objectForKey:@"bar"]); -} - -- (void)testCountEviction -{ - [self.cache setObject:@1 forKey:@"foo"]; - [self.cache setObject:@2 forKey:@"bar"]; - [self.cache setObject:@3 forKey:@"baz"]; - [self.cache setObject:@4 forKey:@"bam"]; - - XCTAssertEqual([self.cache count], 3); - XCTAssertNil([self.cache objectForKey:@"foo"]); - - [self.cache setObject:@5 forKey:@"boo"]; - - XCTAssertEqual([self.cache count], 3); - XCTAssertNil([self.cache objectForKey:@"bar"]); -} - -- (void)testCostEviction -{ - [self.cache setObject:@1 forKey:@"foo" cost:99]; - [self.cache setObject:@2 forKey:@"bar" cost:2]; - - XCTAssertEqual([self.cache count], 1); - XCTAssertEqual([self.cache totalCost], 2); - XCTAssertNil([self.cache objectForKey:@"foo"]); - - [self.cache setObject:@3 forKey:@"baz" cost:999]; - - XCTAssertEqual([self.cache count], 0); - XCTAssertEqual([self.cache totalCost], 0); -} - -- (void)testCleanup -{ - [self.cache setObject:@1 forKey:@"foo"]; - [self.cache setObject:@2 forKey:@"bar"]; - [self.cache setObject:@3 forKey:@"baz"]; - - //simulate memory warning - [self.cache cleanUpAllObjects]; - - XCTAssertEqual([self.cache count], 0); - XCTAssertEqual([self.cache totalCost], 0); -} - -- (void)testResequence -{ - [self.cache setObject:@1 forKey:@"foo"]; - [self.cache setObject:@2 forKey:@"bar"]; - [self.cache setObject:@3 forKey:@"baz"]; - - [self.cache resequence]; - - NSDictionary *innerCache = [self.cache cache]; - XCTAssertEqualObjects([innerCache[@"foo"] valueForKey:@"sequenceNumber"], @0); - XCTAssertEqualObjects([innerCache[@"bar"] valueForKey:@"sequenceNumber"], @1); - XCTAssertEqualObjects([innerCache[@"baz"] valueForKey:@"sequenceNumber"], @2); - - [self.cache removeObjectForKey:@"foo"]; - [self.cache resequence]; - - XCTAssertEqualObjects([innerCache[@"bar"] valueForKey:@"sequenceNumber"], @0); - XCTAssertEqualObjects([innerCache[@"baz"] valueForKey:@"sequenceNumber"], @1); -} - -- (void)testResequenceTrigger -{ - [self.cache setObject:@1 forKey:@"foo"]; - [self.cache setObject:@2 forKey:@"bar"]; - - //first object should now be bar with sequence number of 1 - [self.cache removeObjectForKey:@"foo"]; - - //should trigger resequence - [self.cache setSequenceNumber:NSIntegerMax]; - [self.cache setObject:@3 forKey:@"baz"]; - - NSDictionary *innerCache = [self.cache cache]; - XCTAssertEqualObjects([innerCache[@"bar"] valueForKey:@"sequenceNumber"], @0); - XCTAssertEqualObjects([innerCache[@"baz"] valueForKey:@"sequenceNumber"], @1); - - //first object should now be baz with sequence number of 1 - [self.cache removeObjectForKey:@"bar"]; - - //should also trigger resequence - [self.cache setSequenceNumber:NSIntegerMax]; - [self.cache objectForKey:@"baz"]; - - XCTAssertEqualObjects([innerCache[@"baz"] valueForKey:@"sequenceNumber"], @0); -} - -- (void)testName -{ - self.cache.name = @"Hello"; - XCTAssertEqualObjects(self.cache.name, @"Hello"); -} - -@end diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTConvert_UIColorTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTConvert_UIColorTests.m deleted file mode 100644 index f74429974..000000000 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTConvert_UIColorTests.m +++ /dev/null @@ -1,79 +0,0 @@ -/** - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#import - -#import "RCTConvert.h" - -@interface RCTConvert_UIColorTests : XCTestCase - -@end - -@implementation RCTConvert_UIColorTests - -#define XCTAssertEqualColors(color1, color2) do { \ - CGFloat r1, g1, b1, a1; \ - CGFloat r2, g2, b2, a2; \ - XCTAssertTrue([(color1) getRed:&r1 green:&g1 blue:&b1 alpha:&a1] && \ - [(color2) getRed:&r2 green:&g2 blue:&b2 alpha:&a2] && \ - r1 == r2 && g1 == g2 && b1 == b2 && a1 == a2, \ - @"rgba(%d, %d, %d, %.3f) != rgba(%d, %d, %d, %.3f)", \ - (int)(r1 * 255), (int)(g1 * 255), (int)(b1 * 255), a1, \ - (int)(r2 * 255), (int)(g2 * 255), (int)(b2 * 255), a2 \ - ); \ -} while (0) - -- (void)testHex3 -{ - UIColor *color = [RCTConvert UIColor:@"#333"]; - UIColor *expected = [UIColor colorWithWhite:0.2 alpha:1.0]; - XCTAssertEqualColors(color, expected); -} - -- (void)testHex6 -{ - UIColor *color = [RCTConvert UIColor:@"#666"]; - UIColor *expected = [UIColor colorWithWhite:0.4 alpha:1.0]; - XCTAssertEqualColors(color, expected); -} - -- (void)testRGB -{ - UIColor *color = [RCTConvert UIColor:@"rgb(51, 102, 153)"]; - UIColor *expected = [UIColor colorWithRed:0.2 green:0.4 blue:0.6 alpha:1.0]; - XCTAssertEqualColors(color, expected); -} - -- (void)testRGBA -{ - UIColor *color = [RCTConvert UIColor:@"rgba(51, 102, 153, 0.5)"]; - UIColor *expected = [UIColor colorWithRed:0.2 green:0.4 blue:0.6 alpha:0.5]; - XCTAssertEqualColors(color, expected); -} - -- (void)testHSL -{ - UIColor *color = [RCTConvert UIColor:@"hsl(30, 50%, 50%)"]; - UIColor *expected = [UIColor colorWithHue:30.0 / 360.0 saturation:0.5 brightness:0.5 alpha:1.0]; - XCTAssertEqualColors(color, expected); -} - -- (void)testHSLA -{ - UIColor *color = [RCTConvert UIColor:@"hsla(30, 50%, 50%, 0.5)"]; - UIColor *expected = [UIColor colorWithHue:30.0 / 360.0 saturation:0.5 brightness:0.5 alpha:0.5]; - XCTAssertEqualColors(color, expected); -} - -@end diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTEventDispatcherTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTEventDispatcherTests.m index bfa52b94f..e0406d497 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTEventDispatcherTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTEventDispatcherTests.m @@ -26,7 +26,7 @@ @implementation RCTTestEvent -- (instancetype)initWithViewTag:(NSNumber *)viewTag eventName:(NSString *)eventName body:(NSDictionary *)body +- (instancetype)initWithViewTag:(NSNumber *)viewTag eventName:(NSString *)eventName body:(NSDictionary *)body { if (self = [super initWithViewTag:viewTag eventName:eventName body:body]) { self.canCoalesce = YES; @@ -50,7 +50,7 @@ RCTEventDispatcher *_eventDispatcher; NSString *_eventName; - NSDictionary *_body; + NSDictionary *_body; RCTTestEvent *_testEvent; NSString *_JSMethod; } @@ -62,7 +62,7 @@ _bridge = [OCMockObject mockForClass:[RCTBridge class]]; _eventDispatcher = [RCTEventDispatcher new]; - ((id)_eventDispatcher).bridge = _bridge; + [_eventDispatcher setValue:_bridge forKey:@"bridge"]; _eventName = RCTNormalizeInputEventName(@"sampleEvent"); _body = @{ @"foo": @"bar" }; diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTGzipTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTGzipTests.m index ceb58b972..9d5814c56 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTGzipTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTGzipTests.m @@ -16,11 +16,19 @@ #import "RCTUtils.h" #import "RCTNetworking.h" +#define RUN_RUNLOOP_WHILE(CONDITION) \ +{ \ + NSDate *timeout = [NSDate dateWithTimeIntervalSinceNow:5]; \ + while ((CONDITION) && [timeout timeIntervalSinceNow] > 0) { \ + [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; \ + } \ +} + extern BOOL RCTIsGzippedData(NSData *data); @interface RCTNetworking (Private) -- (void)buildRequest:(NSDictionary *)query +- (void)buildRequest:(NSDictionary *)query completionBlock:(void (^)(NSURLRequest *request))block; @end @@ -61,18 +69,21 @@ extern BOOL RCTIsGzippedData(NSData *data); - (void)testRequestBodyEncoding { NSDictionary *query = @{ - @"url": @"http://example.com", - @"method": @"POST", - @"data": @{@"string": @"Hello World"}, - @"headers": @{@"Content-Encoding": @"gzip"}, - }; + @"url": @"http://example.com", + @"method": @"POST", + @"data": @{@"string": @"Hello World"}, + @"headers": @{@"Content-Encoding": @"gzip"}, + }; RCTNetworking *networker = [RCTNetworking new]; + [networker setValue:dispatch_get_main_queue() forKey:@"methodQueue"]; __block NSURLRequest *request = nil; [networker buildRequest:query completionBlock:^(NSURLRequest *_request) { request = _request; }]; + RUN_RUNLOOP_WHILE(request == nil); + XCTAssertNotNil(request); XCTAssertNotNil(request.HTTPBody); XCTAssertTrue(RCTIsGzippedData(request.HTTPBody)); diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderHelpers.h b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderHelpers.h index 364cddc88..fe68f0592 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderHelpers.h +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderHelpers.h @@ -15,7 +15,7 @@ #import "RCTImageLoader.h" typedef BOOL (^RCTImageURLLoaderCanLoadImageURLHandler)(NSURL *requestURL); -typedef RCTImageLoaderCancellationBlock (^RCTImageURLLoaderLoadImageURLHandler)(NSURL *imageURL, CGSize size, CGFloat scale, UIViewContentMode resizeMode, RCTImageLoaderProgressBlock progressHandler, RCTImageLoaderCompletionBlock completionHandler); +typedef RCTImageLoaderCancellationBlock (^RCTImageURLLoaderLoadImageURLHandler)(NSURL *imageURL, CGSize size, CGFloat scale, RCTResizeMode resizeMode, RCTImageLoaderProgressBlock progressHandler, RCTImageLoaderCompletionBlock completionHandler); @interface RCTConcreteImageURLLoader : NSObject @@ -25,14 +25,14 @@ typedef RCTImageLoaderCancellationBlock (^RCTImageURLLoaderLoadImageURLHandler)( @end -typedef BOOL (^RCTImageDecoderCanDecodeImageDataHandler)(NSData *imageData); -typedef RCTImageLoaderCancellationBlock (^RCTImageDecoderDecodeImageDataHandler)(NSData *imageData, CGSize size, CGFloat scale, UIViewContentMode resizeMode, RCTImageLoaderCompletionBlock completionHandler); +typedef BOOL (^RCTImageDataDecoderCanDecodeImageDataHandler)(NSData *imageData); +typedef RCTImageLoaderCancellationBlock (^RCTImageDataDecoderDecodeImageDataHandler)(NSData *imageData, CGSize size, CGFloat scale, RCTResizeMode resizeMode, RCTImageLoaderCompletionBlock completionHandler); -@interface RCTConcreteImageDecoder : NSObject +@interface RCTConcreteImageDecoder : NSObject - (instancetype)initWithPriority:(float)priority - canDecodeImageDataHandler:(RCTImageDecoderCanDecodeImageDataHandler)canDecodeImageDataHandler - decodeImageDataHandler:(RCTImageDecoderDecodeImageDataHandler)decodeImageDataHandler; + canDecodeImageDataHandler:(RCTImageDataDecoderCanDecodeImageDataHandler)canDecodeImageDataHandler + decodeImageDataHandler:(RCTImageDataDecoderDecodeImageDataHandler)decodeImageDataHandler; @end diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderHelpers.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderHelpers.m index 2038fad50..e3b4cb086 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderHelpers.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderHelpers.m @@ -47,12 +47,12 @@ return _canLoadImageURLHandler(requestURL); } -- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode progressHandler:(RCTImageLoaderProgressBlock)progressHandler completionHandler:(RCTImageLoaderCompletionBlock)completionHandler +- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL size:(CGSize)size scale:(CGFloat)scale resizeMode:(RCTResizeMode)resizeMode progressHandler:(RCTImageLoaderProgressBlock)progressHandler completionHandler:(RCTImageLoaderCompletionBlock)completionHandler { return _loadImageURLHandler(imageURL, size, scale, resizeMode, progressHandler, completionHandler); } -- (float)imageLoaderPriority +- (float)loaderPriority { return _priority; } @@ -61,8 +61,8 @@ @implementation RCTConcreteImageDecoder { - RCTImageDecoderCanDecodeImageDataHandler _canDecodeImageDataHandler; - RCTImageDecoderDecodeImageDataHandler _decodeImageDataHandler; + RCTImageDataDecoderCanDecodeImageDataHandler _canDecodeImageDataHandler; + RCTImageDataDecoderDecodeImageDataHandler _decodeImageDataHandler; float _priority; } @@ -76,7 +76,7 @@ return nil; } -- (instancetype)initWithPriority:(float)priority canDecodeImageDataHandler:(RCTImageDecoderCanDecodeImageDataHandler)canDecodeImageDataHandler decodeImageDataHandler:(RCTImageDecoderDecodeImageDataHandler)decodeImageDataHandler +- (instancetype)initWithPriority:(float)priority canDecodeImageDataHandler:(RCTImageDataDecoderCanDecodeImageDataHandler)canDecodeImageDataHandler decodeImageDataHandler:(RCTImageDataDecoderDecodeImageDataHandler)decodeImageDataHandler { if ((self = [super init])) { _canDecodeImageDataHandler = [canDecodeImageDataHandler copy]; @@ -92,12 +92,12 @@ return _canDecodeImageDataHandler(imageData); } -- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode completionHandler:(RCTImageLoaderCompletionBlock)completionHandler +- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData size:(CGSize)size scale:(CGFloat)scale resizeMode:(RCTResizeMode)resizeMode completionHandler:(RCTImageLoaderCompletionBlock)completionHandler { return _decodeImageDataHandler(imageData, size, scale, resizeMode, completionHandler); } -- (float)imageDecoderPriority +- (float)decoderPriority { return _priority; } diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m index d0323df7a..5713d0347 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m @@ -41,7 +41,7 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2) id loader = [[RCTImageLoaderTestsURLLoader1 alloc] initWithPriority:1.0 canLoadImageURLHandler:^BOOL(__unused NSURL *requestURL) { return YES; - } loadImageURLHandler:^RCTImageLoaderCancellationBlock(__unused NSURL *imageURL, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, RCTImageLoaderProgressBlock progressHandler, RCTImageLoaderCompletionBlock completionHandler) { + } loadImageURLHandler:^RCTImageLoaderCancellationBlock(__unused NSURL *imageURL, __unused CGSize size, __unused CGFloat scale, __unused RCTResizeMode resizeMode, RCTImageLoaderProgressBlock progressHandler, RCTImageLoaderCompletionBlock completionHandler) { progressHandler(1, 1); completionHandler(nil, image); return nil; @@ -50,7 +50,7 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2) RCTImageLoader *imageLoader = [RCTImageLoader new]; NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[loader, imageLoader]; } launchOptions:nil]; - [imageLoader loadImageWithTag:@"http://facebook.github.io/react/img/logo_og.png" size:CGSizeMake(100, 100) scale:1.0 resizeMode:UIViewContentModeScaleAspectFit progressBlock:^(int64_t progress, int64_t total) { + [imageLoader loadImageWithTag:@"http://facebook.github.io/react/img/logo_og.png" size:CGSizeMake(100, 100) scale:1.0 resizeMode:RCTResizeModeContain progressBlock:^(int64_t progress, int64_t total) { XCTAssertEqual(progress, 1); XCTAssertEqual(total, 1); } completionBlock:^(NSError *loadError, id loadedImage) { @@ -65,7 +65,7 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2) id loader1 = [[RCTImageLoaderTestsURLLoader1 alloc] initWithPriority:1.0 canLoadImageURLHandler:^BOOL(__unused NSURL *requestURL) { return YES; - } loadImageURLHandler:^RCTImageLoaderCancellationBlock(__unused NSURL *imageURL, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, RCTImageLoaderProgressBlock progressHandler, RCTImageLoaderCompletionBlock completionHandler) { + } loadImageURLHandler:^RCTImageLoaderCancellationBlock(__unused NSURL *imageURL, __unused CGSize size, __unused CGFloat scale, __unused RCTResizeMode resizeMode, RCTImageLoaderProgressBlock progressHandler, RCTImageLoaderCompletionBlock completionHandler) { progressHandler(1, 1); completionHandler(nil, image); return nil; @@ -73,7 +73,7 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2) id loader2 = [[RCTImageLoaderTestsURLLoader2 alloc] initWithPriority:0.5 canLoadImageURLHandler:^BOOL(__unused NSURL *requestURL) { return YES; - } loadImageURLHandler:^RCTImageLoaderCancellationBlock(__unused NSURL *imageURL, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, __unused RCTImageLoaderProgressBlock progressHandler, __unused RCTImageLoaderCompletionBlock completionHandler) { + } loadImageURLHandler:^RCTImageLoaderCancellationBlock(__unused NSURL *imageURL, __unused CGSize size, __unused CGFloat scale, __unused RCTResizeMode resizeMode, __unused RCTImageLoaderProgressBlock progressHandler, __unused RCTImageLoaderCompletionBlock completionHandler) { XCTFail(@"Should not have used loader2"); return nil; }]; @@ -81,7 +81,7 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2) RCTImageLoader *imageLoader = [RCTImageLoader new]; NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[loader1, loader2, imageLoader]; } launchOptions:nil]; - [imageLoader loadImageWithTag:@"http://facebook.github.io/react/img/logo_og.png" size:CGSizeMake(100, 100) scale:1.0 resizeMode:UIViewContentModeScaleAspectFit progressBlock:^(int64_t progress, int64_t total) { + [imageLoader loadImageWithTag:@"http://facebook.github.io/react/img/logo_og.png" size:CGSizeMake(100, 100) scale:1.0 resizeMode:RCTResizeModeContain progressBlock:^(int64_t progress, int64_t total) { XCTAssertEqual(progress, 1); XCTAssertEqual(total, 1); } completionBlock:^(NSError *loadError, id loadedImage) { @@ -95,9 +95,9 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2) NSData *data = [NSData dataWithBytesNoCopy:blackGIF length:sizeof(blackGIF) freeWhenDone:NO]; UIImage *image = [[UIImage alloc] initWithData:data]; - id decoder = [[RCTImageLoaderTestsDecoder1 alloc] initWithPriority:1.0 canDecodeImageDataHandler:^BOOL(__unused NSData *imageData) { + id decoder = [[RCTImageLoaderTestsDecoder1 alloc] initWithPriority:1.0 canDecodeImageDataHandler:^BOOL(__unused NSData *imageData) { return YES; - } decodeImageDataHandler:^RCTImageLoaderCancellationBlock(NSData *imageData, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, RCTImageLoaderCompletionBlock completionHandler) { + } decodeImageDataHandler:^RCTImageLoaderCancellationBlock(NSData *imageData, __unused CGSize size, __unused CGFloat scale, __unused RCTResizeMode resizeMode, RCTImageLoaderCompletionBlock completionHandler) { XCTAssertEqualObjects(imageData, data); completionHandler(nil, image); return nil; @@ -106,7 +106,7 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2) RCTImageLoader *imageLoader = [RCTImageLoader new]; NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[decoder, imageLoader]; } launchOptions:nil]; - RCTImageLoaderCancellationBlock cancelBlock = [imageLoader decodeImageData:data size:CGSizeMake(1, 1) scale:1.0 resizeMode:UIViewContentModeScaleToFill completionBlock:^(NSError *decodeError, id decodedImage) { + RCTImageLoaderCancellationBlock cancelBlock = [imageLoader decodeImageData:data size:CGSizeMake(1, 1) scale:1.0 resizeMode:RCTResizeModeStretch completionBlock:^(NSError *decodeError, id decodedImage) { XCTAssertEqualObjects(decodedImage, image); XCTAssertNil(decodeError); }]; @@ -118,17 +118,17 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2) NSData *data = [NSData dataWithBytesNoCopy:blackGIF length:sizeof(blackGIF) freeWhenDone:NO]; UIImage *image = [[UIImage alloc] initWithData:data]; - id decoder1 = [[RCTImageLoaderTestsDecoder1 alloc] initWithPriority:1.0 canDecodeImageDataHandler:^BOOL(__unused NSData *imageData) { + id decoder1 = [[RCTImageLoaderTestsDecoder1 alloc] initWithPriority:1.0 canDecodeImageDataHandler:^BOOL(__unused NSData *imageData) { return YES; - } decodeImageDataHandler:^RCTImageLoaderCancellationBlock(NSData *imageData, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, RCTImageLoaderCompletionBlock completionHandler) { + } decodeImageDataHandler:^RCTImageLoaderCancellationBlock(NSData *imageData, __unused CGSize size, __unused CGFloat scale, __unused RCTResizeMode resizeMode, RCTImageLoaderCompletionBlock completionHandler) { XCTAssertEqualObjects(imageData, data); completionHandler(nil, image); return nil; }]; - id decoder2 = [[RCTImageLoaderTestsDecoder2 alloc] initWithPriority:0.5 canDecodeImageDataHandler:^BOOL(__unused NSData *imageData) { + id decoder2 = [[RCTImageLoaderTestsDecoder2 alloc] initWithPriority:0.5 canDecodeImageDataHandler:^BOOL(__unused NSData *imageData) { return YES; - } decodeImageDataHandler:^RCTImageLoaderCancellationBlock(__unused NSData *imageData, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, __unused RCTImageLoaderCompletionBlock completionHandler) { + } decodeImageDataHandler:^RCTImageLoaderCancellationBlock(__unused NSData *imageData, __unused CGSize size, __unused CGFloat scale, __unused RCTResizeMode resizeMode, __unused RCTImageLoaderCompletionBlock completionHandler) { XCTFail(@"Should not have used decoder2"); return nil; }]; @@ -136,7 +136,7 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2) RCTImageLoader *imageLoader = [RCTImageLoader new]; NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[decoder1, decoder2, imageLoader]; } launchOptions:nil]; - RCTImageLoaderCancellationBlock cancelBlock = [imageLoader decodeImageData:data size:CGSizeMake(1, 1) scale:1.0 resizeMode:UIViewContentModeScaleToFill completionBlock:^(NSError *decodeError, id decodedImage) { + RCTImageLoaderCancellationBlock cancelBlock = [imageLoader decodeImageData:data size:CGSizeMake(1, 1) scale:1.0 resizeMode:RCTResizeModeStretch completionBlock:^(NSError *decodeError, id decodedImage) { XCTAssertEqualObjects(decodedImage, image); XCTAssertNil(decodeError); }]; diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTImageUtilTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageUtilTests.m index f1effc8d6..810e079ab 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTImageUtilTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageUtilTests.m @@ -46,19 +46,19 @@ RCTAssertEqualSizes(a.size, b.size); \ { CGRect expected = {CGPointZero, {100, 20}}; - CGRect result = RCTTargetRect(content, target, 1, UIViewContentModeScaleToFill); + CGRect result = RCTTargetRect(content, target, 1, RCTResizeModeStretch); RCTAssertEqualRects(expected, result); } { - CGRect expected = {CGPointZero, {100, 10}}; - CGRect result = RCTTargetRect(content, target, 1, UIViewContentModeScaleAspectFit); + CGRect expected = {{0, 5}, {100, 10}}; + CGRect result = RCTTargetRect(content, target, 1, RCTResizeModeContain); RCTAssertEqualRects(expected, result); } { CGRect expected = {{-50, 0}, {200, 20}}; - CGRect result = RCTTargetRect(content, target, 1, UIViewContentModeScaleAspectFill); + CGRect result = RCTTargetRect(content, target, 1, RCTResizeModeCover); RCTAssertEqualRects(expected, result); } } @@ -70,19 +70,19 @@ RCTAssertEqualSizes(a.size, b.size); \ { CGRect expected = {CGPointZero, {100, 20}}; - CGRect result = RCTTargetRect(content, target, 1, UIViewContentModeScaleToFill); + CGRect result = RCTTargetRect(content, target, 1, RCTResizeModeStretch); RCTAssertEqualRects(expected, result); } { - CGRect expected = {CGPointZero, {2, 20}}; - CGRect result = RCTTargetRect(content, target, 1, UIViewContentModeScaleAspectFit); + CGRect expected = {{49, 0}, {2, 20}}; + CGRect result = RCTTargetRect(content, target, 1, RCTResizeModeContain); RCTAssertEqualRects(expected, result); } { CGRect expected = {{0, -490}, {100, 1000}}; - CGRect result = RCTTargetRect(content, target, 1, UIViewContentModeScaleAspectFill); + CGRect result = RCTTargetRect(content, target, 1, RCTResizeModeCover); RCTAssertEqualRects(expected, result); } } @@ -94,19 +94,19 @@ RCTAssertEqualSizes(a.size, b.size); \ { CGRect expected = {CGPointZero, {20, 50}}; - CGRect result = RCTTargetRect(content, target, 1, UIViewContentModeScaleToFill); + CGRect result = RCTTargetRect(content, target, 1, RCTResizeModeStretch); RCTAssertEqualRects(expected, result); } { - CGRect expected = {CGPointZero, {5, 50}}; - CGRect result = RCTTargetRect(content, target, 1, UIViewContentModeScaleAspectFit); + CGRect expected = {{7,0}, {5, 50}}; + CGRect result = RCTTargetRect(content, target, 1, RCTResizeModeContain); RCTAssertEqualRects(expected, result); } { CGRect expected = {{0, -75}, {20, 200}}; - CGRect result = RCTTargetRect(content, target, 2, UIViewContentModeScaleAspectFill); + CGRect result = RCTTargetRect(content, target, 2, RCTResizeModeCover); RCTAssertEqualRects(expected, result); } } @@ -118,7 +118,7 @@ RCTAssertEqualSizes(a.size, b.size); \ { CGRect expected = {{0, -75}, {20, 200}}; - CGRect result = RCTTargetRect(content, target, 1, UIViewContentModeScaleAspectFill); + CGRect result = RCTTargetRect(content, target, 1, RCTResizeModeCover); RCTAssertEqualRects(expected, result); } } @@ -129,8 +129,56 @@ RCTAssertEqualSizes(a.size, b.size); \ CGSize target = {3, 3}; CGRect expected = {CGPointZero, {3, 3}}; - CGRect result = RCTTargetRect(content, target, 1, UIViewContentModeScaleToFill); + CGRect result = RCTTargetRect(content, target, 1, RCTResizeModeStretch); RCTAssertEqualRects(expected, result); } +- (void)testPlaceholderImage +{ + CGSize size = {45, 22}; + CGFloat expectedScale = 1.0; + UIImage *image = RCTGetPlaceholderImage(size, nil); + RCTAssertEqualSizes(size, image.size); + XCTAssertEqual(expectedScale, image.scale); +} + +- (void)testPlaceholderNonintegralSize +{ + CGSize size = {3.0/2, 7.0/3}; + CGFloat expectedScale = 6; + CGSize pixelSize = { + round(size.width * expectedScale), + round(size.height * expectedScale) + }; + UIImage *image = RCTGetPlaceholderImage(size, nil); + RCTAssertEqualSizes(size, image.size); + XCTAssertEqual(pixelSize.width, CGImageGetWidth(image.CGImage)); + XCTAssertEqual(pixelSize.height, CGImageGetHeight(image.CGImage)); + XCTAssertEqual(expectedScale, image.scale); +} + +- (void)testPlaceholderSquareImage +{ + CGSize size = {333, 333}; + CGFloat expectedScale = 1.0/333; + CGSize pixelSize = {1, 1}; + UIImage *image = RCTGetPlaceholderImage(size, nil); + RCTAssertEqualSizes(size, image.size); + XCTAssertEqual(pixelSize.width, CGImageGetWidth(image.CGImage)); + XCTAssertEqual(pixelSize.height, CGImageGetHeight(image.CGImage)); + XCTAssertEqual(expectedScale, image.scale); +} + +- (void)testPlaceholderNonsquareImage +{ + CGSize size = {640, 480}; + CGFloat expectedScale = 1.0/160; + CGSize pixelSize = {4, 3}; + UIImage *image = RCTGetPlaceholderImage(size, nil); + RCTAssertEqualSizes(size, image.size); + XCTAssertEqual(pixelSize.width, CGImageGetWidth(image.CGImage)); + XCTAssertEqual(pixelSize.height, CGImageGetHeight(image.CGImage)); + XCTAssertEqual(expectedScale, image.scale); +} + @end diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTContextExecutorTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTJSCExecutorTests.m similarity index 83% rename from Examples/UIExplorer/UIExplorerUnitTests/RCTContextExecutorTests.m rename to Examples/UIExplorer/UIExplorerUnitTests/RCTJSCExecutorTests.m index fa48fcfbf..5d08f8f46 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTContextExecutorTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTJSCExecutorTests.m @@ -16,31 +16,31 @@ #import -#import "RCTContextExecutor.h" +#import "RCTJSCExecutor.h" #import "RCTUtils.h" #define RUN_PERF_TESTS 0 -@interface RCTContextExecutorTests : XCTestCase +@interface RCTJSCExecutorTests : XCTestCase @end -@implementation RCTContextExecutorTests +@implementation RCTJSCExecutorTests { - RCTContextExecutor *_executor; + RCTJSCExecutor *_executor; } - (void)setUp { [super setUp]; - _executor = [RCTContextExecutor new]; + _executor = [RCTJSCExecutor new]; [_executor setUp]; } - (void)testNativeLoggingHookExceptionBehavior { dispatch_semaphore_t doneSem = dispatch_semaphore_create(0); - [_executor executeApplicationScript:@"var x = {toString: function() { throw 1; }}; nativeLoggingHook(x);" + [_executor executeApplicationScript:[@"var x = {toString: function() { throw 1; }}; nativeLoggingHook(x);" dataUsingEncoding:NSUTF8StringEncoding] sourceURL:[NSURL URLWithString:@"file://"] onComplete:^(__unused id error){ dispatch_semaphore_signal(doneSem); @@ -97,8 +97,8 @@ static uint64_t _get_time_nanoseconds(void) - (void)testJavaScriptCallSpeed { /** - * Since we almost don't change the RCTContextExecutor logic, and this test is - * very likely to become flaky (specially accross different devices) leave it + * Since we almost don't change the RCTJSCExecutor logic, and this test is + * very likely to become flaky (specially across different devices) leave it * to be run manually * * Previous Values: If you change the executor code, you should update this values @@ -123,12 +123,17 @@ static uint64_t _get_time_nanoseconds(void) } \ } \ }; \ + var Bridge = { \ + callFunctionReturnFlushedQueue: function(module, method, args) { \ + modules[module].apply(modules[module], args); \ + } \ + }; \ function require(module) { \ - return modules[module]; \ + return Bridge; \ } \ "; - [_executor executeApplicationScript:script sourceURL:[NSURL URLWithString:@"http://localhost:8081/"] onComplete:^(__unused NSError *error) { + [_executor executeApplicationScript:[script dataUsingEncoding:NSUTF8StringEncoding] sourceURL:[NSURL URLWithString:@"http://localhost:8081/"] onComplete:^(__unused NSError *error) { NSMutableArray *params = [NSMutableArray new]; id data = @1; for (int i = 0; i < 4; i++) { @@ -138,12 +143,11 @@ static uint64_t _get_time_nanoseconds(void) for (int j = 0; j < runs; j++) { @autoreleasepool { double start = _get_time_nanoseconds(); - [_executor executeJSCall:@"module" - method:@"method" - arguments:params - callback:^(id json, __unused NSError *unused) { - XCTAssert([json isEqual:@YES], @"Invalid return"); - }]; + [_executor callFunctionOnModule:@"module" + method:@"method" + arguments:params + callback:^(__unused id json, __unused NSError *unused) { + }]; double run = _get_time_nanoseconds() - start; if ((j % frequency) == frequency - 1) { // Warmup total += run; diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTJSONTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTJSONTests.m index b8951ca02..fd8343c08 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTJSONTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTJSONTests.m @@ -23,14 +23,14 @@ - (void)testEncodingObject { - NSDictionary *obj = @{@"foo": @"bar"}; + NSDictionary *obj = @{@"foo": @"bar"}; NSString *json = @"{\"foo\":\"bar\"}"; XCTAssertEqualObjects(json, RCTJSONStringify(obj, NULL)); } - (void)testEncodingArray { - NSArray *array = @[@"foo", @"bar"]; + NSArray *array = @[@"foo", @"bar"]; NSString *json = @"[\"foo\",\"bar\"]"; XCTAssertEqualObjects(json, RCTJSONStringify(array, NULL)); } @@ -44,14 +44,14 @@ - (void)testDecodingObject { - NSDictionary *obj = @{@"foo": @"bar"}; + NSDictionary *obj = @{@"foo": @"bar"}; NSString *json = @"{\"foo\":\"bar\"}"; XCTAssertEqualObjects(obj, RCTJSONParse(json, NULL)); } - (void)testDecodingArray { - NSArray *array = @[@"foo", @"bar"]; + NSArray *array = @[@"foo", @"bar"]; NSString *json = @"[\"foo\",\"bar\"]"; XCTAssertEqualObjects(array, RCTJSONParse(json, NULL)); } @@ -66,14 +66,14 @@ - (void)testDecodingMutableArray { NSString *json = @"[1,2,3]"; - NSMutableArray *array = RCTJSONParseMutable(json, NULL); + NSMutableArray *array = RCTJSONParseMutable(json, NULL); XCTAssertNoThrow([array addObject:@4]); XCTAssertEqualObjects(array, (@[@1, @2, @3, @4])); } - (void)testLeadingWhitespace { - NSDictionary *obj = @{@"foo": @"bar"}; + NSDictionary *obj = @{@"foo": @"bar"}; NSString *json = @" \r\n\t{\"foo\":\"bar\"}"; XCTAssertEqualObjects(obj, RCTJSONParse(json, NULL)); } diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTMethodArgumentTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTMethodArgumentTests.m index 3f5381247..53d9d0f74 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTMethodArgumentTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTMethodArgumentTests.m @@ -23,14 +23,14 @@ @implementation RCTMethodArgumentTests -extern void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **argTypes); +extern SEL RCTParseMethodSignature(NSString *methodSignature, NSArray **argTypes); - (void)testOneArgument { NSArray *arguments; - NSString *methodName = @"foo:(NSInteger)foo"; - RCTParseObjCMethodName(&methodName, &arguments); - XCTAssertEqualObjects(methodName, @"foo:"); + NSString *methodSignature = @"foo:(NSInteger)foo"; + SEL selector = RCTParseMethodSignature(methodSignature, &arguments); + XCTAssertEqualObjects(NSStringFromSelector(selector), @"foo:"); XCTAssertEqual(arguments.count, (NSUInteger)1); XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSInteger"); } @@ -38,9 +38,9 @@ extern void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **argTypes - (void)testTwoArguments { NSArray *arguments; - NSString *methodName = @"foo:(NSInteger)foo bar:(BOOL)bar"; - RCTParseObjCMethodName(&methodName, &arguments); - XCTAssertEqualObjects(methodName, @"foo:bar:"); + NSString *methodSignature = @"foo:(NSInteger)foo bar:(BOOL)bar"; + SEL selector = RCTParseMethodSignature(methodSignature, &arguments); + XCTAssertEqualObjects(NSStringFromSelector(selector), @"foo:bar:"); XCTAssertEqual(arguments.count, (NSUInteger)2); XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSInteger"); XCTAssertEqualObjects(((RCTMethodArgument *)arguments[1]).type, @"BOOL"); @@ -49,9 +49,9 @@ extern void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **argTypes - (void)testSpaces { NSArray *arguments; - NSString *methodName = @"foo : (NSInteger)foo bar : (BOOL) bar"; - RCTParseObjCMethodName(&methodName, &arguments); - XCTAssertEqualObjects(methodName, @"foo:bar:"); + NSString *methodSignature = @"foo : (NSInteger)foo bar : (BOOL) bar"; + SEL selector = RCTParseMethodSignature(methodSignature, &arguments); + XCTAssertEqualObjects(NSStringFromSelector(selector), @"foo:bar:"); XCTAssertEqual(arguments.count, (NSUInteger)2); XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSInteger"); XCTAssertEqualObjects(((RCTMethodArgument *)arguments[1]).type, @"BOOL"); @@ -60,9 +60,9 @@ extern void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **argTypes - (void)testNewlines { NSArray *arguments; - NSString *methodName = @"foo : (NSInteger)foo\nbar : (BOOL) bar"; - RCTParseObjCMethodName(&methodName, &arguments); - XCTAssertEqualObjects(methodName, @"foo:bar:"); + NSString *methodSignature = @"foo : (NSInteger)foo\nbar : (BOOL) bar"; + SEL selector = RCTParseMethodSignature(methodSignature, &arguments); + XCTAssertEqualObjects(NSStringFromSelector(selector), @"foo:bar:"); XCTAssertEqual(arguments.count, (NSUInteger)2); XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSInteger"); XCTAssertEqualObjects(((RCTMethodArgument *)arguments[1]).type, @"BOOL"); @@ -71,9 +71,9 @@ extern void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **argTypes - (void)testUnnamedArgs { NSArray *arguments; - NSString *methodName = @"foo:(NSInteger)foo:(BOOL)bar"; - RCTParseObjCMethodName(&methodName, &arguments); - XCTAssertEqualObjects(methodName, @"foo::"); + NSString *methodSignature = @"foo:(NSInteger)foo:(BOOL)bar"; + SEL selector = RCTParseMethodSignature(methodSignature, &arguments); + XCTAssertEqualObjects(NSStringFromSelector(selector), @"foo::"); XCTAssertEqual(arguments.count, (NSUInteger)2); XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSInteger"); XCTAssertEqualObjects(((RCTMethodArgument *)arguments[1]).type, @"BOOL"); @@ -82,9 +82,9 @@ extern void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **argTypes - (void)testUntypedUnnamedArgs { NSArray *arguments; - NSString *methodName = @"foo:foo:bar:bar"; - RCTParseObjCMethodName(&methodName, &arguments); - XCTAssertEqualObjects(methodName, @"foo:::"); + NSString *methodSignature = @"foo:foo:bar:bar"; + SEL selector = RCTParseMethodSignature(methodSignature, &arguments); + XCTAssertEqualObjects(NSStringFromSelector(selector), @"foo:::"); XCTAssertEqual(arguments.count, (NSUInteger)3); XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"id"); XCTAssertEqualObjects(((RCTMethodArgument *)arguments[1]).type, @"id"); @@ -94,9 +94,9 @@ extern void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **argTypes - (void)testAttributes { NSArray *arguments; - NSString *methodName = @"foo:(__attribute__((nonnull)) NSString *)foo bar:(__unused BOOL)bar"; - RCTParseObjCMethodName(&methodName, &arguments); - XCTAssertEqualObjects(methodName, @"foo:bar:"); + NSString *methodSignature = @"foo:(__attribute__((unused)) NSString *)foo bar:(__unused BOOL)bar"; + SEL selector = RCTParseMethodSignature(methodSignature, &arguments); + XCTAssertEqualObjects(NSStringFromSelector(selector), @"foo:bar:"); XCTAssertEqual(arguments.count, (NSUInteger)2); XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSString"); XCTAssertEqualObjects(((RCTMethodArgument *)arguments[1]).type, @"BOOL"); @@ -105,9 +105,9 @@ extern void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **argTypes - (void)testNullability { NSArray *arguments; - NSString *methodName = @"foo:(nullable NSString *)foo bar:(nonnull NSNumber *)bar baz:(id)baz"; - RCTParseObjCMethodName(&methodName, &arguments); - XCTAssertEqualObjects(methodName, @"foo:bar:baz:"); + NSString *methodSignature = @"foo:(nullable NSString *)foo bar:(nonnull NSNumber *)bar baz:(id)baz"; + SEL selector = RCTParseMethodSignature(methodSignature, &arguments); + XCTAssertEqualObjects(NSStringFromSelector(selector), @"foo:bar:baz:"); XCTAssertEqual(arguments.count, (NSUInteger)3); XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSString"); XCTAssertEqualObjects(((RCTMethodArgument *)arguments[1]).type, @"NSNumber"); @@ -120,9 +120,9 @@ extern void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **argTypes - (void)testSemicolonStripping { NSArray *arguments; - NSString *methodName = @"foo:(NSString *)foo bar:(BOOL)bar;"; - RCTParseObjCMethodName(&methodName, &arguments); - XCTAssertEqualObjects(methodName, @"foo:bar:"); + NSString *methodSignature = @"foo:(NSString *)foo bar:(BOOL)bar;"; + SEL selector = RCTParseMethodSignature(methodSignature, &arguments); + XCTAssertEqualObjects(NSStringFromSelector(selector), @"foo:bar:"); XCTAssertEqual(arguments.count, (NSUInteger)2); XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSString"); XCTAssertEqualObjects(((RCTMethodArgument *)arguments[1]).type, @"BOOL"); @@ -131,9 +131,9 @@ extern void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **argTypes - (void)testUnused { NSArray *arguments; - NSString *methodName = @"foo:(__unused NSString *)foo bar:(NSNumber *)bar"; - RCTParseObjCMethodName(&methodName, &arguments); - XCTAssertEqualObjects(methodName, @"foo:bar:"); + NSString *methodSignature = @"foo:(__unused NSString *)foo bar:(NSNumber *)bar"; + SEL selector = RCTParseMethodSignature(methodSignature, &arguments); + XCTAssertEqualObjects(NSStringFromSelector(selector), @"foo:bar:"); XCTAssertEqual(arguments.count, (NSUInteger)2); XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSString"); XCTAssertEqualObjects(((RCTMethodArgument *)arguments[1]).type, @"NSNumber"); @@ -141,4 +141,44 @@ extern void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **argTypes XCTAssertFalse(((RCTMethodArgument *)arguments[1]).unused); } +- (void)testGenericArray +{ + NSArray *arguments; + NSString *methodSignature = @"foo:(NSArray *)foo;"; + SEL selector = RCTParseMethodSignature(methodSignature, &arguments); + XCTAssertEqualObjects(NSStringFromSelector(selector), @"foo:"); + XCTAssertEqual(arguments.count, (NSUInteger)1); + XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSStringArray"); +} + +- (void)testNestedGenericArray +{ + NSArray *arguments; + NSString *methodSignature = @"foo:(NSArray *> *)foo;"; + SEL selector = RCTParseMethodSignature(methodSignature, &arguments); + XCTAssertEqualObjects(NSStringFromSelector(selector), @"foo:"); + XCTAssertEqual(arguments.count, (NSUInteger)1); + XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSStringArrayArray"); +} + +- (void)testGenericSet +{ + NSArray *arguments; + NSString *methodSignature = @"foo:(NSSet *)foo;"; + SEL selector = RCTParseMethodSignature(methodSignature, &arguments); + XCTAssertEqualObjects(NSStringFromSelector(selector), @"foo:"); + XCTAssertEqual(arguments.count, (NSUInteger)1); + XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSNumberSet"); +} + +- (void)testGenericDictionary +{ + NSArray *arguments; + NSString *methodSignature = @"foo:(NSDictionary *)foo;"; + SEL selector = RCTParseMethodSignature(methodSignature, &arguments); + XCTAssertEqualObjects(NSStringFromSelector(selector), @"foo:"); + XCTAssertEqual(arguments.count, (NSUInteger)1); + XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSNumberDictionary"); +} + @end diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTModuleMethodTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTModuleMethodTests.m index b11bffa26..f247da4ff 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTModuleMethodTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTModuleMethodTests.m @@ -23,6 +23,7 @@ static BOOL RCTLogsError(void (^block)(void)) { __block BOOL loggedError = NO; RCTPerformBlockWithLogFunction(block, ^(RCTLogLevel level, + __unused RCTLogSource source, __unused NSString *fileName, __unused NSNumber *lineNumber, __unused NSString *message) { @@ -46,10 +47,10 @@ static BOOL RCTLogsError(void (^block)(void)) - (void)testNonnull { - NSString *methodName = @"doFooWithBar:(nonnull NSString *)bar"; - RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithObjCMethodName:methodName - JSMethodName:nil - moduleClass:[self class]]; + NSString *methodSignature = @"doFooWithBar:(nonnull NSString *)bar"; + RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithMethodSignature:methodSignature + JSMethodName:nil + moduleClass:[self class]]; XCTAssertFalse(RCTLogsError(^{ [method invokeWithBridge:nil module:self arguments:@[@"Hello World"]]; })); @@ -64,45 +65,47 @@ static BOOL RCTLogsError(void (^block)(void)) - (void)doFooWithInteger:(__unused NSInteger)n { } - (void)doFooWithCGRect:(CGRect)s { _s = s; } +- (void)doFoo : (__unused NSString *)foo { } + - (void)testNumbersNonnull { { // Specifying an NSNumber param without nonnull isn't allowed XCTAssertTrue(RCTLogsError(^{ - NSString *methodName = @"doFooWithNumber:(NSNumber *)n"; - RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithObjCMethodName:methodName - JSMethodName:nil - moduleClass:[self class]]; + NSString *methodSignature = @"doFooWithNumber:(NSNumber *)n"; + RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithMethodSignature:methodSignature + JSMethodName:nil + moduleClass:[self class]]; // Invoke method to trigger parsing [method invokeWithBridge:nil module:self arguments:@[@1]]; })); } { - NSString *methodName = @"doFooWithNumber:(nonnull NSNumber *)n"; - RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithObjCMethodName:methodName - JSMethodName:nil - moduleClass:[self class]]; + NSString *methodSignature = @"doFooWithNumber:(nonnull NSNumber *)n"; + RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithMethodSignature:methodSignature + JSMethodName:nil + moduleClass:[self class]]; XCTAssertTrue(RCTLogsError(^{ [method invokeWithBridge:nil module:self arguments:@[[NSNull null]]]; })); } { - NSString *methodName = @"doFooWithDouble:(double)n"; - RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithObjCMethodName:methodName - JSMethodName:nil - moduleClass:[self class]]; + NSString *methodSignature = @"doFooWithDouble:(double)n"; + RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithMethodSignature:methodSignature + JSMethodName:nil + moduleClass:[self class]]; XCTAssertTrue(RCTLogsError(^{ [method invokeWithBridge:nil module:self arguments:@[[NSNull null]]]; })); } { - NSString *methodName = @"doFooWithInteger:(NSInteger)n"; - RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithObjCMethodName:methodName - JSMethodName:nil - moduleClass:[self class]]; + NSString *methodSignature = @"doFooWithInteger:(NSInteger)n"; + RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithMethodSignature:methodSignature + JSMethodName:nil + moduleClass:[self class]]; XCTAssertTrue(RCTLogsError(^{ [method invokeWithBridge:nil module:self arguments:@[[NSNull null]]]; })); @@ -111,14 +114,32 @@ static BOOL RCTLogsError(void (^block)(void)) - (void)testStructArgument { - NSString *methodName = @"doFooWithCGRect:(CGRect)s"; - RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithObjCMethodName:methodName - JSMethodName:nil - moduleClass:[self class]]; + NSString *methodSignature = @"doFooWithCGRect:(CGRect)s"; + RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithMethodSignature:methodSignature + JSMethodName:nil + moduleClass:[self class]]; CGRect r = CGRectMake(10, 20, 30, 40); [method invokeWithBridge:nil module:self arguments:@[@[@10, @20, @30, @40]]]; XCTAssertTrue(CGRectEqualToRect(r, _s)); } +- (void)testWhitespaceTolerance +{ + NSString *methodSignature = @"doFoo : \t (NSString *)foo"; + + __block RCTModuleMethod *method; + XCTAssertFalse(RCTLogsError(^{ + method = [[RCTModuleMethod alloc] initWithMethodSignature:methodSignature + JSMethodName:nil + moduleClass:[self class]]; + })); + + XCTAssertEqualObjects(method.JSMethodName, @"doFoo"); + + XCTAssertFalse(RCTLogsError(^{ + [method invokeWithBridge:nil module:self arguments:@[@"bar"]]; + })); +} + @end diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTShadowViewTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTShadowViewTests.m index 9bdda8a8d..f2715947a 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTShadowViewTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTShadowViewTests.m @@ -83,7 +83,8 @@ [parentView insertReactSubview:mainView atIndex:1]; [parentView insertReactSubview:footerView atIndex:2]; - [parentView collectRootUpdatedFrames:nil parentConstraint:CGSizeZero]; + parentView.reactTag = @1; // must be valid rootView tag + [parentView collectRootUpdatedFrames]; XCTAssertTrue(CGRectEqualToRect([parentView measureLayoutRelativeToAncestor:parentView], CGRectMake(0, 0, 440, 440))); XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets([parentView paddingAsInsets], UIEdgeInsetsMake(10, 10, 10, 10))); diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTSparseArrayTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTSparseArrayTests.m deleted file mode 100644 index 57e7a0bf2..000000000 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTSparseArrayTests.m +++ /dev/null @@ -1,55 +0,0 @@ -/** - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#import - -#import "RCTSparseArray.h" -#import "UIView+React.h" - -@interface RCTSparseArrayTests : XCTestCase - -@end - -@implementation RCTSparseArrayTests - -- (void)testDictionary -{ - id myView = [UIView new]; - myView.reactTag = @4; - - id myOtherView = [UIView new]; - myOtherView.reactTag = @5; - - RCTSparseArray *registry = [RCTSparseArray new]; - XCTAssertNil(registry[@4], @"how did you have a view when none are registered?"); - XCTAssertNil(registry[@5], @"how did you have a view when none are registered?"); - - registry[myView.reactTag] = myView; - XCTAssertEqual(registry[@4], myView); - XCTAssertNil(registry[@5], @"didn't register other view yet"); - - registry[myOtherView.reactTag] = myOtherView; - XCTAssertEqual(registry[@4], myView); - XCTAssertEqual(registry[@5], myOtherView); - - registry[myView.reactTag] = nil; - XCTAssertNil(registry[@4]); - XCTAssertEqual(registry[@5], myOtherView); - - registry[myOtherView.reactTag] = nil; - XCTAssertNil(registry[@4], @"how did you have a view when none are registered?"); - XCTAssertNil(registry[@5], @"how did you have a view when none are registered?"); -} - -@end diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTUIManagerTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTUIManagerTests.m index 9be80dca9..4ae4b9f5c 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTUIManagerTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTUIManagerTests.m @@ -14,7 +14,6 @@ #import -#import "RCTSparseArray.h" #import "RCTUIManager.h" #import "UIView+React.h" @@ -26,9 +25,9 @@ addChildReactTags:(NSArray *)addChildReactTags addAtIndices:(NSArray *)addAtIndices removeAtIndices:(NSArray *)removeAtIndices - registry:(RCTSparseArray *)registry; + registry:(NSDictionary> *)registry; -@property (nonatomic, readonly) RCTSparseArray *viewRegistry; +@property (nonatomic, copy, readonly) NSMutableDictionary *viewRegistry; @end @@ -50,13 +49,13 @@ for (NSInteger i = 1; i <= 20; i++) { UIView *registeredView = [UIView new]; registeredView.reactTag = @(i); - _uiManager.viewRegistry[i] = registeredView; + _uiManager.viewRegistry[@(i)] = registeredView; } } - (void)testManagingChildrenToAddViews { - UIView *containerView = _uiManager.viewRegistry[20]; + UIView *containerView = _uiManager.viewRegistry[@20]; NSMutableArray *addedViews = [NSMutableArray array]; NSArray *tagsToAdd = @[@1, @2, @3, @4, @5]; @@ -86,7 +85,7 @@ - (void)testManagingChildrenToRemoveViews { - UIView *containerView = _uiManager.viewRegistry[20]; + UIView *containerView = _uiManager.viewRegistry[@20]; NSMutableArray *removedViews = [NSMutableArray array]; NSArray *removeAtIndices = @[@0, @4, @8, @12, @16]; @@ -95,7 +94,7 @@ [removedViews addObject:_uiManager.viewRegistry[reactTag]]; } for (NSInteger i = 2; i < 20; i++) { - UIView *view = _uiManager.viewRegistry[i]; + UIView *view = _uiManager.viewRegistry[@(i)]; [containerView addSubview:view]; } @@ -119,7 +118,7 @@ _uiManager.viewRegistry[view.reactTag] = view; } for (NSInteger i = 2; i < 20; i++) { - UIView *view = _uiManager.viewRegistry[i]; + UIView *view = _uiManager.viewRegistry[@(i)]; if (![removedViews containsObject:view]) { XCTAssertTrue([view superview] == containerView, @"Should not have removed view with react tag %ld during delete but did", (long)i); @@ -138,7 +137,7 @@ // [11,5,1,2,7,8,12,10] - (void)testManagingChildrenToAddRemoveAndMove { - UIView *containerView = _uiManager.viewRegistry[20]; + UIView *containerView = _uiManager.viewRegistry[@20]; NSArray *removeAtIndices = @[@2, @3, @5, @8]; NSArray *addAtIndices = @[@0, @6]; @@ -155,7 +154,7 @@ } for (NSInteger i = 1; i < 11; i++) { - UIView *view = _uiManager.viewRegistry[i]; + UIView *view = _uiManager.viewRegistry[@(i)]; [containerView addSubview:view]; } @@ -180,7 +179,7 @@ // Clean up after ourselves for (NSInteger i = 1; i < 13; i++) { - UIView *view = _uiManager.viewRegistry[i]; + UIView *view = _uiManager.viewRegistry[@(i)]; [view removeFromSuperview]; } for (UIView *view in viewsToRemove) { diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTURLUtilsTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTURLUtilsTests.m new file mode 100644 index 000000000..c2f6db568 --- /dev/null +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTURLUtilsTests.m @@ -0,0 +1,96 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import +#import "RCTUtils.h" + +@interface RCTURLUtilsTests : XCTestCase + +@end + +@implementation RCTURLUtilsTests + +- (void)testGetQueryParam +{ + NSURL *URL = [NSURL URLWithString:@"http://example.com?foo=bar&bar=foo"]; + NSString *foo = RCTGetURLQueryParam(URL, @"foo"); + NSString *bar = RCTGetURLQueryParam(URL, @"bar"); + XCTAssertEqualObjects(foo, @"bar"); + XCTAssertEqualObjects(bar, @"foo"); +} + +- (void)testGetEncodedParam +{ + NSURL *URL = [NSURL URLWithString:@"http://example.com?foo=You%20%26%20Me"]; + NSString *foo = RCTGetURLQueryParam(URL, @"foo"); + XCTAssertEqualObjects(foo, @"You & Me"); +} + +- (void)testQueryParamNotFound +{ + NSURL *URL = [NSURL URLWithString:@"http://example.com?foo=bar"]; + NSString *bar = RCTGetURLQueryParam(URL, @"bar"); + XCTAssertNil(bar); +} + +- (void)testDuplicateParamTakesLatter +{ + NSURL *URL = [NSURL URLWithString:@"http://example.com?foo=bar&foo=foo"]; + NSString *foo = RCTGetURLQueryParam(URL, @"foo"); + XCTAssertEqualObjects(foo, @"foo"); +} + +- (void)testNilURLGetQueryParam +{ + NSURL *URL = nil; + NSString *foo = RCTGetURLQueryParam(URL, @"foo"); + XCTAssertNil(foo); +} + +- (void)testReplaceParam +{ + NSURL *URL = [NSURL URLWithString:@"http://example.com?foo=bar&bar=foo"]; + NSURL *result = RCTURLByReplacingQueryParam(URL, @"foo", @"foo"); + XCTAssertEqualObjects(result.absoluteString, @"http://example.com?foo=foo&bar=foo"); +} + +- (void)testReplaceEncodedParam +{ + NSURL *URL = [NSURL URLWithString:@"http://example.com?foo=You%20%26%20Me"]; + NSURL *result = RCTURLByReplacingQueryParam(URL, @"foo", @"Me & You"); + XCTAssertEqualObjects(result.absoluteString, @"http://example.com?foo=Me%20%26%20You"); +} + +- (void)testAppendParam +{ + NSURL *URL = [NSURL URLWithString:@"http://example.com?bar=foo"]; + NSURL *result = RCTURLByReplacingQueryParam(URL, @"foo", @"bar"); + XCTAssertEqualObjects(result.absoluteString, @"http://example.com?bar=foo&foo=bar"); +} + +- (void)testRemoveParam +{ + NSURL *URL = [NSURL URLWithString:@"http://example.com?bar=foo&foo=bar"]; + NSURL *result = RCTURLByReplacingQueryParam(URL, @"bar", nil); + XCTAssertEqualObjects(result.absoluteString, @"http://example.com?foo=bar"); +} + +- (void)testNilURLAppendQueryParam +{ + NSURL *URL = nil; + NSURL *result = RCTURLByReplacingQueryParam(URL, @"foo", @"bar"); + XCTAssertNil(result); +} + +@end diff --git a/Examples/UIExplorer/ViewExample.js b/Examples/UIExplorer/ViewExample.js index 312fbe230..deed55067 100644 --- a/Examples/UIExplorer/ViewExample.js +++ b/Examples/UIExplorer/ViewExample.js @@ -40,16 +40,6 @@ var ViewBorderStyleExample = React.createClass({ }, render() { - if (Platform.OS !== 'android') { - return ( - - - borderStyle is only supported on android for now. - - - ); - } - return ( @@ -85,7 +75,9 @@ var ViewBorderStyleExample = React.createClass({ }); exports.title = ''; -exports.description = 'Basic building block of all UI.'; +exports.description = 'Basic building block of all UI, examples that ' + + 'demonstrate some of the many styles available.'; + exports.displayName = 'ViewExample'; exports.examples = [ { diff --git a/Examples/UIExplorer/ViewPagerAndroidExample.android.js b/Examples/UIExplorer/ViewPagerAndroidExample.android.js new file mode 100644 index 000000000..e4f33458e --- /dev/null +++ b/Examples/UIExplorer/ViewPagerAndroidExample.android.js @@ -0,0 +1,252 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +'use strict'; + +var React = require('react-native'); +var { + Image, + StyleSheet, + Text, + TouchableWithoutFeedback, + TouchableOpacity, + View, + ViewPagerAndroid, +} = React; + +var PAGES = 5; +var BGCOLOR = ['#fdc08e', '#fff6b9', '#99d1b7', '#dde5fe', '#f79273']; +var IMAGE_URIS = [ + 'http://apod.nasa.gov/apod/image/1410/20141008tleBaldridge001h990.jpg', + 'http://apod.nasa.gov/apod/image/1409/volcanicpillar_vetter_960.jpg', + 'http://apod.nasa.gov/apod/image/1409/m27_snyder_960.jpg', + 'http://apod.nasa.gov/apod/image/1409/PupAmulti_rot0.jpg', + 'http://apod.nasa.gov/apod/image/1510/lunareclipse_27Sep_beletskycrop4.jpg', +]; + +var LikeCount = React.createClass({ + getInitialState: function() { + return { + likes: 7, + }; + }, + onClick: function() { + this.setState({likes: this.state.likes + 1}); + }, + render: function() { + var thumbsUp = '\uD83D\uDC4D'; + return ( + + + + {thumbsUp + ' Like'} + + + + {this.state.likes + ' likes'} + + + ); + }, +}); + +var Button = React.createClass({ + _handlePress: function() { + if (this.props.enabled && this.props.onPress) { + this.props.onPress(); + } + }, + render: function() { + return ( + + + {this.props.text} + + + ); + } +}); + +var ProgressBar = React.createClass({ + render: function() { + var fractionalPosition = (this.props.progress.position + this.props.progress.offset); + var progressBarSize = (fractionalPosition / (PAGES - 1)) * this.props.size; + return ( + + + + ); + } +}); + +var ViewPagerAndroidExample = React.createClass({ + statics: { + title: '', + description: 'Container that allows to flip left and right between child views.' + }, + getInitialState: function() { + return { + page: 0, + animationsAreEnabled: true, + progress: { + position: 0, + offset: 0, + }, + }; + }, + + onPageSelected: function(e) { + this.setState({page: e.nativeEvent.position}); + }, + + onPageScroll: function(e) { + this.setState({progress: e.nativeEvent}); + }, + + move: function(delta) { + var page = this.state.page + delta; + this.go(page); + }, + + go: function(page) { + if (this.state.animationsAreEnabled) { + this.viewPager.setPage(page); + } else { + this.viewPager.setPageWithoutAnimation(page); + } + + this.setState({page}); + }, + + render: function() { + var pages = []; + for (var i = 0; i < PAGES; i++) { + var pageStyle = { + backgroundColor: BGCOLOR[i % BGCOLOR.length], + alignItems: 'center', + padding: 20, + }; + pages.push( + + + + + ); + } + var { page, animationsAreEnabled } = this.state; + return ( + + { this.viewPager = viewPager; }}> + {pages} + + + { animationsAreEnabled ? +