@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 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
@@ -1,6 +1,6 @@
1
1
  import { Props } from '.';
2
2
  type IOSInitialProps = Props & {
3
- isVisible: boolean;
3
+ isVisible?: boolean;
4
4
  };
5
5
  export declare function App({ children, ...props }: IOSInitialProps): import("react/jsx-runtime").JSX.Element;
6
6
  export {};
@@ -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: InitialProps) => JSX.Element;
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": "1.0.42",
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": "1.0.42",
95
- "@granite-js/vitest": "1.0.42",
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": "1.0.42",
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": "1.0.42",
125
- "@granite-js/brownfield-module": "1.0.42",
126
- "@granite-js/cli": "1.0.42",
127
- "@granite-js/jest": "1.0.42",
128
- "@granite-js/mpack": "1.0.42",
129
- "@granite-js/plugin-core": "1.0.42",
130
- "@granite-js/style-utils": "1.0.42",
131
- "@granite-js/video": "1.0.42",
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: boolean;
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
+ });
@@ -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: InitialProps) => JSX.Element {
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: InitialProps) {
90
+ function Root({ presentationVisibility = true, ...initialProps }: RegisteredAppProps) {
84
91
  const initialSchemeValue = (typeof initialScheme === 'function' ? initialScheme() : initialScheme) ?? getSchemeUri();
85
92
 
86
93
  return (
87
- <AppRoot
88
- container={AppContainer}
89
- initialProps={initialProps}
90
- initialScheme={initialSchemeValue}
91
- setIosSwipeGestureEnabled={setIosSwipeGestureEnabled}
92
- setiOSBackPressHandler={setiOSBackPressHandler}
93
- getInitialUrl={getInitialUrl}
94
- appName={appName}
95
- context={context}
96
- router={router}
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
- return <VisibilityChangedContext.Provider value={isVisible}>{children}</VisibilityChangedContext.Provider>;
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
  /**