Merge pull request #32 from empyrical/flow-check

Add WebView Flow types
This commit is contained in:
Thibault Malbranche 2018-09-11 11:21:08 +02:00 committed by GitHub
commit 3f2dd70b94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 680 additions and 55 deletions

88
.flowconfig Normal file
View File

@ -0,0 +1,88 @@
[ignore]
; This flowconfig is forked by platform - the only difference between them is which suffix is ignored.
.*/*[.]android.js
;.*/*[.]ios.js
; Ignore templates for 'react-native init'
.*/local-cli/templates/.*
; Ignore the Dangerfile
node_modules/react-native/bots/dangerfile.js
; Ignore "BUCK" generated dirs
node_modules/react-native/\.buckd/
; Ignore unexpected extra "@providesModule"
.*/node_modules/.*/node_modules/fbjs/.*
; Ignore duplicate module providers
; For RN Apps installed via npm, "Libraries" folder is inside
; "node_modules/react-native" but in the source repo it is in the root
.*/Libraries/react-native/React.js
; Ignore polyfills
.*/Libraries/polyfills/.*
; Ignore metro
.*/node_modules/metro/.*
; Ignore "config-chain"'s test folder - it has a corrupt JSON file that's tripping flow
.*/node_modules/config-chain/test/*.
; These should not be required directly
; require from fbjs/lib instead: require('fbjs/lib/invariant')
.*/node_modules/invariant/.*
.*/node_modules/warning/.*
[include]
[libs]
node_modules/react-native/Libraries/react-native/react-native-interface.js
node_modules/react-native/flow/
node_modules/react-native/flow-github/
[lints]
[options]
emoji=true
esproposal.optional_chaining=enable
esproposal.nullish_coalescing=enable
module.system=haste
module.system.haste.use_name_reducers=true
# keep the following in sync with server/haste/hasteImpl.js
# get basename
module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\.js\(\.flow\)?\)$' -> '\1'
# strip .js or .js.flow suffix
module.system.haste.name_reducers='^\(.*\)\.js\(\.flow\)?$' -> '\1'
# strip platform suffix
module.system.haste.name_reducers='^\(.*\)\.ios$' -> '\1'
module.system.haste.name_reducers='^\(.*\)\.android$' -> '\1'
module.system.haste.name_reducers='^\(.*\)\.native$' -> '\1'
module.system.haste.paths.blacklist=.*/__tests__/.*
module.system.haste.paths.blacklist=.*/__mocks__/.*
module.system.haste.paths.whitelist=<PROJECT_ROOT>/js/.*
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/Libraries/.*
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/RNTester/.*
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/IntegrationTests/.*
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Animated/src/polyfills/.*
; Surpress error `Duplicate module provider` until the slimmening is done
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Components/WebView/*.
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Components/WKWebView/*.
munge_underscores=true
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FlowFixMeProps
suppress_type=$FlowFixMeState
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*[react_native\\(_android\\)?_oss|react_native\\(_android\\)?_fb][a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*[react_native\\(_android\\)?_oss|react_native\\(_android\\)?_fb][a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
[strict]

88
.flowconfig.android Normal file
View File

@ -0,0 +1,88 @@
[ignore]
; This flowconfig is forked by platform - the only difference between them is which suffix is ignored.
;.*/*[.]android.js
.*/*[.]ios.js
; Ignore templates for 'react-native init'
.*/local-cli/templates/.*
; Ignore the Dangerfile
node_modules/react-native/bots/dangerfile.js
; Ignore "BUCK" generated dirs
node_modules/react-native/\.buckd/
; Ignore unexpected extra "@providesModule"
.*/node_modules/.*/node_modules/fbjs/.*
; Ignore duplicate module providers
; For RN Apps installed via npm, "Libraries" folder is inside
; "node_modules/react-native" but in the source repo it is in the root
.*/Libraries/react-native/React.js
; Ignore polyfills
.*/Libraries/polyfills/.*
; Ignore metro
.*/node_modules/metro/.*
; Ignore "config-chain"'s test folder - it has a corrupt JSON file that's tripping flow
.*/node_modules/config-chain/test/*.
; These should not be required directly
; require from fbjs/lib instead: require('fbjs/lib/invariant')
.*/node_modules/invariant/.*
.*/node_modules/warning/.*
[include]
[libs]
node_modules/react-native/Libraries/react-native/react-native-interface.js
node_modules/react-native/flow/
node_modules/react-native/flow-github/
[lints]
[options]
emoji=true
esproposal.optional_chaining=enable
esproposal.nullish_coalescing=enable
module.system=haste
module.system.haste.use_name_reducers=true
# keep the following in sync with server/haste/hasteImpl.js
# get basename
module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\.js\(\.flow\)?\)$' -> '\1'
# strip .js or .js.flow suffix
module.system.haste.name_reducers='^\(.*\)\.js\(\.flow\)?$' -> '\1'
# strip platform suffix
module.system.haste.name_reducers='^\(.*\)\.ios$' -> '\1'
module.system.haste.name_reducers='^\(.*\)\.android$' -> '\1'
module.system.haste.name_reducers='^\(.*\)\.native$' -> '\1'
module.system.haste.paths.blacklist=.*/__tests__/.*
module.system.haste.paths.blacklist=.*/__mocks__/.*
module.system.haste.paths.whitelist=<PROJECT_ROOT>/js/.*
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/Libraries/.*
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/RNTester/.*
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/IntegrationTests/.*
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Animated/src/polyfills/.*
; Surpress error `Duplicate module provider` until the slimmening is done
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Components/WebView/*.
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Components/WKWebView/*.
munge_underscores=true
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FlowFixMeProps
suppress_type=$FlowFixMeState
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*[react_native\\(_android\\)?_oss|react_native\\(_android\\)?_fb][a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*[react_native\\(_android\\)?_oss|react_native\\(_android\\)?_fb][a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
[strict]

View File

@ -51,8 +51,14 @@ Simply install React Native WebView and then use it in place of the core WebView
### Contributor Notes
* I've removed all PropTypes for now. Instead, we'll be moving toward Flow or TypeScript at a later date
* I've removed all PropTypes for now. Instead, we'll be using Flow types. TypeScript types will be added at a later date.
* UIWebView is not tested fully and you will encounter some yellow warning boxes. Since it is deprecated, we don't intend to put a lot of time into supporting it, but feel free to submit PRs if you have a special use case. Note that you will need to specify `useWebKit={false}` to use UIWebView
* After pulling this repo and installing all dependencies, you can run flow on iOS and Android-specific files using the commands:
* `yarn flow` or `npm run flow` for iOS
* `yarn flow-android` or `npm run flow-android` for Android
* If you want to add another React Native platform to this repository, you will need to create another `.flowconfig` for it. If your platform is `example`, copy the main flowconfig and rename it to `.flowconfig.example`. Then edit the config to ignore other platforms, and add `.*/*[.]example.js` to the ignore lists of the other platforms. Then add an entry to `package.json` like this:
* ` "flow-example": "flow check --flowconfig-name .flowconfig.example"`
* Currently you need to install React Native 0.57 to be able to test these types - `flow check` will not pass aganst 0.56.
## Maintainers

View File

@ -13,16 +13,26 @@ import React from 'react';
import { requireNativeComponent } from 'react-native';
import type {DataDetectorTypes} from './WebViewTypes';
const RCTWKWebView = requireNativeComponent('RCTWKWebView');
class WKWebView extends React.Component {
componentWillReceiveProps(nextProps) {
type RCTWKWebViewProps = $ReadOnly<{|
allowsInlineMediaPlayback?: ?boolean,
mediaPlaybackRequiresUserAction?: ?boolean,
dataDetectorTypes?:
| ?DataDetectorTypes
| $ReadOnlyArray<DataDetectorTypes>,
|}>;
class WKWebView extends React.Component<RCTWKWebViewProps> {
componentWillReceiveProps(nextProps: RCTWKWebViewProps) {
this.showRedboxOnPropChanges(nextProps, 'allowsInlineMediaPlayback');
this.showRedboxOnPropChanges(nextProps, 'mediaPlaybackRequiresUserAction');
this.showRedboxOnPropChanges(nextProps, 'dataDetectorTypes');
}
showRedboxOnPropChanges(nextProps, propName) {
showRedboxOnPropChanges(nextProps: RCTWKWebViewProps, propName: string) {
if (this.props[propName] !== nextProps[propName]) {
console.error(`Changes to property ${propName} do nothing after the initial render.`);
}

View File

@ -5,6 +5,7 @@
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow
*/
'use strict';
@ -21,9 +22,20 @@ import {
requireNativeComponent
} from 'react-native';
import invariant from 'fbjs/lib/invariant';
import keyMirror from 'fbjs/lib/keyMirror';
import WebViewShared from './WebViewShared';
import type {
WebViewEvent,
WebViewError,
WebViewErrorEvent,
WebViewMessageEvent,
WebViewNavigation,
WebViewNavigationEvent,
WebViewSharedProps,
WebViewSource,
} from './WebViewTypes';
const resolveAssetSource = Image.resolveAssetSource;
@ -41,10 +53,16 @@ const defaultRenderLoading = () => (
</View>
);
type State = {|
viewState: WebViewState,
lastErrorEvent: ?WebViewError,
startInLoadingState: boolean,
|};
/**
* Renders a native WebView.
*/
class WebView extends React.Component {
class WebView extends React.Component<WebViewSharedProps, State> {
static defaultProps = {
javaScriptEnabled: true,
thirdPartyCookiesEnabled: true,
@ -72,6 +90,7 @@ class WebView extends React.Component {
otherView = (this.props.renderLoading || defaultRenderLoading)();
} else if (this.state.viewState === WebViewState.ERROR) {
const errorEvent = this.state.lastErrorEvent;
invariant(errorEvent != null, 'lastErrorEvent expected to be non-null');
otherView =
this.props.renderError &&
this.props.renderError(
@ -81,7 +100,7 @@ class WebView extends React.Component {
);
} else if (this.state.viewState !== WebViewState.IDLE) {
console.error(
'RCTWebView invalid state encountered: ' + this.state.loading,
'RCTWebView invalid state encountered: ' + this.state.viewState,
);
}
@ -94,11 +113,11 @@ class WebView extends React.Component {
webViewStyles.push(styles.hidden);
}
const source = this.props.source || {};
if (this.props.html) {
source.html = this.props.html;
} else if (this.props.url) {
source.uri = this.props.url;
let source: WebViewSource = this.props.source || {};
if (!this.props.source && this.props.html) {
source = { html: this.props.html };
} else if (!this.props.source && this.props.url) {
source = { uri: this.props.url };
}
if (source.method === 'POST' && source.headers) {
@ -198,7 +217,7 @@ class WebView extends React.Component {
);
};
postMessage = data => {
postMessage = (data: string) => {
UIManager.dispatchViewManagerCommand(
this.getWebViewHandle(),
UIManager.RCTWebView.Commands.postMessage,
@ -212,7 +231,7 @@ class WebView extends React.Component {
* on pages with a Content Security Policy that disallows eval(). If you need that
* functionality, look into postMessage/onMessage.
*/
injectJavaScript = data => {
injectJavaScript = (data: string) => {
UIManager.dispatchViewManagerCommand(
this.getWebViewHandle(),
UIManager.RCTWebView.Commands.injectJavaScript,
@ -224,7 +243,7 @@ class WebView extends React.Component {
* We return an event with a bunch of fields including:
* url, title, loading, canGoBack, canGoForward
*/
updateNavigationState = event => {
updateNavigationState = (event: WebViewNavigationEvent) => {
if (this.props.onNavigationStateChange) {
this.props.onNavigationStateChange(event.nativeEvent);
}
@ -234,13 +253,13 @@ class WebView extends React.Component {
return ReactNative.findNodeHandle(this.refs[RCT_WEBVIEW_REF]);
};
onLoadingStart = event => {
onLoadingStart = (event: WebViewNavigationEvent) => {
const onLoadStart = this.props.onLoadStart;
onLoadStart && onLoadStart(event);
this.updateNavigationState(event);
};
onLoadingError = event => {
onLoadingError = (event: WebViewErrorEvent) => {
event.persist(); // persist this event because we need to store it
const { onError, onLoadEnd } = this.props;
onError && onError(event);
@ -253,7 +272,7 @@ class WebView extends React.Component {
});
};
onLoadingFinish = event => {
onLoadingFinish = (event: WebViewNavigationEvent) => {
const { onLoad, onLoadEnd } = this.props;
onLoad && onLoad(event);
onLoadEnd && onLoadEnd(event);
@ -263,7 +282,7 @@ class WebView extends React.Component {
this.updateNavigationState(event);
};
onMessage = (event) => {
onMessage = (event: WebViewMessageEvent) => {
const { onMessage } = this.props;
onMessage && onMessage(event);
};

View File

@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*
* @format
* @noflow
* @flow
*/
'use strict';
@ -29,6 +29,15 @@ import invariant from 'fbjs/lib/invariant';
import keyMirror from 'fbjs/lib/keyMirror';
import WebViewShared from './WebViewShared';
import type {
WebViewEvent,
WebViewError,
WebViewErrorEvent,
WebViewMessageEvent,
WebViewNavigationEvent,
WebViewSharedProps,
WebViewSource,
} from './WebViewTypes';
const resolveAssetSource = Image.resolveAssetSource;
@ -65,13 +74,11 @@ const NavigationType = keyMirror({
const JSNavigationScheme = 'react-js-navigation';
// type ErrorEvent = {
// domain: any,
// code: any,
// description: any,
// };
// type Event = Object;
type State = {|
viewState: WebViewState,
lastErrorEvent: ?WebViewError,
startInLoadingState: boolean,
|};
const DataDetectorTypes = [
'phoneNumber',
@ -121,7 +128,7 @@ const defaultRenderError = (errorDomain, errorCode, errorDesc) => (
* You can use this component to navigate back and forth in the web view's
* history and configure various properties for the web content.
*/
class WebView extends React.Component {
class WebView extends React.Component<WebViewSharedProps, State> {
static JSNavigationScheme = JSNavigationScheme;
static NavigationType = NavigationType;
@ -174,7 +181,7 @@ class WebView extends React.Component {
);
} else if (this.state.viewState !== WebViewState.IDLE) {
console.error(
'RCTWebView invalid state encountered: ' + this.state.loading,
'RCTWebView invalid state encountered: ' + this.state.viewState,
);
}
@ -217,6 +224,7 @@ class WebView extends React.Component {
shouldStart &&
this.props.onShouldStartLoadWithRequest(event.nativeEvent);
}
invariant(viewManager != null, 'viewManager expected to be non-null');
viewManager.startLoadWithResult(
!!shouldStart,
event.nativeEvent.lockIdentifier,
@ -227,11 +235,11 @@ class WebView extends React.Component {
this.props.decelerationRate,
);
const source = this.props.source || {};
if (this.props.html) {
source.html = this.props.html;
} else if (this.props.url) {
source.uri = this.props.url;
let source: WebViewSource = this.props.source || {};
if (!this.props.source && this.props.html) {
source = { html: this.props.html };
} else if (!this.props.source && this.props.url) {
source = { uri: this.props.url };
}
const messagingEnabled = typeof this.props.onMessage === 'function';
@ -345,7 +353,7 @@ class WebView extends React.Component {
* document.addEventListener('message', e => { document.title = e.data; });
* ```
*/
postMessage = data => {
postMessage = (data: string) => {
UIManager.dispatchViewManagerCommand(
this.getWebViewHandle(),
this._getCommands().postMessage,
@ -359,7 +367,7 @@ class WebView extends React.Component {
* on pages with a Content Security Policy that disallows eval(). If you need that
* functionality, look into postMessage/onMessage.
*/
injectJavaScript = data => {
injectJavaScript = (data: string) => {
UIManager.dispatchViewManagerCommand(
this.getWebViewHandle(),
this._getCommands().injectJavaScript,
@ -371,7 +379,7 @@ class WebView extends React.Component {
* We return an event with a bunch of fields including:
* url, title, loading, canGoBack, canGoForward
*/
_updateNavigationState = (event) => {
_updateNavigationState = (event: WebViewNavigationEvent) => {
if (this.props.onNavigationStateChange) {
this.props.onNavigationStateChange(event.nativeEvent);
}
@ -384,13 +392,13 @@ class WebView extends React.Component {
return ReactNative.findNodeHandle(this.refs[RCT_WEBVIEW_REF]);
};
_onLoadingStart = (event) => {
_onLoadingStart = (event: WebViewNavigationEvent) => {
const onLoadStart = this.props.onLoadStart;
onLoadStart && onLoadStart(event);
this._updateNavigationState(event);
};
_onLoadingError = (event) => {
_onLoadingError = (event: WebViewErrorEvent) => {
event.persist(); // persist this event because we need to store it
const { onError, onLoadEnd } = this.props;
onError && onError(event);
@ -403,7 +411,7 @@ class WebView extends React.Component {
});
};
_onLoadingFinish = (event) => {
_onLoadingFinish = (event: WebViewNavigationEvent) => {
const { onLoad, onLoadEnd } = this.props;
onLoad && onLoad(event);
onLoadEnd && onLoadEnd(event);
@ -413,12 +421,12 @@ class WebView extends React.Component {
this._updateNavigationState(event);
};
_onMessage = (event) => {
_onMessage = (event: WebViewMessageEvent) => {
const { onMessage } = this.props;
onMessage && onMessage(event);
};
componentDidUpdate(prevProps) {
componentDidUpdate(prevProps: WebViewSharedProps) {
if (!(prevProps.useWebKit && this.props.useWebKit)) {
return;
}
@ -434,7 +442,7 @@ class WebView extends React.Component {
}
}
_showRedboxOnPropChanges(prevProps, propName) {
_showRedboxOnPropChanges(prevProps, propName: string) {
if (this.props[propName] !== prevProps[propName]) {
console.error(
`Changes to property ${propName} do nothing after the initial render.`,
@ -443,16 +451,8 @@ class WebView extends React.Component {
}
}
const RCTWebView = requireNativeComponent(
'RCTWebView',
WebView,
WebView.extraNativeComponentConfig,
);
const RCTWKWebView = requireNativeComponent(
'RCTWKWebView',
WebView,
WebView.extraNativeComponentConfig,
);
const RCTWebView = requireNativeComponent('RCTWebView');
const RCTWKWebView = requireNativeComponent('RCTWKWebView');
const styles = StyleSheet.create({
container: {

View File

@ -14,11 +14,11 @@ const escapeStringRegexp = require('escape-string-regexp');
const WebViewShared = {
defaultOriginWhitelist: ['http://*', 'https://*'],
extractOrigin: (url) => {
extractOrigin: (url: string) => {
const result = /^[A-Za-z0-9]+:(\/\/)?[^/]*/.exec(url);
return result === null ? null : result[0];
return result === null ? '' : result[0];
},
originWhitelistToRegex: (originWhitelist) => {
originWhitelistToRegex: (originWhitelist: string) => {
return escapeStringRegexp(originWhitelist).replace(/\\\*/g, '.*');
},
};

406
js/WebViewTypes.js Normal file
View File

@ -0,0 +1,406 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow
*/
'use strict';
import type {Node, Element, ComponentType} from 'react';
import type {SyntheticEvent} from 'CoreEventTypes';
import type {EdgeInsetsProp} from 'EdgeInsetsPropType';
import type {ViewStyleProp} from 'StyleSheet';
import type {ViewProps} from 'ViewPropTypes';
export type WebViewNativeEvent = $ReadOnly<{|
url: string,
loading: boolean,
title: string,
canGoBack: boolean,
canGoForward: boolean,
|}>;
export type WebViewNavigation = $ReadOnly<{|
...WebViewNativeEvent,
navigationType:
| 'click'
| 'formsubmit'
| 'backforward'
| 'reload'
| 'formresubmit'
| 'other',
|}>;
export type WebViewMessage = $ReadOnly<{|
...WebViewNativeEvent,
data: string,
|}>;
export type WebViewError = $ReadOnly<{|
...WebViewNativeEvent,
/**
* `domain` is only used on iOS
*/
domain: ?string,
code: number,
description: string,
|}>;
export type WebViewEvent = SyntheticEvent<WebViewNativeEvent>;
export type WebViewNavigationEvent = SyntheticEvent<WebViewNavigation>;
export type WebViewMessageEvent = SyntheticEvent<WebViewMessage>;
export type WebViewErrorEvent = SyntheticEvent<WebViewError>;
export type DataDetectorTypes =
| 'phoneNumber'
| 'link'
| 'address'
| 'calendarEvent'
| 'trackingNumber'
| 'flightNumber'
| 'lookupSuggestion'
| 'none'
| 'all';
export type WebViewSourceUri = $ReadOnly<{|
/**
* The URI to load in the `WebView`. Can be a local or remote file.
*/
uri?: ?string,
/**
* The HTTP Method to use. Defaults to GET if not specified.
* NOTE: On Android, only GET and POST are supported.
*/
method?: string,
/**
* Additional HTTP headers to send with the request.
* NOTE: On Android, this can only be used with GET requests.
*/
headers?: Object,
/**
* The HTTP body to send with the request. This must be a valid
* UTF-8 string, and will be sent exactly as specified, with no
* additional encoding (e.g. URL-escaping or base64) applied.
* NOTE: On Android, this can only be used with POST requests.
*/
body?: string,
|}>;
export type WebViewSourceHtml = $ReadOnly<{|
/**
* A static HTML page to display in the WebView.
*/
html?: ?string,
/**
* The base URL to be used for any relative links in the HTML.
*/
baseUrl?: ?string,
|}>;
export type WebViewSource = WebViewSourceUri | WebViewSourceHtml;
export type WebViewNativeConfig = $ReadOnly<{|
/*
* The native component used to render the WebView.
*/
component?: ComponentType<WebViewSharedProps>,
/*
* Set props directly on the native component WebView. Enables custom props which the
* original WebView doesn't pass through.
*/
props?: ?Object,
/*
* Set the ViewManager to use for communication with the native side.
* @platform ios
*/
viewManager?: ?Object,
|}>;
export type IOSWebViewProps = $ReadOnly<{|
/**
* If true, use WKWebView instead of UIWebView.
* @platform ios
*/
useWebKit?: ?boolean,
/**
* Boolean value that determines whether the web view bounces
* when it reaches the edge of the content. The default value is `true`.
* @platform ios
*/
bounces?: ?boolean,
/**
* A floating-point number that determines how quickly the scroll view
* decelerates after the user lifts their finger. You may also use the
* string shortcuts `"normal"` and `"fast"` which match the underlying iOS
* settings for `UIScrollViewDecelerationRateNormal` and
* `UIScrollViewDecelerationRateFast` respectively:
*
* - normal: 0.998
* - fast: 0.99 (the default for iOS web view)
* @platform ios
*/
decelerationRate?: ?('fast' | 'normal' | number),
/**
* Boolean value that determines whether scrolling is enabled in the
* `WebView`. The default value is `true`.
* @platform ios
*/
scrollEnabled?: ?boolean,
/**
* The amount by which the web view content is inset from the edges of
* the scroll view. Defaults to {top: 0, left: 0, bottom: 0, right: 0}.
* @platform ios
*/
contentInset?: ?EdgeInsetsProp,
/**
* Determines the types of data converted to clickable URLs in the web view's content.
* By default only phone numbers are detected.
*
* You can provide one type or an array of many types.
*
* Possible values for `dataDetectorTypes` are:
*
* - `'phoneNumber'`
* - `'link'`
* - `'address'`
* - `'calendarEvent'`
* - `'none'`
* - `'all'`
*
* With the new WebKit implementation, we have three new values:
* - `'trackingNumber'`,
* - `'flightNumber'`,
* - `'lookupSuggestion'`,
*
* @platform ios
*/
dataDetectorTypes?:
| ?DataDetectorTypes
| $ReadOnlyArray<DataDetectorTypes>,
/**
* Function that allows custom handling of any web view requests. Return
* `true` from the function to continue loading the request and `false`
* to stop loading.
* @platform ios
*/
onShouldStartLoadWithRequest?: (event: WebViewEvent) => mixed,
/**
* Boolean that determines whether HTML5 videos play inline or use the
* native full-screen controller. The default value is `false`.
*
* **NOTE** : In order for video to play inline, not only does this
* property need to be set to `true`, but the video element in the HTML
* document must also include the `webkit-playsinline` attribute.
* @platform ios
*/
allowsInlineMediaPlayback?: ?boolean,
|}>;
export type AndroidWebViewProps = $ReadOnly<{|
onNavigationStateChange?: (event: WebViewNavigation) => mixed,
onContentSizeChange?: (event: WebViewEvent) => mixed,
/**
* Sets whether Geolocation is enabled. The default is false.
* @platform android
*/
geolocationEnabled?: ?boolean,
/**
* Boolean that sets whether JavaScript running in the context of a file
* scheme URL should be allowed to access content from any origin.
* Including accessing content from other file scheme URLs
* @platform android
*/
allowUniversalAccessFromFileURLs?: ?boolean,
/**
* Used on Android only, controls whether form autocomplete data should be saved
* @platform android
*/
saveFormDataDisabled?: ?boolean,
/*
* Used on Android only, controls whether the given list of URL prefixes should
* make {@link com.facebook.react.views.webview.ReactWebViewClient} to launch a
* default activity intent for those URL instead of loading it within the webview.
* Use this to list URLs that WebView cannot handle, e.g. a PDF url.
* @platform android
*/
urlPrefixesForDefaultIntent?: $ReadOnlyArray<string>,
/**
* Boolean value to enable JavaScript in the `WebView`. Used on Android only
* as JavaScript is enabled by default on iOS. The default value is `true`.
* @platform android
*/
javaScriptEnabled?: ?boolean,
/**
* Boolean value to enable third party cookies in the `WebView`. Used on
* Android Lollipop and above only as third party cookies are enabled by
* default on Android Kitkat and below and on iOS. The default value is `true`.
* @platform android
*/
thirdPartyCookiesEnabled?: ?boolean,
/**
* Boolean value to control whether DOM Storage is enabled. Used only in
* Android.
* @platform android
*/
domStorageEnabled?: ?boolean,
/**
* Sets the user-agent for the `WebView`.
* @platform android
*/
userAgent?: ?string,
/**
* Specifies the mixed content mode. i.e WebView will allow a secure origin to load content from any other origin.
*
* Possible values for `mixedContentMode` are:
*
* - `'never'` (default) - WebView will not allow a secure origin to load content from an insecure origin.
* - `'always'` - WebView will allow a secure origin to load content from any other origin, even if that origin is insecure.
* - `'compatibility'` - WebView will attempt to be compatible with the approach of a modern web browser with regard to mixed content.
* @platform android
*/
mixedContentMode?: ?('never' | 'always' | 'compatibility'),
|}>;
export type WebViewSharedProps = $ReadOnly<{|
...ViewProps,
...IOSWebViewProps,
...AndroidWebViewProps,
/**
* Deprecated. Use `source` instead.
*/
url?: ?string,
/**
* Deprecated. Use `source` instead.
*/
html?: ?string,
/**
* Loads static html or a uri (with optional headers) in the WebView.
*/
source?: ?WebViewSource,
/**
* Function that returns a view to show if there's an error.
*/
renderError: (errorDomain: ?string, errorCode: number, errorDesc: string) => Element<any>, // view to show if there's an error
/**
* Function that returns a loading indicator.
*/
renderLoading: () => Element<any>,
/**
* Function that is invoked when the `WebView` has finished loading.
*/
onLoad: (event: WebViewNavigationEvent) => mixed,
/**
* Function that is invoked when the `WebView` load succeeds or fails.
*/
onLoadEnd: (event: WebViewNavigationEvent | WebViewErrorEvent) => mixed,
/**
* Function that is invoked when the `WebView` starts loading.
*/
onLoadStart: (event: WebViewNavigationEvent) => mixed,
/**
* Function that is invoked when the `WebView` load fails.
*/
onError: (event: WebViewErrorEvent) => mixed,
/**
* Controls whether to adjust the content inset for web views that are
* placed behind a navigation bar, tab bar, or toolbar. The default value
* is `true`.
*/
automaticallyAdjustContentInsets?: ?boolean,
/**
* Function that is invoked when the `WebView` loading starts or ends.
*/
onNavigationStateChange?: (event: WebViewNavigation) => mixed,
/**
* A function that is invoked when the webview calls `window.postMessage`.
* Setting this property will inject a `postMessage` global into your
* webview, but will still call pre-existing values of `postMessage`.
*
* `window.postMessage` accepts one argument, `data`, which will be
* available on the event object, `event.nativeEvent.data`. `data`
* must be a string.
*/
onMessage?: (event: WebViewMessageEvent) => mixed,
/**
* Boolean value that forces the `WebView` to show the loading view
* on the first load.
*/
startInLoadingState?: ?boolean,
/**
* Set this to provide JavaScript that will be injected into the web page
* when the view loads.
*/
injectedJavaScript?: ?string,
/**
* Boolean that controls whether the web content is scaled to fit
* the view and enables the user to change the scale. The default value
* is `true`.
*
* On iOS, when `useWebKit=true`, this prop will not work.
*/
scalesPageToFit?: ?boolean,
/**
* Boolean that determines whether HTML5 audio and video requires the user
* to tap them before they start playing. The default value is `true`.
*/
mediaPlaybackRequiresUserAction?: ?boolean,
/**
* List of origin strings to allow being navigated to. The strings allow
* wildcards and get matched against *just* the origin (not the full URL).
* If the user taps to navigate to a new page but the new page is not in
* this whitelist, we will open the URL in Safari.
* The default whitelisted origins are "http://*" and "https://*".
*/
originWhitelist?: $ReadOnlyArray<string>,
/**
* Override the native component used to render the WebView. Enables a custom native
* WebView which uses the same JavaScript as the original WebView.
*/
nativeConfig?: ?WebViewNativeConfig,
style?: ViewStyleProp,
children: Node,
|}>;

View File

@ -5,6 +5,10 @@
"author": "Jamon Holmgren <jamon@infinite.red>",
"version": "0.1.0",
"homepage": "https://github.com/react-native-community/react-native-webview#readme",
"scripts": {
"flow": "flow check",
"flow-android": "flow check --flowconfig-name .flowconfig.android"
},
"peerDependencies": {
"react": "^16.0",
"react-native": "^0.56"
@ -12,5 +16,9 @@
"dependencies": {
"escape-string-regexp": "^1.0.5",
"fbjs": "^0.8.17"
},
"devDependencies": {
"flow-bin": "^0.80.0",
"react-native": "^0.56"
}
}