@applicaster/zapp-react-native-utils 13.0.0-alpha.3962361544 → 13.0.0-alpha.4012243505

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 (53) hide show
  1. package/actionsExecutor/ActionExecutorContext.tsx +126 -6
  2. package/actionsExecutor/consts.ts +4 -0
  3. package/appUtils/HooksManager/Hook.ts +4 -0
  4. package/appUtils/HooksManager/index.ts +3 -1
  5. package/appUtils/contextKeysManager/contextResolver.ts +1 -12
  6. package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +1 -0
  7. package/appUtils/focusManager/index.ts +14 -10
  8. package/appUtils/localizationsHelper.ts +23 -0
  9. package/appUtils/orientationHelper.ts +11 -13
  10. package/appUtils/playerManager/player.ts +24 -12
  11. package/appUtils/playerManager/playerNative.ts +18 -3
  12. package/appUtils/playerManager/usePlayerTitleSummaryOverlay.tsx +7 -1
  13. package/componentsUtils/__tests__/isFirstComponentGallery.test.ts +33 -0
  14. package/componentsUtils/__tests__/isFirstComponentScreenPicker.test.ts +33 -0
  15. package/componentsUtils/__tests__/isGallery.test.ts +33 -0
  16. package/componentsUtils/__tests__/isScreenPicker.test.ts +33 -0
  17. package/componentsUtils/index.ts +32 -0
  18. package/dateUtils/index.ts +19 -0
  19. package/focusManager/FocusManager.ts +3 -1
  20. package/focusManager/index.ts +3 -21
  21. package/functionUtils/index.ts +46 -8
  22. package/index.d.ts +0 -12
  23. package/localizationUtils/index.ts +5 -10
  24. package/manifestUtils/defaultManifestConfigurations/generalContent.js +13 -0
  25. package/manifestUtils/defaultManifestConfigurations/player.js +14 -0
  26. package/manifestUtils/keys.js +9 -0
  27. package/modalState/__tests__/index.test.ts +51 -0
  28. package/modalState/index.ts +32 -2
  29. package/navigationUtils/__tests__/__snapshots__/navigationUtils.test.js.snap +1 -0
  30. package/navigationUtils/index.ts +3 -1
  31. package/numberUtils/__tests__/toPositiveNumberWithDefault.test.ts +23 -3
  32. package/numberUtils/index.ts +11 -5
  33. package/package.json +2 -6
  34. package/playerUtils/configurationGenerator.ts +14 -0
  35. package/reactHooks/advertising/index.ts +5 -5
  36. package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +5 -7
  37. package/reactHooks/autoscrolling/useTrackedView.ts +12 -1
  38. package/reactHooks/feed/useFeedLoader.tsx +17 -8
  39. package/reactHooks/hookModal/hooks/useHookModalScreenData.ts +39 -0
  40. package/reactHooks/navigation/__tests__/index.test.tsx +0 -11
  41. package/reactHooks/navigation/index.ts +5 -3
  42. package/reactHooks/navigation/useRoute.ts +4 -6
  43. package/reactHooks/useListenEventBusEvent.ts +1 -1
  44. package/services/js2native.ts +673 -0
  45. package/time/BackgroundTimer.ts +55 -0
  46. package/time/Timer.ts +6 -6
  47. package/utils/README.md +30 -0
  48. package/utils/chunk.ts +5 -0
  49. package/utils/index.ts +5 -0
  50. package/utils/times.ts +5 -0
  51. package/zappFrameworkUtils/localStorageHelper.ts +6 -0
  52. package/zappFrameworkUtils/loginPluginUtils.ts +6 -2
  53. package/zappFrameworkUtils/types.ts +11 -0
@@ -1,6 +1,6 @@
1
1
  /// <reference types="@applicaster/applicaster-types" />
