Update prettier and reformat source code

This commit is contained in:
Nicolas Gallagher
2022-07-03 17:37:19 -07:00
parent c8f6db8bfc
commit 174b8f505a
162 changed files with 2950 additions and 1370 deletions

View File

@@ -3,7 +3,10 @@
const babelConfig = require('./babel.config.js');
module.exports = {
coveragePathIgnorePatterns: ['/node_modules/', '<rootDir>/packages/react-native-web/src/vendor/'],
coveragePathIgnorePatterns: [
'/node_modules/',
'<rootDir>/packages/react-native-web/src/vendor/'
],
fakeTimers: {
enableGlobally: true
},

View File

@@ -3,7 +3,10 @@
const babelConfig = require('./babel.config.js');
module.exports = {
coveragePathIgnorePatterns: ['/node_modules/', '<rootDir>/packages/react-native-web/src/vendor/'],
coveragePathIgnorePatterns: [
'/node_modules/',
'<rootDir>/packages/react-native-web/src/vendor/'
],
fakeTimers: {
enableGlobally: true
},

85
package-lock.json generated
View File

@@ -47,7 +47,7 @@
"lint-staged": "^10.5.4",
"minimist": "^1.2.6",
"npm-run-all": "^4.1.3",
"prettier": "^2.2.1"
"prettier": "^2.7.1"
}
},
"node_modules/@11ty/dependency-tree": {
@@ -3802,14 +3802,13 @@
}
},
"node_modules/babel-loader": {
"version": "8.2.2",
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.2.tgz",
"integrity": "sha512-JvTd0/D889PQBtUXJ2PXaKU/pjZDMtHA9V2ecm+eNRmmBCMR09a+fmpGTNwnJtFmFl5Ei7Vy47LjBb+L0wQ99g==",
"version": "8.2.5",
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz",
"integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"find-cache-dir": "^3.3.1",
"loader-utils": "^1.4.0",
"loader-utils": "^2.0.0",
"make-dir": "^3.1.0",
"schema-utils": "^2.6.5"
},
@@ -3821,33 +3820,6 @@
"webpack": ">=2"
}
},
"node_modules/babel-loader/node_modules/json5": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"dev": true,
"license": "MIT",
"dependencies": {
"minimist": "^1.2.0"
},
"bin": {
"json5": "lib/cli.js"
}
},
"node_modules/babel-loader/node_modules/loader-utils": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
"integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
"dev": true,
"license": "MIT",
"dependencies": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^1.0.1"
},
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/babel-loader/node_modules/schema-utils": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz",
@@ -11632,16 +11604,18 @@
}
},
"node_modules/prettier": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz",
"integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==",
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
"integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
"dev": true,
"license": "MIT",
"bin": {
"prettier": "bin-prettier.js"
},
"engines": {
"node": ">=10.13.0"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/pretty": {
@@ -14662,7 +14636,7 @@
"react-native-web": "0.18.4"
},
"devDependencies": {
"babel-loader": "^8.2.2",
"babel-loader": "^8.2.5",
"babel-plugin-react-native-web": "0.18.4",
"css-loader": "^6.7.1",
"style-loader": "^3.3.1",
@@ -17560,36 +17534,17 @@
}
},
"babel-loader": {
"version": "8.2.2",
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.2.tgz",
"integrity": "sha512-JvTd0/D889PQBtUXJ2PXaKU/pjZDMtHA9V2ecm+eNRmmBCMR09a+fmpGTNwnJtFmFl5Ei7Vy47LjBb+L0wQ99g==",
"version": "8.2.5",
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz",
"integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==",
"dev": true,
"requires": {
"find-cache-dir": "^3.3.1",
"loader-utils": "^1.4.0",
"loader-utils": "^2.0.0",
"make-dir": "^3.1.0",
"schema-utils": "^2.6.5"
},
"dependencies": {
"json5": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"dev": true,
"requires": {
"minimist": "^1.2.0"
}
},
"loader-utils": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
"integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
"dev": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^1.0.1"
}
},
"schema-utils": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz",
@@ -17757,7 +17712,7 @@
"benchmarks": {
"version": "file:packages/benchmarks",
"requires": {
"babel-loader": "^8.2.2",
"babel-loader": "^8.2.5",
"babel-plugin-react-native-web": "0.18.4",
"classnames": "^2.3.1",
"css-loader": "^6.7.1",
@@ -23131,9 +23086,9 @@
"dev": true
},
"prettier": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz",
"integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==",
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
"integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
"dev": true
},
"pretty": {

View File

@@ -57,7 +57,7 @@
"lint-staged": "^10.5.4",
"minimist": "^1.2.6",
"npm-run-all": "^4.1.3",
"prettier": "^2.2.1"
"prettier": "^2.7.1"
},
"workspaces": [
"packages/react-native-web",
@@ -81,7 +81,6 @@
]
},
"prettier": {
"printWidth": 100,
"singleQuote": true,
"trailingComma": "none"
},

View File

