@applicaster/zapp-react-native-utils 14.0.0-alpha.6242515303 → 14.0.0-alpha.6461844364

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.
@@ -4,7 +4,7 @@ import { postAnalyticEvent } from "../manager";
4
4
  import { ANALYTICS_CORE_EVENTS } from "../events";
5
5
 
6
6
  type SendHeaderClickEventProps = {
7
- extraProps: ExtraProps;
7
+ extraProps: Record<string, any>;
8
8
  component?: ZappUIComponent;
9
9
  zappPipesData?: ZappPipesData;
10
10
  item?: ZappEntry;
@@ -1,10 +1,11 @@
1
+ /// <reference types="../../" />
1
2
  import { log_error, log_debug } from "../logger";
2
3
  import { replaceAnalyticsPropsNils } from "./helper";
3
4
  import { postAnalyticEvent } from "../manager";
4
5
 
5
6
  import { ANALYTICS_CORE_EVENTS } from "../events";
6
7
 
7
- declare type AnalyticsDefaultHelperProperties = {
8
+ type AnalyticsDefaultHelperProperties = {
8
9
  analyticsScreenData: AnalyticsScreenProperties;
9
10
  extraProps: any;
10
11
  props;
@@ -1,4 +1,3 @@
1
- /// <reference types="@applicaster/zapp-react-native-utils" />
2
1
  import * as R from "ramda";
3
2
  import * as React from "react";
4
3
  import { isWeb } from "@applicaster/zapp-react-native-utils/reactUtils";
@@ -31,7 +30,7 @@ import { ANALYTICS_CORE_EVENTS } from "./events";
31
30
  import { noop } from "../functionUtils";
32
31
 
33
32
  type ComponentWithChildrenProps = {
34
- children: React.ReactChildren;
33
+ children: React.ReactElement;
35
34
  };
36
35
 
37
36
  export function sendSelectCellEvent(item, component, headerTitle, itemIndex) {
@@ -120,11 +119,11 @@ export function getAnalyticsFunctions({
120
119
  export const AnalyticsContext =
121
120
  React.createContext<GetAnalyticsFunctions>(noop);
122
121
 
123
- export function AnalyticsProvider(props: ComponentWithChildrenProps) {
122
+ export function AnalyticsProvider({ children }: ComponentWithChildrenProps) {
124
123
  return (
125
124
  // @ts-ignore - this is a valid context provider
126
125
  <AnalyticsContext.Provider value={getAnalyticsFunctions}>
127
- {props?.children}
126
+ {children}
128
127
  </AnalyticsContext.Provider>
129
128
  );
130
129
  }
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable @typescript-eslint/no-use-before-define */
2
2
  import * as R from "ramda";
3
3
  import { NativeModules } from "react-native";
4
- import { ANALYTICS_CORE_EVENTS } from "@applicaster/zapp-react-native-utils/analyticsUtils/events";
4
+ import { ANALYTICS_CORE_EVENTS } from "./events";
5
5
 
6
6
  import { analyticsUtilsLogger } from "./logger";
7
7
 
@@ -2,8 +2,8 @@ import { BehaviorSubject } from "rxjs";
2
2
  import { accessibilityManagerLogger as logger } from "./logger";
3
3
  import { TTSManager } from "../platform";
4
4
  import { BUTTON_ACCESSIBILITY_KEYS } from "./const";
5
- import { AccessibilityRole } from "react-native";
6
5
  import { toString } from "../../utils";
6
+ import { AccessibilityRole, Role } from "react-native";
7
7
 
8
8
  export class AccessibilityManager {
9
9
  private static _instance: AccessibilityManager | null = null;
@@ -143,12 +143,15 @@ export class AccessibilityManager {
143
143
 
144
144
  if (!buttonConfig) {
145
145
  return {
146
+ accessible: true,
146
147
  accessibilityLabel: buttonName,
147
148
  accessibilityHint: `Press button to perform action on ${buttonName}`,
148
149
  "aria-label": buttonName,
149
150
  "aria-description": `Press button to perform action on ${buttonName}`,
150
- accessibilityRole: "button" as AccessibilityRole,
151
+ accessibilityRole: "button",
151
152
  "aria-role": "button",
153
+ role: "button",
154
+ tabindex: 0,
152
155
  };
153
156
  }
154
157
 
@@ -162,23 +165,29 @@ export class AccessibilityManager {
162
165
  `Press button to perform action on ${buttonName}`;
163
166
 
164
167
  return {
168
+ accessible: true,
165
169
  accessibilityLabel: label,
166
170
  accessibilityHint: hint,
167
171
  "aria-label": label,
168
172
  "aria-description": hint,
169
- accessibilityRole: "button" as AccessibilityRole,
173
+ accessibilityRole: "button",
170
174
  "aria-role": "button",
175
+ role: "button",
176
+ tabindex: 0,
171
177
  };
172
178
  }
173
179
 
174
180
  public getInputAccessibilityProps(inputName: string): AccessibilityProps {
175
181
  return {
182
+ accessible: true,
176
183
  accessibilityLabel: inputName,
177
184
  accessibilityHint: `Enter text into ${inputName}`,
178
185
  "aria-label": inputName,
179
186
  "aria-description": `Enter text into ${inputName}`,
180
187
  accessibilityRole: "textbox" as AccessibilityRole,
181
188
  "aria-role": "textbox",
189
+ role: "textbox" as Role,
190
+ tabindex: 0,
182
191
  };
183
192
  }
184
193
 
@@ -0,0 +1,547 @@
1
+ import { getAllSpecificStyles } from "../manifestKeyParser";
2
+
3
+ // Mock the dependencies
4
+ jest.mock("@applicaster/zapp-react-native-utils/reactUtils", () => ({
5
+ platformSelect: jest.fn((platforms) => platforms.samsung_tv), // Default to samsung for tests
6
+ }));
7
+
8
+ jest.mock("@applicaster/zapp-react-native-utils/stringUtils", () => ({
9
+ toCamelCase: jest.fn((str: string) => {
10
+ // Simple camelCase implementation for testing
11
+ return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
12
+ }),
13
+ }));
14
+
15
+ describe("getAllSpecificStyles", () => {
16
+ beforeEach(() => {
17
+ jest.clearAllMocks();
18
+ });
19
+
20
+ describe("Basic functionality", () => {
21
+ it("should throw error if outStyles is not provided", () => {
22
+ expect(() => {
23
+ getAllSpecificStyles({
24
+ componentName: "button",
25
+ subComponentName: "",
26
+ configuration: {},
27
+ outStyles: null as any,
28
+ });
29
+ }).toThrow("outStyles is required");
30
+ });
31
+
32
+ it("should throw error if componentName is not provided", () => {
33
+ expect(() => {
34
+ getAllSpecificStyles({
35
+ componentName: "",
36
+ subComponentName: "",
37
+ configuration: {},
38
+ outStyles: {},
39
+ });
40
+ }).toThrow("componentName is required");
41
+ });
42
+
43
+ it("should initialize default key if not present", () => {
44
+ const outStyles = {};
45
+
46
+ getAllSpecificStyles({
47
+ componentName: "button",
48
+ subComponentName: "",
49
+ configuration: {},
50
+ outStyles,
51
+ });
52
+
53
+ expect(outStyles).toHaveProperty("default");
54
+ expect(outStyles["default"]).toEqual({});
55
+ });
56
+
57
+ it("should handle empty configuration", () => {
58
+ const outStyles = { default: {} };
59
+
60
+ getAllSpecificStyles({
61
+ componentName: "button",
62
+ subComponentName: "",
63
+ configuration: {},
64
+ outStyles,
65
+ });
66
+
67
+ expect(outStyles).toEqual({ default: {} });
68
+ });
69
+ });
70
+
71
+ describe("Style name parsing", () => {
72
+ it("should parse basic style without state or platform", () => {
73
+ const outStyles = {};
74
+
75
+ const configuration = {
76
+ button_style_background_color: "#FF0000",
77
+ button_style_border_width: 2,
78
+ };
79
+
80
+ getAllSpecificStyles({
81
+ componentName: "button",
82
+ subComponentName: "",
83
+ configuration,
84
+ outStyles,
85
+ });
86
+
87
+ expect(outStyles["default"]).toEqual({
88
+ backgroundColor: "#FF0000",
89
+ borderWidth: 2,
90
+ });
91
+ });
92
+
93
+ it("should handle subComponentName correctly", () => {
94
+ const outStyles = {};
95
+
96
+ const configuration = {
97
+ player_controls_style_button_background_color: "#FF0000",
98
+ player_controls_style_button_text_size: 16,
99
+ };
100
+
101
+ getAllSpecificStyles({
102
+ componentName: "player_controls",
103
+ subComponentName: "button",
104
+ configuration,
105
+ outStyles,
106
+ });
107
+
108
+ expect(outStyles["default"]).toEqual({
109
+ backgroundColor: "#FF0000",
110
+ textSize: 16,
111
+ });
112
+ });
113
+
114
+ it("should ignore keys that do not match the prefix", () => {
115
+ const outStyles = {};
116
+
117
+ const configuration = {
118
+ button_style_background_color: "#FF0000",
119
+ other_component_style_text_color: "#00FF00", // Should be ignored
120
+ random_key: "value", // Should be ignored
121
+ };
122
+
123
+ getAllSpecificStyles({
124
+ componentName: "button",
125
+ subComponentName: "",
126
+ configuration,
127
+ outStyles,
128
+ });
129
+
130
+ expect(outStyles["default"]).toEqual({
131
+ backgroundColor: "#FF0000",
132
+ });
133
+ });
134
+ });
135
+
136
+ describe("State handling", () => {
137
+ it("should handle pressed state", () => {
138
+ const outStyles = {};
139
+
140
+ const configuration = {
141
+ button_style_background_color: "#FF0000",
142
+ button_style_pressed_background_color: "#00FF00",
143
+ };
144
+
145
+ getAllSpecificStyles({
146
+ componentName: "button",
147
+ subComponentName: "",
148
+ configuration,
149
+ outStyles,
150
+ });
151
+
152
+ expect(outStyles["default"]).toEqual({
153
+ backgroundColor: "#FF0000",
154
+ });
155
+
156
+ expect(outStyles["pressed"]).toEqual({
157
+ backgroundColor: "#00FF00",
158
+ });
159
+ });
160
+
161
+ it("should handle focused state", () => {
162
+ const outStyles = {};
163
+
164
+ const configuration = {
165
+ button_style_focused_border_width: 3,
166
+ };
167
+
168
+ getAllSpecificStyles({
169
+ componentName: "button",
170
+ subComponentName: "",
171
+ configuration,
172
+ outStyles,
173
+ });
174
+
175
+ expect(outStyles["focused"]).toEqual({
176
+ borderWidth: 3,
177
+ });
178
+ });
179
+
180
+ it("should handle selected state", () => {
181
+ const outStyles = {};
182
+
183
+ const configuration = {
184
+ button_style_selected_opacity: 0.5,
185
+ };
186
+
187
+ getAllSpecificStyles({
188
+ componentName: "button",
189
+ subComponentName: "",
190
+ configuration,
191
+ outStyles,
192
+ });
193
+
194
+ expect(outStyles["selected"]).toEqual({
195
+ opacity: 0.5,
196
+ });
197
+ });
198
+
199
+ it("should handle focused_selected state", () => {
200
+ const outStyles = {};
201
+
202
+ const configuration = {
203
+ button_style_focused_selected_scale: 1.2,
204
+ };
205
+
206
+ getAllSpecificStyles({
207
+ componentName: "button",
208
+ subComponentName: "",
209
+ configuration,
210
+ outStyles,
211
+ });
212
+
213
+ expect(outStyles["focused_selected"]).toEqual({
214
+ scale: 1.2,
215
+ });
216
+ });
217
+
218
+ it("should merge default styles into other states", () => {
219
+ const outStyles = {};
220
+
221
+ const configuration = {
222
+ button_style_background_color: "#FF0000",
223
+ button_style_border_width: 1,
224
+ button_style_opacity: 1,
225
+ button_style_pressed_background_color: "#00FF00",
226
+ button_style_focused_opacity: 0.8,
227
+ };
228
+
229
+ getAllSpecificStyles({
230
+ componentName: "button",
231
+ subComponentName: "",
232
+ configuration,
233
+ outStyles,
234
+ });
235
+
236
+ expect(outStyles["default"]).toEqual({
237
+ backgroundColor: "#FF0000",
238
+ borderWidth: 1,
239
+ opacity: 1,
240
+ });
241
+
242
+ // Pressed should have default styles merged with its specific override
243
+ expect(outStyles["pressed"]).toEqual({
244
+ backgroundColor: "#00FF00", // Override
245
+ borderWidth: 1, // From default
246
+ opacity: 1, // From default
247
+ });
248
+
249
+ // Focused should have default styles merged with its specific override
250
+ expect(outStyles["focused"]).toEqual({
251
+ backgroundColor: "#FF0000", // From default
252
+ borderWidth: 1, // From default
253
+ opacity: 0.8, // Override
254
+ });
255
+ });
256
+
257
+ it("should preserve existing state values when merging", () => {
258
+ const outStyles = {
259
+ pressed: { existingKey: "existingValue" },
260
+ };
261
+
262
+ const configuration = {
263
+ button_style_background_color: "#FF0000",
264
+ button_style_pressed_text_color: "#FFFFFF",
265
+ };
266
+
267
+ getAllSpecificStyles({
268
+ componentName: "button",
269
+ subComponentName: "",
270
+ configuration,
271
+ outStyles,
272
+ });
273
+
274
+ expect(outStyles["pressed"]).toEqual({
275
+ existingKey: "existingValue", // Should be preserved
276
+ backgroundColor: "#FF0000", // From default
277
+ textColor: "#FFFFFF", // New pressed style
278
+ });
279
+ });
280
+ });
281
+
282
+ describe("Platform handling", () => {
283
+ it("should include styles for current platform (samsung)", () => {
284
+ const outStyles = {};
285
+
286
+ const configuration = {
287
+ button_style_samsung_background_color: "#FF0000",
288
+ button_style_background_color: "#00FF00",
289
+ };
290
+
291
+ getAllSpecificStyles({
292
+ componentName: "button",
293
+ subComponentName: "",
294
+ configuration,
295
+ outStyles,
296
+ });
297
+
298
+ expect(outStyles["default"]).toEqual({
299
+ backgroundColor: "#FF0000", // Samsung-specific should be included
300
+ });
301
+ });
302
+
303
+ it("should skip styles for other platforms", () => {
304
+ const outStyles = {};
305
+
306
+ const configuration = {
307
+ button_style_ios_background_color: "#FF0000",
308
+ button_style_android_text_color: "#00FF00",
309
+ button_style_tvos_border_width: 2,
310
+ button_style_lg_opacity: 0.5,
311
+ button_style_background_color: "#FFFFFF",
312
+ };
313
+
314
+ getAllSpecificStyles({
315
+ componentName: "button",
316
+ subComponentName: "",
317
+ configuration,
318
+ outStyles,
319
+ });
320
+
321
+ expect(outStyles["default"]).toEqual({
322
+ backgroundColor: "#FFFFFF", // Only non-platform specific
323
+ });
324
+ });
325
+
326
+ it("should handle platform in middle of style name", () => {
327
+ const outStyles = {};
328
+
329
+ const configuration = {
330
+ button_style_text_samsung_color: "#FF0000",
331
+ };
332
+
333
+ getAllSpecificStyles({
334
+ componentName: "button",
335
+ subComponentName: "text",
336
+ configuration,
337
+ outStyles,
338
+ });
339
+
340
+ expect(outStyles["default"]).toEqual({
341
+ color: "#FF0000",
342
+ });
343
+ });
344
+
345
+ it("should handle platform-specific styles with states", () => {
346
+ const outStyles = {};
347
+
348
+ const configuration = {
349
+ button_style_samsung_pressed_background_color: "#FF0000",
350
+ button_style_ios_pressed_background_color: "#00FF00", // Should be ignored
351
+ };
352
+
353
+ getAllSpecificStyles({
354
+ componentName: "button",
355
+ subComponentName: "",
356
+ configuration,
357
+ outStyles,
358
+ });
359
+
360
+ expect(outStyles["pressed"]).toEqual({
361
+ backgroundColor: "#FF0000", // Only samsung style
362
+ });
363
+ });
364
+
365
+ it("should work correctly when platform is Samsung", () => {
366
+ const outStyles = {};
367
+
368
+ const configuration = {
369
+ button_style_ios_background_color: "#FF0000",
370
+ button_style_samsung_background_color: "#00FF00",
371
+ button_style_background_color: "#FFFFFF",
372
+ };
373
+
374
+ getAllSpecificStyles({
375
+ componentName: "button",
376
+ subComponentName: "",
377
+ configuration,
378
+ outStyles,
379
+ });
380
+
381
+ expect(outStyles["default"]).toEqual({
382
+ backgroundColor: "#00FF00", // Samsung-specific should win
383
+ });
384
+ });
385
+ });
386
+
387
+ describe("Complex scenarios", () => {
388
+ it("should handle multiple states and platforms correctly", () => {
389
+ const outStyles = {};
390
+
391
+ const configuration = {
392
+ // Default styles
393
+ button_style_background_color: "#FFFFFF",
394
+ button_style_text_color: "#000000",
395
+ button_style_border_width: 1,
396
+
397
+ // Platform-specific default
398
+ button_style_samsung_padding: 10,
399
+
400
+ // State-specific
401
+ button_style_pressed_background_color: "#EEEEEE",
402
+ button_style_focused_scale: 1.1,
403
+
404
+ // Platform + state specific
405
+ button_style_samsung_pressed_opacity: 0.8,
406
+ button_style_ios_pressed_opacity: 0.6, // Should be ignored
407
+ };
408
+
409
+ getAllSpecificStyles({
410
+ componentName: "button",
411
+ subComponentName: "",
412
+ configuration,
413
+ outStyles,
414
+ });
415
+
416
+ expect(outStyles["default"]).toEqual({
417
+ backgroundColor: "#FFFFFF",
418
+ textColor: "#000000",
419
+ borderWidth: 1,
420
+ padding: 10,
421
+ });
422
+
423
+ expect(outStyles["pressed"]).toEqual({
424
+ backgroundColor: "#EEEEEE",
425
+ textColor: "#000000",
426
+ borderWidth: 1,
427
+ padding: 10,
428
+ opacity: 0.8,
429
+ });
430
+
431
+ expect(outStyles["focused"]).toEqual({
432
+ backgroundColor: "#FFFFFF",
433
+ textColor: "#000000",
434
+ borderWidth: 1,
435
+ padding: 10,
436
+ scale: 1.1,
437
+ });
438
+ });
439
+
440
+ it("should handle edge case keys correctly", () => {
441
+ const outStyles = {};
442
+
443
+ const configuration = {
444
+ button_style_: "shouldBeIgnored", // Empty style name
445
+ button_style_samsung_: "shouldBeIgnored", // Platform but no style
446
+ button_style__pressed: "shouldBeIgnored", // No style name before state
447
+ button_style_valid_key: "included",
448
+ };
449
+
450
+ getAllSpecificStyles({
451
+ componentName: "button",
452
+ subComponentName: "",
453
+ configuration,
454
+ outStyles,
455
+ });
456
+
457
+ expect(outStyles["default"]).toEqual({
458
+ validKey: "included",
459
+ });
460
+ });
461
+
462
+ it("should handle all state suffixes in priority order", () => {
463
+ const outStyles = {};
464
+
465
+ const configuration = {
466
+ // Test that focused_selected is recognized before focused
467
+ button_style_focused_selected_test: "focused_selected_value",
468
+ button_style_focused_another: "focused_value",
469
+ };
470
+
471
+ getAllSpecificStyles({
472
+ componentName: "button",
473
+ subComponentName: "",
474
+ configuration,
475
+ outStyles,
476
+ });
477
+
478
+ expect(outStyles["focused_selected"]).toEqual({
479
+ test: "focused_selected_value",
480
+ });
481
+
482
+ expect(outStyles["focused"]).toEqual({
483
+ another: "focused_value",
484
+ });
485
+ });
486
+ });
487
+
488
+ describe("Immutability", () => {
489
+ it("should not mutate configuration object", () => {
490
+ const configuration = Object.freeze({
491
+ button_style_background_color: "#FF0000",
492
+ });
493
+
494
+ const outStyles = {};
495
+
496
+ expect(() => {
497
+ getAllSpecificStyles({
498
+ componentName: "button",
499
+ subComponentName: "",
500
+ configuration,
501
+ outStyles,
502
+ });
503
+ }).not.toThrow();
504
+ });
505
+
506
+ it("platform pressed overrides generic pressed for same key", () => {
507
+ const outStyles = {};
508
+
509
+ const configuration = {
510
+ button_style_pressed_background_color: "#111111", // generic
511
+ button_style_samsung_pressed_background_color: "#222222", // platform
512
+ };
513
+
514
+ getAllSpecificStyles({
515
+ componentName: "button",
516
+ subComponentName: "",
517
+ configuration,
518
+ outStyles,
519
+ });
520
+
521
+ expect(outStyles["pressed"]["backgroundColor"]).toBe("#222222");
522
+ });
523
+
524
+ it("should handle frozen outStyles properties", () => {
525
+ const outStyles = {
526
+ default: Object.freeze({ existingProp: "value" }),
527
+ };
528
+
529
+ const configuration = {
530
+ button_style_background_color: "#FF0000",
531
+ };
532
+
533
+ getAllSpecificStyles({
534
+ componentName: "button",
535
+ subComponentName: "",
536
+ configuration,
537
+ outStyles,
538
+ });
539
+
540
+ // Should create new object instead of mutating frozen one
541
+ expect(outStyles["default"]).toEqual({
542
+ existingProp: "value",
543
+ backgroundColor: "#FF0000",
544
+ });
545
+ });
546
+ });
547
+ });