@applicaster/zapp-react-native-utils 15.1.4 → 16.0.0-alpha.1042942411

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 (176) hide show
  1. package/README.md +0 -6
  2. package/actionUtils/index.ts +7 -0
  3. package/actionsExecutor/ActionExecutor.ts +8 -10
  4. package/actionsExecutor/ActionExecutorContext.tsx +45 -340
  5. package/actionsExecutor/actions/appRestart.ts +24 -0
  6. package/actionsExecutor/actions/confirmDialog.ts +53 -0
  7. package/actionsExecutor/actions/index.ts +30 -0
  8. package/actionsExecutor/actions/localStorageRemove.ts +27 -0
  9. package/actionsExecutor/actions/localStorageSet.ts +28 -0
  10. package/actionsExecutor/actions/localStorageToggleFlag.ts +28 -0
  11. package/actionsExecutor/actions/navigateToScreen.ts +123 -0
  12. package/actionsExecutor/actions/refreshComponent.ts +79 -0
  13. package/actionsExecutor/actions/screenSetVariable.ts +35 -0
  14. package/actionsExecutor/actions/screenToggleFlag.ts +38 -0
  15. package/actionsExecutor/actions/sendCloudEvent.ts +88 -0
  16. package/actionsExecutor/actions/sessionStorageRemove.ts +19 -0
  17. package/actionsExecutor/actions/sessionStorageSet.ts +20 -0
  18. package/actionsExecutor/actions/sessionStorageToggleFlag.ts +15 -0
  19. package/actionsExecutor/actions/showToast.ts +31 -0
  20. package/actionsExecutor/actions/switchLayout.ts +40 -0
  21. package/actionsExecutor/feedDecorator.ts +6 -6
  22. package/actionsExecutor/types.ts +59 -0
  23. package/adsUtils/__tests__/createVMAP.test.ts +419 -0
  24. package/adsUtils/index.ts +2 -2
  25. package/analyticsUtils/README.md +1 -1
  26. package/appDataUtils/__tests__/urlScheme.test.ts +678 -0
  27. package/appUtils/HooksManager/__tests__/__snapshots__/hooksManager.test.js.snap +0 -188
  28. package/appUtils/HooksManager/__tests__/hooksManager.test.js +16 -2
  29. package/appUtils/HooksManager/index.ts +12 -18
  30. package/appUtils/RiverFocusManager/{index.js → index.ts} +25 -18
  31. package/appUtils/accessibilityManager/const.ts +4 -0
  32. package/appUtils/accessibilityManager/utils.ts +0 -1
  33. package/appUtils/contextKeysManager/__tests__/getKeys/failure.test.ts +7 -2
  34. package/appUtils/contextKeysManager/__tests__/getKeys/success.test.ts +48 -0
  35. package/appUtils/contextKeysManager/contextResolver.ts +51 -22
  36. package/appUtils/contextKeysManager/index.ts +65 -10
  37. package/appUtils/contextKeysManager/utils/index.ts +38 -25
  38. package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +3 -0
  39. package/appUtils/focusManager/index.ios.ts +43 -4
  40. package/appUtils/focusManager/treeDataStructure/Tree/__tests__/Tree.test.js +46 -0
  41. package/appUtils/focusManager/treeDataStructure/Tree/index.js +18 -18
  42. package/appUtils/focusManagerAux/utils/index.ios.ts +122 -0
  43. package/appUtils/focusManagerAux/utils/index.ts +11 -3
  44. package/appUtils/focusManagerAux/utils/utils.ios.ts +202 -3
  45. package/appUtils/keyCodes/keys/keys.web.ts +1 -4
  46. package/appUtils/orientationHelper.ts +2 -4
  47. package/appUtils/platform/platformUtils.ts +12 -1
  48. package/appUtils/playerManager/player.ts +4 -0
  49. package/appUtils/playerManager/playerNative.ts +30 -21
  50. package/appUtils/playerManager/usePlayerState.tsx +14 -2
  51. package/bottomSheet/__tests__/scrollContext.test.tsx +35 -0
  52. package/bottomSheet/index.ts +17 -0
  53. package/cloudEventsUtils/__tests__/index.test.ts +529 -0
  54. package/cloudEventsUtils/index.ts +65 -1
  55. package/componentsUtils/index.ts +8 -0
  56. package/configurationUtils/__tests__/manifestKeyParser.test.ts +26 -26
  57. package/enumUtils/__tests__/getEnumKeyByEnumValue.test.ts +207 -0
  58. package/errorUtils/__tests__/GeneralError.test.ts +97 -0
  59. package/errorUtils/__tests__/HttpStatusCode.test.ts +344 -0
  60. package/errorUtils/__tests__/MissingPluginError.test.ts +113 -0
  61. package/errorUtils/__tests__/NetworkError.test.ts +202 -0
  62. package/errorUtils/__tests__/getParsedResponse.test.ts +188 -0
  63. package/errorUtils/__tests__/invariant.test.ts +112 -0
  64. package/focusManager/aux/index.ts +1 -1
  65. package/headersUtils/__tests__/headersUtils.test.js +11 -1
  66. package/headersUtils/index.ts +2 -1
  67. package/manifestUtils/_internals/__tests__/index.test.js +41 -0
  68. package/manifestUtils/_internals/index.js +33 -0
  69. package/manifestUtils/defaultManifestConfigurations/generalContent.js +35 -0
  70. package/manifestUtils/defaultManifestConfigurations/player.js +63 -5
  71. package/manifestUtils/index.js +3 -1
  72. package/manifestUtils/keys.js +236 -0
  73. package/manifestUtils/mobileAction/button/__tests__/mobileActionButton.test.js +168 -0
  74. package/manifestUtils/mobileAction/button/index.js +156 -0
  75. package/manifestUtils/mobileAction/container/__tests__/mobileActionButtonsContainer.test.js +102 -0
  76. package/manifestUtils/mobileAction/container/index.js +75 -0
  77. package/manifestUtils/mobileAction/groups/__tests__/buildMobileActionButtonGroups.test.js +127 -0
  78. package/manifestUtils/mobileAction/groups/defaults.js +78 -0
  79. package/manifestUtils/mobileAction/groups/index.js +80 -0
  80. package/manifestUtils/tvAction/container/index.js +1 -1
  81. package/numberUtils/__tests__/isMinusZero.test.ts +20 -0
  82. package/numberUtils/__tests__/isZero.test.ts +27 -0
  83. package/numberUtils/__tests__/requireNumber.test.ts +50 -0
  84. package/numberUtils/__tests__/toNumber.test.ts +54 -0
  85. package/numberUtils/__tests__/toNumberWithDefault.test.ts +64 -0
  86. package/numberUtils/__tests__/toNumberWithDefaultZero.test.ts +48 -0
  87. package/numberUtils/__tests__/toPositiveNumber.test.ts +193 -0
  88. package/numberUtils/index.ts +46 -5
  89. package/package.json +4 -4
  90. package/pluginUtils/index.ts +4 -0
  91. package/reactHooks/actions/index.ts +51 -1
  92. package/reactHooks/advertising/index.ts +2 -2
  93. package/reactHooks/analytics/__tests__/useSendAnalyticsOnPress.test.ts +537 -0
  94. package/reactHooks/app/__tests__/useAppState.test.ts +1 -1
  95. package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +1 -1
  96. package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +1 -2
  97. package/reactHooks/cell-click/__tests__/index.test.js +1 -3
  98. package/reactHooks/cell-click/index.ts +15 -7
  99. package/reactHooks/configuration/__tests__/index.test.tsx +1 -1
  100. package/reactHooks/connection/__tests__/index.test.js +1 -1
  101. package/reactHooks/debugging/__tests__/index.test.js +4 -4
  102. package/reactHooks/dev/__tests__/useReRenderLog.test.ts +188 -0
  103. package/reactHooks/device/useIsTablet.tsx +14 -19
  104. package/reactHooks/device/useMemoizedIsTablet.ts +3 -3
  105. package/reactHooks/feed/__mocks__/useMarkPipesDataStale.ts +4 -0
  106. package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +32 -23
  107. package/reactHooks/feed/__tests__/useBuildPipesUrl.test.tsx +19 -19
  108. package/reactHooks/feed/__tests__/useEntryScreenId.test.tsx +4 -1
  109. package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +42 -30
  110. package/reactHooks/feed/__tests__/useInflatedUrl.test.tsx +26 -1
  111. package/reactHooks/feed/index.ts +5 -3
  112. package/reactHooks/feed/useBatchLoading.ts +17 -3
  113. package/reactHooks/feed/useEntryScreenId.ts +2 -2
  114. package/reactHooks/feed/{usePipesCacheReset.ts → useMarkPipesDataStale.ts} +14 -4
  115. package/reactHooks/flatList/useLoadNextPageIfNeeded.ts +13 -16
  116. package/reactHooks/layout/__tests__/index.test.tsx +1 -1
  117. package/reactHooks/layout/__tests__/useLayoutVersion.test.tsx +1 -1
  118. package/reactHooks/layout/useDimensions/__tests__/{useDimensions.test.ts → useDimensions.test.tsx} +105 -25
  119. package/reactHooks/layout/useDimensions/useDimensions.ts +2 -2
  120. package/reactHooks/navigation/__tests__/index.test.tsx +2 -4
  121. package/reactHooks/navigation/index.ts +7 -6
  122. package/reactHooks/navigation/useRoute.ts +8 -6
  123. package/reactHooks/player/TVSeekControlller/TVSeekController.ts +27 -10
  124. package/reactHooks/player/__tests__/useAutoSeek._test.tsx +1 -1
  125. package/reactHooks/player/__tests__/useTapSeek._test.ts +1 -1
  126. package/reactHooks/resolvers/__tests__/useCellResolver.test.tsx +1 -1
  127. package/reactHooks/resolvers/__tests__/useComponentResolver.test.tsx +1 -1
  128. package/reactHooks/resolvers/useCellResolver.ts +6 -2
  129. package/reactHooks/resolvers/useComponentResolver.ts +8 -2
  130. package/reactHooks/screen/__tests__/useCurrentScreenData.test.tsx +2 -2
  131. package/reactHooks/screen/__tests__/useIsStandaloneFullscreen.test.ts +114 -0
  132. package/reactHooks/screen/__tests__/useScreenBackgroundColor.test.tsx +1 -1
  133. package/reactHooks/screen/__tests__/useScreenData.test.tsx +1 -1
  134. package/reactHooks/screen/__tests__/useTargetScreenData.test.tsx +12 -4
  135. package/reactHooks/screen/useTargetScreenData.ts +4 -2
  136. package/reactHooks/state/useComponentScreenState.ts +1 -1
  137. package/reactHooks/state/useRivers.ts +1 -1
  138. package/reactHooks/ui/__tests__/useFadeOutWhenBlurred.test.ts +580 -0
  139. package/reactHooks/usePluginConfiguration.ts +2 -2
  140. package/reactHooks/utils/__tests__/index.test.js +1 -1
  141. package/reactHooks/utils/index.ts +3 -2
  142. package/rectUtils/__tests__/index.test.ts +549 -0
  143. package/rectUtils/index.ts +2 -2
  144. package/refreshUtils/RefreshCoordinator/__tests__/refreshCoordinator.test.ts +206 -0
  145. package/refreshUtils/RefreshCoordinator/index.ts +245 -0
  146. package/refreshUtils/RefreshCoordinator/utils/__tests__/getDataRefreshConfig.test.ts +104 -0
  147. package/refreshUtils/RefreshCoordinator/utils/index.ts +29 -0
  148. package/riverComponetsMeasurementProvider/index.tsx +1 -1
  149. package/screenPickerUtils/__tests__/index.test.ts +333 -0
  150. package/screenState/__tests__/index.test.ts +1 -1
  151. package/screenUtils/index.ts +3 -0
  152. package/searchUtils/const.ts +7 -0
  153. package/searchUtils/index.ts +3 -0
  154. package/stringUtils/__tests__/stringUtils.test.js +42 -0
  155. package/stringUtils/index.ts +3 -3
  156. package/testUtils/index.tsx +30 -21
  157. package/time/__tests__/BackgroundTimer.test.ts +156 -0
  158. package/time/__tests__/Timer.test.ts +236 -0
  159. package/typeGuards/__tests__/isString.test.ts +21 -0
  160. package/typeGuards/index.ts +4 -0
  161. package/uiActionsRegistrator/index.ts +203 -0
  162. package/utils/__tests__/mergeRight.test.ts +48 -0
  163. package/utils/__tests__/path.test.ts +7 -0
  164. package/utils/__tests__/selectors.test.ts +124 -0
  165. package/utils/index.ts +13 -0
  166. package/utils/mergeRight.ts +5 -0
  167. package/utils/path.ts +6 -3
  168. package/utils/pathOr.ts +5 -1
  169. package/utils/selectors.ts +46 -0
  170. package/zappFrameworkUtils/HookCallback/callbackNavigationAction.ts +1 -1
  171. package/zappFrameworkUtils/HookCallback/hookCallbackManifestExtensions.config.js +1 -1
  172. package/zappFrameworkUtils/loginPluginUtils.ts +1 -1
  173. package/reactHooks/componentsMap/index.ts +0 -55
  174. package/reactHooks/feed/__mocks__/usePipesCacheReset.ts +0 -1
  175. package/reactHooks/feed/__tests__/useFeedRefresh.test.tsx +0 -75
  176. package/reactHooks/feed/useFeedRefresh.tsx +0 -65