@@ -4,7 +4,8 @@ const isCommonJS = (opts) => opts.commonjs === true;
const getDistLocation = (importName, opts) => {
const format = isCommonJS(opts) ? 'cjs/' : '';
const internalName = importName === 'unstable_createElement' ? 'createElement' : importName;
const internalName =
importName === 'unstable_createElement' ? 'createElement' : importName;
if (internalName === 'index') {
return `react-native-web/dist/${format}index`;
} else if (internalName && moduleMap[internalName]) {
@@ -24,7 +25,8 @@ const isReactNativeRequire = (t, node) => {
t.isIdentifier(init.callee) &&
init.callee.name === 'require' &&
init.arguments.length === 1 &&
(init.arguments[0].value === 'react-native' || init.arguments[0].value === 'react-native-web')
(init.arguments[0].value === 'react-native' ||
init.arguments[0].value === 'react-native-web')
);
};
@@ -48,7 +50,11 @@ module.exports = function ({ types: t }) {
if (distLocation) {
return t.importDeclaration(
[t.importDefaultSpecifier(t.identifier(specifier.local.name))],
[
t.importDefaultSpecifier(
t.identifier(specifier.local.name)
)
],
t.stringLiteral(distLocation)
);
}
@@ -76,7 +82,12 @@ module.exports = function ({ types: t }) {
if (distLocation) {
return t.exportNamedDeclaration(
null,
[t.exportSpecifier(t.identifier('default'), t.identifier(exportName))],
[
t.exportSpecifier(
t.identifier('default'),
t.identifier(exportName)
)
],
t.stringLiteral(distLocation)
);
}
@@ -98,13 +109,18 @@ module.exports = function ({ types: t }) {
if (t.isObjectPattern(id)) {
const imports = id.properties
.map((identifier) => {
const distLocation = getDistLocation(identifier.key.name, state.opts);
const distLocation = getDistLocation(
identifier.key.name,
state.opts
);
if (distLocation) {
return t.variableDeclaration(path.node.kind, [
t.variableDeclarator(
t.identifier(identifier.value.name),
t.memberExpression(
t.callExpression(t.identifier('require'), [t.stringLiteral(distLocation)]),
t.callExpression(t.identifier('require'), [
t.stringLiteral(distLocation)
]),
t.identifier('default')
)
)

View File

@@ -16,7 +16,7 @@
"react-native-web": "0.18.4"
},
"devDependencies": {
"babel-loader": "^8.2.2",
"babel-loader": "^8.2.5",
"babel-plugin-react-native-web": "0.18.4",
"css-loader": "^6.7.1",
"style-loader": "^3.3.1",

View File

@@ -1,5 +1,11 @@
import Benchmark from './Benchmark';
import { Picker, StyleSheet, ScrollView, TouchableOpacity, View } from 'react-native';
import {
Picker,
StyleSheet,
ScrollView,
TouchableOpacity,
View
} from 'react-native';
import React, { Component } from 'react';
import Button from './Button';
import { IconClear, IconEye } from './Icons';
@@ -26,9 +32,12 @@ export default class App extends Component {
render() {
const { tests } = this.props;
const { currentBenchmarkName, status, currentLibraryName, results } = this.state;
const currentImplementation = tests[currentBenchmarkName][currentLibraryName];
const { Component, Provider, getComponentProps, sampleCount } = currentImplementation;
const { currentBenchmarkName, status, currentLibraryName, results } =
this.state;
const currentImplementation =
tests[currentBenchmarkName][currentLibraryName];
const { Component, Provider, getComponentProps, sampleCount } =
currentImplementation;
return (
<Layout
@@ -45,9 +54,15 @@ export default class App extends Component {
selectedValue={currentLibraryName}
style={styles.picker}
>
{Object.keys(tests[currentBenchmarkName]).map((libraryName) => (
<Picker.Item key={libraryName} label={libraryName} value={libraryName} />
))}
{Object.keys(tests[currentBenchmarkName]).map(
(libraryName) => (
<Picker.Item
key={libraryName}
label={libraryName}
value={libraryName}
/>
)
)}
</Picker>
</View>
<View style={{ width: 1, backgroundColor: colors.fadedGray }} />
@@ -185,24 +200,27 @@ export default class App extends Component {
}
};
_createHandleComplete = ({ benchmarkName, libraryName, sampleCount }) => (results) => {
this.setState(
(state) => ({
results: state.results.concat([
{
...results,
benchmarkName,
libraryName,
libraryVersion: this.props.tests[benchmarkName][libraryName].version
}
]),
status: 'complete'
}),
this._scrollToEnd
);
// console.log(results);
// console.log(results.samples.map(sample => sample.elapsed.toFixed(1)).join('\n'));
};
_createHandleComplete =
({ benchmarkName, libraryName, sampleCount }) =>
(results) => {
this.setState(
(state) => ({
results: state.results.concat([
{
...results,
benchmarkName,
libraryName,
libraryVersion:
this.props.tests[benchmarkName][libraryName].version
}
]),
status: 'complete'
}),
this._scrollToEnd
);
// console.log(results);
// console.log(results.samples.map(sample => sample.elapsed.toFixed(1)).join('\n'));
};
_handleClear = () => {
this.setState(() => ({ results: [] }));

View File

@@ -13,7 +13,11 @@ import * as Timing from './timing';
import React, { Component } from 'react';
import { getMean, getMedian, getStdDev } from './math';
import type { BenchResultsType, FullSampleTimingType, SampleTimingType } from './types';
import type {
BenchResultsType,
FullSampleTimingType,
SampleTimingType
} from './types';
export const BenchmarkType = {
MOUNT: 'mount',
@@ -21,7 +25,10 @@ export const BenchmarkType = {
UNMOUNT: 'unmount'
};
const shouldRender = (cycle: number, type: $Values<typeof BenchmarkType>): boolean => {
const shouldRender = (
cycle: number,
type: $Values<typeof BenchmarkType>
): boolean => {
switch (type) {
// Render every odd iteration (first, third, etc)
// Mounts and unmounts the component
@@ -36,7 +43,10 @@ const shouldRender = (cycle: number, type: $Values<typeof BenchmarkType>): boole
}
};
const shouldRecord = (cycle: number, type: $Values<typeof BenchmarkType>): boolean => {
const shouldRecord = (
cycle: number,
type: $Values<typeof BenchmarkType>
): boolean => {
switch (type) {
// Record every odd iteration (when mounted: first, third, etc)
case BenchmarkType.MOUNT:
@@ -91,7 +101,10 @@ type BenchmarkStateType = {
* Benchmark
* TODO: documentation
*/
export default class Benchmark extends Component<BenchmarkPropsType, BenchmarkStateType> {
export default class Benchmark extends Component<
BenchmarkPropsType,
BenchmarkStateType
> {
_raf: ?Function;
_startTime: number;
_samples: Array<SampleTimingType>;
@@ -125,11 +138,16 @@ export default class Benchmark extends Component<BenchmarkPropsType, BenchmarkSt
componentWillReceiveProps(nextProps: BenchmarkPropsType) {
if (nextProps) {
this.setState((state) => ({ componentProps: nextProps.getComponentProps(state.cycle) }));
this.setState((state) => ({
componentProps: nextProps.getComponentProps(state.cycle)
}));
}
}
componentWillUpdate(nextProps: BenchmarkPropsType, nextState: BenchmarkStateType) {
componentWillUpdate(
nextProps: BenchmarkPropsType,
nextState: BenchmarkStateType
) {
if (nextState.running && !this.state.running) {
this._startTime = Timing.now();
}
@@ -154,7 +172,10 @@ export default class Benchmark extends Component<BenchmarkPropsType, BenchmarkSt
if (running) {
const now = Timing.now();
if (!isDone(cycle, sampleCount, type) && now - this._startTime < timeout) {
if (
!isDone(cycle, sampleCount, type) &&
now - this._startTime < timeout
) {
this._handleCycleComplete();
} else {
this._handleComplete(now);
@@ -174,7 +195,9 @@ export default class Benchmark extends Component<BenchmarkPropsType, BenchmarkSt
if (running && shouldRecord(cycle, type)) {
this._samples[cycle] = { scriptingStart: Timing.now() };
}
return running && shouldRender(cycle, type) ? <Component {...componentProps} /> : null;
return running && shouldRender(cycle, type) ? (
<Component {...componentProps} />
) : null;
}
start() {
@@ -209,7 +232,12 @@ export default class Benchmark extends Component<BenchmarkPropsType, BenchmarkSt
return this._samples.reduce(
(
memo: Array<FullSampleTimingType>,
{ scriptingStart, scriptingEnd, layoutStart, layoutEnd }: SampleTimingType
{
scriptingStart,
scriptingEnd,
layoutStart,
layoutEnd
}: SampleTimingType
): Array<FullSampleTimingType> => {
memo.push({
start: scriptingStart,
@@ -232,12 +260,16 @@ export default class Benchmark extends Component<BenchmarkPropsType, BenchmarkSt
this.setState(() => ({ running: false, cycle: 0 }));
const runTime = endTime - this._startTime;
const sortedElapsedTimes = samples.map(({ start, end }) => end - start).sort(sortNumbers);
const sortedElapsedTimes = samples
.map(({ start, end }) => end - start)
.sort(sortNumbers);
const sortedScriptingElapsedTimes = samples
.map(({ scriptingStart, scriptingEnd }) => scriptingEnd - scriptingStart)
.sort(sortNumbers);
const sortedLayoutElapsedTimes = samples
.map(({ layoutStart, layoutEnd }) => (layoutEnd || 0) - (layoutStart || 0))
.map(
({ layoutStart, layoutEnd }) => (layoutEnd || 0) - (layoutStart || 0)
)
.sort(sortNumbers);
onComplete({

View File

@@ -23,5 +23,7 @@ export const getMedian = (values: ValuesType): number => {
}
const numbers = values.sort((a: number, b: number) => a - b);
return (numbers[(numbers.length - 1) >> 1] + numbers[numbers.length >> 1]) / 2;
return (
(numbers[(numbers.length - 1) >> 1] + numbers[numbers.length >> 1]) / 2
);
};

View File

@@ -30,7 +30,9 @@ export default class Button extends Component<*> {
]}
testID={testID}
>
<Text style={[styles.text, textStyle, disabled && styles.textDisabled]}>{title}</Text>
<Text style={[styles.text, textStyle, disabled && styles.textDisabled]}>
{title}
</Text>
</TouchableHighlight>
);
}

View File

@@ -1,5 +1,8 @@
import React, { Fragment } from 'react';
import { unstable_createElement as createElement, StyleSheet } from 'react-native';
import {
unstable_createElement as createElement,
StyleSheet
} from 'react-native';
const styles = StyleSheet.create({
root: {

View File

@@ -11,8 +11,13 @@ export default class Layout extends Component {
const { viewPanel, actionPanel, listPanel } = this.props;
const { widescreen } = this.state;
return (
<View onLayout={this._handleLayout} style={[styles.root, widescreen && styles.row]}>
<View style={[widescreen ? styles.grow : styles.stackPanel, styles.layer]}>
<View
onLayout={this._handleLayout}
style={[styles.root, widescreen && styles.row]}
>
<View
style={[widescreen ? styles.grow : styles.stackPanel, styles.layer]}
>
{viewPanel}
</View>
<View style={styles.grow}>

View File

@@ -13,7 +13,9 @@ class AppText extends React.Component {
render() {
const { style, ...rest } = this.props;
const { isInAParentText } = this.context;
return <Text {...rest} style={[!isInAParentText && styles.baseText, style]} />;
return (
<Text {...rest} style={[!isInAParentText && styles.baseText, style]} />
);
}
}

View File

@@ -6,7 +6,10 @@ const baseUnit = 1.3125;
const { canUseDOM } = ExecutionEnvironment;
const createPlatformLength = (multiplier) =>
Platform.select({ web: `${multiplier}rem`, default: multiplier * baseFontSize });
Platform.select({
web: `${multiplier}rem`,
default: multiplier * baseFontSize
});
/**
* Exported variables
@@ -52,8 +55,7 @@ export const colors = {
export const fontFamilies = {
normal: 'System',
japan: Platform.select({
web:
'Arial, "ヒラギノ角ゴ Pro W3", "Hiragino Kaku Gothic Pro", Osaka, "メイリオ", Meiryo, " Pゴシック", "MS PGothic", sans-serif',
web: 'Arial, "ヒラギノ角ゴ Pro W3", "Hiragino Kaku Gothic Pro", Osaka, "メイリオ", Meiryo, " Pゴシック", "MS PGothic", sans-serif',
default: 'System'
}),
rtl: Platform.select({ web: 'Tahoma, Arial, sans-serif', default: 'System' })
@@ -89,7 +91,8 @@ if (Platform.OS === 'web' && canUseDOM) {
const { medium, large } = breakpoints;
const htmlElement = document.documentElement;
const setFontSize = (width) => {
const fontSize = width > medium ? (width > large ? '18px' : '17px') : '16px';
const fontSize =
width > medium ? (width > large ? '18px' : '17px') : '16px';
if (htmlElement) {
htmlElement.style.fontSize = fontSize;
}

View File

@@ -1,6 +1,10 @@
import { BenchmarkType } from '../app/Benchmark';
import React from 'react';
import { interpolatePurples, interpolateBuPu, interpolateRdPu } from 'd3-scale-chromatic';
import {
interpolatePurples,
interpolateBuPu,
interpolateRdPu
} from 'd3-scale-chromatic';
const targetSize = 10;
@@ -37,7 +41,12 @@ class SierpinskiTriangle extends React.Component {
// introduce randomness to ensure that repeated runs don't produce the same colors
const color = fn((renderCount * Math.random()) / 20);
return (
<Dot color={color} size={targetSize} x={x - targetSize / 2} y={y - targetSize / 2} />
<Dot
color={color}
size={targetSize}
x={x - targetSize / 2}
y={y - targetSize / 2}
/>
);
}
@@ -72,7 +81,9 @@ class SierpinskiTriangle extends React.Component {
</React.Fragment>
);
} else {
return <span style={{ color: 'white' }}>No implementation available</span>;
return (
<span style={{ color: 'white' }}>No implementation available</span>
);
}
}
}

View File

@@ -3,7 +3,13 @@ import React from 'react';
import View from './View';
import styles from './box-styles.css';
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => (
const Box = ({
color,
fixed = false,
layout = 'column',
outer = false,
...other
}) => (
<View
{...other}
className={classnames(styles[`color${color}`], {

View File

@@ -5,7 +5,9 @@ import styles from './view-styles.css';
class View extends React.Component {
render() {
const props = this.props;
return <div {...props} className={classnames(styles.initial, props.className)} />;
return (
<div {...props} className={classnames(styles.initial, props.className)} />
);
}
}

View File

@@ -1,7 +1,13 @@
import React from 'react';
import View from './View';
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => (
const Box = ({
color,
fixed = false,
layout = 'column',
outer = false,
...other
}) => (
<View
{...other}
style={{

View File

@@ -1,7 +1,13 @@
import React from 'react';
import { StyleSheet, View } from 'react-native';
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => (
const Box = ({
color,
fixed = false,
layout = 'column',
outer = false,
...other
}) => (
<View
{...other}
style={[

View File

@@ -1,4 +1,7 @@
import { unstable_createElement as createElement, StyleSheet } from 'react-native';
import {
unstable_createElement as createElement,
StyleSheet
} from 'react-native';
const Dot = ({ size, x, y, children, color }) =>
createElement('div', {

View File

@@ -1,7 +1,13 @@
import React from 'react';
import View from './View';
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => (
const Box = ({
color,
fixed = false,
layout = 'column',
outer = false,
...other
}) => (
<View
{...other}
style={[

View File

@@ -2,7 +2,13 @@ import React from 'react';
import View from './View';
import { StyleSheet } from 'react-native-web';
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => (
const Box = ({
color,
fixed = false,
layout = 'column',
outer = false,
...other
}) => (
<View
{...other}
style={[

View File

@@ -12,7 +12,13 @@ const packageNames = Object.keys(implementations);
const createTestBlock = (fn) => {
return packageNames.reduce((testSetups, packageName) => {
const { name, components, version } = implementations[packageName];
const { Component, getComponentProps, sampleCount, Provider, benchmarkType } = fn(components);
const {
Component,
getComponentProps,
sampleCount,
Provider,
benchmarkType
} = fn(components);
testSetups[packageName] = {
Component,
@@ -31,14 +37,26 @@ const tests = {
'Mount deep tree': createTestBlock((components) => ({
benchmarkType: 'mount',
Component: Tree,
getComponentProps: () => ({ breadth: 2, components, depth: 7, id: 0, wrap: 1 }),
getComponentProps: () => ({
breadth: 2,
components,
depth: 7,
id: 0,
wrap: 1
}),
Provider: components.Provider,
sampleCount: 50
})),
'Mount wide tree': createTestBlock((components) => ({
benchmarkType: 'mount',
Component: Tree,
getComponentProps: () => ({ breadth: 6, components, depth: 3, id: 0, wrap: 2 }),
getComponentProps: () => ({
breadth: 6,
components,
depth: 3,
id: 0,
wrap: 2
}),
Provider: components.Provider,
sampleCount: 50
})),

View File

@@ -1,4 +1,5 @@
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const BundleAnalyzerPlugin =
require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const path = require('path');
const appDirectory = path.resolve(__dirname);

View File

@@ -158,7 +158,9 @@ export function contextmenu(target, defaultPayload = {}) {
}
dispatch(domEvents.mousedown({ ...payload, ctrlKey }));
if (platform.get() === 'mac') {
dispatch(domEvents.contextmenu({ button, buttons, ctrlKey, preventDefault }));
dispatch(
domEvents.contextmenu({ button, buttons, ctrlKey, preventDefault })
);
}
} else {
const button = buttonType.secondary;
@@ -167,7 +169,9 @@ export function contextmenu(target, defaultPayload = {}) {
dispatch(domEvents.pointerdown({ ...payload, button, buttons }));
}
dispatch(domEvents.mousedown({ ...payload, button, buttons }));
dispatch(domEvents.contextmenu({ ...payload, button, buttons, preventDefault }));
dispatch(
domEvents.contextmenu({ ...payload, button, buttons, preventDefault })
);
}
}
}
@@ -323,7 +327,9 @@ export function pointermove(target, defaultPayload) {
if (pointerType === 'mouse') {
if (hasPointerEvent()) {
dispatch(domEvents.pointermove({ pressure: 0.5, button: -1, ...payload }));
dispatch(
domEvents.pointermove({ pressure: 0.5, button: -1, ...payload })
);
}
dispatch(domEvents.mousemove(payload));
} else {
@@ -354,7 +360,8 @@ export function pointerup(target, defaultPayload) {
};
const isPrimaryButton = payload.button === buttonType.primary;
const isContextMenuAction = platform.get() === 'mac' && payload.ctrlKey === true;
const isContextMenuAction =
platform.get() === 'mac' && payload.ctrlKey === true;
if (pointerType === 'mouse') {
if (hasPointerEvent()) {

View File

@@ -8,7 +8,12 @@
'use strict';
import createEvent from './createEvent';
import { buttonType, buttonsType, defaultPointerSize, defaultBrowserChromeSize } from './constants';
import {
buttonType,
buttonsType,
defaultPointerSize,
defaultBrowserChromeSize
} from './constants';
/**
* Native event object mocks for higher-level events.
@@ -299,9 +304,13 @@ export function lostpointercapture(payload) {
export function mousedown(payload) {
// The value of 'button' and 'buttons' for 'mousedown' must not be none.
const button =
payload != null && payload.button !== buttonType.none ? payload.button : buttonType.primary;
payload != null && payload.button !== buttonType.none
? payload.button
: buttonType.primary;
const buttons =
payload != null && payload.buttons !== buttonsType.none ? payload.buttons : buttonsType.primary;
payload != null && payload.buttons !== buttonsType.none
? payload.buttons
: buttonsType.primary;
return createMouseEvent('mousedown', {
...payload,

View File

@@ -24,7 +24,9 @@ export function describeWithPointerEvent(message, describeFn) {
}
export function testWithPointerType(message, testFn) {
const table = hasPointerEvent() ? ['mouse', 'touch', 'pen'] : ['mouse', 'touch'];
const table = hasPointerEvent()
? ['mouse', 'touch', 'pen']
: ['mouse', 'touch'];
test.each(table)(`${message}: %s`, (pointerType) => {
testFn(pointerType);
});

View File

@@ -34,7 +34,9 @@ function markdownitTagToClass(md, mapping = {}) {
}
});
}
md.core.ruler.push('markdownit-tag-to-class', (state) => parseTokens(state.tokens));
md.core.ruler.push('markdownit-tag-to-class', (state) =>
parseTokens(state.tokens)
);
}
module.exports = function (eleventyConfig) {

View File

@@ -29,7 +29,11 @@ function setupNavigationDrawer() {
navopen = false;
}
document.addEventListener('click', (e) => {
if (navopen && !navigation.contains(e.target) && !showbtn.contains(e.target)) {
if (
navopen &&
!navigation.contains(e.target) &&
!showbtn.contains(e.target)
) {
closeNavigation();
}
});

View File

@@ -18,8 +18,16 @@ export default function ActivityIndicatorPage() {
<Example title="ActivityIndicator">
<View style={styles.row}>
<ActivityIndicator style={styles.item} />
<ActivityIndicator animating={false} hidesWhenStopped={false} style={styles.item} />
<ActivityIndicator animating={animating} hidesWhenStopped={false} style={styles.item} />
<ActivityIndicator
animating={false}
hidesWhenStopped={false}
style={styles.item}
/>
<ActivityIndicator
animating={animating}
hidesWhenStopped={false}
style={styles.item}
/>
</View>
<View style={styles.row}>
<ActivityIndicator color="#1DA1F2" size="small" style={styles.item} />

View File

@@ -27,7 +27,8 @@ export default function AppStatePage() {
return (
<Example title="AppState">
<Text style={{ marginTop: '1rem' }}>
AppState.currentState: <Text style={{ fontWeight: 'bold' }}>{state.currentState}</Text>
AppState.currentState:{' '}
<Text style={{ fontWeight: 'bold' }}>{state.currentState}</Text>
</Text>
<Text>Active count: {state.active}</Text>
<Text>Background count: {state.background}</Text>

View File

@@ -44,7 +44,11 @@ export default function CheckboxPage() {
<CheckBox color="#E0245E" value={true} />
</View>
<View style={styles.row}>
<CheckBox color="#1DA1F2" style={{ height: 32, width: 32 }} value={checked} />
<CheckBox
color="#1DA1F2"
style={{ height: 32, width: 32 }}
value={checked}
/>
</View>
</Example>
);

View File

@@ -4,7 +4,9 @@ import Example from '../../shared/example';
export default function ClipboardPage() {
const setString = () => {
const success = Clipboard.setString('This text was copied to the clipboard by React Native');
const success = Clipboard.setString(
'This text was copied to the clipboard by React Native'
);
console.log(`Clipboard.setString success? ${success}`);
};

View File

@@ -23,7 +23,9 @@ export default function DimensionsPage() {
<Text style={{ marginVertical: '1em' }} suppressHydrationWarnings={true}>
window: {JSON.stringify(windowDims, null, 2)}
</Text>
<Text suppressHydrationWarnings={true}>screen: {JSON.stringify(screenDims, null, 2)}</Text>
<Text suppressHydrationWarnings={true}>
screen: {JSON.stringify(screenDims, null, 2)}
</Text>
</Example>
);
}

View File

@@ -3,7 +3,10 @@ import { StyleSheet } from 'react-native';
import Example from '../../shared/example';
import { FlatList, Text, TouchableOpacity, View } from 'react-native-web';
const multiSelectData = ['First', 'Second', 'Third'].map((title, id) => ({ id, title }));
const multiSelectData = ['First', 'Second', 'Third'].map((title, id) => ({
id,
title
}));
const minimalData = ['a', 'b', 'c', 'd', 'e'].map((key) => ({ key }));
const pageExamplesData = ['minimal', 'multiSelect'].map((type) => ({ type }));
@@ -17,7 +20,9 @@ class MyListItem extends React.PureComponent {
return (
<TouchableOpacity onPress={this._onPress}>
<View>
<Text style={[styles.listItemText, { color: textColor }]}>{this.props.title}</Text>
<Text style={[styles.listItemText, { color: textColor }]}>
{this.props.title}
</Text>
</View>
</TouchableOpacity>
);
@@ -70,7 +75,9 @@ function renderExampleItem({ item }) {
<Text style={styles.exampleHeaderText}>Minimal FlatList:</Text>
<FlatList
data={minimalData}
renderItem={({ item }) => <Text style={styles.listItemText}>{item.key}</Text>}
renderItem={({ item }) => (
<Text style={styles.listItemText}>{item.key}</Text>
)}
/>
</View>
);
@@ -93,10 +100,14 @@ export default function FlatListPage() {
<Example title="FlatList">
<FlatList
ListFooterComponent={
<Text style={styles.allExamplesFooter}>(Example ListFooterComponent Here)</Text>
<Text style={styles.allExamplesFooter}>
(Example ListFooterComponent Here)
</Text>
}
ListHeaderComponent={
<Text style={styles.allExamplesHeader}>(Example ListHeaderComponent Here)</Text>
<Text style={styles.allExamplesHeader}>
(Example ListHeaderComponent Here)
</Text>
}
data={pageExamplesData}
renderItem={renderExampleItem}

View File

@@ -80,22 +80,38 @@ export default function ImagePage() {
<View style={styles.row}>
<View style={styles.column}>
<Text style={[styles.text]}>Center</Text>
<Image resizeMode="center" source={resizesource} style={styles.resizeMode} />
<Image
resizeMode="center"
source={resizesource}
style={styles.resizeMode}
/>
</View>
<View style={styles.column}>
<Text style={[styles.text]}>Contain</Text>
<Image resizeMode="contain" source={resizesource} style={styles.resizeMode} />
<Image
resizeMode="contain"
source={resizesource}
style={styles.resizeMode}
/>
</View>
</View>
<Divider />
<View style={styles.row}>
<View style={styles.column}>
<Text style={[styles.text]}>Cover</Text>
<Image resizeMode="cover" source={resizesource} style={styles.resizeMode} />
<Image
resizeMode="cover"
source={resizesource}
style={styles.resizeMode}
/>
</View>
<View style={styles.column}>
<Text style={[styles.text]}>Stretch</Text>
<Image resizeMode="stretch" source={resizesource} style={styles.resizeMode} />
<Image
resizeMode="stretch"
source={resizesource}
style={styles.resizeMode}
/>
</View>
</View>
</Example>

View File

@@ -30,8 +30,10 @@ export default function IndexPage() {
<Text style={styles.title}>React Native for Web</Text>
</View>
<Text style={styles.text}>
<Link href="https://github.com/necolas/react-native-web">React Native for Web</Link> example
app built on Next.js
<Link href="https://github.com/necolas/react-native-web">
React Native for Web
</Link>{' '}
example app built on Next.js
</Text>
<View accessibilityRole="list">

View File

@@ -22,7 +22,13 @@ import {
} from 'react-native';
import Example from '../../shared/example';
type Item = { title: string, text: string, key: string, pressed: boolean, noImage?: ?boolean };
type Item = {
title: string,
text: string,
key: string,
pressed: boolean,
noImage?: ?boolean
};
const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);
@@ -103,7 +109,10 @@ class ItemComponent extends React.PureComponent<{
]}
>
{!item.noImage && <Image source={imgSource} style={styles.thumb} />}
<Text numberOfLines={horizontal || fixedHeight ? 3 : undefined} style={styles.text}>
<Text
numberOfLines={horizontal || fixedHeight ? 3 : undefined}
style={styles.text}
>
{item.title} - {item.text}
</Text>
</View>
@@ -147,7 +156,10 @@ class SeparatorComponent extends React.PureComponent<{}> {
class ItemSeparatorComponent extends React.PureComponent<{}> {
render() {
const style = this.props.highlighted
? [styles.itemSeparator, { marginLeft: 0, backgroundColor: 'rgb(217, 217, 217)' }]
? [
styles.itemSeparator,
{ marginLeft: 0, backgroundColor: 'rgb(217, 217, 217)' }
]
: styles.itemSeparator;
return <View style={style} />;
}
@@ -251,16 +263,24 @@ class SingleColumnExample extends React.PureComponent {
};
_onChangeScrollToIndex = (text) => {
this._listRef.getNode().scrollToIndex({ viewPosition: 0.5, index: Number(text) });
this._listRef
.getNode()
.scrollToIndex({ viewPosition: 0.5, index: Number(text) });
};
_scrollPos = new Animated.Value(0);
_scrollSinkX = Animated.event([{ nativeEvent: { contentOffset: { x: this._scrollPos } } }], {
useNativeDriver: true
});
_scrollSinkY = Animated.event([{ nativeEvent: { contentOffset: { y: this._scrollPos } } }], {
useNativeDriver: true
});
_scrollSinkX = Animated.event(
[{ nativeEvent: { contentOffset: { x: this._scrollPos } } }],
{
useNativeDriver: true
}
);
_scrollSinkY = Animated.event(
[{ nativeEvent: { contentOffset: { y: this._scrollPos } } }],
{
useNativeDriver: true
}
);
componentDidUpdate() {
this._listRef.getNode().recordInteraction(); // e.g. flipping logViewable switch
@@ -268,7 +288,8 @@ class SingleColumnExample extends React.PureComponent {
render() {
const filterRegex = new RegExp(String(this.state.filterText), 'i');
const filter = (item) => filterRegex.test(item.text) || filterRegex.test(item.title);
const filter = (item) =>
filterRegex.test(item.text) || filterRegex.test(item.title);
const filteredData = this.state.data.filter(filter);
return (
<View style={styles.container}>
@@ -279,7 +300,10 @@ class SingleColumnExample extends React.PureComponent {
placeholder="Search..."
value={this.state.filterText}
/>
<PlainInput onChangeText={this._onChangeScrollToIndex} placeholder="scrollToIndex..." />
<PlainInput
onChangeText={this._onChangeScrollToIndex}
placeholder="scrollToIndex..."
/>
</View>
<View style={styles.options}>
{renderSmallSwitchOption(this, 'virtualized')}
@@ -300,17 +324,24 @@ class SingleColumnExample extends React.PureComponent {
data={filteredData}
debug={this.state.debug}
disableVirtualization={!this.state.virtualized}
getItemLayout={this.state.fixedHeight ? this._getItemLayout : undefined}
getItemLayout={
this.state.fixedHeight ? this._getItemLayout : undefined
}
horizontal={this.state.horizontal}
inverted={this.state.inverted}
key={(this.state.horizontal ? 'h' : 'v') + (this.state.fixedHeight ? 'f' : 'd')}
key={
(this.state.horizontal ? 'h' : 'v') +
(this.state.fixedHeight ? 'f' : 'd')
}
keyboardDismissMode="on-drag"
keyboardShouldPersistTaps="always"
legacyImplementation={false}
numColumns={1}
onEndReached={this._onEndReached}
onRefresh={this._onRefresh}
onScroll={this.state.horizontal ? this._scrollSinkX : this._scrollSinkY}
onScroll={
this.state.horizontal ? this._scrollSinkX : this._scrollSinkY
}
onViewableItemsChanged={this._onViewableItemsChanged}
ref={this._captureRef}
refreshing={false}

View File

@@ -309,7 +309,9 @@ const BorderRadiiExample = withRTLState(({ isRTL, setRTL }) => {
function Block(props) {
let description;
if (props.description) {
description = <Text style={blockStyles.descriptionText}>{props.description}</Text>;
description = (
<Text style={blockStyles.descriptionText}>{props.description}</Text>
);
}
return (
@@ -379,13 +381,19 @@ class LayoutRTLExample extends React.Component {
render() {
return (
<ScrollView dir={this.state.isRTL ? 'rtl' : 'ltr'} style={[styles.container]}>
<ScrollView
dir={this.state.isRTL ? 'rtl' : 'ltr'}
style={[styles.container]}
>
<Block title={'Current layout direction'}>
<View dir="ltr" style={styles.directionBox}>
<Text style={styles.directionText}>
{this.state.isRTL ? 'Right-to-Left' : 'Left-to-Right'}
</Text>
<Switch onValueChange={this._onDirectionChange} value={this.state.isRTL} />
<Switch
onValueChange={this._onDirectionChange}
value={this.state.isRTL}
/>
</View>
</Block>
@@ -419,13 +427,22 @@ class LayoutRTLExample extends React.Component {
<Block title={'A simple list-item layout'}>
<View style={styles.list}>
<ListItem imageSource={{ uri: 'https://picsum.photos/130/130?image=222' }} />
<ListItem imageSource={{ uri: 'https://picsum.photos/130/130?image=250' }} />
<ListItem
imageSource={{ uri: 'https://picsum.photos/130/130?image=222' }}
/>
<ListItem
imageSource={{ uri: 'https://picsum.photos/130/130?image=250' }}
/>
</View>
</Block>
<Block title={'Working with icons'}>
<View style={[styles.flexDirectionRow, { justifyContent: 'space-around' }]}>
<View
style={[
styles.flexDirectionRow,
{ justifyContent: 'space-around' }
]}
>
<View style={{ alignItems: 'center' }}>
<Image source={iconSource} style={styles.image} />
<Text style={styles.fontSizeSmall}>No RTL flip</Text>
@@ -433,7 +450,10 @@ class LayoutRTLExample extends React.Component {
<View style={{ alignItems: 'center' }}>
<Image
source={iconSource}
style={[styles.image, { transform: [{ scaleX: this.state.isRTL ? -1 : 1 }] }]}
style={[
styles.image,
{ transform: [{ scaleX: this.state.isRTL ? -1 : 1 }] }
]}
/>
<Text style={styles.fontSizeSmall}>RTL flip</Text>
</View>

View File

@@ -11,7 +11,10 @@ function AnimatedModal({ animationType }) {
return (
<>
<Button onPress={() => setIsVisible(true)} title={`Animation '${animationType}'`} />
<Button
onPress={() => setIsVisible(true)}
title={`Animation '${animationType}'`}
/>
<Modal
animationType={animationType}
onRequestClose={() => setIsVisible(false)}
@@ -54,13 +57,21 @@ function Modalception({ depth = 1 }) {
return (
<>
<Button onPress={() => setIsVisible(true)} title={'Open modal'} />
<Modal onRequestClose={() => setIsVisible(false)} transparent visible={isVisible}>
<Modal
onRequestClose={() => setIsVisible(false)}
transparent
visible={isVisible}
>
<View style={[styles.containeralt, offset]}>
<Text>This is in Modal {depth}</Text>
<Gap />
{isVisible ? <Modalception depth={depth + 1} /> : null}
<Gap />
<Button color="red" onPress={() => setIsVisible(false)} title={'Close'} />
<Button
color="red"
onPress={() => setIsVisible(false)}
title={'Close'}
/>
</View>
</Modal>
</>
@@ -90,9 +101,15 @@ function TransparentModal() {
return (
<>
<Button onPress={() => setIsVisible(true)} title={'Transparent modal'} />
<Modal onRequestClose={() => setIsVisible(false)} transparent visible={isVisible}>
<Modal
onRequestClose={() => setIsVisible(false)}
transparent
visible={isVisible}
>
<View style={styles.containeralt}>
<Text style={{ textAlign: 'center' }}>Modal with "transparent" value</Text>
<Text style={{ textAlign: 'center' }}>
Modal with "transparent" value
</Text>
<Gap />
<Button onPress={() => setIsVisible(false)} title={'Close'} />
</View>

View File

@@ -42,7 +42,11 @@ class DraggableCircle extends React.PureComponent {
render() {
return (
<View style={styles.container}>
<View ref={this._setCircleRef} style={styles.circle} {...this._panResponder.panHandlers} />
<View
ref={this._setCircleRef}
style={styles.circle}
{...this._panResponder.panHandlers}
/>
</View>
);
}
@@ -65,12 +69,18 @@ class DraggableCircle extends React.PureComponent {
this.circle && this.circle.setNativeProps(this._circleStyles);
}
_handleStartShouldSetPanResponder = (e: Object, gestureState: Object): boolean => {
_handleStartShouldSetPanResponder = (
e: Object,
gestureState: Object
): boolean => {
// Should we become active when the user presses down on the circle?
return true;
};
_handleMoveShouldSetPanResponder = (e: Object, gestureState: Object): boolean => {
_handleMoveShouldSetPanResponder = (
e: Object,
gestureState: Object
): boolean => {
// Should we become active when the user moves a touch over the circle?
return true;
};

View File

@@ -1,5 +1,12 @@
import React from 'react';
import { Button, ScrollView, StyleSheet, View, Text, Pressable } from 'react-native';
import {
Button,
ScrollView,
StyleSheet,
View,
Text,
Pressable
} from 'react-native';
import Example from '../../shared/example';
export default function PressablePage() {

View File

@@ -23,9 +23,17 @@ export default function ProgressBarPage() {
<Divider />
<ProgressBar color="#E0245E" progress={1} />
<Divider />
<ProgressBar color="rgb(23, 191, 99)" progress={0.1} trackColor="rgba(23, 191, 99, 0.3)" />
<ProgressBar
color="rgb(23, 191, 99)"
progress={0.1}
trackColor="rgba(23, 191, 99, 0.3)"
/>
<Divider />
<ProgressBar color="rgb(244, 93, 34)" progress={0.2} trackColor="rgba(244, 93, 34, 0.3)" />
<ProgressBar
color="rgb(244, 93, 34)"
progress={0.2}
trackColor="rgba(244, 93, 34, 0.3)"
/>
<Divider />
<ProgressBar
color="rgb(121, 75, 196)"
@@ -33,7 +41,12 @@ export default function ProgressBarPage() {
trackColor="rgba(121, 75, 196, 0.3)"
/>
<Divider />
<ProgressBar color="#1DA1F2" progress={0.33} style={styles.custom} trackColor="#D1E3F6" />
<ProgressBar
color="#1DA1F2"
progress={0.33}
style={styles.custom}
trackColor="#D1E3F6"
/>
</View>
</Example>
);

View File

@@ -1,5 +1,12 @@
import React from 'react';
import { Button, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
import {
Button,
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
View
} from 'react-native';
import Example from '../../shared/example';
const ITEMS = [...Array(12)].map((_, i) => `Item ${i}`);

View File

@@ -29,14 +29,21 @@ function renderItem({ item }) {
function renderSectionHeader({ section }) {
const extraStyle = { backgroundColor: section.color };
return (
<Text key={`sh_${section.key}`} style={[styles.sectionHeaderText, extraStyle]}>
<Text
key={`sh_${section.key}`}
style={[styles.sectionHeaderText, extraStyle]}
>
{section.title}
</Text>
);
}
function renderSectionFooter({ section }) {
const footerStyle = { height: 10, backgroundColor: section.color, marginBottom: 10 };
const footerStyle = {
height: 10,
backgroundColor: section.color,
marginBottom: 10
};
return <View key={`sf_${section.key}`} style={footerStyle} />;
}
@@ -45,10 +52,14 @@ export default function SectionListPage() {
<Example title="SectionList">
<SectionList
ListFooterComponent={
<Text style={styles.examplesFooter}>(Example ListFooterComponent Here)</Text>
<Text style={styles.examplesFooter}>
(Example ListFooterComponent Here)
</Text>
}
ListHeaderComponent={
<Text style={styles.examplesHeader}>(Example ListHeaderComponent Here)</Text>
<Text style={styles.examplesHeader}>
(Example ListHeaderComponent Here)
</Text>
}
renderItem={renderItem}
renderSectionFooter={renderSectionFooter}

View File

@@ -25,13 +25,31 @@ export default function SwitchPage() {
<Divider />
<Switch disabled={true} value={true} />
<Divider />
<Switch activeThumbColor="#fff" activeTrackColor="#E0245E" disabled={true} value={true} />
<Switch
activeThumbColor="#fff"
activeTrackColor="#E0245E"
disabled={true}
value={true}
/>
<Divider />
<Switch disabled={true} thumbColor="#fff" trackColor="#E0245E" value={false} />
<Switch
disabled={true}
thumbColor="#fff"
trackColor="#E0245E"
value={false}
/>
<Divider />
<Switch disabled={true} trackColor={{ true: '#E0245E', false: '#1DA1F2' }} value={false} />
<Switch
disabled={true}
trackColor={{ true: '#E0245E', false: '#1DA1F2' }}
value={false}
/>
<Divider />
<Switch disabled={true} trackColor={{ true: '#E0245E', false: '#1DA1F2' }} value={true} />
<Switch
disabled={true}
trackColor={{ true: '#E0245E', false: '#1DA1F2' }}
value={true}
/>
</View>
<View style={styles.row}>
<Switch value={false} />
@@ -39,30 +57,78 @@ export default function SwitchPage() {
<Switch value={true} />
</View>
<View style={styles.row}>
<Switch activeThumbColor="#1DA1F2" activeTrackColor="#ccc" value={true} />
<Switch
activeThumbColor="#1DA1F2"
activeTrackColor="#ccc"
value={true}
/>
<Divider />
<Switch activeThumbColor="#17BF63" activeTrackColor="#ccc" value={true} />
<Switch
activeThumbColor="#17BF63"
activeTrackColor="#ccc"
value={true}
/>
<Divider />
<Switch activeThumbColor="#FFAD1F" activeTrackColor="#ccc" value={true} />
<Switch
activeThumbColor="#FFAD1F"
activeTrackColor="#ccc"
value={true}
/>
<Divider />
<Switch activeThumbColor="#F45D22" activeTrackColor="#ccc" value={true} />
<Switch
activeThumbColor="#F45D22"
activeTrackColor="#ccc"
value={true}
/>
<Divider />
<Switch activeThumbColor="#794BC4" activeTrackColor="#ccc" value={true} />
<Switch
activeThumbColor="#794BC4"
activeTrackColor="#ccc"
value={true}
/>
<Divider />
<Switch activeThumbColor="#E0245E" activeTrackColor="#ccc" value={true} />
<Switch
activeThumbColor="#E0245E"
activeTrackColor="#ccc"
value={true}
/>
</View>
<View style={styles.row}>
<Switch activeThumbColor="#fff" activeTrackColor="#1DA1F2" value={true} />
<Switch
activeThumbColor="#fff"
activeTrackColor="#1DA1F2"
value={true}
/>
<Divider />
<Switch activeThumbColor="#fff" activeTrackColor="#17BF63" value={true} />
<Switch
activeThumbColor="#fff"
activeTrackColor="#17BF63"
value={true}
/>
<Divider />
<Switch activeThumbColor="#fff" activeTrackColor="#FFAD1F" value={true} />
<Switch
activeThumbColor="#fff"
activeTrackColor="#FFAD1F"
value={true}
/>
<Divider />
<Switch activeThumbColor="#fff" activeTrackColor="#F45D22" value={true} />
<Switch
activeThumbColor="#fff"
activeTrackColor="#F45D22"
value={true}
/>
<Divider />
<Switch activeThumbColor="#fff" activeTrackColor="#794BC4" value={true} />
<Switch
activeThumbColor="#fff"
activeTrackColor="#794BC4"
value={true}
/>
<Divider />
<Switch activeThumbColor="#fff" activeTrackColor="#E0245E" value={true} />
<Switch
activeThumbColor="#fff"
activeTrackColor="#E0245E"
value={true}
/>
</View>
<View style={styles.row}>
<Switch activeTrackColor="#ccc" thumbColor="#1DA1F2" value={false} />
@@ -92,7 +158,11 @@ export default function SwitchPage() {
</View>
<View style={styles.row}>
<Switch style={{ height: 32, width: 32 }} thumbColor="#1DA1F2" value={checked} />
<Switch
style={{ height: 32, width: 32 }}
thumbColor="#1DA1F2"
value={checked}
/>
</View>
</Example>
);

View File

@@ -33,14 +33,26 @@ export default function TextInputPage() {
ref={nextFocus}
style={styles.textinput}
/>
<TextInput defaultValue="disabled" disabled={true} style={styles.textinput} />
<TextInput defaultValue="editable (false)" editable={false} style={styles.textinput} />
<TextInput
defaultValue="disabled"
disabled={true}
style={styles.textinput}
/>
<TextInput
defaultValue="editable (false)"
editable={false}
style={styles.textinput}
/>
<TextInput
keyboardType="numeric"
placeholder="keyboardType 'numeric'"
style={styles.textinput}
/>
<TextInput maxLength={5} placeholder="maxLength" style={styles.textinput} />
<TextInput
maxLength={5}
placeholder="maxLength"
style={styles.textinput}
/>
<TextInput
placeholder="placeholderTextColor"
@@ -52,7 +64,11 @@ export default function TextInputPage() {
selectTextOnFocus={true}
style={styles.textinput}
/>
<TextInput defaultValue="secureTextEntry" secureTextEntry={true} style={styles.textinput} />
<TextInput
defaultValue="secureTextEntry"
secureTextEntry={true}
style={styles.textinput}
/>
<TextInput
multiline={true}
numberOfLines={3}

View File

@@ -36,7 +36,9 @@ function FontFamily() {
Cochin bold
</Text>
<Text style={{ fontFamily: 'Helvetica' }}>Helvetica</Text>
<Text style={{ fontFamily: 'Helvetica', fontWeight: 'bold' }}>Helvetica bold</Text>
<Text style={{ fontFamily: 'Helvetica', fontWeight: 'bold' }}>
Helvetica bold
</Text>
<Text style={{ fontFamily: 'Verdana' }}>Verdana</Text>
<Text
style={{
@@ -107,11 +109,21 @@ function FontWeight() {
return (
<View>
<Heading>fontWeight</Heading>
<Text style={{ fontSize: 20, fontWeight: '100' }}>Move fast and be ultralight</Text>
<Text style={{ fontSize: 20, fontWeight: '200' }}>Move fast and be light</Text>
<Text style={{ fontSize: 20, fontWeight: 'normal' }}>Move fast and be normal</Text>
<Text style={{ fontSize: 20, fontWeight: 'bold' }}>Move fast and be bold</Text>
<Text style={{ fontSize: 20, fontWeight: '900' }}>Move fast and be ultrabold</Text>
<Text style={{ fontSize: 20, fontWeight: '100' }}>
Move fast and be ultralight
</Text>
<Text style={{ fontSize: 20, fontWeight: '200' }}>
Move fast and be light
</Text>
<Text style={{ fontSize: 20, fontWeight: 'normal' }}>
Move fast and be normal
</Text>
<Text style={{ fontSize: 20, fontWeight: 'bold' }}>
Move fast and be bold
</Text>
<Text style={{ fontSize: 20, fontWeight: '900' }}>
Move fast and be ultrabold
</Text>
</View>
);
}
@@ -124,12 +136,23 @@ function LetterSpacing() {
<Text style={{ letterSpacing: 2, marginTop: 5 }}>letterSpacing = 2</Text>
<Text style={{ letterSpacing: 9, marginTop: 5 }}>letterSpacing = 9</Text>
<View style={{ flexDirection: 'row' }}>
<Text style={{ fontSize: 12, letterSpacing: 2, backgroundColor: 'fuchsia', marginTop: 5 }}>
<Text
style={{
fontSize: 12,
letterSpacing: 2,
backgroundColor: 'fuchsia',
marginTop: 5
}}
>
With size and background color
</Text>
</View>
<Text style={{ letterSpacing: -1, marginTop: 5 }}>letterSpacing = -1</Text>
<Text style={{ letterSpacing: 3, backgroundColor: '#dddddd', marginTop: 5 }}>
<Text style={{ letterSpacing: -1, marginTop: 5 }}>
letterSpacing = -1
</Text>
<Text
style={{ letterSpacing: 3, backgroundColor: '#dddddd', marginTop: 5 }}
>
[letterSpacing = 3]
<Text style={{ letterSpacing: 0, backgroundColor: '#bbbbbb' }}>
[Nested letterSpacing = 0]
@@ -147,9 +170,9 @@ function LineHeight() {
<View>
<Heading>lineHeight</Heading>
<Text style={{ lineHeight: 35 }}>
A lot of space should display between the lines of this long passage as they wrap across
several lines. A lot of space should display between the lines of this long passage as they
wrap across several lines.
A lot of space should display between the lines of this long passage as
they wrap across several lines. A lot of space should display between
the lines of this long passage as they wrap across several lines.
</Text>
</View>
);
@@ -166,18 +189,21 @@ function TextAlign() {
'RTL'}
</Text>
<Text style={{ textAlign: 'left' }}>
left left left left left left left left left left left left left left left
left left left left left left left left left left left left left left
left
</Text>
<Text style={{ textAlign: 'center' }}>
center center center center center center center center center center center
center center center center center center center center center center
center
</Text>
<Text style={{ textAlign: 'right' }}>
right right right right right right right right right right right right right
right right right right right right right right right right right right
right
</Text>
<Text style={{ textAlign: 'justify' }}>
justify: this text component{"'"}s contents are laid out with "textAlign: justify" and as
you can see all of the lines except the last one span the available width of the parent
container.
justify: this text component{"'"}s contents are laid out with
"textAlign: justify" and as you can see all of the lines except the last
one span the available width of the parent container.
</Text>
</View>
);
@@ -306,8 +332,9 @@ export default function TextPage() {
<Example title="Text">
<View style={{ maxWidth: 500 }}>
<Text>
Text wraps across multiple lines by default. Text wraps across multiple lines by default.
Text wraps across multiple lines by default. Text wraps across multiple lines by default.
Text wraps across multiple lines by default. Text wraps across
multiple lines by default. Text wraps across multiple lines by
default. Text wraps across multiple lines by default.
</Text>
<Spacer />
@@ -351,8 +378,10 @@ export default function TextPage() {
<Text>
This text contains an inline blue view{' '}
<View style={{ width: 25, height: 25, backgroundColor: 'steelblue' }} /> and an inline
image{' '}
<View
style={{ width: 25, height: 25, backgroundColor: 'steelblue' }}
/>{' '}
and an inline image{' '}
<Image
source={{ uri: 'http://lorempixel.com/30/11' }}
style={{ width: 30, height: 11, resizeMode: 'cover' }}
@@ -365,8 +394,12 @@ export default function TextPage() {
<Text>
This text contains a view{' '}
<View style={{ borderColor: 'red', borderWidth: 1 }}>
<Text style={{ borderColor: 'blue', borderWidth: 1 }}>which contains</Text>
<Text style={{ borderColor: 'green', borderWidth: 1 }}>another text.</Text>
<Text style={{ borderColor: 'blue', borderWidth: 1 }}>
which contains
</Text>
<Text style={{ borderColor: 'green', borderWidth: 1 }}>
another text.
</Text>
<Text style={{ borderColor: 'yellow', borderWidth: 1 }}>
And contains another view
<View style={{ borderColor: 'red', borderWidth: 1 }}>
@@ -380,10 +413,12 @@ export default function TextPage() {
</Text>
<Text selectable={true}>
This text is <Text style={{ fontWeight: 'bold' }}>selectable</Text> if you click-and-hold.
This text is <Text style={{ fontWeight: 'bold' }}>selectable</Text> if
you click-and-hold.
</Text>
<Text selectable={false}>
This text is <Text style={{ fontWeight: 'bold' }}>not selectable</Text> if you
This text is{' '}
<Text style={{ fontWeight: 'bold' }}>not selectable</Text> if you
click-and-hold.
</Text>
@@ -421,7 +456,9 @@ export default function TextPage() {
</LineExample>
<LineExample description="With very long word, text is limited to 1 line and long word is truncated.">
<Text numberOfLines={1}>goal aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</Text>
<Text numberOfLines={1}>
goal aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
</Text>
</LineExample>
<LineExample description="With space characters within adjacent truncated lines">

View File

@@ -16,7 +16,10 @@ function Box({ pointerEvents }) {
pointerEvents={pointerEvents}
style={({ pressed }) => [styles.box, pressed && styles.purple]}
>
<Pressable onPress={log} style={({ pressed }) => [styles.content, pressed && styles.orange]}>
<Pressable
onPress={log}
style={({ pressed }) => [styles.content, pressed && styles.orange]}
>
<Text>{pointerEvents}</Text>
</Pressable>
</Pressable>

View File

@@ -23,7 +23,9 @@ const prefersReducedMotionMedia =
function isReduceMotionEnabled(): Promise<*> {
return new Promise((resolve, reject) => {
resolve(prefersReducedMotionMedia ? prefersReducedMotionMedia.matches : true);
resolve(
prefersReducedMotionMedia ? prefersReducedMotionMedia.matches : true
);
});
}

View File

@@ -14,14 +14,18 @@ import { render } from '@testing-library/react';
describe('components/ActivityIndicator', () => {
describe('prop "accessibilityLabel"', () => {
test('value is set', () => {
const { container } = render(<ActivityIndicator accessibilityLabel="accessibility label" />);
const { container } = render(
<ActivityIndicator accessibilityLabel="accessibility label" />
);
expect(container.firstChild).toMatchSnapshot();
});
});
describe('prop "accessibilityLiveRegion"', () => {
test('value is set', () => {
const { container } = render(<ActivityIndicator accessibilityLiveRegion="polite" />);
const { container } = render(
<ActivityIndicator accessibilityLiveRegion="polite" />
);
expect(container.firstChild).toMatchSnapshot();
});
});
@@ -46,14 +50,18 @@ describe('components/ActivityIndicator', () => {
describe('prop "dataSet"', () => {
test('value is set', () => {
const { container } = render(<ActivityIndicator dataSet={{ one: 'one', two: 'two' }} />);
const { container } = render(
<ActivityIndicator dataSet={{ one: 'one', two: 'two' }} />
);
expect(container.firstChild).toMatchSnapshot();
});
});
describe('prop "hidesWhenStopped"', () => {
test('is "true"', () => {
const { container } = render(<ActivityIndicator animating={false} hidesWhenStopped={true} />);
const { container } = render(
<ActivityIndicator animating={false} hidesWhenStopped={true} />
);
expect(container.firstChild).toMatchSnapshot();
});
@@ -126,7 +134,9 @@ describe('components/ActivityIndicator', () => {
describe('prop "style"', () => {
test('value is set', () => {
const { container } = render(<ActivityIndicator style={{ borderWidth: 5 }} />);
const { container } = render(
<ActivityIndicator style={{ borderWidth: 5 }} />
);
expect(container.firstChild).toMatchSnapshot();
});
});

View File

@@ -65,7 +65,9 @@ const ActivityIndicator: React.AbstractComponent<
<View
children={svg}
style={[
typeof size === 'number' ? { height: size, width: size } : indicatorSizes[size],
typeof size === 'number'
? { height: size, width: size }
: indicatorSizes[size],
styles.animation,
!animating && styles.animationPause,
!animating && hidesWhenStopped && styles.hidesWhenStopped

View File

@@ -25,7 +25,12 @@ export default function AppContainer(props: Props): React.Node {
const { children, WrapperComponent } = props;
let innerView = (
<View children={children} key={1} pointerEvents="box-none" style={styles.appContainer} />
<View
children={children}
key={1}
pointerEvents="box-none"
style={styles.appContainer}
/>
);
if (WrapperComponent) {

View File

@@ -27,7 +27,11 @@ describe('AppRegistry', () => {
test('callback after render', () => {
const callback = jest.fn();
AppRegistry.registerComponent('App', () => NoopComponent);
AppRegistry.runApplication('App', { initialProps: {}, rootTag, callback });
AppRegistry.runApplication('App', {
initialProps: {},
rootTag,
callback
});
expect(callback).toHaveBeenCalledTimes(1);
});
@@ -45,14 +49,19 @@ describe('AppRegistry', () => {
// Run in iframe
AppRegistry.registerComponent('App', () => NoopComponent);
AppRegistry.runApplication('App', { initialProps: {}, rootTag: iframeRootTag });
AppRegistry.runApplication('App', {
initialProps: {},
rootTag: iframeRootTag
});
const iframedoc = iframeRootTag.ownerDocument;
expect(iframedoc).toBe(iframe.contentWindow.document);
expect(iframedoc).not.toBe(document);
const cssText = Array.prototype.slice
.call(iframedoc.getElementById('react-native-stylesheet').sheet.cssRules)
.call(
iframedoc.getElementById('react-native-stylesheet').sheet.cssRules
)
.map((cssRule) => cssRule.cssText);
expect(cssText).toMatchInlineSnapshot(`

View File

@@ -22,8 +22,13 @@ describe('AppRegistry', () => {
test('returns "element" and "getStyleElement"', () => {
AppRegistry.registerComponent('App', () => NoopComponent);
const { element, getStyleElement } = AppRegistry.getApplication('App', {});
const styleElement = ReactDOMServer.renderToStaticMarkup(getStyleElement());
const { element, getStyleElement } = AppRegistry.getApplication(
'App',
{}
);
const styleElement = ReactDOMServer.renderToStaticMarkup(
getStyleElement()
);
expect(element).toMatchInlineSnapshot(`
<AppContainer
@@ -102,9 +107,15 @@ describe('AppRegistry', () => {
`);
// Second render "AlternativeComponent"
const styles = StyleSheet.create({ root: { borderWidth: 1234, backgroundColor: 'purple' } });
const AlternativeComponent = () => React.createElement(View, { style: styles.root });
AppRegistry.registerComponent('AlternativeApp', () => AlternativeComponent);
const styles = StyleSheet.create({
root: { borderWidth: 1234, backgroundColor: 'purple' }
});
const AlternativeComponent = () =>
React.createElement(View, { style: styles.root });
AppRegistry.registerComponent(
'AlternativeApp',
() => AlternativeComponent
);
const second = getApplicationStyles('AlternativeApp');
expect(second).toMatchInlineSnapshot(`
"[stylesheet-group=\\"0\\"]{}

View File

@@ -16,7 +16,10 @@ import renderApplication, { getApplication } from './renderApplication';
type AppParams = Object;
type Runnable = {|
getApplication?: (AppParams) => {| element: Node, getStyleElement: (any) => Node |},
getApplication?: (AppParams) => {|
element: Node,
getStyleElement: (any) => Node
|},
run: (AppParams) => any
|};
@@ -36,9 +39,8 @@ export type AppConfig = {
const emptyObject = {};
const runnables: {| [appKey: string]: Runnable |} = {};
let componentProviderInstrumentationHook: ComponentProviderInstrumentationHook = (
component: ComponentProvider
) => component();
let componentProviderInstrumentationHook: ComponentProviderInstrumentationHook =
(component: ComponentProvider) => component();
let wrapperComponentProvider: ?WrapperComponentProvider;
/**
@@ -62,7 +64,10 @@ export default class AppRegistry {
return runnables[appKey].getApplication(appParameters);
}
static registerComponent(appKey: string, componentProvider: ComponentProvider): string {
static registerComponent(
appKey: string,
componentProvider: ComponentProvider
): string {
runnables[appKey] = {
getApplication: (appParameters) =>
getApplication(
@@ -103,7 +108,8 @@ export default class AppRegistry {
}
static runApplication(appKey: string, appParameters: Object): void {
const isDevelopment = process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'test';
const isDevelopment =
process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'test';
if (isDevelopment) {
const params = { ...appParameters };
params.rootTag = `#${params.rootTag.id}`;
@@ -125,7 +131,9 @@ export default class AppRegistry {
runnables[appKey].run(appParameters);
}
static setComponentProviderInstrumentationHook(hook: ComponentProviderInstrumentationHook) {
static setComponentProviderInstrumentationHook(
hook: ComponentProviderInstrumentationHook
) {
componentProviderInstrumentationHook = hook;
}

View File

@@ -54,7 +54,11 @@ export function getApplication(
const getStyleElement = (props) => {
const sheet = StyleSheet.getSheet();
return (
<style {...props} dangerouslySetInnerHTML={{ __html: sheet.textContent }} id={sheet.id} />
<style
{...props}
dangerouslySetInnerHTML={{ __html: sheet.textContent }}
id={sheet.id}
/>
);
};
return { element, getStyleElement };

View File

@@ -13,7 +13,9 @@ describe('apis/AppState', () => {
describe('addEventListener', () => {
test('throws if the provided "eventType" is not supported', () => {
expect(() => AppState.addEventListener('foo', handler)).toThrow();
expect(() => AppState.addEventListener('change', handler).remove()).not.toThrow();
expect(() =>
AppState.addEventListener('change', handler).remove()
).not.toThrow();
});
test('returns remove subscription', () => {

View File

@@ -15,11 +15,17 @@ const { canUseDOM } = ExecutionEnvironment;
// Android 4.4 browser
const isPrefixed =
canUseDOM && !document.hasOwnProperty('hidden') && document.hasOwnProperty('webkitHidden');
canUseDOM &&
!document.hasOwnProperty('hidden') &&
document.hasOwnProperty('webkitHidden');
const EVENT_TYPES = ['change', 'memoryWarning'];
const VISIBILITY_CHANGE_EVENT = isPrefixed ? 'webkitvisibilitychange' : 'visibilitychange';
const VISIBILITY_STATE_PROPERTY = isPrefixed ? 'webkitVisibilityState' : 'visibilityState';
const VISIBILITY_CHANGE_EVENT = isPrefixed
? 'webkitvisibilitychange'
: 'visibilitychange';
const VISIBILITY_STATE_PROPERTY = isPrefixed
? 'webkitVisibilityState'
: 'visibilityState';
const AppStates = {
BACKGROUND: 'background',

View File

@@ -27,7 +27,10 @@ function getQuery(): MediaQueryList | null {
}
const query = getQuery();
const listenerMapping = new WeakMap<AppearanceListener, DOMAppearanceListener>();
const listenerMapping = new WeakMap<
AppearanceListener,
DOMAppearanceListener
>();
const Appearance = {
getColorScheme(): ColorSchemeName {

View File

@@ -6,7 +6,9 @@ import { render } from '@testing-library/react';
describe('components/Button', () => {
test('prop "accessibilityLabel"', () => {
const { container } = render(<Button accessibilityLabel="accessibility label" title="" />);
const { container } = render(
<Button accessibilityLabel="accessibility label" title="" />
);
expect(container.firstChild).toMatchSnapshot();
});

View File

@@ -43,7 +43,9 @@ const Button: React.AbstractComponent<
]}
testID={testID}
>
<Text style={[styles.text, disabled && styles.textDisabled]}>{title}</Text>
<Text style={[styles.text, disabled && styles.textDisabled]}>
{title}
</Text>
</TouchableOpacity>
);
});

View File

@@ -18,7 +18,9 @@ function findCheckbox(container) {
describe('CheckBox', () => {
describe('prop "accessibilityLabel"', () => {
test('value is set', () => {
const { container } = render(<CheckBox accessibilityLabel="accessibility label" />);
const { container } = render(
<CheckBox accessibilityLabel="accessibility label" />
);
expect(container.firstChild).toMatchSnapshot();
});
});
@@ -32,7 +34,9 @@ describe('CheckBox', () => {
describe('prop "dataSet"', () => {
test('value is set', () => {
const { container } = render(<CheckBox dataSet={{ one: 'one', two: 'two' }} />);
const { container } = render(
<CheckBox dataSet={{ one: 'one', two: 'two' }} />
);
expect(container.firstChild).toMatchSnapshot();
});
});
@@ -76,7 +80,9 @@ describe('CheckBox', () => {
describe('prop "onChange"', () => {
test('is called with the event object', () => {
const onChange = jest.fn();
const { container } = render(<CheckBox onChange={onChange} value={false} />);
const { container } = render(
<CheckBox onChange={onChange} value={false} />
);
const checkbox = findCheckbox(container);
checkbox.click(); // Needed to get ReactDOM to trigger 'change' event
expect(onChange).toHaveBeenCalled();
@@ -101,7 +107,9 @@ describe('CheckBox', () => {
describe('prop "onValueChange"', () => {
test('when value is "false" it receives "true"', () => {
const onValueChange = jest.fn();
const { container } = render(<CheckBox onValueChange={onValueChange} value={false} />);
const { container } = render(
<CheckBox onValueChange={onValueChange} value={false} />
);
const checkbox = findCheckbox(container);
checkbox.click(); // Needed to get ReactDOM to trigger 'change' event
expect(onValueChange).toHaveBeenCalledWith(true);
@@ -109,7 +117,9 @@ describe('CheckBox', () => {
test('when value is "true" it receives "false"', () => {
const onValueChange = jest.fn();
const { container } = render(<CheckBox onValueChange={onValueChange} value />);
const { container } = render(
<CheckBox onValueChange={onValueChange} value />
);
const checkbox = findCheckbox(container);
checkbox.click(); // Needed to get ReactDOM to trigger 'change' event
expect(onValueChange).toHaveBeenCalledWith(false);

View File

@@ -29,7 +29,8 @@ const CheckBox: React.AbstractComponent<
CheckBoxProps,
React.ElementRef<typeof View>
> = React.forwardRef((props, forwardedRef) => {
const { color, disabled, onChange, onValueChange, style, value, ...other } = props;
const { color, disabled, onChange, onValueChange, style, value, ...other } =
props;
function handleChange(event: Object) {
const value = event.nativeEvent.target.checked;

View File

@@ -118,7 +118,9 @@ export default class Dimensions {
handler: (DimensionsValue) => void
): void {
if (Array.isArray(listeners[type])) {
listeners[type] = listeners[type].filter((_handler) => _handler !== handler);
listeners[type] = listeners[type].filter(
(_handler) => _handler !== handler
);
}
}
}

View File

@@ -30,14 +30,19 @@ describe('components/Image', () => {
test('prop "accessibilityLabel"', () => {
const defaultSource = { uri: 'https://google.com/favicon.ico' };
const { container } = render(
<Image accessibilityLabel="accessibilityLabel" defaultSource={defaultSource} />
<Image
accessibilityLabel="accessibilityLabel"
defaultSource={defaultSource}
/>
);
expect(container.firstChild).toMatchSnapshot();
});
test('prop "blurRadius"', () => {
const defaultSource = { uri: 'https://google.com/favicon.ico' };
const { container } = render(<Image blurRadius={5} defaultSource={defaultSource} />);
const { container } = render(
<Image blurRadius={5} defaultSource={defaultSource} />
);
expect(container.firstChild).toMatchSnapshot();
});
@@ -72,7 +77,10 @@ describe('components/Image', () => {
width: 20
};
const { container } = render(
<Image defaultSource={defaultSource} style={{ height: 20, width: 40 }} />
<Image
defaultSource={defaultSource}
style={{ height: 20, width: 40 }}
/>
);
expect(container.firstChild).toMatchSnapshot();
});
@@ -80,7 +88,9 @@ describe('components/Image', () => {
test('prop "draggable"', () => {
const defaultSource = { uri: 'https://google.com/favicon.ico' };
const { container } = render(<Image defaultSource={defaultSource} draggable={true} />);
const { container } = render(
<Image defaultSource={defaultSource} draggable={true} />
);
expect(container.firstChild).toMatchSnapshot();
});
@@ -218,17 +228,25 @@ describe('components/Image', () => {
});
describe('prop "resizeMode"', () => {
['contain', 'cover', 'none', 'repeat', 'stretch', undefined].forEach((resizeMode) => {
test(`value "${resizeMode}"`, () => {
const { container } = render(<Image resizeMode={resizeMode} />);
expect(container.firstChild).toMatchSnapshot();
});
});
['contain', 'cover', 'none', 'repeat', 'stretch', undefined].forEach(
(resizeMode) => {
test(`value "${resizeMode}"`, () => {
const { container } = render(<Image resizeMode={resizeMode} />);
expect(container.firstChild).toMatchSnapshot();
});
}
);
});
describe('prop "source"', () => {
test('does not throw', () => {
const sources = [null, '', {}, { uri: '' }, { uri: 'https://google.com' }];
const sources = [
null,
'',
{},
{ uri: '' },
{ uri: 'https://google.com' }
];
sources.forEach((source) => {
expect(() => render(<Image source={source} />)).not.toThrow();
});
@@ -243,12 +261,16 @@ describe('components/Image', () => {
test('is set immediately if the image was preloaded', () => {
const uri = 'https://yahoo.com/favicon.ico';
ImageLoader.load = jest.fn().mockImplementationOnce((_, onLoad, onError) => {
onLoad();
});
ImageLoader.load = jest
.fn()
.mockImplementationOnce((_, onLoad, onError) => {
onLoad();
});
return Image.prefetch(uri).then(() => {
const source = { uri };
const { container } = render(<Image source={source} />, { disableLifecycleMethods: true });
const { container } = render(<Image source={source} />, {
disableLifecycleMethods: true
});
expect(container.firstChild).toMatchSnapshot();
ImageUriCache.remove(uri);
});
@@ -261,7 +283,9 @@ describe('components/Image', () => {
ImageUriCache.add(uriTwo);
// initial render
const { container, rerender } = render(<Image source={{ uri: uriOne }} />);
const { container, rerender } = render(
<Image source={{ uri: uriOne }} />
);
ImageUriCache.remove(uriOne);
expect(container.firstChild).toMatchSnapshot();
// props update
@@ -285,10 +309,14 @@ describe('components/Image', () => {
const defaultUri = 'https://testing.com/preview.jpg';
const uri = 'https://testing.com/fullSize.jpg';
let loadCallback;
ImageLoader.load = jest.fn().mockImplementationOnce((_, onLoad, onError) => {
loadCallback = onLoad;
});
const { container } = render(<Image defaultSource={{ uri: defaultUri }} source={{ uri }} />);
ImageLoader.load = jest
.fn()
.mockImplementationOnce((_, onLoad, onError) => {
loadCallback = onLoad;
});
const { container } = render(
<Image defaultSource={{ uri: defaultUri }} source={{ uri }} />
);
expect(container.firstChild).toMatchSnapshot();
act(() => {
loadCallback();
@@ -306,13 +334,17 @@ describe('components/Image', () => {
PixelRatio.get = jest.fn(() => 1.0);
let { container } = render(<Image source={1} />);
expect(container.querySelector('img').src).toBe('http://localhost/static/img.png');
expect(container.querySelector('img').src).toBe(
'http://localhost/static/img.png'
);
act(() => {
PixelRatio.get = jest.fn(() => 2.2);
({ container } = render(<Image source={1} />));
});
expect(container.querySelector('img').src).toBe('http://localhost/static/img@2x.png');
expect(container.querySelector('img').src).toBe(
'http://localhost/static/img@2x.png'
);
});
});
@@ -324,7 +356,9 @@ describe('components/Image', () => {
test('supports "shadow" properties (convert to filter)', () => {
const { container } = render(
<Image style={{ shadowColor: 'red', shadowOffset: { width: 1, height: 1 } }} />
<Image
style={{ shadowColor: 'red', shadowOffset: { width: 1, height: 1 } }}
/>
);
expect(container.firstChild).toMatchSnapshot();
});
@@ -338,7 +372,9 @@ describe('components/Image', () => {
});
test('removes other unsupported View styles', () => {
const { container } = render(<Image style={{ overlayColor: 'red', tintColor: 'blue' }} />);
const { container } = render(
<Image style={{ overlayColor: 'red', tintColor: 'blue' }} />
);
expect(container.firstChild).toMatchSnapshot();
});
});

View File

@@ -32,7 +32,14 @@ const svgDataUriPattern = /^(data:image\/svg\+xml;utf8,)(.*)/;
function createTintColorSVG(tintColor, id) {
return tintColor && id != null ? (
<svg style={{ position: 'absolute', height: 0, visibility: 'hidden', width: 0 }}>
<svg
style={{
position: 'absolute',
height: 0,
visibility: 'hidden',
width: 0
}}
>
<defs>
<filter id={`tint-${id}`} suppressHydrationWarning={true}>
<feFlood floodColor={`${tintColor}`} key={tintColor} />
@@ -91,7 +98,11 @@ function resolveAssetDimensions(source) {
if (typeof source === 'number') {
const { height, width } = getAssetByID(source);
return { height, width };
} else if (source != null && !Array.isArray(source) && typeof source === 'object') {
} else if (
source != null &&
!Array.isArray(source) &&
typeof source === 'object'
) {
const { height, width } = source;
return { height, width };
}
@@ -107,11 +118,15 @@ function resolveAssetUri(source): ?string {
const preferredScale = PixelRatio.get();
// Get the scale which is closest to the preferred scale
scale = asset.scales.reduce((prev, curr) =>
Math.abs(curr - preferredScale) < Math.abs(prev - preferredScale) ? curr : prev
Math.abs(curr - preferredScale) < Math.abs(prev - preferredScale)
? curr
: prev
);
}
const scaleSuffix = scale !== 1 ? `@${scale}x` : '';
uri = asset ? `${asset.httpServerLocation}/${asset.name}${scaleSuffix}.${asset.type}` : '';
uri = asset
? `${asset.httpServerLocation}/${asset.name}${scaleSuffix}.${asset.type}`
: '';
} else if (typeof source === 'string') {
uri = source;
} else if (source && typeof source.uri === 'string') {
@@ -138,168 +153,184 @@ interface ImageStatics {
failure: () => void
) => void;
prefetch: (uri: string) => Promise<void>;
queryCache: (uris: Array<string>) => Promise<{| [uri: string]: 'disk/memory' |}>;
queryCache: (
uris: Array<string>
) => Promise<{| [uri: string]: 'disk/memory' |}>;
}
const Image: React.AbstractComponent<ImageProps, React.ElementRef<typeof View>> = React.forwardRef(
(props, ref) => {
const {
accessibilityLabel,
blurRadius,
defaultSource,
draggable,
onError,
onLayout,
onLoad,
onLoadEnd,
onLoadStart,
pointerEvents,
source,
style,
...rest
} = props;
const Image: React.AbstractComponent<
ImageProps,
React.ElementRef<typeof View>
> = React.forwardRef((props, ref) => {
const {
accessibilityLabel,
blurRadius,
defaultSource,
draggable,
onError,
onLayout,
onLoad,
onLoadEnd,
onLoadStart,
pointerEvents,
source,
style,
...rest
} = props;
if (process.env.NODE_ENV !== 'production') {
if (props.children) {
throw new Error(
'The <Image> component cannot contain children. If you want to render content on top of the image, consider using the <ImageBackground> component or absolute positioning.'
);
}
if (process.env.NODE_ENV !== 'production') {
if (props.children) {
throw new Error(
'The <Image> component cannot contain children. If you want to render content on top of the image, consider using the <ImageBackground> component or absolute positioning.'
);
}
const [state, updateState] = React.useState(() => {
const uri = resolveAssetUri(source);
if (uri != null) {
const isLoaded = ImageLoader.has(uri);
if (isLoaded) {
return LOADED;
}
}
return IDLE;
});
const [layout, updateLayout] = React.useState({});
const hasTextAncestor = React.useContext(TextAncestorContext);
const hiddenImageRef = React.useRef(null);
const filterRef = React.useRef(_filterId++);
const requestRef = React.useRef(null);
const shouldDisplaySource = state === LOADED || (state === LOADING && defaultSource == null);
const [flatStyle, _resizeMode, filter, tintColor] = getFlatStyle(
style,
blurRadius,
filterRef.current
);
const resizeMode = props.resizeMode || _resizeMode || 'cover';
const selectedSource = shouldDisplaySource ? source : defaultSource;
const displayImageUri = resolveAssetUri(selectedSource);
const imageSizeStyle = resolveAssetDimensions(selectedSource);
const backgroundImage = displayImageUri ? `url("${displayImageUri}")` : null;
const backgroundSize = getBackgroundSize();
// Accessibility image allows users to trigger the browser's image context menu
const hiddenImage = displayImageUri
? createElement('img', {
alt: accessibilityLabel || '',
style: styles.accessibilityImage$raw,
draggable: draggable || false,
ref: hiddenImageRef,
src: displayImageUri
})
: null;
function getBackgroundSize(): ?string {
if (hiddenImageRef.current != null && (resizeMode === 'center' || resizeMode === 'repeat')) {
const { naturalHeight, naturalWidth } = hiddenImageRef.current;
const { height, width } = layout;
if (naturalHeight && naturalWidth && height && width) {
const scaleFactor = Math.min(1, width / naturalWidth, height / naturalHeight);
const x = Math.ceil(scaleFactor * naturalWidth);
const y = Math.ceil(scaleFactor * naturalHeight);
return `${x}px ${y}px`;
}
}
}
function handleLayout(e) {
if (resizeMode === 'center' || resizeMode === 'repeat' || onLayout) {
const { layout } = e.nativeEvent;
onLayout && onLayout(e);
updateLayout(layout);
}
}
// Image loading
const uri = resolveAssetUri(source);
React.useEffect(() => {
abortPendingRequest();
if (uri != null) {
updateState(LOADING);
if (onLoadStart) {
onLoadStart();
}
requestRef.current = ImageLoader.load(
uri,
function load(e) {
updateState(LOADED);
if (onLoad) {
onLoad(e);
}
if (onLoadEnd) {
onLoadEnd();
}
},
function error() {
updateState(ERRORED);
if (onError) {
onError({
nativeEvent: {
error: `Failed to load resource ${uri} (404)`
}
});
}
if (onLoadEnd) {
onLoadEnd();
}
}
);
}
function abortPendingRequest() {
if (requestRef.current != null) {
ImageLoader.abort(requestRef.current);
requestRef.current = null;
}
}
return abortPendingRequest;
}, [uri, requestRef, updateState, onError, onLoad, onLoadEnd, onLoadStart]);
return (
<View
{...rest}
accessibilityLabel={accessibilityLabel}
onLayout={handleLayout}
pointerEvents={pointerEvents}
ref={ref}
style={[styles.root, hasTextAncestor && styles.inline, imageSizeStyle, flatStyle]}
>
<View
style={[
styles.image,
resizeModeStyles[resizeMode],
{ backgroundImage, filter },
backgroundSize != null && { backgroundSize }
]}
suppressHydrationWarning={true}
/>
{hiddenImage}
{createTintColorSVG(tintColor, filterRef.current)}
</View>
);
}
);
const [state, updateState] = React.useState(() => {
const uri = resolveAssetUri(source);
if (uri != null) {
const isLoaded = ImageLoader.has(uri);
if (isLoaded) {
return LOADED;
}
}
return IDLE;
});
const [layout, updateLayout] = React.useState({});
const hasTextAncestor = React.useContext(TextAncestorContext);
const hiddenImageRef = React.useRef(null);
const filterRef = React.useRef(_filterId++);
const requestRef = React.useRef(null);
const shouldDisplaySource =
state === LOADED || (state === LOADING && defaultSource == null);
const [flatStyle, _resizeMode, filter, tintColor] = getFlatStyle(
style,
blurRadius,
filterRef.current
);
const resizeMode = props.resizeMode || _resizeMode || 'cover';
const selectedSource = shouldDisplaySource ? source : defaultSource;
const displayImageUri = resolveAssetUri(selectedSource);
const imageSizeStyle = resolveAssetDimensions(selectedSource);
const backgroundImage = displayImageUri ? `url("${displayImageUri}")` : null;
const backgroundSize = getBackgroundSize();
// Accessibility image allows users to trigger the browser's image context menu
const hiddenImage = displayImageUri
? createElement('img', {
alt: accessibilityLabel || '',
style: styles.accessibilityImage$raw,
draggable: draggable || false,
ref: hiddenImageRef,
src: displayImageUri
})
: null;
function getBackgroundSize(): ?string {
if (
hiddenImageRef.current != null &&
(resizeMode === 'center' || resizeMode === 'repeat')
) {
const { naturalHeight, naturalWidth } = hiddenImageRef.current;
const { height, width } = layout;
if (naturalHeight && naturalWidth && height && width) {
const scaleFactor = Math.min(
1,
width / naturalWidth,
height / naturalHeight
);
const x = Math.ceil(scaleFactor * naturalWidth);
const y = Math.ceil(scaleFactor * naturalHeight);
return `${x}px ${y}px`;
}
}
}
function handleLayout(e) {
if (resizeMode === 'center' || resizeMode === 'repeat' || onLayout) {
const { layout } = e.nativeEvent;
onLayout && onLayout(e);
updateLayout(layout);
}
}
// Image loading
const uri = resolveAssetUri(source);
React.useEffect(() => {
abortPendingRequest();
if (uri != null) {
updateState(LOADING);
if (onLoadStart) {
onLoadStart();
}
requestRef.current = ImageLoader.load(
uri,
function load(e) {
updateState(LOADED);
if (onLoad) {
onLoad(e);
}
if (onLoadEnd) {
onLoadEnd();
}
},
function error() {
updateState(ERRORED);
if (onError) {
onError({
nativeEvent: {
error: `Failed to load resource ${uri} (404)`
}
});
}
if (onLoadEnd) {
onLoadEnd();
}
}
);
}
function abortPendingRequest() {
if (requestRef.current != null) {
ImageLoader.abort(requestRef.current);
requestRef.current = null;
}
}
return abortPendingRequest;
}, [uri, requestRef, updateState, onError, onLoad, onLoadEnd, onLoadStart]);
return (
<View
{...rest}
accessibilityLabel={accessibilityLabel}
onLayout={handleLayout}
pointerEvents={pointerEvents}
ref={ref}
style={[
styles.root,
hasTextAncestor && styles.inline,
imageSizeStyle,
flatStyle
]}
>
<View
style={[
styles.image,
resizeModeStyles[resizeMode],
{ backgroundImage, filter },
backgroundSize != null && { backgroundSize }
]}
suppressHydrationWarning={true}
/>
{hiddenImage}
{createTintColorSVG(tintColor, filterRef.current)}
</View>
);
});
Image.displayName = 'Image';

View File

@@ -77,7 +77,13 @@ type SourceObject = {
width?: number
};
export type ResizeMode = 'center' | 'contain' | 'cover' | 'none' | 'repeat' | 'stretch';
export type ResizeMode =
| 'center'
| 'contain'
| 'cover'
| 'none'
| 'repeat'
| 'stretch';
export type Source = number | string | SourceObject | Array<SourceObject>;

View File

@@ -31,7 +31,9 @@ describe('components/ImageBackground', () => {
const imageStyle = { width: 40, height: 60 };
const { container } = render(<ImageBackground imageStyle={imageStyle} />);
expect(
findImage(container).getAttribute('style').includes('width: 40px; height: 60px;')
findImage(container)
.getAttribute('style')
.includes('width: 40px; height: 60px;')
).toBe(true);
});
});
@@ -40,7 +42,9 @@ describe('components/ImageBackground', () => {
test('sets the style of the container View', () => {
const style = { margin: 40 };
const { container } = render(<ImageBackground style={style} />);
expect(container.firstChild.getAttribute('style')).toEqual('margin: 40px 40px 40px 40px;');
expect(container.firstChild.getAttribute('style')).toEqual(
'margin: 40px 40px 40px 40px;'
);
});
});
});

View File

@@ -32,7 +32,13 @@ const ImageBackground: React.AbstractComponent<
ImageBackgroundProps,
React.ElementRef<typeof View>
> = forwardRef((props, forwardedRef) => {
const { children, style = emptyObject, imageStyle, imageRef, ...rest } = props;
const {
children,
style = emptyObject,
imageStyle,
imageRef,
...rest
} = props;
const { height, width } = StyleSheet.flatten(style);
return (

View File

@@ -9,7 +9,9 @@
*/
import invariant from 'fbjs/lib/invariant';
import requestIdleCallback, { cancelIdleCallback } from '../../modules/requestIdleCallback';
import requestIdleCallback, {
cancelIdleCallback
} from '../../modules/requestIdleCallback';
const InteractionManager = {
Events: {
@@ -20,7 +22,11 @@ const InteractionManager = {
/**
* Schedule a function to run after all interactions have completed.
*/
runAfterInteractions(task: ?Function): { then: Function, done: Function, cancel: Function } {
runAfterInteractions(task: ?Function): {
then: Function,
done: Function,
cancel: Function
} {
let handle;
const promise = new Promise((resolve) => {

View File

@@ -29,7 +29,8 @@ class KeyboardAvoidingView extends React.Component<KeyboardAvoidingViewProps> {
if (!frame || !keyboardFrame) {
return 0;
}
const keyboardY = keyboardFrame.screenY - (this.props.keyboardVerticalOffset || 0);
const keyboardY =
keyboardFrame.screenY - (this.props.keyboardVerticalOffset || 0);
return Math.max(frame.y + frame.height - keyboardY, 0);
}

View File

@@ -5,32 +5,38 @@ import Linking from '..';
describe('apis/Linking', () => {
describe('openURL', () => {
test('calls open with a url and target', (done) => {
jest.spyOn(window, 'open').mockImplementationOnce((url, target, opener) => {
expect(url).toBe('http://foo.com/');
expect(target).toBe('target_name');
expect(opener).toBe('noopener');
done();
});
jest
.spyOn(window, 'open')
.mockImplementationOnce((url, target, opener) => {
expect(url).toBe('http://foo.com/');
expect(target).toBe('target_name');
expect(opener).toBe('noopener');
done();
});
Linking.openURL('http://foo.com', 'target_name');
});
test('defaults target to _blank if not provided', (done) => {
jest.spyOn(window, 'open').mockImplementationOnce((url, target, opener) => {
expect(url).toBe('http://foo.com/');
expect(target).toBe('_blank');
expect(opener).toBe('noopener');
done();
});
jest
.spyOn(window, 'open')
.mockImplementationOnce((url, target, opener) => {
expect(url).toBe('http://foo.com/');
expect(target).toBe('_blank');
expect(opener).toBe('noopener');
done();
});
Linking.openURL('http://foo.com');
});
test('accepts undefined as a target', (done) => {
jest.spyOn(window, 'open').mockImplementationOnce((url, target, opener) => {
expect(url).toBe('http://foo.com/');
expect(target).toBe(undefined);
expect(opener).toBe('noopener');
done();
});
jest
.spyOn(window, 'open')
.mockImplementationOnce((url, target, opener) => {
expect(url).toBe('http://foo.com/');
expect(target).toBe(undefined);
expect(opener).toBe('noopener');
done();
});
Linking.openURL('http://foo.com', undefined);
});
});

View File

@@ -16,7 +16,10 @@ const initialURL = canUseDOM ? window.location.href : '';
type Callback = (...args: any) => void;
type OnOpenCallback = (event: 'onOpen', callback: (url: string) => void) => void;
type OnOpenCallback = (
event: 'onOpen',
callback: (url: string) => void
) => void;
type GenericCallback = (event: string, callback: Callback) => void;
class Linking {
@@ -39,7 +42,10 @@ class Linking {
* Adds a event listener for the specified event. The callback will be called when the
* said event is dispatched.
*/
addEventListener: OnOpenCallback | GenericCallback = (event: string, callback: Callback) => {
addEventListener: OnOpenCallback | GenericCallback = (
event: string,
callback: Callback
) => {
if (!this._eventCallbacks[event]) {
this._eventCallbacks[event] = [callback];
return;
@@ -51,9 +57,14 @@ class Linking {
* Removes a previously added event listener for the specified event. The callback must
* be the same object as the one passed to `addEventListener`.
*/
removeEventListener: OnOpenCallback | GenericCallback = (event: string, callback: Callback) => {
removeEventListener: OnOpenCallback | GenericCallback = (
event: string,
callback: Callback
) => {
const callbacks = this._eventCallbacks[event];
const filteredCallbacks = callbacks.filter((c) => c.toString() !== callback.toString());
const filteredCallbacks = callbacks.filter(
(c) => c.toString() !== callback.toString()
);
this._eventCallbacks[event] = filteredCallbacks;
};
@@ -85,7 +96,10 @@ class Linking {
}
_validateURL(url: string) {
invariant(typeof url === 'string', 'Invalid URL: should be a string. Was: ' + url);
invariant(
typeof url === 'string',
'Invalid URL: should be a string. Was: ' + url
);
invariant(url, 'Invalid URL: cannot be empty');
}
}

View File

@@ -75,7 +75,9 @@ function ModalAnimation(props: ModalAnimationProps): React.Node {
return isRendering || visible
? createElement('div', {
style: isRendering ? getAnimationStyle(animationType, visible) : styles.hidden,
style: isRendering
? getAnimationStyle(animationType, visible)
: styles.hidden,
onAnimationEnd: animationEndCallback,
children
})
@@ -133,9 +135,25 @@ const styles = StyleSheet.create({
}
});
const animatedSlideInStyles = [styles.container, styles.animatedIn, styles.slideIn];
const animatedSlideOutStyles = [styles.container, styles.animatedOut, styles.slideOut];
const animatedFadeInStyles = [styles.container, styles.animatedIn, styles.fadeIn];
const animatedFadeOutStyles = [styles.container, styles.animatedOut, styles.fadeOut];
const animatedSlideInStyles = [
styles.container,
styles.animatedIn,
styles.slideIn
];
const animatedSlideOutStyles = [
styles.container,
styles.animatedOut,
styles.slideOut
];
const animatedFadeInStyles = [
styles.container,
styles.animatedIn,
styles.fadeIn
];
const animatedFadeOutStyles = [
styles.container,
styles.animatedOut,
styles.fadeOut
];
export default ModalAnimation;

View File

@@ -46,7 +46,10 @@ const ModalContent: React.AbstractComponent<
}, [active, onRequestClose]);
const style = React.useMemo(() => {
return [styles.modal, transparent ? styles.modalTransparent : styles.modalOpaque];
return [
styles.modal,
transparent ? styles.modalTransparent : styles.modalOpaque
];
}, [transparent]);
return (

View File

@@ -71,14 +71,18 @@ export type ModalFocusTrapProps = {|
children?: any
|};
const ModalFocusTrap = ({ active, children }: ModalFocusTrapProps): React.Node => {
const ModalFocusTrap = ({
active,
children
}: ModalFocusTrapProps): React.Node => {
const trapElementRef = React.useRef<?HTMLElement>();
const focusRef = React.useRef<{ trapFocusInProgress: boolean, lastFocusedElement: ?HTMLElement }>(
{
trapFocusInProgress: false,
lastFocusedElement: null
}
);
const focusRef = React.useRef<{
trapFocusInProgress: boolean,
lastFocusedElement: ?HTMLElement
}>({
trapFocusInProgress: false,
lastFocusedElement: null
});
React.useEffect(() => {
if (canUseDOM) {
@@ -87,7 +91,11 @@ const ModalFocusTrap = ({ active, children }: ModalFocusTrapProps): React.Node =
// - The modal hasn't fully initialized with an HTMLElement ref
// - Focus is already in the process of being trapped (e.g., we're refocusing)
// - isTrapActive prop being falsey tells us to do nothing
if (trapElementRef.current == null || focusRef.current.trapFocusInProgress || !active) {
if (
trapElementRef.current == null ||
focusRef.current.trapFocusInProgress ||
!active
) {
return;
}
@@ -104,11 +112,17 @@ const ModalFocusTrap = ({ active, children }: ModalFocusTrapProps): React.Node =
// and we're leaving it - this means that we should be looping
// around to the other side of the modal.
let hasFocused = focusFirstDescendant(trapElementRef.current);
if (focusRef.current.lastFocusedElement === document.activeElement) {
if (
focusRef.current.lastFocusedElement === document.activeElement
) {
hasFocused = focusLastDescendant(trapElementRef.current);
}
// If we couldn't focus a new element then we need to focus onto the trap target
if (!hasFocused && trapElementRef.current != null && document.activeElement) {
if (
!hasFocused &&
trapElementRef.current != null &&
document.activeElement
) {
UIManager.focus(trapElementRef.current);
}
}
@@ -132,7 +146,10 @@ const ModalFocusTrap = ({ active, children }: ModalFocusTrapProps): React.Node =
if (canUseDOM) {
const lastFocusedElementOutsideTrap = document.activeElement;
return function () {
if (lastFocusedElementOutsideTrap && document.contains(lastFocusedElementOutsideTrap)) {
if (
lastFocusedElementOutsideTrap &&
document.contains(lastFocusedElementOutsideTrap)
) {
UIManager.focus(lastFocusedElementOutsideTrap);
}
};

View File

@@ -25,7 +25,11 @@ describe('components/Modal', () => {
test('forwards props', () => {
const { getByTestId } = render(
<Modal accessibilityLabel="label" accessibilityLabelledBy="labelledby" testID="root" />
<Modal
accessibilityLabel="label"
accessibilityLabelledBy="labelledby"
testID="root"
/>
);
expect(getByTestId('root')).toMatchSnapshot();
});
@@ -189,7 +193,9 @@ describe('components/Modal', () => {
test('executes onShow callback when visibility changes', () => {
const onShowCallback = jest.fn();
const { rerender } = render(<Modal onShow={onShowCallback} visible={false} />);
const { rerender } = render(
<Modal onShow={onShowCallback} visible={false} />
);
expect(onShowCallback).toBeCalledTimes(0);
rerender(<Modal onShow={onShowCallback} visible={true} />);
expect(onShowCallback).toBeCalledTimes(1);
@@ -197,19 +203,29 @@ describe('components/Modal', () => {
test('executes onDismiss callback when visibility changes', () => {
const onDismissCallback = jest.fn();
const { rerender } = render(<Modal onDismiss={onDismissCallback} visible={true} />);
const { rerender } = render(
<Modal onDismiss={onDismissCallback} visible={true} />
);
expect(onDismissCallback).toBeCalledTimes(0);
rerender(<Modal onDismiss={onDismissCallback} visible={false} />);
expect(onDismissCallback).toBeCalledTimes(1);
});
test('animationTypes none is the same as omitting', () => {
const { rerender, baseElement } = render(<Modal animationType={'none'} visible={true} />);
const { rerender, baseElement } = render(
<Modal animationType={'none'} visible={true} />
);
const animationNoneElement = baseElement.lastChild.lastChild;
const animationNoneStyle = window.getComputedStyle(animationNoneElement, null);
const animationNoneStyle = window.getComputedStyle(
animationNoneElement,
null
);
rerender(<Modal visible={true} />);
const animationMissingElement = baseElement.lastChild.lastChild;
const animationMissingStyle = window.getComputedStyle(animationMissingElement, null);
const animationMissingStyle = window.getComputedStyle(
animationMissingElement,
null
);
const styleProps = new Set();
for (let i = 0; i < animationNoneStyle.length; i++) {
@@ -231,7 +247,8 @@ describe('components/Modal', () => {
<a href={'#hello'}>Hello</a>
</Modal>
);
const dialogElement = baseElement.lastChild.querySelector('[role="dialog"]');
const dialogElement =
baseElement.lastChild.querySelector('[role="dialog"]');
expect(dialogElement).not.toBeNull();
expect(dialogElement.getAttribute('role')).toBe('dialog');
expect(dialogElement.getAttribute('aria-modal')).toBe('true');
@@ -309,7 +326,9 @@ describe('components/Modal', () => {
</>
);
const modalTrigger = document.querySelector('[data-testid="modal-trigger"]');
const modalTrigger = document.querySelector(
'[data-testid="modal-trigger"]'
);
modalTrigger.focus();
expect(document.activeElement).toBe(modalTrigger);
@@ -368,7 +387,9 @@ describe('components/Modal', () => {
</>
);
const modalTrigger = document.querySelector('[data-testid="modal-trigger"]');
const modalTrigger = document.querySelector(
'[data-testid="modal-trigger"]'
);
modalTrigger.focus();
expect(document.activeElement).toBe(modalTrigger);
@@ -444,10 +465,13 @@ describe('components/Modal', () => {
</>
);
const insideStartElement = document.querySelector('[data-testid="inside-a"]');
const insideStartElement = document.querySelector(
'[data-testid="inside-a"]'
);
const insideEndElement = document.querySelector('[data-testid="inside-c"]');
// This is ugly - perhaps there's a better way?
const focusBracket = insideEndElement.parentNode.parentNode.parentNode.nextSibling;
const focusBracket =
insideEndElement.parentNode.parentNode.parentNode.nextSibling;
insideEndElement.focus();
focusBracket.focus();
expect(document.activeElement).toBe(insideStartElement);
@@ -470,10 +494,13 @@ describe('components/Modal', () => {
</>
);
const insideStartElement = document.querySelector('[data-testid="inside-a"]');
const insideStartElement = document.querySelector(
'[data-testid="inside-a"]'
);
const insideEndElement = document.querySelector('[data-testid="inside-c"]');
// This is ugly - perhaps there's a better way?
const focusBracket = insideEndElement.parentNode.parentNode.parentNode.previousSibling;
const focusBracket =
insideEndElement.parentNode.parentNode.parentNode.previousSibling;
insideStartElement.focus();
focusBracket.focus();
expect(document.activeElement).toBe(insideEndElement);
@@ -614,10 +641,10 @@ describe('components/Modal', () => {
);
// This is kind of ugly but I can't find a better way to target just the animation div
const animationAElement = getByTestId('a').parentElement.parentElement.parentElement
.parentElement;
const animationBElement = getByTestId('b').parentElement.parentElement.parentElement
.parentElement;
const animationAElement =
getByTestId('a').parentElement.parentElement.parentElement.parentElement;
const animationBElement =
getByTestId('b').parentElement.parentElement.parentElement.parentElement;
fireEvent.animationEnd(animationAElement);
fireEvent.animationEnd(animationBElement);

View File

@@ -22,13 +22,24 @@ export type ModalProps = {
children: any,
hardwareAccelerated?: ?boolean,
onDismiss?: ?() => mixed,
onOrientationChange?: ?(e: {| orientation: 'portrait' | 'landscape' |}) => void,
onOrientationChange?: ?(e: {|
orientation: 'portrait' | 'landscape'
|}) => void,
onRequestClose?: ?() => void,
onShow?: ?() => void,
presentationStyle?: ?('fullScreen' | 'pageSheet' | 'formSheet' | 'overFullScreen'),
presentationStyle?: ?(
| 'fullScreen'
| 'pageSheet'
| 'formSheet'
| 'overFullScreen'
),
statusBarTranslucent?: ?boolean,
supportedOrientations?: ?Array<
'portrait' | 'portrait-upside-down' | 'landscape' | 'landscape-left' | 'landscape-right'
| 'portrait'
| 'portrait-upside-down'
| 'landscape'
| 'landscape-left'
| 'landscape-right'
>,
transparent?: ?boolean,
visible?: ?boolean

View File

@@ -80,7 +80,10 @@ export type GestureState = {|
_accountsForMovesUpTo: number
|};
type ActiveCallback = (event: PressEvent, gestureState: GestureState) => boolean;
type ActiveCallback = (
event: PressEvent,
gestureState: GestureState
) => boolean;
type PassiveCallback = (event: PressEvent, gestureState: GestureState) => void;
type PanResponderConfig = $ReadOnly<{|
@@ -171,8 +174,14 @@ const PanResponder = {
touchHistory: $PropertyType<PressEvent, 'touchHistory'>
) {
const movedAfter = gestureState._accountsForMovesUpTo;
const prevX = previousCentroidXOfTouchesChangedAfter(touchHistory, movedAfter);
const prevY = previousCentroidYOfTouchesChangedAfter(touchHistory, movedAfter);
const prevX = previousCentroidXOfTouchesChangedAfter(
touchHistory,
movedAfter
);
const prevY = previousCentroidYOfTouchesChangedAfter(
touchHistory,
movedAfter
);
const prevDeltaX = gestureState.deltaX;
const prevDeltaY = gestureState.deltaY;
@@ -181,7 +190,8 @@ const PanResponder = {
const deltaX = prevDeltaX + (x - prevX);
const deltaY = prevDeltaY + (y - prevY);
// TODO: This must be filtered intelligently.
const dt = touchHistory.mostRecentTimeStamp - gestureState._accountsForMovesUpTo;
const dt =
touchHistory.mostRecentTimeStamp - gestureState._accountsForMovesUpTo;
gestureState.deltaX = deltaX;
gestureState.deltaY = deltaY;
@@ -201,9 +211,7 @@ const PanResponder = {
* gestureState once in the capture phase and can use it in the bubble phase
* as well.
*/
create(
config: PanResponderConfig
): {|
create(config: PanResponderConfig): {|
getInteractionHandle: () => ?number,
panHandlers: {|
onMoveShouldSetResponder: (event: PressEvent) => boolean,
@@ -270,7 +278,8 @@ const PanResponder = {
if (event.nativeEvent.touches.length === 1) {
PanResponder._initializeGestureState(gestureState);
}
gestureState.numberActiveTouches = event.touchHistory.numberActiveTouches;
gestureState.numberActiveTouches =
event.touchHistory.numberActiveTouches;
return onStartShouldSetResponderCapture != null
? onStartShouldSetResponderCapture(event, gestureState)
: false;
@@ -293,7 +302,8 @@ const PanResponder = {
onResponderGrant(event: PressEvent): void {
if (!interactionState.handle) {
interactionState.handle = InteractionManager.createInteractionHandle();
interactionState.handle =
InteractionManager.createInteractionHandle();
}
gestureState.initialX = currentCentroidX(event.touchHistory);
gestureState.initialY = currentCentroidY(event.touchHistory);
@@ -305,7 +315,12 @@ const PanResponder = {
},
onResponderReject(event: PressEvent): void {
clearInteractionHandle(interactionState, onPanReject, event, gestureState);
clearInteractionHandle(
interactionState,
onPanReject,
event,
gestureState
);
},
onResponderStart(event: PressEvent): void {
@@ -320,7 +335,10 @@ const PanResponder = {
const touchHistory = event.touchHistory;
// Guard against the dispatch of two touch moves when there are two
// simultaneously changed touches.
if (gestureState._accountsForMovesUpTo === touchHistory.mostRecentTimeStamp) {
if (
gestureState._accountsForMovesUpTo ===
touchHistory.mostRecentTimeStamp
) {
return;
}
// Filter out any touch moves past the first one - we would have
@@ -338,12 +356,22 @@ const PanResponder = {
},
onResponderRelease(event: PressEvent): void {
clearInteractionHandle(interactionState, onPanRelease, event, gestureState);
clearInteractionHandle(
interactionState,
onPanRelease,
event,
gestureState
);
PanResponder._initializeGestureState(gestureState);
},
onResponderTerminate(event: PressEvent): void {
clearInteractionHandle(interactionState, onPanTerminate, event, gestureState);
clearInteractionHandle(
interactionState,
onPanTerminate,
event,
gestureState
);
PanResponder._initializeGestureState(gestureState);
},

View File

@@ -15,7 +15,12 @@ describe('apis/Platform', () => {
test('chooses "web"', () => {
expect(
Platform.select({ android: 'android', ios: 'ios', web: 'web', default: 'default' })
Platform.select({
android: 'android',
ios: 'ios',
web: 'web',
default: 'default'
})
).toEqual('web');
});
});

View File

@@ -26,7 +26,9 @@ describe('components/Pressable', () => {
describe('prop "accessibilityLiveRegion"', () => {
test('value is set', () => {
const { container } = render(<Pressable accessibilityLiveRegion="polite" />);
const { container } = render(
<Pressable accessibilityLiveRegion="polite" />
);
expect(container.firstChild).toMatchSnapshot();
});
});
@@ -73,7 +75,9 @@ describe('components/Pressable', () => {
act(() => {
({ container } = render(
<Pressable
children={({ focused }) => (focused ? <div data-testid="focus-content" /> : null)}
children={({ focused }) =>
focused ? <div data-testid="focus-content" /> : null
}
onBlur={onBlur}
onFocus={onFocus}
ref={ref}
@@ -101,7 +105,14 @@ describe('components/Pressable', () => {
const onFocus = jest.fn();
const ref = React.createRef();
act(() => {
render(<Pressable disabled={true} onBlur={onBlur} onFocus={onFocus} ref={ref} />);
render(
<Pressable
disabled={true}
onBlur={onBlur}
onFocus={onFocus}
ref={ref}
/>
);
});
const target = createEventTarget(ref.current);
const body = createEventTarget(document.body);
@@ -123,7 +134,9 @@ describe('components/Pressable', () => {
act(() => {
({ container } = render(
<Pressable
children={({ hovered }) => (hovered ? <div data-testid="hover-content" /> : null)}
children={({ hovered }) =>
hovered ? <div data-testid="hover-content" /> : null
}
onHoverIn={onHoverIn}
onHoverOut={onHoverOut}
ref={ref}
@@ -155,7 +168,9 @@ describe('components/Pressable', () => {
act(() => {
({ container } = render(
<Pressable
children={({ pressed }) => (pressed ? <div data-testid="press-content" /> : null)}
children={({ pressed }) =>
pressed ? <div data-testid="press-content" /> : null
}
onContextMenu={onContextMenu}
onPress={onPress}
onPressIn={onPressIn}
@@ -197,7 +212,9 @@ describe('components/Pressable', () => {
const [shown, setShown] = React.useState(true);
return shown ? (
<Pressable
children={({ pressed }) => (pressed ? <div data-testid="press-content" /> : null)}
children={({ pressed }) =>
pressed ? <div data-testid="press-content" /> : null
}
onPress={(e) => {
onPress(e);
setShown(false);

View File

@@ -129,7 +129,8 @@ function Pressable(props: Props, forwardedRef): React.Node {
const pressEventHandlers = usePressEvents(hostRef, pressConfig);
const { onContextMenu: onContextMenuPress, onKeyDown: onKeyDownPress } = pressEventHandlers;
const { onContextMenu: onContextMenuPress, onKeyDown: onKeyDownPress } =
pressEventHandlers;
useHover(hostRef, {
contain: true,
@@ -232,4 +233,7 @@ const styles = StyleSheet.create({
const MemoedPressable = memo(forwardRef(Pressable));
MemoedPressable.displayName = 'Pressable';
export default (MemoedPressable: React.AbstractComponent<Props, React.ElementRef<typeof View>>);
export default (MemoedPressable: React.AbstractComponent<
Props,
React.ElementRef<typeof View>
>);

View File

@@ -17,7 +17,9 @@ describe('components/ProgressBar', () => {
});
test('is ignored when "indeterminate" is "true"', () => {
const { container } = render(<ProgressBar indeterminate progress={0.5} />);
const { container } = render(
<ProgressBar indeterminate progress={0.5} />
);
expect(container.firstChild.getAttribute('aria-valuenow')).toBe(null);
});
});

View File

@@ -33,7 +33,9 @@ const SafeAreaView: React.AbstractComponent<
React.ElementRef<typeof View>
> = React.forwardRef((props, ref) => {
const { style, ...rest } = props;
return <View {...rest} ref={ref} style={StyleSheet.compose(styles.root, style)} />;
return (
<View {...rest} ref={ref} style={StyleSheet.compose(styles.root, style)} />
);
});
SafeAreaView.displayName = 'SafeAreaView';

View File

@@ -112,7 +112,12 @@ const ScrollViewBase: React.AbstractComponent<
}, 100);
if (scrollState.current.isScrolling) {
// Scroll last tick may have changed, check if we need to notify
if (shouldEmitScrollEvent(scrollState.current.scrollLastTick, scrollEventThrottle)) {
if (
shouldEmitScrollEvent(
scrollState.current.scrollLastTick,
scrollEventThrottle
)
) {
handleScrollTick(e);
}
} else {
@@ -142,7 +147,8 @@ const ScrollViewBase: React.AbstractComponent<
}
const hideScrollbar =
showsHorizontalScrollIndicator === false || showsVerticalScrollIndicator === false;
showsHorizontalScrollIndicator === false ||
showsVerticalScrollIndicator === false;
return (
<View

View File

@@ -11,7 +11,9 @@ describe('components/ScrollView', () => {
const onScroll = jest.fn();
const ref = React.createRef();
act(() => {
render(<ScrollView onScroll={onScroll} ref={ref} scrollEventThrottle={16} />);
render(
<ScrollView onScroll={onScroll} ref={ref} scrollEventThrottle={16} />
);
});
const target = createEventTarget(findDOMNode(ref.current));
act(() => {
@@ -50,11 +52,15 @@ describe('components/ScrollView', () => {
const ref = jest.fn();
let rerender;
act(() => {
({ rerender } = render(<ScrollView nativeID="123" ref={ref} style={{ borderWidth: 5 }} />));
({ rerender } = render(
<ScrollView nativeID="123" ref={ref} style={{ borderWidth: 5 }} />
));
});
expect(ref).toHaveBeenCalledTimes(1);
act(() => {
rerender(<ScrollView nativeID="1234" ref={ref} style={{ borderWidth: 6 }} />);
rerender(
<ScrollView nativeID="1234" ref={ref} style={{ borderWidth: 6 }} />
);
});
expect(ref).toHaveBeenCalledTimes(1);
});
@@ -83,13 +89,17 @@ describe('components/ScrollView', () => {
expect(typeof node.scrollToEnd === 'function').toBe(true);
expect(typeof node.flashScrollIndicators === 'function').toBe(true);
expect(typeof node.scrollResponderZoomTo === 'function').toBe(true);
expect(typeof node.scrollResponderScrollNativeHandleToKeyboard === 'function').toBe(true);
expect(
typeof node.scrollResponderScrollNativeHandleToKeyboard === 'function'
).toBe(true);
});
});
describe('prop "refreshControl"', () => {
test('without', () => {
const { container } = render(<ScrollView style={{ backgroundColor: 'red' }} />);
const { container } = render(
<ScrollView style={{ backgroundColor: 'red' }} />
);
expect(container.firstChild).toMatchSnapshot();
});

View File

@@ -117,7 +117,8 @@ const ScrollView = ((createReactClass({
const animated = (options && options.animated) !== false;
const { horizontal } = this.props;
const scrollResponder = this.getScrollResponder();
const scrollResponderNode = scrollResponder.scrollResponderGetScrollableNode();
const scrollResponderNode =
scrollResponder.scrollResponderGetScrollableNode();
const x = horizontal ? scrollResponderNode.scrollWidth : 0;
const y = horizontal ? 0 : scrollResponderNode.scrollHeight;
scrollResponder.scrollResponderScrollTo({ x, y, animated });
@@ -158,11 +159,13 @@ const ScrollView = ((createReactClass({
};
}
const hasStickyHeaderIndices = !horizontal && Array.isArray(stickyHeaderIndices);
const hasStickyHeaderIndices =
!horizontal && Array.isArray(stickyHeaderIndices);
const children =
hasStickyHeaderIndices || pagingEnabled
? React.Children.map(this.props.children, (child, i) => {
const isSticky = hasStickyHeaderIndices && stickyHeaderIndices.indexOf(i) > -1;
const isSticky =
hasStickyHeaderIndices && stickyHeaderIndices.indexOf(i) > -1;
if (child != null && (isSticky || pagingEnabled)) {
return (
<View
@@ -208,12 +211,16 @@ const ScrollView = ((createReactClass({
onScrollEndDrag: this.scrollResponderHandleScrollEndDrag,
onMomentumScrollBegin: this.scrollResponderHandleMomentumScrollBegin,
onMomentumScrollEnd: this.scrollResponderHandleMomentumScrollEnd,
onStartShouldSetResponder: this.scrollResponderHandleStartShouldSetResponder,
onStartShouldSetResponderCapture: this.scrollResponderHandleStartShouldSetResponderCapture,
onScrollShouldSetResponder: this.scrollResponderHandleScrollShouldSetResponder,
onStartShouldSetResponder:
this.scrollResponderHandleStartShouldSetResponder,
onStartShouldSetResponderCapture:
this.scrollResponderHandleStartShouldSetResponderCapture,
onScrollShouldSetResponder:
this.scrollResponderHandleScrollShouldSetResponder,
onScroll: this._handleScroll,
onResponderGrant: this.scrollResponderHandleResponderGrant,
onResponderTerminationRequest: this.scrollResponderHandleTerminationRequest,
onResponderTerminationRequest:
this.scrollResponderHandleTerminationRequest,
onResponderTerminate: this.scrollResponderHandleTerminate,
onResponderRelease: this.scrollResponderHandleResponderRelease,
onResponderReject: this.scrollResponderHandleResponderReject
@@ -221,7 +228,10 @@ const ScrollView = ((createReactClass({
const ScrollViewClass = ScrollViewBase;
invariant(ScrollViewClass !== undefined, 'ScrollViewClass must not be undefined');
invariant(
ScrollViewClass !== undefined,
'ScrollViewClass must not be undefined'
);
const scrollView = (
<ScrollViewClass {...props} ref={this._setScrollNodeRef}>
@@ -230,7 +240,11 @@ const ScrollView = ((createReactClass({
);
if (refreshControl) {
return React.cloneElement(refreshControl, { style: props.style }, scrollView);
return React.cloneElement(
refreshControl,
{ style: props.style },
scrollView
);
}
return scrollView;
@@ -280,7 +294,8 @@ const ScrollView = ((createReactClass({
node.scrollToEnd = this.scrollToEnd;
node.flashScrollIndicators = this.flashScrollIndicators;
node.scrollResponderZoomTo = this.scrollResponderZoomTo;
node.scrollResponderScrollNativeHandleToKeyboard = this.scrollResponderScrollNativeHandleToKeyboard;
node.scrollResponderScrollNativeHandleToKeyboard =
this.scrollResponderScrollNativeHandleToKeyboard;
}
const ref = mergeRefs(this.props.forwardedRef);
ref(node);

View File

@@ -24,7 +24,10 @@ class Share {
typeof content.url === 'string' || typeof content.message === 'string',
'At least one of URL and message is required'
);
invariant(typeof options === 'object' && options !== null, 'Options must be a valid object');
invariant(
typeof options === 'object' && options !== null,
'Options must be a valid object'
);
invariant(
!content.title || typeof content.title === 'string',
'Invalid title: title should be a string.'
@@ -37,7 +40,9 @@ class Share {
url: content.url
});
} else {
return Promise.reject(new Error('Share is not supported in this browser'));
return Promise.reject(
new Error('Share is not supported in this browser')
);
}
}

View File

@@ -134,7 +134,8 @@ describe('compiler/createReactDOMStyle', () => {
describe('fontFamily', () => {
test('general case', () => {
expect(createReactDOMStyle({ fontFamily: 'Georgia, Times, serif' })).toMatchInlineSnapshot(`
expect(createReactDOMStyle({ fontFamily: 'Georgia, Times, serif' }))
.toMatchInlineSnapshot(`
{
"fontFamily": "Georgia, Times, serif",
}
@@ -142,7 +143,8 @@ describe('compiler/createReactDOMStyle', () => {
});
test('"monospace"', () => {
expect(createReactDOMStyle({ fontFamily: 'monospace' })).toMatchInlineSnapshot(`
expect(createReactDOMStyle({ fontFamily: 'monospace' }))
.toMatchInlineSnapshot(`
{
"fontFamily": "monospace,monospace",
}
@@ -150,12 +152,14 @@ describe('compiler/createReactDOMStyle', () => {
});
test('"System"', () => {
expect(createReactDOMStyle({ fontFamily: 'System' })).toMatchInlineSnapshot(`
expect(createReactDOMStyle({ fontFamily: 'System' }))
.toMatchInlineSnapshot(`
{
"fontFamily": "-apple-system,BlinkMacSystemFont,\\"Segoe UI\\",Roboto,Helvetica,Arial,sans-serif",
}
`);
expect(createReactDOMStyle({ font: '14px System' })).toMatchInlineSnapshot(`
expect(createReactDOMStyle({ font: '14px System' }))
.toMatchInlineSnapshot(`
{
"font": "14px -apple-system,BlinkMacSystemFont,\\"Segoe UI\\",Roboto,Helvetica,Arial,sans-serif",
}
@@ -163,12 +167,14 @@ describe('compiler/createReactDOMStyle', () => {
});
test('"Noto, System"', () => {
expect(createReactDOMStyle({ fontFamily: 'Noto, System' })).toMatchInlineSnapshot(`
expect(createReactDOMStyle({ fontFamily: 'Noto, System' }))
.toMatchInlineSnapshot(`
{
"fontFamily": "Noto,-apple-system,BlinkMacSystemFont,\\"Segoe UI\\",Roboto,Helvetica,Arial,sans-serif",
}
`);
expect(createReactDOMStyle({ font: '14px Noto, System' })).toMatchInlineSnapshot(`
expect(createReactDOMStyle({ font: '14px Noto, System' }))
.toMatchInlineSnapshot(`
{
"font": "14px Noto, -apple-system,BlinkMacSystemFont,\\"Segoe UI\\",Roboto,Helvetica,Arial,sans-serif",
}
@@ -186,7 +192,9 @@ describe('compiler/createReactDOMStyle', () => {
});
test('fontVariant', () => {
expect(createReactDOMStyle({ fontVariant: ['common-ligatures', 'small-caps'] })).toEqual({
expect(
createReactDOMStyle({ fontVariant: ['common-ligatures', 'small-caps'] })
).toEqual({
fontVariant: 'common-ligatures small-caps'
});
});
@@ -204,7 +212,8 @@ describe('compiler/createReactDOMStyle', () => {
describe('transform', () => {
// passthrough if transform value is ever a string
test('string', () => {
const transform = 'perspective(50px) scaleX(20) translateX(20px) rotate(20deg)';
const transform =
'perspective(50px) scaleX(20) translateX(20px) rotate(20deg)';
const style = { transform };
const resolved = createReactDOMStyle(style);

View File

@@ -310,7 +310,9 @@ describe('StyleSheet/compile', () => {
marginRight: '5px',
paddingRight: '10px'
};
expect(inline(initial, isRTL)).toEqual(isRTL ? expectedRTL : expectedLTR);
expect(inline(initial, isRTL)).toEqual(
isRTL ? expectedRTL : expectedLTR
);
});
test(`converts "end" properties for ${dir}`, () => {
@@ -339,7 +341,9 @@ describe('StyleSheet/compile', () => {
marginLeft: '5px',
paddingLeft: '10px'
};
expect(inline(initial, isRTL)).toEqual(isRTL ? expectedRTL : expectedLTR);
expect(inline(initial, isRTL)).toEqual(
isRTL ? expectedRTL : expectedLTR
);
});
test(`converts "start" values for ${dir}`, () => {
@@ -362,7 +366,9 @@ describe('StyleSheet/compile', () => {
textAlign: 'right',
transitionProperty: 'right'
};
expect(inline(initial, isRTL)).toEqual(isRTL ? expectedRTL : expectedLTR);
expect(inline(initial, isRTL)).toEqual(
isRTL ? expectedRTL : expectedLTR
);
});
test(`converts "end" values for ${dir}`, () => {
@@ -385,7 +391,9 @@ describe('StyleSheet/compile', () => {
textAlign: 'left',
transitionProperty: 'left'
};
expect(inline(initial, isRTL)).toEqual(isRTL ? expectedRTL : expectedLTR);
expect(inline(initial, isRTL)).toEqual(
isRTL ? expectedRTL : expectedLTR
);
});
test('end/start properties take precedence over left/right', () => {

View File

@@ -140,7 +140,12 @@ describe('StyleSheet', () => {
});
test('should recursively flatten arrays', () => {
const style = StyleSheet.flatten([null, [], [{ order: 2 }, { opacity: 1 }], { order: 3 }]);
const style = StyleSheet.flatten([
null,
[],
[{ order: 2 }, { opacity: 1 }],
{ order: 3 }
]);
expect(style).toMatchInlineSnapshot(`
{
"opacity": 1,
@@ -240,17 +245,29 @@ describe('StyleSheet', () => {
color: null
};
const [className1, inlineStyle1] = StyleSheet([styleACompiled, styleBCompiled, styleBInline]);
const [className1, inlineStyle1] = StyleSheet([
styleACompiled,
styleBCompiled,
styleBInline
]);
expect(className1).toBe('display-block');
expect(inlineStyle1).toEqual({ backgroundColor: 'rgba(0,0,255,1.00)' });
const [className2, inlineStyle2] = StyleSheet([styleACompiled, styleBInline, styleBCompiled]);
expect(className2).toBe('display-block backgroundColor-green color-green');
const [className2, inlineStyle2] = StyleSheet([
styleACompiled,
styleBInline,
styleBCompiled
]);
expect(className2).toBe(
'display-block backgroundColor-green color-green'
);
expect(inlineStyle2).toEqual(null);
});
test('long form inline style properties take precedence over static shorthand properties', () => {
const styles1 = StyleSheet.create({ test: { paddingHorizontal: '40px' } });
const styles1 = StyleSheet.create({
test: { paddingHorizontal: '40px' }
});
const inlineStyle1 = { padding: '8px', paddingHorizontal: '40px' };
expect(StyleSheet([styles1.test, inlineStyle1])).toMatchInlineSnapshot(`
[
@@ -301,7 +318,8 @@ describe('StyleSheet', () => {
},
]
`);
expect(StyleSheet(inlineStyle, { writingDirection })).toMatchInlineSnapshot(`
expect(StyleSheet(inlineStyle, { writingDirection }))
.toMatchInlineSnapshot(`
[
"",
{
@@ -313,7 +331,10 @@ describe('StyleSheet', () => {
`);
expect(
StyleSheet(
[inlineStyle, { marginLeft: 1, marginEnd: 0, marginStart: 0, marginRight: 11 }],
[
inlineStyle,
{ marginLeft: 1, marginEnd: 0, marginStart: 0, marginRight: 11 }
],
{ writingDirection }
)
).toMatchInlineSnapshot(`
@@ -327,8 +348,11 @@ describe('StyleSheet', () => {
},
]
`);
expect(StyleSheet([inlineStyle, { marginEnd: null, marginLeft: 11 }], { writingDirection }))
.toMatchInlineSnapshot(`
expect(
StyleSheet([inlineStyle, { marginEnd: null, marginLeft: 11 }], {
writingDirection
})
).toMatchInlineSnapshot(`
[
"",
{
@@ -347,7 +371,8 @@ describe('StyleSheet', () => {
null,
]
`);
expect(StyleSheet(staticStyle, { writingDirection })).toMatchInlineSnapshot(`
expect(StyleSheet(staticStyle, { writingDirection }))
.toMatchInlineSnapshot(`
[
"r-right-1bnbe1j r-textAlign-1ff274t r-marginLeft-1n0xq6e",
null,
@@ -356,7 +381,10 @@ describe('StyleSheet', () => {
// logical wins
expect(
StyleSheet(
[staticStyle, { marginLeft: 1, marginEnd: 0, marginStart: 0, marginRight: 11 }],
[
staticStyle,
{ marginLeft: 1, marginEnd: 0, marginStart: 0, marginRight: 11 }
],
{
writingDirection
}
@@ -371,8 +399,11 @@ describe('StyleSheet', () => {
]
`);
// logical can be nulled
expect(StyleSheet([staticStyle, { marginEnd: null, marginLeft: 11 }], { writingDirection }))
.toMatchInlineSnapshot(`
expect(
StyleSheet([staticStyle, { marginEnd: null, marginLeft: 11 }], {
writingDirection
})
).toMatchInlineSnapshot(`
[
"r-right-1bnbe1j r-textAlign-1ff274t",
{

View File

@@ -53,7 +53,9 @@ describe('StyleSheet/preprocess', () => {
});
test('textShadowOffset only', () => {
expect(preprocess({ textShadowOffset: { width: 1, height: 2 } })).toEqual({});
expect(preprocess({ textShadowOffset: { width: 1, height: 2 } })).toEqual(
{}
);
});
test('textShadowRadius only', () => {
@@ -86,8 +88,12 @@ describe('StyleSheet/preprocess', () => {
});
test('textShadowColor and textShadowRadius only', () => {
expect(preprocess({ textShadowColor: 'red', textShadowRadius: 0 })).toEqual({});
expect(preprocess({ textShadowColor: 'red', textShadowRadius: 5 })).toEqual({
expect(
preprocess({ textShadowColor: 'red', textShadowRadius: 0 })
).toEqual({});
expect(
preprocess({ textShadowColor: 'red', textShadowRadius: 5 })
).toEqual({
textShadow: '0px 0px 5px rgba(255,0,0,1.00)'
});
});

View File

@@ -46,15 +46,30 @@ const SYSTEM_FONT_STACK =
'-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif';
const STYLE_SHORT_FORM_EXPANSIONS = {
borderColor: ['borderTopColor', 'borderRightColor', 'borderBottomColor', 'borderLeftColor'],
borderColor: [
'borderTopColor',
'borderRightColor',
'borderBottomColor',
'borderLeftColor'
],
borderRadius: [
'borderTopLeftRadius',
'borderTopRightRadius',
'borderBottomRightRadius',
'borderBottomLeftRadius'
],
borderStyle: ['borderTopStyle', 'borderRightStyle', 'borderBottomStyle', 'borderLeftStyle'],
borderWidth: ['borderTopWidth', 'borderRightWidth', 'borderBottomWidth', 'borderLeftWidth'],
borderStyle: [
'borderTopStyle',
'borderRightStyle',
'borderBottomStyle',
'borderLeftStyle'
],
borderWidth: [
'borderTopWidth',
'borderRightWidth',
'borderBottomWidth',
'borderLeftWidth'
],
marginHorizontal: ['marginRight', 'marginLeft'],
marginVertical: ['marginTop', 'marginBottom'],
overflow: ['overflowX', 'overflowY'],

View File

@@ -28,11 +28,16 @@ function murmurhash2_32_gc(str, seed) {
((str.charCodeAt(++i) & 0xff) << 16) |
((str.charCodeAt(++i) & 0xff) << 24);
k = (k & 0xffff) * 0x5bd1e995 + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16);
k =
(k & 0xffff) * 0x5bd1e995 + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16);
k ^= k >>> 24;
k = (k & 0xffff) * 0x5bd1e995 + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16);
k =
(k & 0xffff) * 0x5bd1e995 + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16);
h = ((h & 0xffff) * 0x5bd1e995 + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16)) ^ k;
h =
((h & 0xffff) * 0x5bd1e995 +
((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16)) ^
k;
l -= 4;
++i;
@@ -45,7 +50,9 @@ function murmurhash2_32_gc(str, seed) {
h ^= (str.charCodeAt(i + 1) & 0xff) << 8;
case 1:
h ^= str.charCodeAt(i) & 0xff;
h = (h & 0xffff) * 0x5bd1e995 + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16);
h =
(h & 0xffff) * 0x5bd1e995 +
((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16);
}
h ^= h >>> 13;

View File

@@ -208,7 +208,8 @@ export function classic(style: Style, name: string): CompilerOutput {
const selector = `.${identifier}`;
let animationName;
if (animationKeyframes != null) {
const [animationNames, keyframesRules] = processKeyframesValue(animationKeyframes);
const [animationNames, keyframesRules] =
processKeyframesValue(animationKeyframes);
animationName = animationNames.join(',');
compiledRules.push(...keyframesRules);
}
@@ -223,7 +224,10 @@ export function classic(style: Style, name: string): CompilerOutput {
* Compile simple style object to inline DOM styles.
* No support for 'animationKeyframes', 'placeholderTextColor', 'scrollbarWidth', or 'pointerEvents'.
*/
export function inline(originalStyle: Style, isRTL?: boolean): { [key: string]: mixed } {
export function inline(
originalStyle: Style,
isRTL?: boolean
): { [key: string]: mixed } {
const style = originalStyle || emptyObject;
const frozenProps = {};
const nextStyle = {};
@@ -233,7 +237,10 @@ export function inline(originalStyle: Style, isRTL?: boolean): { [key: string]:
let prop = originalProp;
let value = originalValue;
if (!Object.prototype.hasOwnProperty.call(style, originalProp) || originalValue == null) {
if (
!Object.prototype.hasOwnProperty.call(style, originalProp) ||
originalValue == null
) {
continue;
}
@@ -253,12 +260,16 @@ export function inline(originalStyle: Style, isRTL?: boolean): { [key: string]:
// BiDi flip transitionProperty value
if (originalProp === 'transitionProperty') {
// $FlowFixMe
const originalValues = Array.isArray(originalValue) ? originalValue : [originalValue];
const originalValues = Array.isArray(originalValue)
? originalValue
: [originalValue];
originalValues.forEach((val, i) => {
if (typeof val === 'string') {
const valuePolyfill = PROPERTIES_I18N[val];
if (valuePolyfill != null) {
originalValues[i] = isRTL ? PROPERTIES_FLIP[valuePolyfill] : valuePolyfill;
originalValues[i] = isRTL
? PROPERTIES_FLIP[valuePolyfill]
: valuePolyfill;
}
}
});
@@ -281,7 +292,10 @@ export function inline(originalStyle: Style, isRTL?: boolean): { [key: string]:
* Create a value string that normalizes different input values with a common
* output.
*/
export function stringifyValueWithProperty(value: Value, property: ?string): string {
export function stringifyValueWithProperty(
value: Value,
property: ?string
): string {
// e.g., 0 => '0px', 'black' => 'rgba(0,0,0,1)'
const normalizedValue = normalizeValueWithProperty(value, property);
return typeof normalizedValue !== 'string'
@@ -302,7 +316,9 @@ function createAtomicRules(identifier: string, property, value): Rules {
switch (property) {
case 'animationKeyframes': {
const [animationNames, keyframesRules] = processKeyframesValue(value);
const block = createDeclarationBlock({ animationName: animationNames.join(',') });
const block = createDeclarationBlock({
animationName: animationNames.join(',')
});
rules.push(`${selector}${block}`, ...keyframesRules);
break;
}
@@ -433,7 +449,9 @@ function processKeyframesValue(keyframesValue) {
const animationNames = [];
const rules = [];
const value = Array.isArray(keyframesValue) ? keyframesValue : [keyframesValue];
const value = Array.isArray(keyframesValue)
? keyframesValue
: [keyframesValue];
value.forEach((keyframes) => {
if (typeof keyframes === 'string') {

View File

@@ -10,7 +10,10 @@
import isWebColor from '../../../modules/isWebColor';
import processColor from '../../../exports/processColor';
const normalizeColor = (color?: number | string, opacity?: number = 1): void | string => {
const normalizeColor = (
color?: number | string,
opacity?: number = 1
): void | string => {
if (color == null) return;
if (typeof color === 'string' && isWebColor(color)) {

View File

@@ -23,9 +23,15 @@ const colorProps = {
textShadowColor: true
};
export default function normalizeValueWithProperty(value: any, property?: ?string): any {
export default function normalizeValueWithProperty(
value: any,
property?: ?string
): any {
let returnValue = value;
if ((property == null || !unitlessNumbers[property]) && typeof value === 'number') {
if (
(property == null || !unitlessNumbers[property]) &&
typeof value === 'number'
) {
returnValue = `${value}px`;
} else if (property != null && colorProps[property]) {
returnValue = normalizeColor(value);

View File

@@ -19,7 +19,12 @@ const resolveShadowValue = (style: Object): void | string => {
const offsetY = normalizeValueWithProperty(height);
const blurRadius = normalizeValueWithProperty(shadowRadius || 0);
const color = normalizeColor(shadowColor || 'black', shadowOpacity);
if (color != null && offsetX != null && offsetY != null && blurRadius != null) {
if (
color != null &&
offsetX != null &&
offsetY != null &&
blurRadius != null
) {
return `${offsetX} ${offsetY} ${blurRadius} ${color}`;
}
};

View File

@@ -32,7 +32,9 @@ const slice = Array.prototype.slice;
* https://developer.mozilla.org/en-US/docs/Web/API/CSSMediaRule
* https://gist.github.com/necolas/aa0c37846ad6bd3b05b727b959e82674
*/
export default function createOrderedCSSStyleSheet(sheet: ?CSSStyleSheet): OrderedCSSStyleSheet {
export default function createOrderedCSSStyleSheet(
sheet: ?CSSStyleSheet
): OrderedCSSStyleSheet {
const groups: Groups = {};
const selectors: Selectors = {};

Some files were not shown because too many files have changed in this diff Show More