@exodus/react-native-webview 11.26.1-exodus.12 → 11.26.1-exodus.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/.all-contributorsrc +185 -0
  2. package/.circleci/config.yml +66 -0
  3. package/.eslintignore +2 -0
  4. package/.eslintrc.js +94 -0
  5. package/.flowconfig +88 -0
  6. package/.flowconfig.android +88 -0
  7. package/.gitattributes +12 -0
  8. package/.github/CODEOWNERS +1 -0
  9. package/.github/ISSUE_TEMPLATE/bug-report.md +42 -0
  10. package/.github/ISSUE_TEMPLATE/feature_request.md +30 -0
  11. package/.github/workflows/android-ci.yml +35 -0
  12. package/.github/workflows/detox.yml +20 -0
  13. package/.github/workflows/ios-ci.yml +31 -0
  14. package/.github/workflows/scripts/install-vs-features.ps1 +108 -0
  15. package/.github/workflows/stale.yml +17 -0
  16. package/.gitignore +62 -0
  17. package/.prettierrc.js +10 -0
  18. package/.releaserc +15 -0
  19. package/.vscode/settings.json +9 -0
  20. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java +5 -6
  21. package/apple/RNCWebView.h +1 -0
  22. package/apple/RNCWebView.m +17 -10
  23. package/apple/RNCWebViewManager.m +1 -0
  24. package/babel.config.js +11 -0
  25. package/bin/setup +26 -0
  26. package/docs/Contributing.md +102 -0
  27. package/docs/Custom-Android.md +222 -0
  28. package/docs/Custom-iOS.md +236 -0
  29. package/docs/Debugging.md +101 -0
  30. package/docs/Getting-Started.md +142 -0
  31. package/docs/Guide.md +613 -0
  32. package/docs/Reference.md +1639 -0
  33. package/example/.gitignore +14 -0
  34. package/example/.watchmanconfig +1 -0
  35. package/example/App.tsx +262 -0
  36. package/example/android/build.gradle +15 -0
  37. package/example/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  38. package/example/android/gradle/wrapper/gradle-wrapper.properties +5 -0
  39. package/example/android/gradle.properties +34 -0
  40. package/example/android/gradlew +234 -0
  41. package/example/android/gradlew.bat +89 -0
  42. package/example/android/settings.gradle +12 -0
  43. package/example/app.json +20 -0
  44. package/example/assets/test.html +9 -0
  45. package/example/babel.config.js +3 -0
  46. package/example/examples/Alerts.tsx +72 -0
  47. package/example/examples/ApplePay.tsx +23 -0
  48. package/example/examples/Background.tsx +54 -0
  49. package/example/examples/Downloads.tsx +57 -0
  50. package/example/examples/Injection.tsx +161 -0
  51. package/example/examples/LocalPageLoad.tsx +16 -0
  52. package/example/examples/Messaging.tsx +63 -0
  53. package/example/examples/NativeWebpage.tsx +22 -0
  54. package/example/examples/Scrolling.tsx +68 -0
  55. package/example/examples/Uploads.tsx +69 -0
  56. package/example/index.js +9 -0
  57. package/example/ios/Podfile +8 -0
  58. package/example/ios/Podfile.lock +445 -0
  59. package/jest-setups/jest.setup.js +8 -0
  60. package/jest.config.js +184 -0
  61. package/lib/WebView.android.d.ts.map +1 -0
  62. package/lib/WebView.android.js +2 -3
  63. package/lib/WebView.d.ts.map +1 -0
  64. package/lib/WebView.ios.d.ts.map +1 -0
  65. package/lib/WebView.ios.js +2 -3
  66. package/lib/WebView.styles.d.ts.map +1 -0
  67. package/lib/WebViewNativeComponent.android.d.ts.map +1 -0
  68. package/lib/WebViewNativeComponent.ios.d.ts.map +1 -0
  69. package/lib/WebViewShared.d.ts.map +1 -0
  70. package/lib/WebViewTypes.d.ts +5 -0
  71. package/lib/WebViewTypes.d.ts.map +1 -0
  72. package/lib/index.d.ts.map +1 -0
  73. package/metro.config.js +57 -0
  74. package/package.json +1 -1
  75. package/src/WebView.android.tsx +246 -0
  76. package/src/WebView.ios.tsx +221 -0
  77. package/src/WebView.styles.ts +44 -0
  78. package/src/WebView.tsx +18 -0
  79. package/src/WebViewNativeComponent.android.ts +8 -0
  80. package/src/WebViewNativeComponent.ios.ts +8 -0
  81. package/src/WebViewShared.tsx +257 -0
  82. package/src/WebViewTypes.ts +920 -0
  83. package/src/__tests__/WebViewShared-test.js +208 -0
  84. package/src/__tests__/__snapshots__/WebViewShared-test.js.snap +8 -0
  85. package/src/index.ts +4 -0
  86. package/tsconfig.json +24 -0
  87. package/yarn.lock +13397 -0
