mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Compare commits
23
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab50c6ee2e | ||
|
|
dc453da92c | ||
|
|
8b6d7fddd6 | ||
|
|
614a370d86 | ||
|
|
c97015d2fa | ||
|
|
c282c2d3b7 | ||
|
|
2133172ea2 | ||
|
|
013e623fbb | ||
|
|
b4a1d2ba64 | ||
|
|
0d7586c620 | ||
|
|
d552362f85 | ||
|
|
1594af13bc | ||
|
|
038cdda238 | ||
|
|
ea5109fc0e | ||
|
|
b23dd8565a | ||
|
|
d20b34a562 | ||
|
|
a0429ebb54 | ||
|
|
ab829005bd | ||
|
|
27073242f2 | ||
|
|
b78f6cee7d | ||
|
|
10daf76e68 | ||
|
|
e6eb81e097 | ||
|
|
72eb06009b |
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"presets": [
|
||||
"module:metro-react-native-babel-preset"
|
||||
],
|
||||
"plugins": [
|
||||
"babel-plugin-transform-flow-enums"
|
||||
]
|
||||
}
|
||||
+1
-8
@@ -1,10 +1,6 @@
|
||||
|
||||
[android]
|
||||
target = android-31
|
||||
|
||||
[kotlin]
|
||||
compile_against_abis = True
|
||||
kotlin_version = 1.6.10
|
||||
target = android-30
|
||||
|
||||
[download]
|
||||
max_number_of_retries = 3
|
||||
@@ -15,6 +11,3 @@
|
||||
|
||||
[alias]
|
||||
rntester = //packages/rn-tester/android/app:app
|
||||
|
||||
[buildfile]
|
||||
includes = //tools/build_defs/oss/preload.bzl
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
BUNDLE_PATH: "vendor/bundle"
|
||||
BUNDLE_FORCE_RUBY_PLATFORM: 1
|
||||
@@ -14,31 +14,41 @@
|
||||
# and build a Android application that can be used to run the
|
||||
# tests specified in the scripts/ directory.
|
||||
#
|
||||
FROM reactnativecommunity/react-native-android:7.0
|
||||
FROM reactnativecommunity/react-native-android:4.0
|
||||
|
||||
LABEL Description="React Native Android Test Image"
|
||||
LABEL maintainer="Héctor Ramos <hector@fb.com>"
|
||||
|
||||
# set default environment variables
|
||||
ENV GRADLE_OPTS="-Dorg.gradle.daemon=false -Dfile.encoding=utf-8 -Dorg.gradle.jvmargs=\"-Xmx512m -XX:+HeapDumpOnOutOfMemoryError\""
|
||||
ENV KOTLIN_HOME="packages/react-native/third-party/kotlin"
|
||||
ENV GRADLE_OPTS="-Dorg.gradle.daemon=false -Dorg.gradle.jvmargs=\"-Xmx512m -XX:+HeapDumpOnOutOfMemoryError\""
|
||||
ENV JAVA_TOOL_OPTIONS="-Dfile.encoding=UTF8"
|
||||
|
||||
ADD .buckconfig /app/.buckconfig
|
||||
ADD .buckjavaargs /app/.buckjavaargs
|
||||
ADD BUCK /app/BUCK
|
||||
ADD packages/react-native/Libraries /app/packages/react-native/Libraries
|
||||
ADD packages/react-native/ReactAndroid /app/packages/react-native/ReactAndroid
|
||||
ADD packages/react-native/ReactCommon /app/packages/react-native/ReactCommon
|
||||
ADD packages/react-native/React /app/packages/react-native/React
|
||||
ADD Libraries /app/Libraries
|
||||
ADD ReactAndroid /app/ReactAndroid
|
||||
ADD ReactCommon /app/ReactCommon
|
||||
ADD React /app/React
|
||||
ADD keystores /app/keystores
|
||||
ADD packages/react-native-codegen /app/packages/react-native-codegen
|
||||
ADD tools /app/tools
|
||||
ADD scripts /app/scripts
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN buck fetch ReactAndroid/src/test/java/com/facebook/react/modules
|
||||
RUN buck fetch ReactAndroid/src/main/java/com/facebook/react
|
||||
RUN buck fetch ReactAndroid/src/main/java/com/facebook/react/shell
|
||||
RUN buck fetch ReactAndroid/src/test/...
|
||||
RUN buck fetch ReactAndroid/src/androidTest/...
|
||||
|
||||
RUN buck build ReactAndroid/src/main/java/com/facebook/react
|
||||
RUN buck build ReactAndroid/src/main/java/com/facebook/react/shell
|
||||
|
||||
ADD . /app
|
||||
|
||||
RUN yarn
|
||||
|
||||
RUN ./gradlew :packages:react-native:ReactAndroid:assembleDebug
|
||||
RUN ./gradlew :ReactAndroid:downloadBoost :ReactAndroid:downloadDoubleConversion :ReactAndroid:downloadFolly :ReactAndroid:downloadGlog
|
||||
|
||||
RUN ./gradlew :ReactAndroid:packageReactNdkLibsForBuck -Pjobs=1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/bash
|
||||
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
@@ -33,7 +33,7 @@ node cli.js bundle --platform android --dev true --entry-file ReactAndroid/src/a
|
||||
|
||||
# build test APK
|
||||
# shellcheck disable=SC1091
|
||||
source ./scripts/android-setup.sh && NO_BUCKD=1 scripts/retry3 buck install ReactAndroid/src/androidTest/buck-runner:instrumentation-tests --config build.threads=1
|
||||
source ./scripts/android-setup.sh && NO_BUCKD=1 retry3 buck install ReactAndroid/src/androidTest/buck-runner:instrumentation-tests --config build.threads=1
|
||||
|
||||
# run installed apk with tests
|
||||
node ./.circleci/Dockerfiles/scripts/run-android-ci-instrumentation-tests.js "$*"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/bash
|
||||
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/bash
|
||||
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
@@ -141,7 +141,7 @@ function e2e_suite() {
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! (cd packages/react-native && FILENAME=$(npm pack | tail -1) && mv "$FILENAME" ../../)
|
||||
if ! npm pack
|
||||
then
|
||||
echo "Failed to pack react-native"
|
||||
return 1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/bash
|
||||
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
+472
-1456
File diff suppressed because it is too large
Load Diff
@@ -1,36 +0,0 @@
|
||||
storage: ./storage
|
||||
auth:
|
||||
htpasswd:
|
||||
file: ./htpasswd
|
||||
uplinks:
|
||||
npmjs:
|
||||
url: https://registry.npmjs.org/
|
||||
max_fails: 40
|
||||
maxage: 30m
|
||||
timeout: 60s
|
||||
fail_timeout: 10m
|
||||
cache: false
|
||||
agent_options:
|
||||
keepAlive: true
|
||||
maxSockets: 40
|
||||
maxFreeSockets: 10
|
||||
packages:
|
||||
# Get @react-native/normalize-color from npm registry, since its used in deprecated-react-native-prop-types package
|
||||
'@react-native/normalize-color':
|
||||
access: $all
|
||||
publish: $authenticated
|
||||
proxy: npmjs
|
||||
# Group and isolate all local packages, avoid being proxy from outside
|
||||
'@react-native/*':
|
||||
access: $all
|
||||
publish: $all
|
||||
'@*/*':
|
||||
access: $all
|
||||
publish: $authenticated
|
||||
proxy: npmjs
|
||||
'**':
|
||||
access: $all
|
||||
publish: $all
|
||||
proxy: npmjs
|
||||
logs:
|
||||
- {type: file, path: verdaccio.log, format: json, level: warn}
|
||||
@@ -18,3 +18,7 @@ indent_size = 4
|
||||
|
||||
[BUCK]
|
||||
indent_size = 4
|
||||
|
||||
# Windows files
|
||||
[*.bat]
|
||||
end_of_line = crlf
|
||||
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
**/main.js
|
||||
**/staticBundle.js
|
||||
bots/node_modules
|
||||
docs/generatedComponentApiDocs.js
|
||||
packages/react-native/flow/
|
||||
packages/react-native/Libraries/Renderer/*
|
||||
packages/react-native/Libraries/vendor/**/*
|
||||
flow/
|
||||
Libraries/Renderer/*
|
||||
Libraries/vendor/**/*
|
||||
node_modules/
|
||||
packages/*/node_modules
|
||||
packages/react-native-codegen/lib
|
||||
tools/eslint/rules/sort-imports.js
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"root": true,
|
||||
|
||||
"extends": [
|
||||
"./packages/eslint-config-react-native-community/index.js"
|
||||
],
|
||||
|
||||
"plugins": [
|
||||
"@react-native/eslint-plugin-codegen"
|
||||
],
|
||||
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"Libraries/**/*.js",
|
||||
],
|
||||
"rules": {
|
||||
"@react-native-community/no-haste-imports": 2,
|
||||
"@react-native-community/error-subclass-name": 2,
|
||||
"@react-native-community/platform-colors": 2,
|
||||
"@react-native/codegen/react-native-modules": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"flow-typed/**/*.js",
|
||||
],
|
||||
"rules": {
|
||||
quotes: 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"**/__fixtures__/**/*.js",
|
||||
"**/__mocks__/**/*.js",
|
||||
"**/__tests__/**/*.js",
|
||||
"jest/**/*.js",
|
||||
"packages/rn-tester/**/*.js",
|
||||
],
|
||||
"globals": {
|
||||
// Expose some Jest globals for test helpers
|
||||
"afterAll": true,
|
||||
"afterEach": true,
|
||||
"beforeAll": true,
|
||||
"beforeEach": true,
|
||||
"expect": true,
|
||||
"jest": true,
|
||||
},
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"**/__tests__/**/*-test.js",
|
||||
],
|
||||
"env": {
|
||||
"jasmine": true,
|
||||
"jest": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
-105
@@ -1,105 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const path = require('node:path');
|
||||
|
||||
require('eslint-plugin-lint').load(path.join(__dirname, 'tools/eslint/rules'));
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
|
||||
extends: ['@react-native'],
|
||||
|
||||
plugins: ['@react-native/eslint-plugin-specs', 'lint'],
|
||||
|
||||
overrides: [
|
||||
// overriding the JS config from eslint-config-react-native-community config to ensure
|
||||
// that we use hermes-eslint for all js files
|
||||
{
|
||||
files: ['*.js'],
|
||||
parser: 'hermes-eslint',
|
||||
rules: {
|
||||
// These rules are not required with hermes-eslint
|
||||
'ft-flow/define-flow-type': 0,
|
||||
'ft-flow/use-flow-type': 0,
|
||||
// flow handles this check for us, so it's not required
|
||||
'no-undef': 0,
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
files: ['packages/react-native/Libraries/**/*.js'],
|
||||
rules: {
|
||||
'@react-native/platform-colors': 2,
|
||||
'@react-native/specs/react-native-modules': 2,
|
||||
'lint/no-haste-imports': 2,
|
||||
'lint/no-react-native-imports': 2,
|
||||
'lint/require-extends-error': 2,
|
||||
'lint/sort-imports': 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['packages/react-native/flow-typed/**/*.js'],
|
||||
rules: {
|
||||
'lint/valid-flow-typed-signature': 2,
|
||||
'no-unused-vars': 0,
|
||||
quotes: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'**/__fixtures__/**/*.js',
|
||||
'**/__mocks__/**/*.js',
|
||||
'**/__tests__/**/*.js',
|
||||
'packages/react-native/jest/**/*.js',
|
||||
'packages/rn-tester/**/*.js',
|
||||
],
|
||||
globals: {
|
||||
// Expose some Jest globals for test helpers
|
||||
afterAll: true,
|
||||
afterEach: true,
|
||||
beforeAll: true,
|
||||
beforeEach: true,
|
||||
expect: true,
|
||||
jest: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/__tests__/**/*-test.js'],
|
||||
env: {
|
||||
jasmine: true,
|
||||
jest: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.{ts,tsx}'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['@typescript-eslint/eslint-plugin'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'react-native/no-inline-styles': 'off',
|
||||
'@typescript-eslint/no-shadow': 'off',
|
||||
'no-self-compare': 'off',
|
||||
'react/self-closing-comp': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.d.ts'],
|
||||
plugins: ['redundant-undefined'],
|
||||
rules: {
|
||||
'redundant-undefined/redundant-undefined': [
|
||||
'error',
|
||||
{followExactOptionalPropertyTypes: true},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
+11
-14
@@ -3,18 +3,16 @@
|
||||
.*/*[.]android.js
|
||||
|
||||
; Ignore templates for 'react-native init'
|
||||
<PROJECT_ROOT>/packages/react-native/template/.*
|
||||
<PROJECT_ROOT>/template/.*
|
||||
|
||||
; Ignore the Dangerfile
|
||||
<PROJECT_ROOT>/packages/react-native-bots/dangerfile.js
|
||||
<PROJECT_ROOT>/bots/dangerfile.js
|
||||
|
||||
; Ignore "BUCK" generated dirs
|
||||
<PROJECT_ROOT>/\.buckd/
|
||||
|
||||
; Flow doesn't support platforms
|
||||
.*/packages/react-native/Libraries/Utilities/LoadingView.js
|
||||
|
||||
.*/node_modules/resolve/test/resolver/malformed_package_json/package\.json$
|
||||
.*/Libraries/Utilities/LoadingView.js
|
||||
|
||||
[untyped]
|
||||
.*/node_modules/@react-native-community/cli/.*/.*
|
||||
@@ -25,16 +23,14 @@
|
||||
.*/node_modules/.*
|
||||
|
||||
[libs]
|
||||
packages/react-native/interface.js
|
||||
packages/react-native/flow/
|
||||
packages/react-native/flow-typed/
|
||||
interface.js
|
||||
flow/
|
||||
|
||||
[options]
|
||||
enums=true
|
||||
|
||||
emoji=true
|
||||
|
||||
exact_by_default=true
|
||||
indexed_access=false
|
||||
|
||||
format.bracket_spacing=false
|
||||
|
||||
@@ -44,9 +40,9 @@ module.file_ext=.ios.js
|
||||
|
||||
munge_underscores=true
|
||||
|
||||
module.name_mapper='^react-native$' -> '<PROJECT_ROOT>/packages/react-native/index.js'
|
||||
module.name_mapper='^react-native/\(.*\)$' -> '<PROJECT_ROOT>/packages/react-native\1'
|
||||
module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '<PROJECT_ROOT>/packages/react-native/Libraries/Image/RelativeImageStub'
|
||||
module.name_mapper='^react-native$' -> '<PROJECT_ROOT>/index.js'
|
||||
module.name_mapper='^react-native/\(.*\)$' -> '<PROJECT_ROOT>/\1'
|
||||
module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '<PROJECT_ROOT>/Libraries/Image/RelativeImageStub'
|
||||
|
||||
suppress_type=$FlowIssue
|
||||
suppress_type=$FlowFixMe
|
||||
@@ -63,6 +59,7 @@ nonstrict-import=warn
|
||||
deprecated-type=error
|
||||
unsafe-getters-setters=warn
|
||||
unnecessary-invariant=warn
|
||||
signature-verification-failure=warn
|
||||
|
||||
[strict]
|
||||
deprecated-type
|
||||
@@ -74,4 +71,4 @@ untyped-import
|
||||
untyped-type-import
|
||||
|
||||
[version]
|
||||
^0.202.0
|
||||
^0.158.0
|
||||
|
||||
+11
-14
@@ -3,18 +3,16 @@
|
||||
.*/*[.]ios.js
|
||||
|
||||
; Ignore templates for 'react-native init'
|
||||
<PROJECT_ROOT>/packages/react-native/template/.*
|
||||
<PROJECT_ROOT>/template/.*
|
||||
|
||||
; Ignore the Dangerfile
|
||||
<PROJECT_ROOT>/packages/react-native-bots/dangerfile.js
|
||||
<PROJECT_ROOT>/bots/dangerfile.js
|
||||
|
||||
; Ignore "BUCK" generated dirs
|
||||
<PROJECT_ROOT>/\.buckd/
|
||||
|
||||
; Flow doesn't support platforms
|
||||
.*/packages/react-native/Libraries/Utilities/LoadingView.js
|
||||
|
||||
.*/node_modules/resolve/test/resolver/malformed_package_json/package\.json$
|
||||
.*/Libraries/Utilities/LoadingView.js
|
||||
|
||||
[untyped]
|
||||
.*/node_modules/@react-native-community/cli/.*/.*
|
||||
@@ -25,16 +23,14 @@
|
||||
.*/node_modules/.*
|
||||
|
||||
[libs]
|
||||
packages/react-native/interface.js
|
||||
packages/react-native/flow/
|
||||
packages/react-native/flow-typed/
|
||||
interface.js
|
||||
flow/
|
||||
|
||||
[options]
|
||||
enums=true
|
||||
|
||||
emoji=true
|
||||
|
||||
exact_by_default=true
|
||||
indexed_access=false
|
||||
|
||||
format.bracket_spacing=false
|
||||
|
||||
@@ -44,9 +40,9 @@ module.file_ext=.android.js
|
||||
|
||||
munge_underscores=true
|
||||
|
||||
module.name_mapper='^react-native$' -> '<PROJECT_ROOT>/packages/react-native/index.js'
|
||||
module.name_mapper='^react-native/\(.*\)$' -> '<PROJECT_ROOT>/packages/react-native\1'
|
||||
module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '<PROJECT_ROOT>/packages/react-native/Libraries/Image/RelativeImageStub'
|
||||
module.name_mapper='^react-native$' -> '<PROJECT_ROOT>/index.js'
|
||||
module.name_mapper='^react-native/\(.*\)$' -> '<PROJECT_ROOT>/\1'
|
||||
module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '<PROJECT_ROOT>/Libraries/Image/RelativeImageStub'
|
||||
|
||||
suppress_type=$FlowIssue
|
||||
suppress_type=$FlowFixMe
|
||||
@@ -63,6 +59,7 @@ nonstrict-import=warn
|
||||
deprecated-type=error
|
||||
unsafe-getters-setters=warn
|
||||
unnecessary-invariant=warn
|
||||
signature-verification-failure=warn
|
||||
|
||||
[strict]
|
||||
deprecated-type
|
||||
@@ -74,4 +71,4 @@ untyped-import
|
||||
untyped-type-import
|
||||
|
||||
[version]
|
||||
^0.202.0
|
||||
^0.158.0
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# Windows files should use crlf line endings
|
||||
# https://help.github.com/articles/dealing-with-line-endings/
|
||||
*.bat text eol=crlf
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
|
||||
# GitHub Settings, Bots
|
||||
/.github/ @hramos
|
||||
/packages/react-native-bots @hramos
|
||||
/bots @hramos
|
||||
|
||||
# Continuous Integration
|
||||
/.circleci/ @hramos
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
name: "🐛 Bug Report"
|
||||
about: Report a reproducible bug or regression in React Native.
|
||||
title: ''
|
||||
labels: 'Needs: Triage :mag:'
|
||||
|
||||
---
|
||||
|
||||
Please provide all the information requested. Issues that do not follow this format are likely to stall.
|
||||
|
||||
## Description
|
||||
Please provide a clear and concise description of what the bug is. Include screenshots if needed.
|
||||
Please test using the latest React Native release to make sure your issue has not already been fixed: https://reactnative.dev/docs/upgrading.html
|
||||
|
||||
## React Native version:
|
||||
Run `react-native info` in your terminal and copy the results here.
|
||||
|
||||
## Steps To Reproduce
|
||||
Provide a detailed list of steps that reproduce the issue.
|
||||
|
||||
1.
|
||||
2.
|
||||
|
||||
## Expected Results
|
||||
Describe what you expected to happen.
|
||||
|
||||
## Snack, code example, screenshot, or link to a repository:
|
||||
Please provide a Snack (https://snack.expo.io/), a link to a repository on GitHub, or provide a minimal code example that reproduces the problem.
|
||||
You may provide a screenshot of the application if you think it is relevant to your bug report.
|
||||
Here are some tips for providing a minimal example: https://stackoverflow.com/help/mcve
|
||||
@@ -1,48 +0,0 @@
|
||||
name: 🐛 Bug Report
|
||||
description: Report a reproducible bug or regression in React Native.
|
||||
labels: ["Needs: Triage :mag:"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Please provide all the information requested. Issues that do not follow this format are likely to stall.
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
description: Please provide a clear and concise description of what the bug is. Include screenshots if needed. Test using the [latest React Native release](https://github.com/facebook/react-native/releases/latest) to make sure your issue has not already been fixed.
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: React Native Version
|
||||
description: What is the latest version of react-native that this issue reproduces on? Please only list the highest version you tested. Bear in mind that only issues on [supported versions](https://github.com/reactwg/react-native-releases#which-versions-are-currently-supported) will be looked into.
|
||||
placeholder: ex. 0.71.0
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: react-native-info
|
||||
attributes:
|
||||
label: Output of `npx react-native info`
|
||||
description: Run `npx react-native info` in your terminal, copy and paste the results here.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproduction
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: Provide a detailed list of steps that reproduce the issue.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: extra
|
||||
attributes:
|
||||
label: Snack, code example, screenshot, or link to a repository
|
||||
description: |
|
||||
Please provide a Snack (https://snack.expo.dev/), a link to a repository on GitHub, or provide a minimal code example that reproduces the problem.
|
||||
You may provide a screenshot of the application if you think it is relevant to your bug report.
|
||||
Here are some tips for providing a minimal example: https://stackoverflow.com/help/mcve
|
||||
Please note that a reproducer is **mandatory**. Issues without reproducer are more likely to stall and will be closed.
|
||||
validations:
|
||||
required: true
|
||||
@@ -3,12 +3,12 @@ contact_links:
|
||||
- name: 📃 Documentation Issue
|
||||
url: https://github.com/facebook/react-native-website/issues
|
||||
about: Please report documentation issues in the React Native website repository.
|
||||
- name: ⤴️ Upgrade Issue
|
||||
url: https://github.com/react-native-community/upgrade-support
|
||||
about: Need help upgrading to a newer React Native version? Visit the Upgrade Support repository.
|
||||
- name: 🤔 Questions and Help
|
||||
url: https://reactnative.dev/help
|
||||
about: Looking for help with your app? Please refer to the React Native community's support resources.
|
||||
- name: 💫 New Architecture - Questions & Technical Deep dive insights
|
||||
url: https://github.com/reactwg/react-native-new-architecture
|
||||
about: Questions and doubts related to technical questions for the New Architecture should be directed to the Working Group. Instructions on how to join are available in the README.
|
||||
- name: 🚀 Discussions and Proposals
|
||||
url: https://github.com/react-native-community/discussions-and-proposals
|
||||
about: Discuss the future of React Native in the React Native community's discussions and proposals repository.
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
name: 💫 New Architecture - Bug Report
|
||||
description: Report a reproducible bug or a build issue when using the New Architecture (Fabric & TurboModules) in React Native.
|
||||
labels: ["Needs: Triage :mag:", "Type: New Architecture"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Please provide all the information requested. Issues that do not follow this format are going to be closed.
|
||||
This issue report is reserved to bug & build issues for users on the New Architecture. If you're not using
|
||||
the New Architecture, please don't open issues on this category.
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
description: |
|
||||
Please provide a clear and concise description of what the bug or issue is. Include screenshots if needed.
|
||||
Please make sure you check the New Architecture documentation first, as your issue might
|
||||
already be answered there - https://reactnative.dev/docs/next/new-architecture-intro
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: React Native Version
|
||||
description: What is the latest version of react-native that this issue reproduces on? Please test against the latest stable version, and list only the highest version you tested. Bug reports against older versions are more likely to stall.
|
||||
placeholder: ex. 0.71.0
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: react-native-info
|
||||
attributes:
|
||||
label: Output of `npx react-native info`
|
||||
description: Run `npx react-native info` in your terminal, copy and paste the results here.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproduction
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: Provide a detailed list of steps that reproduce the issue.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: extra
|
||||
attributes:
|
||||
label: Snack, code example, screenshot, or link to a repository
|
||||
description: |
|
||||
Please provide a Snack (https://snack.expo.dev/), a link to a repository on GitHub, or provide a minimal code example that reproduces the problem.
|
||||
You may provide a screenshot of the application if you think it is relevant to your bug report.
|
||||
Here are some tips for providing a minimal example: https://stackoverflow.com/help/mcve
|
||||
Please note that a reproducer is **mandatory**. Issues without reproducer are more likely to stall and will be closed.
|
||||
validations:
|
||||
required: true
|
||||
@@ -1,46 +0,0 @@
|
||||
name: ⬆️ Upgrade - Build Regression
|
||||
description: If you are upgrading to a new React Native version (stable or pre-release) and encounter a build regression.
|
||||
labels: ["Needs: Triage :mag:", "Type: Upgrade Issue"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Please use this form to file an issue if you have upgraded or are upgrading to [latest stable release](https://github.com/facebook/react-native/releases/latest) and have experienced a regression (something that used to work in previous version).
|
||||
- type: input
|
||||
id: new-version
|
||||
attributes:
|
||||
label: New Version
|
||||
description: This is the version you are attempting to upgrade to.
|
||||
placeholder: ex. 0.66.1
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: old-version
|
||||
attributes:
|
||||
label: Old Version
|
||||
description: This is the version you were on where the behavior was working.
|
||||
placeholder: ex. 0.65.1
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: target
|
||||
attributes:
|
||||
label: Build Target(s)
|
||||
description: What target(s) are encountering this issue?
|
||||
placeholder: iOS simulator in release flavor
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: react-native-info
|
||||
attributes:
|
||||
label: Output of `react-native info`
|
||||
description: Run `react-native info` in your terminal, copy and paste the results here.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproduction
|
||||
attributes:
|
||||
label: Issue and Reproduction Steps
|
||||
description: Please describe the issue and list out commands run to reproduce.
|
||||
validations:
|
||||
required: true
|
||||
@@ -6,16 +6,12 @@
|
||||
|
||||
## Changelog
|
||||
|
||||
<!-- Help reviewers and the release process by writing your own changelog entry.
|
||||
|
||||
Pick one each for the category and type tags:
|
||||
|
||||
[ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message
|
||||
|
||||
For more details, see:
|
||||
https://reactnative.dev/contributing/changelogs-in-pull-requests
|
||||
<!-- Help reviewers and the release process by writing your own changelog entry. For an example, see:
|
||||
https://github.com/facebook/react-native/wiki/Changelog
|
||||
-->
|
||||
|
||||
[CATEGORY] [TYPE] - Message
|
||||
|
||||
## Test Plan
|
||||
|
||||
<!-- Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes the user interface. -->
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
<!-- Template for pre-release GitHub release -->
|
||||
|
||||
<!-- TODO Post about this release to https://github.com/reactwg/react-native-releases/discussions) -->
|
||||
|
||||
- <!-- TODO List out notable picks for this patch -->
|
||||
|
||||
---
|
||||
|
||||
To test it, run:
|
||||
|
||||
npx react-native init RN__SHORT_VERSION__ --version __VERSION__
|
||||
|
||||
---
|
||||
|
||||
You can participate in the conversation on the status of this release in the [working group](https://github.com/reactwg/react-native-releases/discussions).
|
||||
|
||||
---
|
||||
|
||||
To help you upgrade to this version, you can use the [upgrade helper](https://react-native-community.github.io/upgrade-helper/) ⚛️
|
||||
|
||||
---
|
||||
|
||||
See changes from this release in the [changelog PR](https://github.com/facebook/react-native/labels/%F0%9F%93%9D%20Changelog)
|
||||
@@ -0,0 +1,56 @@
|
||||
# Configuration for Respond To Issue Based on Label https://github.com/marketplace/actions/respond-to-issue-based-on-label
|
||||
|
||||
"Type: Invalid":
|
||||
close: true
|
||||
"Type: Question":
|
||||
comment: >
|
||||
We are using GitHub issues exclusively to track bugs in React Native. GitHub may not be the ideal place to ask a question, but you can try asking over on [Stack Overflow](http://stackoverflow.com/questions/tagged/react-native), or on [Reactiflux](https://www.reactiflux.com/).
|
||||
close: true
|
||||
"Type: Docs":
|
||||
comment: >
|
||||
Please report documentation issues in the [`react-native-website`](https://github.com/facebook/react-native-website/issues) repository.
|
||||
close: true
|
||||
"Type: Upgrade Issue":
|
||||
comment: >
|
||||
Do you need help upgrading to a newer React Native version? Visit the [Upgrade Support repository](https://github.com/react-native-community/upgrade-support) or use the [upgrade helper](https://react-native-community.github.io/upgrade-helper/) to see the changes that need to be made to upgrade your app.
|
||||
close: true
|
||||
"Resolution: For Stack Overflow":
|
||||
comment: >
|
||||
We are using GitHub issues exclusively to track bugs in the core React Native library. Please try asking over on [Stack Overflow](http://stackoverflow.com/questions/tagged/react-native) as it is better suited for this type of question.
|
||||
close: true
|
||||
"Needs: Issue Template":
|
||||
comment: >
|
||||
<table><tbody><tr><th width="50">:warning:</th><th width="100%">
|
||||
Missing Required Fields
|
||||
</th></tr><tr><td>:information_source:</td><td>
|
||||
It looks like your issue may be missing some necessary information. GitHub provides an example template whenever a <a href="https://github.com/facebook/react-native/issues/new?template=bug_report.md">new issue is created</a>. Could you go back and make sure to fill out the template? You may edit this issue, or close it and open a new one.
|
||||
</td></tr></tbody></table>
|
||||
labels:
|
||||
- "Needs: Author Feedback"
|
||||
"Needs: Environment Info":
|
||||
comment: >
|
||||
<table><tbody><tr><th width="50">:warning:</th><th width="100%">
|
||||
Missing Environment Information
|
||||
</th></tr><tr><td>:information_source:</td><td>
|
||||
Your issue may be missing information about your development environment. You can obtain the missing information by running <code>react-native info</code> in a console.
|
||||
</td></tr></tbody></table>
|
||||
labels:
|
||||
- "Needs: Author Feedback"
|
||||
"Needs: Verify on Latest Version":
|
||||
comment: >
|
||||
<table><tbody><tr><th width="50">:warning:</th><th width="100%">
|
||||
Using Old Version
|
||||
</th></tr><tr><td>:information_source:</td><td>
|
||||
It looks like you are using an older version of React Native. Please <a href="https://reactnative.dev/docs/upgrading">upgrade</a> to the latest version, and verify if the issue persists. If it does not, please let us know so we can close out this issue. This helps us ensure we are looking at issues that still exist in the current release.
|
||||
</td></tr></tbody></table>
|
||||
labels:
|
||||
- "Needs: Author Feedback"
|
||||
"Needs: Repro":
|
||||
comment: >
|
||||
<table><tbody><tr><th width="50">:warning:</th><th width="100%">
|
||||
Missing Reproducible Example
|
||||
</th></tr><tr><td>:information_source:</td><td>
|
||||
It looks like your issue is missing a reproducible example. Please provide a <a href="https://snack.expo.io">Snack</a> or a repository that demonstrates the issue you are reporting in a <a href="https://stackoverflow.com/help/minimal-reproducible-example">minimal, complete, and reproducible</a> manner.
|
||||
</td></tr></tbody></table>
|
||||
labels:
|
||||
- "Needs: Author Feedback"
|
||||
@@ -0,0 +1,26 @@
|
||||
# Configuration for probot-stale - https://github.com/probot/stale
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 90
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- Good first issue
|
||||
- "Type: Discussion"
|
||||
- Partner
|
||||
- Core Team
|
||||
- "Help Wanted :octocat:"
|
||||
- "Impact: Regression"
|
||||
- "Resolution: PR Submitted"
|
||||
- "Resolution: Backlog"
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: Stale
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs.
|
||||
You may also label this issue as a "Discussion" or add it to the "Backlog" and I will leave it open.
|
||||
Thank you for your contributions.
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: >
|
||||
Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please feel free to create a new issue with up-to-date information.
|
||||
only: issues
|
||||
@@ -1,137 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
module.exports = async (github, context, label) => {
|
||||
const closeIssue = async () => {
|
||||
await github.rest.issues.update({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
state: 'closed',
|
||||
});
|
||||
};
|
||||
|
||||
const addComment = async comment => {
|
||||
await github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: comment,
|
||||
});
|
||||
};
|
||||
|
||||
const requestAuthorFeedback = async () => {
|
||||
// Remove the triage label if it exists (ignore the 404 if not; it's not expected to always be there)
|
||||
try {
|
||||
await github.rest.issues.removeLabel({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
name: 'Needs: Triage :mag:',
|
||||
});
|
||||
} catch {}
|
||||
|
||||
await github.rest.issues.addLabels({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
labels: ['Needs: Author Feedback'],
|
||||
});
|
||||
};
|
||||
|
||||
switch (label) {
|
||||
case 'Type: Invalid':
|
||||
await addComment(
|
||||
`| :warning: | Issue is Invalid |\n` +
|
||||
`| --- | --- |\n` +
|
||||
`| :information_source: | This issue doesn't match any of the expected types for this repository - closing. |`,
|
||||
);
|
||||
await closeIssue();
|
||||
return;
|
||||
case 'Type: Question':
|
||||
await addComment(
|
||||
`| :warning: | Issue is a Question |\n` +
|
||||
`| --- | --- |\n` +
|
||||
`| :information_source: | We are using GitHub issues exclusively to track bugs in React Native. GitHub may not be the ideal place to ask a question, but you can try asking over on [Stack Overflow](http://stackoverflow.com/questions/tagged/react-native), or on [Reactiflux](https://www.reactiflux.com/). |`,
|
||||
);
|
||||
await closeIssue();
|
||||
return;
|
||||
case 'Type: Docs':
|
||||
await addComment(
|
||||
`| :warning: | Documentation Issue |\n` +
|
||||
`| --- | --- |\n` +
|
||||
`| :information_source: | Please report documentation issues in the [react-native-website](https://github.com/facebook/react-native-website/issues) repository. |`,
|
||||
);
|
||||
await closeIssue();
|
||||
return;
|
||||
case 'Resolution: For Stack Overflow':
|
||||
await addComment(
|
||||
`| :warning: | Issue is a Question |\n` +
|
||||
`| --- | --- |\n` +
|
||||
`| :information_source: | We are using GitHub issues exclusively to track bugs in the core React Native library. Please try asking over on [Stack Overflow](http://stackoverflow.com/questions/tagged/react-native) as it is better suited for this type of question. |`,
|
||||
);
|
||||
await closeIssue();
|
||||
return;
|
||||
case 'Type: Expo':
|
||||
await addComment(
|
||||
`| :warning: | Issue is Related to Expo |\n` +
|
||||
`| --- | --- |\n` +
|
||||
`| :information_source: | It looks like your issue is related to Expo and not React Native core. Please open your issue in [Expo's repository](https://github.com/expo/expo/issues/new). If you are able to create a repro that showcases that this issue is also happening in React Native vanilla, we will be happy to re-open. |`,
|
||||
);
|
||||
await closeIssue();
|
||||
return;
|
||||
case 'Needs: Issue Template':
|
||||
await addComment(
|
||||
`| :warning: | Missing Required Fields |\n` +
|
||||
`| --- | --- |\n` +
|
||||
`| :information_source: | It looks like your issue may be missing some necessary information. GitHub provides an example template whenever a [new issue is created](https://github.com/facebook/react-native/issues/new?template=bug_report.md). Could you go back and make sure to fill out the template? You may edit this issue, or close it and open a new one. |`,
|
||||
);
|
||||
await requestAuthorFeedback();
|
||||
return;
|
||||
case 'Needs: Environment Info':
|
||||
await addComment(
|
||||
`| :warning: | Missing Environment Information |\n` +
|
||||
`| --- | --- |\n` +
|
||||
`| :information_source: | Your issue may be missing information about your development environment. You can obtain the missing information by running <code>react-native info</code> in a console. |`,
|
||||
);
|
||||
await requestAuthorFeedback();
|
||||
return;
|
||||
case 'Needs: Verify on Latest Version':
|
||||
await addComment(
|
||||
`| :warning: | Newer Version of React Native is Available! |\n` +
|
||||
`| --- | --- |\n` +
|
||||
`| :information_source: | You are on a supported minor version, but it looks like there's a newer patch available. Please [upgrade](https://reactnative.dev/docs/upgrading) to the highest patch for your minor or latest and verify if the issue persists (alternatively, create a new project and repro the issue in it). If it does not repro, please let us know so we can close out this issue. This helps us ensure we are looking at issues that still exist in the most recent releases. |`,
|
||||
);
|
||||
return;
|
||||
case 'Needs: Version Info':
|
||||
await addComment(
|
||||
`| :warning: | Add or Reformat Version Info |\n` +
|
||||
`| --- | --- |\n` +
|
||||
`| :information_source: | We could not find or parse the version number of React Native in your issue report. Please use the template, and report your version including major, minor, and patch numbers - e.g. 0.70.2 |`,
|
||||
);
|
||||
await requestAuthorFeedback();
|
||||
return;
|
||||
case 'Needs: Repro':
|
||||
await addComment(
|
||||
`| :warning: | Missing Reproducible Example |\n` +
|
||||
`| --- | --- |\n` +
|
||||
`| :information_source: | It looks like your issue is missing a reproducible example. Please provide a [Snack](https://snack.expo.dev) or a repository that demonstrates the issue you are reporting in a [minimal, complete, and reproducible](https://stackoverflow.com/help/minimal-reproducible-example) manner. |`,
|
||||
);
|
||||
await requestAuthorFeedback();
|
||||
return;
|
||||
case 'Type: Unsupported Version':
|
||||
await addComment(
|
||||
`| :warning: | Unsupported Version of React Native |\n` +
|
||||
`| --- | --- |\n` +
|
||||
`| :information_source: | It looks like your issue or the example you provided uses an [unsupported version of React Native](https://github.com/reactwg/react-native-releases/blob/main/README.md#releases-support-policy). Due to the number of issues we receive, we're currently only accepting new issues against one of the supported versions. Please [upgrade](https://reactnative.dev/docs/upgrading) to latest and verify if the issue persists (alternatively, create a new project and repro the issue in it). If you cannot upgrade, please open your issue on [StackOverflow](https://stackoverflow.com/questions/tagged/react-native) to get further community support. |`,
|
||||
);
|
||||
await requestAuthorFeedback();
|
||||
return;
|
||||
}
|
||||
};
|
||||
@@ -1,22 +0,0 @@
|
||||
name: Apply version label to issue
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened, edited]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
add-version-label-issue:
|
||||
permissions:
|
||||
issues: write # for react-native-community/actions-apply-version-label to label issues
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
if: github.repository == 'facebook/react-native'
|
||||
|
||||
steps:
|
||||
- uses: react-native-community/actions-apply-version-label@v0.0.3
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
required-label: "Type: Upgrade Issue"
|
||||
@@ -1,24 +0,0 @@
|
||||
name: Automatic Rebase
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
rebase:
|
||||
permissions:
|
||||
contents: write # for cirrus-actions/rebase to push code to rebase
|
||||
pull-requests: read # for cirrus-actions/rebase to get info about PR
|
||||
name: Rebase
|
||||
if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '/rebase')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the latest code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
fetch-depth: 0 # otherwise, you will fail to push refs to dest repo
|
||||
- name: Automatic Rebase
|
||||
uses: cirrus-actions/rebase@1.7
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -1,63 +0,0 @@
|
||||
name: Label closed PR as merged and leave a comment
|
||||
on:
|
||||
push
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
comment-and-label:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'facebook/react-native'
|
||||
steps:
|
||||
- uses: actions/github-script@v6
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
if(!context.payload.commits || !context.payload.commits.length) return;
|
||||
const sha = context.payload.commits[0].id;
|
||||
|
||||
const {commit, author} = (await github.rest.repos.getCommit({
|
||||
ref: sha,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
})).data;
|
||||
|
||||
// Looking at the commit message, checks which PR number, if any, was closed by this commit
|
||||
const getClosedPrIfExists = (commit) => {
|
||||
if(!commit || !commit.message) return;
|
||||
const prClosingRegex = /Closes https:\/\/github.com\/facebook\/react-native\/pull\/([0-9]+)|Pull Request resolved: https:\/\/github.com\/facebook\/react-native\/pull\/([0-9]+)/;
|
||||
const prClosingMatch = commit.message.match(prClosingRegex);
|
||||
if(!prClosingMatch || (!prClosingMatch[1] && ! prClosingMatch[2])) return;
|
||||
return prClosingMatch[1] ?? prClosingMatch[2];
|
||||
};
|
||||
|
||||
const closedPrNumber = getClosedPrIfExists(commit);
|
||||
if(!closedPrNumber) return;
|
||||
|
||||
const pr = (await github.rest.pulls.get({
|
||||
pull_number: closedPrNumber,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
})).data;
|
||||
|
||||
// If the PR has already been processed (labeled as Merged), skip it
|
||||
const mergedLabel = "Merged";
|
||||
if(pr.labels && pr.labels.some(label => label.name === mergedLabel)) return;
|
||||
|
||||
const authorName = author?.login ? `@${author.login}` : commit.author.name;
|
||||
|
||||
github.rest.issues.createComment({
|
||||
issue_number: closedPrNumber,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: `This pull request was successfully merged by ${authorName} in **${sha}**.\n\n<sup>[When will my fix make it into a release?](https://github.com/facebook/react-native/wiki/Release-FAQ#when-will-my-fix-make-it-into-a-release) | [Upcoming Releases](https://github.com/reactwg/react-native-releases/discussions/categories/releases)</sup>`
|
||||
});
|
||||
|
||||
github.rest.issues.addLabels({
|
||||
issue_number: closedPrNumber,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
labels: [mergedLabel]
|
||||
});
|
||||
@@ -1,28 +0,0 @@
|
||||
name: Run Danger on PR
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, edited, reopened, synchronize]
|
||||
|
||||
permissions:
|
||||
actions: write
|
||||
checks: write
|
||||
contents: write
|
||||
issues: write
|
||||
pull-requests: write
|
||||
statuses: write
|
||||
|
||||
jobs:
|
||||
danger:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'facebook/react-native'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Run Yarn Install on Root
|
||||
run: yarn install
|
||||
working-directory: .
|
||||
- name: Danger
|
||||
run: yarn danger ci --use-github-checks --failOnErrors
|
||||
working-directory: packages/react-native-bots
|
||||
env:
|
||||
DANGER_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -4,19 +4,12 @@ on:
|
||||
issue_comment:
|
||||
types: created
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
applyNeedsAttentionLabel:
|
||||
permissions:
|
||||
contents: read # for actions/checkout to fetch code
|
||||
issues: write # for hramos/needs-attention to label issues
|
||||
name: Apply Needs Attention Label
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'facebook/react-native'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v2
|
||||
- name: Apply Needs Attention Label
|
||||
uses: hramos/needs-attention@v1
|
||||
with:
|
||||
|
||||
@@ -4,43 +4,13 @@ on:
|
||||
issues:
|
||||
types: labeled
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
# Runs automatic checks on issues labeled with "Needs: Triage",
|
||||
# then invokes actOnLabel to react to any added labels
|
||||
triage-issue:
|
||||
respondToIssueBasedOnLabel:
|
||||
name: Respond to Issue Based on Label
|
||||
runs-on: ubuntu-latest
|
||||
if: "${{ github.repository == 'facebook/react-native' && contains(github.event.label.name, 'Needs: Triage :mag:') }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/github-script@v6
|
||||
- uses: actions/checkout@v2
|
||||
- name: Respond to Issue Based on Label
|
||||
uses: hramos/respond-to-issue-based-on-label@v2
|
||||
with:
|
||||
script: |
|
||||
const verifyVersion = require('./.github/workflows/verifyVersion.js')
|
||||
const labelToAdd = await verifyVersion(github, context);
|
||||
|
||||
if(labelToAdd) {
|
||||
await github.rest.issues.addLabels({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
labels: [labelToAdd]
|
||||
})
|
||||
|
||||
const actOnLabel = require('./.github/workflows/actOnLabel.js')
|
||||
await actOnLabel(github, context, labelToAdd)
|
||||
}
|
||||
# Reacts to the label that triggered this workflow (added manually or via other workflows)
|
||||
act-on-label:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'facebook/react-native'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
const actOnLabel = require('./.github/workflows/actOnLabel.js')
|
||||
await actOnLabel(github, context, context.payload.label.name)
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
name: Mark stale issues and pull requests
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0,6,12,18 * * *"
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'facebook/react-native'
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@v5
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 180
|
||||
stale-issue-message: 'This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.'
|
||||
stale-pr-message: 'This PR is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.'
|
||||
close-issue-message: 'This issue was closed because it has been stalled for 7 days with no activity.'
|
||||
close-pr-message: 'This PR was closed because it has been stalled for 7 days with no activity.'
|
||||
exempt-issue-labels: Help Wanted :octocat:, Good first issue, Never gets stale
|
||||
exempt-pr-labels: Help Wanted :octocat:, Good first issue, Never gets stale
|
||||
stale-asc:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'facebook/react-native'
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@v5
|
||||
with:
|
||||
ascending: true
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 180
|
||||
stale-issue-message: 'This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.'
|
||||
stale-pr-message: 'This PR is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.'
|
||||
close-issue-message: 'This issue was closed because it has been stalled for 7 days with no activity.'
|
||||
close-pr-message: 'This PR was closed because it has been stalled for 7 days with no activity.'
|
||||
exempt-issue-labels: Help Wanted :octocat:, Good first issue, Never gets stale
|
||||
exempt-pr-labels: Help Wanted :octocat:, Good first issue, Never gets stale
|
||||
stale-needs-author-feedback:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'facebook/react-native'
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@v5
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
any-of-labels: 'Needs: Author Feedback'
|
||||
days-before-stale: 24
|
||||
stale-issue-message: "This issue is waiting for author's feedback since 24 days. Please provide the requested feedback or this will be closed in 7 days."
|
||||
stale-pr-message: "This PR is waiting for author's feedback since 24 days. Please provide the requested feedback or this will be closed in 7 days"
|
||||
close-issue-message: "This issue was closed because the author hasn't provided the requested feedback after 7 days."
|
||||
close-pr-message: "This PR was closed because the author hasn't provided the requested feedback after 7 days."
|
||||
exempt-issue-labels: Help Wanted :octocat:, Good first issue, Never gets stale
|
||||
exempt-pr-labels: Help Wanted :octocat:, Good first issue, Never gets stale
|
||||
stale-needs-author-feedback-asc:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'facebook/react-native'
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@v5
|
||||
with:
|
||||
ascending: true
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
any-of-labels: 'Needs: Author Feedback'
|
||||
days-before-stale: 24
|
||||
stale-issue-message: "This issue is waiting for author's feedback since 24 days. Please provide the requested feedback or this will be closed in 7 days."
|
||||
stale-pr-message: "This PR is waiting for author's feedback since 24 days. Please provide the requested feedback or this will be closed in 7 days"
|
||||
close-issue-message: "This issue was closed because the author hasn't provided the requested feedback after 7 days."
|
||||
close-pr-message: "This PR was closed because the author hasn't provided the requested feedback after 7 days."
|
||||
exempt-issue-labels: Help Wanted :octocat:, Good first issue, Never gets stale
|
||||
exempt-pr-labels: Help Wanted :octocat:, Good first issue, Never gets stale
|
||||
@@ -1,117 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
module.exports = async (github, context) => {
|
||||
const issue = context.payload.issue;
|
||||
|
||||
// Ignore issues using upgrade template (they use a special label)
|
||||
if (issue.labels.find(label => label.name == 'Type: Upgrade Issue')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const issueVersionUnparsed =
|
||||
getReactNativeVersionFromIssueBodyIfExists(issue);
|
||||
const issueVersion = parseVersionFromString(issueVersionUnparsed);
|
||||
|
||||
// Nightly versions are always supported
|
||||
if (reportedVersionIsNightly(issueVersionUnparsed, issueVersion)) return;
|
||||
|
||||
if (!issueVersion) {
|
||||
return 'Needs: Version Info';
|
||||
}
|
||||
|
||||
// Ensure the version matches one we support
|
||||
const recentReleases = (
|
||||
await github.rest.repos.listReleases({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
})
|
||||
).data.map(release => release.name);
|
||||
|
||||
const latestRelease = (
|
||||
await github.rest.repos.getLatestRelease({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
})
|
||||
).data;
|
||||
const latestVersion = parseVersionFromString(latestRelease.name);
|
||||
|
||||
if (!isVersionSupported(issueVersion, latestVersion)) {
|
||||
return 'Type: Unsupported Version';
|
||||
}
|
||||
|
||||
// We want to encourage users to repro the issue on the highest available patch for the given minor.
|
||||
const latestPatchForVersion = getLatestPatchForVersion(
|
||||
issueVersion,
|
||||
recentReleases,
|
||||
);
|
||||
if (latestPatchForVersion > issueVersion.patch) {
|
||||
return 'Needs: Verify on Latest Version';
|
||||
}
|
||||
};
|
||||
|
||||
// We support N-2 minor versions, and the latest major.
|
||||
function isVersionSupported(actualVersion, latestVersion) {
|
||||
return (
|
||||
actualVersion.major >= latestVersion.major &&
|
||||
actualVersion.minor >= latestVersion.minor - 2
|
||||
);
|
||||
}
|
||||
|
||||
// Assumes that releases are sorted in the order of recency (i.e. most recent releases are earlier in the list)
|
||||
// This enables us to stop looking as soon as we find the first release with a matching major/minor version, since
|
||||
// we know it's the most recent release, therefore the highest patch available.
|
||||
function getLatestPatchForVersion(version, releases) {
|
||||
for (releaseName of releases) {
|
||||
const release = parseVersionFromString(releaseName);
|
||||
if (
|
||||
release &&
|
||||
release.major == version.major &&
|
||||
release.minor == version.minor
|
||||
) {
|
||||
return release.patch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getReactNativeVersionFromIssueBodyIfExists(issue) {
|
||||
if (!issue || !issue.body) return;
|
||||
const rnVersionRegex = /React Native Version[\r\n]+(?<version>.+)[\r\n]*/;
|
||||
const rnVersionMatch = issue.body.match(rnVersionRegex);
|
||||
if (!rnVersionMatch || !rnVersionMatch.groups.version) return;
|
||||
return rnVersionMatch.groups.version;
|
||||
}
|
||||
|
||||
function reportedVersionIsNightly(unparsedVersionString, version) {
|
||||
if (!unparsedVersionString && !version) return false;
|
||||
const nightlyRegex = /nightly/i;
|
||||
const nightlyMatch = unparsedVersionString.match(nightlyRegex);
|
||||
const versionIsNightly = nightlyMatch && nightlyMatch[0];
|
||||
|
||||
const versionIsZero =
|
||||
version && version.major == 0 && version.minor == 0 && version.patch == 0;
|
||||
|
||||
return versionIsZero || versionIsNightly;
|
||||
}
|
||||
|
||||
function parseVersionFromString(version) {
|
||||
if (!version) return;
|
||||
// This will match the standard x.x.x semver format, as well as the non-standard prerelease x.x.x-rc.x
|
||||
const semverRegex =
|
||||
/(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)(-[rR]{1}[cC]{1}\.(?<prerelease>\d+))?/;
|
||||
const versionMatch = version.match(semverRegex);
|
||||
if (!versionMatch) return;
|
||||
const {major, minor, patch, prerelease} = versionMatch.groups;
|
||||
return {
|
||||
major: parseInt(major),
|
||||
minor: parseInt(minor),
|
||||
patch: parseInt(patch),
|
||||
prerelease: parseInt(prerelease),
|
||||
};
|
||||
}
|
||||
+24
-58
@@ -20,36 +20,28 @@ DerivedData
|
||||
*.ipa
|
||||
*.xcuserstate
|
||||
project.xcworkspace
|
||||
**/.xcode.env.local
|
||||
|
||||
# Gradle
|
||||
/build/
|
||||
/packages/react-native-codegen/android/build/
|
||||
/packages/react-native-gradle-plugin/build/
|
||||
/packages/rn-tester/build
|
||||
/packages/rn-tester/android/app/.cxx/
|
||||
/packages/rn-tester/android/app/build/
|
||||
/packages/rn-tester/android/app/gradle/
|
||||
/packages/rn-tester/android/app/gradlew
|
||||
/packages/rn-tester/android/app/gradlew.bat
|
||||
/packages/react-native/ReactAndroid/build/
|
||||
/packages/react-native/ReactAndroid/.cxx/
|
||||
/packages/react-native/ReactAndroid/gradle/
|
||||
/packages/react-native/ReactAndroid/gradlew
|
||||
/packages/react-native/ReactAndroid/gradlew.bat
|
||||
/packages/react-native/ReactAndroid/external-artifacts/build/
|
||||
/packages/react-native/ReactAndroid/external-artifacts/artifacts/
|
||||
/packages/react-native/ReactAndroid/hermes-engine/build/
|
||||
/packages/react-native/ReactAndroid/hermes-engine/.cxx/
|
||||
/packages/react-native/template/android/app/build/
|
||||
/packages/react-native/template/android/build/
|
||||
/ReactAndroid/build/
|
||||
/ReactAndroid/gradle/
|
||||
/ReactAndroid/gradlew
|
||||
/ReactAndroid/gradlew.bat
|
||||
|
||||
# Buck
|
||||
.buckd
|
||||
buck-out
|
||||
/.lsp.buckd
|
||||
/.lsp-buck-out
|
||||
/packages/react-native/ReactAndroid/src/main/jni/prebuilt/lib/
|
||||
/packages/react-native/ReactAndroid/src/main/gen
|
||||
/ReactAndroid/src/main/jni/prebuilt/lib/
|
||||
/ReactAndroid/src/main/gen
|
||||
|
||||
# Android Studio
|
||||
.project
|
||||
@@ -64,76 +56,53 @@ buck-out
|
||||
.gradle
|
||||
local.properties
|
||||
*.iml
|
||||
/packages/react-native/android/*
|
||||
!/packages/react-native/android/README.md
|
||||
/android/
|
||||
|
||||
# Node
|
||||
node_modules
|
||||
*.log
|
||||
.nvm
|
||||
/bots/node_modules/
|
||||
package-lock.json
|
||||
|
||||
# OS X
|
||||
.DS_Store
|
||||
|
||||
# Test generated files
|
||||
/packages/react-native/ReactAndroid/src/androidTest/assets/AndroidTestBundle.js
|
||||
/ReactAndroid/src/androidTest/assets/AndroidTestBundle.js
|
||||
*.js.meta
|
||||
|
||||
/coverage
|
||||
/third-party
|
||||
|
||||
# Test Reports
|
||||
/reports
|
||||
|
||||
# Stack Dumps generated when programs crash (Ex. bash.exe.stackdump on Win)
|
||||
*.stackdump
|
||||
|
||||
# Root dir shouldn't have Xcode project
|
||||
/*.xcodeproj
|
||||
|
||||
# ReactCommon subdir shouldn't have Xcode project
|
||||
/packages/react-native/ReactCommon/**/*.xcodeproj
|
||||
/ReactCommon/**/*.xcodeproj
|
||||
/packages/rn-tester/build
|
||||
/packages/rn-tester/android/app/build/*
|
||||
|
||||
# Libs that shouldn't have Xcode project
|
||||
/packages/react-native/Libraries/FBLazyVector/**/*.xcodeproj
|
||||
/packages/react-native/Libraries/RCTRequired/**/*.xcodeproj
|
||||
/packages/react-native/React/CoreModules/**/*.xcodeproj
|
||||
/packages/react-native/React/FBReactNativeSpec/**/*.xcodeproj
|
||||
/Libraries/FBLazyVector/**/*.xcodeproj
|
||||
/Libraries/RCTRequired/**/*.xcodeproj
|
||||
/React/CoreModules/**/*.xcodeproj
|
||||
/React/FBReactNativeSpec/**/*.xcodeproj
|
||||
/packages/react-native-codegen/**/*.xcodeproj
|
||||
|
||||
# Ruby Gems (Bundler)
|
||||
/packages/react-native/vendor
|
||||
/packages/react-native/template/vendor
|
||||
.ruby-version
|
||||
/**/.ruby-version
|
||||
./vendor/
|
||||
vendor/
|
||||
|
||||
# iOS / CocoaPods
|
||||
/packages/react-native/template/ios/build/
|
||||
/packages/react-native/template/ios/Pods/
|
||||
/packages/react-native/template/ios/Podfile.lock
|
||||
# CocoaPods
|
||||
/template/ios/Pods/
|
||||
/template/ios/Podfile.lock
|
||||
/packages/rn-tester/Gemfile.lock
|
||||
|
||||
# Ignore RNTester specific Pods, but keep the __offline_mirrors__ here.
|
||||
/packages/rn-tester/Pods/*
|
||||
!/packages/rn-tester/Pods/__offline_mirrors_hermes__
|
||||
!/packages/rn-tester/Pods/__offline_mirrors_jsc__
|
||||
!/packages/rn-tester/Pods/__offline_mirrors__
|
||||
|
||||
# @react-native/codegen
|
||||
/packages/react-native/React/FBReactNativeSpec/FBReactNativeSpec
|
||||
# react-native-codegen
|
||||
/React/FBReactNativeSpec/FBReactNativeSpec
|
||||
/packages/react-native-codegen/lib
|
||||
/packages/react-native-codegen/tmp/
|
||||
/packages/react-native/ReactCommon/react/renderer/components/rncore/
|
||||
/packages/rn-tester/NativeModuleExample/ScreenshotManagerSpec*
|
||||
/**/RCTThirdPartyFabricComponentsProvider.*
|
||||
|
||||
|
||||
# Additional SDKs
|
||||
/packages/react-native/sdks/download
|
||||
/packages/react-native/sdks/hermes
|
||||
/packages/react-native/sdks/hermesc
|
||||
/ReactCommon/react/renderer/components/rncore/
|
||||
|
||||
# Visual studio
|
||||
.vscode
|
||||
@@ -141,6 +110,3 @@ vendor/
|
||||
|
||||
# Android memory profiler files
|
||||
*.hprof
|
||||
|
||||
# Temporary files created by Metro to check the health of the file watcher
|
||||
.metro-health-check*
|
||||
|
||||
+4
-4
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"arrowParens": "avoid",
|
||||
"bracketSameLine": true,
|
||||
"bracketSpacing": false,
|
||||
"requirePragma": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all"
|
||||
"trailingComma": "all",
|
||||
"bracketSpacing": false,
|
||||
"jsxBracketSameLine": true,
|
||||
"arrowParens": "avoid"
|
||||
}
|
||||
|
||||
-5537
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -67,8 +67,8 @@ members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1,
|
||||
available at https://www.contributor-covenant.org/version/2/1/code_of_conduct/
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
|
||||
+109
-4
@@ -1,7 +1,112 @@
|
||||
# Contributing to React Native
|
||||
|
||||
Thank you for your interest in contributing to React Native! From commenting on and triaging issues, to reviewing and sending Pull Requests, all contributions are welcome.
|
||||
We aim to build a vibrant and inclusive [ecosystem of partners, core contributors, and community](ECOSYSTEM.md) that goes beyond the main React Native GitHub repository.
|
||||
Thank you for your interest in contributing to React Native! From commenting on and triaging issues, to reviewing and sending Pull Requests, all contributions are welcome. We aim to build a vibrant and inclusive [ecosystem of partners, core contributors, and community](ECOSYSTEM.md) that goes beyond the main React Native GitHub repository.
|
||||
|
||||
To learn more about how to contribute check out the Contributing section on the React Native website:
|
||||
* https://reactnative.dev/contributing/overview
|
||||
The [Open Source Guides](https://opensource.guide/) website has a collection of resources for individuals, communities, and companies who want to learn how to run and contribute to an open source project. Contributors and people new to open source alike will find the following guides especially useful:
|
||||
|
||||
* [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
|
||||
* [Building Welcoming Communities](https://opensource.guide/building-community/)
|
||||
|
||||
|
||||
### [Code of Conduct](https://github.com/facebook/react-native/blob/HEAD/CODE_OF_CONDUCT.md)
|
||||
|
||||
As a reminder, all contributors are expected to adhere to the [Code of Conduct](https://github.com/facebook/react-native/blob/HEAD/CODE_OF_CONDUCT.md).
|
||||
|
||||
## Ways to Contribute
|
||||
|
||||
If you are eager to start contributing code right away, we have a list of [good first issues](https://github.com/facebook/react-native/labels/good%20first%20issue) that contain bugs which have a relatively limited scope. As you gain more experience and demonstrate a commitment to evolving React Native, you may be granted issue management permissions in the repository.
|
||||
|
||||
There are other ways you can contribute without writing a single line of code. Here are a few things you can do to help out:
|
||||
|
||||
1. **Replying and handling open issues.** We get a lot of issues every day, and some of them may lack necessary information. You can help out by guiding people through the process of filling out the issue template, asking for clarifying information, or pointing them to existing issues that match their description of the problem. We cover more about this process in the [Issue Triage wiki](https://github.com/facebook/react-native/wiki/Triaging-GitHub-Issues).
|
||||
2. **Reviewing pull requests for the docs.** Reviewing [documentation updates](https://github.com/facebook/react-native-website/pulls) can be as simple as checking for spelling and grammar. If you encounter situations that can be explained better in the docs, click **Edit** at the top of most docs pages to get started with your own contribution.
|
||||
3. **Help people write test plans.** Some pull requests sent to the main repository may lack a proper test plan. These help reviewers understand how the change was tested, and can speed up the time it takes for a contribution to be accepted.
|
||||
|
||||
Each of these tasks is highly impactful, and maintainers will greatly appreciate your help.
|
||||
|
||||
### Our Development Process
|
||||
|
||||
We use GitHub issues and pull requests to keep track of bug reports and contributions from the community. All changes from engineers at Facebook will sync to [GitHub](https://github.com/facebook/react-native) through a bridge with Facebook's internal source control. Changes from the community are handled through GitHub pull requests. Once a change made on GitHub is approved, it will first be imported into Facebook's internal source control and tested against Facebook's codebase. Once merged at Facebook, the change will eventually sync back to GitHub as a single commit once it has passed Facebook's internal tests.
|
||||
|
||||
You can learn more about the contribution process in the following documents:
|
||||
|
||||
* [Issues](https://github.com/facebook/react-native/wiki/Triaging-GitHub-Issues)
|
||||
* [Pull Requests](https://github.com/facebook/react-native/wiki/Managing-Pull-Requests)
|
||||
|
||||
We also have a thriving community of contributors who would be happy to help you get set up. You can reach out to us through [@ReactNative](http://twitter.com/reactnative) (the React Native team) and [@ReactNativeComm](http://twitter.com/reactnativecomm) (the React Native Community organization).
|
||||
|
||||
### Repositories
|
||||
|
||||
The main repository, <https://github.com/facebook/react-native>, contains the React Native framework itself and it is here where we keep track of bug reports and manage pull requests.
|
||||
|
||||
There are a few other repositories you might want to familiarize yourself with:
|
||||
|
||||
* **React Native website** which contains the source code for the website, including the documentation, located at <https://github.com/facebook/react-native-website>
|
||||
* **Releases** are coordinated through the <https://github.com/react-native-community/releases> repository. This includes important documents such as the Changelog.
|
||||
* **Discussions** about the future of React Native take place in the <https://github.com/react-native-community/discussions-and-proposals> repository.
|
||||
* **High-quality plugins** for React Native can be found throughout the [React Native Community GitHub Organization](http://github.com/react-native-community/).
|
||||
|
||||
Browsing through these repositories should provide some insight into how the React Native open source project is managed.
|
||||
|
||||
## GitHub Issues
|
||||
|
||||
We use GitHub issues to track bugs exclusively. We have documented our issue handling processes in the [Issues wiki](https://github.com/facebook/react-native/wiki/Triaging-GitHub-Issues).
|
||||
|
||||
### Security Bugs
|
||||
|
||||
Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe disclosure of security bugs. In those cases, please go through the process outlined on that page and do not file a public issue.
|
||||
|
||||
## Helping with Documentation
|
||||
|
||||
The React Native documentation is hosted as part of the React Native website repository at https://github.com/facebook/react-native-website. The website itself is located at <https://reactnative.dev/> and it is built using [Docusaurus](https://docusaurus.io/). If there's anything you'd like to change in the docs, you can get started by clicking on the "Edit" button located on the upper right of most pages in the website.
|
||||
|
||||
If you are adding new functionality or introducing a change in behavior, we will ask you to update the documentation to reflect your changes.
|
||||
|
||||
### Contributing to the Blog
|
||||
|
||||
The React Native blog is generated [from the Markdown sources for the blog](https://github.com/facebook/react-native-website/tree/HEAD/website/blog).
|
||||
|
||||
Please open an issue in the https://github.com/facebook/react-native-website repository or tag us on [@ReactNative on Twitter](http://twitter.com/reactnative) and get the go-ahead from a maintainer before writing an article intended for the React Native blog. In most cases, you might want to share your article on your own blog or writing medium instead. It's worth asking, though, in case we find your article is a good fit for the blog.
|
||||
|
||||
We recommend referring to the [CONTRIBUTING](https://github.com/facebook/react-native-website/blob/HEAD/CONTRIBUTING.md) document for the `react-native-website` repository to learn more about contributing to the website in general.
|
||||
|
||||
## Contributing Code
|
||||
|
||||
Code-level contributions to React Native generally come in the form of [pull requests](https://help.github.com/en/articles/about-pull-requests). These are done by forking the repo and making changes locally. Directly in the repo, there is the [`rn-tester` app](/packages/rn-tester) that you can install on your device (or simulators) and use to test the changes you're making to React Native sources.
|
||||
|
||||
The process of proposing a change to React Native can be summarized as follows:
|
||||
|
||||
1. Fork the React Native repository and create your branch from `main`.
|
||||
2. Make the desired changes to React Native sources. Use the `packages/rn-tester` app to test them out.
|
||||
3. If you've added code that should be tested, add tests.
|
||||
4. If you've changed APIs, update the documentation, which lives in [another repo](https://github.com/facebook/react-native-website/).
|
||||
5. Ensure the test suite passes, either locally or on CI once you opened a pull request.
|
||||
6. Make sure your code lints (for example via `yarn lint --fix`).
|
||||
7. Push the changes to your fork.
|
||||
8. Create a pull request to the React Native repository.
|
||||
9. Review and address comments on your pull request.
|
||||
1. A bot may comment with suggestions. Generally we ask you to resolve these first before a maintainer will review your code.
|
||||
2. If changes are requested and addressed, please [request review](https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/requesting-a-pull-request-review) to notify reviewers to take another look.
|
||||
10. If you haven't already, please complete the [Contributor License Agreement](https://github.com/facebook/react-native/wiki/Contributor-License-Agreement) ("CLA"). **[Complete your CLA here.](https://code.facebook.com/cla)**
|
||||
|
||||
If all goes well, your pull request will be merged. If it is not merged, maintainers will do their best to explain the reason why.
|
||||
|
||||
### Step-by-step Guide
|
||||
|
||||
Whenever you are ready to contribute code, check out our [step-by-step guide to sending your first pull request](https://github.com/facebook/react-native/wiki/How-to-Open-a-Pull-Request), or read the [How to Contribute Code](https://github.com/facebook/react-native/wiki/How-to-Contribute-Code) wiki for more details.
|
||||
|
||||
### Tests
|
||||
|
||||
Tests help us prevent regressions from being introduced to the codebase. The GitHub repository is continuously tested using Circle and Appveyor, the results of which are available through the Checks functionality on [commits](https://github.com/facebook/react-native/commits/HEAD) and pull requests. You can learn more about running and writing tests in the [Tests wiki](http://github.com/facebook/react-native/wiki/Tests).
|
||||
|
||||
## Community Contributions
|
||||
|
||||
Contributions to React Native are not limited to GitHub. You can help others by sharing your experience using React Native, whether that is through blog posts, presenting talks at conferences, or simply sharing your thoughts on Twitter and tagging @ReactNative.
|
||||
|
||||
## Where to Get Help
|
||||
|
||||
As you work on React Native, it is natural that sooner or later you may require help. In addition to the resources listed in [SUPPORT](.github/SUPPORT.md), people interested in contributing may take advantage of the following:
|
||||
|
||||
* **Twitter**. The React Native team at Facebook has its own account at [@reactnative](https://twitter.com/reactnative), and the React Native Community uses [@reactnativecomm](https://twitter.com/reactnativecomm). If you feel stuck, or need help contributing, please do not hesitate to reach out.
|
||||
* **Proposals Repository**. If you are considering working on a feature large in scope, consider [creating a proposal first](https://github.com/react-native-community/discussions-and-proposals). The community can help you figure out the right approach, and we'd be happy to help.
|
||||
* **React Native Community Discord**. While we try to hold most discussions in public, sometimes it can be beneficial to have conversations in real time with other contributors. People who have demonstrated a commitment to moving React Native forward through sustained contributions to the project may eventually be invited to join the React Native Community Discord.
|
||||
|
||||
+14
-29
@@ -1,36 +1,27 @@
|
||||
# The React Native Ecosystem
|
||||
|
||||
We aim to build a vibrant and inclusive ecosystem of partners, core contributors, and community that goes beyond the main React Native GitHub repository. This document explains the roles and responsibilities of various stakeholders and provides guidelines for the community organization. The structure outlined in this document has been in place for a while but had not been written down before.
|
||||
We aim to build a vibrant and inclusive ecosystem of partners, core contributors, and community that goes beyond the main React Native GitHub repository. This document explains the roles and responsibilities of various stakeholders and provides guidelines for the community organization. The structure outlined in this document has been in place for a while but hadn't been written down before.
|
||||
|
||||
There are three types of stakeholders:
|
||||
|
||||
* **Partners:** Companies significantly invested in React Native and take responsibility for the React Native vision and community.
|
||||
* **Partners:** Companies that are significantly invested in React Native and have been for years.
|
||||
* **Core Contributors:** Individual people who contribute to the React Native project.
|
||||
* **Community Contributors:** Individuals who support projects in the [react-native-community](https://github.com/react-native-community) organization.
|
||||
|
||||
## Partners
|
||||
|
||||
Partners are companies that are significantly invested in React Native and demonstrate ownership. Informed by their use of React Native, they push for improvements of the core and/or the ecosystem around it. Examples of this may include large scale contributions to `react-native` or owning essential tools or libraries.
|
||||
Partners are companies that are significantly invested in React Native and have been for years. Informed by their use of React Native, they push for improvements of the core and/or the ecosystem around it. Partners think of React Native as a product: they understand the trade offs that the project makes as well as future plans and goals. Together we shape the vision for React Native to make it the best way to build applications.
|
||||
|
||||
Partners think of React Native as a product; they understand the trade offs that the project makes as well as future plans and goals. Together we shape the vision for React Native to make it the best way to build applications.
|
||||
React Native's current set of partners include Callstack, Expo, Facebook, Infinite Red, Microsoft and Software Mansion. Many engineers from these companies are core contributors, and their partner responsibilities also include:
|
||||
|
||||
Baseline responsibilities of partners include:
|
||||
* Attending monthly meeting
|
||||
* Contributing to the release process. Examples include being a [community releaser](https://reactnative.dev/contributing/release-roles-responsibilities#release-role-2-community-releaser), testing new releases, technical support for release issues.
|
||||
* Engage on the core contributor Discord
|
||||
* **[Callstack](https://callstack.com/):** Manages releases, maintains the [React Native CLI](https://github.com/react-native-community/react-native-cli) and organizes [React Native EU](https://react-native.eu/)
|
||||
* **[Expo](https://expo.io/):** Builds [expo](https://github.com/expo/expo) on top of React Native to simplify app development
|
||||
* **[Facebook](https://opensource.facebook.com):** Oversees the React Native product and maintains the [React Native core repo](https://reactnative.dev/)
|
||||
* **[Infinite Red](https://infinite.red/):** Maintains the [ignite cli/boilerplate](https://github.com/infinitered/ignite), organizes [Chain React Conf](https://cr.infinite.red/)
|
||||
* **[Microsoft](http://aka.ms/reactnative):** Develops [React Native Windows](https://github.com/Microsoft/react-native-windows) and [React Native macOS](https://github.com/microsoft/react-native-macos) for building apps that target Windows and macOS
|
||||
* **[Software Mansion](https://swmansion.com/):** Maintain core infrastructure including JSC, Animated, and other popular third-party plugins.
|
||||
|
||||
Our current partners and other areas of ownership:
|
||||
* **[Coinbase](https://www.coinbase.com/):** Publishes [posts](https://blog.coinbase.com/tagged/react-native) advocating React Native usage. Supports `@react-native-community/datetimepicker` and other community modules to migrate to the new architecture. Supports releases in testing and feedback.
|
||||
* **[Callstack](https://callstack.com/):** Maintains the [React Native CLI](https://github.com/react-native-community/react-native-cli) and [other community libraries](https://github.com/callstack), organizes [React Native EU](https://react-native.eu/) and hosts [The React Native Show podcast](https://www.callstack.com/podcast-react-native-show)
|
||||
* **[Expo](https://expo.dev/):** Builds [Expo Go and SDK](https://github.com/expo/expo), [Snack](https://snack.expo.dev/), and [Expo Application Services](https://expo.dev/eas). Maintains [React Native Directory](https://reactnative.directory/), stewards [React Navigation](https://reactnavigation.org/) along with other partners.
|
||||
* **[Infinite Red](https://infinite.red/):** Maintains the [ignite cli/boilerplate](https://github.com/infinitered/ignite), organizes [Chain React Conf](https://cr.infinite.red/), hosts the [React Native Radio podcast](https://reactnativeradio.com), publishes the [React Native Newsletter](https://reactnativenewsletter.com)
|
||||
* **[Meta](https://opensource.fb.com/):** Oversees the React Native product and maintains the [React Native core repo](https://reactnative.dev/)
|
||||
* **[Microsoft](https://twitter.com/ReactNativeMSFT):** Develops [React Native Windows](https://github.com/Microsoft/react-native-windows) and [React Native macOS](https://github.com/microsoft/react-native-macos) for building apps that target Windows and macOS; maintains [rnx-kit](https://github.com/microsoft/rnx-kit), [react-native-test-app](https://github.com/microsoft/react-native-test-app) and coordinates cross-companies efforts such as the [bundle working group](https://github.com/microsoft/rnx-kit/discussions/categories/bundle-working-group).
|
||||
* **[Shopify](https://www.shopify.com/):** Maintains React Native open source libraries such as [flash-list](https://github.com/Shopify/flash-list) or [@shopify/react-native-skia](https://github.com/Shopify/react-native-skia) and sponsors Software Mansion.
|
||||
* **[Software Mansion](https://swmansion.com/):** Maintain core infrastructure including JSC, Animated, and other popular third-party plugins and organizes [App.js Conf](https://appjs.co/)
|
||||
* **[Wix.com](https://wix.engineering/open-source):** Maintains a variety of React Native open source projects ([see all](https://github.com/orgs/wix/repositories?q=react-native)), including: [Detox](https://wix.github.io/Detox/) end-to-end testing library for React Native apps, [RN UILib](https://wix.github.io/react-native-ui-lib/), [RN Navigation](https://wix.github.io/react-native-navigation/), [RN Calendars](https://wix.github.io/react-native-calendars/) and [RN Notifications](https://github.com/wix/react-native-notifications).
|
||||
|
||||
This list may fluctuate in response to newcomers who meet our partner definition. In terms of open source work, pull requests from partners are commonly prioritized. When you are contributing to React Native, you'll most likely meet somebody who works at one of the partner companies and who is a core contributor:
|
||||
In terms of open source work, pull requests from partners are commonly prioritized. When you are contributing to React Native, you'll most likely meet somebody who works at one of the partner companies and who is a core contributor:
|
||||
|
||||
## Core Contributors
|
||||
|
||||
@@ -47,19 +38,13 @@ Core contributors are individuals who contribute to the React Native project. A
|
||||
|
||||
These are behaviors we have observed in our existing core contributors. They aren't strict rules but rather outline their usual responsibilities. We do not expect every core contributor to do all of the above things all the time. Most importantly, we want to create a supportive and friendly environment that fosters collaboration. Above all else, **we are always polite and friendly.**
|
||||
|
||||
Core contributor status is attained after consistently contributing and taking on the responsibilities outlined above and granted by other core contributors. Similarly, after a long period of inactivity (~6 months or more), a core contributor may be contacted to understand if they’re still interested in being part of the program.
|
||||
Core contributor status is attained after consistently contributing and taking on the responsibilities outlined above and granted by other core contributors. Similarly, after a long period of inactivity, a core contributor may be removed.
|
||||
|
||||
You can use this [form](https://forms.gle/4jpA4QeNUvAUDnNe8) to either:
|
||||
* Apply yourself to become a Core Contributor. Make sure to include a list of valuable contributions you did to the React Native repository and ecosystem.
|
||||
* Nominate someone to become a Core Contributor.
|
||||
|
||||
As a core contributor, you will have access to the core contributor Discord which is used for light-weight coordination and discussion.
|
||||
|
||||
**We aim to make contributing to React Native as easy and transparent as possible.** We have discussion groups dedicated to the [new architecture rollout](https://github.com/reactwg/react-native-new-architecture), [releases](https://github.com/reactwg/react-native-releases), and [general questions and proposals](https://github.com/react-native-community/discussions-and-proposals). We are always looking for active, enthusiastic members of the React Native community to become core contributors.
|
||||
We aim to make contributing to React Native as easy and transparent as possible. All important topics are handled through a [discussion or RFC process on GitHub](https://github.com/react-native-community/discussions-and-proposals). We are always looking for active, enthusiastic members of the React Native community to become core contributors.
|
||||
|
||||
## Community Contributors
|
||||
|
||||
Community contributors are individuals who support projects in the [react-native-community](https://github.com/react-native-community) organization. This organization exists as an incubator for high quality components that extend the capabilities of React Native with functionality that many but not all applications require. Meta engineers will provide guidance to help build a vibrant community of people and components that make React Native better.
|
||||
Community contributors are individuals who support projects in the [react-native-community](https://github.com/react-native-community) organization. This organization exists as an incubator for high quality components that extend the capabilities of React Native with functionality that many but not all applications require. Facebook engineers will provide guidance to help build a vibrant community of people and components that make React Native better.
|
||||
|
||||
This structure has multiple benefits:
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
|
||||
ruby ">= 2.6.10"
|
||||
|
||||
gem 'cocoapods', '~> 1.12'
|
||||
gem 'activesupport', '>= 6.1.7.3', '< 7.1.0'
|
||||
-103
@@ -1,103 +0,0 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
CFPropertyList (3.0.7)
|
||||
base64
|
||||
nkf
|
||||
rexml
|
||||
activesupport (7.0.8.1)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 1.6, < 2)
|
||||
minitest (>= 5.1)
|
||||
tzinfo (~> 2.0)
|
||||
addressable (2.8.6)
|
||||
public_suffix (>= 2.0.2, < 6.0)
|
||||
algoliasearch (1.27.5)
|
||||
httpclient (~> 2.8, >= 2.8.3)
|
||||
json (>= 1.5.1)
|
||||
atomos (0.1.3)
|
||||
base64 (0.2.0)
|
||||
claide (1.1.0)
|
||||
cocoapods (1.15.2)
|
||||
addressable (~> 2.8)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
cocoapods-core (= 1.15.2)
|
||||
cocoapods-deintegrate (>= 1.0.3, < 2.0)
|
||||
cocoapods-downloader (>= 2.1, < 3.0)
|
||||
cocoapods-plugins (>= 1.0.0, < 2.0)
|
||||
cocoapods-search (>= 1.0.0, < 2.0)
|
||||
cocoapods-trunk (>= 1.6.0, < 2.0)
|
||||
cocoapods-try (>= 1.1.0, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
escape (~> 0.0.4)
|
||||
fourflusher (>= 2.3.0, < 3.0)
|
||||
gh_inspector (~> 1.0)
|
||||
molinillo (~> 0.8.0)
|
||||
nap (~> 1.0)
|
||||
ruby-macho (>= 2.3.0, < 3.0)
|
||||
xcodeproj (>= 1.23.0, < 2.0)
|
||||
cocoapods-core (1.15.2)
|
||||
activesupport (>= 5.0, < 8)
|
||||
addressable (~> 2.8)
|
||||
algoliasearch (~> 1.0)
|
||||
concurrent-ruby (~> 1.1)
|
||||
fuzzy_match (~> 2.0.4)
|
||||
nap (~> 1.0)
|
||||
netrc (~> 0.11)
|
||||
public_suffix (~> 4.0)
|
||||
typhoeus (~> 1.0)
|
||||
cocoapods-deintegrate (1.0.5)
|
||||
cocoapods-downloader (2.1)
|
||||
cocoapods-plugins (1.0.0)
|
||||
nap
|
||||
cocoapods-search (1.0.1)
|
||||
cocoapods-trunk (1.6.0)
|
||||
nap (>= 0.8, < 2.0)
|
||||
netrc (~> 0.11)
|
||||
cocoapods-try (1.2.0)
|
||||
colored2 (3.1.2)
|
||||
concurrent-ruby (1.2.3)
|
||||
escape (0.0.4)
|
||||
ethon (0.16.0)
|
||||
ffi (>= 1.15.0)
|
||||
ffi (1.16.3)
|
||||
fourflusher (2.3.1)
|
||||
fuzzy_match (2.0.4)
|
||||
gh_inspector (1.1.3)
|
||||
httpclient (2.8.3)
|
||||
i18n (1.14.4)
|
||||
concurrent-ruby (~> 1.0)
|
||||
json (2.7.1)
|
||||
minitest (5.22.2)
|
||||
molinillo (0.8.0)
|
||||
nanaimo (0.3.0)
|
||||
nap (1.1.0)
|
||||
netrc (0.11.0)
|
||||
nkf (0.2.0)
|
||||
public_suffix (4.0.7)
|
||||
rexml (3.2.6)
|
||||
ruby-macho (2.5.1)
|
||||
typhoeus (1.4.1)
|
||||
ethon (>= 0.9.0)
|
||||
tzinfo (2.0.6)
|
||||
concurrent-ruby (~> 1.0)
|
||||
xcodeproj (1.24.0)
|
||||
CFPropertyList (>= 2.3.3, < 4.0)
|
||||
atomos (~> 0.1.3)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
nanaimo (~> 0.3.0)
|
||||
rexml (~> 3.2.4)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
activesupport (>= 6.1.7.3, < 7.1.0)
|
||||
cocoapods (~> 1.12)
|
||||
|
||||
RUBY VERSION
|
||||
ruby 3.0.6p216
|
||||
|
||||
BUNDLED WITH
|
||||
2.4.13
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
@@ -16,7 +16,7 @@ import * as React from 'react';
|
||||
const {TestModule} = NativeModules;
|
||||
|
||||
class AccessibilityManagerTest extends React.Component<{...}> {
|
||||
componentDidMount(): void {
|
||||
componentDidMount() {
|
||||
invariant(
|
||||
NativeAccessibilityManager,
|
||||
"NativeAccessibilityManager doesn't exist",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
const ReactNative = require('react-native');
|
||||
const {AsyncStorage, Text, View, StyleSheet} = ReactNative;
|
||||
const {TestModule} = ReactNative.NativeModules;
|
||||
|
||||
const deepDiffer = require('react-native/Libraries/Utilities/differ/deepDiffer');
|
||||
const nullthrows = require('nullthrows');
|
||||
|
||||
const DEBUG = false;
|
||||
|
||||
const KEY_1 = 'key_1';
|
||||
const VAL_1 = 'val_1';
|
||||
const KEY_2 = 'key_2';
|
||||
const VAL_2 = 'val_2';
|
||||
const KEY_MERGE = 'key_merge';
|
||||
const VAL_MERGE_1 = {foo: 1, bar: {hoo: 1, boo: 1}, moo: {a: 3}};
|
||||
const VAL_MERGE_2 = {bar: {hoo: 2}, baz: 2, moo: {a: 3}};
|
||||
const VAL_MERGE_EXPECT = {foo: 1, bar: {hoo: 2, boo: 1}, baz: 2, moo: {a: 3}};
|
||||
|
||||
// setup in componentDidMount
|
||||
let done = (result: ?boolean) => {};
|
||||
let updateMessage = (message: string) => {};
|
||||
|
||||
function runTestCase(description: string, fn) {
|
||||
updateMessage(description);
|
||||
fn();
|
||||
}
|
||||
|
||||
function expectTrue(condition: boolean, message: string) {
|
||||
if (!condition) {
|
||||
throw new Error(message);
|
||||
}
|
||||
}
|
||||
|
||||
// Type-safe wrapper around JSON.stringify
|
||||
function stringify(
|
||||
value:
|
||||
| void
|
||||
| null
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| {...}
|
||||
| $ReadOnlyArray<mixed>,
|
||||
): string {
|
||||
if (typeof value === 'undefined') {
|
||||
return 'undefined';
|
||||
}
|
||||
return JSON.stringify(value);
|
||||
}
|
||||
|
||||
function expectEqual(lhs, rhs, testname: string) {
|
||||
expectTrue(
|
||||
!deepDiffer(lhs, rhs),
|
||||
'Error in test ' +
|
||||
testname +
|
||||
': expected\n' +
|
||||
stringify(rhs) +
|
||||
'\ngot\n' +
|
||||
stringify(lhs),
|
||||
);
|
||||
}
|
||||
|
||||
function expectAsyncNoError(place, err) {
|
||||
if (err instanceof Error) {
|
||||
err = err.message;
|
||||
}
|
||||
expectTrue(
|
||||
err === null,
|
||||
'Unexpected error in ' + place + ': ' + stringify(err),
|
||||
);
|
||||
}
|
||||
|
||||
function testSetAndGet() {
|
||||
AsyncStorage.setItem(KEY_1, VAL_1, err1 => {
|
||||
expectAsyncNoError('testSetAndGet/setItem', err1);
|
||||
AsyncStorage.getItem(KEY_1, (err2, result) => {
|
||||
expectAsyncNoError('testSetAndGet/getItem', err2);
|
||||
expectEqual(result, VAL_1, 'testSetAndGet setItem');
|
||||
updateMessage('get(key_1) correctly returned ' + String(result));
|
||||
runTestCase('should get null for missing key', testMissingGet);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function testMissingGet() {
|
||||
AsyncStorage.getItem(KEY_2, (err, result) => {
|
||||
expectAsyncNoError('testMissingGet/setItem', err);
|
||||
expectEqual(result, null, 'testMissingGet');
|
||||
updateMessage('missing get(key_2) correctly returned ' + String(result));
|
||||
runTestCase('check set twice results in a single key', testSetTwice);
|
||||
});
|
||||
}
|
||||
|
||||
function testSetTwice() {
|
||||
AsyncStorage.setItem(KEY_1, VAL_1, () => {
|
||||
AsyncStorage.setItem(KEY_1, VAL_1, () => {
|
||||
AsyncStorage.getItem(KEY_1, (err, result) => {
|
||||
expectAsyncNoError('testSetTwice/setItem', err);
|
||||
expectEqual(result, VAL_1, 'testSetTwice');
|
||||
updateMessage('setTwice worked as expected');
|
||||
runTestCase('test removeItem', testRemoveItem);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function testRemoveItem() {
|
||||
AsyncStorage.setItem(KEY_1, VAL_1, () => {
|
||||
AsyncStorage.setItem(KEY_2, VAL_2, () => {
|
||||
AsyncStorage.getAllKeys((err, result) => {
|
||||
expectAsyncNoError('testRemoveItem/getAllKeys', err);
|
||||
expectTrue(
|
||||
nullthrows(result).indexOf(KEY_1) >= 0 &&
|
||||
nullthrows(result).indexOf(KEY_2) >= 0,
|
||||
'Missing KEY_1 or KEY_2 in ' + '(' + nullthrows(result).join() + ')',
|
||||
);
|
||||
updateMessage('testRemoveItem - add two items');
|
||||
AsyncStorage.removeItem(KEY_1, err2 => {
|
||||
expectAsyncNoError('testRemoveItem/removeItem', err2);
|
||||
updateMessage('delete successful ');
|
||||
AsyncStorage.getItem(KEY_1, (err3, result2) => {
|
||||
expectAsyncNoError('testRemoveItem/getItem', err3);
|
||||
expectEqual(
|
||||
result2,
|
||||
null,
|
||||
'testRemoveItem: key_1 present after delete',
|
||||
);
|
||||
updateMessage('key properly removed ');
|
||||
AsyncStorage.getAllKeys((err4, result3) => {
|
||||
expectAsyncNoError('testRemoveItem/getAllKeys', err4);
|
||||
expectTrue(
|
||||
nullthrows(result3).indexOf(KEY_1) === -1,
|
||||
'Unexpected: KEY_1 present in ' + nullthrows(result3).join(),
|
||||
);
|
||||
updateMessage('proper length returned.');
|
||||
runTestCase('should merge values', testMerge);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function testMerge() {
|
||||
AsyncStorage.setItem(KEY_MERGE, stringify(VAL_MERGE_1), err1 => {
|
||||
expectAsyncNoError('testMerge/setItem', err1);
|
||||
AsyncStorage.mergeItem(KEY_MERGE, stringify(VAL_MERGE_2), err2 => {
|
||||
expectAsyncNoError('testMerge/mergeItem', err2);
|
||||
AsyncStorage.getItem(KEY_MERGE, (err3, result) => {
|
||||
expectAsyncNoError('testMerge/setItem', err3);
|
||||
expectEqual(
|
||||
JSON.parse(nullthrows(result)),
|
||||
VAL_MERGE_EXPECT,
|
||||
'testMerge',
|
||||
);
|
||||
updateMessage('objects deeply merged\nDone!');
|
||||
runTestCase('multi set and get', testOptimizedMultiGet);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function testOptimizedMultiGet() {
|
||||
let batch = [
|
||||
[KEY_1, VAL_1],
|
||||
[KEY_2, VAL_2],
|
||||
];
|
||||
let keys = batch.map(([key, value]) => key);
|
||||
AsyncStorage.multiSet(batch, err1 => {
|
||||
// yes, twice on purpose
|
||||
[1, 2].forEach(i => {
|
||||
expectAsyncNoError(`${i} testOptimizedMultiGet/multiSet`, err1);
|
||||
AsyncStorage.multiGet(keys, (err2, result) => {
|
||||
expectAsyncNoError(`${i} testOptimizedMultiGet/multiGet`, err2);
|
||||
expectEqual(result, batch, `${i} testOptimizedMultiGet multiGet`);
|
||||
updateMessage(
|
||||
'multiGet([key_1, key_2]) correctly returned ' + stringify(result),
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class AsyncStorageTest extends React.Component<{...}, $FlowFixMeState> {
|
||||
state: any | {|done: boolean, messages: string|} = {
|
||||
messages: 'Initializing...',
|
||||
done: false,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
done = () =>
|
||||
this.setState({done: true}, () => {
|
||||
TestModule.markTestCompleted();
|
||||
});
|
||||
updateMessage = msg => {
|
||||
this.setState({messages: this.state.messages.concat('\n' + msg)});
|
||||
DEBUG && console.log(msg);
|
||||
};
|
||||
AsyncStorage.clear(testSetAndGet);
|
||||
}
|
||||
|
||||
render(): React.Node {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text>
|
||||
{/* $FlowFixMe[incompatible-type] (>=0.54.0 site=react_native_fb,react_
|
||||
* native_oss) This comment suppresses an error found when Flow v0.54
|
||||
* was deployed. To see the error delete this comment and run Flow.
|
||||
*/
|
||||
this.constructor.displayName + ': '}
|
||||
{this.state.done ? 'Done' : 'Testing...'}
|
||||
{'\n\n' + this.state.messages}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: 'white',
|
||||
padding: 40,
|
||||
},
|
||||
});
|
||||
|
||||
AsyncStorageTest.displayName = 'AsyncStorageTest';
|
||||
|
||||
module.exports = AsyncStorageTest;
|
||||
@@ -1,4 +1,4 @@
|
||||
load("@fbsource//tools/build_defs:js_library_glob.bzl", "js_library_glob")
|
||||
load("@fbsource//tools/build_defs:js_glob.bzl", "js_glob")
|
||||
load("@fbsource//tools/build_defs/oss:metro_defs.bzl", "rn_library")
|
||||
|
||||
# This file was generated by running
|
||||
@@ -6,7 +6,7 @@ load("@fbsource//tools/build_defs/oss:metro_defs.bzl", "rn_library")
|
||||
|
||||
rn_library(
|
||||
name = "IntegrationTests",
|
||||
srcs = js_library_glob(
|
||||
srcs = js_glob(
|
||||
[
|
||||
"**/*",
|
||||
],
|
||||
@@ -17,9 +17,7 @@ rn_library(
|
||||
"websocket_integration_test_server.js",
|
||||
],
|
||||
),
|
||||
labels = [
|
||||
"pfh:ReactNative_CommonInfrastructurePlaceholder",
|
||||
],
|
||||
labels = ["supermodule:xplat/default/public.react_native.tests"],
|
||||
skip_processors = True,
|
||||
visibility = ["PUBLIC"],
|
||||
deps = [
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
@@ -20,7 +20,7 @@ const {View} = ReactNative;
|
||||
const {TestModule} = ReactNative.NativeModules;
|
||||
|
||||
class GlobalEvalWithSourceUrlTest extends React.Component<{...}> {
|
||||
componentDidMount(): void {
|
||||
componentDidMount() {
|
||||
if (typeof global.globalEvalWithSourceUrl !== 'function') {
|
||||
throw new Error(
|
||||
'Expected to find globalEvalWithSourceUrl function on global object but found ' +
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
@@ -16,7 +16,7 @@ const {Image} = ReactNative;
|
||||
const {TestModule} = ReactNative.NativeModules;
|
||||
|
||||
class ImageSnapshotTest extends React.Component<{...}> {
|
||||
componentDidMount(): void {
|
||||
componentDidMount() {
|
||||
if (!TestModule.verifySnapshot) {
|
||||
throw new Error('TestModule.verifySnapshot not defined.');
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
@@ -56,14 +56,11 @@ class IntegrationTestHarnessTest extends React.Component<Props, State> {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text>
|
||||
{
|
||||
/* $FlowFixMe[incompatible-type] (>=0.54.0 site=react_native_fb,react_
|
||||
* native_oss) This comment suppresses an error found when Flow v0.54
|
||||
* was deployed. To see the error delete this comment and run Flow.
|
||||
*/
|
||||
// $FlowFixMe[unsafe-addition]
|
||||
this.constructor.displayName + ': '
|
||||
}
|
||||
{/* $FlowFixMe[incompatible-type] (>=0.54.0 site=react_native_fb,react_
|
||||
* native_oss) This comment suppresses an error found when Flow v0.54
|
||||
* was deployed. To see the error delete this comment and run Flow.
|
||||
*/
|
||||
this.constructor.displayName + ': '}
|
||||
{this.state.done ? 'Done' : 'Testing...'}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
@@ -13,13 +13,20 @@
|
||||
require('react-native/Libraries/Core/InitializeCore');
|
||||
const React = require('react');
|
||||
const ReactNative = require('react-native');
|
||||
const {AppRegistry, ScrollView, StyleSheet, Text, TouchableOpacity, View} =
|
||||
ReactNative;
|
||||
const {
|
||||
AppRegistry,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
// Keep this list in sync with RNTesterIntegrationTests.m
|
||||
const TESTS = [
|
||||
require('./IntegrationTestHarnessTest'),
|
||||
require('./TimersTest'),
|
||||
require('./AsyncStorageTest'),
|
||||
require('./LayoutEventsTest'),
|
||||
require('./AppEventsTest'),
|
||||
require('./SimpleSnapshotTest'),
|
||||
@@ -45,11 +52,11 @@ require('./LoggingTestModule');
|
||||
type Test = any;
|
||||
|
||||
class IntegrationTestsApp extends React.Component<{...}, $FlowFixMeState> {
|
||||
state: {test: ?Test} = {
|
||||
state = {
|
||||
test: (null: ?Test),
|
||||
};
|
||||
|
||||
render(): React.Node {
|
||||
render() {
|
||||
if (this.state.test) {
|
||||
return (
|
||||
<ScrollView>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
@@ -19,7 +19,7 @@ import type {ViewStyleProp} from 'react-native/Libraries/StyleSheet/StyleSheet';
|
||||
|
||||
const deepDiffer = require('react-native/Libraries/Utilities/differ/deepDiffer');
|
||||
|
||||
function debug(...args: Array<void | Layout | string>) {
|
||||
function debug(...args) {
|
||||
// console.log.apply(null, arguments);
|
||||
}
|
||||
|
||||
@@ -119,19 +119,16 @@ class LayoutEventsTest extends React.Component<Props, State> {
|
||||
}
|
||||
|
||||
onViewLayout: (e: LayoutEvent) => void = (e: LayoutEvent) => {
|
||||
// $FlowFixMe[incompatible-call]
|
||||
debug('received view layout event\n', e.nativeEvent);
|
||||
this.setState({viewLayout: e.nativeEvent.layout}, this.checkLayout);
|
||||
};
|
||||
|
||||
onTextLayout: (e: LayoutEvent) => void = (e: LayoutEvent) => {
|
||||
// $FlowFixMe[incompatible-call]
|
||||
debug('received text layout event\n', e.nativeEvent);
|
||||
this.setState({textLayout: e.nativeEvent.layout}, this.checkLayout);
|
||||
};
|
||||
|
||||
onImageLayout: (e: LayoutEvent) => void = (e: LayoutEvent) => {
|
||||
// $FlowFixMe[incompatible-call]
|
||||
debug('received image layout event\n', e.nativeEvent);
|
||||
this.setState({imageLayout: e.nativeEvent.layout}, this.checkLayout);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
@@ -14,24 +14,24 @@ const BatchedBridge = require('react-native/Libraries/BatchedBridge/BatchedBridg
|
||||
const invariant = require('invariant');
|
||||
|
||||
const LoggingTestModule = {
|
||||
logToConsole: function (str) {
|
||||
logToConsole: function(str) {
|
||||
console.log(str);
|
||||
},
|
||||
logToConsoleAfterWait: function (str, timeout_ms) {
|
||||
setTimeout(function () {
|
||||
logToConsoleAfterWait: function(str, timeout_ms) {
|
||||
setTimeout(function() {
|
||||
console.log(str);
|
||||
}, timeout_ms);
|
||||
},
|
||||
warning: function (str) {
|
||||
warning: function(str) {
|
||||
console.warn(str);
|
||||
},
|
||||
invariant: function (str) {
|
||||
invariant: function(str) {
|
||||
invariant(false, str);
|
||||
},
|
||||
logErrorToConsole: function (str) {
|
||||
logErrorToConsole: function(str) {
|
||||
console.error(str);
|
||||
},
|
||||
throwError: function (str) {
|
||||
throwError: function(str) {
|
||||
throw new Error(str);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
@@ -12,8 +12,14 @@
|
||||
const React = require('react');
|
||||
const ReactNative = require('react-native');
|
||||
|
||||
const {AppRegistry, ScrollView, StyleSheet, Text, TouchableOpacity, View} =
|
||||
ReactNative;
|
||||
const {
|
||||
AppRegistry,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
/* Keep this list in sync with RCTRootViewIntegrationTests.m */
|
||||
const TESTS = [
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
@@ -17,7 +17,7 @@ const {StyleSheet, View} = ReactNative;
|
||||
const {TestModule} = ReactNative.NativeModules;
|
||||
|
||||
class SimpleSnapshotTest extends React.Component<{...}> {
|
||||
componentDidMount(): void {
|
||||
componentDidMount() {
|
||||
if (!TestModule.verifySnapshot) {
|
||||
throw new Error('TestModule.verifySnapshot not defined.');
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
@@ -17,7 +17,7 @@ const {View} = ReactNative;
|
||||
const {TestModule, RNTesterTestModule} = ReactNative.NativeModules;
|
||||
|
||||
class SyncMethodTest extends React.Component<{...}> {
|
||||
componentDidMount(): void {
|
||||
componentDidMount() {
|
||||
if (
|
||||
RNTesterTestModule.echoString('test string value') !== 'test string value'
|
||||
) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
@@ -39,7 +39,7 @@ class TimersTest extends React.Component<Props, State> {
|
||||
};
|
||||
|
||||
setTimeout(fn: () => void, time: number): TimeoutID {
|
||||
const id: TimeoutID = setTimeout(() => {
|
||||
const id = setTimeout(() => {
|
||||
this._timeoutIDs.delete(id);
|
||||
fn();
|
||||
}, time);
|
||||
@@ -70,7 +70,7 @@ class TimersTest extends React.Component<Props, State> {
|
||||
}
|
||||
|
||||
setImmediate(fn: () => void): ImmediateID {
|
||||
const id: any = setImmediate(() => {
|
||||
const id = setImmediate(() => {
|
||||
this._immediateIDs.delete(id);
|
||||
fn();
|
||||
});
|
||||
@@ -81,7 +81,7 @@ class TimersTest extends React.Component<Props, State> {
|
||||
}
|
||||
|
||||
requestAnimationFrame(fn: () => void): AnimationFrameID {
|
||||
const id: AnimationFrameID = requestAnimationFrame(() => {
|
||||
const id = requestAnimationFrame(() => {
|
||||
this._animationFrameIDs.delete(id);
|
||||
fn();
|
||||
});
|
||||
@@ -253,7 +253,7 @@ class TimersTest extends React.Component<Props, State> {
|
||||
);
|
||||
}
|
||||
|
||||
_incrementInterval(): void {
|
||||
_incrementInterval() {
|
||||
if (this.state.count > 3) {
|
||||
throw new Error('interval incremented past end.');
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
@@ -45,7 +45,7 @@ class WebSocketTest extends React.Component<{...}, State> {
|
||||
|
||||
_waitFor = (condition: any, timeout: any, callback: any) => {
|
||||
let remaining = timeout;
|
||||
const timeoutFunction = function () {
|
||||
const timeoutFunction = function() {
|
||||
if (condition()) {
|
||||
callback(true);
|
||||
return;
|
||||
@@ -69,11 +69,11 @@ class WebSocketTest extends React.Component<{...}, State> {
|
||||
});
|
||||
};
|
||||
|
||||
_socketIsConnected = (): boolean => {
|
||||
_socketIsConnected = () => {
|
||||
return this.state.socketState === 1; //'OPEN'
|
||||
};
|
||||
|
||||
_socketIsDisconnected = (): boolean => {
|
||||
_socketIsDisconnected = () => {
|
||||
return this.state.socketState === 3; //'CLOSED'
|
||||
};
|
||||
|
||||
@@ -106,7 +106,7 @@ class WebSocketTest extends React.Component<{...}, State> {
|
||||
this._sendText(this.state.testMessage);
|
||||
};
|
||||
|
||||
_receivedTestExpectedResponse = (): boolean => {
|
||||
_receivedTestExpectedResponse = () => {
|
||||
return this.state.lastMessage === this.state.testExpectedResponse;
|
||||
};
|
||||
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
# Set terminal title
|
||||
echo -en "\033]0;Web Socket Test Server\a"
|
||||
clear
|
||||
|
||||
THIS_DIR=$(cd -P "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo "${BASH_SOURCE[0]}")")" && pwd)
|
||||
pushd "$THIS_DIR"
|
||||
./websocket_integration_test_server.js
|
||||
popd
|
||||
|
||||
echo "Process terminated. Press <enter> to close the window"
|
||||
read
|
||||
@@ -1,12 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
THIS_DIR=$(cd -P "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo "${BASH_SOURCE[0]}")")" && pwd)
|
||||
pushd "$THIS_DIR" || exit
|
||||
./websocket_integration_test_server.js
|
||||
popd || exit
|
||||
|
||||
echo "Process terminated."
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
+8
-29
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
@@ -8,18 +8,17 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import type {ProcessedColorValue} from '../StyleSheet/processColor';
|
||||
import type {ColorValue} from '../StyleSheet/StyleSheet';
|
||||
|
||||
import RCTActionSheetManager from './NativeActionSheetManager';
|
||||
|
||||
const processColor = require('../StyleSheet/processColor').default;
|
||||
const invariant = require('invariant');
|
||||
const processColor = require('../StyleSheet/processColor');
|
||||
import type {ColorValue} from '../StyleSheet/StyleSheet';
|
||||
import type {ProcessedColorValue} from '../StyleSheet/processColor';
|
||||
|
||||
/**
|
||||
* Display action sheets and share sheets on iOS.
|
||||
*
|
||||
* See https://reactnative.dev/docs/actionsheetios
|
||||
* See https://reactnative.dev/docs/actionsheetios.html
|
||||
*/
|
||||
const ActionSheetIOS = {
|
||||
/**
|
||||
@@ -37,7 +36,7 @@ const ActionSheetIOS = {
|
||||
* The 'callback' function takes one parameter, the zero-based index
|
||||
* of the selected item.
|
||||
*
|
||||
* See https://reactnative.dev/docs/actionsheetios#showactionsheetwithoptions
|
||||
* See https://reactnative.dev/docs/actionsheetios.html#showactionsheetwithoptions
|
||||
*/
|
||||
showActionSheetWithOptions(
|
||||
options: {|
|
||||
@@ -48,7 +47,6 @@ const ActionSheetIOS = {
|
||||
+cancelButtonIndex?: ?number,
|
||||
+anchor?: ?number,
|
||||
+tintColor?: ColorValue | ProcessedColorValue,
|
||||
+cancelButtonTintColor?: ColorValue | ProcessedColorValue,
|
||||
+userInterfaceStyle?: string,
|
||||
+disabledButtonIndices?: Array<number>,
|
||||
|},
|
||||
@@ -61,12 +59,7 @@ const ActionSheetIOS = {
|
||||
invariant(typeof callback === 'function', 'Must provide a valid callback');
|
||||
invariant(RCTActionSheetManager, "ActionSheetManager doesn't exist");
|
||||
|
||||
const {
|
||||
tintColor,
|
||||
cancelButtonTintColor,
|
||||
destructiveButtonIndex,
|
||||
...remainingOptions
|
||||
} = options;
|
||||
const {tintColor, destructiveButtonIndex, ...remainingOptions} = options;
|
||||
let destructiveButtonIndices = null;
|
||||
|
||||
if (Array.isArray(destructiveButtonIndex)) {
|
||||
@@ -76,21 +69,14 @@ const ActionSheetIOS = {
|
||||
}
|
||||
|
||||
const processedTintColor = processColor(tintColor);
|
||||
const processedCancelButtonTintColor = processColor(cancelButtonTintColor);
|
||||
invariant(
|
||||
processedTintColor == null || typeof processedTintColor === 'number',
|
||||
'Unexpected color given for ActionSheetIOS.showActionSheetWithOptions tintColor',
|
||||
);
|
||||
invariant(
|
||||
processedCancelButtonTintColor == null ||
|
||||
typeof processedCancelButtonTintColor === 'number',
|
||||
'Unexpected color given for ActionSheetIOS.showActionSheetWithOptions cancelButtonTintColor',
|
||||
);
|
||||
RCTActionSheetManager.showActionSheetWithOptions(
|
||||
{
|
||||
...remainingOptions,
|
||||
tintColor: processedTintColor,
|
||||
cancelButtonTintColor: processedCancelButtonTintColor,
|
||||
destructiveButtonIndices,
|
||||
},
|
||||
callback,
|
||||
@@ -118,7 +104,7 @@ const ActionSheetIOS = {
|
||||
* - a boolean value signifying success or failure
|
||||
* - a string that, in the case of success, indicates the method of sharing
|
||||
*
|
||||
* See https://reactnative.dev/docs/actionsheetios#showshareactionsheetwithoptions
|
||||
* See https://reactnative.dev/docs/actionsheetios.html#showshareactionsheetwithoptions
|
||||
*/
|
||||
showShareActionSheetWithOptions(
|
||||
options: Object,
|
||||
@@ -144,13 +130,6 @@ const ActionSheetIOS = {
|
||||
successCallback,
|
||||
);
|
||||
},
|
||||
|
||||
dismissActionSheet: () => {
|
||||
invariant(RCTActionSheetManager, "ActionSheetManager doesn't exist");
|
||||
if (typeof RCTActionSheetManager.dismissActionSheet === 'function') {
|
||||
RCTActionSheetManager.dismissActionSheet();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = ActionSheetIOS;
|
||||
+1
-5
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
@@ -9,7 +9,6 @@
|
||||
*/
|
||||
|
||||
import type {TurboModule} from '../TurboModule/RCTExport';
|
||||
|
||||
import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry';
|
||||
|
||||
export interface Spec extends TurboModule {
|
||||
@@ -23,7 +22,6 @@ export interface Spec extends TurboModule {
|
||||
+cancelButtonIndex?: ?number,
|
||||
+anchor?: ?number,
|
||||
+tintColor?: ?number,
|
||||
+cancelButtonTintColor?: ?number,
|
||||
+userInterfaceStyle?: ?string,
|
||||
+disabledButtonIndices?: Array<number>,
|
||||
|},
|
||||
@@ -36,7 +34,6 @@ export interface Spec extends TurboModule {
|
||||
+subject?: ?string,
|
||||
+anchor?: ?number,
|
||||
+tintColor?: ?number,
|
||||
+cancelButtonTintColor?: ?number,
|
||||
+excludedActivityTypes?: ?Array<string>,
|
||||
+userInterfaceStyle?: ?string,
|
||||
|},
|
||||
@@ -48,7 +45,6 @@ export interface Spec extends TurboModule {
|
||||
|}) => void,
|
||||
successCallback: (completed: boolean, activityType: ?string) => void,
|
||||
) => void;
|
||||
+dismissActionSheet?: () => void;
|
||||
}
|
||||
|
||||
export default (TurboModuleRegistry.get<Spec>('ActionSheetManager'): ?Spec);
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
@@ -23,8 +23,8 @@ Pod::Spec.new do |s|
|
||||
s.homepage = "https://reactnative.dev/"
|
||||
s.documentation_url = "https://reactnative.dev/docs/actionsheetios"
|
||||
s.license = package["license"]
|
||||
s.author = "Meta Platforms, Inc. and its affiliates"
|
||||
s.platforms = { :ios => "12.4" }
|
||||
s.author = "Facebook, Inc. and its affiliates"
|
||||
s.platforms = { :ios => "11.0" }
|
||||
s.source = source
|
||||
s.source_files = "*.{m}"
|
||||
s.preserve_paths = "package.json", "LICENSE", "LICENSE-docs"
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
@@ -8,9 +8,8 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import type {DialogOptions} from '../NativeModules/specs/NativeDialogManagerAndroid';
|
||||
|
||||
import Platform from '../Utilities/Platform';
|
||||
import type {DialogOptions} from '../NativeModules/specs/NativeDialogManagerAndroid';
|
||||
import RCTAlertManager from './RCTAlertManager';
|
||||
|
||||
export type AlertType =
|
||||
@@ -22,14 +21,12 @@ export type AlertButtonStyle = 'default' | 'cancel' | 'destructive';
|
||||
export type Buttons = Array<{
|
||||
text?: string,
|
||||
onPress?: ?Function,
|
||||
isPreferred?: boolean,
|
||||
style?: AlertButtonStyle,
|
||||
...
|
||||
}>;
|
||||
|
||||
type Options = {
|
||||
cancelable?: ?boolean,
|
||||
userInterfaceStyle?: 'unspecified' | 'light' | 'dark',
|
||||
onDismiss?: ?() => void,
|
||||
...
|
||||
};
|
||||
@@ -37,7 +34,7 @@ type Options = {
|
||||
/**
|
||||
* Launches an alert dialog with the specified title and message.
|
||||
*
|
||||
* See https://reactnative.dev/docs/alert
|
||||
* See https://reactnative.dev/docs/alert.html
|
||||
*/
|
||||
class Alert {
|
||||
static alert(
|
||||
@@ -47,18 +44,10 @@ class Alert {
|
||||
options?: Options,
|
||||
): void {
|
||||
if (Platform.OS === 'ios') {
|
||||
Alert.prompt(
|
||||
title,
|
||||
message,
|
||||
buttons,
|
||||
'default',
|
||||
undefined,
|
||||
undefined,
|
||||
options,
|
||||
);
|
||||
Alert.prompt(title, message, buttons, 'default');
|
||||
} else if (Platform.OS === 'android') {
|
||||
const NativeDialogManagerAndroid =
|
||||
require('../NativeModules/specs/NativeDialogManagerAndroid').default;
|
||||
const NativeDialogManagerAndroid = require('../NativeModules/specs/NativeDialogManagerAndroid')
|
||||
.default;
|
||||
if (!NativeDialogManagerAndroid) {
|
||||
return;
|
||||
}
|
||||
@@ -93,8 +82,6 @@ class Alert {
|
||||
config.buttonPositive = buttonPositive.text || defaultPositiveText;
|
||||
}
|
||||
|
||||
/* $FlowFixMe[missing-local-annot] The type annotation(s) required by
|
||||
* Flow's LTI update could not be added via codemod */
|
||||
const onAction = (action, buttonKey) => {
|
||||
if (action === constants.buttonClicked) {
|
||||
if (buttonKey === constants.buttonNeutral) {
|
||||
@@ -108,7 +95,7 @@ class Alert {
|
||||
options && options.onDismiss && options.onDismiss();
|
||||
}
|
||||
};
|
||||
const onError = (errorMessage: string) => console.warn(errorMessage);
|
||||
const onError = errorMessage => console.warn(errorMessage);
|
||||
NativeDialogManagerAndroid.showAlert(config, onError, onAction);
|
||||
}
|
||||
}
|
||||
@@ -120,14 +107,12 @@ class Alert {
|
||||
type?: ?AlertType = 'plain-text',
|
||||
defaultValue?: string,
|
||||
keyboardType?: string,
|
||||
options?: Options,
|
||||
): void {
|
||||
if (Platform.OS === 'ios') {
|
||||
let callbacks: Array<?any> = [];
|
||||
let callbacks = [];
|
||||
const buttons = [];
|
||||
let cancelButtonKey;
|
||||
let destructiveButtonKey;
|
||||
let preferredButtonKey;
|
||||
if (typeof callbackOrButtons === 'function') {
|
||||
callbacks = [callbackOrButtons];
|
||||
} else if (Array.isArray(callbackOrButtons)) {
|
||||
@@ -138,11 +123,8 @@ class Alert {
|
||||
} else if (btn.style === 'destructive') {
|
||||
destructiveButtonKey = String(index);
|
||||
}
|
||||
if (btn.isPreferred) {
|
||||
preferredButtonKey = String(index);
|
||||
}
|
||||
if (btn.text || index < (callbackOrButtons || []).length - 1) {
|
||||
const btnDef: {[number]: string} = {};
|
||||
const btnDef = {};
|
||||
btnDef[index] = btn.text || '';
|
||||
buttons.push(btnDef);
|
||||
}
|
||||
@@ -158,9 +140,7 @@ class Alert {
|
||||
defaultValue,
|
||||
cancelButtonKey,
|
||||
destructiveButtonKey,
|
||||
preferredButtonKey,
|
||||
keyboardType,
|
||||
userInterfaceStyle: options?.userInterfaceStyle || undefined,
|
||||
},
|
||||
(id, value) => {
|
||||
const cb = callbacks[id];
|
||||
+1
-4
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
@@ -9,7 +9,6 @@
|
||||
*/
|
||||
|
||||
import type {TurboModule} from '../TurboModule/RCTExport';
|
||||
|
||||
import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry';
|
||||
|
||||
export type Args = {|
|
||||
@@ -20,9 +19,7 @@ export type Args = {|
|
||||
defaultValue?: string,
|
||||
cancelButtonKey?: string,
|
||||
destructiveButtonKey?: string,
|
||||
preferredButtonKey?: string,
|
||||
keyboardType?: string,
|
||||
userInterfaceStyle?: string,
|
||||
|};
|
||||
|
||||
export interface Spec extends TurboModule {
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
@@ -12,7 +12,7 @@ import NativeDialogManagerAndroid from '../NativeModules/specs/NativeDialogManag
|
||||
function emptyCallback() {}
|
||||
|
||||
module.exports = {
|
||||
alertWithArgs: function (args, callback) {
|
||||
alertWithArgs: function(args, callback) {
|
||||
// TODO(5998984): Polyfill it correctly with DialogManagerAndroid
|
||||
if (!NativeDialogManagerAndroid) {
|
||||
return;
|
||||
+2
-3
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
@@ -8,9 +8,8 @@
|
||||
* @flow strict-local
|
||||
*/
|
||||
|
||||
import type {Args} from './NativeAlertManager';
|
||||
|
||||
import NativeAlertManager from './NativeAlertManager';
|
||||
import type {Args} from './NativeAlertManager';
|
||||
|
||||
module.exports = {
|
||||
alertWithArgs(
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
import Platform from '../Utilities/Platform';
|
||||
import typeof AnimatedFlatList from './components/AnimatedFlatList';
|
||||
import typeof AnimatedImage from './components/AnimatedImage';
|
||||
import typeof AnimatedScrollView from './components/AnimatedScrollView';
|
||||
import typeof AnimatedSectionList from './components/AnimatedSectionList';
|
||||
import typeof AnimatedText from './components/AnimatedText';
|
||||
import typeof AnimatedView from './components/AnimatedView';
|
||||
|
||||
const AnimatedMock = require('./AnimatedMock');
|
||||
const AnimatedImplementation = require('./AnimatedImplementation');
|
||||
|
||||
const Animated = ((Platform.isTesting
|
||||
? AnimatedMock
|
||||
: AnimatedImplementation): typeof AnimatedMock);
|
||||
|
||||
module.exports = {
|
||||
get FlatList(): AnimatedFlatList {
|
||||
return require('./components/AnimatedFlatList');
|
||||
},
|
||||
get Image(): AnimatedImage {
|
||||
return require('./components/AnimatedImage');
|
||||
},
|
||||
get ScrollView(): AnimatedScrollView {
|
||||
return require('./components/AnimatedScrollView');
|
||||
},
|
||||
get SectionList(): AnimatedSectionList {
|
||||
return require('./components/AnimatedSectionList');
|
||||
},
|
||||
get Text(): AnimatedText {
|
||||
return require('./components/AnimatedText');
|
||||
},
|
||||
get View(): AnimatedView {
|
||||
return require('./components/AnimatedView');
|
||||
},
|
||||
...Animated,
|
||||
};
|
||||
@@ -0,0 +1,234 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const AnimatedValue = require('./nodes/AnimatedValue');
|
||||
const NativeAnimatedHelper = require('./NativeAnimatedHelper');
|
||||
const ReactNative = require('../Renderer/shims/ReactNative');
|
||||
|
||||
const invariant = require('invariant');
|
||||
|
||||
const {shouldUseNativeDriver} = require('./NativeAnimatedHelper');
|
||||
|
||||
export type Mapping = {[key: string]: Mapping, ...} | AnimatedValue;
|
||||
export type EventConfig = {
|
||||
listener?: ?Function,
|
||||
useNativeDriver: boolean,
|
||||
};
|
||||
|
||||
function attachNativeEvent(
|
||||
viewRef: any,
|
||||
eventName: string,
|
||||
argMapping: $ReadOnlyArray<?Mapping>,
|
||||
): {detach: () => void} {
|
||||
// Find animated values in `argMapping` and create an array representing their
|
||||
// key path inside the `nativeEvent` object. Ex.: ['contentOffset', 'x'].
|
||||
const eventMappings = [];
|
||||
|
||||
const traverse = (value, path) => {
|
||||
if (value instanceof AnimatedValue) {
|
||||
value.__makeNative();
|
||||
|
||||
eventMappings.push({
|
||||
nativeEventPath: path,
|
||||
animatedValueTag: value.__getNativeTag(),
|
||||
});
|
||||
} else if (typeof value === 'object') {
|
||||
for (const key in value) {
|
||||
traverse(value[key], path.concat(key));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
invariant(
|
||||
argMapping[0] && argMapping[0].nativeEvent,
|
||||
'Native driven events only support animated values contained inside `nativeEvent`.',
|
||||
);
|
||||
|
||||
// Assume that the event containing `nativeEvent` is always the first argument.
|
||||
traverse(argMapping[0].nativeEvent, []);
|
||||
|
||||
const viewTag = ReactNative.findNodeHandle(viewRef);
|
||||
if (viewTag != null) {
|
||||
eventMappings.forEach(mapping => {
|
||||
NativeAnimatedHelper.API.addAnimatedEventToView(
|
||||
viewTag,
|
||||
eventName,
|
||||
mapping,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
detach() {
|
||||
if (viewTag != null) {
|
||||
eventMappings.forEach(mapping => {
|
||||
NativeAnimatedHelper.API.removeAnimatedEventFromView(
|
||||
viewTag,
|
||||
eventName,
|
||||
// $FlowFixMe[incompatible-call]
|
||||
mapping.animatedValueTag,
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function validateMapping(argMapping, args) {
|
||||
const validate = (recMapping, recEvt, key) => {
|
||||
if (recMapping instanceof AnimatedValue) {
|
||||
invariant(
|
||||
typeof recEvt === 'number',
|
||||
'Bad mapping of event key ' +
|
||||
key +
|
||||
', should be number but got ' +
|
||||
typeof recEvt,
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (typeof recEvt === 'number') {
|
||||
invariant(
|
||||
recMapping instanceof AnimatedValue,
|
||||
'Bad mapping of type ' +
|
||||
typeof recMapping +
|
||||
' for key ' +
|
||||
key +
|
||||
', event value must map to AnimatedValue',
|
||||
);
|
||||
return;
|
||||
}
|
||||
invariant(
|
||||
typeof recMapping === 'object',
|
||||
'Bad mapping of type ' + typeof recMapping + ' for key ' + key,
|
||||
);
|
||||
invariant(
|
||||
typeof recEvt === 'object',
|
||||
'Bad event of type ' + typeof recEvt + ' for key ' + key,
|
||||
);
|
||||
for (const mappingKey in recMapping) {
|
||||
validate(recMapping[mappingKey], recEvt[mappingKey], mappingKey);
|
||||
}
|
||||
};
|
||||
|
||||
invariant(
|
||||
args.length >= argMapping.length,
|
||||
'Event has less arguments than mapping',
|
||||
);
|
||||
argMapping.forEach((mapping, idx) => {
|
||||
validate(mapping, args[idx], 'arg' + idx);
|
||||
});
|
||||
}
|
||||
|
||||
class AnimatedEvent {
|
||||
_argMapping: $ReadOnlyArray<?Mapping>;
|
||||
_listeners: Array<Function> = [];
|
||||
_callListeners: Function;
|
||||
_attachedEvent: ?{detach: () => void, ...};
|
||||
__isNative: boolean;
|
||||
|
||||
constructor(argMapping: $ReadOnlyArray<?Mapping>, config: EventConfig) {
|
||||
this._argMapping = argMapping;
|
||||
|
||||
if (config == null) {
|
||||
console.warn('Animated.event now requires a second argument for options');
|
||||
config = {useNativeDriver: false};
|
||||
}
|
||||
|
||||
if (config.listener) {
|
||||
this.__addListener(config.listener);
|
||||
}
|
||||
this._callListeners = this._callListeners.bind(this);
|
||||
this._attachedEvent = null;
|
||||
this.__isNative = shouldUseNativeDriver(config);
|
||||
}
|
||||
|
||||
__addListener(callback: Function): void {
|
||||
this._listeners.push(callback);
|
||||
}
|
||||
|
||||
__removeListener(callback: Function): void {
|
||||
this._listeners = this._listeners.filter(listener => listener !== callback);
|
||||
}
|
||||
|
||||
__attach(viewRef: any, eventName: string) {
|
||||
invariant(
|
||||
this.__isNative,
|
||||
'Only native driven events need to be attached.',
|
||||
);
|
||||
|
||||
this._attachedEvent = attachNativeEvent(
|
||||
viewRef,
|
||||
eventName,
|
||||
this._argMapping,
|
||||
);
|
||||
}
|
||||
|
||||
__detach(viewTag: any, eventName: string) {
|
||||
invariant(
|
||||
this.__isNative,
|
||||
'Only native driven events need to be detached.',
|
||||
);
|
||||
|
||||
this._attachedEvent && this._attachedEvent.detach();
|
||||
}
|
||||
|
||||
__getHandler(): any | ((...args: any) => void) {
|
||||
if (this.__isNative) {
|
||||
if (__DEV__) {
|
||||
let validatedMapping = false;
|
||||
return (...args: any) => {
|
||||
if (!validatedMapping) {
|
||||
validateMapping(this._argMapping, args);
|
||||
validatedMapping = true;
|
||||
}
|
||||
this._callListeners(...args);
|
||||
};
|
||||
} else {
|
||||
return this._callListeners;
|
||||
}
|
||||
}
|
||||
|
||||
let validatedMapping = false;
|
||||
return (...args: any) => {
|
||||
if (__DEV__ && !validatedMapping) {
|
||||
validateMapping(this._argMapping, args);
|
||||
validatedMapping = true;
|
||||
}
|
||||
|
||||
const traverse = (recMapping, recEvt, key) => {
|
||||
if (recMapping instanceof AnimatedValue) {
|
||||
if (typeof recEvt === 'number') {
|
||||
recMapping.setValue(recEvt);
|
||||
}
|
||||
} else if (typeof recMapping === 'object') {
|
||||
for (const mappingKey in recMapping) {
|
||||
/* $FlowFixMe[prop-missing] (>=0.120.0) This comment suppresses an
|
||||
* error found when Flow v0.120 was deployed. To see the error,
|
||||
* delete this comment and run Flow. */
|
||||
traverse(recMapping[mappingKey], recEvt[mappingKey], mappingKey);
|
||||
}
|
||||
}
|
||||
};
|
||||
this._argMapping.forEach((mapping, idx) => {
|
||||
traverse(mapping, args[idx], 'arg' + idx);
|
||||
});
|
||||
|
||||
this._callListeners(...args);
|
||||
};
|
||||
}
|
||||
|
||||
_callListeners(...args: any) {
|
||||
this._listeners.forEach(listener => listener(...args));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {AnimatedEvent, attachNativeEvent};
|
||||
@@ -0,0 +1,709 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const {AnimatedEvent, attachNativeEvent} = require('./AnimatedEvent');
|
||||
const AnimatedAddition = require('./nodes/AnimatedAddition');
|
||||
const AnimatedDiffClamp = require('./nodes/AnimatedDiffClamp');
|
||||
const AnimatedDivision = require('./nodes/AnimatedDivision');
|
||||
const AnimatedInterpolation = require('./nodes/AnimatedInterpolation');
|
||||
const AnimatedModulo = require('./nodes/AnimatedModulo');
|
||||
const AnimatedMultiplication = require('./nodes/AnimatedMultiplication');
|
||||
const AnimatedNode = require('./nodes/AnimatedNode');
|
||||
const AnimatedProps = require('./nodes/AnimatedProps');
|
||||
const AnimatedSubtraction = require('./nodes/AnimatedSubtraction');
|
||||
const AnimatedTracking = require('./nodes/AnimatedTracking');
|
||||
const AnimatedValue = require('./nodes/AnimatedValue');
|
||||
const AnimatedValueXY = require('./nodes/AnimatedValueXY');
|
||||
const DecayAnimation = require('./animations/DecayAnimation');
|
||||
const SpringAnimation = require('./animations/SpringAnimation');
|
||||
const TimingAnimation = require('./animations/TimingAnimation');
|
||||
|
||||
const createAnimatedComponent = require('./createAnimatedComponent');
|
||||
|
||||
import type {
|
||||
AnimationConfig,
|
||||
EndCallback,
|
||||
EndResult,
|
||||
} from './animations/Animation';
|
||||
import type {TimingAnimationConfig} from './animations/TimingAnimation';
|
||||
import type {DecayAnimationConfig} from './animations/DecayAnimation';
|
||||
import type {SpringAnimationConfig} from './animations/SpringAnimation';
|
||||
import type {Mapping, EventConfig} from './AnimatedEvent';
|
||||
|
||||
export type CompositeAnimation = {
|
||||
start: (callback?: ?EndCallback) => void,
|
||||
stop: () => void,
|
||||
reset: () => void,
|
||||
_startNativeLoop: (iterations?: number) => void,
|
||||
_isUsingNativeDriver: () => boolean,
|
||||
...
|
||||
};
|
||||
|
||||
const add = function(
|
||||
a: AnimatedNode | number,
|
||||
b: AnimatedNode | number,
|
||||
): AnimatedAddition {
|
||||
return new AnimatedAddition(a, b);
|
||||
};
|
||||
|
||||
const subtract = function(
|
||||
a: AnimatedNode | number,
|
||||
b: AnimatedNode | number,
|
||||
): AnimatedSubtraction {
|
||||
return new AnimatedSubtraction(a, b);
|
||||
};
|
||||
|
||||
const divide = function(
|
||||
a: AnimatedNode | number,
|
||||
b: AnimatedNode | number,
|
||||
): AnimatedDivision {
|
||||
return new AnimatedDivision(a, b);
|
||||
};
|
||||
|
||||
const multiply = function(
|
||||
a: AnimatedNode | number,
|
||||
b: AnimatedNode | number,
|
||||
): AnimatedMultiplication {
|
||||
return new AnimatedMultiplication(a, b);
|
||||
};
|
||||
|
||||
const modulo = function(a: AnimatedNode, modulus: number): AnimatedModulo {
|
||||
return new AnimatedModulo(a, modulus);
|
||||
};
|
||||
|
||||
const diffClamp = function(
|
||||
a: AnimatedNode,
|
||||
min: number,
|
||||
max: number,
|
||||
): AnimatedDiffClamp {
|
||||
return new AnimatedDiffClamp(a, min, max);
|
||||
};
|
||||
|
||||
const _combineCallbacks = function(
|
||||
callback: ?EndCallback,
|
||||
config: {...AnimationConfig, ...},
|
||||
) {
|
||||
if (callback && config.onComplete) {
|
||||
return (...args) => {
|
||||
config.onComplete && config.onComplete(...args);
|
||||
callback && callback(...args);
|
||||
};
|
||||
} else {
|
||||
return callback || config.onComplete;
|
||||
}
|
||||
};
|
||||
|
||||
const maybeVectorAnim = function(
|
||||
value: AnimatedValue | AnimatedValueXY,
|
||||
config: Object,
|
||||
anim: (value: AnimatedValue, config: Object) => CompositeAnimation,
|
||||
): ?CompositeAnimation {
|
||||
if (value instanceof AnimatedValueXY) {
|
||||
const configX = {...config};
|
||||
const configY = {...config};
|
||||
for (const key in config) {
|
||||
const {x, y} = config[key];
|
||||
if (x !== undefined && y !== undefined) {
|
||||
configX[key] = x;
|
||||
configY[key] = y;
|
||||
}
|
||||
}
|
||||
const aX = anim((value: AnimatedValueXY).x, configX);
|
||||
const aY = anim((value: AnimatedValueXY).y, configY);
|
||||
// We use `stopTogether: false` here because otherwise tracking will break
|
||||
// because the second animation will get stopped before it can update.
|
||||
return parallel([aX, aY], {stopTogether: false});
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const spring = function(
|
||||
value: AnimatedValue | AnimatedValueXY,
|
||||
config: SpringAnimationConfig,
|
||||
): CompositeAnimation {
|
||||
const start = function(
|
||||
animatedValue: AnimatedValue | AnimatedValueXY,
|
||||
configuration: SpringAnimationConfig,
|
||||
callback?: ?EndCallback,
|
||||
): void {
|
||||
callback = _combineCallbacks(callback, configuration);
|
||||
const singleValue: any = animatedValue;
|
||||
const singleConfig: any = configuration;
|
||||
singleValue.stopTracking();
|
||||
if (configuration.toValue instanceof AnimatedNode) {
|
||||
singleValue.track(
|
||||
new AnimatedTracking(
|
||||
singleValue,
|
||||
configuration.toValue,
|
||||
SpringAnimation,
|
||||
singleConfig,
|
||||
callback,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
singleValue.animate(new SpringAnimation(singleConfig), callback);
|
||||
}
|
||||
};
|
||||
return (
|
||||
maybeVectorAnim(value, config, spring) || {
|
||||
start: function(callback?: ?EndCallback): void {
|
||||
start(value, config, callback);
|
||||
},
|
||||
|
||||
stop: function(): void {
|
||||
value.stopAnimation();
|
||||
},
|
||||
|
||||
reset: function(): void {
|
||||
value.resetAnimation();
|
||||
},
|
||||
|
||||
_startNativeLoop: function(iterations?: number): void {
|
||||
const singleConfig = {...config, iterations};
|
||||
start(value, singleConfig);
|
||||
},
|
||||
|
||||
_isUsingNativeDriver: function(): boolean {
|
||||
return config.useNativeDriver || false;
|
||||
},
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const timing = function(
|
||||
value: AnimatedValue | AnimatedValueXY,
|
||||
config: TimingAnimationConfig,
|
||||
): CompositeAnimation {
|
||||
const start = function(
|
||||
animatedValue: AnimatedValue | AnimatedValueXY,
|
||||
configuration: TimingAnimationConfig,
|
||||
callback?: ?EndCallback,
|
||||
): void {
|
||||
callback = _combineCallbacks(callback, configuration);
|
||||
const singleValue: any = animatedValue;
|
||||
const singleConfig: any = configuration;
|
||||
singleValue.stopTracking();
|
||||
if (configuration.toValue instanceof AnimatedNode) {
|
||||
singleValue.track(
|
||||
new AnimatedTracking(
|
||||
singleValue,
|
||||
configuration.toValue,
|
||||
TimingAnimation,
|
||||
singleConfig,
|
||||
callback,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
singleValue.animate(new TimingAnimation(singleConfig), callback);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
maybeVectorAnim(value, config, timing) || {
|
||||
start: function(callback?: ?EndCallback): void {
|
||||
start(value, config, callback);
|
||||
},
|
||||
|
||||
stop: function(): void {
|
||||
value.stopAnimation();
|
||||
},
|
||||
|
||||
reset: function(): void {
|
||||
value.resetAnimation();
|
||||
},
|
||||
|
||||
_startNativeLoop: function(iterations?: number): void {
|
||||
const singleConfig = {...config, iterations};
|
||||
start(value, singleConfig);
|
||||
},
|
||||
|
||||
_isUsingNativeDriver: function(): boolean {
|
||||
return config.useNativeDriver || false;
|
||||
},
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const decay = function(
|
||||
value: AnimatedValue | AnimatedValueXY,
|
||||
config: DecayAnimationConfig,
|
||||
): CompositeAnimation {
|
||||
const start = function(
|
||||
animatedValue: AnimatedValue | AnimatedValueXY,
|
||||
configuration: DecayAnimationConfig,
|
||||
callback?: ?EndCallback,
|
||||
): void {
|
||||
callback = _combineCallbacks(callback, configuration);
|
||||
const singleValue: any = animatedValue;
|
||||
const singleConfig: any = configuration;
|
||||
singleValue.stopTracking();
|
||||
singleValue.animate(new DecayAnimation(singleConfig), callback);
|
||||
};
|
||||
|
||||
return (
|
||||
maybeVectorAnim(value, config, decay) || {
|
||||
start: function(callback?: ?EndCallback): void {
|
||||
start(value, config, callback);
|
||||
},
|
||||
|
||||
stop: function(): void {
|
||||
value.stopAnimation();
|
||||
},
|
||||
|
||||
reset: function(): void {
|
||||
value.resetAnimation();
|
||||
},
|
||||
|
||||
_startNativeLoop: function(iterations?: number): void {
|
||||
const singleConfig = {...config, iterations};
|
||||
start(value, singleConfig);
|
||||
},
|
||||
|
||||
_isUsingNativeDriver: function(): boolean {
|
||||
return config.useNativeDriver || false;
|
||||
},
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const sequence = function(
|
||||
animations: Array<CompositeAnimation>,
|
||||
): CompositeAnimation {
|
||||
let current = 0;
|
||||
return {
|
||||
start: function(callback?: ?EndCallback) {
|
||||
const onComplete = function(result) {
|
||||
if (!result.finished) {
|
||||
callback && callback(result);
|
||||
return;
|
||||
}
|
||||
|
||||
current++;
|
||||
|
||||
if (current === animations.length) {
|
||||
callback && callback(result);
|
||||
return;
|
||||
}
|
||||
|
||||
animations[current].start(onComplete);
|
||||
};
|
||||
|
||||
if (animations.length === 0) {
|
||||
callback && callback({finished: true});
|
||||
} else {
|
||||
animations[current].start(onComplete);
|
||||
}
|
||||
},
|
||||
|
||||
stop: function() {
|
||||
if (current < animations.length) {
|
||||
animations[current].stop();
|
||||
}
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
animations.forEach((animation, idx) => {
|
||||
if (idx <= current) {
|
||||
animation.reset();
|
||||
}
|
||||
});
|
||||
current = 0;
|
||||
},
|
||||
|
||||
_startNativeLoop: function() {
|
||||
throw new Error(
|
||||
'Loops run using the native driver cannot contain Animated.sequence animations',
|
||||
);
|
||||
},
|
||||
|
||||
_isUsingNativeDriver: function(): boolean {
|
||||
return false;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
type ParallelConfig = {
|
||||
// If one is stopped, stop all. default: true
|
||||
stopTogether?: boolean,
|
||||
...
|
||||
};
|
||||
const parallel = function(
|
||||
animations: Array<CompositeAnimation>,
|
||||
config?: ?ParallelConfig,
|
||||
): CompositeAnimation {
|
||||
let doneCount = 0;
|
||||
// Make sure we only call stop() at most once for each animation
|
||||
const hasEnded = {};
|
||||
const stopTogether = !(config && config.stopTogether === false);
|
||||
|
||||
const result = {
|
||||
start: function(callback?: ?EndCallback) {
|
||||
if (doneCount === animations.length) {
|
||||
callback && callback({finished: true});
|
||||
return;
|
||||
}
|
||||
|
||||
animations.forEach((animation, idx) => {
|
||||
const cb = function(endResult) {
|
||||
hasEnded[idx] = true;
|
||||
doneCount++;
|
||||
if (doneCount === animations.length) {
|
||||
doneCount = 0;
|
||||
callback && callback(endResult);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!endResult.finished && stopTogether) {
|
||||
result.stop();
|
||||
}
|
||||
};
|
||||
|
||||
if (!animation) {
|
||||
cb({finished: true});
|
||||
} else {
|
||||
animation.start(cb);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
stop: function(): void {
|
||||
animations.forEach((animation, idx) => {
|
||||
!hasEnded[idx] && animation.stop();
|
||||
hasEnded[idx] = true;
|
||||
});
|
||||
},
|
||||
|
||||
reset: function(): void {
|
||||
animations.forEach((animation, idx) => {
|
||||
animation.reset();
|
||||
hasEnded[idx] = false;
|
||||
doneCount = 0;
|
||||
});
|
||||
},
|
||||
|
||||
_startNativeLoop: function() {
|
||||
throw new Error(
|
||||
'Loops run using the native driver cannot contain Animated.parallel animations',
|
||||
);
|
||||
},
|
||||
|
||||
_isUsingNativeDriver: function(): boolean {
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
const delay = function(time: number): CompositeAnimation {
|
||||
// Would be nice to make a specialized implementation
|
||||
return timing(new AnimatedValue(0), {
|
||||
toValue: 0,
|
||||
delay: time,
|
||||
duration: 0,
|
||||
useNativeDriver: false,
|
||||
});
|
||||
};
|
||||
|
||||
const stagger = function(
|
||||
time: number,
|
||||
animations: Array<CompositeAnimation>,
|
||||
): CompositeAnimation {
|
||||
return parallel(
|
||||
animations.map((animation, i) => {
|
||||
return sequence([delay(time * i), animation]);
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
type LoopAnimationConfig = {
|
||||
iterations: number,
|
||||
resetBeforeIteration?: boolean,
|
||||
...
|
||||
};
|
||||
|
||||
const loop = function(
|
||||
animation: CompositeAnimation,
|
||||
{iterations = -1, resetBeforeIteration = true}: LoopAnimationConfig = {},
|
||||
): CompositeAnimation {
|
||||
let isFinished = false;
|
||||
let iterationsSoFar = 0;
|
||||
return {
|
||||
start: function(callback?: ?EndCallback) {
|
||||
const restart = function(result: EndResult = {finished: true}): void {
|
||||
if (
|
||||
isFinished ||
|
||||
iterationsSoFar === iterations ||
|
||||
result.finished === false
|
||||
) {
|
||||
callback && callback(result);
|
||||
} else {
|
||||
iterationsSoFar++;
|
||||
resetBeforeIteration && animation.reset();
|
||||
animation.start(restart);
|
||||
}
|
||||
};
|
||||
if (!animation || iterations === 0) {
|
||||
callback && callback({finished: true});
|
||||
} else {
|
||||
if (animation._isUsingNativeDriver()) {
|
||||
animation._startNativeLoop(iterations);
|
||||
} else {
|
||||
restart(); // Start looping recursively on the js thread
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
stop: function(): void {
|
||||
isFinished = true;
|
||||
animation.stop();
|
||||
},
|
||||
|
||||
reset: function(): void {
|
||||
iterationsSoFar = 0;
|
||||
isFinished = false;
|
||||
animation.reset();
|
||||
},
|
||||
|
||||
_startNativeLoop: function() {
|
||||
throw new Error(
|
||||
'Loops run using the native driver cannot contain Animated.loop animations',
|
||||
);
|
||||
},
|
||||
|
||||
_isUsingNativeDriver: function(): boolean {
|
||||
return animation._isUsingNativeDriver();
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
function forkEvent(
|
||||
event: ?AnimatedEvent | ?Function,
|
||||
listener: Function,
|
||||
): AnimatedEvent | Function {
|
||||
if (!event) {
|
||||
return listener;
|
||||
} else if (event instanceof AnimatedEvent) {
|
||||
event.__addListener(listener);
|
||||
return event;
|
||||
} else {
|
||||
return (...args) => {
|
||||
typeof event === 'function' && event(...args);
|
||||
listener(...args);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function unforkEvent(
|
||||
event: ?AnimatedEvent | ?Function,
|
||||
listener: Function,
|
||||
): void {
|
||||
if (event && event instanceof AnimatedEvent) {
|
||||
event.__removeListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
const event = function(
|
||||
argMapping: $ReadOnlyArray<?Mapping>,
|
||||
config: EventConfig,
|
||||
): any {
|
||||
const animatedEvent = new AnimatedEvent(argMapping, config);
|
||||
if (animatedEvent.__isNative) {
|
||||
return animatedEvent;
|
||||
} else {
|
||||
return animatedEvent.__getHandler();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The `Animated` library is designed to make animations fluid, powerful, and
|
||||
* easy to build and maintain. `Animated` focuses on declarative relationships
|
||||
* between inputs and outputs, with configurable transforms in between, and
|
||||
* simple `start`/`stop` methods to control time-based animation execution.
|
||||
* If additional transforms are added, be sure to include them in
|
||||
* AnimatedMock.js as well.
|
||||
*
|
||||
* See https://reactnative.dev/docs/animated.html
|
||||
*/
|
||||
module.exports = {
|
||||
/**
|
||||
* Standard value class for driving animations. Typically initialized with
|
||||
* `new Animated.Value(0);`
|
||||
*
|
||||
* See https://reactnative.dev/docs/animated.html#value
|
||||
*/
|
||||
Value: AnimatedValue,
|
||||
/**
|
||||
* 2D value class for driving 2D animations, such as pan gestures.
|
||||
*
|
||||
* See https://reactnative.dev/docs/animatedvaluexy.html
|
||||
*/
|
||||
ValueXY: AnimatedValueXY,
|
||||
/**
|
||||
* Exported to use the Interpolation type in flow.
|
||||
*
|
||||
* See https://reactnative.dev/docs/animated.html#interpolation
|
||||
*/
|
||||
Interpolation: AnimatedInterpolation,
|
||||
/**
|
||||
* Exported for ease of type checking. All animated values derive from this
|
||||
* class.
|
||||
*
|
||||
* See https://reactnative.dev/docs/animated.html#node
|
||||
*/
|
||||
Node: AnimatedNode,
|
||||
|
||||
/**
|
||||
* Animates a value from an initial velocity to zero based on a decay
|
||||
* coefficient.
|
||||
*
|
||||
* See https://reactnative.dev/docs/animated.html#decay
|
||||
*/
|
||||
decay,
|
||||
/**
|
||||
* Animates a value along a timed easing curve. The Easing module has tons of
|
||||
* predefined curves, or you can use your own function.
|
||||
*
|
||||
* See https://reactnative.dev/docs/animated.html#timing
|
||||
*/
|
||||
timing,
|
||||
/**
|
||||
* Animates a value according to an analytical spring model based on
|
||||
* damped harmonic oscillation.
|
||||
*
|
||||
* See https://reactnative.dev/docs/animated.html#spring
|
||||
*/
|
||||
spring,
|
||||
|
||||
/**
|
||||
* Creates a new Animated value composed from two Animated values added
|
||||
* together.
|
||||
*
|
||||
* See https://reactnative.dev/docs/animated.html#add
|
||||
*/
|
||||
add,
|
||||
|
||||
/**
|
||||
* Creates a new Animated value composed by subtracting the second Animated
|
||||
* value from the first Animated value.
|
||||
*
|
||||
* See https://reactnative.dev/docs/animated.html#subtract
|
||||
*/
|
||||
subtract,
|
||||
|
||||
/**
|
||||
* Creates a new Animated value composed by dividing the first Animated value
|
||||
* by the second Animated value.
|
||||
*
|
||||
* See https://reactnative.dev/docs/animated.html#divide
|
||||
*/
|
||||
divide,
|
||||
|
||||
/**
|
||||
* Creates a new Animated value composed from two Animated values multiplied
|
||||
* together.
|
||||
*
|
||||
* See https://reactnative.dev/docs/animated.html#multiply
|
||||
*/
|
||||
multiply,
|
||||
|
||||
/**
|
||||
* Creates a new Animated value that is the (non-negative) modulo of the
|
||||
* provided Animated value.
|
||||
*
|
||||
* See https://reactnative.dev/docs/animated.html#modulo
|
||||
*/
|
||||
modulo,
|
||||
|
||||
/**
|
||||
* Create a new Animated value that is limited between 2 values. It uses the
|
||||
* difference between the last value so even if the value is far from the
|
||||
* bounds it will start changing when the value starts getting closer again.
|
||||
*
|
||||
* See https://reactnative.dev/docs/animated.html#diffclamp
|
||||
*/
|
||||
diffClamp,
|
||||
|
||||
/**
|
||||
* Starts an animation after the given delay.
|
||||
*
|
||||
* See https://reactnative.dev/docs/animated.html#delay
|
||||
*/
|
||||
delay,
|
||||
/**
|
||||
* Starts an array of animations in order, waiting for each to complete
|
||||
* before starting the next. If the current running animation is stopped, no
|
||||
* following animations will be started.
|
||||
*
|
||||
* See https://reactnative.dev/docs/animated.html#sequence
|
||||
*/
|
||||
sequence,
|
||||
/**
|
||||
* Starts an array of animations all at the same time. By default, if one
|
||||
* of the animations is stopped, they will all be stopped. You can override
|
||||
* this with the `stopTogether` flag.
|
||||
*
|
||||
* See https://reactnative.dev/docs/animated.html#parallel
|
||||
*/
|
||||
parallel,
|
||||
/**
|
||||
* Array of animations may run in parallel (overlap), but are started in
|
||||
* sequence with successive delays. Nice for doing trailing effects.
|
||||
*
|
||||
* See https://reactnative.dev/docs/animated.html#stagger
|
||||
*/
|
||||
stagger,
|
||||
/**
|
||||
* Loops a given animation continuously, so that each time it reaches the
|
||||
* end, it resets and begins again from the start.
|
||||
*
|
||||
* See https://reactnative.dev/docs/animated.html#loop
|
||||
*/
|
||||
loop,
|
||||
|
||||
/**
|
||||
* Takes an array of mappings and extracts values from each arg accordingly,
|
||||
* then calls `setValue` on the mapped outputs.
|
||||
*
|
||||
* See https://reactnative.dev/docs/animated.html#event
|
||||
*/
|
||||
event,
|
||||
|
||||
/**
|
||||
* Make any React component Animatable. Used to create `Animated.View`, etc.
|
||||
*
|
||||
* See https://reactnative.dev/docs/animated.html#createanimatedcomponent
|
||||
*/
|
||||
createAnimatedComponent,
|
||||
|
||||
/**
|
||||
* Imperative API to attach an animated value to an event on a view. Prefer
|
||||
* using `Animated.event` with `useNativeDrive: true` if possible.
|
||||
*
|
||||
* See https://reactnative.dev/docs/animated.html#attachnativeevent
|
||||
*/
|
||||
attachNativeEvent,
|
||||
|
||||
/**
|
||||
* Advanced imperative API for snooping on animated events that are passed in
|
||||
* through props. Use values directly where possible.
|
||||
*
|
||||
* See https://reactnative.dev/docs/animated.html#forkevent
|
||||
*/
|
||||
forkEvent,
|
||||
unforkEvent,
|
||||
|
||||
/**
|
||||
* Expose Event class, so it can be used as a type for type checkers.
|
||||
*/
|
||||
Event: AnimatedEvent,
|
||||
};
|
||||
@@ -0,0 +1,149 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const {AnimatedEvent, attachNativeEvent} = require('./AnimatedEvent');
|
||||
const AnimatedImplementation = require('./AnimatedImplementation');
|
||||
const AnimatedInterpolation = require('./nodes/AnimatedInterpolation');
|
||||
const AnimatedNode = require('./nodes/AnimatedNode');
|
||||
const AnimatedValue = require('./nodes/AnimatedValue');
|
||||
const AnimatedValueXY = require('./nodes/AnimatedValueXY');
|
||||
|
||||
const createAnimatedComponent = require('./createAnimatedComponent');
|
||||
|
||||
import type {EndCallback} from './animations/Animation';
|
||||
import type {TimingAnimationConfig} from './animations/TimingAnimation';
|
||||
import type {DecayAnimationConfig} from './animations/DecayAnimation';
|
||||
import type {SpringAnimationConfig} from './animations/SpringAnimation';
|
||||
|
||||
/**
|
||||
* Animations are a source of flakiness in snapshot testing. This mock replaces
|
||||
* animation functions from AnimatedImplementation with empty animations for
|
||||
* predictability in tests.
|
||||
*/
|
||||
export type CompositeAnimation = {
|
||||
start: (callback?: ?EndCallback) => void,
|
||||
stop: () => void,
|
||||
reset: () => void,
|
||||
_startNativeLoop: (iterations?: number) => void,
|
||||
_isUsingNativeDriver: () => boolean,
|
||||
...
|
||||
};
|
||||
|
||||
const emptyAnimation = {
|
||||
start: () => {},
|
||||
stop: () => {},
|
||||
reset: () => {},
|
||||
_startNativeLoop: () => {},
|
||||
_isUsingNativeDriver: () => {
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
||||
const spring = function(
|
||||
value: AnimatedValue | AnimatedValueXY,
|
||||
config: SpringAnimationConfig,
|
||||
): CompositeAnimation {
|
||||
const anyValue: any = value;
|
||||
return {
|
||||
...emptyAnimation,
|
||||
start: (callback?: ?EndCallback): void => {
|
||||
anyValue.setValue(config.toValue);
|
||||
callback && callback({finished: true});
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const timing = function(
|
||||
value: AnimatedValue | AnimatedValueXY,
|
||||
config: TimingAnimationConfig,
|
||||
): CompositeAnimation {
|
||||
const anyValue: any = value;
|
||||
return {
|
||||
...emptyAnimation,
|
||||
start: (callback?: ?EndCallback): void => {
|
||||
anyValue.setValue(config.toValue);
|
||||
callback && callback({finished: true});
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const decay = function(
|
||||
value: AnimatedValue | AnimatedValueXY,
|
||||
config: DecayAnimationConfig,
|
||||
): CompositeAnimation {
|
||||
return emptyAnimation;
|
||||
};
|
||||
|
||||
const sequence = function(
|
||||
animations: Array<CompositeAnimation>,
|
||||
): CompositeAnimation {
|
||||
return emptyAnimation;
|
||||
};
|
||||
|
||||
type ParallelConfig = {stopTogether?: boolean, ...};
|
||||
const parallel = function(
|
||||
animations: Array<CompositeAnimation>,
|
||||
config?: ?ParallelConfig,
|
||||
): CompositeAnimation {
|
||||
return emptyAnimation;
|
||||
};
|
||||
|
||||
const delay = function(time: number): CompositeAnimation {
|
||||
return emptyAnimation;
|
||||
};
|
||||
|
||||
const stagger = function(
|
||||
time: number,
|
||||
animations: Array<CompositeAnimation>,
|
||||
): CompositeAnimation {
|
||||
return emptyAnimation;
|
||||
};
|
||||
|
||||
type LoopAnimationConfig = {
|
||||
iterations: number,
|
||||
resetBeforeIteration?: boolean,
|
||||
...
|
||||
};
|
||||
|
||||
const loop = function(
|
||||
animation: CompositeAnimation,
|
||||
{iterations = -1}: LoopAnimationConfig = {},
|
||||
): CompositeAnimation {
|
||||
return emptyAnimation;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
Value: AnimatedValue,
|
||||
ValueXY: AnimatedValueXY,
|
||||
Interpolation: AnimatedInterpolation,
|
||||
Node: AnimatedNode,
|
||||
decay,
|
||||
timing,
|
||||
spring,
|
||||
add: AnimatedImplementation.add,
|
||||
subtract: AnimatedImplementation.subtract,
|
||||
divide: AnimatedImplementation.divide,
|
||||
multiply: AnimatedImplementation.multiply,
|
||||
modulo: AnimatedImplementation.modulo,
|
||||
diffClamp: AnimatedImplementation.diffClamp,
|
||||
delay,
|
||||
sequence,
|
||||
parallel,
|
||||
stagger,
|
||||
loop,
|
||||
event: AnimatedImplementation.event,
|
||||
createAnimatedComponent,
|
||||
attachNativeEvent,
|
||||
forkEvent: AnimatedImplementation.forkEvent,
|
||||
unforkEvent: AnimatedImplementation.unforkEvent,
|
||||
Event: AnimatedEvent,
|
||||
};
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
@@ -10,9 +10,9 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import AnimatedImplementation from './AnimatedImplementation';
|
||||
const AnimatedImplementation = require('./AnimatedImplementation');
|
||||
|
||||
export default {
|
||||
module.exports = {
|
||||
...AnimatedImplementation,
|
||||
/* $FlowFixMe[incompatible-call] createAnimatedComponent expects to receive
|
||||
* types. Plain intrinsic components can't be typed like this */
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
@@ -57,20 +57,20 @@ let ease;
|
||||
* - [`inOut`](docs/easing.html#inout) makes any easing function symmetrical
|
||||
* - [`out`](docs/easing.html#out) runs an easing function backwards
|
||||
*/
|
||||
const Easing = {
|
||||
class Easing {
|
||||
/**
|
||||
* A stepping function, returns 1 for any positive value of `n`.
|
||||
*/
|
||||
step0(n: number): number {
|
||||
static step0(n: number): number {
|
||||
return n > 0 ? 1 : 0;
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* A stepping function, returns 1 if `n` is greater than or equal to 1.
|
||||
*/
|
||||
step1(n: number): number {
|
||||
static step1(n: number): number {
|
||||
return n >= 1 ? 1 : 0;
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* A linear function, `f(t) = t`. Position correlates to elapsed time one to
|
||||
@@ -78,9 +78,9 @@ const Easing = {
|
||||
*
|
||||
* http://cubic-bezier.com/#0,0,1,1
|
||||
*/
|
||||
linear(t: number): number {
|
||||
static linear(t: number): number {
|
||||
return t;
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple inertial interaction, similar to an object slowly accelerating to
|
||||
@@ -88,12 +88,12 @@ const Easing = {
|
||||
*
|
||||
* http://cubic-bezier.com/#.42,0,1,1
|
||||
*/
|
||||
ease(t: number): number {
|
||||
static ease(t: number): number {
|
||||
if (!ease) {
|
||||
ease = Easing.bezier(0.42, 0, 1, 1);
|
||||
}
|
||||
return ease(t);
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* A quadratic function, `f(t) = t * t`. Position equals the square of elapsed
|
||||
@@ -101,9 +101,9 @@ const Easing = {
|
||||
*
|
||||
* http://easings.net/#easeInQuad
|
||||
*/
|
||||
quad(t: number): number {
|
||||
static quad(t: number): number {
|
||||
return t * t;
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* A cubic function, `f(t) = t * t * t`. Position equals the cube of elapsed
|
||||
@@ -111,9 +111,9 @@ const Easing = {
|
||||
*
|
||||
* http://easings.net/#easeInCubic
|
||||
*/
|
||||
cubic(t: number): number {
|
||||
static cubic(t: number): number {
|
||||
return t * t * t;
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* A power function. Position is equal to the Nth power of elapsed time.
|
||||
@@ -121,36 +121,36 @@ const Easing = {
|
||||
* n = 4: http://easings.net/#easeInQuart
|
||||
* n = 5: http://easings.net/#easeInQuint
|
||||
*/
|
||||
poly(n: number): (t: number) => number {
|
||||
static poly(n: number): (t: number) => number {
|
||||
return (t: number) => Math.pow(t, n);
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* A sinusoidal function.
|
||||
*
|
||||
* http://easings.net/#easeInSine
|
||||
*/
|
||||
sin(t: number): number {
|
||||
static sin(t: number): number {
|
||||
return 1 - Math.cos((t * Math.PI) / 2);
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* A circular function.
|
||||
*
|
||||
* http://easings.net/#easeInCirc
|
||||
*/
|
||||
circle(t: number): number {
|
||||
static circle(t: number): number {
|
||||
return 1 - Math.sqrt(1 - t * t);
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* An exponential function.
|
||||
*
|
||||
* http://easings.net/#easeInExpo
|
||||
*/
|
||||
exp(t: number): number {
|
||||
static exp(t: number): number {
|
||||
return Math.pow(2, 10 * (t - 1));
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple elastic interaction, similar to a spring oscillating back and
|
||||
@@ -162,27 +162,29 @@ const Easing = {
|
||||
*
|
||||
* http://easings.net/#easeInElastic
|
||||
*/
|
||||
elastic(bounciness: number = 1): (t: number) => number {
|
||||
static elastic(bounciness: number = 1): (t: number) => number {
|
||||
const p = bounciness * Math.PI;
|
||||
return t => 1 - Math.pow(Math.cos((t * Math.PI) / 2), 3) * Math.cos(t * p);
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Use with `Animated.parallel()` to create a simple effect where the object
|
||||
* animates back slightly as the animation starts.
|
||||
*
|
||||
* https://easings.net/#easeInBack
|
||||
* Wolfram Plot:
|
||||
*
|
||||
* - http://tiny.cc/back_default (s = 1.70158, default)
|
||||
*/
|
||||
back(s: number = 1.70158): (t: number) => number {
|
||||
static back(s: number = 1.70158): (t: number) => number {
|
||||
return t => t * t * ((s + 1) * t - s);
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a simple bouncing effect.
|
||||
*
|
||||
* http://easings.net/#easeInBounce
|
||||
*/
|
||||
bounce(t: number): number {
|
||||
static bounce(t: number): number {
|
||||
if (t < 1 / 2.75) {
|
||||
return 7.5625 * t * t;
|
||||
}
|
||||
@@ -199,7 +201,7 @@ const Easing = {
|
||||
|
||||
const t2 = t - 2.625 / 2.75;
|
||||
return 7.5625 * t2 * t2 + 0.984375;
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a cubic bezier curve, equivalent to CSS Transitions'
|
||||
@@ -208,43 +210,43 @@ const Easing = {
|
||||
* A useful tool to visualize cubic bezier curves can be found at
|
||||
* http://cubic-bezier.com/
|
||||
*/
|
||||
bezier(
|
||||
static bezier(
|
||||
x1: number,
|
||||
y1: number,
|
||||
x2: number,
|
||||
y2: number,
|
||||
): (t: number) => number {
|
||||
const _bezier = require('./bezier').default;
|
||||
const _bezier = require('./bezier');
|
||||
return _bezier(x1, y1, x2, y2);
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs an easing function forwards.
|
||||
*/
|
||||
in(easing: (t: number) => number): (t: number) => number {
|
||||
static in(easing: (t: number) => number): (t: number) => number {
|
||||
return easing;
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs an easing function backwards.
|
||||
*/
|
||||
out(easing: (t: number) => number): (t: number) => number {
|
||||
static out(easing: (t: number) => number): (t: number) => number {
|
||||
return t => 1 - easing(1 - t);
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes any easing function symmetrical. The easing function will run
|
||||
* forwards for half of the duration, then backwards for the rest of the
|
||||
* duration.
|
||||
*/
|
||||
inOut(easing: (t: number) => number): (t: number) => number {
|
||||
static inOut(easing: (t: number) => number): (t: number) => number {
|
||||
return t => {
|
||||
if (t < 0.5) {
|
||||
return easing(t * 2) / 2;
|
||||
}
|
||||
return 1 - easing((1 - t) * 2) / 2;
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default Easing;
|
||||
module.exports = Easing;
|
||||
@@ -0,0 +1,408 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
import NativeAnimatedNonTurboModule from './NativeAnimatedModule';
|
||||
import NativeAnimatedTurboModule from './NativeAnimatedTurboModule';
|
||||
import NativeEventEmitter from '../EventEmitter/NativeEventEmitter';
|
||||
import Platform from '../Utilities/Platform';
|
||||
import type {EventConfig} from './AnimatedEvent';
|
||||
import type {
|
||||
EventMapping,
|
||||
AnimatedNodeConfig,
|
||||
AnimatingNodeConfig,
|
||||
} from './NativeAnimatedModule';
|
||||
import type {AnimationConfig, EndCallback} from './animations/Animation';
|
||||
import type {InterpolationConfigType} from './nodes/AnimatedInterpolation';
|
||||
import invariant from 'invariant';
|
||||
|
||||
// TODO T69437152 @petetheheat - Delete this fork when Fabric ships to 100%.
|
||||
const NativeAnimatedModule =
|
||||
Platform.OS === 'ios' && global.RN$Bridgeless
|
||||
? NativeAnimatedTurboModule
|
||||
: NativeAnimatedNonTurboModule;
|
||||
|
||||
let __nativeAnimatedNodeTagCount = 1; /* used for animated nodes */
|
||||
let __nativeAnimationIdCount = 1; /* used for started animations */
|
||||
|
||||
let nativeEventEmitter;
|
||||
|
||||
let waitingForQueuedOperations = new Set();
|
||||
let queueOperations = false;
|
||||
let queue: Array<() => void> = [];
|
||||
|
||||
/**
|
||||
* Simple wrappers around NativeAnimatedModule to provide flow and autocomplete support for
|
||||
* the native module methods
|
||||
*/
|
||||
const API = {
|
||||
getValue: function(
|
||||
tag: number,
|
||||
saveValueCallback: (value: number) => void,
|
||||
): void {
|
||||
invariant(NativeAnimatedModule, 'Native animated module is not available');
|
||||
API.queueOperation(() => {
|
||||
NativeAnimatedModule.getValue(tag, saveValueCallback);
|
||||
});
|
||||
},
|
||||
setWaitingForIdentifier: function(id: string): void {
|
||||
waitingForQueuedOperations.add(id);
|
||||
queueOperations = true;
|
||||
},
|
||||
unsetWaitingForIdentifier: function(id: string): void {
|
||||
waitingForQueuedOperations.delete(id);
|
||||
|
||||
if (waitingForQueuedOperations.size === 0) {
|
||||
queueOperations = false;
|
||||
API.disableQueue();
|
||||
}
|
||||
},
|
||||
disableQueue: function(): void {
|
||||
invariant(NativeAnimatedModule, 'Native animated module is not available');
|
||||
|
||||
if (Platform.OS === 'android') {
|
||||
NativeAnimatedModule.startOperationBatch();
|
||||
}
|
||||
for (let q = 0, l = queue.length; q < l; q++) {
|
||||
queue[q]();
|
||||
}
|
||||
queue.length = 0;
|
||||
if (Platform.OS === 'android') {
|
||||
NativeAnimatedModule.finishOperationBatch();
|
||||
}
|
||||
},
|
||||
queueOperation: (fn: () => void): void => {
|
||||
if (queueOperations) {
|
||||
queue.push(fn);
|
||||
} else {
|
||||
fn();
|
||||
}
|
||||
},
|
||||
createAnimatedNode: function(tag: number, config: AnimatedNodeConfig): void {
|
||||
invariant(NativeAnimatedModule, 'Native animated module is not available');
|
||||
API.queueOperation(() =>
|
||||
NativeAnimatedModule.createAnimatedNode(tag, config),
|
||||
);
|
||||
},
|
||||
startListeningToAnimatedNodeValue: function(tag: number) {
|
||||
invariant(NativeAnimatedModule, 'Native animated module is not available');
|
||||
API.queueOperation(() =>
|
||||
NativeAnimatedModule.startListeningToAnimatedNodeValue(tag),
|
||||
);
|
||||
},
|
||||
stopListeningToAnimatedNodeValue: function(tag: number) {
|
||||
invariant(NativeAnimatedModule, 'Native animated module is not available');
|
||||
API.queueOperation(() =>
|
||||
NativeAnimatedModule.stopListeningToAnimatedNodeValue(tag),
|
||||
);
|
||||
},
|
||||
connectAnimatedNodes: function(parentTag: number, childTag: number): void {
|
||||
invariant(NativeAnimatedModule, 'Native animated module is not available');
|
||||
API.queueOperation(() =>
|
||||
NativeAnimatedModule.connectAnimatedNodes(parentTag, childTag),
|
||||
);
|
||||
},
|
||||
disconnectAnimatedNodes: function(parentTag: number, childTag: number): void {
|
||||
invariant(NativeAnimatedModule, 'Native animated module is not available');
|
||||
API.queueOperation(() =>
|
||||
NativeAnimatedModule.disconnectAnimatedNodes(parentTag, childTag),
|
||||
);
|
||||
},
|
||||
startAnimatingNode: function(
|
||||
animationId: number,
|
||||
nodeTag: number,
|
||||
config: AnimatingNodeConfig,
|
||||
endCallback: EndCallback,
|
||||
): void {
|
||||
invariant(NativeAnimatedModule, 'Native animated module is not available');
|
||||
API.queueOperation(() =>
|
||||
NativeAnimatedModule.startAnimatingNode(
|
||||
animationId,
|
||||
nodeTag,
|
||||
config,
|
||||
endCallback,
|
||||
),
|
||||
);
|
||||
},
|
||||
stopAnimation: function(animationId: number) {
|
||||
invariant(NativeAnimatedModule, 'Native animated module is not available');
|
||||
API.queueOperation(() => NativeAnimatedModule.stopAnimation(animationId));
|
||||
},
|
||||
setAnimatedNodeValue: function(nodeTag: number, value: number): void {
|
||||
invariant(NativeAnimatedModule, 'Native animated module is not available');
|
||||
API.queueOperation(() =>
|
||||
NativeAnimatedModule.setAnimatedNodeValue(nodeTag, value),
|
||||
);
|
||||
},
|
||||
setAnimatedNodeOffset: function(nodeTag: number, offset: number): void {
|
||||
invariant(NativeAnimatedModule, 'Native animated module is not available');
|
||||
API.queueOperation(() =>
|
||||
NativeAnimatedModule.setAnimatedNodeOffset(nodeTag, offset),
|
||||
);
|
||||
},
|
||||
flattenAnimatedNodeOffset: function(nodeTag: number): void {
|
||||
invariant(NativeAnimatedModule, 'Native animated module is not available');
|
||||
API.queueOperation(() =>
|
||||
NativeAnimatedModule.flattenAnimatedNodeOffset(nodeTag),
|
||||
);
|
||||
},
|
||||
extractAnimatedNodeOffset: function(nodeTag: number): void {
|
||||
invariant(NativeAnimatedModule, 'Native animated module is not available');
|
||||
API.queueOperation(() =>
|
||||
NativeAnimatedModule.extractAnimatedNodeOffset(nodeTag),
|
||||
);
|
||||
},
|
||||
connectAnimatedNodeToView: function(nodeTag: number, viewTag: number): void {
|
||||
invariant(NativeAnimatedModule, 'Native animated module is not available');
|
||||
API.queueOperation(() =>
|
||||
NativeAnimatedModule.connectAnimatedNodeToView(nodeTag, viewTag),
|
||||
);
|
||||
},
|
||||
disconnectAnimatedNodeFromView: function(
|
||||
nodeTag: number,
|
||||
viewTag: number,
|
||||
): void {
|
||||
invariant(NativeAnimatedModule, 'Native animated module is not available');
|
||||
API.queueOperation(() =>
|
||||
NativeAnimatedModule.disconnectAnimatedNodeFromView(nodeTag, viewTag),
|
||||
);
|
||||
},
|
||||
restoreDefaultValues: function(nodeTag: number): void {
|
||||
invariant(NativeAnimatedModule, 'Native animated module is not available');
|
||||
// Backwards compat with older native runtimes, can be removed later.
|
||||
if (NativeAnimatedModule.restoreDefaultValues != null) {
|
||||
API.queueOperation(() =>
|
||||
NativeAnimatedModule.restoreDefaultValues(nodeTag),
|
||||
);
|
||||
}
|
||||
},
|
||||
dropAnimatedNode: function(tag: number): void {
|
||||
invariant(NativeAnimatedModule, 'Native animated module is not available');
|
||||
API.queueOperation(() => NativeAnimatedModule.dropAnimatedNode(tag));
|
||||
},
|
||||
addAnimatedEventToView: function(
|
||||
viewTag: number,
|
||||
eventName: string,
|
||||
eventMapping: EventMapping,
|
||||
) {
|
||||
invariant(NativeAnimatedModule, 'Native animated module is not available');
|
||||
API.queueOperation(() =>
|
||||
NativeAnimatedModule.addAnimatedEventToView(
|
||||
viewTag,
|
||||
eventName,
|
||||
eventMapping,
|
||||
),
|
||||
);
|
||||
},
|
||||
removeAnimatedEventFromView(
|
||||
viewTag: number,
|
||||
eventName: string,
|
||||
animatedNodeTag: number,
|
||||
) {
|
||||
invariant(NativeAnimatedModule, 'Native animated module is not available');
|
||||
API.queueOperation(() =>
|
||||
NativeAnimatedModule.removeAnimatedEventFromView(
|
||||
viewTag,
|
||||
eventName,
|
||||
animatedNodeTag,
|
||||
),
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Styles allowed by the native animated implementation.
|
||||
*
|
||||
* In general native animated implementation should support any numeric property that doesn't need
|
||||
* to be updated through the shadow view hierarchy (all non-layout properties).
|
||||
*/
|
||||
const SUPPORTED_STYLES = {
|
||||
opacity: true,
|
||||
transform: true,
|
||||
borderRadius: true,
|
||||
borderBottomEndRadius: true,
|
||||
borderBottomLeftRadius: true,
|
||||
borderBottomRightRadius: true,
|
||||
borderBottomStartRadius: true,
|
||||
borderTopEndRadius: true,
|
||||
borderTopLeftRadius: true,
|
||||
borderTopRightRadius: true,
|
||||
borderTopStartRadius: true,
|
||||
elevation: true,
|
||||
zIndex: true,
|
||||
/* ios styles */
|
||||
shadowOpacity: true,
|
||||
shadowRadius: true,
|
||||
/* legacy android transform properties */
|
||||
scaleX: true,
|
||||
scaleY: true,
|
||||
translateX: true,
|
||||
translateY: true,
|
||||
};
|
||||
|
||||
const SUPPORTED_TRANSFORMS = {
|
||||
translateX: true,
|
||||
translateY: true,
|
||||
scale: true,
|
||||
scaleX: true,
|
||||
scaleY: true,
|
||||
rotate: true,
|
||||
rotateX: true,
|
||||
rotateY: true,
|
||||
rotateZ: true,
|
||||
perspective: true,
|
||||
};
|
||||
|
||||
const SUPPORTED_INTERPOLATION_PARAMS = {
|
||||
inputRange: true,
|
||||
outputRange: true,
|
||||
extrapolate: true,
|
||||
extrapolateRight: true,
|
||||
extrapolateLeft: true,
|
||||
};
|
||||
|
||||
function addWhitelistedStyleProp(prop: string): void {
|
||||
SUPPORTED_STYLES[prop] = true;
|
||||
}
|
||||
|
||||
function addWhitelistedTransformProp(prop: string): void {
|
||||
SUPPORTED_TRANSFORMS[prop] = true;
|
||||
}
|
||||
|
||||
function addWhitelistedInterpolationParam(param: string): void {
|
||||
SUPPORTED_INTERPOLATION_PARAMS[param] = true;
|
||||
}
|
||||
|
||||
function validateTransform(
|
||||
configs: Array<
|
||||
| {
|
||||
type: 'animated',
|
||||
property: string,
|
||||
nodeTag: ?number,
|
||||
...
|
||||
}
|
||||
| {
|
||||
type: 'static',
|
||||
property: string,
|
||||
value: number | string,
|
||||
...
|
||||
},
|
||||
>,
|
||||
): void {
|
||||
configs.forEach(config => {
|
||||
if (!SUPPORTED_TRANSFORMS.hasOwnProperty(config.property)) {
|
||||
throw new Error(
|
||||
`Property '${config.property}' is not supported by native animated module`,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function validateStyles(styles: {[key: string]: ?number, ...}): void {
|
||||
for (const key in styles) {
|
||||
if (!SUPPORTED_STYLES.hasOwnProperty(key)) {
|
||||
throw new Error(
|
||||
`Style property '${key}' is not supported by native animated module`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function validateInterpolation(config: InterpolationConfigType): void {
|
||||
for (const key in config) {
|
||||
if (!SUPPORTED_INTERPOLATION_PARAMS.hasOwnProperty(key)) {
|
||||
throw new Error(
|
||||
`Interpolation property '${key}' is not supported by native animated module`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function generateNewNodeTag(): number {
|
||||
return __nativeAnimatedNodeTagCount++;
|
||||
}
|
||||
|
||||
function generateNewAnimationId(): number {
|
||||
return __nativeAnimationIdCount++;
|
||||
}
|
||||
|
||||
function assertNativeAnimatedModule(): void {
|
||||
invariant(NativeAnimatedModule, 'Native animated module is not available');
|
||||
}
|
||||
|
||||
let _warnedMissingNativeAnimated = false;
|
||||
|
||||
function shouldUseNativeDriver(
|
||||
config: {...AnimationConfig, ...} | EventConfig,
|
||||
): boolean {
|
||||
if (config.useNativeDriver == null) {
|
||||
console.warn(
|
||||
'Animated: `useNativeDriver` was not specified. This is a required ' +
|
||||
'option and must be explicitly set to `true` or `false`',
|
||||
);
|
||||
}
|
||||
|
||||
if (config.useNativeDriver === true && !NativeAnimatedModule) {
|
||||
if (!_warnedMissingNativeAnimated) {
|
||||
console.warn(
|
||||
'Animated: `useNativeDriver` is not supported because the native ' +
|
||||
'animated module is missing. Falling back to JS-based animation. To ' +
|
||||
'resolve this, add `RCTAnimation` module to this app, or remove ' +
|
||||
'`useNativeDriver`. ' +
|
||||
'Make sure to run `pod install` first. Read more about autolinking: https://github.com/react-native-community/cli/blob/master/docs/autolinking.md',
|
||||
);
|
||||
_warnedMissingNativeAnimated = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return config.useNativeDriver || false;
|
||||
}
|
||||
|
||||
function transformDataType(value: number | string): number | string {
|
||||
// Change the string type to number type so we can reuse the same logic in
|
||||
// iOS and Android platform
|
||||
if (typeof value !== 'string') {
|
||||
return value;
|
||||
}
|
||||
if (/deg$/.test(value)) {
|
||||
const degrees = parseFloat(value) || 0;
|
||||
const radians = (degrees * Math.PI) / 180.0;
|
||||
return radians;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
API,
|
||||
addWhitelistedStyleProp,
|
||||
addWhitelistedTransformProp,
|
||||
addWhitelistedInterpolationParam,
|
||||
validateStyles,
|
||||
validateTransform,
|
||||
validateInterpolation,
|
||||
generateNewNodeTag,
|
||||
generateNewAnimationId,
|
||||
assertNativeAnimatedModule,
|
||||
shouldUseNativeDriver,
|
||||
transformDataType,
|
||||
// $FlowExpectedError[unsafe-getters-setters] - unsafe getter lint suppresion
|
||||
// $FlowExpectedError[missing-type-arg] - unsafe getter lint suppresion
|
||||
get nativeEventEmitter(): NativeEventEmitter {
|
||||
if (!nativeEventEmitter) {
|
||||
nativeEventEmitter = new NativeEventEmitter(
|
||||
// T88715063: NativeEventEmitter only used this parameter on iOS. Now it uses it on all platforms, so this code was modified automatically to preserve its behavior
|
||||
// If you want to use the native module on other platforms, please remove this condition and test its behavior
|
||||
Platform.OS !== 'ios' ? null : NativeAnimatedModule,
|
||||
);
|
||||
}
|
||||
return nativeEventEmitter;
|
||||
},
|
||||
};
|
||||
+1
-6
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
@@ -9,7 +9,6 @@
|
||||
*/
|
||||
|
||||
import type {TurboModule} from '../TurboModule/RCTExport';
|
||||
|
||||
import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry';
|
||||
|
||||
type EndResult = {finished: boolean, ...};
|
||||
@@ -30,7 +29,6 @@ export interface Spec extends TurboModule {
|
||||
+startOperationBatch: () => void;
|
||||
+finishOperationBatch: () => void;
|
||||
+createAnimatedNode: (tag: number, config: AnimatedNodeConfig) => void;
|
||||
+updateAnimatedNodeConfig?: (tag: number, config: AnimatedNodeConfig) => void;
|
||||
+getValue: (tag: number, saveValueCallback: SaveValueCallback) => void;
|
||||
+startListeningToAnimatedNodeValue: (tag: number) => void;
|
||||
+stopListeningToAnimatedNodeValue: (tag: number) => void;
|
||||
@@ -65,9 +63,6 @@ export interface Spec extends TurboModule {
|
||||
// Events
|
||||
+addListener: (eventName: string) => void;
|
||||
+removeListeners: (count: number) => void;
|
||||
|
||||
// All of the above in a batched mode
|
||||
+queueAndExecuteBatchedOperations?: (operationsAndArgs: Array<any>) => void;
|
||||
}
|
||||
|
||||
export default (TurboModuleRegistry.get<Spec>('NativeAnimatedModule'): ?Spec);
|
||||
+1
-6
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
@@ -9,7 +9,6 @@
|
||||
*/
|
||||
|
||||
import type {TurboModule} from '../TurboModule/RCTExport';
|
||||
|
||||
import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry';
|
||||
|
||||
type EndResult = {finished: boolean, ...};
|
||||
@@ -30,7 +29,6 @@ export interface Spec extends TurboModule {
|
||||
+startOperationBatch: () => void;
|
||||
+finishOperationBatch: () => void;
|
||||
+createAnimatedNode: (tag: number, config: AnimatedNodeConfig) => void;
|
||||
+updateAnimatedNodeConfig?: (tag: number, config: AnimatedNodeConfig) => void;
|
||||
+getValue: (tag: number, saveValueCallback: SaveValueCallback) => void;
|
||||
+startListeningToAnimatedNodeValue: (tag: number) => void;
|
||||
+stopListeningToAnimatedNodeValue: (tag: number) => void;
|
||||
@@ -65,9 +63,6 @@ export interface Spec extends TurboModule {
|
||||
// Events
|
||||
+addListener: (eventName: string) => void;
|
||||
+removeListeners: (count: number) => void;
|
||||
|
||||
// All of the above in a batched mode
|
||||
+queueAndExecuteBatchedOperations?: (operationsAndArgs: Array<any>) => void;
|
||||
}
|
||||
|
||||
export default (TurboModuleRegistry.get<Spec>(
|
||||
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
type SpringConfigType = {
|
||||
stiffness: number,
|
||||
damping: number,
|
||||
...
|
||||
};
|
||||
|
||||
function stiffnessFromOrigamiValue(oValue) {
|
||||
return (oValue - 30) * 3.62 + 194;
|
||||
}
|
||||
|
||||
function dampingFromOrigamiValue(oValue) {
|
||||
return (oValue - 8) * 3 + 25;
|
||||
}
|
||||
|
||||
function fromOrigamiTensionAndFriction(
|
||||
tension: number,
|
||||
friction: number,
|
||||
): SpringConfigType {
|
||||
return {
|
||||
stiffness: stiffnessFromOrigamiValue(tension),
|
||||
damping: dampingFromOrigamiValue(friction),
|
||||
};
|
||||
}
|
||||
|
||||
function fromBouncinessAndSpeed(
|
||||
bounciness: number,
|
||||
speed: number,
|
||||
): SpringConfigType {
|
||||
function normalize(value, startValue, endValue) {
|
||||
return (value - startValue) / (endValue - startValue);
|
||||
}
|
||||
|
||||
function projectNormal(n, start, end) {
|
||||
return start + n * (end - start);
|
||||
}
|
||||
|
||||
function linearInterpolation(t, start, end) {
|
||||
return t * end + (1 - t) * start;
|
||||
}
|
||||
|
||||
function quadraticOutInterpolation(t, start, end) {
|
||||
return linearInterpolation(2 * t - t * t, start, end);
|
||||
}
|
||||
|
||||
function b3Friction1(x) {
|
||||
return 0.0007 * Math.pow(x, 3) - 0.031 * Math.pow(x, 2) + 0.64 * x + 1.28;
|
||||
}
|
||||
|
||||
function b3Friction2(x) {
|
||||
return 0.000044 * Math.pow(x, 3) - 0.006 * Math.pow(x, 2) + 0.36 * x + 2;
|
||||
}
|
||||
|
||||
function b3Friction3(x) {
|
||||
return (
|
||||
0.00000045 * Math.pow(x, 3) -
|
||||
0.000332 * Math.pow(x, 2) +
|
||||
0.1078 * x +
|
||||
5.84
|
||||
);
|
||||
}
|
||||
|
||||
function b3Nobounce(tension) {
|
||||
if (tension <= 18) {
|
||||
return b3Friction1(tension);
|
||||
} else if (tension > 18 && tension <= 44) {
|
||||
return b3Friction2(tension);
|
||||
} else {
|
||||
return b3Friction3(tension);
|
||||
}
|
||||
}
|
||||
|
||||
let b = normalize(bounciness / 1.7, 0, 20);
|
||||
b = projectNormal(b, 0, 0.8);
|
||||
const s = normalize(speed / 1.7, 0, 20);
|
||||
const bouncyTension = projectNormal(s, 0.5, 200);
|
||||
const bouncyFriction = quadraticOutInterpolation(
|
||||
b,
|
||||
b3Nobounce(bouncyTension),
|
||||
0.01,
|
||||
);
|
||||
|
||||
return {
|
||||
stiffness: stiffnessFromOrigamiValue(bouncyTension),
|
||||
damping: dampingFromOrigamiValue(bouncyFriction),
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
fromOrigamiTensionAndFriction,
|
||||
fromBouncinessAndSpeed,
|
||||
};
|
||||
+33
-190
@@ -1,20 +1,17 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* @oncall react_native
|
||||
* @emails oncall+react_native
|
||||
*/
|
||||
|
||||
import AnimatedProps from '../nodes/AnimatedProps';
|
||||
import TestRenderer from 'react-test-renderer';
|
||||
import * as React from 'react';
|
||||
|
||||
const {PlatformColor} = require('../../StyleSheet/PlatformColorValueTypes');
|
||||
let Animated = require('../Animated').default;
|
||||
const AnimatedProps = require('../nodes/AnimatedProps').default;
|
||||
const TestRenderer = require('react-test-renderer');
|
||||
|
||||
jest.mock('../../BatchedBridge/NativeModules', () => ({
|
||||
NativeAnimatedModule: {},
|
||||
PlatformConstants: {
|
||||
@@ -24,6 +21,8 @@ jest.mock('../../BatchedBridge/NativeModules', () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
let Animated = require('../Animated');
|
||||
|
||||
describe('Animated tests', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
@@ -32,10 +31,6 @@ describe('Animated tests', () => {
|
||||
describe('Animated', () => {
|
||||
it('works end to end', () => {
|
||||
const anim = new Animated.Value(0);
|
||||
const translateAnim = anim.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [100, 200],
|
||||
});
|
||||
|
||||
const callback = jest.fn();
|
||||
|
||||
@@ -46,10 +41,10 @@ describe('Animated tests', () => {
|
||||
opacity: anim,
|
||||
transform: [
|
||||
{
|
||||
translate: [translateAnim, translateAnim],
|
||||
},
|
||||
{
|
||||
translateX: translateAnim,
|
||||
translateX: anim.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [100, 200],
|
||||
}),
|
||||
},
|
||||
{scale: anim},
|
||||
],
|
||||
@@ -66,7 +61,7 @@ describe('Animated tests', () => {
|
||||
style: {
|
||||
backgroundColor: 'red',
|
||||
opacity: 0,
|
||||
transform: [{translate: [100, 100]}, {translateX: 100}, {scale: 0}],
|
||||
transform: [{translateX: 100}, {scale: 0}],
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: 0,
|
||||
@@ -88,7 +83,7 @@ describe('Animated tests', () => {
|
||||
style: {
|
||||
backgroundColor: 'red',
|
||||
opacity: 0.5,
|
||||
transform: [{translate: [150, 150]}, {translateX: 150}, {scale: 0.5}],
|
||||
transform: [{translateX: 150}, {scale: 0.5}],
|
||||
shadowOffset: {
|
||||
width: 0.5,
|
||||
height: 0.5,
|
||||
@@ -145,6 +140,21 @@ describe('Animated tests', () => {
|
||||
expect(callback).toBeCalled();
|
||||
});
|
||||
|
||||
// This test is flaky and we are asking open source to fix it
|
||||
// https://github.com/facebook/react-native/issues/21517
|
||||
it.skip('send toValue when an underdamped spring stops', () => {
|
||||
const anim = new Animated.Value(0);
|
||||
const listener = jest.fn();
|
||||
anim.addListener(listener);
|
||||
Animated.spring(anim, {toValue: 15, useNativeDriver: false}).start();
|
||||
jest.runAllTimers();
|
||||
const lastValue =
|
||||
listener.mock.calls[listener.mock.calls.length - 2][0].value;
|
||||
expect(lastValue).not.toBe(15);
|
||||
expect(lastValue).toBeCloseTo(15);
|
||||
expect(anim.__getValue()).toBe(15);
|
||||
});
|
||||
|
||||
it('send toValue when a critically damped spring stops', () => {
|
||||
const anim = new Animated.Value(0);
|
||||
const listener = jest.fn();
|
||||
@@ -176,13 +186,11 @@ describe('Animated tests', () => {
|
||||
|
||||
expect(testRenderer.toJSON().props.style.opacity).toEqual(0);
|
||||
|
||||
TestRenderer.act(() => {
|
||||
Animated.timing(opacity, {
|
||||
toValue: 1,
|
||||
duration: 0,
|
||||
useNativeDriver: false,
|
||||
}).start();
|
||||
});
|
||||
Animated.timing(opacity, {
|
||||
toValue: 1,
|
||||
duration: 0,
|
||||
useNativeDriver: false,
|
||||
}).start();
|
||||
|
||||
expect(testRenderer.toJSON().props.style.opacity).toEqual(1);
|
||||
});
|
||||
@@ -630,16 +638,6 @@ describe('Animated tests', () => {
|
||||
handler({bar: 'ignoreBar'}, {state: {baz: 'ignoreBaz', foo: 42}});
|
||||
expect(value.__getValue()).toBe(42);
|
||||
});
|
||||
|
||||
it('should validate AnimatedValueXY mappings', () => {
|
||||
const value = new Animated.ValueXY({x: 0, y: 0});
|
||||
const handler = Animated.event([{state: value}], {
|
||||
useNativeDriver: false,
|
||||
});
|
||||
handler({state: {x: 1, y: 2}});
|
||||
expect(value.__getValue()).toMatchObject({x: 1, y: 2});
|
||||
});
|
||||
|
||||
it('should call listeners', () => {
|
||||
const value = new Animated.Value(0);
|
||||
const listener = jest.fn();
|
||||
@@ -652,7 +650,6 @@ describe('Animated tests', () => {
|
||||
expect(listener.mock.calls.length).toBe(1);
|
||||
expect(listener).toBeCalledWith({foo: 42});
|
||||
});
|
||||
|
||||
it('should call forked event listeners, with Animated.event() listener', () => {
|
||||
const value = new Animated.Value(0);
|
||||
const listener = jest.fn();
|
||||
@@ -669,7 +666,6 @@ describe('Animated tests', () => {
|
||||
expect(listener2.mock.calls.length).toBe(1);
|
||||
expect(listener2).toBeCalledWith({foo: 42});
|
||||
});
|
||||
|
||||
it('should call forked event listeners, with js listener', () => {
|
||||
const listener = jest.fn();
|
||||
const listener2 = jest.fn();
|
||||
@@ -680,7 +676,6 @@ describe('Animated tests', () => {
|
||||
expect(listener2.mock.calls.length).toBe(1);
|
||||
expect(listener2).toBeCalledWith({foo: 42});
|
||||
});
|
||||
|
||||
it('should call forked event listeners, with undefined listener', () => {
|
||||
const listener = undefined;
|
||||
const listener2 = jest.fn();
|
||||
@@ -699,7 +694,7 @@ describe('Animated tests', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
jest.mock('../../Interaction/InteractionManager');
|
||||
Animated = require('../Animated').default;
|
||||
Animated = require('../Animated');
|
||||
InteractionManager = require('../../Interaction/InteractionManager');
|
||||
});
|
||||
|
||||
@@ -789,19 +784,6 @@ describe('Animated tests', () => {
|
||||
value1.setValue(1492);
|
||||
expect(value2.__getValue()).toBe(7);
|
||||
});
|
||||
|
||||
it('should start tracking immediately on animation start', () => {
|
||||
const value1 = new Animated.Value(42);
|
||||
const value2 = new Animated.Value(0);
|
||||
Animated.timing(value2, {
|
||||
toValue: value1,
|
||||
duration: 0,
|
||||
useNativeDriver: false,
|
||||
}).start();
|
||||
expect(value2.__getValue()).toBe(42);
|
||||
value1.setValue(7);
|
||||
expect(value2.__getValue()).toBe(7);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Animated Vectors', () => {
|
||||
@@ -979,143 +961,4 @@ describe('Animated tests', () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('Animated Colors', () => {
|
||||
it('should normalize colors', () => {
|
||||
let color = new Animated.Color();
|
||||
expect(color.__getValue()).toEqual('rgba(0, 0, 0, 1)');
|
||||
|
||||
color = new Animated.Color({r: 11, g: 22, b: 33, a: 1.0});
|
||||
expect(color.__getValue()).toEqual('rgba(11, 22, 33, 1)');
|
||||
|
||||
color = new Animated.Color('rgba(255, 0, 0, 1.0)');
|
||||
expect(color.__getValue()).toEqual('rgba(255, 0, 0, 1)');
|
||||
|
||||
color = new Animated.Color('#ff0000ff');
|
||||
expect(color.__getValue()).toEqual('rgba(255, 0, 0, 1)');
|
||||
|
||||
color = new Animated.Color('red');
|
||||
expect(color.__getValue()).toEqual('rgba(255, 0, 0, 1)');
|
||||
|
||||
color = new Animated.Color({
|
||||
r: new Animated.Value(255),
|
||||
g: new Animated.Value(0),
|
||||
b: new Animated.Value(0),
|
||||
a: new Animated.Value(1.0),
|
||||
});
|
||||
expect(color.__getValue()).toEqual('rgba(255, 0, 0, 1)');
|
||||
|
||||
color = new Animated.Color('unknown');
|
||||
expect(color.__getValue()).toEqual('rgba(0, 0, 0, 1)');
|
||||
|
||||
color = new Animated.Color({key: 'value'});
|
||||
expect(color.__getValue()).toEqual('rgba(0, 0, 0, 1)');
|
||||
});
|
||||
|
||||
it('should animate colors', () => {
|
||||
const color = new Animated.Color({r: 255, g: 0, b: 0, a: 1.0});
|
||||
const callback = jest.fn();
|
||||
const node = new AnimatedProps(
|
||||
{
|
||||
style: {
|
||||
backgroundColor: color,
|
||||
transform: [
|
||||
{
|
||||
scale: color.a.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [1, 2],
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
callback,
|
||||
);
|
||||
|
||||
expect(node.__getValue()).toEqual({
|
||||
style: {
|
||||
backgroundColor: 'rgba(255, 0, 0, 1)',
|
||||
transform: [{scale: 2}],
|
||||
},
|
||||
});
|
||||
|
||||
node.__attach();
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
|
||||
color.setValue({r: 11, g: 22, b: 33, a: 0.5});
|
||||
expect(callback).toHaveBeenCalledTimes(5);
|
||||
expect(node.__getValue()).toEqual({
|
||||
style: {
|
||||
backgroundColor: 'rgba(11, 22, 33, 0.5)',
|
||||
transform: [{scale: 1.5}],
|
||||
},
|
||||
});
|
||||
|
||||
node.__detach();
|
||||
color.setValue({r: 255, g: 0, b: 0, a: 1.0});
|
||||
expect(callback).toHaveBeenCalledTimes(5);
|
||||
});
|
||||
|
||||
it('should track colors', () => {
|
||||
const color1 = new Animated.Color();
|
||||
const color2 = new Animated.Color();
|
||||
Animated.timing(color2, {
|
||||
toValue: color1,
|
||||
duration: 0,
|
||||
useNativeDriver: false,
|
||||
}).start();
|
||||
color1.setValue({r: 11, g: 22, b: 33, a: 0.5});
|
||||
expect(color2.__getValue()).toEqual('rgba(11, 22, 33, 0.5)');
|
||||
|
||||
// Make sure tracking keeps working (see stopTogether in ParallelConfig used
|
||||
// by maybeVectorAnim).
|
||||
color1.setValue({r: 255, g: 0, b: 0, a: 1.0});
|
||||
expect(color2.__getValue()).toEqual('rgba(255, 0, 0, 1)');
|
||||
});
|
||||
|
||||
it('should track with springs', () => {
|
||||
const color1 = new Animated.Color();
|
||||
const color2 = new Animated.Color();
|
||||
Animated.spring(color2, {
|
||||
toValue: color1,
|
||||
tension: 3000, // faster spring for faster test
|
||||
friction: 60,
|
||||
useNativeDriver: false,
|
||||
}).start();
|
||||
color1.setValue({r: 11, g: 22, b: 33, a: 0.5});
|
||||
jest.runAllTimers();
|
||||
expect(color2.__getValue()).toEqual('rgba(11, 22, 33, 0.5)');
|
||||
color1.setValue({r: 44, g: 55, b: 66, a: 0.0});
|
||||
jest.runAllTimers();
|
||||
expect(color2.__getValue()).toEqual('rgba(44, 55, 66, 0)');
|
||||
});
|
||||
|
||||
it('should provide updates for native colors', () => {
|
||||
const color = new Animated.Color('red');
|
||||
|
||||
const listener = jest.fn();
|
||||
color.addListener(listener);
|
||||
|
||||
const callback = jest.fn(() => {
|
||||
console.log('callback', color.__getValue());
|
||||
});
|
||||
const node = new AnimatedProps({style: {color}}, callback);
|
||||
node.__attach();
|
||||
|
||||
color.setValue('blue');
|
||||
expect(callback).toHaveBeenCalledTimes(5);
|
||||
expect(listener).toHaveBeenCalledTimes(1);
|
||||
expect(listener).toHaveBeenCalledWith({value: 'rgba(0, 0, 255, 1)'});
|
||||
expect(color.__getValue()).toEqual('rgba(0, 0, 255, 1)');
|
||||
|
||||
callback.mockClear();
|
||||
listener.mockClear();
|
||||
|
||||
color.setValue(PlatformColor('bar'));
|
||||
expect(callback).toHaveBeenCalledTimes(1);
|
||||
expect(listener).toHaveBeenCalledTimes(1);
|
||||
expect(listener).toHaveBeenCalledWith({value: PlatformColor('bar')});
|
||||
expect(color.__getValue()).toEqual(PlatformColor('bar'));
|
||||
});
|
||||
});
|
||||
});
|
||||
+4
-4
@@ -1,17 +1,17 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* @oncall react_native
|
||||
* @emails oncall+react_native
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import AnimatedImplementation from '../AnimatedImplementation';
|
||||
import AnimatedMock from '../AnimatedMock';
|
||||
const AnimatedMock = require('../AnimatedMock');
|
||||
const AnimatedImplementation = require('../AnimatedImplementation');
|
||||
|
||||
describe('Animated Mock', () => {
|
||||
it('matches implementation keys', () => {
|
||||
@@ -0,0 +1,921 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* @emails oncall+react_native
|
||||
*/
|
||||
|
||||
jest
|
||||
.clearAllMocks()
|
||||
.mock('../../BatchedBridge/NativeModules', () => ({
|
||||
NativeAnimatedModule: {},
|
||||
PlatformConstants: {
|
||||
getConstants() {
|
||||
return {};
|
||||
},
|
||||
},
|
||||
}))
|
||||
.mock('../NativeAnimatedModule')
|
||||
.mock('../../EventEmitter/NativeEventEmitter')
|
||||
// findNodeHandle is imported from ReactNative so mock that whole module.
|
||||
.setMock('../../Renderer/shims/ReactNative', {findNodeHandle: () => 1});
|
||||
|
||||
import TestRenderer from 'react-test-renderer';
|
||||
import * as React from 'react';
|
||||
|
||||
const Animated = require('../Animated');
|
||||
const NativeAnimatedHelper = require('../NativeAnimatedHelper');
|
||||
|
||||
describe('Native Animated', () => {
|
||||
const NativeAnimatedModule = require('../NativeAnimatedModule').default;
|
||||
|
||||
beforeEach(() => {
|
||||
Object.assign(NativeAnimatedModule, {
|
||||
getValue: jest.fn(),
|
||||
addAnimatedEventToView: jest.fn(),
|
||||
connectAnimatedNodes: jest.fn(),
|
||||
connectAnimatedNodeToView: jest.fn(),
|
||||
createAnimatedNode: jest.fn(),
|
||||
disconnectAnimatedNodeFromView: jest.fn(),
|
||||
disconnectAnimatedNodes: jest.fn(),
|
||||
dropAnimatedNode: jest.fn(),
|
||||
extractAnimatedNodeOffset: jest.fn(),
|
||||
flattenAnimatedNodeOffset: jest.fn(),
|
||||
removeAnimatedEventFromView: jest.fn(),
|
||||
restoreDefaultValues: jest.fn(),
|
||||
setAnimatedNodeOffset: jest.fn(),
|
||||
setAnimatedNodeValue: jest.fn(),
|
||||
startAnimatingNode: jest.fn(),
|
||||
startListeningToAnimatedNodeValue: jest.fn(),
|
||||
stopAnimation: jest.fn(),
|
||||
stopListeningToAnimatedNodeValue: jest.fn(),
|
||||
});
|
||||
});
|
||||
|
||||
describe('Animated Value', () => {
|
||||
it('proxies `setValue` correctly', () => {
|
||||
const opacity = new Animated.Value(0);
|
||||
const ref = React.createRef(null);
|
||||
|
||||
Animated.timing(opacity, {
|
||||
toValue: 10,
|
||||
duration: 1000,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
|
||||
TestRenderer.create(<Animated.View ref={ref} style={{opacity}} />);
|
||||
|
||||
expect(ref.current).not.toBeNull();
|
||||
jest.spyOn(ref.current, 'setNativeProps');
|
||||
|
||||
opacity.setValue(0.5);
|
||||
|
||||
expect(NativeAnimatedModule.setAnimatedNodeValue).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
0.5,
|
||||
);
|
||||
expect(ref.current.setNativeProps).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should set offset', () => {
|
||||
const opacity = new Animated.Value(0);
|
||||
opacity.setOffset(10);
|
||||
opacity.__makeNative();
|
||||
|
||||
TestRenderer.create(<Animated.View style={{opacity}} />);
|
||||
|
||||
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
{type: 'value', value: 0, offset: 10},
|
||||
);
|
||||
opacity.setOffset(20);
|
||||
expect(NativeAnimatedModule.setAnimatedNodeOffset).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
20,
|
||||
);
|
||||
});
|
||||
|
||||
it('should flatten offset', () => {
|
||||
const opacity = new Animated.Value(0);
|
||||
opacity.__makeNative();
|
||||
|
||||
TestRenderer.create(<Animated.View style={{opacity}} />);
|
||||
|
||||
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
{type: 'value', value: 0, offset: 0},
|
||||
);
|
||||
opacity.flattenOffset();
|
||||
expect(NativeAnimatedModule.flattenAnimatedNodeOffset).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
it('should save value on unmount', () => {
|
||||
NativeAnimatedModule.getValue = jest.fn((tag, saveCallback) => {
|
||||
saveCallback(1);
|
||||
});
|
||||
const opacity = new Animated.Value(0);
|
||||
|
||||
opacity.__makeNative();
|
||||
|
||||
const root = TestRenderer.create(<Animated.View style={{opacity}} />);
|
||||
const tag = opacity.__getNativeTag();
|
||||
|
||||
root.unmount();
|
||||
|
||||
expect(NativeAnimatedModule.getValue).toBeCalledWith(
|
||||
tag,
|
||||
expect.any(Function),
|
||||
);
|
||||
expect(opacity.__getValue()).toBe(1);
|
||||
});
|
||||
|
||||
it('should extract offset', () => {
|
||||
const opacity = new Animated.Value(0);
|
||||
opacity.__makeNative();
|
||||
|
||||
TestRenderer.create(<Animated.View style={{opacity}} />);
|
||||
|
||||
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
{type: 'value', value: 0, offset: 0},
|
||||
);
|
||||
opacity.extractOffset();
|
||||
expect(NativeAnimatedModule.extractAnimatedNodeOffset).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Animated Listeners', () => {
|
||||
it('should get updates', () => {
|
||||
const value1 = new Animated.Value(0);
|
||||
value1.__makeNative();
|
||||
const listener = jest.fn();
|
||||
const id = value1.addListener(listener);
|
||||
expect(
|
||||
NativeAnimatedModule.startListeningToAnimatedNodeValue,
|
||||
).toHaveBeenCalledWith(value1.__getNativeTag());
|
||||
|
||||
NativeAnimatedHelper.nativeEventEmitter.emit('onAnimatedValueUpdate', {
|
||||
value: 42,
|
||||
tag: value1.__getNativeTag(),
|
||||
});
|
||||
expect(listener).toHaveBeenCalledTimes(1);
|
||||
expect(listener).toBeCalledWith({value: 42});
|
||||
expect(value1.__getValue()).toBe(42);
|
||||
|
||||
NativeAnimatedHelper.nativeEventEmitter.emit('onAnimatedValueUpdate', {
|
||||
value: 7,
|
||||
tag: value1.__getNativeTag(),
|
||||
});
|
||||
expect(listener).toHaveBeenCalledTimes(2);
|
||||
expect(listener).toBeCalledWith({value: 7});
|
||||
expect(value1.__getValue()).toBe(7);
|
||||
|
||||
value1.removeListener(id);
|
||||
expect(
|
||||
NativeAnimatedModule.stopListeningToAnimatedNodeValue,
|
||||
).toHaveBeenCalledWith(value1.__getNativeTag());
|
||||
|
||||
NativeAnimatedHelper.nativeEventEmitter.emit('onAnimatedValueUpdate', {
|
||||
value: 1492,
|
||||
tag: value1.__getNativeTag(),
|
||||
});
|
||||
expect(listener).toHaveBeenCalledTimes(2);
|
||||
expect(value1.__getValue()).toBe(7);
|
||||
});
|
||||
|
||||
it('should removeAll', () => {
|
||||
const value1 = new Animated.Value(0);
|
||||
value1.__makeNative();
|
||||
const listener = jest.fn();
|
||||
[1, 2, 3, 4].forEach(() => value1.addListener(listener));
|
||||
expect(
|
||||
NativeAnimatedModule.startListeningToAnimatedNodeValue,
|
||||
).toHaveBeenCalledWith(value1.__getNativeTag());
|
||||
|
||||
NativeAnimatedHelper.nativeEventEmitter.emit('onAnimatedValueUpdate', {
|
||||
value: 42,
|
||||
tag: value1.__getNativeTag(),
|
||||
});
|
||||
expect(listener).toHaveBeenCalledTimes(4);
|
||||
expect(listener).toBeCalledWith({value: 42});
|
||||
|
||||
value1.removeAllListeners();
|
||||
expect(
|
||||
NativeAnimatedModule.stopListeningToAnimatedNodeValue,
|
||||
).toHaveBeenCalledWith(value1.__getNativeTag());
|
||||
|
||||
NativeAnimatedHelper.nativeEventEmitter.emit('onAnimatedValueUpdate', {
|
||||
value: 7,
|
||||
tag: value1.__getNativeTag(),
|
||||
});
|
||||
expect(listener).toHaveBeenCalledTimes(4);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Animated Events', () => {
|
||||
it('should map events', () => {
|
||||
const value = new Animated.Value(0);
|
||||
value.__makeNative();
|
||||
const event = Animated.event([{nativeEvent: {state: {foo: value}}}], {
|
||||
useNativeDriver: true,
|
||||
});
|
||||
|
||||
const root = TestRenderer.create(<Animated.View onTouchMove={event} />);
|
||||
expect(NativeAnimatedModule.addAnimatedEventToView).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
'onTouchMove',
|
||||
{
|
||||
nativeEventPath: ['state', 'foo'],
|
||||
animatedValueTag: value.__getNativeTag(),
|
||||
},
|
||||
);
|
||||
|
||||
expect(
|
||||
NativeAnimatedModule.removeAnimatedEventFromView,
|
||||
).not.toHaveBeenCalled();
|
||||
root.unmount();
|
||||
expect(NativeAnimatedModule.removeAnimatedEventFromView).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
'onTouchMove',
|
||||
value.__getNativeTag(),
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw on invalid event path', () => {
|
||||
const value = new Animated.Value(0);
|
||||
value.__makeNative();
|
||||
const event = Animated.event([{notNativeEvent: {foo: value}}], {
|
||||
useNativeDriver: true,
|
||||
});
|
||||
|
||||
jest.spyOn(console, 'error').mockImplementationOnce((...args) => {
|
||||
if (args[0].startsWith('The above error occurred in the')) {
|
||||
return;
|
||||
}
|
||||
console.errorDebug(...args);
|
||||
});
|
||||
|
||||
expect(() => {
|
||||
TestRenderer.create(<Animated.View onTouchMove={event} />);
|
||||
}).toThrowError(/nativeEvent/);
|
||||
expect(NativeAnimatedModule.addAnimatedEventToView).not.toBeCalled();
|
||||
|
||||
console.error.mockRestore();
|
||||
});
|
||||
|
||||
it('should call listeners', () => {
|
||||
const value = new Animated.Value(0);
|
||||
value.__makeNative();
|
||||
const listener = jest.fn();
|
||||
const event = Animated.event([{nativeEvent: {foo: value}}], {
|
||||
useNativeDriver: true,
|
||||
listener,
|
||||
});
|
||||
const handler = event.__getHandler();
|
||||
handler({nativeEvent: {foo: 42}});
|
||||
expect(listener).toHaveBeenCalledTimes(1);
|
||||
expect(listener).toBeCalledWith({nativeEvent: {foo: 42}});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Animated Graph', () => {
|
||||
it('creates and detaches nodes', () => {
|
||||
const opacity = new Animated.Value(0);
|
||||
const root = TestRenderer.create(<Animated.View style={{opacity}} />);
|
||||
|
||||
Animated.timing(opacity, {
|
||||
toValue: 10,
|
||||
duration: 1000,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
|
||||
expect(NativeAnimatedModule.createAnimatedNode).toHaveBeenCalledTimes(3);
|
||||
expect(NativeAnimatedModule.connectAnimatedNodes).toHaveBeenCalledTimes(
|
||||
2,
|
||||
);
|
||||
|
||||
expect(NativeAnimatedModule.startAnimatingNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
expect.any(Number),
|
||||
{
|
||||
type: 'frames',
|
||||
frames: expect.any(Array),
|
||||
toValue: expect.any(Number),
|
||||
iterations: 1,
|
||||
},
|
||||
expect.any(Function),
|
||||
);
|
||||
|
||||
expect(
|
||||
NativeAnimatedModule.disconnectAnimatedNodes,
|
||||
).not.toHaveBeenCalled();
|
||||
expect(NativeAnimatedModule.dropAnimatedNode).not.toHaveBeenCalled();
|
||||
|
||||
root.unmount();
|
||||
|
||||
expect(
|
||||
NativeAnimatedModule.disconnectAnimatedNodes,
|
||||
).toHaveBeenCalledTimes(2);
|
||||
expect(NativeAnimatedModule.dropAnimatedNode).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
it('sends a valid description for value, style and props nodes', () => {
|
||||
const opacity = new Animated.Value(0);
|
||||
TestRenderer.create(<Animated.View style={{opacity}} />);
|
||||
|
||||
Animated.timing(opacity, {
|
||||
toValue: 10,
|
||||
duration: 1000,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
|
||||
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
{type: 'value', value: 0, offset: 0},
|
||||
);
|
||||
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
{type: 'style', style: {opacity: expect.any(Number)}},
|
||||
);
|
||||
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
{type: 'props', props: {style: expect.any(Number)}},
|
||||
);
|
||||
});
|
||||
|
||||
it('sends a valid graph description for Animated.add nodes', () => {
|
||||
const first = new Animated.Value(1);
|
||||
const second = new Animated.Value(2);
|
||||
first.__makeNative();
|
||||
second.__makeNative();
|
||||
|
||||
TestRenderer.create(
|
||||
<Animated.View style={{opacity: Animated.add(first, second)}} />,
|
||||
);
|
||||
|
||||
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
{type: 'addition', input: expect.any(Array)},
|
||||
);
|
||||
const additionCalls = NativeAnimatedModule.createAnimatedNode.mock.calls.filter(
|
||||
call => call[1].type === 'addition',
|
||||
);
|
||||
expect(additionCalls.length).toBe(1);
|
||||
const additionCall = additionCalls[0];
|
||||
const additionNodeTag = additionCall[0];
|
||||
const additionConnectionCalls = NativeAnimatedModule.connectAnimatedNodes.mock.calls.filter(
|
||||
call => call[1] === additionNodeTag,
|
||||
);
|
||||
expect(additionConnectionCalls.length).toBe(2);
|
||||
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
additionCall[1].input[0],
|
||||
{
|
||||
type: 'value',
|
||||
value: 1,
|
||||
offset: 0,
|
||||
},
|
||||
);
|
||||
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
additionCall[1].input[1],
|
||||
{
|
||||
type: 'value',
|
||||
value: 2,
|
||||
offset: 0,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('sends a valid graph description for Animated.subtract nodes', () => {
|
||||
const first = new Animated.Value(2);
|
||||
const second = new Animated.Value(1);
|
||||
first.__makeNative();
|
||||
second.__makeNative();
|
||||
|
||||
TestRenderer.create(
|
||||
<Animated.View style={{opacity: Animated.subtract(first, second)}} />,
|
||||
);
|
||||
|
||||
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
{type: 'subtraction', input: expect.any(Array)},
|
||||
);
|
||||
const subtractionCalls = NativeAnimatedModule.createAnimatedNode.mock.calls.filter(
|
||||
call => call[1].type === 'subtraction',
|
||||
);
|
||||
expect(subtractionCalls.length).toBe(1);
|
||||
const subtractionCall = subtractionCalls[0];
|
||||
const subtractionNodeTag = subtractionCall[0];
|
||||
const subtractionConnectionCalls = NativeAnimatedModule.connectAnimatedNodes.mock.calls.filter(
|
||||
call => call[1] === subtractionNodeTag,
|
||||
);
|
||||
expect(subtractionConnectionCalls.length).toBe(2);
|
||||
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
subtractionCall[1].input[0],
|
||||
{
|
||||
type: 'value',
|
||||
value: 2,
|
||||
offset: 0,
|
||||
},
|
||||
);
|
||||
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
subtractionCall[1].input[1],
|
||||
{
|
||||
type: 'value',
|
||||
value: 1,
|
||||
offset: 0,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('sends a valid graph description for Animated.multiply nodes', () => {
|
||||
const first = new Animated.Value(2);
|
||||
const second = new Animated.Value(1);
|
||||
first.__makeNative();
|
||||
second.__makeNative();
|
||||
|
||||
TestRenderer.create(
|
||||
<Animated.View style={{opacity: Animated.multiply(first, second)}} />,
|
||||
);
|
||||
|
||||
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
{type: 'multiplication', input: expect.any(Array)},
|
||||
);
|
||||
const multiplicationCalls = NativeAnimatedModule.createAnimatedNode.mock.calls.filter(
|
||||
call => call[1].type === 'multiplication',
|
||||
);
|
||||
expect(multiplicationCalls.length).toBe(1);
|
||||
const multiplicationCall = multiplicationCalls[0];
|
||||
const multiplicationNodeTag = multiplicationCall[0];
|
||||
const multiplicationConnectionCalls = NativeAnimatedModule.connectAnimatedNodes.mock.calls.filter(
|
||||
call => call[1] === multiplicationNodeTag,
|
||||
);
|
||||
expect(multiplicationConnectionCalls.length).toBe(2);
|
||||
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
multiplicationCall[1].input[0],
|
||||
{
|
||||
type: 'value',
|
||||
value: 2,
|
||||
offset: 0,
|
||||
},
|
||||
);
|
||||
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
multiplicationCall[1].input[1],
|
||||
{
|
||||
type: 'value',
|
||||
value: 1,
|
||||
offset: 0,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('sends a valid graph description for Animated.divide nodes', () => {
|
||||
const first = new Animated.Value(4);
|
||||
const second = new Animated.Value(2);
|
||||
first.__makeNative();
|
||||
second.__makeNative();
|
||||
|
||||
TestRenderer.create(
|
||||
<Animated.View style={{opacity: Animated.divide(first, second)}} />,
|
||||
);
|
||||
|
||||
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
{type: 'division', input: expect.any(Array)},
|
||||
);
|
||||
const divisionCalls = NativeAnimatedModule.createAnimatedNode.mock.calls.filter(
|
||||
call => call[1].type === 'division',
|
||||
);
|
||||
expect(divisionCalls.length).toBe(1);
|
||||
const divisionCall = divisionCalls[0];
|
||||
const divisionNodeTag = divisionCall[0];
|
||||
const divisionConnectionCalls = NativeAnimatedModule.connectAnimatedNodes.mock.calls.filter(
|
||||
call => call[1] === divisionNodeTag,
|
||||
);
|
||||
expect(divisionConnectionCalls.length).toBe(2);
|
||||
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
divisionCall[1].input[0],
|
||||
{
|
||||
type: 'value',
|
||||
value: 4,
|
||||
offset: 0,
|
||||
},
|
||||
);
|
||||
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
divisionCall[1].input[1],
|
||||
{
|
||||
type: 'value',
|
||||
value: 2,
|
||||
offset: 0,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('sends a valid graph description for Animated.modulo nodes', () => {
|
||||
const value = new Animated.Value(4);
|
||||
value.__makeNative();
|
||||
|
||||
TestRenderer.create(
|
||||
<Animated.View style={{opacity: Animated.modulo(value, 4)}} />,
|
||||
);
|
||||
|
||||
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
{type: 'modulus', modulus: 4, input: expect.any(Number)},
|
||||
);
|
||||
const moduloCalls = NativeAnimatedModule.createAnimatedNode.mock.calls.filter(
|
||||
call => call[1].type === 'modulus',
|
||||
);
|
||||
expect(moduloCalls.length).toBe(1);
|
||||
const moduloCall = moduloCalls[0];
|
||||
const moduloNodeTag = moduloCall[0];
|
||||
const moduloConnectionCalls = NativeAnimatedModule.connectAnimatedNodes.mock.calls.filter(
|
||||
call => call[1] === moduloNodeTag,
|
||||
);
|
||||
expect(moduloConnectionCalls.length).toBe(1);
|
||||
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
moduloCall[1].input,
|
||||
{
|
||||
type: 'value',
|
||||
value: 4,
|
||||
offset: 0,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('sends a valid graph description for interpolate() nodes', () => {
|
||||
const value = new Animated.Value(10);
|
||||
value.__makeNative();
|
||||
|
||||
TestRenderer.create(
|
||||
<Animated.View
|
||||
style={{
|
||||
opacity: value.interpolate({
|
||||
inputRange: [10, 20],
|
||||
outputRange: [0, 1],
|
||||
}),
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
{type: 'value', value: 10, offset: 0},
|
||||
);
|
||||
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
{
|
||||
type: 'interpolation',
|
||||
inputRange: [10, 20],
|
||||
outputRange: [0, 1],
|
||||
extrapolateLeft: 'extend',
|
||||
extrapolateRight: 'extend',
|
||||
},
|
||||
);
|
||||
const interpolationNodeTag = NativeAnimatedModule.createAnimatedNode.mock.calls.find(
|
||||
call => call[1].type === 'interpolation',
|
||||
)[0];
|
||||
const valueNodeTag = NativeAnimatedModule.createAnimatedNode.mock.calls.find(
|
||||
call => call[1].type === 'value',
|
||||
)[0];
|
||||
expect(NativeAnimatedModule.connectAnimatedNodes).toBeCalledWith(
|
||||
valueNodeTag,
|
||||
interpolationNodeTag,
|
||||
);
|
||||
});
|
||||
|
||||
it('sends a valid graph description for transform nodes', () => {
|
||||
const translateX = new Animated.Value(0);
|
||||
translateX.__makeNative();
|
||||
|
||||
TestRenderer.create(
|
||||
<Animated.View style={{transform: [{translateX}, {scale: 2}]}} />,
|
||||
);
|
||||
|
||||
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
{
|
||||
type: 'transform',
|
||||
transforms: [
|
||||
{
|
||||
nodeTag: expect.any(Number),
|
||||
property: 'translateX',
|
||||
type: 'animated',
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
property: 'scale',
|
||||
type: 'static',
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('sends a valid graph description for Animated.diffClamp nodes', () => {
|
||||
const value = new Animated.Value(2);
|
||||
value.__makeNative();
|
||||
|
||||
TestRenderer.create(
|
||||
<Animated.View style={{opacity: Animated.diffClamp(value, 0, 20)}} />,
|
||||
);
|
||||
|
||||
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
{type: 'diffclamp', input: expect.any(Number), max: 20, min: 0},
|
||||
);
|
||||
const diffClampCalls = NativeAnimatedModule.createAnimatedNode.mock.calls.filter(
|
||||
call => call[1].type === 'diffclamp',
|
||||
);
|
||||
expect(diffClampCalls.length).toBe(1);
|
||||
const diffClampCall = diffClampCalls[0];
|
||||
const diffClampNodeTag = diffClampCall[0];
|
||||
const diffClampConnectionCalls = NativeAnimatedModule.connectAnimatedNodes.mock.calls.filter(
|
||||
call => call[1] === diffClampNodeTag,
|
||||
);
|
||||
expect(diffClampConnectionCalls.length).toBe(1);
|
||||
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
diffClampCall[1].input,
|
||||
{
|
||||
type: 'value',
|
||||
value: 2,
|
||||
offset: 0,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("doesn't call into native API if useNativeDriver is set to false", () => {
|
||||
const opacity = new Animated.Value(0);
|
||||
|
||||
const root = TestRenderer.create(<Animated.View style={{opacity}} />);
|
||||
|
||||
Animated.timing(opacity, {
|
||||
toValue: 10,
|
||||
duration: 1000,
|
||||
useNativeDriver: false,
|
||||
}).start();
|
||||
|
||||
root.unmount();
|
||||
|
||||
expect(NativeAnimatedModule.createAnimatedNode).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('fails when trying to run non-native animation on native node', () => {
|
||||
const opacity = new Animated.Value(0);
|
||||
const ref = React.createRef(null);
|
||||
|
||||
TestRenderer.create(<Animated.View ref={ref} style={{opacity}} />);
|
||||
|
||||
// Necessary to simulate the native animation.
|
||||
expect(ref.current).not.toBeNull();
|
||||
ref.current.setNativeProps = jest.fn();
|
||||
|
||||
Animated.timing(opacity, {
|
||||
toValue: 10,
|
||||
duration: 50,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
jest.runAllTimers();
|
||||
|
||||
Animated.timing(opacity, {
|
||||
toValue: 4,
|
||||
duration: 500,
|
||||
useNativeDriver: false,
|
||||
}).start();
|
||||
try {
|
||||
process.env.NODE_ENV = 'development';
|
||||
expect(jest.runAllTimers).toThrow(
|
||||
'Attempting to run JS driven animation on animated node that has ' +
|
||||
'been moved to "native" earlier by starting an animation with ' +
|
||||
'`useNativeDriver: true`',
|
||||
);
|
||||
} finally {
|
||||
process.env.NODE_ENV = 'test';
|
||||
}
|
||||
});
|
||||
|
||||
it('fails for unsupported styles', () => {
|
||||
const left = new Animated.Value(0);
|
||||
|
||||
TestRenderer.create(<Animated.View style={{left}} />);
|
||||
|
||||
const animation = Animated.timing(left, {
|
||||
toValue: 10,
|
||||
duration: 50,
|
||||
useNativeDriver: true,
|
||||
});
|
||||
expect(animation.start).toThrowError(/left/);
|
||||
});
|
||||
|
||||
it('works for any `static` props and styles', () => {
|
||||
// Passing "unsupported" props should work just fine as long as they are not animated
|
||||
const opacity = new Animated.Value(0);
|
||||
opacity.__makeNative();
|
||||
|
||||
TestRenderer.create(
|
||||
<Animated.View
|
||||
removeClippedSubviews={true}
|
||||
style={{left: 10, opacity, top: 20}}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
{type: 'style', style: {opacity: expect.any(Number)}},
|
||||
);
|
||||
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
{type: 'props', props: {style: expect.any(Number)}},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Animations', () => {
|
||||
it('sends a valid timing animation description', () => {
|
||||
const anim = new Animated.Value(0);
|
||||
Animated.timing(anim, {
|
||||
toValue: 10,
|
||||
duration: 1000,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
|
||||
expect(NativeAnimatedModule.startAnimatingNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
expect.any(Number),
|
||||
{
|
||||
type: 'frames',
|
||||
frames: expect.any(Array),
|
||||
toValue: expect.any(Number),
|
||||
iterations: 1,
|
||||
},
|
||||
expect.any(Function),
|
||||
);
|
||||
});
|
||||
|
||||
it('sends a valid spring animation description', () => {
|
||||
const anim = new Animated.Value(0);
|
||||
Animated.spring(anim, {
|
||||
toValue: 10,
|
||||
friction: 5,
|
||||
tension: 164,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
expect(NativeAnimatedModule.startAnimatingNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
expect.any(Number),
|
||||
{
|
||||
type: 'spring',
|
||||
stiffness: 679.08,
|
||||
damping: 16,
|
||||
mass: 1,
|
||||
initialVelocity: 0,
|
||||
overshootClamping: false,
|
||||
restDisplacementThreshold: 0.001,
|
||||
restSpeedThreshold: 0.001,
|
||||
toValue: 10,
|
||||
iterations: 1,
|
||||
},
|
||||
expect.any(Function),
|
||||
);
|
||||
|
||||
Animated.spring(anim, {
|
||||
toValue: 10,
|
||||
stiffness: 1000,
|
||||
damping: 500,
|
||||
mass: 3,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
expect(NativeAnimatedModule.startAnimatingNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
expect.any(Number),
|
||||
{
|
||||
type: 'spring',
|
||||
stiffness: 1000,
|
||||
damping: 500,
|
||||
mass: 3,
|
||||
initialVelocity: 0,
|
||||
overshootClamping: false,
|
||||
restDisplacementThreshold: 0.001,
|
||||
restSpeedThreshold: 0.001,
|
||||
toValue: 10,
|
||||
iterations: 1,
|
||||
},
|
||||
expect.any(Function),
|
||||
);
|
||||
|
||||
Animated.spring(anim, {
|
||||
toValue: 10,
|
||||
bounciness: 8,
|
||||
speed: 10,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
expect(NativeAnimatedModule.startAnimatingNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
expect.any(Number),
|
||||
{
|
||||
type: 'spring',
|
||||
damping: 23.05223140901191,
|
||||
initialVelocity: 0,
|
||||
overshootClamping: false,
|
||||
restDisplacementThreshold: 0.001,
|
||||
restSpeedThreshold: 0.001,
|
||||
stiffness: 299.61882352941177,
|
||||
mass: 1,
|
||||
toValue: 10,
|
||||
iterations: 1,
|
||||
},
|
||||
expect.any(Function),
|
||||
);
|
||||
});
|
||||
|
||||
it('sends a valid decay animation description', () => {
|
||||
const anim = new Animated.Value(0);
|
||||
Animated.decay(anim, {
|
||||
velocity: 10,
|
||||
deceleration: 0.1,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
|
||||
expect(NativeAnimatedModule.startAnimatingNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
expect.any(Number),
|
||||
{type: 'decay', deceleration: 0.1, velocity: 10, iterations: 1},
|
||||
expect.any(Function),
|
||||
);
|
||||
});
|
||||
|
||||
it('works with Animated.loop', () => {
|
||||
const anim = new Animated.Value(0);
|
||||
Animated.loop(
|
||||
Animated.decay(anim, {
|
||||
velocity: 10,
|
||||
deceleration: 0.1,
|
||||
useNativeDriver: true,
|
||||
}),
|
||||
{iterations: 10},
|
||||
).start();
|
||||
|
||||
expect(NativeAnimatedModule.startAnimatingNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
expect.any(Number),
|
||||
{type: 'decay', deceleration: 0.1, velocity: 10, iterations: 10},
|
||||
expect.any(Function),
|
||||
);
|
||||
});
|
||||
|
||||
it('sends stopAnimation command to native', () => {
|
||||
const value = new Animated.Value(0);
|
||||
const animation = Animated.timing(value, {
|
||||
toValue: 10,
|
||||
duration: 50,
|
||||
useNativeDriver: true,
|
||||
});
|
||||
|
||||
animation.start();
|
||||
expect(NativeAnimatedModule.startAnimatingNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
expect.any(Number),
|
||||
{
|
||||
type: 'frames',
|
||||
frames: expect.any(Array),
|
||||
toValue: expect.any(Number),
|
||||
iterations: 1,
|
||||
},
|
||||
expect.any(Function),
|
||||
);
|
||||
const animationId =
|
||||
NativeAnimatedModule.startAnimatingNode.mock.calls[0][0];
|
||||
|
||||
animation.stop();
|
||||
expect(NativeAnimatedModule.stopAnimation).toBeCalledWith(animationId);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Animated Components', () => {
|
||||
it('Should restore default values on prop updates only', () => {
|
||||
const opacity = new Animated.Value(0);
|
||||
opacity.__makeNative();
|
||||
|
||||
const root = TestRenderer.create(<Animated.View style={{opacity}} />);
|
||||
expect(NativeAnimatedModule.restoreDefaultValues).not.toHaveBeenCalled();
|
||||
|
||||
root.update(<Animated.View style={{opacity}} />);
|
||||
expect(NativeAnimatedModule.restoreDefaultValues).toHaveBeenCalledWith(
|
||||
expect.any(Number),
|
||||
);
|
||||
|
||||
root.unmount();
|
||||
// Make sure it doesn't get called on unmount.
|
||||
expect(NativeAnimatedModule.restoreDefaultValues).toHaveBeenCalledTimes(
|
||||
1,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,467 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* @emails oncall+react_native
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const Easing = require('../Easing');
|
||||
describe('Easing', () => {
|
||||
it('should work with linear', () => {
|
||||
const easing = Easing.linear;
|
||||
|
||||
expect(easing(0)).toBe(0);
|
||||
expect(easing(0.5)).toBe(0.5);
|
||||
expect(easing(0.8)).toBe(0.8);
|
||||
expect(easing(1)).toBe(1);
|
||||
});
|
||||
|
||||
it('should work with ease in linear', () => {
|
||||
const easing = Easing.in(Easing.linear);
|
||||
expect(easing(0)).toBe(0);
|
||||
expect(easing(0.5)).toBe(0.5);
|
||||
expect(easing(0.8)).toBe(0.8);
|
||||
expect(easing(1)).toBe(1);
|
||||
});
|
||||
|
||||
it('should work with easy out linear', () => {
|
||||
const easing = Easing.out(Easing.linear);
|
||||
expect(easing(0)).toBe(0);
|
||||
expect(easing(0.5)).toBe(0.5);
|
||||
expect(easing(0.6)).toBe(0.6);
|
||||
expect(easing(1)).toBe(1);
|
||||
});
|
||||
|
||||
it('should work with ease in quad', () => {
|
||||
function easeInQuad(t) {
|
||||
return t * t;
|
||||
}
|
||||
const easing = Easing.in(Easing.quad);
|
||||
for (let t = -0.5; t < 1.5; t += 0.1) {
|
||||
expect(easing(t)).toBe(easeInQuad(t));
|
||||
}
|
||||
});
|
||||
|
||||
it('should work with ease out quad', () => {
|
||||
function easeOutQuad(t) {
|
||||
return -t * (t - 2);
|
||||
}
|
||||
const easing = Easing.out(Easing.quad);
|
||||
for (let t = 0; t <= 1; t += 0.1) {
|
||||
expect(easing(1)).toBe(easeOutQuad(1));
|
||||
}
|
||||
});
|
||||
|
||||
it('should work with ease in-out quad', () => {
|
||||
function easeInOutQuad(t) {
|
||||
t = t * 2;
|
||||
if (t < 1) {
|
||||
return 0.5 * t * t;
|
||||
}
|
||||
return -((t - 1) * (t - 3) - 1) / 2;
|
||||
}
|
||||
const easing = Easing.inOut(Easing.quad);
|
||||
for (let t = -0.5; t < 1.5; t += 0.1) {
|
||||
expect(easing(t)).toBeCloseTo(easeInOutQuad(t), 4);
|
||||
}
|
||||
});
|
||||
|
||||
it('should satisfy boundary conditions with elastic', () => {
|
||||
for (let b = 0; b < 4; b += 0.3) {
|
||||
const easing = Easing.elastic(b);
|
||||
expect(easing(0)).toBe(0);
|
||||
expect(easing(1)).toBe(1);
|
||||
}
|
||||
});
|
||||
|
||||
function sampleEasingFunction(easing) {
|
||||
const DURATION = 300;
|
||||
const tickCount = Math.round((DURATION * 60) / 1000);
|
||||
const samples = [];
|
||||
for (let i = 0; i <= tickCount; i++) {
|
||||
samples.push(easing(i / tickCount));
|
||||
}
|
||||
return samples;
|
||||
}
|
||||
|
||||
const Samples = {
|
||||
in_quad: [
|
||||
0,
|
||||
0.0030864197530864196,
|
||||
0.012345679012345678,
|
||||
0.027777777777777776,
|
||||
0.04938271604938271,
|
||||
0.0771604938271605,
|
||||
0.1111111111111111,
|
||||
0.15123456790123457,
|
||||
0.19753086419753085,
|
||||
0.25,
|
||||
0.308641975308642,
|
||||
0.37345679012345684,
|
||||
0.4444444444444444,
|
||||
0.5216049382716049,
|
||||
0.6049382716049383,
|
||||
0.6944444444444445,
|
||||
0.7901234567901234,
|
||||
0.8919753086419753,
|
||||
1,
|
||||
],
|
||||
out_quad: [
|
||||
0,
|
||||
0.10802469135802469,
|
||||
0.20987654320987653,
|
||||
0.3055555555555555,
|
||||
0.3950617283950617,
|
||||
0.47839506172839513,
|
||||
0.5555555555555556,
|
||||
0.6265432098765432,
|
||||
0.691358024691358,
|
||||
0.75,
|
||||
0.8024691358024691,
|
||||
0.8487654320987654,
|
||||
0.888888888888889,
|
||||
0.9228395061728394,
|
||||
0.9506172839506174,
|
||||
0.9722222222222221,
|
||||
0.9876543209876543,
|
||||
0.9969135802469136,
|
||||
1,
|
||||
],
|
||||
inOut_quad: [
|
||||
0,
|
||||
0.006172839506172839,
|
||||
0.024691358024691357,
|
||||
0.05555555555555555,
|
||||
0.09876543209876543,
|
||||
0.154320987654321,
|
||||
0.2222222222222222,
|
||||
0.30246913580246915,
|
||||
0.3950617283950617,
|
||||
0.5,
|
||||
0.6049382716049383,
|
||||
0.697530864197531,
|
||||
0.7777777777777777,
|
||||
0.845679012345679,
|
||||
0.9012345679012346,
|
||||
0.9444444444444444,
|
||||
0.9753086419753086,
|
||||
0.9938271604938271,
|
||||
1,
|
||||
],
|
||||
in_cubic: [
|
||||
0,
|
||||
0.00017146776406035664,
|
||||
0.0013717421124828531,
|
||||
0.004629629629629629,
|
||||
0.010973936899862825,
|
||||
0.021433470507544586,
|
||||
0.037037037037037035,
|
||||
0.05881344307270234,
|
||||
0.0877914951989026,
|
||||
0.125,
|
||||
0.1714677640603567,
|
||||
0.22822359396433475,
|
||||
0.2962962962962963,
|
||||
0.37671467764060357,
|
||||
0.4705075445816187,
|
||||
0.5787037037037038,
|
||||
0.7023319615912208,
|
||||
0.8424211248285322,
|
||||
1,
|
||||
],
|
||||
out_cubic: [
|
||||
0,
|
||||
0.15757887517146785,
|
||||
0.2976680384087792,
|
||||
0.42129629629629617,
|
||||
0.5294924554183813,
|
||||
0.6232853223593964,
|
||||
0.7037037037037036,
|
||||
0.7717764060356652,
|
||||
0.8285322359396433,
|
||||
0.875,
|
||||
0.9122085048010974,
|
||||
0.9411865569272977,
|
||||
0.9629629629629629,
|
||||
0.9785665294924554,
|
||||
0.9890260631001372,
|
||||
0.9953703703703703,
|
||||
0.9986282578875172,
|
||||
0.9998285322359396,
|
||||
1,
|
||||
],
|
||||
inOut_cubic: [
|
||||
0,
|
||||
0.0006858710562414266,
|
||||
0.0054869684499314125,
|
||||
0.018518518518518517,
|
||||
0.0438957475994513,
|
||||
0.08573388203017834,
|
||||
0.14814814814814814,
|
||||
0.23525377229080935,
|
||||
0.3511659807956104,
|
||||
0.5,
|
||||
0.6488340192043895,
|
||||
0.7647462277091908,
|
||||
0.8518518518518519,
|
||||
0.9142661179698217,
|
||||
0.9561042524005487,
|
||||
0.9814814814814815,
|
||||
0.9945130315500685,
|
||||
0.9993141289437586,
|
||||
1,
|
||||
],
|
||||
in_sin: [
|
||||
0,
|
||||
0.003805301908254455,
|
||||
0.01519224698779198,
|
||||
0.03407417371093169,
|
||||
0.06030737921409157,
|
||||
0.09369221296335006,
|
||||
0.1339745962155613,
|
||||
0.1808479557110082,
|
||||
0.233955556881022,
|
||||
0.2928932188134524,
|
||||
0.35721239031346064,
|
||||
0.42642356364895384,
|
||||
0.4999999999999999,
|
||||
0.5773817382593005,
|
||||
0.6579798566743311,
|
||||
0.7411809548974793,
|
||||
0.8263518223330696,
|
||||
0.9128442572523416,
|
||||
0.9999999999999999,
|
||||
],
|
||||
out_sin: [
|
||||
0,
|
||||
0.08715574274765817,
|
||||
0.17364817766693033,
|
||||
0.25881904510252074,
|
||||
0.3420201433256687,
|
||||
0.42261826174069944,
|
||||
0.49999999999999994,
|
||||
0.573576436351046,
|
||||
0.6427876096865393,
|
||||
0.7071067811865475,
|
||||
0.766044443118978,
|
||||
0.8191520442889918,
|
||||
0.8660254037844386,
|
||||
0.9063077870366499,
|
||||
0.9396926207859083,
|
||||
0.9659258262890683,
|
||||
0.984807753012208,
|
||||
0.9961946980917455,
|
||||
1,
|
||||
],
|
||||
inOut_sin: [
|
||||
0,
|
||||
0.00759612349389599,
|
||||
0.030153689607045786,
|
||||
0.06698729810778065,
|
||||
0.116977778440511,
|
||||
0.17860619515673032,
|
||||
0.24999999999999994,
|
||||
0.32898992833716556,
|
||||
0.4131759111665348,
|
||||
0.49999999999999994,
|
||||
0.5868240888334652,
|
||||
0.6710100716628343,
|
||||
0.7499999999999999,
|
||||
0.8213938048432696,
|
||||
0.883022221559489,
|
||||
0.9330127018922194,
|
||||
0.9698463103929542,
|
||||
0.9924038765061041,
|
||||
1,
|
||||
],
|
||||
in_exp: [
|
||||
0,
|
||||
0.0014352875901128893,
|
||||
0.002109491677524035,
|
||||
0.0031003926796253885,
|
||||
0.004556754060844206,
|
||||
0.006697218616039631,
|
||||
0.009843133202303688,
|
||||
0.014466792379488908,
|
||||
0.021262343752724643,
|
||||
0.03125,
|
||||
0.045929202883612456,
|
||||
0.06750373368076916,
|
||||
0.09921256574801243,
|
||||
0.1458161299470146,
|
||||
0.2143109957132682,
|
||||
0.31498026247371835,
|
||||
0.46293735614364506,
|
||||
0.6803950000871883,
|
||||
1,
|
||||
],
|
||||
out_exp: [
|
||||
0,
|
||||
0.31960499991281155,
|
||||
0.5370626438563548,
|
||||
0.6850197375262816,
|
||||
0.7856890042867318,
|
||||
0.8541838700529854,
|
||||
0.9007874342519875,
|
||||
0.9324962663192309,
|
||||
0.9540707971163875,
|
||||
0.96875,
|
||||
0.9787376562472754,
|
||||
0.9855332076205111,
|
||||
0.9901568667976963,
|
||||
0.9933027813839603,
|
||||
0.9954432459391558,
|
||||
0.9968996073203746,
|
||||
0.9978905083224759,
|
||||
0.9985647124098871,
|
||||
1,
|
||||
],
|
||||
inOut_exp: [
|
||||
0,
|
||||
0.0010547458387620175,
|
||||
0.002278377030422103,
|
||||
0.004921566601151844,
|
||||
0.010631171876362321,
|
||||
0.022964601441806228,
|
||||
0.049606282874006216,
|
||||
0.1071554978566341,
|
||||
0.23146867807182253,
|
||||
0.5,
|
||||
0.7685313219281775,
|
||||
0.892844502143366,
|
||||
0.9503937171259937,
|
||||
0.9770353985581938,
|
||||
0.9893688281236377,
|
||||
0.9950784333988482,
|
||||
0.9977216229695779,
|
||||
0.998945254161238,
|
||||
1,
|
||||
],
|
||||
in_circle: [
|
||||
0,
|
||||
0.0015444024660317135,
|
||||
0.006192010000093506,
|
||||
0.013986702816730645,
|
||||
0.025003956956430873,
|
||||
0.03935464078941209,
|
||||
0.057190958417936644,
|
||||
0.07871533601238889,
|
||||
0.10419358352238339,
|
||||
0.1339745962155614,
|
||||
0.1685205807169019,
|
||||
0.20845517506805522,
|
||||
0.2546440075000701,
|
||||
0.3083389112228482,
|
||||
0.37146063894529113,
|
||||
0.4472292016074334,
|
||||
0.5418771527091488,
|
||||
0.6713289009389102,
|
||||
1,
|
||||
],
|
||||
out_circle: [
|
||||
0,
|
||||
0.3286710990610898,
|
||||
0.45812284729085123,
|
||||
0.5527707983925666,
|
||||
0.6285393610547089,
|
||||
0.6916610887771518,
|
||||
0.7453559924999298,
|
||||
0.7915448249319448,
|
||||
0.8314794192830981,
|
||||
0.8660254037844386,
|
||||
0.8958064164776166,
|
||||
0.9212846639876111,
|
||||
0.9428090415820634,
|
||||
0.9606453592105879,
|
||||
0.9749960430435691,
|
||||
0.9860132971832694,
|
||||
0.9938079899999065,
|
||||
0.9984555975339683,
|
||||
1,
|
||||
],
|
||||
inOut_circle: [
|
||||
0,
|
||||
0.003096005000046753,
|
||||
0.012501978478215436,
|
||||
0.028595479208968322,
|
||||
0.052096791761191696,
|
||||
0.08426029035845095,
|
||||
0.12732200375003505,
|
||||
0.18573031947264557,
|
||||
0.2709385763545744,
|
||||
0.5,
|
||||
0.7290614236454256,
|
||||
0.8142696805273546,
|
||||
0.8726779962499649,
|
||||
0.915739709641549,
|
||||
0.9479032082388084,
|
||||
0.9714045207910317,
|
||||
0.9874980215217846,
|
||||
0.9969039949999532,
|
||||
1,
|
||||
],
|
||||
in_back_: [
|
||||
0,
|
||||
-0.004788556241426612,
|
||||
-0.017301289437585736,
|
||||
-0.0347587962962963,
|
||||
-0.05438167352537723,
|
||||
-0.07339051783264748,
|
||||
-0.08900592592592595,
|
||||
-0.09844849451303156,
|
||||
-0.0989388203017833,
|
||||
-0.08769750000000004,
|
||||
-0.06194513031550073,
|
||||
-0.018902307956104283,
|
||||
0.044210370370370254,
|
||||
0.13017230795610413,
|
||||
0.2417629080932785,
|
||||
0.3817615740740742,
|
||||
0.5529477091906719,
|
||||
0.7581007167352535,
|
||||
0.9999999999999998,
|
||||
],
|
||||
out_back_: [
|
||||
2.220446049250313e-16,
|
||||
0.24189928326474652,
|
||||
0.44705229080932807,
|
||||
0.6182384259259258,
|
||||
0.7582370919067215,
|
||||
0.8698276920438959,
|
||||
0.9557896296296297,
|
||||
1.0189023079561044,
|
||||
1.0619451303155008,
|
||||
1.0876975,
|
||||
1.0989388203017834,
|
||||
1.0984484945130315,
|
||||
1.089005925925926,
|
||||
1.0733905178326475,
|
||||
1.0543816735253773,
|
||||
1.0347587962962963,
|
||||
1.0173012894375857,
|
||||
1.0047885562414267,
|
||||
1,
|
||||
],
|
||||
};
|
||||
|
||||
Object.keys(Samples).forEach(function(type) {
|
||||
it('should ease ' + type, function() {
|
||||
const [modeName, easingName, isFunction] = type.split('_');
|
||||
let easing = Easing[easingName];
|
||||
if (isFunction !== undefined) {
|
||||
easing = easing();
|
||||
}
|
||||
const computed = sampleEasingFunction(Easing[modeName](easing));
|
||||
const samples = Samples[type];
|
||||
|
||||
computed.forEach((value, key) => {
|
||||
expect(value).toBeCloseTo(samples[key], 2);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,316 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* @emails oncall+react_native
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const AnimatedInterpolation = require('../nodes/AnimatedInterpolation');
|
||||
const Easing = require('../Easing');
|
||||
|
||||
describe('Interpolation', () => {
|
||||
it('should work with defaults', () => {
|
||||
const interpolation = AnimatedInterpolation.__createInterpolation({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [0, 1],
|
||||
});
|
||||
|
||||
expect(interpolation(0)).toBe(0);
|
||||
expect(interpolation(0.5)).toBe(0.5);
|
||||
expect(interpolation(0.8)).toBe(0.8);
|
||||
expect(interpolation(1)).toBe(1);
|
||||
});
|
||||
|
||||
it('should work with output range', () => {
|
||||
const interpolation = AnimatedInterpolation.__createInterpolation({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [100, 200],
|
||||
});
|
||||
|
||||
expect(interpolation(0)).toBe(100);
|
||||
expect(interpolation(0.5)).toBe(150);
|
||||
expect(interpolation(0.8)).toBe(180);
|
||||
expect(interpolation(1)).toBe(200);
|
||||
});
|
||||
|
||||
it('should work with input range', () => {
|
||||
const interpolation = AnimatedInterpolation.__createInterpolation({
|
||||
inputRange: [100, 200],
|
||||
outputRange: [0, 1],
|
||||
});
|
||||
|
||||
expect(interpolation(100)).toBe(0);
|
||||
expect(interpolation(150)).toBe(0.5);
|
||||
expect(interpolation(180)).toBe(0.8);
|
||||
expect(interpolation(200)).toBe(1);
|
||||
});
|
||||
|
||||
it('should throw for non monotonic input ranges', () => {
|
||||
expect(() =>
|
||||
AnimatedInterpolation.__createInterpolation({
|
||||
inputRange: [0, 2, 1],
|
||||
outputRange: [0, 1, 2],
|
||||
}),
|
||||
).toThrow();
|
||||
|
||||
expect(() =>
|
||||
AnimatedInterpolation.__createInterpolation({
|
||||
inputRange: [0, 1, 2],
|
||||
outputRange: [0, 3, 1],
|
||||
}),
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
it('should work with empty input range', () => {
|
||||
const interpolation = AnimatedInterpolation.__createInterpolation({
|
||||
inputRange: [0, 10, 10],
|
||||
outputRange: [1, 2, 3],
|
||||
extrapolate: 'extend',
|
||||
});
|
||||
|
||||
expect(interpolation(0)).toBe(1);
|
||||
expect(interpolation(5)).toBe(1.5);
|
||||
expect(interpolation(10)).toBe(2);
|
||||
expect(interpolation(10.1)).toBe(3);
|
||||
expect(interpolation(15)).toBe(3);
|
||||
});
|
||||
|
||||
it('should work with empty output range', () => {
|
||||
const interpolation = AnimatedInterpolation.__createInterpolation({
|
||||
inputRange: [1, 2, 3],
|
||||
outputRange: [0, 10, 10],
|
||||
extrapolate: 'extend',
|
||||
});
|
||||
|
||||
expect(interpolation(0)).toBe(-10);
|
||||
expect(interpolation(1.5)).toBe(5);
|
||||
expect(interpolation(2)).toBe(10);
|
||||
expect(interpolation(2.5)).toBe(10);
|
||||
expect(interpolation(3)).toBe(10);
|
||||
expect(interpolation(4)).toBe(10);
|
||||
});
|
||||
|
||||
it('should work with easing', () => {
|
||||
const interpolation = AnimatedInterpolation.__createInterpolation({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [0, 1],
|
||||
easing: Easing.quad,
|
||||
});
|
||||
|
||||
expect(interpolation(0)).toBe(0);
|
||||
expect(interpolation(0.5)).toBe(0.25);
|
||||
expect(interpolation(0.9)).toBe(0.81);
|
||||
expect(interpolation(1)).toBe(1);
|
||||
});
|
||||
|
||||
it('should work with extrapolate', () => {
|
||||
let interpolation = AnimatedInterpolation.__createInterpolation({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [0, 1],
|
||||
extrapolate: 'extend',
|
||||
easing: Easing.quad,
|
||||
});
|
||||
|
||||
expect(interpolation(-2)).toBe(4);
|
||||
expect(interpolation(2)).toBe(4);
|
||||
|
||||
interpolation = AnimatedInterpolation.__createInterpolation({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [0, 1],
|
||||
extrapolate: 'clamp',
|
||||
easing: Easing.quad,
|
||||
});
|
||||
|
||||
expect(interpolation(-2)).toBe(0);
|
||||
expect(interpolation(2)).toBe(1);
|
||||
|
||||
interpolation = AnimatedInterpolation.__createInterpolation({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [0, 1],
|
||||
extrapolate: 'identity',
|
||||
easing: Easing.quad,
|
||||
});
|
||||
|
||||
expect(interpolation(-2)).toBe(-2);
|
||||
expect(interpolation(2)).toBe(2);
|
||||
});
|
||||
|
||||
it('should work with keyframes with extrapolate', () => {
|
||||
const interpolation = AnimatedInterpolation.__createInterpolation({
|
||||
inputRange: [0, 10, 100, 1000],
|
||||
outputRange: [0, 5, 50, 500],
|
||||
extrapolate: true,
|
||||
});
|
||||
|
||||
expect(interpolation(-5)).toBe(-2.5);
|
||||
expect(interpolation(0)).toBe(0);
|
||||
expect(interpolation(5)).toBe(2.5);
|
||||
expect(interpolation(10)).toBe(5);
|
||||
expect(interpolation(50)).toBe(25);
|
||||
expect(interpolation(100)).toBe(50);
|
||||
expect(interpolation(500)).toBe(250);
|
||||
expect(interpolation(1000)).toBe(500);
|
||||
expect(interpolation(2000)).toBe(1000);
|
||||
});
|
||||
|
||||
it('should work with keyframes without extrapolate', () => {
|
||||
const interpolation = AnimatedInterpolation.__createInterpolation({
|
||||
inputRange: [0, 1, 2],
|
||||
outputRange: [0.2, 1, 0.2],
|
||||
extrapolate: 'clamp',
|
||||
});
|
||||
|
||||
expect(interpolation(5)).toBeCloseTo(0.2);
|
||||
});
|
||||
|
||||
it('should throw for an infinite input range', () => {
|
||||
expect(() =>
|
||||
AnimatedInterpolation.__createInterpolation({
|
||||
inputRange: [-Infinity, Infinity],
|
||||
outputRange: [0, 1],
|
||||
}),
|
||||
).toThrow();
|
||||
|
||||
expect(() =>
|
||||
AnimatedInterpolation.__createInterpolation({
|
||||
inputRange: [-Infinity, 0, Infinity],
|
||||
outputRange: [1, 2, 3],
|
||||
}),
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
it('should work with negative infinite', () => {
|
||||
const interpolation = AnimatedInterpolation.__createInterpolation({
|
||||
inputRange: [-Infinity, 0],
|
||||
outputRange: [-Infinity, 0],
|
||||
easing: Easing.quad,
|
||||
extrapolate: 'identity',
|
||||
});
|
||||
|
||||
expect(interpolation(-Infinity)).toBe(-Infinity);
|
||||
expect(interpolation(-100)).toBeCloseTo(-10000);
|
||||
expect(interpolation(-10)).toBeCloseTo(-100);
|
||||
expect(interpolation(0)).toBeCloseTo(0);
|
||||
expect(interpolation(1)).toBeCloseTo(1);
|
||||
expect(interpolation(100)).toBeCloseTo(100);
|
||||
});
|
||||
|
||||
it('should work with positive infinite', () => {
|
||||
const interpolation = AnimatedInterpolation.__createInterpolation({
|
||||
inputRange: [5, Infinity],
|
||||
outputRange: [5, Infinity],
|
||||
easing: Easing.quad,
|
||||
extrapolate: 'identity',
|
||||
});
|
||||
|
||||
expect(interpolation(-100)).toBeCloseTo(-100);
|
||||
expect(interpolation(-10)).toBeCloseTo(-10);
|
||||
expect(interpolation(0)).toBeCloseTo(0);
|
||||
expect(interpolation(5)).toBeCloseTo(5);
|
||||
expect(interpolation(6)).toBeCloseTo(5 + 1);
|
||||
expect(interpolation(10)).toBeCloseTo(5 + 25);
|
||||
expect(interpolation(100)).toBeCloseTo(5 + 95 * 95);
|
||||
expect(interpolation(Infinity)).toBe(Infinity);
|
||||
});
|
||||
|
||||
it('should work with output ranges as string', () => {
|
||||
const interpolation = AnimatedInterpolation.__createInterpolation({
|
||||
inputRange: [0, 1],
|
||||
outputRange: ['rgba(0, 100, 200, 0)', 'rgba(50, 150, 250, 0.4)'],
|
||||
});
|
||||
|
||||
expect(interpolation(0)).toBe('rgba(0, 100, 200, 0)');
|
||||
expect(interpolation(0.5)).toBe('rgba(25, 125, 225, 0.2)');
|
||||
expect(interpolation(1)).toBe('rgba(50, 150, 250, 0.4)');
|
||||
});
|
||||
|
||||
it('should work with output ranges as short hex string', () => {
|
||||
const interpolation = AnimatedInterpolation.__createInterpolation({
|
||||
inputRange: [0, 1],
|
||||
outputRange: ['#024', '#9BF'],
|
||||
});
|
||||
|
||||
expect(interpolation(0)).toBe('rgba(0, 34, 68, 1)');
|
||||
expect(interpolation(0.5)).toBe('rgba(77, 111, 162, 1)');
|
||||
expect(interpolation(1)).toBe('rgba(153, 187, 255, 1)');
|
||||
});
|
||||
|
||||
it('should work with output ranges as long hex string', () => {
|
||||
const interpolation = AnimatedInterpolation.__createInterpolation({
|
||||
inputRange: [0, 1],
|
||||
outputRange: ['#FF9500', '#87FC70'],
|
||||
});
|
||||
|
||||
expect(interpolation(0)).toBe('rgba(255, 149, 0, 1)');
|
||||
expect(interpolation(0.5)).toBe('rgba(195, 201, 56, 1)');
|
||||
expect(interpolation(1)).toBe('rgba(135, 252, 112, 1)');
|
||||
});
|
||||
|
||||
it('should work with output ranges with mixed hex and rgba strings', () => {
|
||||
const interpolation = AnimatedInterpolation.__createInterpolation({
|
||||
inputRange: [0, 1],
|
||||
outputRange: ['rgba(100, 120, 140, .4)', '#87FC70'],
|
||||
});
|
||||
|
||||
expect(interpolation(0)).toBe('rgba(100, 120, 140, 0.4)');
|
||||
expect(interpolation(0.5)).toBe('rgba(118, 186, 126, 0.7)');
|
||||
expect(interpolation(1)).toBe('rgba(135, 252, 112, 1)');
|
||||
});
|
||||
|
||||
it('should work with negative and decimal values in string ranges', () => {
|
||||
const interpolation = AnimatedInterpolation.__createInterpolation({
|
||||
inputRange: [0, 1],
|
||||
outputRange: ['-100.5deg', '100deg'],
|
||||
});
|
||||
|
||||
expect(interpolation(0)).toBe('-100.5deg');
|
||||
expect(interpolation(0.5)).toBe('-0.25deg');
|
||||
expect(interpolation(1)).toBe('100deg');
|
||||
});
|
||||
|
||||
it('should crash when chaining an interpolation that returns a string', () => {
|
||||
const interpolation = AnimatedInterpolation.__createInterpolation({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [0, 1],
|
||||
});
|
||||
expect(() => {
|
||||
interpolation('45rad');
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
it('should support a mix of color patterns', () => {
|
||||
const interpolation = AnimatedInterpolation.__createInterpolation({
|
||||
inputRange: [0, 1, 2],
|
||||
outputRange: ['rgba(0, 100, 200, 0)', 'rgb(50, 150, 250)', 'red'],
|
||||
});
|
||||
|
||||
expect(interpolation(0)).toBe('rgba(0, 100, 200, 0)');
|
||||
expect(interpolation(0.5)).toBe('rgba(25, 125, 225, 0.5)');
|
||||
expect(interpolation(1.5)).toBe('rgba(153, 75, 125, 1)');
|
||||
expect(interpolation(2)).toBe('rgba(255, 0, 0, 1)');
|
||||
});
|
||||
|
||||
it('should crash when defining output range with different pattern', () => {
|
||||
expect(() =>
|
||||
AnimatedInterpolation.__createInterpolation({
|
||||
inputRange: [0, 1],
|
||||
outputRange: ['20deg', '30rad'],
|
||||
}),
|
||||
).toThrow();
|
||||
});
|
||||
|
||||
it('should round the alpha channel of a color to the nearest thousandth', () => {
|
||||
const interpolation = AnimatedInterpolation.__createInterpolation({
|
||||
inputRange: [0, 1],
|
||||
outputRange: ['rgba(0, 0, 0, 0)', 'rgba(0, 0, 0, 1)'],
|
||||
});
|
||||
|
||||
expect(interpolation(1e-12)).toBe('rgba(0, 0, 0, 0)');
|
||||
expect(interpolation(2 / 3)).toBe('rgba(0, 0, 0, 0.667)');
|
||||
});
|
||||
});
|
||||
+3
-3
@@ -1,16 +1,16 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* @oncall react_native
|
||||
* @emails oncall+react_native
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import TimingAnimation from '../animations/TimingAnimation';
|
||||
const TimingAnimation = require('../animations/TimingAnimation');
|
||||
|
||||
describe('Timing Animation', () => {
|
||||
it('should return array of 61 items', () => {
|
||||
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
* Portions Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails oncall+react_native
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
/**
|
||||
* BezierEasing - use bezier curve for transition easing function
|
||||
* https://github.com/gre/bezier-easing
|
||||
* @copyright 2014-2015 Gaetan Renaudeau. MIT License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const bezier = require('../bezier');
|
||||
|
||||
const identity = function(x) {
|
||||
return x;
|
||||
};
|
||||
|
||||
function assertClose(a, b, precision = 3) {
|
||||
expect(a).toBeCloseTo(b, precision);
|
||||
}
|
||||
|
||||
function makeAssertCloseWithPrecision(precision) {
|
||||
return function(a, b) {
|
||||
assertClose(a, b, precision);
|
||||
};
|
||||
}
|
||||
|
||||
function allEquals(be1, be2, samples, assertion) {
|
||||
if (!assertion) {
|
||||
assertion = assertClose;
|
||||
}
|
||||
for (let i = 0; i <= samples; ++i) {
|
||||
const x = i / samples;
|
||||
assertion(be1(x), be2(x));
|
||||
}
|
||||
}
|
||||
|
||||
function repeat(n) {
|
||||
return function(f) {
|
||||
for (let i = 0; i < n; ++i) {
|
||||
f();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
describe('bezier', function() {
|
||||
it('should be a function', function() {
|
||||
expect(typeof bezier === 'function').toBe(true);
|
||||
});
|
||||
it('should creates an object', function() {
|
||||
expect(typeof bezier(0, 0, 1, 1) === 'function').toBe(true);
|
||||
});
|
||||
it('should fail with wrong arguments', function() {
|
||||
expect(function() {
|
||||
bezier(0.5, 0.5, -5, 0.5);
|
||||
}).toThrow();
|
||||
expect(function() {
|
||||
bezier(0.5, 0.5, 5, 0.5);
|
||||
}).toThrow();
|
||||
expect(function() {
|
||||
bezier(-2, 0.5, 0.5, 0.5);
|
||||
}).toThrow();
|
||||
expect(function() {
|
||||
bezier(2, 0.5, 0.5, 0.5);
|
||||
}).toThrow();
|
||||
});
|
||||
describe('linear curves', function() {
|
||||
it('should be linear', function() {
|
||||
allEquals(bezier(0, 0, 1, 1), bezier(1, 1, 0, 0), 100);
|
||||
allEquals(bezier(0, 0, 1, 1), identity, 100);
|
||||
});
|
||||
});
|
||||
describe('common properties', function() {
|
||||
it('should be the right value at extremes', function() {
|
||||
repeat(10)(function() {
|
||||
const a = Math.random(),
|
||||
b = 2 * Math.random() - 0.5,
|
||||
c = Math.random(),
|
||||
d = 2 * Math.random() - 0.5;
|
||||
const easing = bezier(a, b, c, d);
|
||||
expect(easing(0)).toBe(0);
|
||||
expect(easing(1)).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('should approach the projected value of its x=y projected curve', function() {
|
||||
repeat(10)(function() {
|
||||
const a = Math.random(),
|
||||
b = Math.random(),
|
||||
c = Math.random(),
|
||||
d = Math.random();
|
||||
const easing = bezier(a, b, c, d);
|
||||
const projected = bezier(b, a, d, c);
|
||||
const composed = function(x) {
|
||||
return projected(easing(x));
|
||||
};
|
||||
allEquals(identity, composed, 100, makeAssertCloseWithPrecision(2));
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('two same instances', function() {
|
||||
it('should be strictly equals', function() {
|
||||
repeat(10)(function() {
|
||||
const a = Math.random(),
|
||||
b = 2 * Math.random() - 0.5,
|
||||
c = Math.random(),
|
||||
d = 2 * Math.random() - 0.5;
|
||||
allEquals(bezier(a, b, c, d), bezier(a, b, c, d), 100, 0);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('symmetric curves', function() {
|
||||
it('should have a central value y~=0.5 at x=0.5', function() {
|
||||
repeat(10)(function() {
|
||||
const a = Math.random(),
|
||||
b = 2 * Math.random() - 0.5,
|
||||
c = 1 - a,
|
||||
d = 1 - b;
|
||||
const easing = bezier(a, b, c, d);
|
||||
assertClose(easing(0.5), 0.5, 2);
|
||||
});
|
||||
});
|
||||
it('should be symmetrical', function() {
|
||||
repeat(10)(function() {
|
||||
const a = Math.random(),
|
||||
b = 2 * Math.random() - 0.5,
|
||||
c = 1 - a,
|
||||
d = 1 - b;
|
||||
const easing = bezier(a, b, c, d);
|
||||
const sym = function(x) {
|
||||
return 1 - easing(1 - x);
|
||||
};
|
||||
allEquals(easing, sym, 100, makeAssertCloseWithPrecision(2));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails oncall+react_native
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const createAnimatedComponent = require('../createAnimatedComponent');
|
||||
const createAnimatedComponentInjection = require('../createAnimatedComponentInjection');
|
||||
const React = require('react');
|
||||
|
||||
function injected<TProps: {...}, TInstance>(
|
||||
Component: React.AbstractComponent<TProps, TInstance>,
|
||||
): React.AbstractComponent<TProps, TInstance> {
|
||||
return createAnimatedComponent(Component);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
test('does nothing without injection', () => {
|
||||
expect(typeof createAnimatedComponent).toBe('function');
|
||||
expect(createAnimatedComponent).not.toBe(injected);
|
||||
});
|
||||
|
||||
test('injection overrides `createAnimatedComponent`', () => {
|
||||
createAnimatedComponentInjection.inject(injected);
|
||||
|
||||
expect(createAnimatedComponent).toBe(injected);
|
||||
});
|
||||
|
||||
test('injection errors if called too late', () => {
|
||||
jest.spyOn(console, 'error').mockReturnValue(undefined);
|
||||
|
||||
// Causes `createAnimatedComponent` to be initialized.
|
||||
createAnimatedComponent;
|
||||
|
||||
createAnimatedComponentInjection.inject(injected);
|
||||
|
||||
expect(createAnimatedComponent).not.toBe(injected);
|
||||
expect(console.error).toBeCalledWith(
|
||||
'createAnimatedComponentInjection: Must be called before `createAnimatedComponent`.',
|
||||
);
|
||||
});
|
||||
|
||||
test('injection errors if called more than once', () => {
|
||||
jest.spyOn(console, 'error').mockReturnValue(undefined);
|
||||
|
||||
createAnimatedComponentInjection.inject(injected);
|
||||
|
||||
expect(createAnimatedComponent).toBe(injected);
|
||||
expect(console.error).not.toBeCalled();
|
||||
|
||||
createAnimatedComponentInjection.inject(injected);
|
||||
|
||||
expect(console.error).toBeCalledWith(
|
||||
'createAnimatedComponentInjection: Cannot be called more than once.',
|
||||
);
|
||||
});
|
||||
+8
-9
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
@@ -10,10 +10,9 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import type {PlatformConfig} from '../AnimatedPlatformConfig';
|
||||
import type AnimatedValue from '../nodes/AnimatedValue';
|
||||
const NativeAnimatedHelper = require('../NativeAnimatedHelper');
|
||||
|
||||
import NativeAnimatedHelper from '../NativeAnimatedHelper';
|
||||
import type AnimatedValue from '../nodes/AnimatedValue';
|
||||
|
||||
export type EndResult = {finished: boolean, ...};
|
||||
export type EndCallback = (result: EndResult) => void;
|
||||
@@ -21,7 +20,6 @@ export type EndCallback = (result: EndResult) => void;
|
||||
export type AnimationConfig = {
|
||||
isInteraction?: boolean,
|
||||
useNativeDriver: boolean,
|
||||
platformConfig?: PlatformConfig,
|
||||
onComplete?: ?EndCallback,
|
||||
iterations?: number,
|
||||
};
|
||||
@@ -31,7 +29,7 @@ let startNativeAnimationNextId = 1;
|
||||
// Important note: start() and stop() will only be called at most once.
|
||||
// Once an animation has been stopped or finished its course, it will
|
||||
// not be reused.
|
||||
export default class Animation {
|
||||
class Animation {
|
||||
__active: boolean;
|
||||
__isInteraction: boolean;
|
||||
__nativeId: number;
|
||||
@@ -67,13 +65,12 @@ export default class Animation {
|
||||
startNativeAnimationWaitId,
|
||||
);
|
||||
try {
|
||||
const config = this.__getNativeAnimationConfig();
|
||||
animatedValue.__makeNative(config.platformConfig);
|
||||
animatedValue.__makeNative();
|
||||
this.__nativeId = NativeAnimatedHelper.generateNewAnimationId();
|
||||
NativeAnimatedHelper.API.startAnimatingNode(
|
||||
this.__nativeId,
|
||||
animatedValue.__getNativeTag(),
|
||||
config,
|
||||
this.__getNativeAnimationConfig(),
|
||||
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
|
||||
this.__debouncedOnEnd.bind(this),
|
||||
);
|
||||
@@ -86,3 +83,5 @@ export default class Animation {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Animation;
|
||||
+9
-11
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
@@ -10,13 +10,13 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import type {PlatformConfig} from '../AnimatedPlatformConfig';
|
||||
const Animation = require('./Animation');
|
||||
|
||||
const {shouldUseNativeDriver} = require('../NativeAnimatedHelper');
|
||||
|
||||
import type AnimatedValue from '../nodes/AnimatedValue';
|
||||
import type {AnimationConfig, EndCallback} from './Animation';
|
||||
|
||||
import NativeAnimatedHelper from '../NativeAnimatedHelper';
|
||||
import Animation from './Animation';
|
||||
|
||||
export type DecayAnimationConfig = {
|
||||
...AnimationConfig,
|
||||
velocity:
|
||||
@@ -35,7 +35,7 @@ export type DecayAnimationConfigSingle = {
|
||||
deceleration?: number,
|
||||
};
|
||||
|
||||
export default class DecayAnimation extends Animation {
|
||||
class DecayAnimation extends Animation {
|
||||
_startTime: number;
|
||||
_lastValue: number;
|
||||
_fromValue: number;
|
||||
@@ -44,14 +44,12 @@ export default class DecayAnimation extends Animation {
|
||||
_onUpdate: (value: number) => void;
|
||||
_animationFrame: any;
|
||||
_useNativeDriver: boolean;
|
||||
_platformConfig: ?PlatformConfig;
|
||||
|
||||
constructor(config: DecayAnimationConfigSingle) {
|
||||
super();
|
||||
this._deceleration = config.deceleration ?? 0.998;
|
||||
this._velocity = config.velocity;
|
||||
this._useNativeDriver = NativeAnimatedHelper.shouldUseNativeDriver(config);
|
||||
this._platformConfig = config.platformConfig;
|
||||
this._useNativeDriver = shouldUseNativeDriver(config);
|
||||
this.__isInteraction = config.isInteraction ?? !this._useNativeDriver;
|
||||
this.__iterations = config.iterations ?? 1;
|
||||
}
|
||||
@@ -59,7 +57,6 @@ export default class DecayAnimation extends Animation {
|
||||
__getNativeAnimationConfig(): {|
|
||||
deceleration: number,
|
||||
iterations: number,
|
||||
platformConfig: ?PlatformConfig,
|
||||
type: $TEMPORARY$string<'decay'>,
|
||||
velocity: number,
|
||||
|} {
|
||||
@@ -68,7 +65,6 @@ export default class DecayAnimation extends Animation {
|
||||
deceleration: this._deceleration,
|
||||
velocity: this._velocity,
|
||||
iterations: this.__iterations,
|
||||
platformConfig: this._platformConfig,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -122,3 +118,5 @@ export default class DecayAnimation extends Animation {
|
||||
this.__debouncedOnEnd({finished: false});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DecayAnimation;
|
||||
+18
-28
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
@@ -10,17 +10,17 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import type {PlatformConfig} from '../AnimatedPlatformConfig';
|
||||
import type AnimatedInterpolation from '../nodes/AnimatedInterpolation';
|
||||
import type AnimatedValue from '../nodes/AnimatedValue';
|
||||
import type AnimatedValueXY from '../nodes/AnimatedValueXY';
|
||||
import type {AnimationConfig, EndCallback} from './Animation';
|
||||
const AnimatedValue = require('../nodes/AnimatedValue');
|
||||
const AnimatedValueXY = require('../nodes/AnimatedValueXY');
|
||||
const AnimatedInterpolation = require('../nodes/AnimatedInterpolation');
|
||||
const Animation = require('./Animation');
|
||||
const SpringConfig = require('../SpringConfig');
|
||||
|
||||
import NativeAnimatedHelper from '../NativeAnimatedHelper';
|
||||
import AnimatedColor from '../nodes/AnimatedColor';
|
||||
import * as SpringConfig from '../SpringConfig';
|
||||
import Animation from './Animation';
|
||||
import invariant from 'invariant';
|
||||
const invariant = require('invariant');
|
||||
|
||||
const {shouldUseNativeDriver} = require('../NativeAnimatedHelper');
|
||||
|
||||
import type {AnimationConfig, EndCallback} from './Animation';
|
||||
|
||||
export type SpringAnimationConfig = {
|
||||
...AnimationConfig,
|
||||
@@ -33,15 +33,7 @@ export type SpringAnimationConfig = {
|
||||
...
|
||||
}
|
||||
| AnimatedValueXY
|
||||
| {
|
||||
r: number,
|
||||
g: number,
|
||||
b: number,
|
||||
a: number,
|
||||
...
|
||||
}
|
||||
| AnimatedColor
|
||||
| AnimatedInterpolation<number>,
|
||||
| AnimatedInterpolation,
|
||||
overshootClamping?: boolean,
|
||||
restDisplacementThreshold?: number,
|
||||
restSpeedThreshold?: number,
|
||||
@@ -64,7 +56,7 @@ export type SpringAnimationConfig = {
|
||||
|
||||
export type SpringAnimationConfigSingle = {
|
||||
...AnimationConfig,
|
||||
toValue: number,
|
||||
toValue: number | AnimatedValue | AnimatedInterpolation,
|
||||
overshootClamping?: boolean,
|
||||
restDisplacementThreshold?: number,
|
||||
restSpeedThreshold?: number,
|
||||
@@ -79,7 +71,7 @@ export type SpringAnimationConfigSingle = {
|
||||
delay?: number,
|
||||
};
|
||||
|
||||
export default class SpringAnimation extends Animation {
|
||||
class SpringAnimation extends Animation {
|
||||
_overshootClamping: boolean;
|
||||
_restDisplacementThreshold: number;
|
||||
_restSpeedThreshold: number;
|
||||
@@ -87,7 +79,7 @@ export default class SpringAnimation extends Animation {
|
||||
_startPosition: number;
|
||||
_lastPosition: number;
|
||||
_fromValue: number;
|
||||
_toValue: number;
|
||||
_toValue: any;
|
||||
_stiffness: number;
|
||||
_damping: number;
|
||||
_mass: number;
|
||||
@@ -100,7 +92,6 @@ export default class SpringAnimation extends Animation {
|
||||
_onUpdate: (value: number) => void;
|
||||
_animationFrame: any;
|
||||
_useNativeDriver: boolean;
|
||||
_platformConfig: ?PlatformConfig;
|
||||
|
||||
constructor(config: SpringAnimationConfigSingle) {
|
||||
super();
|
||||
@@ -112,8 +103,7 @@ export default class SpringAnimation extends Animation {
|
||||
this._lastVelocity = config.velocity ?? 0;
|
||||
this._toValue = config.toValue;
|
||||
this._delay = config.delay ?? 0;
|
||||
this._useNativeDriver = NativeAnimatedHelper.shouldUseNativeDriver(config);
|
||||
this._platformConfig = config.platformConfig;
|
||||
this._useNativeDriver = shouldUseNativeDriver(config);
|
||||
this.__isInteraction = config.isInteraction ?? !this._useNativeDriver;
|
||||
this.__iterations = config.iterations ?? 1;
|
||||
|
||||
@@ -172,7 +162,6 @@ export default class SpringAnimation extends Animation {
|
||||
initialVelocity: number,
|
||||
iterations: number,
|
||||
mass: number,
|
||||
platformConfig: ?PlatformConfig,
|
||||
overshootClamping: boolean,
|
||||
restDisplacementThreshold: number,
|
||||
restSpeedThreshold: number,
|
||||
@@ -191,7 +180,6 @@ export default class SpringAnimation extends Animation {
|
||||
initialVelocity: this._initialVelocity ?? this._lastVelocity,
|
||||
toValue: this._toValue,
|
||||
iterations: this.__iterations,
|
||||
platformConfig: this._platformConfig,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -368,3 +356,5 @@ export default class SpringAnimation extends Animation {
|
||||
this.__debouncedOnEnd({finished: false});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SpringAnimation;
|
||||
+21
-25
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
@@ -10,18 +10,16 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import type {PlatformConfig} from '../AnimatedPlatformConfig';
|
||||
import type {RgbaValue} from '../nodes/AnimatedColor';
|
||||
import type AnimatedInterpolation from '../nodes/AnimatedInterpolation';
|
||||
import type AnimatedValue from '../nodes/AnimatedValue';
|
||||
import type AnimatedValueXY from '../nodes/AnimatedValueXY';
|
||||
const AnimatedValue = require('../nodes/AnimatedValue');
|
||||
const AnimatedValueXY = require('../nodes/AnimatedValueXY');
|
||||
const AnimatedInterpolation = require('../nodes/AnimatedInterpolation');
|
||||
const Animation = require('./Animation');
|
||||
|
||||
const {shouldUseNativeDriver} = require('../NativeAnimatedHelper');
|
||||
|
||||
import type {AnimationConfig, EndCallback} from './Animation';
|
||||
|
||||
import NativeAnimatedHelper from '../NativeAnimatedHelper';
|
||||
import AnimatedColor from '../nodes/AnimatedColor';
|
||||
import Animation from './Animation';
|
||||
|
||||
export type TimingAnimationConfig = $ReadOnly<{
|
||||
export type TimingAnimationConfig = {
|
||||
...AnimationConfig,
|
||||
toValue:
|
||||
| number
|
||||
@@ -32,35 +30,34 @@ export type TimingAnimationConfig = $ReadOnly<{
|
||||
...
|
||||
}
|
||||
| AnimatedValueXY
|
||||
| RgbaValue
|
||||
| AnimatedColor
|
||||
| AnimatedInterpolation<number>,
|
||||
| AnimatedInterpolation,
|
||||
easing?: (value: number) => number,
|
||||
duration?: number,
|
||||
delay?: number,
|
||||
}>;
|
||||
};
|
||||
|
||||
export type TimingAnimationConfigSingle = $ReadOnly<{
|
||||
export type TimingAnimationConfigSingle = {
|
||||
...AnimationConfig,
|
||||
toValue: number,
|
||||
toValue: number | AnimatedValue | AnimatedInterpolation,
|
||||
easing?: (value: number) => number,
|
||||
duration?: number,
|
||||
delay?: number,
|
||||
}>;
|
||||
};
|
||||
|
||||
let _easeInOut;
|
||||
function easeInOut() {
|
||||
if (!_easeInOut) {
|
||||
const Easing = require('../Easing').default;
|
||||
const Easing = require('../Easing');
|
||||
// $FlowFixMe[method-unbinding]
|
||||
_easeInOut = Easing.inOut(Easing.ease);
|
||||
}
|
||||
return _easeInOut;
|
||||
}
|
||||
|
||||
export default class TimingAnimation extends Animation {
|
||||
class TimingAnimation extends Animation {
|
||||
_startTime: number;
|
||||
_fromValue: number;
|
||||
_toValue: number;
|
||||
_toValue: any;
|
||||
_duration: number;
|
||||
_delay: number;
|
||||
_easing: (value: number) => number;
|
||||
@@ -68,7 +65,6 @@ export default class TimingAnimation extends Animation {
|
||||
_animationFrame: any;
|
||||
_timeout: any;
|
||||
_useNativeDriver: boolean;
|
||||
_platformConfig: ?PlatformConfig;
|
||||
|
||||
constructor(config: TimingAnimationConfigSingle) {
|
||||
super();
|
||||
@@ -77,8 +73,7 @@ export default class TimingAnimation extends Animation {
|
||||
this._duration = config.duration ?? 500;
|
||||
this._delay = config.delay ?? 0;
|
||||
this.__iterations = config.iterations ?? 1;
|
||||
this._useNativeDriver = NativeAnimatedHelper.shouldUseNativeDriver(config);
|
||||
this._platformConfig = config.platformConfig;
|
||||
this._useNativeDriver = shouldUseNativeDriver(config);
|
||||
this.__isInteraction = config.isInteraction ?? !this._useNativeDriver;
|
||||
}
|
||||
|
||||
@@ -95,7 +90,6 @@ export default class TimingAnimation extends Animation {
|
||||
frames,
|
||||
toValue: this._toValue,
|
||||
iterations: this.__iterations,
|
||||
platformConfig: this._platformConfig,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -170,3 +164,5 @@ export default class TimingAnimation extends Animation {
|
||||
this.__debouncedOnEnd({finished: false});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TimingAnimation;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user