Skip to content

Commit 9ddaeef

Browse files
authored
feat: web example (#1254)
## 📜 Description Added web example for FabricExample. ## 💡 Motivation and Context This PR adds initial changes for #43 implementation. For now I just add: - example project; - job for CI to assure that build can be done successfully; - e2e test that assures that app can be running without runtime crashes. Later I'm planning to see how `VirtualKeyboard` API can be integrated in this package and whether it's possible to use any kind of polyfill for unsupported platforms. Also I'll explore how this package can be integrated in `WebView`. ## 📢 Changelog <!-- High level overview of important changes --> <!-- For example: fixed status bar manipulation; added new types declarations; --> <!-- If your changes don't affect one of platform/language below - then remove this platform/language --> ### JS - update react-navigation (couldn't make it working with navigation 6.x); - polyfilled `BlurView`+`haptic` in example app; - added web example; - updated snapshots (testing-library); ### E2E - updated all assets for all platforms; - starts to use Maestro for web testing (maybe in future we'll migrate to Maestro as universal testing framework); ### CI - added build stage for web example; - added e2e test (maestro) that verifies that app is running successfully; ## 🤔 How Has This Been Tested? Tested via CI + manually on localhost:8080. ## 📸 Screenshots (if appropriate): |Main menu|Example| |-----------|---------| |![telegram-cloud-photo-size-2-5348379378095689628-y](https://github.com/user-attachments/assets/c4526831-b45d-432c-8bbd-771f0d8dd344)|<video src="https://github.com/user-attachments/assets/e55ee1c9-8f76-4f09-8460-95aa01a87d3e">| ## 📝 Checklist - [x] CI successfully passed - [x] I added new mocks and corresponding unit-tests if library API was changed
1 parent afeccd1 commit 9ddaeef

442 files changed

Lines changed: 2494 additions & 186 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/web-e2e-test.yml

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
name: 🌐 Test Web
2+
on:
3+
pull_request:
4+
paths:
5+
- ".github/workflows/web-e2e-test.yml"
6+
- "package.json"
7+
- "yarn.lock"
8+
- "FabricExample/**"
9+
- "src/**"
10+
- "e2e/**"
11+
push:
12+
branches:
13+
- main
14+
paths:
15+
- ".github/workflows/web-e2e-test.yml"
16+
- "package.json"
17+
- "yarn.lock"
18+
- "FabricExample/**"
19+
- "src/**"
20+
- "e2e/**"
21+
22+
jobs:
23+
build-web:
24+
name: 🏗️ Build web
25+
defaults:
26+
run:
27+
working-directory: FabricExample
28+
runs-on: macos-15
29+
steps:
30+
- uses: actions/checkout@v4
31+
32+
- name: Get yarn cache directory path
33+
id: fabric-yarn-cache-dir-path
34+
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
35+
- name: Restore node_modules from cache
36+
uses: actions/cache@v4
37+
id: yarn-cache
38+
with:
39+
path: ${{ steps.fabric-yarn-cache-dir-path.outputs.dir }}
40+
key: ${{ runner.os }}-fabric-yarn-${{ hashFiles('**/yarn.lock') }}
41+
restore-keys: |
42+
${{ runner.os }}-fabric-yarn-
43+
- name: Install node_modules for FabricExample/
44+
run: yarn install --frozen-lockfile
45+
- name: Install root dependencies
46+
run: yarn install --frozen-lockfile --cwd ..
47+
48+
- name: Build web
49+
run: yarn build:web
50+
51+
- uses: actions/upload-artifact@v4
52+
with:
53+
name: web-e2e-app
54+
path: FabricExample/dist/**
55+
56+
test-web:
57+
name: ⚙️ Automated test cases
58+
runs-on: macos-15
59+
timeout-minutes: 60
60+
needs: build-web
61+
steps:
62+
- uses: actions/checkout@v4
63+
- name: Download a single artifact
64+
uses: actions/download-artifact@v4
65+
with:
66+
name: web-e2e-app
67+
path: FabricExample/dist/
68+
- name: Start static server
69+
run: |
70+
cd FabricExample
71+
npx http-server dist -p 8080 --silent --gzip --brotli --hostname 127.0.0.1 >/dev/null 2>&1 &
72+
echo $! > .server-pid
73+
- name: Install Maestro
74+
run: |
75+
curl -fsSL "https://get.maestro.mobile.dev" | bash
76+
echo "$HOME/.maestro/bin" >> $GITHUB_PATH
77+
export MAESTRO_DRIVER_STARTUP_TIMEOUT=600000
78+
- name: Specify web env
79+
run: |
80+
find e2e/flows -name "*.yml" -exec sh -c '
81+
for file; do
82+
printf "url: http://localhost:8080\n" | cat - "$file" > temp && mv temp "$file"
83+
done
84+
' sh {} +
85+
shell: bash
86+
- name: Run tests
87+
run: |
88+
export MAESTRO_CLI_NO_ANALYTICS="true"
89+
export MAESTRO_CHROME_FLAGS="--no-sandbox --user-data-dir=/tmp/chrome-profile-$GITHUB_RUN_ID"
90+
maestro --platform web test -e DEVICE="Chrome" --headless e2e/flows/* --format html ./e2e/reports/debug --debug-output ./e2e/reports/debug --flatten-debug-output
91+
- name: Stop server
92+
if: always()
93+
run: |
94+
kill $(cat .server-pid) || true
95+
- name: Upload test report
96+
if: failure()
97+
uses: actions/upload-artifact@v4
98+
with:
99+
path: ./e2e/reports
100+
name: e2e-report-web

FabricExample/__tests__/__snapshots__/components-rendering.spec.tsx.snap

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -114,16 +114,7 @@ exports[`components rendering should render \`KeyboardStickyView\` 1`] = `
114114

115115
exports[`components rendering should render \`KeyboardToolbar\` 1`] = `
116116
<View
117-
content={
118-
{
119-
"$$typeof": Symbol(react.transitional.element),
120-
"_owner": null,
121-
"_store": {},
122-
"key": null,
123-
"props": {},
124-
"type": [Function],
125-
}
126-
}
117+
content={<EmptyView />}
127118
/>
128119
`;
129120

FabricExample/index.html

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
7+
<title>Keyboard Controller Example</title>
8+
<style>
9+
#app-root {
10+
display: flex;
11+
flex: 1 1 100%;
12+
height: 100vh;
13+
}
14+
</style>
15+
</head>
16+
<body>
17+
<div id="app-root"></div>
18+
</body>
19+
</html>

FabricExample/index.web.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { AppRegistry } from "react-native";
2+
3+
import name from "./app.json";
4+
import App from "./src/App";
5+
6+
AppRegistry.registerComponent(name, () => App);
7+
AppRegistry.runApplication(name, {
8+
initialProps: {},
9+
rootTag: document.getElementById("app-root"),
10+
});

FabricExample/package.json

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,25 @@
55
"scripts": {
66
"android": "react-native run-android",
77
"ios": "react-native run-ios",
8+
"web": "webpack serve --mode=development --config webpack.config.js",
9+
"build:web": "webpack --mode=production --config webpack.config.js",
810
"start": "react-native start",
911
"test": "jest",
1012
"postinstall": "patch-package"
1113
},
1214
"dependencies": {
1315
"@gorhom/bottom-sheet": "5.2.6",
16+
"@lottiefiles/dotlottie-react": "^0.17.10",
1417
"@react-native-community/blur": "^4.4.1",
1518
"@react-native-masked-view/masked-view": "^0.3.2",
16-
"@react-navigation/bottom-tabs": "^6.6.1",
17-
"@react-navigation/elements": "^2.2.5",
18-
"@react-navigation/native": "^6.1.18",
19-
"@react-navigation/native-stack": "^6.11.0",
20-
"@react-navigation/stack": "^6.4.1",
19+
"@react-navigation/bottom-tabs": "^7.9.0",
20+
"@react-navigation/elements": "2.6.4",
21+
"@react-navigation/native": "7.1.17",
22+
"@react-navigation/native-stack": "7.3.26",
23+
"@react-navigation/stack": "7.3.6",
2124
"lottie-react-native": "^7.3.4",
2225
"react": "19.1.0",
26+
"react-dom": "19.1.0",
2327
"react-native": "0.81.4",
2428
"react-native-advanced-input-mask": "^1.4.6",
2529
"react-native-gesture-handler": "2.28.0",
@@ -29,6 +33,7 @@
2933
"react-native-safe-area-context": "5.6.1",
3034
"react-native-screens": "4.16.0",
3135
"react-native-toast-message": "^2.3.0",
36+
"react-native-web": "^0.21.2",
3237
"react-native-worklets": "^0.6.0"
3338
},
3439
"devDependencies": {
@@ -47,9 +52,16 @@
4752
"@types/jest": "^29.5.13",
4853
"@types/react": "^19.1.0",
4954
"@types/react-test-renderer": "^19.1.0",
55+
"babel-loader": "^10.0.0",
56+
"babel-plugin-react-native-web": "^0.21.2",
57+
"html-webpack-plugin": "^5.6.5",
5058
"jest": "^29.6.3",
5159
"patch-package": "^6.4.7",
52-
"react-test-renderer": "19.1.0"
60+
"react-test-renderer": "19.1.0",
61+
"url-loader": "^4.1.1",
62+
"webpack": "^5.104.1",
63+
"webpack-cli": "^6.0.1",
64+
"webpack-dev-server": "^5.2.2"
5365
},
5466
"jest": {
5567
"preset": "react-native",
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { BlurView } from "@react-native-community/blur";
2+
3+
export default BlurView;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import type { ViewStyle } from "react-native";
2+
3+
type BlurViewProps = {
4+
blurAmount: number;
5+
blurType: string;
6+
reducedTransparencyFallbackColor: string;
7+
style: ViewStyle;
8+
children?: React.ReactNode;
9+
};
10+
11+
function BlurView({ blurAmount, children, style }: BlurViewProps) {
12+
return (
13+
<div
14+
style={{
15+
...(style as React.CSSProperties),
16+
filter: `blur(${blurAmount}px)`,
17+
}}
18+
>
19+
{children}
20+
</div>
21+
);
22+
}
23+
24+
export default BlurView;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { trigger } from "react-native-haptic-feedback";
2+
3+
export default trigger;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const trigger = (_mode: string, _options: Record<string, boolean>) => {};
2+
3+
export default trigger;

FabricExample/src/screens/Examples/ImageGallery/index.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,11 @@ const SharedTransition = ({
150150

151151
{!!image.img && (
152152
<GestureDetector gesture={gesture}>
153-
<Reanimated.Image fadeDuration={0} src={image.img} style={style} />
153+
<Reanimated.Image
154+
fadeDuration={0}
155+
source={{ uri: image.img }}
156+
style={style}
157+
/>
154158
</GestureDetector>
155159
)}
156160
</GestureHandlerRootView>
@@ -193,7 +197,12 @@ const ImagePreview = ({
193197
{modal === src && isModalFullyVisible ? (
194198
<View style={styles.image} />
195199
) : (
196-
<Image ref={ref} fadeDuration={0} src={src} style={styles.image} />
200+
<Image
201+
ref={ref}
202+
fadeDuration={0}
203+
source={{ uri: src }}
204+
style={styles.image}
205+
/>
197206
)}
198207
</TouchableOpacity>
199208
);

0 commit comments

Comments
 (0)