@@ -0,0 +1,102 @@
1
+ import { mobileActionButtonsContainer } from "..";
2
+
3
+ describe("mobileActionButtonsContainer", () => {
4
+ it("generates container fields with inferred conditionals", () => {
5
+ const result = mobileActionButtonsContainer({
6
+ label: "Mobile Buttons Container",
7
+ description: "container",
8
+ defaults: {
9
+ buttonsEnabled: false,
10
+ position: ["over_image", "text_label_1", "text_label_2"],
11
+ align: "left",
12
+ overImagePosition: "bottom_left",
13
+ marginTop: 0,
14
+ marginRight: 20,
15
+ marginBottom: 0,
16
+ marginLeft: 20,
17
+ stacking: "horizontal",
18
+ horizontalGutter: 8,
19
+ verticalGutter: 8,
20
+ independentStyles: true,
21
+ },
22
+ });
23
+
24
+ expect(result.group).toBe(true);
25
+ expect(result.label).toBe("Mobile Buttons Container");
26
+
27
+ const enabledField = result.fields.find(
28
+ (field) => field.key === "mobile_buttons_container_buttons_enabled"
29
+ );
30
+
31
+ expect(enabledField).toBeTruthy();
32
+ expect(enabledField.type).toBe("switch");
33
+
34
+ const positionField = result.fields.find(
35
+ (field) => field.key === "mobile_buttons_container_position"
36
+ );
37
+
38
+ expect(positionField.options).toEqual([
39
+ "over_image",
40
+ "text_label_1",
41
+ "text_label_2",
42
+ ]);
43
+
44
+ expect(positionField.conditional_fields).toEqual([
45
+ {
46
+ key: "styles/mobile_buttons_container_buttons_enabled",
47
+ condition_value: true,
48
+ },
49
+ ]);
50
+
51
+ const overImagePositionField = result.fields.find(
52
+ (field) => field.key === "mobile_buttons_container_over_image_position"
53
+ );
54
+
55
+ expect(overImagePositionField.rules).toBe("all_conditions");
56
+
57
+ expect(overImagePositionField.conditional_fields).toEqual([
58
+ {
59
+ key: "styles/mobile_buttons_container_buttons_enabled",
60
+ condition_value: true,
61
+ },
62
+ {
63
+ key: "styles/mobile_buttons_container_position",
64
+ condition_value: "over_image",
65
+ },
66
+ ]);
67
+
68
+ const horizontalGutterField = result.fields.find(
69
+ (field) => field.key === "mobile_buttons_container_horizontal_gutter"
70
+ );
71
+
72
+ expect(horizontalGutterField.rules).toBe("all_conditions");
73
+
74
+ expect(horizontalGutterField.conditional_fields).toEqual([
75
+ {
76
+ key: "styles/mobile_buttons_container_buttons_enabled",
77
+ condition_value: true,
78
+ },
79
+ {
80
+ key: "styles/mobile_buttons_container_stacking",
81
+ condition_value: "horizontal",
82
+ },
83
+ ]);
84
+
85
+ const verticalGutterField = result.fields.find(
86
+ (field) => field.key === "mobile_buttons_container_vertical_gutter"
87
+ );
88
+
89
+ expect(verticalGutterField.rules).toBe("all_conditions");
90
+
91
+ expect(verticalGutterField.conditional_fields).toEqual([
92
+ {
93
+ key: "styles/mobile_buttons_container_buttons_enabled",
94
+ condition_value: true,
95
+ },
96
+ {
97
+ key: "styles/mobile_buttons_container_stacking",
98
+ condition_value: "vertical",
99
+ },
100
+ ]);
101
+ });
102
+ });
@@ -0,0 +1,75 @@
1
+ const { mobileActionButtonContainerFields } = require("../../keys");
2
+
3
+ const {
4
+ isKeyHasSuffix,
5
+ toSnakeCase,
6
+ generateFieldsFromDefaultsWithoutPrefixedLabel,
7
+ getKeyWithPrefixGenerator,
8
+ } = require("../../_internals");
9
+
10
+ const { withConditional, createConditionalField } = require("../../fieldUtils");
11
+
12
+ const { fieldsGroup } = require("../../utils");
13
+
14
+ /**
15
+ * Builds the manifest group for the mobile action buttons container and wires
16
+ * child field visibility to the container-level toggles and layout selections.
17
+ *
18
+ * @param {object} options
19
+ * @param {string} options.label
20
+ * @param {string} options.description
21
+ * @param {object} options.defaults
22
+ * @returns {object}
23
+ */
24
+ function mobileActionButtonsContainer({ label, description, defaults }) {
25
+ const containerKeyFromLabel = toSnakeCase(label); // "Mobile Buttons Container" -> "mobile_buttons_container"
26
+
27
+ const generatedFields = generateFieldsFromDefaultsWithoutPrefixedLabel(
28
+ label,
29
+ defaults,
30
+ mobileActionButtonContainerFields(defaults.position)
31
+ );
32
+
33
+ const keyPrefixGenerator = getKeyWithPrefixGenerator(containerKeyFromLabel);
34
+
35
+ const enabledKey = keyPrefixGenerator("buttons_enabled");
36
+ const positionKey = keyPrefixGenerator("position");
37
+ const stackingKey = keyPrefixGenerator("stacking");
38
+
39
+ const defaultConditions = [createConditionalField(enabledKey, true)];
40
+
41
+ const fields = generatedFields.map((field) => {
42
+ const key = field.key;
43
+
44
+ if (isKeyHasSuffix("buttons_enabled", key)) {
45
+ return field;
46
+ }
47
+
48
+ const conditions = [...defaultConditions];
49
+
50
+ // over_image_position depends on [positionKey: over_image]
51
+ if (isKeyHasSuffix("over_image_position", key)) {
52
+ conditions.push(
53
+ createConditionalField(positionKey, defaults.position[0])
54
+ );
55
+ }
56
+
57
+ // horizontal_gutter depends on [stackingKey: horizontal]
58
+ if (isKeyHasSuffix("horizontal_gutter", key)) {
59
+ conditions.push(createConditionalField(stackingKey, "horizontal"));
60
+ }
61
+
62
+ // vertical_gutter depends on [stackingKey: vertical]
63
+ if (isKeyHasSuffix("vertical_gutter", key)) {
64
+ conditions.push(createConditionalField(stackingKey, "vertical"));
65
+ }
66
+
67
+ return withConditional(conditions)(field);
68
+ });
69
+
70
+ return fieldsGroup(label, description, fields);
71
+ }
72
+
73
+ module.exports = {
74
+ mobileActionButtonsContainer,
75
+ };
@@ -0,0 +1,127 @@
1
+ import { buildMobileActionButtonGroups } from "..";
2
+
3
+ describe("buildMobileActionButtonGroups", () => {
4
+ it("builds the container and three button groups with shared defaults", () => {
5
+ const groups = buildMobileActionButtonGroups({
6
+ containerDefaults: {
7
+ position: ["over_image", "text_label_1", "text_label_2"],
8
+ },
9
+ });
10
+
11
+ expect(groups).toHaveLength(4);
12
+
13
+ expect(groups.map((group) => group.label)).toEqual([
14
+ "Mobile Buttons Container",
15
+ "Mobile Button 1",
16
+ "Mobile Button 2",
17
+ "Mobile Button 3",
18
+ ]);
19
+
20
+ const containerFields = groups[0].fields;
21
+
22
+ const positionField = containerFields.find(
23
+ (field) => field.key === "mobile_buttons_container_position"
24
+ );
25
+
26
+ const buttonsEnabledField = containerFields.find(
27
+ (field) => field.key === "mobile_buttons_container_buttons_enabled"
28
+ );
29
+
30
+ expect(positionField.options).toEqual([
31
+ "over_image",
32
+ "text_label_1",
33
+ "text_label_2",
34
+ ]);
35
+
36
+ expect(buttonsEnabledField.initial_value).toBe(false);
37
+
38
+ const button1Fields = groups[1].fields;
39
+
40
+ const button1EnabledField = button1Fields.find(
41
+ (field) => field.key === "mobile_button_1_button_enabled"
42
+ );
43
+
44
+ const button1AssignActionField = button1Fields.find(
45
+ (field) => field.key === "mobile_button_1_assign_action"
46
+ );
47
+
48
+ const button1BackgroundColorField = button1Fields.find(
49
+ (field) => field.key === "mobile_button_1_background_color"
50
+ );
51
+
52
+ expect(button1EnabledField.initial_value).toBe(true);
53
+ expect(button1AssignActionField.initial_value).toBe("navigation_action");
54
+
55
+ expect(button1BackgroundColorField.initial_value).toBe(
56
+ "rgba(254, 20, 72, 1)"
57
+ );
58
+
59
+ const button2Fields = groups[2].fields;
60
+
61
+ const button2EnabledField = button2Fields.find(
62
+ (field) => field.key === "mobile_button_2_button_enabled"
63
+ );
64
+
65
+ const button2AssignActionField = button2Fields.find(
66
+ (field) => field.key === "mobile_button_2_assign_action"
67
+ );
68
+
69
+ expect(button2EnabledField.initial_value).toBe(false);
70
+ expect(button2AssignActionField.initial_value).toBe("secondary_navigation");
71
+
72
+ const button3Fields = groups[3].fields;
73
+
74
+ const button3EnabledField = button3Fields.find(
75
+ (field) => field.key === "mobile_button_3_button_enabled"
76
+ );
77
+
78
+ const button3AssignActionField = button3Fields.find(
79
+ (field) => field.key === "mobile_button_3_assign_action"
80
+ );
81
+
82
+ expect(button3EnabledField.initial_value).toBe(false);
83
+
84
+ expect(button3AssignActionField.initial_value).toBe(
85
+ "local_storage_favourites_action"
86
+ );
87
+ });
88
+
89
+ it("merges container, shared button, and per-button overrides", () => {
90
+ const groups = buildMobileActionButtonGroups({
91
+ containerDefaults: {
92
+ align: "center",
93
+ position: ["over_image"],
94
+ },
95
+ sharedButtonDefaults: {
96
+ assetAlignment: "right",
97
+ },
98
+ buttonOverrides: {
99
+ 2: {
100
+ buttonEnabled: true,
101
+ assignAction: "downloads",
102
+ },
103
+ },
104
+ });
105
+
106
+ const alignField = groups[0].fields.find(
107
+ (field) => field.key === "mobile_buttons_container_align"
108
+ );
109
+
110
+ const button1AssetAlignmentField = groups[1].fields.find(
111
+ (field) => field.key === "mobile_button_1_asset_alignment"
112
+ );
113
+
114
+ const button2EnabledField = groups[2].fields.find(
115
+ (field) => field.key === "mobile_button_2_button_enabled"
116
+ );
117
+
118
+ const button2AssignActionField = groups[2].fields.find(
119
+ (field) => field.key === "mobile_button_2_assign_action"
120
+ );
121
+
122
+ expect(alignField.initial_value).toBe("center");
123
+ expect(button1AssetAlignmentField.initial_value).toBe("right");
124
+ expect(button2EnabledField.initial_value).toBe(true);
125
+ expect(button2AssignActionField.initial_value).toBe("downloads");
126
+ });
127
+ });
@@ -0,0 +1,78 @@
1
+ const DEFAULT_MOBILE_ACTION_BUTTONS_CONTAINER_DEFAULTS = {
2
+ buttonsEnabled: false,
3
+
4
+ align: "left",
5
+ overImagePosition: "bottom_left",
6
+ marginTop: 0,
7
+ marginRight: 20,
8
+ marginBottom: 0,
9
+ marginLeft: 20,
10
+ stacking: "horizontal",
11
+ horizontalGutter: 8,
12
+ verticalGutter: 8,
13
+ independentStyles: true,
14
+ };
15
+
16
+ const DEFAULT_MOBILE_ACTION_BUTTON_SHARED_DEFAULTS = {
17
+ displayMode: "dynamic",
18
+ width: 140,
19
+ contentsAlignment: "center",
20
+ backgroundColor: "rgba(62, 62, 62, 1)",
21
+ focusedBackgroundColor: "rgba(46, 46, 46, 1)",
22
+ borderColor: "rgba(0, 0, 0, 0)",
23
+ focusedBorderColor: "rgba(0, 0, 0, 0)",
24
+ borderSize: 0,
25
+ cornerRadius: 8,
26
+ paddingTop: 14,
27
+ paddingRight: 24,
28
+ paddingBottom: 14,
29
+ paddingLeft: 16,
30
+ assetEnabled: true,
31
+ actionAssetFlavour: "flavour_1",
32
+ assetAlignment: "left",
33
+ assetHeight: 24,
34
+ assetWidth: 24,
35
+ assetMarginTop: 0,
36
+ assetMarginRight: 0,
37
+ assetMarginBottom: 0,
38
+ assetMarginLeft: 0,
39
+ labelEnabled: true,
40
+ fontColor: "rgba(239, 239, 239, 1)",
41
+ focusedFontColor: "rgba(239, 239, 239, 1)",
42
+ iosFontFamily: "Ubuntu-Bold",
43
+ androidFontFamily: "Ubuntu-Bold",
44
+ fontSize: 15,
45
+ lineHeight: 24,
46
+ iosLetterSpacing: -0.2,
47
+ androidLetterSpacing: -0.2,
48
+ textTransform: "default",
49
+ marginTop: 0,
50
+ marginRight: 0,
51
+ marginBottom: 0,
52
+ marginLeft: 0,
53
+ horizontalGutter: 8,
54
+ verticalGutter: 8,
55
+ };
56
+
57
+ const DEFAULT_MOBILE_ACTION_BUTTON_PRESETS = {
58
+ 1: {
59
+ buttonEnabled: true,
60
+ assignAction: "navigation_action",
61
+ backgroundColor: "rgba(254, 20, 72, 1)",
62
+ focusedBackgroundColor: "rgba(213, 8, 54, 1)",
63
+ },
64
+ 2: {
65
+ buttonEnabled: false,
66
+ assignAction: "secondary_navigation",
67
+ },
68
+ 3: {
69
+ buttonEnabled: false,
70
+ assignAction: "local_storage_favourites_action",
71
+ },
72
+ };
73
+
74
+ module.exports = {
75
+ DEFAULT_MOBILE_ACTION_BUTTONS_CONTAINER_DEFAULTS,
76
+ DEFAULT_MOBILE_ACTION_BUTTON_SHARED_DEFAULTS,
77
+ DEFAULT_MOBILE_ACTION_BUTTON_PRESETS,
78
+ };
@@ -0,0 +1,80 @@
1
+ const { mobileActionButtonsContainer } = require("../container");
2
+ const { mobileActionButton } = require("../button");
3
+
4
+ const {
5
+ DEFAULT_MOBILE_ACTION_BUTTONS_CONTAINER_DEFAULTS,
6
+ DEFAULT_MOBILE_ACTION_BUTTON_SHARED_DEFAULTS,
7
+ DEFAULT_MOBILE_ACTION_BUTTON_PRESETS,
8
+ } = require("./defaults");
9
+
10
+ const CONTAINER_GROUP_LABEL = "Mobile Buttons Container"; // NOTE: used as key – "Mobile Buttons Container" -> "mobile_buttons_container"
11
+
12
+ const CONTAINER_GROUP_DESCRIPTION =
13
+ "Configuration for mobile action buttons container";
14
+
15
+ const BUTTON_GROUPS = [
16
+ {
17
+ index: 1,
18
+ label: "Mobile Button 1",
19
+ description: "Primary mobile action button",
20
+ },
21
+ {
22
+ index: 2,
23
+ label: "Mobile Button 2",
24
+ description: "Secondary mobile action button",
25
+ },
26
+ {
27
+ index: 3,
28
+ label: "Mobile Button 3",
29
+ description: "Tertiary mobile action button",
30
+ },
31
+ ];
32
+
33
+ /**
34
+ * Builds the shared mobile action button manifest groups while letting each
35
+ * plugin supply only its supported insertion positions and explicit overrides.
36
+ *
37
+ * @param {object} options
38
+ * @param {object} [options.containerDefaults={}]
39
+ * @param {object} [options.sharedButtonDefaults={}]
40
+ * @param {Record<number, object>} [options.buttonOverrides={}]
41
+ * @returns {object[]}
42
+ */
43
+ function buildMobileActionButtonGroups({
44
+ containerDefaults = {},
45
+ sharedButtonDefaults = {},
46
+ buttonOverrides = {},
47
+ }) {
48
+ const groups = [
49
+ mobileActionButtonsContainer({
50
+ label: CONTAINER_GROUP_LABEL,
51
+ description: CONTAINER_GROUP_DESCRIPTION,
52
+ defaults: {
53
+ ...DEFAULT_MOBILE_ACTION_BUTTONS_CONTAINER_DEFAULTS,
54
+ ...containerDefaults,
55
+ },
56
+ }),
57
+ ];
58
+
59
+ BUTTON_GROUPS.forEach(({ index, label, description }) => {
60
+ groups.push(
61
+ mobileActionButton({
62
+ isFirstButton: index === 1,
63
+ label,
64
+ description,
65
+ defaults: {
66
+ ...DEFAULT_MOBILE_ACTION_BUTTON_SHARED_DEFAULTS,
67
+ ...sharedButtonDefaults,
68
+ ...DEFAULT_MOBILE_ACTION_BUTTON_PRESETS[index],
69
+ ...(buttonOverrides[index] || {}),
70
+ },
71
+ })
72
+ );
73
+ });
74
+
75
+ return groups;
76
+ }
77
+
78
+ module.exports = {
79
+ buildMobileActionButtonGroups,
80
+ };
@@ -38,7 +38,7 @@ function tvActionButtonsContainer({ label, description, defaults }) {
38
38
  generateFieldsFromDefaultsWithoutPrefixedLabel(
39
39
  label,
40
40
  defaults,
41
- tvActionButtonContainerFields(defaults["position"])
41
+ tvActionButtonContainerFields(defaults.position)
42
42
  )
43
43
  );