2
2
  import { localStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/LocalStorage";
3
- import React, { createContext, useMemo } from "react";
3
+ import React, { createContext, useEffect, useMemo } from "react";
4
4
  import {
5
5
  actionExecutor as _actionExecutor,
6
6
  ActionResult,
@@ -19,8 +19,17 @@ import {
19
19
  EntryResolver,
20
20
  resolveObjectValues,
21
21
  } from "../appUtils/contextKeysManager/contextResolver";
22
+ import { useNavigation } from "../reactHooks";
23
+ import { get } from "lodash";
22
24
 
23
- const { log_error, log_info } = createLogger({
25
+ import {
26
+ useContentTypes,
27
+ usePickFromState,
28
+ } from "@applicaster/zapp-react-native-redux/hooks";
29
+ import { TOGGLE_FLAG_MAX_ITEMS_REACHED_EVENT } from "./consts";
30
+ import { postEvent } from "../reactHooks/useSubscriberFor";
31
+
32
+ export const { log_error, log_info, log_debug } = createLogger({
24
33
  subsystem: "ActionExecutorContext",
25
34
  category: "General",
26
35
  });
@@ -51,6 +60,35 @@ type Props = {
51
60
  children: React.ReactNode;
52
61
  };
53
62
 
63
+ function findParentComponent(
64
+ childId: string,
65
+ parent: ZappRiver | ZappUIComponent
66
+ ): ZappRiver | ZappUIComponent | null {
67
+ for (const child of parent.ui_components) {
68
+ if (child.id === childId) return parent;
69
+
70
+ if (child.ui_components) {
71
+ const found = findParentComponent(childId, child);
72
+ if (found) return found;
73
+ }
74
+ }
75
+
76
+ return null;
77
+ }
78
+
79
+ // send all data just in case (like for message string formatting)
80
+ // Type is not exported for now
81
+ type MaxTagsReachedEvent = {
82
+ selectedItems: string[];
83
+ maxItems: number;
84
+ tag: string;
85
+ keyNamespace: string;
86
+ };
87
+
88
+ async function onMaxTagsReached(data: MaxTagsReachedEvent) {
89
+ postEvent(TOGGLE_FLAG_MAX_ITEMS_REACHED_EVENT, [data]);
90
+ }
91
+
54
92
  const prepareDefaultActions = (actionExecutor) => {
55
93
  actionExecutor.registerAction("localStorageSet", async (action) => {
56
94
  const namespaces = action.options.content;
@@ -65,11 +103,13 @@ const prepareDefaultActions = (actionExecutor) => {
65
103
  async (_action, context) => {
66
104
  const dispatch = appStore.getDispatch();
67
105
 
68
- log_info("handleAction: refreshComponent event");
106
+ const dataSource =
107
+ context?.component?.data?.source ||
108
+ findParentComponent(context?.component.id, context?.screenData)?.data
109
+ ?.source;
69
110
 
70
- const dataSource = context?.component?.data?.source;
111
+ log_info(`handleAction: refreshComponent for dataSource:${dataSource}`);
71
112
 
72
- // TODO: Later we can pass component ids to do refresh instead of current one
73
113
  // TODO: In theory we should wait callback to complete, before completing the action, but now it's not needed
74
114
  // TODO: handle focused item removal
75
115
  dispatch(
@@ -169,8 +209,29 @@ const prepareDefaultActions = (actionExecutor) => {
169
209
  action: ActionType,
170
210
  context?: Record<string, any>
171
211
  ): Promise<ActionResult> => {
212
+ if (!context) {
213
+ log_error(
214
+ "handleAction: localStorageToggleFlag action missing context"
215
+ );
216
+
217
+ return ActionResult.Error;
218
+ }
219
+
220
+ const entry = context?.entry as ZappEntry;
221
+
222
+ if (!entry) {
223
+ log_error(
224
+ "handleAction: localStorageToggleFlag action missing entry. Entry is required to get the tag."
225
+ );
226
+
227
+ return ActionResult.Error;
228
+ }
229
+
230
+ const tag = action.options?.selector
231
+ ? get(entry, action.options.selector)
232
+ : (entry.extensions?.tag ?? entry.id);
233
+
172
234
  const keyNamespace = action.options?.key;
173
- const tag = context?.entry?.extensions?.tag;
174
235
 
175
236
  if (keyNamespace && tag) {
176
237
  const multiSelectProvider =
@@ -188,6 +249,23 @@ const prepareDefaultActions = (actionExecutor) => {
188
249
  if (selectedItems.includes(tag)) {
189
250
  await multiSelectProvider.removeItem(tag);
190
251
  } else {
252
+ const maxItems = action.options?.max_items;
253
+
254
+ if (maxItems && selectedItems.length >= maxItems) {
255
+ log_info(
256
+ `handleAction: localStorageToggleFlag event reached max items limit: ${maxItems}, cannot add tag: ${tag}`
257
+ );
258
+
259
+ await onMaxTagsReached({
260
+ selectedItems,
261
+ maxItems,
262
+ tag,
263
+ keyNamespace,
264
+ });
265
+
266
+ return ActionResult.Cancel;
267
+ }
268
+
191
269
  await multiSelectProvider.addItem(tag);
192
270
  }
193
271
  } else {
@@ -211,6 +289,10 @@ export function withActionExecutor(Component) {
211
289
  prepareDefaultActions(_actionExecutor);
212
290
 
213
291
  return function ActionExecutorComponent(props: Props) {
292
+ const navigator = useNavigation();
293
+ const { rivers } = usePickFromState(["rivers"]);
294
+ const contentTypes = useContentTypes();
295
+
214
296
  const handlers = useMemo(() => {
215
297
  return {
216
298
  unregisterAction: _actionExecutor.unregisterAction,
@@ -221,6 +303,44 @@ export function withActionExecutor(Component) {
221
303
  };
222
304
  }, []);
223
305
 
306
+ useEffect(() => {
307
+ return _actionExecutor.registerAction(
308
+ "navigateToScreen",
309
+ async (action: ActionType, context?: Record<string, any>) => {
310
+ const screenType = action.options?.typeMapping;
311
+
312
+ if (!screenType) {
313
+ log_error("navigateToScreen: typeMapping option is missing");
314
+
315
+ return ActionResult.Error;
316
+ }
317
+
318
+ const screenId = contentTypes?.[screenType]?.screen_id || null;
319
+
320
+ if (!screenId) {
321
+ log_error(
322
+ `navigateToScreen: can not resolve screen type mapping: ${screenType}`
323
+ );
324
+
325
+ return ActionResult.Error;
326
+ }
327
+
328
+ const river = rivers[screenId];
329
+
330
+ if (!river) {
331
+ log_error("navigateToScreen: can not resolve river");
332
+
333
+ return ActionResult.Error;
334
+ }
335
+
336
+ context?.callback?.({ success: false, error: null, abort: true });
337
+ navigator.replace(river);
338
+
339
+ return ActionResult.Success;
340
+ }
341
+ );
342
+ }, [navigator, rivers, contentTypes]);
343
+
224
344
  return (
225
345
  <ActionExecutorContext.Provider value={handlers}>
226
346
  <Component {...props} />
@@ -0,0 +1,4 @@
1
+ export const TOGGLE_FLAG_MAX_ITEMS_REACHED_EVENT =
2
+ "action.localStorageToggleFlag.maxItemsReached";
3
+
4
+ export const ACTION_EXECUTOR_EVENT_SOURCE = "ActionExecutor";
@@ -65,6 +65,10 @@ export class Hook implements HookInterface {
65
65
  event: (typeof HOOKS_EVENTS)[keyof typeof HOOKS_EVENTS],
66
66
  ...args
67
67
  ) {
68
+ if (this.state === hookState(HOOKS_EVENTS.CANCEL)) {
69
+ return;
70
+ }
71
+
68
72
  this.state = hookState(event);
69
73
  this.manager.subscriber.invokeHandler(event, ...args);
70
74
  }
@@ -84,7 +84,7 @@ export function HooksManager({
84
84
  plugins,
85
85
  startUpHooksData = {},
86
86
  }: HookManagerArgs): HookManager {
87
- hooksManagerLogger.addContext({ targetScreen });
87
+ hooksManagerLogger.addContext({ targetScreenId: targetScreen.id });
88
88
 
89
89
  function logHookEvent(func, message, data) {
90
90
  func({
@@ -269,6 +269,7 @@ export function HooksManager({
269
269
  payload = initialPayload,
270
270
  callback: callbackArg,
271
271
  cancelled,
272
+ abort,
272
273
  }) {
273
274
  let callback = callbackArg;
274
275
 
@@ -308,6 +309,7 @@ export function HooksManager({
308
309
  hookPlugin.setStateAndNotify(HOOKS_EVENTS.CANCEL, {
309
310
  hookPlugin,
310
311
  payload,
312
+ abort,
311
313
  });
312
314
 
313
315
  const isHookInHomescreen = payload?.home;
@@ -1,12 +1,6 @@
1
- import { createLogger } from "../../logger";
2
1
  import { ContextKeysManager } from "./index";
3
2
  import * as R from "ramda";
4
3
 
5
- const { log_error } = createLogger({
6
- subsystem: "contextResolver",
7
- category: "General",
8
- });
9
-
10
4
  interface IResolver {
11
5
  resolve: (string) => Promise<string | number | object>;
12
6
  }
@@ -64,12 +58,7 @@ export const resolveObjectValues = async (
64
58
  return [key, resolvedValue];
65
59
  }
66
60
 
67
- log_error("resolveObjectValues: invalid context string", {
68
- key,
69
- value,
70
- });
71
-
72
- return [key, null];
61
+ return [key, value];
73
62
  })
74
63
  );
75
64
 
@@ -5,6 +5,7 @@ exports[`focusManager should be defined 1`] = `
5
5
  "blur": [Function],
6
6
  "disableFocus": [Function],
7
7
  "enableFocus": [Function],
8
+ "findPreferredFocusChild": [Function],
8
9
  "focusableTree": Tree {
9
10
  "loadingCounter": 0,
10
11
  "root": {
@@ -334,16 +334,7 @@ export const focusManager = (function () {
334
334
  return { success: false };
335
335
  }
336
336
 
337
- function treeLoaded(val, node) {
338
- logger.log({
339
- message: "treeLoaded",
340
- data: {
341
- val,
342
- node,
343
- currentFocusNode,
344
- },
345
- });
346
- }
337
+ function treeLoaded(_val, _node) {}
347
338
 
348
339
  function pressIn() {
349
340
  const currentFocusable = getCurrentFocus();
@@ -543,6 +534,18 @@ export const focusManager = (function () {
543
534
 
544
535
  const { on, invokeHandler, removeHandler } = eventHandler;
545
536
 
537
+ function findPreferredFocusChild(id) {
538
+ const node = getCurrentFocusNode(id);
539
+
540
+ const preferredFocus = findPriorityItem([node]);
541
+
542
+ if (isNilOrEmpty(preferredFocus)) {
543
+ return undefined;
544
+ }
545
+
546
+ return preferredFocus[0];
547
+ }
548
+
546
549
  /**
547
550
  * this is the list of the functions available externally
548
551
  * when importing the focus manager
@@ -572,5 +575,6 @@ export const focusManager = (function () {
572
575
  isGroupItemFocused,
573
576
  recoverFocus,
574
577
  isCurrentFocusOnTheTopScreen,
578
+ findPreferredFocusChild,
575
579
  };
576
580
  })();
@@ -65,6 +65,29 @@ export function getUILanguage() {
65
65
  return getLanguageCode();
66
66
  }
67
67
 
68
+ export const toDayJSLocaleMap = (code) => {
69
+ const map = {
70
+ hy: "hy-am",
71
+ fj: null,
72
+ la: null,
73
+ no: null,
74
+ pa: "pa-in",
75
+ qu: null,
76
+ "es-LA": "es",
77
+ sm: null,
78
+ to: null,
79
+ xh: null,
80
+ };
81
+
82
+ if (map[code] === null) {
83
+ return "en";
84
+ } else if (map[code]) {
85
+ return map[code];
86
+ } else {
87
+ return code;
88
+ }
89
+ };
90
+
68
91
  /**
69
92
  * returns the localization for the app based on the locale. If the locale is null
70
93
  * or undefined, or if the given locale doesn't exist in the localizations,
@@ -1,8 +1,8 @@
1
- /// <reference types="@types/react-native" />
2
1
  import * as ReactNative from "react-native";
3
- import { isTV, platformSelect } from "../reactUtils";
4
2
  import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
5
- import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks/device/useIsTablet";
3
+
4
+ import { isTV, platformSelect } from "../reactUtils";
5
+ import { useIsTablet } from "../reactHooks";
6
6
 
7
7
  import {
8
8
  QUICK_BRICK_EVENTS,
@@ -121,8 +121,15 @@ export const removeOrientationChangeListener = (listener: any): void => {
121
121
  */
122
122
  export const isOrientationCompatible = ({
123
123
  orientation,
124
- layoutData: { width, height },
124
+ layoutData: { width, height, isTablet, isTabletPortrait },
125
125
  }) => {
126
+ // We count portrait as compatible, because if default in zapp for mobile layout
127
+ // We count landscape as compatible, because if isTabletPortrait is false,
128
+ // it defaults for device
129
+ if (isTablet && !isTabletPortrait) {
130
+ return true;
131
+ }
132
+
126
133
  const isLandscape = width > height;
127
134
 
128
135
  if (
@@ -146,15 +153,6 @@ export const isOrientationCompatible = ({
146
153
  return true;
147
154
  };
148
155
 
149
- export const useIsOrientationCompatible = (orientation) => {
150
- const { height, width } = ReactNative.useWindowDimensions();
151
-
152
- return isOrientationCompatible({
153
- orientation,
154
- layoutData: { width, height },
155
- });
156
- };
157
-
158
156
  /**
159
157
  * Determine final screen orientation based on device type,
160
158
  * settings and orientation value provided by screen data settings
@@ -97,7 +97,7 @@ export class Player {
97
97
  this.onVideoLoad = this.onVideoLoad.bind(this);
98
98
  this.isFullScreenSupported = this.isFullScreenSupported.bind(this);
99
99
  this.onPlayerSeekComplete = this.onPlayerSeekComplete.bind(this);
100
-
100
+ this.onAdBreakEnd = this.onAdBreakEnd.bind(this);
101
101
  this.entryObservable = null;
102
102
  }
103
103
 
@@ -201,17 +201,25 @@ export class Player {
201
201
  adPosition: currentTime,
202
202
  };
203
203
  } else {
204
- if (!isEmptyOrNil(seekableDuration) && seekableDuration >= 0) {
205
- this.playerState.seekableDuration = seekableDuration;
206
- }
204
+ this.updateContentDuration({ seekableDuration, duration, currentTime });
205
+ }
206
+ };
207
207
 
208
- if (!isEmptyOrNil(duration) && duration >= 0) {
209
- this.playerState.contentDuration = duration;
210
- }
208
+ protected updateContentDuration = ({
209
+ seekableDuration = null,
210
+ duration = null,
211
+ currentTime = null,
212
+ }) => {
213
+ if (!isEmptyOrNil(seekableDuration) && seekableDuration >= 0) {
214
+ this.playerState.seekableDuration = seekableDuration;
215
+ }
211
216
 
212
- if (!isEmptyOrNil(currentTime) && currentTime >= 0) {
213
- this.playerState.contentPosition = currentTime;
214
- }
217
+ if (!isEmptyOrNil(duration) && duration >= 0) {
218
+ this.playerState.contentDuration = duration;
219
+ }
220
+
221
+ if (!isEmptyOrNil(currentTime) && currentTime >= 0) {
222
+ this.playerState.contentPosition = currentTime;
215
223
  }
216
224
  };
217
225
 
@@ -559,7 +567,7 @@ export class Player {
559
567
  });
560
568
  };
561
569
 
562
- onAdBreakEnd = (event) => {
570
+ onAdBreakEnd(event) {
563
571
  this.playerState.adState = null;
564
572
  this.playerState.isInAdBreak = false;
565
573
 
@@ -567,7 +575,7 @@ export class Player {
567
575
  callbackName: SharedPlayerCallBacksKeys.OnAdBreakEnd,
568
576
  event,
569
577
  });
570
- };
578
+ }
571
579
 
572
580
  onAdEnd = (event) => {
573
581
  this.playerState.adState = null;
@@ -736,6 +744,10 @@ export class Player {
736
744
  return null;
737
745
  };
738
746
 
747
+ public hasResumePosition() {
748
+ return false;
749
+ }
750
+
739
751
  public getConfig = () => this.config;
740
752
 
741
753
  // TODO: replace with enum of supported orientations
@@ -54,6 +54,10 @@ export class PlayerNative extends Player {
54
54
  });
55
55
  }
56
56
 
57
+ public hasResumePosition() {
58
+ return true;
59
+ }
60
+
57
61
  getContentPosition = () => {
58
62
  return this.playerState.contentPosition;
59
63
  };
@@ -221,11 +225,13 @@ export class PlayerNative extends Player {
221
225
  };
222
226
 
223
227
  onVideoLoad = (event) => {
224
- if (this.startPosition) {
225
- this.seekTo(this.startPosition);
226
- this.startPosition = null;
228
+ this.updateContentDuration(event);
227
229
 
230
+ if (this.startPosition && !this.playerState.isInAdBreak) {
228
231
  super.onVideoLoad({ ...event, ignoreContinueWatching: true });
232
+
233
+ this.seekTo(this.startPosition);
234
+ this.startPosition = null;
229
235
  } else {
230
236
  super.onVideoLoad(event);
231
237
  }
@@ -249,6 +255,15 @@ export class PlayerNative extends Player {
249
255
  }
250
256
  };
251
257
 
258
+ onAdBreakEnd = (event) => {
259
+ super.onAdBreakEnd(event);
260
+
261
+ if (this.startPosition) {
262
+ this.seekTo(this.startPosition);
263
+ this.startPosition = null;
264
+ }
265
+ };
266
+
252
267
  // This function is called when native player used directly without wrapped component,
253
268
  // Since event from native comes with wrapped in NativeEvent property
254
269
  getNativeViewListener = (): QuickBrickPlayer.SharedPlayerCallBacks => {
@@ -3,12 +3,18 @@ import { OverlaysObserver } from "./OverlayObserver/OverlaysObserver";
3
3
  import { useFeedLoader } from "../../reactHooks";
4
4
  import { usePlayer } from "./usePlayer";
5
5
 
6
+ const getLiveUrl = (url: string) => {
7
+ // Use the replace method with a regular expression to replace all instances of subClass with livePage
8
+ return url.replace(new RegExp("subClass", "g"), "livePage");
9
+ };
10
+
6
11
  export const useTitleSummaryOverlay = (
7
- feedUrl,
8
12
  entry,
9
13
  isLive = false,
10
14
  interval: number
11
15
  ) => {
16
+ // TODO: Have generic place for live url from configuration. Currently its hardcoded for client.
17
+ const feedUrl = getLiveUrl(entry?.extensions?.live?.channel_src ?? "");
12
18
  const player = usePlayer();
13
19
  const [title, setTitle] = useState<string | number>(entry?.title || "");
14
20
  const [summary, setSummary] = useState<string | number>(entry?.summary || "");
@@ -0,0 +1,33 @@
1
+ import { isFirstComponentGallery } from "../index";
2
+
3
+ describe("isFirstComponentGallery", () => {
4
+ it("should return true if the first component is a gallery", () => {
5
+ const components = [
6
+ { component_type: "gallery-qb" },
7
+ { component_type: "other-component" },
8
+ ];
9
+
10
+ expect(isFirstComponentGallery(components)).toBe(true);
11
+ });
12
+
13
+ it("should return false if the first component is not a gallery", () => {
14
+ const components = [
15
+ { component_type: "other-component" },
16
+ { component_type: "gallery-qb" },
17
+ ];
18
+
19
+ expect(isFirstComponentGallery(components)).toBe(false);
20
+ });
21
+
22
+ it("should return false if the components array is empty", () => {
23
+ const components = [];
24
+
25
+ expect(isFirstComponentGallery(components)).toBe(false);
26
+ });
27
+
28
+ it("should return false if the components array is undefined", () => {
29
+ const components = undefined;
30
+
31
+ expect(isFirstComponentGallery(components)).toBe(false);
32
+ });
33
+ });
@@ -0,0 +1,33 @@
1
+ import { isFirstComponentScreenPicker } from "..";
2
+
3
+ describe("isFirstComponentScreenPicker", () => {
4
+ it("should return true if the first component screen-picker", () => {
5
+ const items = [{ component_type: "screen-picker-qb-tv" }];
6
+ expect(isFirstComponentScreenPicker(items)).toBe(true);
7
+ });
8
+
9
+ it("should return false if the first component type is not screen-picker", () => {
10
+ const items = [{ component_type: "other-component" }];
11
+ expect(isFirstComponentScreenPicker(items)).toBe(false);
12
+ });
13
+
14
+ it("should return false if the component type is undefined", () => {
15
+ const items = [{ component_type: undefined }];
16
+ expect(isFirstComponentScreenPicker(items)).toBe(false);
17
+ });
18
+
19
+ it("should return false if the item is null", () => {
20
+ const items = null;
21
+ expect(isFirstComponentScreenPicker(items)).toBe(false);
22
+ });
23
+
24
+ it("should return false if the item is undefined", () => {
25
+ const items = undefined;
26
+ expect(isFirstComponentScreenPicker(items)).toBe(false);
27
+ });
28
+
29
+ it("should return false if the item is an empty object", () => {
30
+ const items = {};
31
+ expect(isFirstComponentScreenPicker(items)).toBe(false);
32
+ });
33
+ });
@@ -0,0 +1,33 @@
1
+ import { isGallery } from "..";
2
+
3
+ describe("isGallery", () => {
4
+ it("should return true if the component type is 'gallery-qb'", () => {
5
+ const item = { component_type: "gallery-qb" };
6
+ expect(isGallery(item)).toBe(true);
7
+ });
8
+
9
+ it("should return false if the component type is not 'gallery-qb'", () => {
10
+ const item = { component_type: "other-component" };
11
+ expect(isGallery(item)).toBe(false);
12
+ });
13
+
14
+ it("should return false if the component type is undefined", () => {
15
+ const item = { component_type: undefined };
16
+ expect(isGallery(item)).toBe(false);
17
+ });
18
+
19
+ it("should return false if the item is null", () => {
20
+ const item = null;
21
+ expect(isGallery(item)).toBe(false);
22
+ });
23
+
24
+ it("should return false if the item is undefined", () => {
25
+ const item = undefined;
26
+ expect(isGallery(item)).toBe(false);
27
+ });
28
+
29
+ it("should return false if the item is an empty object", () => {
30
+ const item = {};
31
+ expect(isGallery(item)).toBe(false);
32
+ });
33
+ });
@@ -0,0 +1,33 @@
1
+ import { isScreenPicker } from "..";
2
+
3
+ describe("isScreenPicker", () => {
4
+ it("should return true if the component type is 'screen-picker-qb-tv'", () => {
5
+ const item = { component_type: "screen-picker-qb-tv" };
6
+ expect(isScreenPicker(item)).toBe(true);
7
+ });
8
+
9
+ it("should return false if the component type is not 'screen-picker-qb-tv'", () => {
10
+ const item = { component_type: "other-component" };
11
+ expect(isScreenPicker(item)).toBe(false);
12
+ });
13
+
14
+ it("should return false if the component type is undefined", () => {
15
+ const item = { component_type: undefined };
16
+ expect(isScreenPicker(item)).toBe(false);
17
+ });
18
+
19
+ it("should return false if the item is null", () => {
20
+ const item = null;
21
+ expect(isScreenPicker(item)).toBe(false);
22
+ });
23
+
24
+ it("should return false if the item is undefined", () => {
25
+ const item = undefined;
26
+ expect(isScreenPicker(item)).toBe(false);
27
+ });
28
+
29
+ it("should return false if the item is an empty object", () => {
30
+ const item = {};
31
+ expect(isScreenPicker(item)).toBe(false);
32
+ });
33
+ });