@applicaster/zapp-react-native-utils 12.0.0-alpha.3804385444 → 12.0.0-alpha.4201443043

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 (47) hide show
  1. package/analyticsUtils/AnalyticsEvents/index.ts +6 -0
  2. package/analyticsUtils/AnalyticsEvents/searchEvents.ts +21 -0
  3. package/analyticsUtils/analyticsPlugins.ts +25 -0
  4. package/analyticsUtils/events.ts +6 -0
  5. package/analyticsUtils/index.tsx +6 -0
  6. package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +2 -3
  7. package/appUtils/focusManager/__tests__/focusManager.test.js +4 -24
  8. package/appUtils/focusManager/__tests__/index.test.ts +2 -3
  9. package/appUtils/focusManager/index.ts +76 -56
  10. package/appUtils/focusManager/treeDataStructure/Utils/index.js +74 -43
  11. package/appUtils/focusManager/utils/index.ts +100 -0
  12. package/appUtils/focusManagerAux/index.ts +76 -0
  13. package/appUtils/focusManagerAux/utils/index.ts +100 -0
  14. package/appUtils/playerManager/player.ts +1 -0
  15. package/appUtils/playerManager/playerNative.ts +5 -0
  16. package/appUtils/playerManager/usePlayerControllerSetup.tsx +6 -1
  17. package/arrayUtils/__tests__/isFilledArray.test.ts +63 -0
  18. package/arrayUtils/index.ts +5 -0
  19. package/cellUtils/__tests__/cellUtils.test.ts +9 -20
  20. package/cellUtils/index.ts +6 -12
  21. package/dateUtils/index.ts +11 -0
  22. package/headersUtils/index.ts +1 -0
  23. package/inflatedUrlUtils/index.ts +4 -4
  24. package/localizationUtils/__tests__/localizationUtils.test.ts +72 -53
  25. package/localizationUtils/index.ts +13 -22
  26. package/numberUtils/__tests__/isLikeNumber.test.ts +18 -0
  27. package/numberUtils/index.ts +9 -18
  28. package/package.json +2 -2
  29. package/reactHooks/cell-click/__tests__/index.test.js +10 -1
  30. package/reactHooks/cell-click/index.ts +12 -12
  31. package/reactHooks/feed/__tests__/useBuildPipesUrl.test.tsx +30 -70
  32. package/reactHooks/feed/__tests__/useInflatedUrl.test.ts +18 -3
  33. package/reactHooks/feed/useBuildPipesUrl.ts +7 -10
  34. package/reactHooks/feed/useInflatedUrl.ts +44 -15
  35. package/reactHooks/navigation/__mocks__/index.ts +13 -0
  36. package/reactHooks/navigation/__mocks__/useNavigation.ts +3 -0
  37. package/reactHooks/navigation/__tests__/index.test.tsx +11 -8
  38. package/reactHooks/navigation/index.ts +53 -66
  39. package/reactHooks/screen/__tests__/useCurrentScreenData.test.tsx +21 -23
  40. package/reactHooks/screen/index.ts +2 -0
  41. package/reactHooks/screen/useScreenBackgroundColor.ts +23 -0
  42. package/reactHooks/screen/useScreenContext.ts +3 -3
  43. package/screenState/index.ts +0 -8
  44. package/stringUtils/index.ts +5 -0
  45. package/testUtils/getByTestId.ts +14 -0
  46. package/testUtils/index.tsx +24 -22
  47. package/types.ts +4 -0
@@ -1,9 +1,10 @@
1
1
  /* eslint-disable @typescript-eslint/no-use-before-define */
2
2
  /* eslint max-len: off */
3
3
 
4
- import React, { useEffect, useMemo } from "react";
4
+ import React, { useContext, useEffect, useMemo } from "react";
5
5
  import { BackHandler, InteractionManager, NativeModules } from "react-native";
6
6
  import { useSafeAreaInsets } from "react-native-safe-area-context";
7
+ import { isEmpty } from "ramda";
7
8
 
