@applicaster/zapp-react-native-utils 14.0.0-alpha.3259144304 → 14.0.0-alpha.3497255033

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 (109) hide show
  1. package/actionsExecutor/ActionExecutorContext.tsx +60 -84
  2. package/actionsExecutor/ScreenActions.ts +164 -0
  3. package/actionsExecutor/StorageActions.ts +110 -0
  4. package/actionsExecutor/feedDecorator.ts +171 -0
  5. package/actionsExecutor/screenResolver.ts +11 -0
  6. package/analyticsUtils/AnalyticPlayerListener.ts +5 -2
  7. package/analyticsUtils/AnalyticsEvents/helper.ts +81 -0
  8. package/analyticsUtils/AnalyticsEvents/sendOnClickEvent.ts +14 -4
  9. package/analyticsUtils/__tests__/analyticsUtils.test.js +3 -0
  10. package/analyticsUtils/events.ts +8 -0
  11. package/analyticsUtils/playerAnalyticsTracker.ts +2 -1
  12. package/appUtils/HooksManager/Hook.ts +4 -4
  13. package/appUtils/HooksManager/index.ts +11 -1
  14. package/appUtils/accessibilityManager/const.ts +13 -0
  15. package/appUtils/accessibilityManager/hooks.ts +35 -1
  16. package/appUtils/accessibilityManager/index.ts +152 -31
  17. package/appUtils/accessibilityManager/utils.ts +24 -0
  18. package/appUtils/contextKeysManager/contextResolver.ts +42 -1
  19. package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +8 -0
  20. package/appUtils/focusManager/__tests__/focusManager.test.js +1 -1
  21. package/appUtils/focusManager/events.ts +2 -0
  22. package/appUtils/focusManager/index.ios.ts +27 -0
  23. package/appUtils/focusManager/index.ts +86 -11
  24. package/appUtils/focusManager/treeDataStructure/Tree/index.js +1 -1
  25. package/appUtils/focusManagerAux/utils/index.ts +112 -3
  26. package/appUtils/focusManagerAux/utils/utils.ios.ts +35 -0
  27. package/appUtils/platform/platformUtils.ts +33 -3
  28. package/appUtils/playerManager/OverlayObserver/OverlaysObserver.ts +0 -15
  29. package/appUtils/playerManager/conts.ts +21 -0
  30. package/appUtils/playerManager/useChapterMarker.tsx +0 -1
  31. package/appUtils/playerManager/usePlayerControllerSetup.tsx +16 -0
  32. package/arrayUtils/__tests__/allTruthy.test.ts +24 -0
  33. package/arrayUtils/__tests__/anyThruthy.test.ts +24 -0
  34. package/arrayUtils/index.ts +6 -1
  35. package/componentsUtils/__tests__/isTabsScreen.test.ts +38 -0
  36. package/componentsUtils/index.ts +4 -1
  37. package/configurationUtils/__tests__/manifestKeyParser.test.ts +546 -0
  38. package/configurationUtils/index.ts +1 -1
  39. package/configurationUtils/manifestKeyParser.ts +57 -32
  40. package/focusManager/FocusManager.ts +104 -20
  41. package/focusManager/Tree.ts +25 -21
  42. package/focusManager/__tests__/FocusManager.test.ts +50 -8
  43. package/focusManager/aux/index.ts +98 -0
  44. package/focusManager/utils.ts +12 -6
  45. package/index.d.ts +1 -10
  46. package/manifestUtils/defaultManifestConfigurations/player.js +196 -2
  47. package/manifestUtils/index.js +4 -0
  48. package/manifestUtils/keys.js +12 -0
  49. package/manifestUtils/sharedConfiguration/screenPicker/stylesFields.js +6 -0
  50. package/navigationUtils/__tests__/mapContentTypesToRivers.test.ts +130 -0
  51. package/navigationUtils/index.ts +26 -21
  52. package/package.json +2 -3
  53. package/playerUtils/PlayerTTS/PlayerTTS.ts +359 -0
  54. package/playerUtils/PlayerTTS/index.ts +1 -0
  55. package/playerUtils/getPlayerActionButtons.ts +2 -2
  56. package/playerUtils/index.ts +51 -0
  57. package/playerUtils/usePlayerTTS.ts +21 -0
  58. package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +3 -1
  59. package/reactHooks/cell-click/__tests__/index.test.js +3 -0
  60. package/reactHooks/cell-click/index.ts +8 -1
  61. package/reactHooks/debugging/__tests__/index.test.js +0 -1
  62. package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +8 -2
  63. package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +71 -31
  64. package/reactHooks/feed/index.ts +2 -0
  65. package/reactHooks/feed/useBatchLoading.ts +16 -9
  66. package/reactHooks/feed/useFeedLoader.tsx +36 -43
  67. package/reactHooks/feed/useInflatedUrl.ts +23 -29
  68. package/reactHooks/feed/useLoadPipesDataDispatch.ts +63 -0
  69. package/reactHooks/feed/usePipesCacheReset.ts +3 -3
  70. package/reactHooks/flatList/useSequentialRenderItem.tsx +3 -3
  71. package/reactHooks/layout/__tests__/index.test.tsx +3 -1
  72. package/reactHooks/layout/index.ts +1 -1
  73. package/reactHooks/layout/isTablet/index.ts +12 -5
  74. package/reactHooks/layout/useDimensions/__tests__/useDimensions.test.ts +34 -36
  75. package/reactHooks/layout/useDimensions/useDimensions.ts +2 -3
  76. package/reactHooks/layout/useLayoutVersion.ts +5 -5
  77. package/reactHooks/navigation/index.ts +7 -5
  78. package/reactHooks/navigation/useIsScreenActive.ts +9 -5
  79. package/reactHooks/navigation/useRoute.ts +7 -2
  80. package/reactHooks/navigation/useScreenStateStore.ts +8 -0
  81. package/reactHooks/resolvers/__tests__/useCellResolver.test.tsx +4 -0
  82. package/reactHooks/screen/useScreenContext.ts +1 -1
  83. package/reactHooks/state/__tests__/ZStoreProvider.test.tsx +2 -1
  84. package/reactHooks/state/index.ts +1 -1
  85. package/reactHooks/state/useHomeRiver.ts +4 -2
  86. package/reactHooks/state/useRivers.ts +7 -8
  87. package/riverComponetsMeasurementProvider/index.tsx +1 -1
  88. package/screenPickerUtils/index.ts +13 -0
  89. package/services/js2native.ts +1 -0
  90. package/storage/ScreenSingleValueProvider.ts +204 -0
  91. package/storage/ScreenStateMultiSelectProvider.ts +293 -0
  92. package/storage/StorageMultiSelectProvider.ts +192 -0
  93. package/storage/StorageSingleSelectProvider.ts +108 -0
  94. package/time/BackgroundTimer.ts +6 -4
  95. package/utils/__tests__/endsWith.test.ts +30 -0
  96. package/utils/__tests__/find.test.ts +36 -0
  97. package/utils/__tests__/omit.test.ts +19 -0
  98. package/utils/__tests__/path.test.ts +33 -0
  99. package/utils/__tests__/pathOr.test.ts +37 -0
  100. package/utils/__tests__/startsWith.test.ts +30 -0
  101. package/utils/__tests__/take.test.ts +40 -0
  102. package/utils/endsWith.ts +9 -0
  103. package/utils/find.ts +3 -0
  104. package/utils/index.ts +23 -0
  105. package/utils/omit.ts +5 -0
  106. package/utils/path.ts +5 -0
  107. package/utils/pathOr.ts +5 -0
  108. package/utils/startsWith.ts +9 -0
  109. package/utils/take.ts +5 -0
