@applicaster/zapp-react-native-utils 13.0.0-rc.99 → 14.0.0-alpha.1235043154

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 (77) hide show
  1. package/actionsExecutor/ActionExecutorContext.tsx +55 -6
  2. package/actionsExecutor/consts.ts +4 -0
  3. package/analyticsUtils/AnalyticsEvents/sendHeaderClickEvent.ts +1 -1
  4. package/analyticsUtils/AnalyticsEvents/sendMenuClickEvent.ts +2 -1
  5. package/analyticsUtils/index.tsx +3 -4
  6. package/analyticsUtils/manager.ts +1 -1
  7. package/appUtils/__tests__/__snapshots__/localizationsHelper.test.ts.snap +151 -0
  8. package/appUtils/__tests__/allZappLocales.ts +79 -0
  9. package/appUtils/__tests__/{localizationsHelper.test.js → localizationsHelper.test.ts} +11 -0
  10. package/appUtils/accessibilityManager/const.ts +18 -0
  11. package/appUtils/accessibilityManager/index.ts +4 -1
  12. package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +1 -0
  13. package/appUtils/focusManager/index.ios.ts +14 -4
  14. package/appUtils/focusManager/utils/__tests__/findChild.test.ts +35 -0
  15. package/appUtils/focusManager/utils/index.ts +5 -0
  16. package/appUtils/localizationsHelper.ts +10 -2
  17. package/appUtils/playerManager/playerHooks/usePlayerCurrentTime.tsx +11 -7
  18. package/arrayUtils/__tests__/isEmptyArray.test.ts +63 -0
  19. package/arrayUtils/__tests__/isFilledArray.test.ts +1 -1
  20. package/arrayUtils/index.ts +7 -2
  21. package/audioPlayerUtils/__tests__/getArtworkImage.test.ts +144 -0
  22. package/audioPlayerUtils/__tests__/getBackgroundImage.test.ts +72 -0
  23. package/audioPlayerUtils/__tests__/getImageFromEntry.test.ts +110 -0
  24. package/audioPlayerUtils/assets/index.ts +2 -0
  25. package/audioPlayerUtils/index.ts +242 -0
  26. package/cellUtils/index.ts +9 -5
  27. package/componentsUtils/index.ts +8 -1
  28. package/conf/player/__tests__/selectors.test.ts +34 -0
  29. package/conf/player/selectors.ts +10 -0
  30. package/configurationUtils/__tests__/configurationUtils.test.js +0 -31
  31. package/configurationUtils/__tests__/getMediaItems.test.ts +65 -0
  32. package/configurationUtils/__tests__/imageSrcFromMediaItem.test.ts +34 -0
  33. package/configurationUtils/index.ts +63 -34
  34. package/localizationUtils/index.ts +3 -3
  35. package/manifestUtils/_internals/getDefaultConfiguration.js +28 -0
  36. package/manifestUtils/{_internals.js → _internals/index.js} +2 -25
  37. package/manifestUtils/createConfig.js +4 -1
  38. package/manifestUtils/defaultManifestConfigurations/generalContent.js +13 -0
  39. package/manifestUtils/defaultManifestConfigurations/player.js +1228 -205
  40. package/manifestUtils/index.js +2 -0
  41. package/manifestUtils/keys.js +27 -2
  42. package/manifestUtils/progressBar/__tests__/mobileProgressBar.test.js +0 -30
  43. package/manifestUtils/sharedConfiguration/screenPicker/stylesFields.js +1 -2
  44. package/navigationUtils/__tests__/navigationUtils.test.js +0 -65
  45. package/navigationUtils/index.ts +0 -31
  46. package/package.json +2 -2
  47. package/playerUtils/__tests__/configurationUtils.test.ts +1 -65
  48. package/playerUtils/__tests__/getPlayerActionButtons.test.ts +54 -0
  49. package/playerUtils/_internals/__tests__/utils.test.ts +71 -0
  50. package/playerUtils/_internals/index.ts +1 -0
  51. package/playerUtils/_internals/utils.ts +31 -0
  52. package/playerUtils/configurationUtils.ts +0 -44
  53. package/playerUtils/getPlayerActionButtons.ts +17 -0
  54. package/playerUtils/index.ts +25 -0
  55. package/playerUtils/useValidatePlayerConfig.tsx +22 -19
  56. package/reactHooks/app/useAppState.ts +2 -2
  57. package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +12 -13
  58. package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +39 -88
  59. package/reactHooks/feed/useBatchLoading.ts +12 -14
  60. package/reactHooks/navigation/{useGetTabBarHeight.ts → getTabBarHeight.ts} +1 -1
  61. package/reactHooks/navigation/index.ts +2 -2
  62. package/reactHooks/navigation/useGetBottomTabBarHeight.ts +10 -3
  63. package/reactHooks/navigation/useNavigationPluginData.ts +8 -4
  64. package/reactHooks/navigation/useNavigationType.ts +4 -2
  65. package/reactHooks/screen/__tests__/useScreenBackgroundColor.test.tsx +69 -0
  66. package/reactHooks/screen/useScreenBackgroundColor.ts +3 -15
  67. package/reactHooks/state/README.md +79 -0
  68. package/reactHooks/state/ZStoreProvider.tsx +71 -0
  69. package/reactHooks/state/__tests__/ZStoreProvider.test.tsx +66 -0
  70. package/reactHooks/state/index.ts +2 -0
  71. package/reactHooks/useListenEventBusEvent.ts +1 -1
  72. package/reactUtils/index.ts +9 -0
  73. package/testUtils/index.tsx +7 -8
  74. package/typeGuards/index.ts +3 -0
  75. package/utils/index.ts +12 -1
  76. package/zappFrameworkUtils/localStorageHelper.ts +32 -10
  77. package/playerUtils/configurationGenerator.ts +0 -2588