8
9
  import {
9
10
  useContentTypes,
@@ -12,7 +13,11 @@ import {
12
13
  import { HooksManager } from "@applicaster/zapp-react-native-utils/appUtils/HooksManager";
13
14
 
14
15
  import { legacyScreenData } from "@applicaster/quick-brick-core/App/NavigationProvider/utils";
15
- import { LONG_KEY_PRESS_TIMEOUT } from "@applicaster/quick-brick-core/const";
16
+ import {
17
+ LONG_KEY_PRESS_TIMEOUT,
18
+ QUICK_BRICK_NAVBAR,
19
+ QUICK_BRICK_CONTENT,
20
+ } from "@applicaster/quick-brick-core/const";
16
21
  import { useZappHooksModalState } from "@applicaster/zapp-react-dom-ui-components/Components/ZappHooksModal/ZappHooksModal";
17
22
  import { ZappHookModalContext } from "@applicaster/zapp-react-native-ui-components/Contexts";
18
23
  import { PathnameContext } from "@applicaster/zapp-react-native-ui-components/Contexts/PathnameContext";
@@ -31,6 +36,7 @@ import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
31
36
  import { useNavbarState } from "../screen";
32
37
  import { useNavigation } from "./useNavigation";
33
38
  import { useModalStore } from "../../modalState";
39
+ import { ScreenDataContext } from "@applicaster/zapp-react-native-ui-components/Contexts/ScreenDataContext";
34
40
 
35
41
  export { useNavigation } from "./useNavigation";
36
42
 
@@ -146,16 +152,8 @@ export const useRoute = (): {
146
152
  pathname: string;
147
153
  } => {
148
154
  const pathname = usePathname() || "";
149
-
150
- /**
151
- * compose `useRoute()` output data
152
- */
153
- const composeRouteData = (screenData: VariousScreenData) => ({
154
- screenData,
155
- pathname,
156
- });
157
-
158
155
  const navigator = useNavigation();
156
+ const screenDataContext = legacyScreenData(useContext(ScreenDataContext));
159
157
 
160
158
  const { plugins, contentTypes, rivers } = usePickFromState([
161
159
  "plugins",
@@ -163,10 +161,11 @@ export const useRoute = (): {
163
161
  "contentTypes",
164
162
  ]);
165
163
 
166
- const currentStack = navigator.getStackForPathname(pathname);
167
164
  const { modalState } = useModalStore();
168
165
  const hookModalState = useZappHooksModalState();
166
+
169
167
  const modalScreenData = modalState.screen;
168
+
170
169
  const hookModalScreenData = hookModalState.state.screenData?.payload;
171
170
 
172
171
  const videoModalScreenData =
@@ -183,15 +182,6 @@ export const useRoute = (): {
183
182
  plugins
184
183
  );
185
184
 
186
- const currentRouteScreenData = legacyScreenData(
187
- {
188
- screen: currentStack?.state?.screen ?? navigator.data?.screen ?? {},
189
- entry: currentStack?.state?.entry ?? {},
190
- } as NavigationScreenData,
191
-
192
- plugins
193
- );
194
-
195
185
  // There are 4 route scenarios
196
186
  // For regular screens take date from navigator stack.
197
187
  // is path is model grab screenData from modal state
@@ -200,63 +190,33 @@ export const useRoute = (): {
200
190
  // if path is hook grab screenData from screenData
201
191
 
202
192
  if (isModalPathname(pathname)) {
203
- return composeRouteData(modalScreenData ?? ({} as ZappEntry));
193
+ const screenData = modalScreenData ?? ({} as ZappEntry);
194
+
195
+ return { screenData, pathname };
204
196
  }
205
197
 
206
198
  if (isVideoModalPathname(pathname)) {
207
- return composeRouteData(videoModalScreenData as LegacyNavigationScreenData);
199
+ const screenData = videoModalScreenData as LegacyNavigationScreenData;
200
+
201
+ return { screenData, pathname };
208
202
  }
209
203
 
210
204
  if (isHookModalPathname(pathname)) {
211
- return composeRouteData(hookModalScreenData);
205
+ const screenData = hookModalScreenData;
206
+
207
+ return { screenData, pathname };
212
208
  }
213
209
 
214
210
  if (isHookPathname(pathname)) {
215
- /**
216
- * HACK
217
- *
218
- * This condition relies on hacky approach to get the current hook screen data
219
- * using navigator.screenData for some cases.
220
- * This is used when hook is in sequence.
221
- */
222
-
223
- // case 1: stack not yet updated in navigator context, but navigator.screenData is updated already.
224
- if (!currentStack) {
225
- return composeRouteData(navigator.screenData);
226
- }
227
-
228
- const isValidHookInCurrentRouteScreenData =
229
- "hookPlugin" in currentRouteScreenData &&
230
- !!currentRouteScreenData.hookPlugin?.module;
231
-
232
- let screenData;
233
-
234
- // case 2: stack has valid hook data
235
- if (isValidHookInCurrentRouteScreenData) {
236
- screenData = currentRouteScreenData;
237
- }
238
-
239
- const navigatorScreenData = navigator.screenData as NavigatorEntry;
240
-
241
- // case 3: current hookPlugin is missing weight, but navigator.screenData has weight, use navigator.screenData
242
- if (
243
- screenData.hookPlugin.identifier ===
244
- navigatorScreenData?.hookPlugin?.identifier &&
245
- !screenData.hookPlugin?.weight &&
246
- navigatorScreenData?.hookPlugin?.weight
247
- ) {
248
- screenData = navigator.screenData;
249
- }
250
-
251
- return composeRouteData(screenData);
211
+ return { screenData: screenDataContext, pathname };
252
212
  }
253
213
 
254
- // case 1: stack not yet updated in navigator context, but navigator.screenData is updated already.
255
- if (!currentStack) {
256
- return composeRouteData(navigator.screenData);
257
- }
214
+ // Cover cases where screenContext is used before navigation is initialized
215
+ const screenData = isEmpty(screenDataContext)
216
+ ? navigator.screenData
217
+ : screenDataContext;
258
218
 
259
- return composeRouteData(currentRouteScreenData);
219
+ return { screenData, pathname };
260
220
  };
261
221
 
262
222
  type Callbacks = Partial<{
@@ -496,3 +456,30 @@ export const useIsScreenActive = () => {
496
456
 
497
457
  return pathname === currentRoute;
498
458
  };
459
+
460
+ type Return = (prefix: string) => string;
461
+
462
+ export const useUniqueRouteSuffix = (): Return => {
463
+ const pathname = usePathname();
464
+ const { currentRoute } = useNavigation();
465
+
466
+ const suffix = `currentRoute:${currentRoute}_pathname:${pathname}`;
467
+
468
+ const withUniqueRouteSuffix = React.useCallback((prefix: string) => {
469
+ return `${prefix}___${suffix}`;
470
+ }, []);
471
+
472
+ return withUniqueRouteSuffix;
473
+ };
474
+
475
+ export const useNavbarId = () => {
476
+ const withUniqueRouteSuffix = useUniqueRouteSuffix();
477
+
478
+ return withUniqueRouteSuffix(QUICK_BRICK_NAVBAR);
479
+ };
480
+
481
+ export const useContentId = () => {
482
+ const withUniqueRouteSuffix = useUniqueRouteSuffix();
483
+
484
+ return withUniqueRouteSuffix(QUICK_BRICK_CONTENT);
485
+ };
@@ -3,23 +3,28 @@ import { Provider } from "react-redux";
3
3
  import { renderHook } from "@testing-library/react-hooks";
4
4
  import configureStore from "redux-mock-store";
5
5
  import thunk from "redux-thunk";
6
- import { NavigationContext } from "@applicaster/zapp-react-native-ui-components/Contexts/NavigationContext";
7
6
 
8
7
  const mockStore = configureStore([thunk]);
9
8
 
10
- import { useCurrentScreenData } from "../useCurrentScreenData";
11
-
12
- describe("useScreenData", function () {
13
- const river_id_2: ZappRiver = {
14
- name: "River 2",
15
- id: "A1234",
16
- home: false,
17
- home_offline: false,
18
- supports_offline: false,
19
- navigations: [],
20
- hooks: { preload_plugins: [] },
21
- };
22
-
9
+ const river_id_2: ZappRiver = {
10
+ name: "River 2",
11
+ id: "A1234",
12
+ home: false,
13
+ home_offline: false,
14
+ supports_offline: false,
15
+ navigations: [],
16
+ hooks: { preload_plugins: [] },
17
+ };
18
+
19
+ jest.mock("../useScreenContext", () => ({
20
+ useScreenContext: () => ({
21
+ screen: river_id_2,
22
+ }),
23
+ }));
24
+
25
+ const { useCurrentScreenData } = require("../useCurrentScreenData");
26
+
27
+ describe("useScreenData", () => {
23
28
  const river_id_1 = { id: "B5678" };
24
29
 
25
30
  const store = mockStore({
@@ -27,17 +32,10 @@ describe("useScreenData", function () {
27
32
  });
28
33
 
29
34
  const wrapper: React.FC<any> = ({ children }) => (
30
- <NavigationContext.Provider
31
- value={{
32
- currentRoute: "/river/river_id_1/river/river_id_2",
33
- data: { screen: river_id_2 },
34
- }}
35
- >
36
- <Provider store={store}>{children}</Provider>
37
- </NavigationContext.Provider>
35
+ <Provider store={store}>{children}</Provider>
38
36
  );
39
37
 
40
- it("should return river data for the current screen/route", function () {
38
+ it("should return river data for the current screen/route", () => {
41
39
  const { result } = renderHook(() => useCurrentScreenData(), { wrapper });
42
40
 
43
41
  expect(result.current).toBe(river_id_2);
@@ -10,3 +10,5 @@ export {
10
10
  useNavbarState,
11
11
  useSetNavbarState,
12
12
  } from "./useScreenContext";
13
+
14
+ export { useScreenBackgroundColor } from "./useScreenBackgroundColor";
@@ -0,0 +1,23 @@
1
+ import * as React from "react";
2
+ import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
3
+ import { useScreenConfiguration } from "@applicaster/zapp-react-native-ui-components/Components/River/useScreenConfiguration";
4
+ import { ifEmptyUseFallback } from "@applicaster/zapp-react-native-utils/cellUtils";
5
+
6
+ export const useScreenBackgroundColor = (screenId: string): string => {
7
+ const { backgroundColor: screenBackgroundColor } =
8
+ useScreenConfiguration(screenId);
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;
23
+ };
@@ -5,8 +5,9 @@ import { useNestedNavigationContext } from "@applicaster/zapp-react-native-ui-co
5
5
  import { useNavigation } from "../navigation/useNavigation";
6
6
 
7
7
  import { ScreenContext } from "@applicaster/zapp-react-native-ui-components/Contexts/ScreenContext";
8
+ import { ScreenDataContext } from "@applicaster/zapp-react-native-ui-components/Contexts/ScreenDataContext";
8
9
 
9
- function useScreenContextV2() {
10
+ export function useScreenContextV2() {
10
11
  return useContext(ScreenContext);
11
12
  }
12
13
 
@@ -44,9 +45,8 @@ export function useSetNavbarState() {
44
45
 
45
46
  export function useScreenContext(): NavigationScreenData {
46
47
  const navigator = useNavigation();
48
+ const screenData = useContext(ScreenDataContext);
47
49
 
48
- const screenKey = useMemo(() => navigator?.key, [navigator]);
49
- const screenData = useMemo(() => navigator?.data, [screenKey]);
50
50
  const modalScreenData = navigator?.modalData;
51
51
 
52
52
  const isModal = useModalNavigationContext();
@@ -49,11 +49,3 @@ export function useScreenState(
49
49
  setSelectedEntry: (entryId) => setSelectedEntry(screenId, entryId),
50
50
  };
51
51
  }
52
-
53
- export const useScreenScrollStore = create<{
54
- scrollPosition: number;
55
- scrollingVelocity: number;
56
- }>(() => ({
57
- scrollPosition: 0,
58
- scrollingVelocity: 0,
59
- }));
@@ -152,3 +152,8 @@ export function objectToCamelCase(obj) {
152
152
  export function isString(value: unknown): value is string {
153
153
  return typeof value === "string" || value instanceof String;
154
154
  }
155
+
156
+ /** Checks if a value is a non-empty string */
157
+ export function isNotEmptyString(value: unknown): value is string {
158
+ return isString(value) && value.length > 0;
159
+ }
@@ -0,0 +1,14 @@
1
+ export const getByTestId = (wrapper, testId) => {
2
+ try {
3
+ if (wrapper?.getByTestId) {
4
+ return wrapper?.queryAllByTestId(testId)?.[0] ?? undefined;
5
+ } else if (wrapper?.root?.findByProps) {
6
+ return wrapper.root.findAllByProps({ testID: testId })?.[0] ?? undefined;
7
+ }
8
+ } catch (e) {
9
+ // eslint-disable-next-line no-console
10
+ console.log("getByTestId E:", e);
11
+
12
+ return undefined;
13
+ }
14
+ };
@@ -1,6 +1,6 @@
1
1
  import * as R from "ramda";
2
2
 
3
- import React from "react";
3
+ import React, { PropsWithChildren } from "react";
4
4
  import { View } from "react-native";
5
5
 
6
6
  import { Provider } from "react-redux";
@@ -11,6 +11,8 @@ import { SafeAreaProvider } from "react-native-safe-area-context";
11
11
  import { render } from "@testing-library/react-native";
12
12
  import { AnalyticsProvider } from "../analyticsUtils";
13
13
 
14
+ export { getByTestId } from "./getByTestId";
15
+
14
16
  /**
15
17
  * Function to be used in tandem with enzyme findWhere(node => node.prop('testID') === 'yourID')
16
18
  * Example:
@@ -21,17 +23,6 @@ import { AnalyticsProvider } from "../analyticsUtils";
21
23
  export const testIDEquals = (testId: any): boolean =>
22
24
  R.compose(R.equals(testId), R.invoker(1, "prop")("testID"));
23
25
 
24
- export const getByTestId = (wrapper, testId) => {
25
- try {
26
- return wrapper?.queryAllByTestId(testId)?.[0] ?? undefined;
27
- } catch (e) {
28
- // eslint-disable-next-line no-console
29
- console.log("getByTestId E:", e);
30
-
31
- return undefined;
32
- }
33
- };
34
-
35
26
  /** Helper function for react-test-renderer, accepts type and renderer
36
27
  * * @param {*} type React Component type instance or name of component
37
28
  * * @param {*} renderer renderer instance
@@ -47,11 +38,11 @@ export const findByType = (type, renderer) => {
47
38
  export function createComponentMock(mockName, hasChildren = false) {
48
39
  return hasChildren
49
40
  ? jest.fn(({ children }) => (
50
- <View comp={mockName} testID={mockName}>
41
+ <View testID={mockName}>
51
42
  {typeof children === "function" ? children() : children}
52
43
  </View>
53
44
  ))
54
- : jest.fn(() => <View comp={mockName} testID={mockName} />);
45
+ : jest.fn(() => <View testID={mockName} />);
55
46
  }
56
47
 
57
48
  const initialState = {
@@ -64,14 +55,21 @@ const initialState = {
64
55
  AppContainer: createComponentMock("appContainer", true),
65
56
  DisplayError: createComponentMock("displayError"),
66
57
  },
67
- rivers: [{ id: "A1234", home: true }],
58
+ rivers: { id: "A1234", home: true },
68
59
  appSettings: {},
69
60
  plugins: [],
70
- appState: { appReady: true },
61
+ appState: { appReady: true, appLaunched: true },
62
+ appData: { layoutVersion: "v1" },
63
+ contentTypes: null,
71
64
  };
72
65
 
73
- export const WrappedWithProviders = ({ children, store = {} }: any) => {
74
- const _store = configureStore([thunk])({ ...initialState, ...store });
66
+ export const WrappedWithProviders = ({
67
+ children,
68
+ store: storeObj = {},
69
+ }: any) => {
70
+ const _store = configureStore([thunk])(
71
+ R.mergeDeepWith(R.concat, { ...initialState }, storeObj)
72
+ );
75
73
 
76
74
  return (
77
75
  <SafeAreaProvider
@@ -93,8 +91,12 @@ export const WrappedWithProviders = ({ children, store = {} }: any) => {
93
91
  * @param store optional store to pass to the provider
94
92
  * @returns
95
93
  */
96
- export const renderWithProviders = (component, store?: unknown) => {
97
- return render(
98
- <WrappedWithProviders store={store}>{component}</WrappedWithProviders>
99
- );
94
+ export const renderWithProviders = (component, storeObj?: unknown) => {
95
+ return render(component, {
96
+ wrapper: function TestWrapper({ children }: PropsWithChildren<any>) {
97
+ return (
98
+ <WrappedWithProviders store={storeObj}>{children}</WrappedWithProviders>
99
+ );
100
+ },
101
+ });
100
102
  };
package/types.ts CHANGED
@@ -5,3 +5,7 @@ export type ManifestField<T> = {
5
5
  group?: boolean;
6
6
  fields?: ManifestField<any>[];
7
7
  };
8
+
9
+ export enum ENDPOINT_TAGS {
10
+ allow_missing_keys = "allow_missing_keys",
11
+ }