@admin-layout/gluestack-ui-mobile 12.2.5-alpha.15 → 12.2.5-alpha.17
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.
- package/CHANGELOG.md +6 -0
- package/babel.config.cjs +7 -0
- package/jest-react-navigation-setup.js +1 -1
- package/{jest.config.js → jest.config.cjs} +9 -5
- package/lib/components/ErrorBounday.d.ts +9 -1
- package/lib/components/ErrorBounday.js +13 -3
- package/lib/components/ErrorBounday.js.map +1 -1
- package/lib/components/Fallback.d.ts +2 -0
- package/lib/components/Fallback.js +14 -5
- package/lib/components/Fallback.js.map +1 -1
- package/lib/components/__tests__/ErrorBoundary.test.d.ts +1 -0
- package/lib/components/__tests__/ErrorBoundary.test.js +47 -0
- package/lib/components/__tests__/ErrorBoundary.test.js.map +1 -0
- package/package.json +2 -2
- package/src/components/ErrorBounday.tsx +14 -4
- package/src/components/Fallback.tsx +19 -6
- package/src/components/__tests__/ErrorBoundary.test.tsx +56 -0
- package/babel.config.js +0 -26
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [12.2.5-alpha.17](https://github.com/CDEBase/admin-layout/compare/v12.2.5-alpha.16...v12.2.5-alpha.17) (2026-10-10)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
- **gluestack-ui-mobile:** the error screen's button shows and brings the app back ([4d21d32](https://github.com/CDEBase/admin-layout/commit/4d21d32b142d54cc0edd99bb15084b33cf2ab11d))
|
|
11
|
+
|
|
6
12
|
## [12.2.5-alpha.15](https://github.com/CDEBase/admin-layout/compare/v12.2.5-alpha.14...v12.2.5-alpha.15) (2026-10-08)
|
|
7
13
|
|
|
8
14
|
### Bug Fixes
|
package/babel.config.cjs
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Jest only (the package builds with tsc). The React Native preset already
|
|
2
|
+
// carries TypeScript, JSX, class properties and CommonJS modules; the
|
|
3
|
+
// hand-listed plugins this used to add clashed with it ("'loose' mode
|
|
4
|
+
// configuration must be the same") and the suite never ran.
|
|
5
|
+
module.exports = {
|
|
6
|
+
presets: ['module:@react-native/babel-preset'],
|
|
7
|
+
};
|
|
@@ -11,4 +11,4 @@ jest.mock('react-native-reanimated', () => {
|
|
|
11
11
|
});
|
|
12
12
|
|
|
13
13
|
// Silence the warning: Animated: `useNativeDriver` is not supported because the native animated module is missing
|
|
14
|
-
jest.mock('react-native/
|
|
14
|
+
jest.mock('react-native/src/private/animated/NativeAnimatedHelper');
|
|
@@ -10,18 +10,21 @@ const mergeData = merge.recursive(
|
|
|
10
10
|
babelConfig: true,
|
|
11
11
|
},
|
|
12
12
|
},
|
|
13
|
-
name: packageJson.name,
|
|
14
13
|
displayName: packageJson.name,
|
|
15
14
|
testEnvironment: "jsdom",
|
|
16
15
|
preset: "react-native",
|
|
17
16
|
setupFiles: [
|
|
18
17
|
"<rootDir>/jest-react-navigation-setup.js"
|
|
19
18
|
],
|
|
20
|
-
setupFilesAfterEnv: ["
|
|
19
|
+
setupFilesAfterEnv: ["./jest.env.js"],
|
|
20
|
+
// React Native ships Flow + ESM source: compile it with this package's
|
|
21
|
+
// RN preset (babel.config.cjs). The repo-wide transform.js has no Flow
|
|
22
|
+
// support, and node_modules is hoisted to the repo root, so a
|
|
23
|
+
// <rootDir>/node_modules pattern never matched it.
|
|
21
24
|
"transform": {
|
|
22
|
-
"\\.(js|jsx)?$": "
|
|
25
|
+
"\\.(js|jsx)?$": "babel-jest",
|
|
23
26
|
},
|
|
24
|
-
transformIgnorePatterns: ["
|
|
27
|
+
transformIgnorePatterns: ["/node_modules/(?!((jest-)?react-native[^/]*|@react-native[^/]*|@react-navigation|@expo|@common-stack|@cdm-logger|@apollo|@web3-storage)/)"],
|
|
25
28
|
moduleNameMapper: {
|
|
26
29
|
'^__mocks__/(.*)$': '<rootDir>/../../__mocks__/$1',
|
|
27
30
|
// we'll use commonjs version of lodash for tests 👌
|
|
@@ -31,7 +34,8 @@ const mergeData = merge.recursive(
|
|
|
31
34
|
// '@react-navigation': '<rootDir>/__mocks__/@react-navigation.js',
|
|
32
35
|
},
|
|
33
36
|
roots: [
|
|
34
|
-
"src/utils"
|
|
37
|
+
"src/utils",
|
|
38
|
+
"src/components/__tests__"
|
|
35
39
|
],
|
|
36
40
|
},
|
|
37
41
|
);
|
|
@@ -12,7 +12,15 @@ export declare class ErrorBoundary extends React.Component<IErrorBoundryProps, I
|
|
|
12
12
|
hasError: boolean;
|
|
13
13
|
error: Error;
|
|
14
14
|
};
|
|
15
|
-
componentDidCatch(error: Error): void;
|
|
15
|
+
componentDidCatch(error: Error, info: React.ErrorInfo): void;
|
|
16
|
+
/**
|
|
17
|
+
* Mount the children again. The app's boundary wraps the navigation
|
|
18
|
+
* container itself, so while the fallback shows there is no navigator to
|
|
19
|
+
* navigate with: "Go to homepage" called navigate on nothing and the app
|
|
20
|
+
* stayed on this screen until it was force-quit. A fresh mount starts the
|
|
21
|
+
* navigator at its initial route.
|
|
22
|
+
*/
|
|
23
|
+
resetError: () => void;
|
|
16
24
|
render(): React.JSX.Element;
|
|
17
25
|
}
|
|
18
26
|
export {};
|
|
@@ -4,17 +4,27 @@ import { logger } from '@cdm-logger/client';
|
|
|
4
4
|
export class ErrorBoundary extends React.Component {
|
|
5
5
|
constructor(props) {
|
|
6
6
|
super(props);
|
|
7
|
+
/**
|
|
8
|
+
* Mount the children again. The app's boundary wraps the navigation
|
|
9
|
+
* container itself, so while the fallback shows there is no navigator to
|
|
10
|
+
* navigate with: "Go to homepage" called navigate on nothing and the app
|
|
11
|
+
* stayed on this screen until it was force-quit. A fresh mount starts the
|
|
12
|
+
* navigator at its initial route.
|
|
13
|
+
*/
|
|
14
|
+
this.resetError = () => {
|
|
15
|
+
this.setState({ hasError: false, error: null });
|
|
16
|
+
};
|
|
7
17
|
this.state = { hasError: false, error: null };
|
|
8
18
|
}
|
|
9
19
|
static getDerivedStateFromError(error) {
|
|
10
20
|
return { hasError: true, error };
|
|
11
21
|
}
|
|
12
|
-
componentDidCatch(error) {
|
|
13
|
-
logger.
|
|
22
|
+
componentDidCatch(error, info) {
|
|
23
|
+
logger.error(error, info?.componentStack ?? '');
|
|
14
24
|
}
|
|
15
25
|
render() {
|
|
16
26
|
if (this.state.hasError) {
|
|
17
|
-
return React.createElement(FallbackComponent, { error: this.state.error });
|
|
27
|
+
return React.createElement(FallbackComponent, { error: this.state.error, resetError: this.resetError });
|
|
18
28
|
}
|
|
19
29
|
return this.props.children;
|
|
20
30
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ErrorBounday.js","sourceRoot":"","sources":["../../src/components/ErrorBounday.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuB,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"ErrorBounday.js","sourceRoot":"","sources":["../../src/components/ErrorBounday.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuB,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAO5C,MAAM,OAAO,aAAc,SAAQ,KAAK,CAAC,SAAiD;IACtF,YAAY,KAAyB;QACjC,KAAK,CAAC,KAAK,CAAC,CAAC;QAYjB;;;;;;WAMG;QACH,eAAU,GAAG,GAAG,EAAE;YACd,IAAI,CAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACpD,CAAC,CAAC;QApBE,IAAI,CAAC,KAAK,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAClD,CAAC;IAED,MAAM,CAAC,wBAAwB,CAAC,KAAY;QACxC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IACrC,CAAC;IAED,iBAAiB,CAAC,KAAY,EAAE,IAAqB;QACjD,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,cAAc,IAAI,EAAE,CAAC,CAAC;IACpD,CAAC;IAaD,MAAM;QACF,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;YACtB,OAAO,oBAAC,iBAAiB,IAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,KAAc,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,GAAI,CAAC;QAChG,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;IAC/B,CAAC;CACJ"}
|
|
@@ -3,15 +3,20 @@ import { Button, ButtonText } from '@gluestack-ui/themed';
|
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
import { StyleSheet } from 'react-native';
|
|
5
5
|
import { SafeAreaView, Text, View } from 'react-native';
|
|
6
|
-
import { useDispatch } from 'react-redux';
|
|
7
6
|
export const FallbackComponent = (props) => {
|
|
8
|
-
const
|
|
7
|
+
const goHome = () => {
|
|
8
|
+
if (props.resetError) {
|
|
9
|
+
props.resetError();
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
navigationRef.navigate('MainStack.Layout.Home');
|
|
13
|
+
};
|
|
9
14
|
return (React.createElement(SafeAreaView, { style: styles.container },
|
|
10
15
|
React.createElement(View, { style: styles.content },
|
|
11
16
|
React.createElement(Text, { style: styles.title }, "Oops!"),
|
|
12
17
|
React.createElement(Text, { style: styles.subtitle }, "There's an error"),
|
|
13
18
|
React.createElement(Text, { style: styles.error }, props?.error?.message),
|
|
14
|
-
React.createElement(Button, { style: styles.button, onPress:
|
|
19
|
+
React.createElement(Button, { style: styles.button, onPress: goHome },
|
|
15
20
|
React.createElement(ButtonText, { style: styles.buttonText }, "Go to homepage")))));
|
|
16
21
|
};
|
|
17
22
|
const styles = StyleSheet.create({
|
|
@@ -37,14 +42,18 @@ const styles = StyleSheet.create({
|
|
|
37
42
|
error: {
|
|
38
43
|
paddingVertical: 16,
|
|
39
44
|
},
|
|
45
|
+
// A fixed height with no vertical padding: `padding: 25` inside
|
|
46
|
+
// `height: 40` left the label -10pt and it never showed.
|
|
40
47
|
button: {
|
|
41
48
|
backgroundColor: '#2196f3',
|
|
42
49
|
borderRadius: 50,
|
|
43
|
-
|
|
44
|
-
|
|
50
|
+
height: 52,
|
|
51
|
+
paddingVertical: 0,
|
|
52
|
+
paddingHorizontal: 24,
|
|
45
53
|
},
|
|
46
54
|
buttonText: {
|
|
47
55
|
color: '#fff',
|
|
56
|
+
fontSize: 17,
|
|
48
57
|
fontWeight: '600',
|
|
49
58
|
textAlign: 'center',
|
|
50
59
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Fallback.js","sourceRoot":"","sources":["../../src/components/Fallback.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"Fallback.js","sourceRoot":"","sources":["../../src/components/Fallback.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAQxD,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,KAAY,EAAE,EAAE;IAC9C,MAAM,MAAM,GAAG,GAAG,EAAE;QAChB,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;YACnB,KAAK,CAAC,UAAU,EAAE,CAAC;YACnB,OAAO;QACX,CAAC;QACD,aAAa,CAAC,QAAQ,CAAC,uBAAuB,CAAC,CAAC;IACpD,CAAC,CAAC;IACF,OAAO,CACH,oBAAC,YAAY,IAAC,KAAK,EAAE,MAAM,CAAC,SAAS;QACjC,oBAAC,IAAI,IAAC,KAAK,EAAE,MAAM,CAAC,OAAO;YACvB,oBAAC,IAAI,IAAC,KAAK,EAAE,MAAM,CAAC,KAAK,YAAc;YACvC,oBAAC,IAAI,IAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,IAAG,kBAAkB,CAAQ;YACzD,oBAAC,IAAI,IAAC,KAAK,EAAE,MAAM,CAAC,KAAK,IAAG,KAAK,EAAE,KAAK,EAAE,OAAO,CAAQ;YACzD,oBAAC,MAAM,IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM;gBACzC,oBAAC,UAAU,IAAC,KAAK,EAAE,MAAM,CAAC,UAAU,qBAA6B,CAC5D,CACN,CACI,CAClB,CAAC;AACN,CAAC,CAAC;AAEF,MAAM,MAAM,GAAQ,UAAU,CAAC,MAAM,CAAC;IAClC,SAAS,EAAE;QACP,eAAe,EAAE,SAAS;QAC1B,IAAI,EAAE,CAAC;QACP,cAAc,EAAE,QAAQ;KAC3B;IACD,OAAO,EAAE;QACL,gBAAgB,EAAE,EAAE;KACvB;IACD,KAAK,EAAE;QACH,QAAQ,EAAE,EAAE;QACZ,UAAU,EAAE,KAAK;QACjB,aAAa,EAAE,EAAE;QACjB,KAAK,EAAE,MAAM;KAChB;IACD,QAAQ,EAAE;QACN,QAAQ,EAAE,EAAE;QACZ,UAAU,EAAE,KAAK;QACjB,KAAK,EAAE,MAAM;KAChB;IACD,KAAK,EAAE;QACH,eAAe,EAAE,EAAE;KACtB;IACD,gEAAgE;IAChE,yDAAyD;IACzD,MAAM,EAAE;QACJ,eAAe,EAAE,SAAS;QAC1B,YAAY,EAAE,EAAE;QAChB,MAAM,EAAE,EAAE;QACV,eAAe,EAAE,CAAC;QAClB,iBAAiB,EAAE,EAAE;KACxB;IACD,UAAU,EAAE;QACR,KAAK,EAAE,MAAM;QACb,QAAQ,EAAE,EAAE;QACZ,UAAU,EAAE,KAAK;QACjB,SAAS,EAAE,QAAQ;KACtB;CACJ,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The app-wide boundary wraps the navigation container. Live 2026-10-10: a
|
|
3
|
+
* screen threw, the fallback showed, and "Go to homepage" did nothing (its
|
|
4
|
+
* label did not even show) - the app was stuck until it was force-quit.
|
|
5
|
+
*/
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { Text } from 'react-native';
|
|
8
|
+
import { fireEvent, render } from '@testing-library/react-native';
|
|
9
|
+
import { ErrorBoundary } from '../ErrorBounday';
|
|
10
|
+
const mockNavigate = jest.fn();
|
|
11
|
+
jest.mock('@common-stack/client-react', () => ({
|
|
12
|
+
navigationRef: { navigate: (...a) => mockNavigate(...a) },
|
|
13
|
+
}));
|
|
14
|
+
jest.mock('@cdm-logger/client', () => ({ logger: { error: jest.fn(), debug: jest.fn() } }));
|
|
15
|
+
jest.mock('@gluestack-ui/themed', () => {
|
|
16
|
+
const { Pressable: P, Text: T } = jest.requireActual('react-native');
|
|
17
|
+
return { Button: P, ButtonText: T };
|
|
18
|
+
});
|
|
19
|
+
let crash = true;
|
|
20
|
+
function Screen() {
|
|
21
|
+
if (crash)
|
|
22
|
+
throw new Error("Property 'promoProgress' doesn't exist");
|
|
23
|
+
return React.createElement(Text, null, "Home");
|
|
24
|
+
}
|
|
25
|
+
describe('app error boundary', () => {
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
crash = true;
|
|
28
|
+
mockNavigate.mockClear();
|
|
29
|
+
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
30
|
+
});
|
|
31
|
+
it('shows the error and a labelled way home', () => {
|
|
32
|
+
const { getByText } = render(React.createElement(ErrorBoundary, null,
|
|
33
|
+
React.createElement(Screen, null)));
|
|
34
|
+
expect(getByText("Property 'promoProgress' doesn't exist")).toBeTruthy();
|
|
35
|
+
expect(getByText('Go to homepage')).toBeTruthy();
|
|
36
|
+
});
|
|
37
|
+
it('"Go to homepage" mounts the app again instead of navigating a navigator that is gone', () => {
|
|
38
|
+
const { getByText, queryByText } = render(React.createElement(ErrorBoundary, null,
|
|
39
|
+
React.createElement(Screen, null)));
|
|
40
|
+
crash = false;
|
|
41
|
+
fireEvent.press(getByText('Go to homepage'));
|
|
42
|
+
expect(getByText('Home')).toBeTruthy();
|
|
43
|
+
expect(queryByText("There's an error")).toBeNull();
|
|
44
|
+
expect(mockNavigate).not.toHaveBeenCalled();
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
//# sourceMappingURL=ErrorBoundary.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ErrorBoundary.test.js","sourceRoot":"","sources":["../../../src/components/__tests__/ErrorBoundary.test.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,MAAM,YAAY,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;AAC/B,IAAI,CAAC,IAAI,CAAC,4BAA4B,EAAE,GAAG,EAAE,CAAC,CAAC;IAC3C,aAAa,EAAE,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAY,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,EAAE;CACvE,CAAC,CAAC,CAAC;AACJ,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;AAC5F,IAAI,CAAC,IAAI,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACnC,MAAM,EAAE,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC;IACrE,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC;AACxC,CAAC,CAAC,CAAC;AAEH,IAAI,KAAK,GAAG,IAAI,CAAC;AACjB,SAAS,MAAM;IACX,IAAI,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IACrE,OAAO,oBAAC,IAAI,eAAY,CAAC;AAC7B,CAAC;AAED,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAChC,UAAU,CAAC,GAAG,EAAE;QACZ,KAAK,GAAG,IAAI,CAAC;QACb,YAAY,CAAC,SAAS,EAAE,CAAC;QACzB,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QAC/C,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CACxB,oBAAC,aAAa;YACV,oBAAC,MAAM,OAAG,CACE,CACnB,CAAC;QACF,MAAM,CAAC,SAAS,CAAC,wCAAwC,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;QACzE,MAAM,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sFAAsF,EAAE,GAAG,EAAE;QAC5F,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,GAAG,MAAM,CACrC,oBAAC,aAAa;YACV,oBAAC,MAAM,OAAG,CACE,CACnB,CAAC;QACF,KAAK,GAAG,KAAK,CAAC;QACd,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC,CAAC;QAC7C,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;QACvC,MAAM,CAAC,WAAW,CAAC,kBAAkB,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QACnD,MAAM,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAChD,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@admin-layout/gluestack-ui-mobile",
|
|
3
|
-
"version": "12.2.5-alpha.
|
|
3
|
+
"version": "12.2.5-alpha.17",
|
|
4
4
|
"description": "Glustack-ui for mobile component layout",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"author": "CDMBase LLC",
|
|
@@ -50,5 +50,5 @@
|
|
|
50
50
|
"typescript": {
|
|
51
51
|
"definition": "lib/index.d.ts"
|
|
52
52
|
},
|
|
53
|
-
"gitHead": "
|
|
53
|
+
"gitHead": "c9eb00d88ad357e72bd1fc58b67081a434476dbe"
|
|
54
54
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React, { ReactElement } from 'react';
|
|
2
2
|
import { FallbackComponent } from './Fallback';
|
|
3
3
|
import { logger } from '@cdm-logger/client';
|
|
4
|
-
import { useDispatch } from 'react-redux';
|
|
5
4
|
|
|
6
5
|
type IErrorBoundryState = { hasError: boolean; error: Error | null };
|
|
7
6
|
type IErrorBoundryProps = {
|
|
@@ -18,13 +17,24 @@ export class ErrorBoundary extends React.Component<IErrorBoundryProps, IErrorBou
|
|
|
18
17
|
return { hasError: true, error };
|
|
19
18
|
}
|
|
20
19
|
|
|
21
|
-
componentDidCatch(error: Error) {
|
|
22
|
-
logger.
|
|
20
|
+
componentDidCatch(error: Error, info: React.ErrorInfo) {
|
|
21
|
+
logger.error(error, info?.componentStack ?? '');
|
|
23
22
|
}
|
|
24
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Mount the children again. The app's boundary wraps the navigation
|
|
26
|
+
* container itself, so while the fallback shows there is no navigator to
|
|
27
|
+
* navigate with: "Go to homepage" called navigate on nothing and the app
|
|
28
|
+
* stayed on this screen until it was force-quit. A fresh mount starts the
|
|
29
|
+
* navigator at its initial route.
|
|
30
|
+
*/
|
|
31
|
+
resetError = () => {
|
|
32
|
+
this.setState({ hasError: false, error: null });
|
|
33
|
+
};
|
|
34
|
+
|
|
25
35
|
render() {
|
|
26
36
|
if (this.state.hasError) {
|
|
27
|
-
return <FallbackComponent error={this.state.error as Error} />;
|
|
37
|
+
return <FallbackComponent error={this.state.error as Error} resetError={this.resetError} />;
|
|
28
38
|
}
|
|
29
39
|
return this.props.children;
|
|
30
40
|
}
|
|
@@ -4,19 +4,28 @@ import * as React from 'react';
|
|
|
4
4
|
import { StyleSheet } from 'react-native';
|
|
5
5
|
|
|
6
6
|
import { SafeAreaView, Text, View } from 'react-native';
|
|
7
|
-
import { useDispatch } from 'react-redux';
|
|
8
7
|
|
|
9
|
-
type Props = {
|
|
8
|
+
type Props = {
|
|
9
|
+
error: Error;
|
|
10
|
+
/** From the boundary that caught the error: mounts the app again. */
|
|
11
|
+
resetError?: () => void;
|
|
12
|
+
};
|
|
10
13
|
|
|
11
14
|
export const FallbackComponent = (props: Props) => {
|
|
12
|
-
const
|
|
15
|
+
const goHome = () => {
|
|
16
|
+
if (props.resetError) {
|
|
17
|
+
props.resetError();
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
navigationRef.navigate('MainStack.Layout.Home');
|
|
21
|
+
};
|
|
13
22
|
return (
|
|
14
23
|
<SafeAreaView style={styles.container}>
|
|
15
24
|
<View style={styles.content}>
|
|
16
25
|
<Text style={styles.title}>Oops!</Text>
|
|
17
26
|
<Text style={styles.subtitle}>{"There's an error"}</Text>
|
|
18
27
|
<Text style={styles.error}>{props?.error?.message}</Text>
|
|
19
|
-
<Button style={styles.button} onPress={
|
|
28
|
+
<Button style={styles.button} onPress={goHome}>
|
|
20
29
|
<ButtonText style={styles.buttonText}>Go to homepage</ButtonText>
|
|
21
30
|
</Button>
|
|
22
31
|
</View>
|
|
@@ -47,14 +56,18 @@ const styles: any = StyleSheet.create({
|
|
|
47
56
|
error: {
|
|
48
57
|
paddingVertical: 16,
|
|
49
58
|
},
|
|
59
|
+
// A fixed height with no vertical padding: `padding: 25` inside
|
|
60
|
+
// `height: 40` left the label -10pt and it never showed.
|
|
50
61
|
button: {
|
|
51
62
|
backgroundColor: '#2196f3',
|
|
52
63
|
borderRadius: 50,
|
|
53
|
-
|
|
54
|
-
|
|
64
|
+
height: 52,
|
|
65
|
+
paddingVertical: 0,
|
|
66
|
+
paddingHorizontal: 24,
|
|
55
67
|
},
|
|
56
68
|
buttonText: {
|
|
57
69
|
color: '#fff',
|
|
70
|
+
fontSize: 17,
|
|
58
71
|
fontWeight: '600',
|
|
59
72
|
textAlign: 'center',
|
|
60
73
|
},
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The app-wide boundary wraps the navigation container. Live 2026-10-10: a
|
|
3
|
+
* screen threw, the fallback showed, and "Go to homepage" did nothing (its
|
|
4
|
+
* label did not even show) - the app was stuck until it was force-quit.
|
|
5
|
+
*/
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { Text } from 'react-native';
|
|
8
|
+
import { fireEvent, render } from '@testing-library/react-native';
|
|
9
|
+
import { ErrorBoundary } from '../ErrorBounday';
|
|
10
|
+
|
|
11
|
+
const mockNavigate = jest.fn();
|
|
12
|
+
jest.mock('@common-stack/client-react', () => ({
|
|
13
|
+
navigationRef: { navigate: (...a: unknown[]) => mockNavigate(...a) },
|
|
14
|
+
}));
|
|
15
|
+
jest.mock('@cdm-logger/client', () => ({ logger: { error: jest.fn(), debug: jest.fn() } }));
|
|
16
|
+
jest.mock('@gluestack-ui/themed', () => {
|
|
17
|
+
const { Pressable: P, Text: T } = jest.requireActual('react-native');
|
|
18
|
+
return { Button: P, ButtonText: T };
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
let crash = true;
|
|
22
|
+
function Screen() {
|
|
23
|
+
if (crash) throw new Error("Property 'promoProgress' doesn't exist");
|
|
24
|
+
return <Text>Home</Text>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
describe('app error boundary', () => {
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
crash = true;
|
|
30
|
+
mockNavigate.mockClear();
|
|
31
|
+
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('shows the error and a labelled way home', () => {
|
|
35
|
+
const { getByText } = render(
|
|
36
|
+
<ErrorBoundary>
|
|
37
|
+
<Screen />
|
|
38
|
+
</ErrorBoundary>,
|
|
39
|
+
);
|
|
40
|
+
expect(getByText("Property 'promoProgress' doesn't exist")).toBeTruthy();
|
|
41
|
+
expect(getByText('Go to homepage')).toBeTruthy();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('"Go to homepage" mounts the app again instead of navigating a navigator that is gone', () => {
|
|
45
|
+
const { getByText, queryByText } = render(
|
|
46
|
+
<ErrorBoundary>
|
|
47
|
+
<Screen />
|
|
48
|
+
</ErrorBoundary>,
|
|
49
|
+
);
|
|
50
|
+
crash = false;
|
|
51
|
+
fireEvent.press(getByText('Go to homepage'));
|
|
52
|
+
expect(getByText('Home')).toBeTruthy();
|
|
53
|
+
expect(queryByText("There's an error")).toBeNull();
|
|
54
|
+
expect(mockNavigate).not.toHaveBeenCalled();
|
|
55
|
+
});
|
|
56
|
+
});
|
package/babel.config.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
compact: false,
|
|
3
|
-
presets: [
|
|
4
|
-
"module:metro-react-native-babel-preset",
|
|
5
|
-
'@babel/preset-typescript',
|
|
6
|
-
'@babel/preset-react',
|
|
7
|
-
['@babel/preset-env', { modules: 'commonjs', loose: true }],
|
|
8
|
-
],
|
|
9
|
-
plugins: [
|
|
10
|
-
'@babel/plugin-transform-modules-commonjs',
|
|
11
|
-
'@babel/plugin-transform-destructuring',
|
|
12
|
-
'@babel/plugin-transform-for-of',
|
|
13
|
-
'@babel/plugin-transform-regenerator',
|
|
14
|
-
'@babel/plugin-transform-runtime',
|
|
15
|
-
'@babel/plugin-syntax-dynamic-import',
|
|
16
|
-
'@babel/plugin-transform-flow-strip-types',
|
|
17
|
-
'@babel/plugin-proposal-class-properties',
|
|
18
|
-
['@babel/plugin-proposal-decorators', { legacy: true }],
|
|
19
|
-
'@babel/plugin-proposal-object-rest-spread',
|
|
20
|
-
],
|
|
21
|
-
env: {
|
|
22
|
-
production: {
|
|
23
|
-
compact: true,
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
};
|