44
44
 
@@ -0,0 +1,20 @@
1
+ import { isMinusZero } from "..";
2
+
3
+ describe("isMinusZero", () => {
4
+ it("return true for minus zero", () => {
5
+ expect(isMinusZero(-0)).toBe(true);
6
+ });
7
+
8
+ it("return false for positive zero", () => {
9
+ expect(isMinusZero(0)).toBe(false);
10
+ });
11
+
12
+ it("return false for non-zero numbers", () => {
13
+ const inputs = [-1, 1, 100, 0.1, -0.1, Infinity, -Infinity];
14
+ expect.assertions(inputs.length);
15
+
16
+ inputs.forEach((input) => {
17
+ expect(isMinusZero(input)).toBe(false);
18
+ });
19
+ });
20
+ });
@@ -0,0 +1,27 @@
1
+ import { isZero } from "..";
2
+
3
+ describe("isZero", () => {
4
+ it("return true for zero and minus zero", () => {
5
+ expect(isZero(0)).toBe(true);
6
+ expect(isZero(-0)).toBe(true);
7
+ });
8
+
9
+ it("return false for non-zero numbers", () => {
10
+ const inputs = [-1, 1, 100, 0.1, -0.1, Infinity, -Infinity, NaN];
11
+ expect.assertions(inputs.length);
12
+
13
+ inputs.forEach((input) => {
14
+ expect(isZero(input)).toBe(false);
15
+ });
16
+ });
17
+
18
+ it("return false for non-number values", () => {
19
+ const inputs = ["0", "-0", null, undefined, "", {}, [], false, true];
20
+
21
+ expect.assertions(inputs.length);
22
+
23
+ inputs.forEach((input) => {
24
+ expect(isZero(input)).toBe(false);
25
+ });
26
+ });
27
+ });
@@ -0,0 +1,50 @@
1
+ import { requireNumber } from "..";
2
+
3
+ describe("requireNumber", () => {
4
+ it("return number if object key holds a number", () => {
5
+ const inputs = [-1, 1, 100, 0.2];
6
+ expect.assertions(inputs.length);
7
+
8
+ inputs.forEach((input) => {
9
+ const output = requireNumber({ value: input }, "value");
10
+ expect(output).toBe(input);
11
+ });
12
+ });
13
+
14
+ it("return number if object key holds a string as number", () => {
15
+ const inputs = ["-1", "1", "100", "0.2"];
16
+ expect.assertions(inputs.length);
17
+
18
+ inputs.forEach((input) => {
19
+ const output = requireNumber({ value: input }, "value");
20
+ expect(output).toBe(Number(input));
21
+ });
22
+ });
23
+
24
+ it("return null if object key holds zero", () => {
25
+ expect(requireNumber({ value: 0 }, "value")).toBeNull();
26
+ expect(requireNumber({ value: "0" }, "value")).toBeNull();
27
+ });
28
+
29
+ it("return null if object key is missing or not a number", () => {
30
+ const cases = [
31
+ [{}, "value"],
32
+ [{ value: null }, "value"],
33
+ [{ value: undefined }, "value"],
34
+ [{ value: NaN }, "value"],
35
+ [{ value: "" }, "value"],
36
+ [{ value: "vfdvf" }, "value"],
37
+ [{ value: {} }, "value"],
38
+ [{ value: [] }, "value"],
39
+ [null, "value"],
40
+ [undefined, "value"],
41
+ ] as const;
42
+
43
+ expect.assertions(cases.length);
44
+
45
+ cases.forEach(([obj, key]) => {
46
+ const output = requireNumber(obj, key);
47
+ expect(output).toBeNull();
48
+ });
49
+ });
50
+ });
@@ -21,9 +21,38 @@ describe("toNumber", () => {
21
21
  });
