@applicaster/zapp-react-native-utils 16.0.0-rc.12 → 16.0.0-rc.120

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 (224) hide show
  1. package/actionUtils/index.ts +3 -1
  2. package/actionsExecutor/ActionExecutor.ts +57 -12
  3. package/actionsExecutor/ActionExecutorContext.tsx +113 -314
  4. package/actionsExecutor/__tests__/ActionExecutor.test.ts +71 -0
  5. package/actionsExecutor/__tests__/feedDecorator.test.ts +61 -0
  6. package/actionsExecutor/actions/__tests__/dismissBottomSheet.test.ts +25 -0
  7. package/actionsExecutor/actions/__tests__/goBack.test.ts +62 -0
  8. package/actionsExecutor/actions/__tests__/goHome.test.ts +19 -0
  9. package/actionsExecutor/actions/__tests__/loadItemsFromSource.test.ts +88 -0
  10. package/actionsExecutor/actions/__tests__/navigateToScreen.test.ts +267 -0
  11. package/actionsExecutor/actions/__tests__/openBottomSheet.customContent.test.ts +100 -0
  12. package/actionsExecutor/actions/__tests__/openBottomSheet.inlineItems.test.ts +300 -0
  13. package/actionsExecutor/actions/__tests__/presentScreen.test.ts +127 -0
  14. package/actionsExecutor/actions/__tests__/showAlert.test.ts +49 -0
  15. package/actionsExecutor/actions/__tests__/showToast.test.ts +88 -0
  16. package/actionsExecutor/actions/appRestart.ts +24 -0
  17. package/actionsExecutor/actions/confirmDialog.ts +53 -0
  18. package/actionsExecutor/actions/dismissBottomSheet.ts +23 -0
  19. package/actionsExecutor/actions/goBack.ts +60 -0
  20. package/actionsExecutor/actions/goHome.ts +31 -0
  21. package/actionsExecutor/actions/index.ts +42 -0
  22. package/actionsExecutor/actions/localStorageRemove.ts +27 -0
  23. package/actionsExecutor/actions/localStorageSet.ts +28 -0
  24. package/actionsExecutor/actions/localStorageToggleFlag.ts +28 -0
  25. package/actionsExecutor/actions/navigateToScreen.ts +121 -0
  26. package/actionsExecutor/actions/openBottomSheet.ts +339 -0
  27. package/actionsExecutor/actions/presentScreen.ts +78 -0
  28. package/actionsExecutor/actions/refreshComponent.ts +85 -0
  29. package/actionsExecutor/actions/screenSetVariable.ts +35 -0
  30. package/actionsExecutor/actions/screenToggleFlag.ts +38 -0
  31. package/actionsExecutor/actions/sendCloudEvent.ts +93 -0
  32. package/actionsExecutor/actions/sessionStorageRemove.ts +19 -0
  33. package/actionsExecutor/actions/sessionStorageSet.ts +20 -0
  34. package/actionsExecutor/actions/sessionStorageToggleFlag.ts +15 -0
  35. package/actionsExecutor/actions/showAlert.ts +51 -0
  36. package/actionsExecutor/actions/showToast.ts +87 -0
  37. package/actionsExecutor/actions/switchLayout.ts +40 -0
  38. package/actionsExecutor/consts.ts +27 -0
  39. package/actionsExecutor/feedDecorator.ts +6 -6
  40. package/actionsExecutor/types.ts +59 -0
  41. package/analyticsUtils/AnalyticsEvents/sendMenuClickEvent.ts +2 -2
  42. package/analyticsUtils/PlayerAnalyticsManager.ts +12 -2
  43. package/analyticsUtils/__tests__/analyticsMapper.test.ts +35 -0
  44. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testACP_events.json +1 -1
  45. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testBlockUnlistedParams_rules.json +1 -1
  46. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testEmptyRenameKeepsName_events.json +4 -0
  47. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testEmptyRenameKeepsName_rules.json +6 -0
  48. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testEventRegex_events.json +3 -9
  49. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testIgnoreOrdering_events.json +18 -0
  50. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testIgnoreOrdering_rules.json +16 -0
  51. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testRegexEventNoFallback_events.json +4 -0
  52. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testRegexEventNoFallback_rules.json +6 -0
  53. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testRegexMultiGroupRename_events.json +4 -0
  54. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testRegexMultiGroupRename_rules.json +6 -0
  55. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testRegexNonNamedFullMatch_events.json +4 -0
  56. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testRegexNonNamedFullMatch_rules.json +6 -0
  57. package/analyticsUtils/__tests__/fixtures/index.js +20 -0
  58. package/analyticsUtils/analyticsMapper.ts +4 -1
  59. package/analyticsUtils/playerAnalyticsTracker.ts +26 -3
  60. package/appUtils/HooksManager/index.ts +9 -0
  61. package/appUtils/contextKeysManager/__tests__/getKey/failure.test.ts +1 -1
  62. package/appUtils/contextKeysManager/__tests__/removeKey/failure.test.ts +1 -1
  63. package/appUtils/contextKeysManager/__tests__/setKey/failure/invalidKey.test.ts +4 -4
  64. package/appUtils/contextKeysManager/index.ts +1 -1
  65. package/appUtils/playerManager/OverlayObserver/OverlaysObserver.ts +142 -59
  66. package/appUtils/playerManager/OverlayObserver/__tests__/getUpcomingQueue.test.ts +67 -0
  67. package/appUtils/playerManager/OverlayObserver/__tests__/liveContent.test.ts +205 -0
  68. package/appUtils/playerManager/OverlayObserver/__tests__/utils.test.ts +56 -0
  69. package/appUtils/playerManager/OverlayObserver/getUpcomingQueue.android.tv.ts +4 -0
  70. package/appUtils/playerManager/OverlayObserver/getUpcomingQueue.ios.tv.ts +4 -0
  71. package/appUtils/playerManager/OverlayObserver/getUpcomingQueue.ts +24 -0
  72. package/appUtils/playerManager/OverlayObserver/getUpcomingQueue.web.ts +4 -0
  73. package/appUtils/playerManager/OverlayObserver/utils.ts +51 -20
  74. package/appUtils/playerManager/__tests__/playerContent.test.ts +403 -0
  75. package/appUtils/playerManager/__tests__/playerFactory.test.ts +1 -1
  76. package/appUtils/playerManager/__tests__/playerNative.test.ts +114 -0
  77. package/appUtils/playerManager/__tests__/playerNativeCommand.test.ts +62 -0
  78. package/appUtils/playerManager/__tests__/playerToolbox.test.ts +97 -0
  79. package/appUtils/playerManager/__tests__/usePlayerContent.test.tsx +64 -0
  80. package/appUtils/playerManager/__tests__/usePlayerControllerSetup.test.tsx +62 -0
  81. package/appUtils/playerManager/{conts.ts → const.ts} +33 -0
  82. package/appUtils/playerManager/index.ts +8 -1
  83. package/appUtils/playerManager/player.ts +125 -3
  84. package/appUtils/playerManager/playerFactory.ts +1 -1
  85. package/appUtils/playerManager/playerNative.ts +28 -4
  86. package/appUtils/playerManager/playerNativeCommand.ts +3 -6
  87. package/appUtils/playerManager/playerToolbox.ts +157 -0
  88. package/appUtils/playerManager/usePlayerContent.tsx +43 -0
  89. package/appUtils/playerManager/usePlayerControllerSetup.tsx +7 -1
  90. package/appUtils/playerManager/userLanguagePreference.ts +1 -1
  91. package/arrayUtils/__tests__/{anyThruthy.test.ts → anyTruthy.test.ts} +8 -0
  92. package/arrayUtils/__tests__/arrayUtils.test.ts +165 -108
  93. package/arrayUtils/__tests__/isEmptyArray.test.ts +11 -0
  94. package/arrayUtils/__tests__/isFilledArray.test.ts +12 -0
  95. package/arrayUtils/__tests__/isFirst.test.ts +17 -0
  96. package/arrayUtils/__tests__/isIndexInRange.test.ts +14 -0
  97. package/arrayUtils/__tests__/isLast.test.ts +31 -0
  98. package/arrayUtils/__tests__/makeListOf.test.ts +31 -0
  99. package/arrayUtils/__tests__/makeListOfIndexes.test.ts +20 -0
  100. package/arrayUtils/__tests__/reorderByIds.test.ts +63 -0
  101. package/arrayUtils/__tests__/sample.test.ts +61 -0
  102. package/arrayUtils/index.ts +119 -20
  103. package/bottomSheet/__tests__/scrollContext.test.tsx +35 -0
  104. package/bottomSheet/index.ts +17 -0
  105. package/cellUtils/__tests__/getButtonsEnabled.test.ts +28 -0
  106. package/cellUtils/__tests__/getCellNavigationEnabled.test.ts +36 -0
  107. package/cellUtils/__tests__/getIsCellNavigationEnabled.test.ts +35 -0
  108. package/cellUtils/__tests__/isCellNavigationEnabled.test.ts +21 -0
  109. package/cellUtils/index.ts +73 -8
  110. package/colorUtils/__tests__/isTransparentColor.test.ts +76 -0
  111. package/colorUtils/__tests__/isValidColor.test.ts +70 -0
  112. package/colorUtils/index.ts +50 -0
  113. package/configurationUtils/__tests__/manifestKeyParser.test.ts +21 -0
  114. package/configurationUtils/__tests__/modalBlocksParser.test.ts +392 -0
  115. package/configurationUtils/manifestKeyParser.ts +50 -10
  116. package/configurationUtils/modalBlocksParser.ts +252 -0
  117. package/entryActions/__tests__/buildEntryActions.test.ts +235 -0
  118. package/entryActions/aliasPresentation.ts +108 -0
  119. package/entryActions/buildEntryActions.ts +167 -0
  120. package/entryActions/index.ts +15 -0
  121. package/entryActions/types.ts +39 -0
  122. package/focusManager/FocusManager.ts +26 -0
  123. package/focusManager/__tests__/FocusManager.test.ts +9 -0
  124. package/localizationUtils/index.ts +12 -6
  125. package/logger/__tests__/xrayToolbox.test.ts +69 -0
  126. package/logger/xrayToolbox.ts +67 -0
  127. package/manifestUtils/__tests__/player.audioControls.test.js +158 -0
  128. package/manifestUtils/_internals/index.js +14 -3
  129. package/manifestUtils/defaultManifestConfigurations/generalContent.js +41 -0
  130. package/manifestUtils/defaultManifestConfigurations/player.js +357 -20
  131. package/manifestUtils/fieldUtils/__tests__/fieldUtils.test.js +84 -0
  132. package/manifestUtils/fieldUtils/index.js +125 -0
  133. package/manifestUtils/index.js +3 -0
  134. package/manifestUtils/keys.js +15 -1
  135. package/manifestUtils/mobileAction/button/index.js +16 -0
  136. package/manifestUtils/mobileAction/container/__tests__/mobileActionButtonsContainer.test.js +21 -0
  137. package/manifestUtils/mobileAction/container/index.js +3 -1
  138. package/manifestUtils/mobileAction/groups/__tests__/buildMobileActionButtonGroups.test.js +1 -1
  139. package/manifestUtils/mobileAction/groups/defaults.js +5 -2
  140. package/manifestUtils/modalBlocks.js +304 -0
  141. package/manifestUtils/platformIsTV.js +1 -0
  142. package/modalState/ContentViewModel.ts +113 -0
  143. package/modalState/EditableCollection.ts +17 -0
  144. package/modalState/EditableCollectionRegistry.ts +51 -0
  145. package/modalState/ModalOrchestrator.ts +212 -0
  146. package/modalState/RemoteEditableCollection.ts +235 -0
  147. package/modalState/__tests__/ContentViewModel.editable.test.ts +69 -0
  148. package/modalState/__tests__/ContentViewModel.test.ts +165 -0
  149. package/modalState/__tests__/EditableCollectionRegistry.test.ts +112 -0
  150. package/modalState/__tests__/ModalOrchestrator.phase3.test.ts +63 -0
  151. package/modalState/__tests__/RemoteEditableCollection.test.ts +415 -0
  152. package/modalState/__tests__/index.test.ts +30 -1
  153. package/modalState/__tests__/useBottomSheetContent.test.ts +349 -0
  154. package/modalState/__tests__/useModalStoreState.test.ts +142 -0
  155. package/modalState/index.ts +35 -83
  156. package/modalState/store.ts +109 -0
  157. package/modalState/types.ts +162 -0
  158. package/modalState/useBottomSheetContent.ts +109 -0
  159. package/navigationUtils/xrayNavigator.ts +12 -0
  160. package/numberUtils/__tests__/isMinusZero.test.ts +20 -0
  161. package/numberUtils/__tests__/isZero.test.ts +27 -0
  162. package/numberUtils/__tests__/requireNumber.test.ts +50 -0
  163. package/numberUtils/__tests__/toNumber.test.ts +27 -0
  164. package/numberUtils/__tests__/toNumberWithDefault.test.ts +64 -0
  165. package/numberUtils/__tests__/toNumberWithDefaultZero.test.ts +48 -0
  166. package/numberUtils/index.ts +27 -8
  167. package/package.json +2 -2
  168. package/pipesUtils/__tests__/buildUrlWithQuery.test.ts +33 -0
  169. package/pipesUtils/__tests__/withPipesEndpoint.test.tsx +56 -0
  170. package/pipesUtils/index.ts +1 -0
  171. package/pipesUtils/withPipesEndpoint.tsx +54 -0
  172. package/playerUtils/__tests__/contentDataKeys.test.ts +297 -0
  173. package/playerUtils/__tests__/resolvePlayerActions.test.ts +138 -0
  174. package/playerUtils/__tests__/resolvePlayerTitleSubtitle.test.ts +180 -0
  175. package/playerUtils/contentDataKeys.ts +134 -0
  176. package/playerUtils/index.ts +37 -16
  177. package/playerUtils/isAudioItem.ts +26 -0
  178. package/playerUtils/resolvePlayerActions.ts +71 -0
  179. package/playerUtils/resolvePlayerTitleSubtitle.ts +76 -0
  180. package/pluginUtils/__tests__/pluginUtils.test.js +32 -0
  181. package/pluginUtils/index.ts +29 -0
  182. package/reactHooks/__tests__/getPluginConfiguration.test.ts +82 -0
  183. package/reactHooks/actions/index.ts +117 -2
  184. package/reactHooks/cell-click/__tests__/index.test.js +64 -0
  185. package/reactHooks/cell-click/index.ts +39 -24
  186. package/reactHooks/connection/index.tsx +8 -5
  187. package/reactHooks/feed/__mocks__/useMarkPipesDataStale.ts +4 -0
  188. package/reactHooks/feed/__tests__/useInflatedUrl.test.tsx +25 -0
  189. package/reactHooks/feed/__tests__/useMarkPipesDataStale.test.tsx +243 -0
  190. package/reactHooks/feed/index.ts +5 -1
  191. package/reactHooks/feed/useBatchLoading.ts +9 -1
  192. package/reactHooks/feed/useMarkPipesDataStale.ts +97 -0
  193. package/reactHooks/index.ts +2 -0
  194. package/reactHooks/layout/index.ts +1 -1
  195. package/reactHooks/navigation/__mocks__/index.ts +4 -0
  196. package/reactHooks/navigation/__tests__/index.test.tsx +176 -1
  197. package/reactHooks/navigation/__tests__/useIsScreenActive.test.ts +42 -0
  198. package/reactHooks/navigation/__tests__/usePresentScreen.test.ts +154 -0
  199. package/reactHooks/navigation/index.ts +3 -1
  200. package/reactHooks/navigation/useIsScreenActive.ts +29 -8
  201. package/reactHooks/navigation/usePresentScreen.ts +157 -0
  202. package/reactHooks/navigation/useRoute.ts +22 -5
  203. package/reactHooks/state/useComponentScreenState.ts +1 -1
  204. package/reactHooks/usePluginConfiguration.ts +88 -47
  205. package/reactHooks/utils/__tests__/index.test.js +22 -2
  206. package/reactHooks/utils/index.ts +13 -2
  207. package/riverComponetsMeasurementProvider/index.tsx +15 -9
  208. package/stringUtils/__tests__/stringUtils.test.js +42 -0
  209. package/stringUtils/index.ts +2 -2
  210. package/uiActionsRegistrator/__tests__/resolveActionIdentifiers.test.ts +93 -0
  211. package/uiActionsRegistrator/__tests__/subscribe.test.ts +133 -0
  212. package/uiActionsRegistrator/__tests__/usableActionItems.test.ts +52 -0
  213. package/uiActionsRegistrator/index.ts +25 -0
  214. package/uiActionsRegistrator/registry.ts +351 -0
  215. package/uiActionsRegistrator/resolveActionIdentifiers.ts +100 -0
  216. package/uiActionsRegistrator/usableActionItems.ts +66 -0
  217. package/zappFrameworkUtils/__tests__/localStorageHelper.test.ts +146 -0
  218. package/zappFrameworkUtils/localStorageHelper.ts +19 -15
  219. package/appUtils/playerManager/usePlayerTitleSummaryOverlay.tsx +0 -56
  220. package/playerUtils/__tests__/getPlayerActionButtons.test.ts +0 -54
  221. package/playerUtils/getPlayerActionButtons.ts +0 -17
  222. package/reactHooks/feed/__mocks__/usePipesCacheReset.ts +0 -1
  223. package/reactHooks/feed/usePipesCacheReset.ts +0 -44
  224. /package/reactHooks/actions/__tests__/{index.test.js → index.test.tsx} +0 -0
