@granite-js/react-native 1.0.42 → 2.0.1
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 +34 -0
- package/dist/app/App/index.ios.d.ts +1 -1
- package/dist/app/Granite.d.ts +6 -1
- package/package.json +12 -12
- package/src/app/App/index.ios.tsx +2 -2
- package/src/app/Granite.spec.tsx +75 -0
- package/src/app/Granite.tsx +22 -13
- package/src/visibility/useVisibilityChanged.spec.tsx +49 -0
- package/src/visibility/useVisibilityChanged.tsx +4 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# @granite-js/react-native
|
|
2
2
|
|
|
3
|
+
## 2.0.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- @granite-js/blur-view@2.0.1
|
|
8
|
+
- @granite-js/brownfield-module@2.0.1
|
|
9
|
+
- @granite-js/cli@2.0.1
|
|
10
|
+
- @granite-js/jest@2.0.1
|
|
11
|
+
- @granite-js/mpack@2.0.1
|
|
12
|
+
- @granite-js/native@2.0.1
|
|
13
|
+
- @granite-js/plugin-core@2.0.1
|
|
14
|
+
- @granite-js/style-utils@2.0.1
|
|
15
|
+
- @granite-js/video@2.0.1
|
|
16
|
+
|
|
17
|
+
## 2.0.0
|
|
18
|
+
|
|
19
|
+
### Major Changes
|
|
20
|
+
|
|
21
|
+
- 864fa2a: Start the Granite v2 release line.
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- 864fa2a: Route session visibility through Granite's existing visibility provider and expose native session lifecycle as React state so remote apps can continue using `useVisibility()`.
|
|
26
|
+
- Updated dependencies [864fa2a]
|
|
27
|
+
- @granite-js/mpack@2.0.0
|
|
28
|
+
- @granite-js/cli@2.0.0
|
|
29
|
+
- @granite-js/blur-view@2.0.0
|
|
30
|
+
- @granite-js/brownfield-module@2.0.0
|
|
31
|
+
- @granite-js/jest@2.0.0
|
|
32
|
+
- @granite-js/native@2.0.0
|
|
33
|
+
- @granite-js/plugin-core@2.0.0
|
|
34
|
+
- @granite-js/style-utils@2.0.0
|
|
35
|
+
- @granite-js/video@2.0.0
|
|
36
|
+
|
|
3
37
|
## 1.0.42
|
|
4
38
|
|
|
5
39
|
### Patch Changes
|
package/dist/app/Granite.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { ComponentType, type JSX, PropsWithChildren } from 'react';
|
|
2
2
|
import type { InitialProps } from '../initial-props';
|
|
3
3
|
import type { RouterProps, RequireContext, SetIOSBackPressHandler } from '../router';
|
|
4
|
+
interface GraniteAppRuntimeProps {
|
|
5
|
+
readonly presentationVisibility?: boolean;
|
|
6
|
+
}
|
|
7
|
+
type RegisteredAppProps = InitialProps & GraniteAppRuntimeProps;
|
|
4
8
|
export interface GraniteProps {
|
|
5
9
|
/**
|
|
6
10
|
* @description
|
|
@@ -82,7 +86,8 @@ export interface GraniteProps {
|
|
|
82
86
|
* ```
|
|
83
87
|
*/
|
|
84
88
|
export declare const Granite: {
|
|
85
|
-
registerApp(AppContainer: ComponentType<PropsWithChildren<InitialProps>>, { appName, context, router, initialScheme, setIosSwipeGestureEnabled, setiOSBackPressHandler, getInitialUrl }: GraniteProps): (initialProps:
|
|
89
|
+
registerApp(AppContainer: ComponentType<PropsWithChildren<InitialProps>>, { appName, context, router, initialScheme, setIosSwipeGestureEnabled, setiOSBackPressHandler, getInitialUrl }: GraniteProps): (initialProps: RegisteredAppProps) => JSX.Element;
|
|
86
90
|
registerHostApp(AppContainer: ComponentType<PropsWithChildren<InitialProps>>, { appName }: Pick<GraniteProps, "appName">): (initialProps: InitialProps) => React.JSX.Element;
|
|
87
91
|
readonly appName: string;
|
|
88
92
|
};
|
|
93
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@granite-js/react-native",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "The Granite Framework",
|
|
5
5
|
"bin": {
|
|
6
6
|
"granite": "./bin/cli.js"
|
|
@@ -91,8 +91,8 @@
|
|
|
91
91
|
"@babel/core": "7.28.5",
|
|
92
92
|
"@babel/preset-env": "7.28.5",
|
|
93
93
|
"@babel/preset-typescript": "7.28.5",
|
|
94
|
-
"@granite-js/native": "
|
|
95
|
-
"@granite-js/vitest": "
|
|
94
|
+
"@granite-js/native": "2.0.1",
|
|
95
|
+
"@granite-js/vitest": "2.0.1",
|
|
96
96
|
"@testing-library/dom": "^10.4.0",
|
|
97
97
|
"@testing-library/react": "^16.1.0",
|
|
98
98
|
"@types/babel__core": "^7",
|
|
@@ -114,21 +114,21 @@
|
|
|
114
114
|
"zod": "^4.1.12"
|
|
115
115
|
},
|
|
116
116
|
"peerDependencies": {
|
|
117
|
-
"@granite-js/native": "
|
|
117
|
+
"@granite-js/native": "2.0.1",
|
|
118
118
|
"@types/react": "*",
|
|
119
119
|
"brick-module": "*",
|
|
120
120
|
"react": "*",
|
|
121
121
|
"react-native": "*"
|
|
122
122
|
},
|
|
123
123
|
"dependencies": {
|
|
124
|
-
"@granite-js/blur-view": "
|
|
125
|
-
"@granite-js/brownfield-module": "
|
|
126
|
-
"@granite-js/cli": "
|
|
127
|
-
"@granite-js/jest": "
|
|
128
|
-
"@granite-js/mpack": "
|
|
129
|
-
"@granite-js/plugin-core": "
|
|
130
|
-
"@granite-js/style-utils": "
|
|
131
|
-
"@granite-js/video": "
|
|
124
|
+
"@granite-js/blur-view": "2.0.1",
|
|
125
|
+
"@granite-js/brownfield-module": "2.0.1",
|
|
126
|
+
"@granite-js/cli": "2.0.1",
|
|
127
|
+
"@granite-js/jest": "2.0.1",
|
|
128
|
+
"@granite-js/mpack": "2.0.1",
|
|
129
|
+
"@granite-js/plugin-core": "2.0.1",
|
|
130
|
+
"@granite-js/style-utils": "2.0.1",
|
|
131
|
+
"@granite-js/video": "2.0.1",
|
|
132
132
|
"@standard-schema/spec": "^1.0.0",
|
|
133
133
|
"es-toolkit": "^1.39.8",
|
|
134
134
|
"react-native-url-polyfill": "3.0.0"
|
|
@@ -3,11 +3,11 @@ import { VisibilityProvider } from '../../visibility';
|
|
|
3
3
|
import { Props } from '.';
|
|
4
4
|
|
|
5
5
|
type IOSInitialProps = Props & {
|
|
6
|
-
isVisible
|
|
6
|
+
isVisible?: boolean;
|
|
7
7
|
};
|
|
8
8
|
|
|
9
9
|
export function App({ children, ...props }: IOSInitialProps) {
|
|
10
|
-
const { isVisible } = useMemo(() => props, [props]);
|
|
10
|
+
const { isVisible = true } = useMemo(() => props, [props]);
|
|
11
11
|
|
|
12
12
|
return <VisibilityProvider isVisible={isVisible}>{children}</VisibilityProvider>;
|
|
13
13
|
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { render } from '@testing-library/react';
|
|
2
|
+
import type { PropsWithChildren } from 'react';
|
|
3
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
4
|
+
import type { InitialProps } from '../initial-props';
|
|
5
|
+
import type { RequireContext } from '../router';
|
|
6
|
+
import { Granite } from './Granite';
|
|
7
|
+
|
|
8
|
+
const visibilityState = vi.hoisted<{ value: boolean | null }>(() => ({ value: null }));
|
|
9
|
+
|
|
10
|
+
vi.mock('./AppRoot', async () => {
|
|
11
|
+
const { VisibilityChangedProvider, useVisibilityChanged } = await import('../visibility/useVisibilityChanged');
|
|
12
|
+
|
|
13
|
+
function Consumer() {
|
|
14
|
+
visibilityState.value = useVisibilityChanged();
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
AppRoot: () => (
|
|
20
|
+
<VisibilityChangedProvider isVisible={true}>
|
|
21
|
+
<Consumer />
|
|
22
|
+
</VisibilityChangedProvider>
|
|
23
|
+
),
|
|
24
|
+
};
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
vi.mock('./HostAppRoot', () => ({
|
|
28
|
+
HostAppRoot: () => null,
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
vi.mock('@granite-js/brownfield-module', () => ({
|
|
32
|
+
GraniteBrownfieldModule: {
|
|
33
|
+
onVisibilityChanged: () => ({ remove: () => undefined }),
|
|
34
|
+
},
|
|
35
|
+
}));
|
|
36
|
+
|
|
37
|
+
vi.mock('../constant-bridges', () => ({
|
|
38
|
+
getSchemeUri: () => 'granite://visibility-runtime-test',
|
|
39
|
+
}));
|
|
40
|
+
|
|
41
|
+
vi.mock('../polyfills', () => ({
|
|
42
|
+
setupPolyfills: () => undefined,
|
|
43
|
+
}));
|
|
44
|
+
|
|
45
|
+
const context: RequireContext = Object.assign(
|
|
46
|
+
<T,>(id: string): T => {
|
|
47
|
+
throw new Error(`The route context is not used by this test: ${id}`);
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: 'visibility-runtime-test',
|
|
51
|
+
keys: () => [],
|
|
52
|
+
resolve: (id: string) => id,
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
describe('Granite.registerApp', () => {
|
|
57
|
+
it('installs presentation visibility inside the registered remote app root', () => {
|
|
58
|
+
// Given
|
|
59
|
+
function AppContainer({ children }: PropsWithChildren<InitialProps>) {
|
|
60
|
+
return children;
|
|
61
|
+
}
|
|
62
|
+
const Root = Granite.registerApp(AppContainer, {
|
|
63
|
+
appName: 'visibility-runtime-test',
|
|
64
|
+
context,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// When
|
|
68
|
+
render(
|
|
69
|
+
<Root platform="android" initialColorPreference="light" presentationVisibility={false} />
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
// Then
|
|
73
|
+
expect(visibilityState.value).toBe(false);
|
|
74
|
+
});
|
|
75
|
+
});
|
package/src/app/Granite.tsx
CHANGED
|
@@ -7,6 +7,13 @@ import { AppRoot } from './AppRoot';
|
|
|
7
7
|
import { HostAppRoot } from './HostAppRoot';
|
|
8
8
|
import { getSchemeUri } from '../constant-bridges';
|
|
9
9
|
import { setupPolyfills } from '../polyfills';
|
|
10
|
+
import { VisibilityChangedProvider } from '../visibility/useVisibilityChanged';
|
|
11
|
+
|
|
12
|
+
interface GraniteAppRuntimeProps {
|
|
13
|
+
readonly presentationVisibility?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
type RegisteredAppProps = InitialProps & GraniteAppRuntimeProps;
|
|
10
17
|
|
|
11
18
|
export interface GraniteProps {
|
|
12
19
|
/**
|
|
@@ -75,26 +82,28 @@ const createApp = () => {
|
|
|
75
82
|
registerApp(
|
|
76
83
|
AppContainer: ComponentType<PropsWithChildren<InitialProps>>,
|
|
77
84
|
{ appName, context, router, initialScheme, setIosSwipeGestureEnabled, setiOSBackPressHandler, getInitialUrl }: GraniteProps
|
|
78
|
-
): (initialProps:
|
|
85
|
+
): (initialProps: RegisteredAppProps) => JSX.Element {
|
|
79
86
|
if (appName === ENTRY_BUNDLE_NAME) {
|
|
80
87
|
throw new Error(`Reserved app name 'shared' cannot be used`);
|
|
81
88
|
}
|
|
82
89
|
|
|
83
|
-
function Root(initialProps:
|
|
90
|
+
function Root({ presentationVisibility = true, ...initialProps }: RegisteredAppProps) {
|
|
84
91
|
const initialSchemeValue = (typeof initialScheme === 'function' ? initialScheme() : initialScheme) ?? getSchemeUri();
|
|
85
92
|
|
|
86
93
|
return (
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
94
|
+
<VisibilityChangedProvider isVisible={presentationVisibility}>
|
|
95
|
+
<AppRoot
|
|
96
|
+
container={AppContainer}
|
|
97
|
+
initialProps={initialProps}
|
|
98
|
+
initialScheme={initialSchemeValue}
|
|
99
|
+
setIosSwipeGestureEnabled={setIosSwipeGestureEnabled}
|
|
100
|
+
setiOSBackPressHandler={setiOSBackPressHandler}
|
|
101
|
+
getInitialUrl={getInitialUrl}
|
|
102
|
+
appName={appName}
|
|
103
|
+
context={context}
|
|
104
|
+
router={router}
|
|
105
|
+
/>
|
|
106
|
+
</VisibilityChangedProvider>
|
|
98
107
|
);
|
|
99
108
|
}
|
|
100
109
|
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { render } from '@testing-library/react';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { VisibilityChangedProvider, useVisibilityChanged } from './useVisibilityChanged';
|
|
4
|
+
|
|
5
|
+
describe('VisibilityChangedProvider', () => {
|
|
6
|
+
it('keeps a child visible when every visibility boundary is visible', () => {
|
|
7
|
+
// Given
|
|
8
|
+
let receivedVisibility: boolean | null = null;
|
|
9
|
+
|
|
10
|
+
function Consumer() {
|
|
11
|
+
receivedVisibility = useVisibilityChanged();
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// When
|
|
16
|
+
render(
|
|
17
|
+
<VisibilityChangedProvider isVisible={true}>
|
|
18
|
+
<VisibilityChangedProvider isVisible={true}>
|
|
19
|
+
<Consumer />
|
|
20
|
+
</VisibilityChangedProvider>
|
|
21
|
+
</VisibilityChangedProvider>
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
// Then
|
|
25
|
+
expect(receivedVisibility).toBe(true);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('keeps a child hidden when an outer visibility boundary is hidden', () => {
|
|
29
|
+
// Given
|
|
30
|
+
let receivedVisibility: boolean | null = null;
|
|
31
|
+
|
|
32
|
+
function Consumer() {
|
|
33
|
+
receivedVisibility = useVisibilityChanged();
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// When
|
|
38
|
+
render(
|
|
39
|
+
<VisibilityChangedProvider isVisible={false}>
|
|
40
|
+
<VisibilityChangedProvider isVisible={true}>
|
|
41
|
+
<Consumer />
|
|
42
|
+
</VisibilityChangedProvider>
|
|
43
|
+
</VisibilityChangedProvider>
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
// Then
|
|
47
|
+
expect(receivedVisibility).toBe(false);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -26,7 +26,10 @@ export function VisibilityChangedProvider({
|
|
|
26
26
|
children,
|
|
27
27
|
isVisible,
|
|
28
28
|
}: PropsWithChildren<{ isVisible: boolean }>): ReactElement {
|
|
29
|
-
|
|
29
|
+
const parentVisibility = useContext(VisibilityChangedContext);
|
|
30
|
+
const visibility = (parentVisibility ?? true) && isVisible;
|
|
31
|
+
|
|
32
|
+
return <VisibilityChangedContext.Provider value={visibility}>{children}</VisibilityChangedContext.Provider>;
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
/**
|