22
22
  });
23
23
 
24
+ it("return number if input is boxed string as number", () => {
25
+ const inputs = [
26
+ new String("-1"),
27
+ new String("0"),
28
+ new String("1"),
29
+ Object("100"),
30
+ Object("0.2"),
31
+ ];
32
+
33
+ expect.assertions(inputs.length);
34
+
35
+ inputs.forEach((input) => {
36
+ const output = toNumber(input);
37
+ expect(output).toBe(Number(input));
38
+ });
39
+ });
40
+
41
+ it("return undefined if input is invalid boxed string", () => {
42
+ const inputs = [new String("vfdvf"), new String("5n"), Object("")];
43
+ expect.assertions(inputs.length);
44
+
45
+ inputs.forEach((input) => {
46
+ const output = toNumber(input);
47
+ expect(output).toBeUndefined();
48
+ });
49
+ });
50
+
24
51
  it("return undefined if input is not a number", () => {
25
52
  const inputs = [
26
53
  "vfdvf",
54
+ "5n",
55
+ "-5n",
27
56
  null,
28
57
  undefined,
29
58
  NaN,
@@ -41,4 +70,29 @@ describe("toNumber", () => {
41
70
  expect(output).toBeUndefined();
42
71
  });
43
72
  });
73
+
74
+ describe("BigInt support", () => {
75
+ // Conditional test based on BigInt availability
76
+ const isBigIntSupported = typeof BigInt !== "undefined";
77
+
78
+ if (isBigIntSupported) {
79
+ it("converts BigInt to number when BigInt is supported", () => {
80
+ expect(toNumber(BigInt(5))).toBe(5);
81
+ expect(toNumber(BigInt(0))).toBe(0);
82
+ expect(toNumber(BigInt(-10))).toBe(-10);
83
+ expect(toNumber(BigInt(1000))).toBe(1000);
84
+ });
85
+
86
+ it("handles large BigInt values that may lose precision", () => {
87
+ const largeBigInt = BigInt(Number.MAX_SAFE_INTEGER) + BigInt(1);
88
+ const result = toNumber(largeBigInt);
89
+ expect(result).toBe(Number(largeBigInt));
90
+ });
91
+ } else {
92
+ it("skips BigInt tests when BigInt is not supported", () => {
93
+ // Placeholder test to indicate BigInt is not available
94
+ expect(typeof BigInt).toBe("undefined");
95
+ });
96
+ }
97
+ });
44
98
  });