@@ -1,25 +1,27 @@
1
1
  import React from "react";
2
2
 
3
- import { act, renderHook } from "@testing-library/react-hooks";
3
+ import { renderHook } from "@testing-library/react-hooks";
4
+ import { act, waitFor } from "@testing-library/react-native";
4
5
  import { Provider } from "react-redux";
5
6
  import configureStore from "redux-mock-store";
7
+ import { useTrackedView } from "../useTrackedView";
6
8
 
7
9
  const mockUpdateComponentsPositions = jest.fn();
8
10
 
9
11
  jest.mock(
10
12
  "@applicaster/zapp-react-native-ui-components/Contexts/ScreenTrackedViewPositionsContext",
11
13
  () => ({
12
- useScreenTrackedViewPositionsContext: jest.fn().mockReturnValue({
14
+ useScreenTrackedViewPositionsContext: jest.fn(() => ({
13
15
  updateComponentsPositions: mockUpdateComponentsPositions,
14
16
  value: {
15
17
  "123": { componentId: "123", centerX: 0.4, centerY: 0.5 },
16
18
  "124": { componentId: "124", centerX: 0.2, centerY: 0.3 },
17
19
  },
18
- }),
20
+ })),
19
21
  })
20
22
  );
21
23
 
22
- jest.useFakeTimers({ legacyFakeTimers: true });
24
+ jest.useFakeTimers();
23
25
 
24
26
  jest.mock("@applicaster/zapp-react-native-utils/reactHooks/navigation");
25
27
 
@@ -32,10 +34,8 @@ const Wrapper = ({ children }: { children: React.ReactChild }) => (
32
34
  <Provider store={store}>{children}</Provider>
33
35
  );
34
36
 