@@ -93,7 +93,7 @@ export const isIndexInRange = (index: number, length: number): boolean => {
93
93
  export const makeListOfIndexes = (size: number): number[] =>
94
94
  Array.from({ length: size }, (_, index) => index);
95
95
 
96
- export const makeListOf = (value: unknown, size: number): number[] => {
96
+ export const makeListOf = <T>(value: T, size: number): T[] => {
97
97
  return Array(size).fill(value);
98
98
  };
99
99
 
@@ -116,3 +116,8 @@ export const sample = (xs: unknown[]): unknown => {
116
116
 
117
117
  return xs[index];
118
118
  };
119
+
120
+ export const allTruthy = (xs: boolean[]) =>
121
+ isFilledArray(xs) && xs.every(Boolean);
122
+
123
+ export const anyTruthy = (xs: boolean[]) => xs.some(Boolean);
@@ -0,0 +1,38 @@
1
+ import { isTabsScreen } from "..";
2
+
3
+ describe("isTabsScreen", () => {
4
+ it("should return true if the component type is 'screen-picker-qb-tv' and tabs_screen=true", () => {
5
+ const item = { component_type: "screen-picker-qb-tv", tabs_screen: true };
6
+ expect(isTabsScreen(item)).toBe(true);
7
+ });
8
+
9
+ it("should return true if the component type is 'screen-picker-qb-tv' and tabs_screen=false", () => {
10
+ const item = { component_type: "screen-picker-qb-tv", tabs_screen: false };
11
+ expect(isTabsScreen(item)).toBe(false);
12
+ });
13
+
14
+ it("should return false if the component type is not 'screen-picker-qb-tv'", () => {
15
+ const item = { component_type: "other-component" };
16
+ expect(isTabsScreen(item)).toBe(false);
17
+ });
18
+
19
+ it("should return false if the component type is undefined", () => {
20
+ const item = { component_type: undefined };
21
+ expect(isTabsScreen(item)).toBe(false);
22
+ });
23
+
24
+ it("should return false if the item is null", () => {
25
+ const item = null;
26
+ expect(isTabsScreen(item)).toBe(false);
27
+ });
28
+
29
+ it("should return false if the item is undefined", () => {
30
+ const item = undefined;
31
+ expect(isTabsScreen(item)).toBe(false);
32
+ });
33
+
34
+ it("should return false if the item is an empty object", () => {
35
+ const item = {};
36
+ expect(isTabsScreen(item)).toBe(false);
37
+ });
38
+ });
@@ -5,7 +5,7 @@ const EMPTY_GROUP_COMPONENT = "empty_group_component";
5
5
 
6
6
  const GALLERY = "gallery-qb";
7
7
 
8
- const SCREEN_PICKER = "screen-picker-qb-tv";
8
+ export const SCREEN_PICKER = "screen-picker-qb-tv";
9
9
 
10
10
  const HORIZONTAL_LIST = "horizontal_list_qb";
11
11
 
@@ -37,3 +37,6 @@ export const isEmptyGroup = (item): boolean =>
37
37
  export const isGroupInfo = (item): boolean =>
38
38
  item?.component_type === GROUP_INFO ||
39
39
  item?.component_type === GROUP_INFO_OLD;
40
+
41
+ export const isTabsScreen = (item): boolean =>
42
+ isScreenPicker(item) && item?.tabs_screen;
@@ -0,0 +1,546 @@
1
+ import { getAllSpecificStyles } from "../manifestKeyParser";
2
+
3
+ jest.mock("@applicaster/zapp-react-native-utils/reactUtils", () => ({
4
+ platformSelect: jest.fn((platforms) => platforms.samsung_tv), // Default to samsung for tests
5
+ }));
6
+
7
+ jest.mock("@applicaster/zapp-react-native-utils/stringUtils", () => ({
8
+ toCamelCase: jest.fn((str: string) => {
9
+ // Simple camelCase implementation for testing
10
+ return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
11
+ }),
12
+ }));
13
+
14
+ describe("getAllSpecificStyles", () => {
15
+ beforeEach(() => {
16
+ jest.clearAllMocks();
17
+ });
18
+
19
+ describe("Basic functionality", () => {
20
+ it("should throw error if outStyles is not provided", () => {
21
+ expect(() => {
22
+ getAllSpecificStyles({
23
+ componentName: "button",
24
+ subComponentName: "",
25
+ configuration: {},
26
+ outStyles: null as any,
27
+ });
28
+ }).toThrow("outStyles is required");
29
+ });
30
+
31
+ it("should throw error if componentName is not provided", () => {
32
+ expect(() => {
33
+ getAllSpecificStyles({
34
+ componentName: "",
35
+ subComponentName: "",
36
+ configuration: {},
37
+ outStyles: {},
38
+ });
39
+ }).toThrow("componentName is required");
40
+ });
41
+
42
+ it("should initialize default key if not present", () => {
43
+ const outStyles = {};
44
+
45
+ getAllSpecificStyles({
46
+ componentName: "button",
47
+ subComponentName: "",
48
+ configuration: {},
49
+ outStyles,
50
+ });
51
+
52
+ expect(outStyles).toHaveProperty("default");
53
+ expect(outStyles["default"]).toEqual({});
54
+ });
55
+
56
+ it("should handle empty configuration", () => {
57
+ const outStyles = { default: {} };
58
+
59
+ getAllSpecificStyles({
60
+ componentName: "button",
61
+ subComponentName: "",
62
+ configuration: {},
63
+ outStyles,
64
+ });
65
+
66
+ expect(outStyles).toEqual({ default: {} });
67
+ });
68
+ });
69
+
70
+ describe("Style name parsing", () => {
71
+ it("should parse basic style without state or platform", () => {
72
+ const outStyles = {};
73
+
74
+ const configuration = {
75
+ button_style_background_color: "#FF0000",
76
+ button_style_border_width: 2,
77
+ };
78
+
79
+ getAllSpecificStyles({
80
+ componentName: "button",
81
+ subComponentName: "",
82
+ configuration,
83
+ outStyles,
84
+ });
85
+
86
+ expect(outStyles["default"]).toEqual({
87
+ backgroundColor: "#FF0000",
88
+ borderWidth: 2,
89
+ });
90
+ });
91
+
92
+ it("should handle subComponentName correctly", () => {
93
+ const outStyles = {};
94
+
95
+ const configuration = {
96
+ player_controls_style_button_background_color: "#FF0000",
97
+ player_controls_style_button_text_size: 16,
98
+ };
99
+
100
+ getAllSpecificStyles({
101
+ componentName: "player_controls",
102
+ subComponentName: "button",
103
+ configuration,
104
+ outStyles,
105
+ });
106
+
107
+ expect(outStyles["default"]).toEqual({
108
+ backgroundColor: "#FF0000",
109
+ textSize: 16,
110
+ });
111
+ });
112
+
113
+ it("should ignore keys that do not match the prefix", () => {
114
+ const outStyles = {};
115
+
116
+ const configuration = {
117
+ button_style_background_color: "#FF0000",
118
+ other_component_style_text_color: "#00FF00", // Should be ignored
119
+ random_key: "value", // Should be ignored
120
+ };
121
+
122
+ getAllSpecificStyles({
123
+ componentName: "button",
124
+ subComponentName: "",
125
+ configuration,
126
+ outStyles,
127
+ });
128
+
129
+ expect(outStyles["default"]).toEqual({
130
+ backgroundColor: "#FF0000",
131
+ });
132
+ });
133
+ });
134
+
135
+ describe("State handling", () => {
136
+ it("should handle pressed state", () => {
137
+ const outStyles = {};
138
+
139
+ const configuration = {
140
+ button_style_background_color: "#FF0000",
141
+ button_style_pressed_background_color: "#00FF00",
142
+ };
143
+
144
+ getAllSpecificStyles({
145
+ componentName: "button",
146
+ subComponentName: "",
147
+ configuration,
148
+ outStyles,
149
+ });
150
+
151
+ expect(outStyles["default"]).toEqual({
152
+ backgroundColor: "#FF0000",
153
+ });
154
+
155
+ expect(outStyles["pressed"]).toEqual({
156
+ backgroundColor: "#00FF00",
157
+ });
158
+ });
159
+
160
+ it("should handle focused state", () => {
161
+ const outStyles = {};
162
+
163
+ const configuration = {
164
+ button_style_focused_border_width: 3,
165
+ };
166
+
167
+ getAllSpecificStyles({
168
+ componentName: "button",
169
+ subComponentName: "",
170
+ configuration,
171
+ outStyles,
172
+ });
173
+
174
+ expect(outStyles["focused"]).toEqual({
175
+ borderWidth: 3,
176
+ });
177
+ });
178
+
179
+ it("should handle selected state", () => {
180
+ const outStyles = {};
181
+
182
+ const configuration = {
183
+ button_style_selected_opacity: 0.5,
184
+ };
185
+
186
+ getAllSpecificStyles({
187
+ componentName: "button",
188
+ subComponentName: "",
189
+ configuration,
190
+ outStyles,
191
+ });
192
+
193
+ expect(outStyles["selected"]).toEqual({
194
+ opacity: 0.5,
195
+ });
196
+ });
197
+
198
+ it("should handle focused_selected state", () => {
199
+ const outStyles = {};
200
+
201
+ const configuration = {
202
+ button_style_focused_selected_scale: 1.2,
203
+ };
204
+
205
+ getAllSpecificStyles({
206
+ componentName: "button",
207
+ subComponentName: "",
208
+ configuration,
209
+ outStyles,
210
+ });
211
+
212
+ expect(outStyles["focused_selected"]).toEqual({
213
+ scale: 1.2,
214
+ });
215
+ });
216
+
217
+ it("should merge default styles into other states", () => {
218
+ const outStyles = {};
219
+
220
+ const configuration = {
221
+ button_style_background_color: "#FF0000",
222
+ button_style_border_width: 1,
223
+ button_style_opacity: 1,
224
+ button_style_pressed_background_color: "#00FF00",
225
+ button_style_focused_opacity: 0.8,
226
+ };
227
+
228
+ getAllSpecificStyles({
229
+ componentName: "button",
230
+ subComponentName: "",
231
+ configuration,
232
+ outStyles,
233
+ });
234
+
235
+ expect(outStyles["default"]).toEqual({
236
+ backgroundColor: "#FF0000",
237
+ borderWidth: 1,
238
+ opacity: 1,
239
+ });
240
+
241
+ // Pressed should have default styles merged with its specific override
242
+ expect(outStyles["pressed"]).toEqual({
243
+ backgroundColor: "#00FF00", // Override
244
+ borderWidth: 1, // From default
245
+ opacity: 1, // From default
246
+ });
247
+
248
+ // Focused should have default styles merged with its specific override
249
+ expect(outStyles["focused"]).toEqual({
250
+ backgroundColor: "#FF0000", // From default
251
+ borderWidth: 1, // From default
252
+ opacity: 0.8, // Override
253
+ });
254
+ });
255
+
256
+ it("should preserve existing state values when merging", () => {
257
+ const outStyles = {
258
+ pressed: { existingKey: "existingValue" },
259
+ };
260
+
261
+ const configuration = {
262
+ button_style_background_color: "#FF0000",
263
+ button_style_pressed_text_color: "#FFFFFF",
264
+ };
265
+
266
+ getAllSpecificStyles({
267
+ componentName: "button",
268
+ subComponentName: "",
269
+ configuration,
270
+ outStyles,
271
+ });
272
+
273
+ expect(outStyles["pressed"]).toEqual({
274
+ existingKey: "existingValue", // Should be preserved
275
+ backgroundColor: "#FF0000", // From default
276
+ textColor: "#FFFFFF", // New pressed style
277
+ });
278
+ });
279
+ });
280
+
281
+ describe("Platform handling", () => {
282
+ it("should include styles for current platform (samsung)", () => {
283
+ const outStyles = {};
284
+
285
+ const configuration = {
286
+ button_style_samsung_background_color: "#FF0000",
287
+ button_style_background_color: "#00FF00",
288
+ };
289
+
290
+ getAllSpecificStyles({
291
+ componentName: "button",
292
+ subComponentName: "",
293
+ configuration,
294
+ outStyles,
295
+ });
296
+
297
+ expect(outStyles["default"]).toEqual({
298
+ backgroundColor: "#FF0000", // Samsung-specific should be included
299
+ });
300
+ });
301
+
302
+ it("should skip styles for other platforms", () => {
303
+ const outStyles = {};
304
+
305
+ const configuration = {
306
+ button_style_ios_background_color: "#FF0000",
307
+ button_style_android_text_color: "#00FF00",
308
+ button_style_tvos_border_width: 2,
309
+ button_style_lg_opacity: 0.5,
310
+ button_style_background_color: "#FFFFFF",
311
+ };
312
+
313
+ getAllSpecificStyles({
314
+ componentName: "button",
315
+ subComponentName: "",
316
+ configuration,
317
+ outStyles,
318
+ });
319
+
320
+ expect(outStyles["default"]).toEqual({
321
+ backgroundColor: "#FFFFFF", // Only non-platform specific
322
+ });
323
+ });
324
+
325
+ it("should handle platform in middle of style name", () => {
326
+ const outStyles = {};
327
+
328
+ const configuration = {
329
+ button_style_text_samsung_color: "#FF0000",
330
+ };
331
+
332
+ getAllSpecificStyles({
333
+ componentName: "button",
334
+ subComponentName: "text",
335
+ configuration,
336
+ outStyles,
337
+ });
338
+
339
+ expect(outStyles["default"]).toEqual({
340
+ color: "#FF0000",
341
+ });
342
+ });
343
+
344
+ it("should handle platform-specific styles with states", () => {
345
+ const outStyles = {};
346
+
347
+ const configuration = {
348
+ button_style_samsung_pressed_background_color: "#FF0000",
349
+ button_style_ios_pressed_background_color: "#00FF00", // Should be ignored
350
+ };
351
+
352
+ getAllSpecificStyles({
353
+ componentName: "button",
354
+ subComponentName: "",
355
+ configuration,
356
+ outStyles,
357
+ });
358
+
359
+ expect(outStyles["pressed"]).toEqual({
360
+ backgroundColor: "#FF0000", // Only samsung style
361
+ });
362
+ });
363
+
364
+ it("should work correctly when platform is Samsung", () => {
365
+ const outStyles = {};
366
+
367
+ const configuration = {
368
+ button_style_ios_background_color: "#FF0000",
369
+ button_style_samsung_background_color: "#00FF00",
370
+ button_style_background_color: "#FFFFFF",
371
+ };
372
+
373
+ getAllSpecificStyles({
374
+ componentName: "button",
375
+ subComponentName: "",
376
+ configuration,
377
+ outStyles,
378
+ });
379
+
380
+ expect(outStyles["default"]).toEqual({
381
+ backgroundColor: "#00FF00", // Samsung-specific should win
382
+ });
383
+ });
384
+ });
385
+
386
+ describe("Complex scenarios", () => {
387
+ it("should handle multiple states and platforms correctly", () => {
388
+ const outStyles = {};
389
+
390
+ const configuration = {
391
+ // Default styles
392
+ button_style_background_color: "#FFFFFF",
393
+ button_style_text_color: "#000000",
394
+ button_style_border_width: 1,
395
+
396
+ // Platform-specific default
397
+ button_style_samsung_padding: 10,
398
+
399
+ // State-specific
400
+ button_style_pressed_background_color: "#EEEEEE",
401
+ button_style_focused_scale: 1.1,
402
+
403
+ // Platform + state specific
404
+ button_style_samsung_pressed_opacity: 0.8,
405
+ button_style_ios_pressed_opacity: 0.6, // Should be ignored
406
+ };
407
+
408
+ getAllSpecificStyles({
409
+ componentName: "button",
410
+ subComponentName: "",
411
+ configuration,
412
+ outStyles,
413
+ });
414
+
415
+ expect(outStyles["default"]).toEqual({
416
+ backgroundColor: "#FFFFFF",
417
+ textColor: "#000000",
418
+ borderWidth: 1,
419
+ padding: 10,
420
+ });
421
+
422
+ expect(outStyles["pressed"]).toEqual({
423
+ backgroundColor: "#EEEEEE",
424
+ textColor: "#000000",
425
+ borderWidth: 1,
426
+ padding: 10,
427
+ opacity: 0.8,
428
+ });
429
+
430
+ expect(outStyles["focused"]).toEqual({
431
+ backgroundColor: "#FFFFFF",
432
+ textColor: "#000000",
433
+ borderWidth: 1,
434
+ padding: 10,
435
+ scale: 1.1,
436
+ });
437
+ });
438
+
439
+ it("should handle edge case keys correctly", () => {
440
+ const outStyles = {};
441
+
442
+ const configuration = {
443
+ button_style_: "shouldBeIgnored", // Empty style name
444
+ button_style_samsung_: "shouldBeIgnored", // Platform but no style
445
+ button_style__pressed: "shouldBeIgnored", // No style name before state
446
+ button_style_valid_key: "included",
447
+ };
448
+
449
+ getAllSpecificStyles({
450
+ componentName: "button",
451
+ subComponentName: "",
452
+ configuration,
453
+ outStyles,
454
+ });
455
+
456
+ expect(outStyles["default"]).toEqual({
457
+ validKey: "included",
458
+ });
459
+ });
460
+
461
+ it("should handle all state suffixes in priority order", () => {
462
+ const outStyles = {};
463
+
464
+ const configuration = {
465
+ // Test that focused_selected is recognized before focused
466
+ button_style_focused_selected_test: "focused_selected_value",
467
+ button_style_focused_another: "focused_value",
468
+ };
469
+
470
+ getAllSpecificStyles({
471
+ componentName: "button",
472
+ subComponentName: "",
473
+ configuration,
474
+ outStyles,
475
+ });
476
+
477
+ expect(outStyles["focused_selected"]).toEqual({
478
+ test: "focused_selected_value",
479
+ });
480
+
481
+ expect(outStyles["focused"]).toEqual({
482
+ another: "focused_value",
483
+ });
484
+ });
485
+ });
486
+
487
+ describe("Immutability", () => {
488
+ it("should not mutate configuration object", () => {
489
+ const configuration = Object.freeze({
490
+ button_style_background_color: "#FF0000",
491
+ });
492
+
493
+ const outStyles = {};
494
+
495
+ expect(() => {
496
+ getAllSpecificStyles({
497
+ componentName: "button",
498
+ subComponentName: "",
499
+ configuration,
500
+ outStyles,
501
+ });
502
+ }).not.toThrow();
503
+ });
504
+
505
+ it("platform pressed overrides generic pressed for same key", () => {
506
+ const outStyles = {};
507
+
508
+ const configuration = {
509
+ button_style_pressed_background_color: "#111111", // generic
510
+ button_style_samsung_pressed_background_color: "#222222", // platform
511
+ };
512
+
513
+ getAllSpecificStyles({
514
+ componentName: "button",
515
+ subComponentName: "",
516
+ configuration,
517
+ outStyles,
518
+ });
519
+
520
+ expect(outStyles["pressed"]["backgroundColor"]).toBe("#222222");
521
+ });
522
+
523
+ it("should handle frozen outStyles properties", () => {
524
+ const outStyles = {
525
+ default: Object.freeze({ existingProp: "value" }),
526
+ };
527
+
528
+ const configuration = {
529
+ button_style_background_color: "#FF0000",
530
+ };
531
+
532
+ getAllSpecificStyles({
533
+ componentName: "button",
534
+ subComponentName: "",
535
+ configuration,
536
+ outStyles,
537
+ });
538
+
539
+ // Should create new object instead of mutating frozen one
540
+ expect(outStyles["default"]).toEqual({
541
+ existingProp: "value",
542
+ backgroundColor: "#FF0000",
543
+ });
544
+ });
545
+ });
546
+ });
@@ -399,7 +399,7 @@ export const populateConfigurationValues =
399
399
  flattenAndPopulateFields(fields, configuration, skipDefaults)
400
400
  );
401
401
 
402
- export const getAccesabilityProps = (item: ZappEntry) => ({
402
+ export const getAccessibilityProps = (item: ZappEntry) => ({
403
403
  accessible: item?.extensions?.accessibility,
404
404
  accessibilityLabel: item?.extensions?.accessibility?.label || item?.title,
405
405
  accessibilityHint: item?.extensions?.accessibility?.hint,