@@ -1,7 +1,7 @@
1
1
  import * as R from "ramda";
2
2
  import { dayjs } from "../dateUtils";
3
- import validateColor from "validate-color";
4
3
 
4
+ import { isValidColor } from "@applicaster/zapp-react-native-utils/colorUtils";
5
5
  import { transformColorCode as fixColorHexCode } from "@applicaster/zapp-react-native-utils/transform";
6
6
  import {
7
7
  capitalize,
@@ -12,6 +12,7 @@ import { isWeb } from "@applicaster/zapp-react-native-utils/reactUtils";
12
12
  import { isNotNil } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
13
13
 
14
14
  import { toNumberWithDefault, toNumberWithDefaultZero } from "../numberUtils";
15
+ import { isTrue } from "../booleanUtils";
15
16
 
16
17
  const CUSTOM_KEY = "other";
17
18
 
@@ -65,11 +66,38 @@ export const isEmptyOrNil = (value: any): boolean =>
65
66
  */
66
67
  export const getLabel = (dataKeyValue, customDataKeyValue) => (entry) => {
67
68
  const label = getKeyForLabel(dataKeyValue, customDataKeyValue);
68
- const isPath = label && R.length(label) > 0;
69
- const fallback = isEmptyOrNil(label) ? "" : R.head(label);
69
+ const isPath = label && label.length > 0;
70
+
71
+ // An unresolved key is rendered as-is, because a data key is allowed to be a
72
+ // literal label ("More") rather than a path. Fall back to the whole key
73
+ // instead of its first segment: "extensions.section" reads as an unresolved
74
+ // path, while "extensions" reads as a legitimate label and hides the problem.
75
+ const fallback = isEmptyOrNil(label) ? "" : label.join(".");
70
76
 
71
77
  try {
72
- return isPath ? R.pathOr(fallback, label, entry) : fallback;
78
+ if (!isPath) {
79
+ return fallback;
80
+ }
81
+
82
+ const resolved = label.reduce(
83
+ (value, key) => (value == null ? undefined : value[key]),
84
+ entry
85
+ );
86
+
87
+ if (isNotNil(resolved)) {
88
+ return resolved;
89
+ }
90
+
91
+ // A multi-segment key is unambiguously a path, so failing to resolve it is
92
+ // a misconfiguration rather than someone typing a literal label.
93
+ if (label.length > 1) {
94
+ cellUtilsLogger.warning({
95
+ message: `getLabel: data key "${fallback}" was not found on the entry, rendering the key itself`,
96
+ data: { dataKeyValue, customDataKeyValue, entryId: entry?.id },
97
+ });
98
+ }
99
+
100
+ return fallback;
73
101
  } catch (error) {
74
102
  cellUtilsLogger.warning({
75
103
  data: { error, dataKeyValue, customDataKeyValue },
@@ -468,15 +496,13 @@ export const getColorFromData = ({
468
496
  data,
469
497
  valueFromLayout,
470
498
  }: GetColorFromData): string => {
471
- // Temporary hack to fix color validation when alpha is floating point number
472
- // https://github.com/dreamyguy/validate-color/issues/44
473
- if (validateColor(valueFromLayout.replace(".00", ""))) {
499
+ if (isValidColor(valueFromLayout)) {
474
500
  return valueFromLayout;
475
501
  }
476
502
 
477
503
  const pathValue = R.path(valueFromLayout.split("."), data);
478
504
 
479
- if (pathValue && validateColor(pathValue)) {
505
+ if (pathValue && isValidColor(pathValue)) {
480
506
  return pathValue;
481
507
  }
482
508
 
@@ -547,3 +573,42 @@ export const hasTextLabelsEnabled = (
547
573
  return Boolean(value);
548
574
  });
549
575
  };
576
+
577
+ /**
578
+ * Reads the mobile buttons container "buttons enabled" switch from cell styles.
579
+ */
580
+ export const getButtonsEnabled = (styles?: Record<string, any>): boolean => {
581
+ return isTrue(styles?.mobile_buttons_container_buttons_enabled);
582
+ };
583
+
584
+ /**
585
+ * Reads the mobile buttons container "cell navigation enabled" switch from cell styles.
586
+ */
587
+ export const getCellNavigationEnabled = (
588
+ styles?: Record<string, any>
589
+ ): boolean => {
590
+ return isTrue(styles?.mobile_buttons_container_cell_navigation_enabled);
591
+ };
592
+
593
+ /**
594
+ * Whether tapping a cell outside its action buttons triggers the cell's navigation.
595
+ * Always true when the buttons are disabled.
596
+ * @param buttonsEnabled the cell's "buttons enabled" switch value
597
+ * @param cellNavigationEnabled the cell's "cell navigation enabled" switch value
598
+ */
599
+ export const isCellNavigationEnabled = (
600
+ buttonsEnabled: unknown,
601
+ cellNavigationEnabled: unknown
602
+ ): boolean => !isTrue(buttonsEnabled) || isTrue(cellNavigationEnabled);
603
+
604
+ /**
605
+ * Whether tapping a mobile cell outside its action buttons triggers the cell's navigation,
606
+ * read from the cell styles.
607
+ */
608
+ export const getIsCellNavigationEnabled = (
609
+ styles?: Record<string, any>
610
+ ): boolean =>
611
+ isCellNavigationEnabled(
612
+ getButtonsEnabled(styles),
613
+ getCellNavigationEnabled(styles)
614
+ );
@@ -0,0 +1,76 @@
1
+ import { isTransparentColor } from "..";
2
+
3
+ describe("isTransparentColor", () => {
4
+ it("returns true for transparent keyword and rgba colors with zero alpha", () => {
5
+ const transparentColors = [
6
+ "transparent",
7
+ "rgba(0,0,0,0)",
8
+ "rgba(255, 255, 255, 0)",
9
+ "rgba(0,0,0,0.0)",
10
+ "rgba(0,0,0,0.00)",
11
+ "rgba(0,0,0, 0)",
12
+ "hsla(0,0%,0%,0)",
13
+ ];
14
+
15
+ expect.assertions(transparentColors.length);
16
+
17
+ transparentColors.forEach((color) => {
18
+ expect(isTransparentColor(color)).toBe(true);
19
+ });
20
+ });
21
+
22
+ it("returns false for valid colors that are not fully transparent", () => {
23
+ const nonTransparentColors = [
24
+ "red",
25
+ "#fff",
26
+ "#ffffff",
27
+ "rgb(0,0,0)",
28
+ "rgba(0,0,0)",
29
+ "rgba(0,0,0,0.5)",
30
+ "rgba(255, 255, 255, 0.3)",
31
+ "rgba(255, 255, 255, 1.0)",
32
+ "rgba(239,239,239,1.0)",
33
+ "currentColor",
34
+ "inherit",
35
+ "#00000000",
36
+ "#fff0",
37
+ ];
38
+
39
+ expect.assertions(nonTransparentColors.length);
40
+
41
+ nonTransparentColors.forEach((color) => {
42
+ expect(isTransparentColor(color)).toBe(false);
43
+ });
44
+ });
45
+
46
+ it("returns false for case variants of the transparent keyword", () => {
47
+ expect(isTransparentColor("Transparent")).toBe(false);
48
+ expect(isTransparentColor("TRANSPARENT")).toBe(false);
49
+ });
50
+
51
+ it("returns false for invalid color strings", () => {
52
+ const invalidColors = [
53
+ "invalid",
54
+ "",
55
+ "#gggggg",
56
+ "#fff.00",
57
+ "unset",
58
+ " rgba(0,0,0,0) ",
59
+ "transparent ",
60
+ ];
61
+
62
+ expect.assertions(invalidColors.length);
63
+
64
+ invalidColors.forEach((color) => {
65
+ expect(isTransparentColor(color)).toBe(false);
66
+ });
67
+ });
68
+
69
+ it("returns false for nullish and non-string values", () => {
70
+ expect(isTransparentColor(undefined)).toBe(false);
71
+ expect(isTransparentColor(null)).toBe(false);
72
+ expect(isTransparentColor(123)).toBe(false);
73
+ expect(isTransparentColor({})).toBe(false);
74
+ expect(isTransparentColor([])).toBe(false);
75
+ });
76
+ });
@@ -0,0 +1,70 @@
1
+ import { isValidColor } from "..";
2
+
3
+ describe("isValidColor", () => {
4
+ it("returns true for valid hex colors", () => {
5
+ const validHexColors = ["#fff", "#ffffff", "#FF0000", "#000", "#abc123"];
6
+
7
+ expect.assertions(validHexColors.length);
8
+
9
+ validHexColors.forEach((color) => {
10
+ expect(isValidColor(color)).toBe(true);
11
+ });
12
+ });
13
+
14
+ it("returns true for valid rgb and rgba colors", () => {
15
+ const validRgbColors = [
16
+ "rgb(255,0,0)",
17
+ "rgb(255, 0, 0)",
18
+ "rgba(255,0,0,0.5)",
19
+ "rgba(255, 0, 0, 0.5)",
20
+ ];
21
+
22
+ expect.assertions(validRgbColors.length);
23
+
24
+ validRgbColors.forEach((color) => {
25
+ expect(isValidColor(color)).toBe(true);
26
+ });
27
+ });
28
+
29
+ it("returns true for valid named and special CSS colors", () => {
30
+ const validNamedColors = ["red", "transparent", "currentColor", "inherit"];
31
+
32
+ expect.assertions(validNamedColors.length);
33
+
34
+ validNamedColors.forEach((color) => {
35
+ expect(isValidColor(color)).toBe(true);
36
+ });
37
+ });
38
+
39
+ it("returns true for valid hsl colors", () => {
40
+ expect(isValidColor("hsl(0, 100%, 50%)")).toBe(true);
41
+ });
42
+
43
+ it("returns false for invalid color strings", () => {
44
+ const invalidColors = [
45
+ "invalid",
46
+ "",
47
+ "#gggggg",
48
+ "#fff.00",
49
+ "unset",
50
+ undefined,
51
+ null,
52
+ ];
53
+
54
+ expect.assertions(invalidColors.length);
55
+
56
+ invalidColors.forEach((color) => {
57
+ expect(isValidColor(color)).toBe(false);
58
+ });
59
+ });
60
+
61
+ it("returns true for valid rgb and rgba colors", () => {
62
+ const validRgbColors = ["rgba(239,239,239,1.0)"];
63
+
64
+ expect.assertions(validRgbColors.length);
65
+
66
+ validRgbColors.forEach((color) => {
67
+ expect(isValidColor(color)).toBe(true);
68
+ });
69
+ });
70
+ });
@@ -0,0 +1,50 @@
1
+ import validateColor from "validate-color";
2
+ import { isNil } from "@applicaster/zapp-react-native-utils/utils";
3
+ import { isString } from "@applicaster/zapp-react-native-utils/stringUtils";
4
+
5
+ const normalizeRgbaAlpha = (color: string): string => {
6
+ return color.replace(
7
+ /^rgba\(([^)]+),\s*(\d+(?:\.\d+)?)\s*\)$/i,
8
+ (_, rgb, alpha) => {
9
+ const alphaValue = parseFloat(alpha);
10
+
11
+ if (!Number.isFinite(alphaValue) || !Number.isInteger(alphaValue)) {
12
+ return color;
13
+ }
14
+
15
+ return `rgba(${rgb},${alphaValue})`;
16
+ }
17
+ );
18
+ };
19
+
20
+ export const isValidColor = (color: string): boolean => {
21
+ if (isNil(color) || !isString(color)) {
22
+ return false;
23
+ }
24
+
25
+ if (validateColor(color)) {
26
+ return true;
27
+ }
28
+
29
+ // validate-color rejects integer alpha values written as floats (e.g. 1.0)
30
+ // https://github.com/dreamyguy/validate-color/issues/44
31
+ const normalizedColor = normalizeRgbaAlpha(color);
32
+
33
+ return normalizedColor !== color && validateColor(normalizedColor);
34
+ };
35
+
36
+ function isRgbaAlphaZero(color: string): boolean {
37
+ const layers = color
38
+ .replace("rgba(", "")
39
+ .replace(")", "")
40
+ .split(",")
41
+ .map((layer) => layer.trim());
42
+
43
+ return Number(layers[3]) === 0;
44
+ }
45
+
46
+ export const isTransparentColor = (color: string): boolean => {
47
+ return (
48
+ isValidColor(color) && (color === "transparent" || isRgbaAlphaZero(color))
49
+ );
50
+ };
@@ -176,6 +176,27 @@ describe("getAllSpecificStyles", () => {
176
176
  });
177
177
  });
178
178
 
179
+ it("should normalize Studio focus alias onto focused state", () => {
180
+ const outStyles = {};
181
+
182
+ const configuration = {
183
+ button_style_focus_background_color: "#FF3B30",
184
+ };
185
+
186
+ getAllSpecificStyles({
187
+ componentName: "button",
188
+ subComponentName: "",
189
+ configuration,
190
+ outStyles,
191
+ });
192
+
193
+ expect(outStyles.focused).toEqual({
194
+ backgroundColor: "#FF3B30",
195
+ });
196
+
197
+ expect(outStyles.focus).toBeUndefined();
198
+ });
199
+
179
200
  it("should handle selected state", () => {
180
201
  const outStyles = {};
181
202