35
- const { useTrackedView } = require("../useTrackedView");
36
-
37
37
  describe("useTrackCurrentAutoScrollingElement", () => {
38
- it("should update position for selected component - onViewportEnter", () => {
38
+ it("should update position for selected component - onViewportEnter", async () => {
39
39
  const { result } = renderHook(() => useTrackedView("123"), {
40
40
  wrapper: Wrapper,
41
41
  });
@@ -46,14 +46,13 @@ describe("useTrackCurrentAutoScrollingElement", () => {
46
46
  rect: { left: 1, right: 1, top: 1, bottom: 1 },
47
47
  };
48
48
 
49
- act(async () => {
50
- await result.current.onPositionUpdated(mockRect);
49
+ act(() => {
50
+ result.current.onPositionUpdated(mockRect);
51
51
  });
52
52
 
53
- // Fast-forward until all timers have been executed
54
- jest.runAllTimers();
55
-
56
- expect(result.current.inViewPort).toBe(true);
53
+ await waitFor(() => {
54
+ expect(result.current.inViewPort).toBe(true);
55
+ });
57
56
 
58
57
  expect(mockUpdateComponentsPositions).toHaveBeenCalledWith(
59
58
  "123",
@@ -1,8 +1,7 @@
1
1
  import { renderHook } from "@testing-library/react-hooks";
2
- import * as reduxMockStore from "redux-mock-store";
3
- import thunk from "redux-thunk";
4
- import React from "react";
5
- import * as ReactRedux from "react-redux";
2
+ import { allFeedsIsReady, useBatchLoading } from "../useBatchLoading";
3
+ import { WrappedWithProviders } from "@applicaster/zapp-react-native-utils/testUtils";
4
+ import { appStore } from "@applicaster/zapp-react-native-redux/AppStore";
6
5
 
7
6
  jest.mock("../../navigation");
8
7
 
@@ -13,17 +12,18 @@ jest.mock(
13
12
  })
14
13
  );
15
14
 
16
- const useBatchLoading = require("../useBatchLoading").useBatchLoading;
17
- const allFeedsIsReady = require("../useBatchLoading").allFeedsIsReady;
18
-
19
- const mockStore = reduxMockStore.default([thunk]);
20
-
21
- const wrapper: React.FC<any> = ({ children, store }) => (
22
- <ReactRedux.Provider store={store}>{children}</ReactRedux.Provider>
23
- );
15
+ const wrapper = WrappedWithProviders;
24
16
 
25
17
  describe("useBatchLoading", () => {
26
- const useDispatchSpy = jest.spyOn(ReactRedux, "useDispatch");
18
+ const data = [
19
+ { data: { source: "url1" }, component_type: "any" },
20
+ { data: { source: "url2" }, component_type: "any" },
21
+ { data: { source: "url3" }, component_type: "any" },
22
+ { data: { source: "url4" }, component_type: "any" },
23
+ { data: { source: "url5" }, component_type: "any" },
24
+ { data: { source: "url6" }, component_type: "any" },
25
+ // ... more items
26
+ ];
27
27
 
28
28
  beforeAll(() => {
29
29
  jest.useFakeTimers();
@@ -34,7 +34,7 @@ describe("useBatchLoading", () => {
34
34
  });
35
35
 
36
36
  it("loadPipesData start loading not started requests", () => {
37
- const store = mockStore({
37
+ const store = {
38
38
  zappPipes: {
39
39
  url1: {
40
40
  loading: true,
@@ -53,29 +53,17 @@ describe("useBatchLoading", () => {
53
53
  },
54
54
  },
55
55
  test: "true",
56
- });
57
-
58
- useDispatchSpy.mockReturnValue(store.dispatch);
56
+ };
59
57
 
60
58
  const initialBatchSize = 3;
61
59
  const riverId = "123";
62
60
 
63
- const data = [
64
- { data: { source: "url1" } },
65
- { data: { source: "url2" } },
66
- { data: { source: "url3" } },
67
- { data: { source: "url4" } },
68
- { data: { source: "url5" } },
69
- { data: { source: "url6" } },
70
- // ... more items
71
- ];
72
-
73
61
  renderHook(() => useBatchLoading(data, { initialBatchSize, riverId }), {
74
62
  wrapper,
75
63
  initialProps: { store },
76
64
  });
77
65
 
78
- const actions = store.getActions();
66
+ const actions = (appStore.getStore() as any).getActions();
79
67
 
80
68
  expect(actions).toHaveLength(2);
81
69
 
@@ -91,7 +79,7 @@ describe("useBatchLoading", () => {
91
79
  });
92
80
 
93
81
  it("loadPipesData start loading new feed when 1 feed is done loading and 1 is in loading state", () => {
94
- const store = mockStore({
82
+ const store = {
95
83
  zappPipes: {
96
84
  url1: {
97
85
  loading: false,
@@ -110,31 +98,17 @@ describe("useBatchLoading", () => {
110
98
  },
111
99
  },
112
100
  test: "true",
113
- });
114
-
115
- useDispatchSpy.mockReturnValue(store.dispatch);
101
+ };
116
102
 
117
103
  const initialBatchSize = 3;
118
104
  const riverId = "123";
119
105
 
120
- const data = [
121
- { data: { source: "url1" } },
122
- { data: { source: "url2" } },
123
- { data: { source: "url3" } },
124
- { data: { source: "url4" } },
125
- { data: { source: "url5" } },
126
- { data: { source: "url6" } },
127
- // ... more items
128
- ];
129
-
130
- expect(useDispatchSpy).toBeCalledTimes(0);
131
-
132
106
  renderHook(() => useBatchLoading(data, { initialBatchSize, riverId }), {
133
107
  wrapper,
134
108
  initialProps: { store },
135
109
  });
136
110
 
137
- const actions = store.getActions();
111
+ const actions = (appStore.getStore() as any).getActions();
138
112
 
139
113
  expect(actions).toHaveLength(1);
140
114
 
@@ -145,38 +119,26 @@ describe("useBatchLoading", () => {
145
119
  });
146
120
 
147
121
  it("loadPipesData has been called when no data cached", () => {
148
- const store = mockStore({
122
+ const store = {
149
123
  zappPipes: {},
150
124
  test: "true",
151
- });
152
-
153
- useDispatchSpy.mockReturnValue(store.dispatch);
125
+ };
154
126
 
155
127
  const initialBatchSize = 3;
156
128
  const riverId = "123";
157
129
 
158
- const data = [
159
- { data: { source: "url1" } },
160
- { data: { source: "url2" } },
161
- { data: { source: "url3" } },
162
- { data: { source: "url4" } },
163
- { data: { source: "url5" } },
164
- { data: { source: "url6" } },
165
- // ... more items
166
- ];
167
-
168
130
  renderHook(() => useBatchLoading(data, { initialBatchSize, riverId }), {
169
131
  wrapper,
170
132
  initialProps: { store },
171
133
  });
172
134
 
173
- const actions = store.getActions();
135
+ const actions = (appStore.getStore() as any).getActions();
174
136
 
175
137
  expect(actions).toHaveLength(3);
176
138
  });
177
139
 
178
140
  it("initial batch ready when all initial items loaded", () => {
179
- const store = mockStore({
141
+ const store = {
180
142
  zappPipes: {
181
143
  url1: {
182
144
  loading: false,
@@ -194,19 +156,11 @@ describe("useBatchLoading", () => {
194
156
  data: {},
195
157
  },
196
158
  },
197
- });
198
-
199
- useDispatchSpy.mockReturnValue(store.dispatch);
159
+ };
200
160
 
201
161
  const initialBatchSize = 3;
202
162
  const riverId = "123";
203
163
 
204
- const data: Partial<ZappUIComponent>[] = [
205
- { data: { source: "url1" } },
206
- { data: { source: "url2" } },
207
- { data: { source: "url3" } },
208
- ];
209
-
210
164
  const { result } = renderHook(
211
165
  () => useBatchLoading(data, { initialBatchSize, riverId }),
212
166
  { wrapper, initialProps: { store } }
@@ -216,12 +170,10 @@ describe("useBatchLoading", () => {
216
170
  });
217
171
 
218
172
  it("gallery-qb: loadPipesData should be called only once for first component in the gallery", () => {
219
- const store = mockStore({
173
+ const store = {
220
174
  zappPipes: {},
221
175
  test: "true",
222
- });
223
-
224
- useDispatchSpy.mockReturnValue(store.dispatch);
176
+ };
225
177
 
226
178
  const initialBatchSize = 3;
227
179
  const riverId = "123";
@@ -231,11 +183,11 @@ describe("useBatchLoading", () => {
231
183
  component_type: "gallery-qb",
232
184
  ui_components: [{ data: { source: "url1" } }],
233
185
  },
234
- { data: { source: "url2" } },
235
- { data: { source: "url3" } },
236
- { data: { source: "url4" } },
237
- { data: { source: "url5" } },
238
- { data: { source: "url6" } },
186
+ { data: { source: "url2" }, component_type: "any" },
187
+ { data: { source: "url3" }, component_type: "any" },
188
+ { data: { source: "url4" }, component_type: "any" },
189
+ { data: { source: "url5" }, component_type: "any" },
190
+ { data: { source: "url6" }, component_type: "any" },
239
191
  // ... more items
240
192
  ];
241
193
 
@@ -244,13 +196,13 @@ describe("useBatchLoading", () => {
244
196
  initialProps: { store },
245
197
  });
246
198
 
247
- const actions = store.getActions();
199
+ const actions = (appStore.getStore() as any).getActions();
248
200
 
249
201
  expect(actions).toHaveLength(1);
250
202
  });
251
203
 
252
204
  it("gallery-qb: initial batch ready when all initial items loaded", () => {
253
- const store = mockStore({
205
+ const store = {
254
206
  zappPipes: {
255
207
  url1: {
256
208
  loading: false,
@@ -258,20 +210,19 @@ describe("useBatchLoading", () => {
258
210
  data: {},
259
211
  },
260
212
  },
261
- });
262
-
263
- useDispatchSpy.mockReturnValue(store.dispatch);
213
+ };
264
214
 
265
215
  const initialBatchSize = 3;
266
216
  const riverId = "123";
267
217
 
268
- const data: Partial<ZappUIComponent>[] = [
218
+ const data = [
269
219
  {
270
220
  component_type: "gallery-qb",
271
- ui_components: [{ data: { source: "url1" } }],
221
+ data: {},
222
+ ui_components: [{ data: { source: "url1" } }] as any,
272
223
  },
273
- { data: { source: "url2" } },
274
- { data: { source: "url3" } },
224
+ { data: { source: "url2" }, component_type: "any" },
225
+ { data: { source: "url3" }, component_type: "any" },
275
226
  ];
276
227
 
277
228
  const { result } = renderHook(
@@ -1,7 +1,7 @@
1
- import { isNil, complement, compose, map, min, prop, take, uniq } from "ramda";
1
+ import { complement, compose, isNil, map, min, prop, take, uniq } from "ramda";
2
2
  import { useDispatch } from "react-redux";
3
3
  import * as React from "react";
4
- import { useZappPipesFeeds } from "@applicaster/zapp-react-native-redux/hooks/useZappPipesFeeds";
4
+ import { useZappPipesFeeds } from "@applicaster/zapp-react-native-redux/hooks";
5
5
  import { loadPipesData } from "@applicaster/zapp-react-native-redux/ZappPipes";
6
6
  import { isNilOrEmpty } from "../../reactUtils/helpers";
7
7
  import { ZappPipesSearchContext } from "@applicaster/zapp-react-native-ui-components/Contexts";
@@ -9,6 +9,7 @@ import {
9
9
  getInflatedDataSourceUrl,
10
10
  getSearchContext,
11
11
  } from "@applicaster/zapp-react-native-utils/reactHooks";
12
+ import { isGallery } from "@applicaster/zapp-react-native-utils/componentsUtils";
12
13
  import { useScreenContext } from "../screen/useScreenContext";
13
14
 
14
15
  type Options = {
@@ -46,9 +47,7 @@ const filterEmptyData = (data) => {
46
47
  };
47
48
 
48
49
  const getData = (rawData) =>
49
- rawData.component_type === "gallery-qb"
50
- ? rawData.ui_components[0].data
51
- : rawData.data;
50
+ isGallery(rawData) ? rawData.ui_components[0].data : rawData.data;
52
51
 
53
52
  const extractData = compose(uniq, map(getData));
54
53
 
@@ -70,13 +69,12 @@ export const useBatchLoading = (
70
69
  const [hasEverBeenReady, setHasEverBeenReady] = React.useState(false);
71
70
 
72
71
  // if first component is gallery-qb, take only one component for initial load
73
- const takeSize =
74
- componentsToRender?.[0]?.component_type === "gallery-qb"
75
- ? 1
76
- : min(
77
- options.initialBatchSize ?? DEFAULT_BATCH_SIZE,
78
- componentsToRender.length
79
- );
72
+ const takeSize = isGallery(componentsToRender?.[0])
73
+ ? 1
74
+ : min(
75
+ options.initialBatchSize ?? DEFAULT_BATCH_SIZE,
76
+ componentsToRender.length
77
+ );
80
78
 
81
79
  const takeBatchSize = React.useCallback(take(takeSize), [takeSize]);
82
80
 
@@ -146,11 +144,11 @@ export const useBatchLoading = (
146
144
  }
147
145
  }
148
146
  });
149
- }, [feedUrls]);
147
+ }, [feedUrls, feeds]);
150
148
 
151
149
  React.useEffect(() => {
152
150
  runBatchLoading();
153
- }, []);
151
+ }, [runBatchLoading]); // Adding runBatchLoading as a dependency to ensure that it reloads feeds when clearPipesData is called
154
152
 
155
153
  React.useEffect(() => {
156
154
  // check if all feeds are ready and set hasEverBeenReady to true
@@ -3,7 +3,7 @@ import { platformSelect } from "../../reactUtils";
3
3
  const TAB_BAR_HEIGHT_IOS = 49;
4
4
  const TAB_BAR_HEIGHT_ANDROID = 56;
5
5
 
6
- export const useGetTabBarHeight = () =>
6
+ export const getTabBarHeight = () =>
7
7
  platformSelect({
8
8
  ios: TAB_BAR_HEIGHT_IOS,
9
9
  android: TAB_BAR_HEIGHT_ANDROID,
@@ -127,10 +127,10 @@ export function isNavBarVisible(
127
127
 
128
128
  export const useBackHandler = (cb: () => boolean) => {
129
129
  useEffect(() => {
130
- BackHandler.addEventListener("hardwareBackPress", cb);
130
+ const unsubscribe = BackHandler.addEventListener("hardwareBackPress", cb);
131
131
 
132
132
  return () => {
133
- BackHandler.removeEventListener("hardwareBackPress", cb);
133
+ unsubscribe.remove();
134
134
  };
135
135
  }, [cb]);
136
136
  };
@@ -1,12 +1,19 @@
1
1
  import { useGetNavBarTopBorderWidth } from "./useGetNavBarTopBorderWidth";
2
- import { useGetTabBarHeight } from "./useGetTabBarHeight";
2
+ import { getTabBarHeight } from "./getTabBarHeight";
3
+ import { useNavigation } from "./useNavigation";
3
4
  import { MenuTypes, useNavigationType } from "./useNavigationType";
5
+ import { useNavigationPluginData } from "./useNavigationPluginData";
4
6
 
5
7
  export const useGetBottomTabBarHeight = (): number => {
8
+ const { activeRiver } = useNavigation();
9
+
10
+ const navigationPluginData = useNavigationPluginData(activeRiver);
11
+ const navigationType = useNavigationType(navigationPluginData);
12
+
6
13
  const topBorderWidth = useGetNavBarTopBorderWidth();
7
- const tabBarHeight = useGetTabBarHeight();
14
+ const tabBarHeight = getTabBarHeight();
8
15
 
9
- const isBottomBarNavigation = useNavigationType() === MenuTypes.bottomTabBar;
16
+ const isBottomBarNavigation = navigationType === MenuTypes.bottomTabBar;
10
17
 
11
18
  return !isBottomBarNavigation ? 0 : tabBarHeight + topBorderWidth;
12
19
  };
@@ -1,13 +1,17 @@
1
1
  import { useRoute } from "./useRoute";
2
2
 
3
- export const useNavigationPluginData = (): ZappNavigation | undefined => {
3
+ export const useNavigationPluginData = (
4
+ screenData?: LegacyNavigationScreenData | null
5
+ ): ZappNavigation | undefined => {
4
6
  const {
5
7
  screenData: useRouteScreenData,
6
8
  }: { screenData: QuickBrickNavigationData | null } = useRoute();
7
9
 
8
- const navigations = useRouteScreenData?.targetScreen
9
- ? (useRouteScreenData.targetScreen as ZappRiver).navigations
10
- : (useRouteScreenData as ZappRiver).navigations;
10
+ const activeScreenData = screenData ?? useRouteScreenData;
11
+
12
+ const navigations = activeScreenData?.targetScreen
13
+ ? (activeScreenData.targetScreen as ZappRiver).navigations
14
+ : (activeScreenData as ZappRiver).navigations;
11
15
 
12
16
  const navigationMenu = navigations?.find((nav) => nav.category === "menu");
13
17
 
@@ -5,8 +5,10 @@ export enum MenuTypes {
5
5
  bottomTabBar = "BOTTOM_TAB_BAR",
6
6
  }
7
7
 
8
- export const useNavigationType = (): MenuTypes => {
9
- const navigationMenu = useNavigationPluginData();
8
+ export const useNavigationType = (navigation?: ZappNavigation): MenuTypes => {
9
+ const navigationPluginData = useNavigationPluginData();
10
+
11
+ const navigationMenu = navigation ?? navigationPluginData;
10
12
 
11
13
  return !navigationMenu ||
12
14
  navigationMenu.navigation_type === "quick_brick_side_menu"
@@ -0,0 +1,69 @@
1
+ import { renderHook } from "@testing-library/react-hooks";
2
+
3
+ jest.mock(
4
+ "@applicaster/zapp-react-native-ui-components/Components/River/useScreenConfiguration",
5
+ () => ({
6
+ useScreenConfiguration: jest.fn(),
7
+ })
8
+ );
9
+
10
+ const {
11
+ useScreenConfiguration,
12
+ } = require("@applicaster/zapp-react-native-ui-components/Components/River/useScreenConfiguration");
13
+
14
+ const { useScreenBackgroundColor } = require("../useScreenBackgroundColor");
15
+
16
+ describe("useScreenBackgroundColor", () => {
17
+ afterEach(() => {
18
+ jest.clearAllMocks();
19
+ });
20
+
21
+ it("should return the background color from screen configuration", () => {
22
+ useScreenConfiguration.mockReturnValue({
23
+ backgroundColor: "#FF0000",
24
+ });
25
+
26
+ // Render the hook with a screen ID
27
+ const { result } = renderHook(() =>
28
+ useScreenBackgroundColor("test-screen-id")
29
+ );
30
+
31
+ expect(result.current).toBe("#FF0000");
32
+
33
+ expect(useScreenConfiguration).toHaveBeenCalledWith("test-screen-id");
34
+ });
35
+
36
+ it("should return 'transparent' when background color is empty", () => {
37
+ useScreenConfiguration.mockReturnValue({
38
+ backgroundColor: "",
39
+ });
40
+
41
+ const { result } = renderHook(() =>
42
+ useScreenBackgroundColor("test-screen-id")
43
+ );
44
+
45
+ expect(result.current).toBe("transparent");
46
+ });
47
+
48
+ it("should return 'transparent' when background color is null", () => {
49
+ useScreenConfiguration.mockReturnValue({
50
+ backgroundColor: null,
51
+ });
52
+
53
+ const { result } = renderHook(() =>
54
+ useScreenBackgroundColor("test-screen-id")
55
+ );
56
+
57
+ expect(result.current).toBe("transparent");
58
+ });
59
+
60
+ it("should return 'transparent' when background color is undefined", () => {
61
+ useScreenConfiguration.mockReturnValue({});
62
+
63
+ const { result } = renderHook(() =>
64
+ useScreenBackgroundColor("test-screen-id")
65
+ );
66
+
67
+ expect(result.current).toBe("transparent");
68
+ });
69
+ });
@@ -1,23 +1,11 @@
1
- import * as React from "react";
2
- import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
3
1
  import { useScreenConfiguration } from "@applicaster/zapp-react-native-ui-components/Components/River/useScreenConfiguration";
4
2
  import { ifEmptyUseFallback } from "@applicaster/zapp-react-native-utils/cellUtils";
5
3
 
4
+ const DEFAULT_BACKGROUND_FALLBACK = "transparent";
5
+
6
6
  export const useScreenBackgroundColor = (screenId: string): string => {
7
7
  const { backgroundColor: screenBackgroundColor } =
8
8
  useScreenConfiguration(screenId);
9
9
 
10
- const theme = useTheme();
11
-
12
- const themeBackgroundColor = React.useMemo(
13
- () => theme.app_background_color,
14
- [theme.app_background_color]
15
- );
16
-
17
- const backgroundColor = ifEmptyUseFallback(
18
- screenBackgroundColor,
19
- themeBackgroundColor
20
- );
21
-
22
- return backgroundColor;
10
+ return ifEmptyUseFallback(screenBackgroundColor, DEFAULT_BACKGROUND_FALLBACK);
23
11
  };
@@ -0,0 +1,79 @@
1
+ # ZStoreProvider and useZStore
2
+
3
+ This module provides a React context-based solution for managing Zustand stores with named access.
4
+
5
+ ## Usage
6
+
7
+ ### ZStoreProvider
8
+
9
+ The `ZStoreProvider` component creates a Zustand store from the provided value and makes it available to child components.
10
+
11
+ ```tsx
12
+ import { ZStoreProvider } from "@applicaster/zapp-react-native-utils/reactHooks/state";
13
+
14
+ // In your component
15
+ <ZStoreProvider name="playerConfiguration" value={controller?.config}>
16
+ <YourComponent />
17
+ </ZStoreProvider>
18
+ ```
19
+
20
+ ### useZStore
21
+
22
+ The `useZStore` hook allows you to access a Zustand store by name from within a `ZStoreProvider`.
23
+
24
+ ```tsx
25
+ import { useZStore } from "@applicaster/zapp-react-native-utils/reactHooks/state";
26
+ import { useStore } from "zustand";
27
+
28
+ // In your component
29
+ const MyComponent = () => {
30
+ const store = useZStore("playerConfiguration");
31
+ const config = useStore(store, (state) => state.someProperty);
32
+
33
+ return <Text>{config}</Text>;
34
+ };
35
+ ```
36
+
37
+ ## Example
38
+
39
+ ```tsx
40
+ import React from "react";
41
+ import { ZStoreProvider, useZStore } from "@applicaster/zapp-react-native-utils/reactHooks/state";
42
+ import { useStore } from "zustand";
43
+
44
+ // Component that uses the store
45
+ const PlayerConfigDisplay = () => {
46
+ const store = useZStore("playerConfiguration");
47
+ const config = useStore(store, (state) => state);
48
+
49
+ return (
50
+ <View>
51
+ <Text>Player Config: {JSON.stringify(config)}</Text>
52
+ </View>
53
+ );
54
+ };
55
+
56
+ // Main component that provides the store
57
+ const PlayerComponent = ({ controller }) => {
58
+ return (
59
+ <ZStoreProvider name="playerConfiguration" value={controller?.config}>
60
+ <PlayerConfigDisplay />
61
+ </ZStoreProvider>
62
+ );
63
+ };
64
+ ```
65
+
66
+ ## Features
67
+
68
+ - **Named Stores**: Access stores by name instead of importing them directly
69
+ - **Context-based**: Uses React Context for store management
70
+ - **Zustand Integration**: Seamlessly works with existing Zustand stores
71
+ - **Type Safety**: Full TypeScript support
72
+ - **Error Handling**: Clear error messages when stores are not found or used outside providers
73
+
74
+ ## Error Handling
75
+
76
+ The `useZStore` hook will throw errors in the following cases:
77
+
78
+ 1. **Used outside provider**: "useZStore must be used within a ZStoreProvider"
79
+ 2. **Store not found**: "Store with name 'storeName' not found. Make sure it's provided by a ZStoreProvider"