@@ -0,0 +1,9 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <title>Test</title>
5
+ </head>
6
+ <body>
7
+ <h2>Local page test</h2>
8
+ </body>
9
+ </html>
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+ presets: ['module:metro-react-native-babel-preset'],
3
+ };
@@ -0,0 +1,72 @@
1
+ import React, {Component} from 'react';
2
+ import {Text, View} from 'react-native';
3
+
4
+ import WebView from 'react-native-webview';
5
+
6
+ const HTML = `
7
+ <!DOCTYPE html>\n
8
+ <html>
9
+ <head>
10
+ <title>Alerts</title>
11
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
12
+ <meta name="viewport" content="width=320, user-scalable=no">
13
+ <style type="text/css">
14
+ body {
15
+ margin: 0;
16
+ padding: 0;
17
+ font: 62.5% arial, sans-serif;
18
+ background: #ccc;
19
+ }
20
+ </style>
21
+ </head>
22
+ <body>
23
+ <button onclick="showAlert()">Show alert</button>
24
+ <button onclick="showConfirm()">Show confirm</button>
25
+ <button onclick="showPrompt()">Show prompt</button>
26
+ <p id="demo"></p>
27
+ <script>
28
+ function showAlert() {
29
+ alert("Hello! I am an alert box!");
30
+ document.getElementById("demo").innerHTML = "Alert dismissed!";
31
+ }
32
+ function showConfirm() {
33
+ var response;
34
+ if (confirm("Press a button!")) {
35
+ response = "You pressed OK on confirm!";
36
+ } else {
37
+ response = "You pressed Cancel on confirm!";
38
+ }
39
+ document.getElementById("demo").innerHTML = response;
40
+ }
41
+ function showPrompt() {
42
+ var message;
43
+ const name = prompt("Please enter your name", "Name");
44
+ if (name !== null) {
45
+ message = "Hello " + name;
46
+ } else {
47
+ message = "You pressed Cancel on prompt!";
48
+ }
49
+ document.getElementById("demo").innerHTML = message;
50
+ }
51
+ </script>
52
+ </body>
53
+ </html>
54
+ `;
55
+
56
+ type Props = {};
57
+ type State = {};
58
+
59
+ export default class Alerts extends Component<Props, State> {
60
+ state = {};
61
+
62
+ render() {
63
+ return (
64
+ <View style={{ height: 120 }}>
65
+ <WebView
66
+ source={{html: HTML}}
67
+ automaticallyAdjustContentInsets={false}
68
+ />
69
+ </View>
70
+ );
71
+ }
72
+ }
@@ -0,0 +1,23 @@
1
+ import React, {Component} from 'react';
2
+ import {View} from 'react-native';
3
+
4
+ import WebView from 'react-native-webview';
5
+
6
+ type Props = {};
7
+ type State = {};
8
+
9
+ export default class Alerts extends Component<Props, State> {
10
+ state = {};
11
+
12
+ render() {
13
+ return (
14
+ <View style={{ flex: 1 }}>
15
+ <WebView
16
+ enableApplePay={true}
17
+ source={{uri: "https://applepaydemo.apple.com/"}}
18
+ automaticallyAdjustContentInsets={false}
19
+ />
20
+ </View>
21
+ );
22
+ }
23
+ }
@@ -0,0 +1,54 @@
1
+ import React, {Component} from 'react';
2
+ import {Text, View} from 'react-native';
3
+
4
+ import WebView from 'react-native-webview';
5
+
6
+ const HTML = `
7
+ <!DOCTYPE html>\n
8
+ <html>
9
+ <head>
10
+ <title>Hello World</title>
11
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
12
+ <meta name="viewport" content="width=320, user-scalable=no">
13
+ <style type="text/css">
14
+ body {
15
+ margin: 0;
16
+ padding: 0;
17
+ font: 62.5% arial, sans-serif;
18
+ background: transparent;
19
+ }
20
+ </style>
21
+ </head>
22
+ <body>
23
+ <p>HTML content in transparent body.</p>
24
+ </body>
25
+ </html>
26
+ `;
27
+
28
+ type Props = {};
29
+ type State = {
30
+ backgroundColor: string,
31
+ };
32
+
33
+ export default class Background extends Component<Props, State> {
34
+ state = {
35
+ backgroundColor: '#FF00FF00'
36
+ };
37
+
38
+ render() {
39
+ return (
40
+ <View>
41
+ <View style={{backgroundColor:'red'}}>
42
+ <View style={{ height: 120 }}>
43
+ <WebView
44
+ source={{html: HTML}}
45
+ automaticallyAdjustContentInsets={false}
46
+ style={{backgroundColor:'#00000000'}}
47
+ />
48
+ </View>
49
+ </View>
50
+ <Text>WebView is transparent contained in a View with a red backgroundColor</Text>
51
+ </View>
52
+ );
53
+ }
54
+ }
@@ -0,0 +1,57 @@
1
+ import React, {Component} from 'react';
2
+ import {Alert, Platform, View} from 'react-native';
3
+
4
+ import WebView, {FileDownload} from 'react-native-webview';
5
+
6
+ const HTML = `
7
+ <!DOCTYPE html>\n
8
+ <html>
9
+ <head>
10
+ <title>Downloads</title>
11
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
12
+ <meta name="viewport" content="width=320, user-scalable=no">
13
+ <style type="text/css">
14
+ body {
15
+ margin: 0;
16
+ padding: 0;
17
+ font: 62.5% arial, sans-serif;
18
+ background: #ccc;
19
+ }
20
+ </style>
21
+ </head>
22
+ <body>
23
+ <a href="https://www.7-zip.org/a/7za920.zip">Example zip file download</a>
24
+ <br>
25
+ <a href="http://test.greenbytes.de/tech/tc2231/attwithisofn2231iso.asis">Download file with non-ascii filename: "foo-ä.html"</a>
26
+ </body>
27
+ </html>
28
+ `;
29
+
30
+ type Props = {};
31
+ type State = {};
32
+
33
+ export default class Downloads extends Component<Props, State> {
34
+ state = {};
35
+
36
+ onFileDownload = ({ nativeEvent }: { nativeEvent: FileDownload } ) => {
37
+ Alert.alert("File download detected", nativeEvent.downloadUrl);
38
+ };
39
+
40
+ render() {
41
+ const platformProps = Platform.select({
42
+ ios: {
43
+ onFileDownload: this.onFileDownload,
44
+ },
45
+ });
46
+
47
+ return (
48
+ <View style={{ height: 120 }}>
49
+ <WebView
50
+ source={{html: HTML}}
51
+ automaticallyAdjustContentInsets={false}
52
+ {...platformProps}
53
+ />
54
+ </View>
55
+ );
56
+ }
57
+ }
@@ -0,0 +1,161 @@
1
+ import React, {Component} from 'react';
2
+ import {Text, View, ScrollView} from 'react-native';
3
+
4
+ import WebView from 'react-native-webview';
5
+
6
+ const HTML = `
7
+ <!DOCTYPE html>
8
+ <html>
9
+ <head>
10
+ <meta charset="utf-8">
11
+ <meta name="viewport" content="width=device-width, initial-scale=1">
12
+ <title>iframe test</title>
13
+ </head>
14
+ <body>
15
+ <p style="">beforeContentLoaded on the top frame <span id="before_failed" style="display: inline-block;">failed</span><span id="before_succeeded" style="display: none;">succeeded</span>!</p>
16
+ <p style="">afterContentLoaded on the top frame <span id="after_failed" style="display: inline-block;">failed</span><span id="after_succeeded" style="display: none;">succeeded</span>!</p>
17
+ <iframe src="https://birchlabs.co.uk/linguabrowse/infopages/obsol/iframe.html?v=1" name="iframe_0" style="width: 100%; height: 25px;"></iframe>
18
+ <iframe src="https://birchlabs.co.uk/linguabrowse/infopages/obsol/iframe2.html?v=1" name="iframe_1" style="width: 100%; height: 25px;"></iframe>
19
+ <iframe src="https://www.ebay.co.uk" name="iframe_2" style="width: 100%; height: 25px;"></iframe>
20
+ </body>
21
+ </html>
22
+ `;
23
+
24
+ type Props = {};
25
+ type State = {
26
+ backgroundColor: string,
27
+ };
28
+
29
+ export default class Injection extends Component<Props, State> {
30
+ state = {
31
+ backgroundColor: '#FF00FF00'
32
+ };
33
+
34
+ render() {
35
+ return (
36
+ <ScrollView>
37
+ <View style={{ }}>
38
+ <View style={{ height: 400 }}>
39
+ <WebView
40
+ /**
41
+ * This HTML is a copy of the hosted multi-frame JS injection test.
42
+ * I have found that Android doesn't support beforeContentLoaded for a hosted HTML webpage, yet does for a static source.
43
+ * The cause of this is unresolved.
44
+ */
45
+ // source={{ html: HTML }}
46
+ source={{ uri: "https://birchlabs.co.uk/linguabrowse/infopages/obsol/rnw_iframe_test.html" }}
47
+ automaticallyAdjustContentInsets={false}
48
+ style={{backgroundColor:'#00000000'}}
49
+
50
+ /* Must be populated in order for `messagingEnabled` to be `true` to activate the
51
+ * JS injection user scripts, consistent with current behaviour. This is undesirable,
52
+ * so needs addressing in a follow-up PR. */
53
+ onMessage={() => {}}
54
+ injectedJavaScriptBeforeContentLoadedForMainFrameOnly={false}
55
+ injectedJavaScriptForMainFrameOnly={false}
56
+
57
+ /* We set this property in each frame */
58
+ injectedJavaScriptBeforeContentLoaded={`
59
+ console.log("executing injectedJavaScriptBeforeContentLoaded... " + (new Date()).toString());
60
+ if(typeof window.top.injectedIframesBeforeContentLoaded === "undefined"){
61
+ window.top.injectedIframesBeforeContentLoaded = [];
62
+ }
63
+ window.self.colourToUse = "orange";
64
+ if(window.self === window.top){
65
+ console.log("Was window.top. window.frames.length is:", window.frames.length);
66
+ window.self.numberOfFramesAtBeforeContentLoaded = window.frames.length;
67
+ function declareSuccessOfBeforeContentLoaded(head){
68
+ var style = window.self.document.createElement('style');
69
+ style.type = 'text/css';
70
+ style.innerHTML = "#before_failed { display: none !important; }#before_succeeded { display: inline-block !important; }";
71
+ head.appendChild(style);
72
+ }
73
+
74
+ const head = (window.self.document.head || window.self.document.getElementsByTagName('head')[0]);
75
+
76
+ if(head){
77
+ declareSuccessOfBeforeContentLoaded(head);
78
+ } else {
79
+ window.self.document.addEventListener("DOMContentLoaded", function (event) {
80
+ const head = (window.self.document.head || window.self.document.getElementsByTagName('head')[0]);
81
+ declareSuccessOfBeforeContentLoaded(head);
82
+ });
83
+ }
84
+ } else {
85
+ window.top.injectedIframesBeforeContentLoaded.push(window.self.name);
86
+ console.log("wasn't window.top.");
87
+ console.log("wasn't window.top. Still going...");
88
+ }
89
+ `}
90
+
91
+ /* We read the colourToUse property in each frame to recolour each frame */
92
+ injectedJavaScript={`
93
+ console.log("executing injectedJavaScript... " + (new Date()).toString());
94
+ if(typeof window.top.injectedIframesAfterContentLoaded === "undefined"){
95
+ window.top.injectedIframesAfterContentLoaded = [];
96
+ }
97
+
98
+ if(window.self.colourToUse){
99
+ window.self.document.body.style.backgroundColor = window.self.colourToUse;
100
+ } else {
101
+ window.self.document.body.style.backgroundColor = "cyan";
102
+ }
103
+
104
+ if(window.self === window.top){
105
+ function declareSuccessOfAfterContentLoaded(head){
106
+ var style = window.self.document.createElement('style');
107
+ style.type = 'text/css';
108
+ style.innerHTML = "#after_failed { display: none !important; }#after_succeeded { display: inline-block !important; }";
109
+ head.appendChild(style);
110
+ }
111
+
112
+ declareSuccessOfAfterContentLoaded(window.self.document.head || window.self.document.getElementsByTagName('head')[0]);
113
+
114
+ // var numberOfFramesAtBeforeContentLoadedEle = document.createElement('p');
115
+ // numberOfFramesAtBeforeContentLoadedEle.textContent = "Number of iframes upon the main frame's beforeContentLoaded: " +
116
+ // window.self.numberOfFramesAtBeforeContentLoaded;
117
+
118
+ // var numberOfFramesAtAfterContentLoadedEle = document.createElement('p');
119
+ // numberOfFramesAtAfterContentLoadedEle.textContent = "Number of iframes upon the main frame's afterContentLoaded: " + window.frames.length;
120
+ // numberOfFramesAtAfterContentLoadedEle.id = "numberOfFramesAtAfterContentLoadedEle";
121
+
122
+ var namedFramesAtBeforeContentLoadedEle = document.createElement('p');
123
+ namedFramesAtBeforeContentLoadedEle.textContent = "Names of iframes that called beforeContentLoaded: " + JSON.stringify(window.top.injectedIframesBeforeContentLoaded || []);
124
+ namedFramesAtBeforeContentLoadedEle.id = "namedFramesAtBeforeContentLoadedEle";
125
+
126
+ var namedFramesAtAfterContentLoadedEle = document.createElement('p');
127
+ namedFramesAtAfterContentLoadedEle.textContent = "Names of iframes that called afterContentLoaded: " + JSON.stringify(window.top.injectedIframesAfterContentLoaded);
128
+ namedFramesAtAfterContentLoadedEle.id = "namedFramesAtAfterContentLoadedEle";
129
+
130
+ // document.body.appendChild(numberOfFramesAtBeforeContentLoadedEle);
131
+ // document.body.appendChild(numberOfFramesAtAfterContentLoadedEle);
132
+ document.body.appendChild(namedFramesAtBeforeContentLoadedEle);
133
+ document.body.appendChild(namedFramesAtAfterContentLoadedEle);
134
+ } else {
135
+ window.top.injectedIframesAfterContentLoaded.push(window.self.name);
136
+ window.top.document.getElementById('namedFramesAtAfterContentLoadedEle').textContent = "Names of iframes that called afterContentLoaded: " + JSON.stringify(window.top.injectedIframesAfterContentLoaded);
137
+ }
138
+ `}
139
+ />
140
+ </View>
141
+ </View>
142
+ <Text>This test presents three iframes: iframe_0 (yellow); iframe_1 (pink); and iframe_2 (transparent, because its 'X-Frame-Options' is set to 'SAMEORIGIN').</Text>
143
+ <Text>Before injection, the main frame's background is the browser's default value (transparent or white) and each frame has its natural colour.</Text>
144
+ {/*<Text>1a) At injection time "beforeContentLoaded", a variable will be set in each frame to set 'orange' as the "colour to be used".</Text>*/}
145
+ {/*<Text>1b) Also upon "beforeContentLoaded", a style element to change the text "beforeContentLoaded failed" -> "beforeContentLoaded succeeded" will be applied as soon as the head has loaded.</Text>*/}
146
+ {/*<Text>2a) At injection time "afterContentLoaded", that variable will be read – if present, the colour orange will be injected into all frames. Otherwise, cyan.</Text>*/}
147
+ {/*<Text>2b) Also upon "afterContentLoaded", a style element to change the text "afterContentLoaded failed" -> "afterContentLoaded succeeded" will be applied as soon as the head has loaded.</Text>*/}
148
+ <Text>✅ If the main frame becomes orange, then top-frame injection both beforeContentLoaded and afterContentLoaded is supported.</Text>
149
+ <Text>✅ If iframe_0, and iframe_1 become orange, then multi-frame injection beforeContentLoaded and afterContentLoaded is supported.</Text>
150
+ <Text>✅ If the two texts say "beforeContentLoaded on the top frame succeeded!" and "afterContentLoaded on the top frame succeeded!", then both injection times are supported at least on the main frame.</Text>
151
+ <Text>❌ If either of the two iframes become coloured cyan, then for that given frame, JS injection succeeded after the content loaded, but didn't occur before the content loaded.</Text>
152
+ <Text>❌ If "Names of iframes that called beforeContentLoaded: " is [], then see above.</Text>
153
+ <Text>❌ If "Names of iframes that called afterContentLoaded: " is [], then afterContentLoaded is not supported in iframes.</Text>
154
+ <Text>❌ If the main frame becomes coloured cyan, then JS injection succeeded after the content loaded, but didn't occur before the content loaded.</Text>
155
+ <Text>❌ If the text "beforeContentLoaded on the top frame failed" remains unchanged, then JS injection has failed on the main frame before the content loaded.</Text>
156
+ <Text>❌ If the text "afterContentLoaded on the top frame failed" remains unchanged, then JS injection has failed on the main frame after the content loaded.</Text>
157
+ <Text>❌ If the iframes remain their original colours (yellow and pink), then multi-frame injection is not supported at all.</Text>
158
+ </ScrollView>
159
+ );
160
+ }
161
+ }
@@ -0,0 +1,16 @@
1
+ import React, {Component} from 'react';
2
+ import {View, Text, Alert, TextInput, Button} from 'react-native';
3
+ import WebView from 'react-native-webview';
4
+ const localHtmlFile = require('../assets/test.html');
5
+
6
+ export default class LocalPageLoad extends Component<Props, State> {
7
+ render() {
8
+ return (
9
+ <View>
10
+ <View style={{ width: '100%', height: '100%' }}>
11
+ <WebView source={localHtmlFile}/>
12
+ </View>
13
+ </View>
14
+ );
15
+ }
16
+ }
@@ -0,0 +1,63 @@
1
+ import React, {Component} from 'react';
2
+ import {View, Alert} from 'react-native';
3
+
4
+ import WebView from 'react-native-webview';
5
+
6
+ const HTML = `<!DOCTYPE html>\n
7
+ <html>
8
+ <head>
9
+ <title>Messaging</title>
10
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
11
+ <meta name="viewport" content="width=320, user-scalable=no">
12
+ <style type="text/css">
13
+ body {
14
+ margin: 0;
15
+ padding: 0;
16
+ font: 62.5% arial, sans-serif;
17
+ background: #ccc;
18
+ }
19
+ </style>
20
+ </head>
21
+ <body>
22
+ <button onclick="sendPostMessage()">Send post message from JS to WebView</button>
23
+ <p id="demo"></p>
24
+ <script>
25
+ function sendPostMessage() {
26
+ window.ReactNativeWebView.postMessage('Message from JS');
27
+ }
28
+
29
+ document.addEventListener('message',function(event){
30
+ console.log("Message received from RN: ",event.data)
31
+ },false);
32
+
33
+ </script>
34
+ </body>
35
+ </html>`;
36
+
37
+ type Props = {};
38
+ type State = {};
39
+
40
+ export default class Messaging extends Component<Props, State> {
41
+ state = {};
42
+
43
+ constructor(props) {
44
+ super(props);
45
+ this.webView = React.createRef();
46
+ }
47
+
48
+ render() {
49
+ return (
50
+ <View style={{height: 120}}>
51
+ <WebView
52
+ ref={this.webView}
53
+ source={{html: HTML}}
54
+ onLoadEnd={()=>{this.webView.current.postMessage('Hello from RN');}}
55
+ automaticallyAdjustContentInsets={false}
56
+ onMessage={(e: {nativeEvent: {data?: string}}) => {
57
+ Alert.alert('Message received from JS: ', e.nativeEvent.data);
58
+ }}
59
+ />
60
+ </View>
61
+ );
62
+ }
63
+ }
@@ -0,0 +1,22 @@
1
+ import React, { Component } from 'react';
2
+ import { View } from 'react-native';
3
+
4
+ import WebView from 'react-native-webview';
5
+
6
+ type Props = {};
7
+ type State = {};
8
+
9
+ export default class NativeWebpage extends Component<Props, State> {
10
+ state = {};
11
+
12
+ render() {
13
+ return (
14
+ <View style={{ height: 400 }}>
15
+ <WebView
16
+ source={{ uri: 'https://infinite.red' }}
17
+ style={{ width: '100%', height: '100%' }}
18
+ />
19
+ </View>
20
+ );
21
+ }
22
+ }
@@ -0,0 +1,68 @@
1
+ import React, {Component} from 'react';
2
+ import {Button, Text, View} from 'react-native';
3
+
4
+ import WebView from 'react-native-webview';
5
+
6
+ const HTML = `
7
+ <!DOCTYPE html>\n
8
+ <html>
9
+ <head>
10
+ <title>Hello World</title>
11
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
12
+ <meta name="viewport" content="width=320, user-scalable=no">
13
+ <style type="text/css">
14
+ body {
15
+ margin: 0;
16
+ padding: 0;
17
+ font: 62.5% arial, sans-serif;
18
+ background: #ccc;
19
+ }
20
+ </style>
21
+ </head>
22
+ <body>
23
+ <p>Lorem ipsum dolor sit amet, virtute utroque voluptaria et duo, probo aeque partiendo pri at. Mea ut stet aliquip deterruisset. Inani erroribus principes ei mel, no dicit recteque delicatissimi usu. Ne has dolore nominavi, feugait hendrerit interesset vis ea, amet regione ne pri. Te cum amet etiam.</p>
24
+ <p>Ut adipiscing neglegentur mediocritatem sea, suas abhorreant ius cu, ne nostrud feugiat per. Nam paulo facete iudicabit an, an brute mundi suavitate has, ex utamur numquam duo. Sea platonem argumentum instructior in, quo no prima inani perfecto. Ex illum postea copiosae has, ei mea sonet ocurreret.</p>
25
+ <p>Has convenire erroribus cu, quo homero facilisis inciderint ea. Vix choro gloriatur definitionem an, te exerci debitis voluptaria pri, mea admodum antiopam neglegentur te. His ea iisque splendide, nam id malorum pertinacia. Iusto tempor in eos, vis debet erant an. An nostrum rationibus sit, et sed dicta delenit suscipiantur. Est dolore vituperatoribus in, ubique explicari est cu. Legere tractatos ut usu, probo atqui vituperata in usu, mazim nemore praesent pro no.</p>
26
+ <p>Ei pri facilisi accusamus. Ut partem quaestio sit, an usu audiam quaerendum, ei qui hinc soleat. Fabulas phaedrum erroribus ut est. Intellegebat delicatissimi vis cu. His ea vidit libris facilis. Usu ne scripta legimus intellegam. Hendrerit urbanitas accommodare mei in.</p>
27
+ <p>Brute appetere efficiendi has ne. Ei ornatus labores vis. Vel harum fierent no, ad erat partiendo vis, harum democritum duo at. Has no labitur vulputate. Has cu autem aperiam hendrerit, sed eu justo verear menandri.</p>
28
+ </body>
29
+ </html>
30
+ `;
31
+
32
+ type Props = {};
33
+ type State = {
34
+ scrollEnabled: boolean,
35
+ lastScrollEvent: string
36
+ };
37
+
38
+ export default class Scrolling extends Component<Props, State> {
39
+ state = {
40
+ scrollEnabled: true,
41
+ lastScrollEvent: ''
42
+ };
43
+
44
+ render() {
45
+ return (
46
+ <View>
47
+ <View style={{ height: 120 }}>
48
+ <WebView
49
+ source={{html: HTML}}
50
+ automaticallyAdjustContentInsets={false}
51
+ onScroll={this._onScroll}
52
+ scrollEnabled={this.state.scrollEnabled}
53
+ />
54
+ </View>
55
+ <Button
56
+ title={this.state.scrollEnabled ? 'Scroll enabled' : 'Scroll disabled'}
57
+ onPress={() => this.setState({scrollEnabled: !this.state.scrollEnabled})}
58
+ />
59
+ <Text>Last scroll event:</Text>
60
+ <Text>{this.state.lastScrollEvent}</Text>
61
+ </View>
62
+ );
63
+ }
64
+
65
+ _onScroll = event => {
66
+ this.setState({lastScrollEvent: JSON.stringify(event.nativeEvent)});
67
+ }
68
+ }
@@ -0,0 +1,69 @@
1
+ import React, {Component} from 'react';
2
+ import {Button, Linking, Text, View} from 'react-native';
3
+
4
+ import WebView from 'react-native-webview';
5
+
6
+ const HTML = `
7
+ <!DOCTYPE html>\n
8
+ <html>
9
+ <head>
10
+ <title>Uploads</title>
11
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
12
+ <meta name="viewport" content="width=320, user-scalable=no">
13
+ <style type="text/css">
14
+ body {
15
+ margin: 0;
16
+ padding: 0;
17
+ font: 62.5% arial, sans-serif;
18
+ background: #ccc;
19
+ }
20
+ </style>
21
+ </head>
22
+ <body>
23
+ <p>
24
+ <label for="images-only">Images only file upload</label>
25
+ <input name="images-only" type="file" accept="image/*">
26
+ </p>
27
+ <p>
28
+ <label for="video-only">Video only file upload</label>
29
+ <input name="video-only" type="file" accept="video/*">
30
+ </p>
31
+ <p>
32
+ <label for="any-file">Any file upload</label>
33
+ <input name="any-file" type="file">
34
+ </p>
35
+ </body>
36
+ </html>
37
+ `;
38
+
39
+ type Props = {};
40
+ type State = {};
41
+
42
+ export default class Uploads extends Component<Props, State> {
43
+ state = {};
44
+
45
+ render() {
46
+ return (
47
+ <View>
48
+ <View style={{ height: 120 }}>
49
+ <WebView
50
+ source={{html: HTML}}
51
+ automaticallyAdjustContentInsets={false}
52
+ />
53
+ </View>
54
+ <Text>
55
+ Android limitation: If the file input should show camera options for the user,
56
+ and the app has the ability to request the camera permission, then the user must
57
+ grant permission first in order to see the options. Since this example app does
58
+ have the permission declared, you must allow it in settings to be able to see
59
+ camera options. If your app does not have the camera permission declared, then
60
+ there is no restriction to showing the camera options.
61
+ </Text>
62
+ <Button
63
+ title="Open settings"
64
+ onPress={() => Linking.openSettings()}
65
+ />
66
+ </View>
67
+ );
68
+ }
69
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @format
3
+ */
4
+
5
+ import {AppRegistry} from 'react-native';
6
+ import App from './App';
7
+ import {name as appName} from './app.json';
8
+
9
+ AppRegistry.registerComponent(appName, () => App);
@@ -0,0 +1,8 @@
1
+ require_relative '../../node_modules/react-native-test-app/test_app'
2
+
3
+ workspace 'WebviewExample.xcworkspace'
4
+
5
+ # Comment out the line below to enable Flipper
6
+ use_flipper! false
7
+
8
+ use_test_app! :hermes_enabled => false