@applicaster/zapp-react-native-utils 15.0.0-alpha.5859164390 → 15.0.0-alpha.5904402522

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.
@@ -0,0 +1,41 @@
1
+ const {
2
+ isKeyHasSuffix,
3
+ isKeyHasAnyOfSuffixes,
4
+ getKeyWithPrefixGenerator,
5
+ } = require("..");
6
+
7
+ describe("manifestUtils/_internals helpers", () => {
8
+ it("checks a key suffix", () => {
9
+ expect(
10
+ isKeyHasSuffix("button_enabled", "mobile_button_1_button_enabled")
11
+ ).toBe(true);
12
+
13
+ expect(
14
+ isKeyHasSuffix("button_enabled", "mobile_button_1_assign_action")
15
+ ).toBe(false);
16
+ });
17
+
18
+ it("checks key against multiple suffixes", () => {
19
+ const suffixes = ["font_color", "focused_font_color", "text_transform"];
20
+
21
+ expect(
22
+ isKeyHasAnyOfSuffixes(suffixes, "mobile_button_1_text_transform")
23
+ ).toBe(true);
24
+
25
+ expect(
26
+ isKeyHasAnyOfSuffixes(suffixes, "mobile_button_1_asset_alignment")
27
+ ).toBe(false);
28
+ });
29
+
30
+ it("generates stable key names using a prefix", () => {
31
+ const withMobileButtonPrefix = getKeyWithPrefixGenerator("mobile_button_2");
32
+
33
+ expect(withMobileButtonPrefix("display_mode")).toBe(
34
+ "mobile_button_2_display_mode"
35
+ );
36
+
37
+ expect(withMobileButtonPrefix("asset_enabled")).toBe(
38
+ "mobile_button_2_asset_enabled"
39
+ );
40
+ });
41
+ });
@@ -174,6 +174,36 @@ function generateFieldsFromDefaultsWithoutPrefixedLabel(
174
174
  )(fields);
175
175
  }
176
176
 
177
+ /**
178
+ * Checks whether a generated manifest field key ends with the given suffix.
179
+ *
180
+ * @param {string} suffix
181
+ * @param {string} key
182
+ * @returns {boolean}
183
+ */
184
+ const isKeyHasSuffix = (suffix, key) => key.endsWith(`_${suffix}`);
185
+
186
+ /**
187
+ * Checks whether a generated manifest field key ends with any supported suffix.
188
+ *
189
+ * @param {string[]} suffixes
190
+ * @param {string} key
191
+ * @returns {boolean}
192
+ */
193
+ const isKeyHasAnyOfSuffixes = (suffixes, key) =>
194
+ suffixes.some((suffix) => key.endsWith(`_${suffix}`));
195
+
196
+ /**
197
+ * Creates a helper that prefixes a manifest field suffix with a shared key stem.
198
+ *
199
+ * @param {string} prefix
200
+ * @returns {(suffix: string) => string}
201
+ */
202
+ function getKeyWithPrefixGenerator(prefix) {
203
+ // expect prefix as lower snake case, e.g. "mobile_buttons_container"
204
+ return (suffix) => `${prefix}_${suffix}`;
205
+ }
206
+
177
207
  module.exports = {
178
208
  toSnakeCase,
179
209
  toCamelCase,
@@ -185,4 +215,7 @@ module.exports = {
185
215
  getDefaultConfiguration,
186
216
  compact,
187
217
  replaceUnderscoreToSpace,
218
+ isKeyHasSuffix,
219
+ isKeyHasAnyOfSuffixes,
220
+ getKeyWithPrefixGenerator,
188
221
  };
@@ -0,0 +1,49 @@
1
+ const {
2
+ withConditional,
3
+ getConditionalKey,
4
+ createConditionalField,
5
+ } = require("..");
6
+
7
+ describe("manifestUtils/fieldUtils", () => {
8
+ it("appends conditions and adds all_conditions when there is more than one condition", () => {
9
+ const config = {
10
+ key: "mobile_button_1_width",
11
+ conditional_fields: [
12
+ { key: "styles/mobile_button_1_button_enabled", condition_value: true },
13
+ ],
14
+ };
15
+
16
+ const result = withConditional([
17
+ { key: "styles/mobile_button_1_display_mode", condition_value: "fixed" },
18
+ ])(config);
19
+
20
+ expect(result.conditional_fields).toEqual([
21
+ { key: "styles/mobile_button_1_button_enabled", condition_value: true },
22
+ { key: "styles/mobile_button_1_display_mode", condition_value: "fixed" },
23
+ ]);
24
+
25
+ expect(result.rules).toBe("all_conditions");
26
+ });
27
+
28
+ it("creates category-prefixed conditional key", () => {
29
+ expect(
30
+ getConditionalKey("mobile_buttons_container_position", "styles")
31
+ ).toBe("styles/mobile_buttons_container_position");
32
+ });
33
+
34
+ it("creates conditional fields with default category and with raw key", () => {
35
+ expect(
36
+ createConditionalField("mobile_button_1_asset_enabled", true)
37
+ ).toEqual({
38
+ key: "styles/mobile_button_1_asset_enabled",
39
+ condition_value: true,
40
+ });
41
+
42
+ expect(
43
+ createConditionalField("mobile_button_1_asset_enabled", true, null)
44
+ ).toEqual({
45
+ key: "mobile_button_1_asset_enabled",
46
+ condition_value: true,
47
+ });
48
+ });
49
+ });
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Appends new conditional_fields from conditions array
3
+ * to config.conditional_fields
4
+ * if there are more than 1 condition, sets rules to "all_conditions"
5
+ *
6
+ * @param {Array<{key: string, condition_value: unknown}>} conditions
7
+ * @returns {(config: object) => object}
8
+ */
9
+ const withConditional = (conditions) => (config) => {
10
+ const conditional_fields = [
11
+ ...(config.conditional_fields || []),
12
+ ...conditions,
13
+ ];
14
+
15
+ const next = { ...config, conditional_fields };
16
+
17
+ if (conditional_fields.length > 1) {
18
+ return { ...next, rules: "all_conditions" };
19
+ }
20
+
21
+ return next;
22
+ };
23
+
24
+ /**
25
+ * Builds key for conditional fields prepending category, e.g. "styles/mobile_buttons_container_position"
26
+ *
27
+ * @param {string} key
28
+ * @param {string} category
29
+ * @returns {string}
30
+ */
31
+ function getConditionalKey(key, category) {
32
+ return `${category}/${key}`;
33
+ }
34
+
35
+ /**
36
+ * Returns a conditional field object for manifest visibility rules.
37
+ *
38
+ * @param {string} key
39
+ * @param {unknown} condition_value
40
+ * @param {string|null} [category="styles"] Pass `null` to use the key as-is.
41
+ * @returns {{key: string, condition_value: unknown}}
42
+ */
43
+ function createConditionalField(key, condition_value, category = "styles") {
44
+ return {
45
+ key: category ? getConditionalKey(key, category) : key,
46
+ condition_value,
47
+ };
48
+ }
49
+
50
+ module.exports = {
51
+ withConditional,
52
+ getConditionalKey,
53
+ createConditionalField,
54
+ };
@@ -8,6 +8,9 @@ const {
8
8
 
9
9
  const { tvActionButtonsContainer } = require("./tvAction/container");
10
10
  const { tvActionButton } = require("./tvAction/button");
11
+ const { mobileActionButtonsContainer } = require("./mobileAction/container");
12
+ const { mobileActionButton } = require("./mobileAction/button");
13
+ const { buildMobileActionButtonGroups } = require("./mobileAction/groups");
11
14
  const { compact } = require("./_internals");
12
15
 
13
16
  const { spacingKey, absolutePositionElement } = require("./containers");
@@ -43,6 +46,9 @@ module.exports = {
43
46
  tvMenuLabel,
44
47
  tvActionButtonsContainer,
45
48
  tvActionButton,
49
+ mobileActionButtonsContainer,
50
+ mobileActionButton,
51
+ buildMobileActionButtonGroups,
46
52
  mobileCellLabel,
47
53
  tvCellLabel,
48
54
  tvBadges,
@@ -406,6 +406,244 @@ const TV_ACTION_BUTTON_FIELDS = [
406
406
  },
407
407
  ];
408
408
 
409
+ const mobileActionButtonContainerFields = (positionOptions) => [
410
+ {
411
+ type: ZAPPIFEST_FIELDS.switch,
412
+ suffix: "buttons enabled",
413
+ },
414
+ {
415
+ type: ZAPPIFEST_FIELDS.switch,
416
+ suffix: "enable cell action",
417
+ },
418
+ {
419
+ type: ZAPPIFEST_FIELDS.select,
420
+ suffix: "position",
421
+ options: positionOptions,
422
+ },
423
+ {
424
+ type: ZAPPIFEST_FIELDS.select,
425
+ suffix: "align",
426
+ options: ["left", "center", "right"],
427
+ },
428
+ {
429
+ type: ZAPPIFEST_FIELDS.select,
430
+ suffix: "over image position",
431
+ options: ["center", "top_left", "top_right", "bottom_left", "bottom_right"],
432
+ },
433
+ {
434
+ type: ZAPPIFEST_FIELDS.number_input,
435
+ suffix: "margin top",
436
+ },
437
+ {
438
+ type: ZAPPIFEST_FIELDS.number_input,
439
+ suffix: "margin right",
440
+ },
441
+ {
442
+ type: ZAPPIFEST_FIELDS.number_input,
443
+ suffix: "margin bottom",
444
+ },
445
+ {
446
+ type: ZAPPIFEST_FIELDS.number_input,
447
+ suffix: "margin left",
448
+ },
449
+ {
450
+ type: ZAPPIFEST_FIELDS.select,
451
+ suffix: "stacking",
452
+ options: ["horizontal", "vertical"],
453
+ },
454
+ {
455
+ type: ZAPPIFEST_FIELDS.number_input,
456
+ suffix: "horizontal gutter",
457
+ },
458
+ {
459
+ type: ZAPPIFEST_FIELDS.number_input,
460
+ suffix: "vertical gutter",
461
+ },
462
+ {
463
+ type: ZAPPIFEST_FIELDS.select,
464
+ suffix: "cell action",
465
+ options: [
466
+ { text: "primary navigation", value: "navigation_action" },
467
+ { text: "secondary navigation", value: "secondary_navigation" },
468
+ ],
469
+ },
470
+ {
471
+ type: ZAPPIFEST_FIELDS.switch,
472
+ suffix: "independent styles",
473
+ },
474
+ ];
475
+
476
+ const MOBILE_ACTION_BUTTON_FIELDS = [
477
+ {
478
+ type: ZAPPIFEST_FIELDS.switch,
479
+ suffix: "button enabled",
480
+ },
481
+ {
482
+ type: ZAPPIFEST_FIELDS.select,
483
+ suffix: "assign action",
484
+ options: [
485
+ { text: "primary navigation", value: "navigation_action" },
486
+ { text: "secondary navigation", value: "secondary_navigation" },
487
+ { text: "favorite", value: "local_storage_favourites_action" },
488
+ { text: "more", value: "more" },
489
+ { text: "add to calendar", value: "add_to_calendar" },
490
+ { text: "share", value: "share" },
491
+ { text: "downloads", value: "downloads" },
492
+ { text: "trailer", value: "trailer_action" },
493
+ { text: "mute/unmute", value: "mute_unmute" },
494
+ ],
495
+ },
496
+ {
497
+ type: ZAPPIFEST_FIELDS.select,
498
+ suffix: "display mode",
499
+ options: ["dynamic", "fixed", "fill"],
500
+ },
501
+ {
502
+ type: ZAPPIFEST_FIELDS.number_input,
503
+ suffix: "width",
504
+ },
505
+ {
506
+ type: ZAPPIFEST_FIELDS.select,
507
+ suffix: "contents alignment",
508
+ options: ["left", "center", "right"],
509
+ },
510
+ {
511
+ type: ZAPPIFEST_FIELDS.color_picker,
512
+ suffix: "background color",
513
+ },
514
+ {
515
+ type: ZAPPIFEST_FIELDS.color_picker,
516
+ suffix: "focused background color",
517
+ },
518
+ {
519
+ type: ZAPPIFEST_FIELDS.color_picker,
520
+ suffix: "border color",
521
+ },
522
+ {
523
+ type: ZAPPIFEST_FIELDS.color_picker,
524
+ suffix: "focused border color",
525
+ },
526
+ {
527
+ type: ZAPPIFEST_FIELDS.number_input,
528
+ suffix: "border size",
529
+ },
530
+ {
531
+ type: ZAPPIFEST_FIELDS.number_input,
532
+ suffix: "corner radius",
533
+ },
534
+ {
535
+ type: ZAPPIFEST_FIELDS.number_input,
536
+ suffix: "padding top",
537
+ },
538
+ {
539
+ type: ZAPPIFEST_FIELDS.number_input,
540
+ suffix: "padding right",
541
+ },
542
+ {
543
+ type: ZAPPIFEST_FIELDS.number_input,
544
+ suffix: "padding bottom",
545
+ },
546
+ {
547
+ type: ZAPPIFEST_FIELDS.number_input,
548
+ suffix: "padding left",
549
+ },
550
+ {
551
+ type: ZAPPIFEST_FIELDS.switch,
552
+ suffix: "asset enabled",
553
+ },
554
+ {
555
+ type: ZAPPIFEST_FIELDS.select,
556
+ suffix: "action asset flavor",
557
+ options: ["flavor_1", "flavor_2"],
558
+ },
559
+ {
560
+ type: ZAPPIFEST_FIELDS.select,
561
+ suffix: "asset alignment",
562
+ options: ["left", "right", "above", "below"],
563
+ },
564
+ {
565
+ type: ZAPPIFEST_FIELDS.number_input,
566
+ suffix: "asset height",
567
+ },
568
+ {
569
+ type: ZAPPIFEST_FIELDS.number_input,
570
+ suffix: "asset width",
571
+ },
572
+ {
573
+ type: ZAPPIFEST_FIELDS.number_input,
574
+ suffix: "asset margin top",
575
+ },
576
+ {
577
+ type: ZAPPIFEST_FIELDS.number_input,
578
+ suffix: "asset margin right",
579
+ },
580
+ {
581
+ type: ZAPPIFEST_FIELDS.number_input,
582
+ suffix: "asset margin bottom",
583
+ },
584
+ {
585
+ type: ZAPPIFEST_FIELDS.number_input,
586
+ suffix: "asset margin left",
587
+ },
588
+ {
589
+ type: ZAPPIFEST_FIELDS.switch,
590
+ suffix: "label enabled",
591
+ },
592
+ {
593
+ type: ZAPPIFEST_FIELDS.color_picker,
594
+ suffix: "font color",
595
+ },
596
+ {
597
+ type: ZAPPIFEST_FIELDS.color_picker,
598
+ suffix: "focused font color",
599
+ },
600
+ {
601
+ type: ZAPPIFEST_FIELDS.font_selector.ios,
602
+ suffix: "iOS font family",
603
+ },
604
+ {
605
+ type: ZAPPIFEST_FIELDS.font_selector.android,
606
+ suffix: "android font family",
607
+ },
608
+ {
609
+ type: ZAPPIFEST_FIELDS.number_input,
610
+ suffix: "font size",
611
+ },
612
+ {
613
+ type: ZAPPIFEST_FIELDS.number_input,
614
+ suffix: "line height",
615
+ },
616
+ {
617
+ type: ZAPPIFEST_FIELDS.number_input,
618
+ suffix: "iOS letter spacing",
619
+ },
620
+ {
621
+ type: ZAPPIFEST_FIELDS.number_input,
622
+ suffix: "android letter spacing",
623
+ },
624
+ {
625
+ type: ZAPPIFEST_FIELDS.select,
626
+ suffix: "text transform",
627
+ options: ["default", "lowercase", "uppercase", "capitalize"],
628
+ },
629
+ {
630
+ type: ZAPPIFEST_FIELDS.number_input,
631
+ suffix: "margin top",
632
+ },
633
+ {
634
+ type: ZAPPIFEST_FIELDS.number_input,
635
+ suffix: "margin right",
636
+ },
637
+ {
638
+ type: ZAPPIFEST_FIELDS.number_input,
639
+ suffix: "margin bottom",
640
+ },
641
+ {
642
+ type: ZAPPIFEST_FIELDS.number_input,
643
+ suffix: "margin left",
644
+ },
645
+ ];
646
+
409
647
  const TV_MENU_LABEL_FIELDS = [
410
648
  {
411
649
  type: ZAPPIFEST_FIELDS.switch,
@@ -2134,6 +2372,8 @@ module.exports = {
2134
2372
  TV_COLOR_STATES,
2135
2373
  TV_ACTION_BUTTON_FIELDS,
2136
2374
  tvActionButtonContainerFields,
2375
+ MOBILE_ACTION_BUTTON_FIELDS,
2376
+ mobileActionButtonContainerFields,
2137
2377
  MOBILE_CELL_LABEL_FIELDS,
2138
2378
  TV_CELL_LABEL_FIELDS,
2139
2379
  TV_CELL_BADGE_FIELDS,
@@ -0,0 +1,168 @@
1
+ import { mobileActionButton } from "..";
2
+
3
+ describe("mobileActionButton", () => {
4
+ const defaults = {
5
+ buttonEnabled: true,
6
+ assignAction: "navigation_action",
7
+ displayMode: "dynamic",
8
+ width: 140,
9
+ contentsAlignment: "center",
10
+ backgroundColor: "rgba(1,1,1,1)",
11
+ focusedBackgroundColor: "rgba(2,2,2,1)",
12
+ borderColor: "rgba(0,0,0,0)",
13
+ focusedBorderColor: "rgba(0,0,0,0)",
14
+ borderSize: 0,
15
+ cornerRadius: 8,
16
+ paddingTop: 14,
17
+ paddingRight: 24,
18
+ paddingBottom: 14,
19
+ paddingLeft: 16,
20
+ assetEnabled: true,
21
+ actionAssetFlavor: "flavor_1",
22
+ assetAlignment: "left",
23
+ assetHeight: 24,
24
+ assetWidth: 24,
25
+ assetMarginTop: 0,
26
+ assetMarginRight: 6,
27
+ assetMarginBottom: 0,
28
+ assetMarginLeft: 0,
29
+ labelEnabled: true,
30
+ fontColor: "rgba(239,239,239,1)",
31
+ focusedFontColor: "rgba(239,239,239,1)",
32
+ iosFontFamily: "Ubuntu-Bold",
33
+ androidFontFamily: "Ubuntu-Bold",
34
+ fontSize: 15,
35
+ lineHeight: 24,
36
+ iosLetterSpacing: -0.2,
37
+ androidLetterSpacing: -0.2,
38
+ textTransform: "default",
39
+ marginTop: 0,
40
+ marginRight: 0,
41
+ marginBottom: 0,
42
+ marginLeft: 0,
43
+ };
44
+
45
+ it("generates button fields with inferred conditional rules", () => {
46
+ const result = mobileActionButton({
47
+ label: "Mobile Button 2",
48
+ description: "button 2",
49
+ defaults,
50
+ isFirstButton: false,
51
+ });
52
+
53
+ expect(result.group).toBe(true);
54
+
55
+ const enabledField = result.fields.find(
56
+ (field) => field.key === "mobile_button_2_button_enabled"
57
+ );
58
+
59
+ expect(enabledField).toBeTruthy();
60
+
61
+ expect(enabledField.conditional_fields).toEqual([
62
+ {
63
+ key: "styles/mobile_buttons_container_buttons_enabled",
64
+ condition_value: true,
65
+ },
66
+ ]);
67
+
68
+ const assignActionField = result.fields.find(
69
+ (field) => field.key === "mobile_button_2_assign_action"
70
+ );
71
+
72
+ expect(assignActionField).toBeTruthy();
73
+
74
+ expect(assignActionField.conditional_fields).toEqual([
75
+ {
76
+ key: "styles/mobile_button_2_button_enabled",
77
+ condition_value: true,
78
+ },
79
+ ]);
80
+
81
+ const widthField = result.fields.find(
82
+ (field) => field.key === "mobile_button_2_width"
83
+ );
84
+
85
+ expect(widthField.rules).toBe("all_conditions");
86
+
87
+ expect(widthField.conditional_fields).toEqual([
88
+ {
89
+ key: "styles/mobile_button_2_button_enabled",
90
+ condition_value: true,
91
+ },
92
+ {
93
+ key: "styles/mobile_buttons_container_independent_styles",
94
+ condition_value: true,
95
+ },
96
+ {
97
+ key: "styles/mobile_button_2_display_mode",
98
+ condition_value: "fixed",
99
+ },
100
+ ]);
101
+
102
+ const actionAssetFlavorField = result.fields.find(
103
+ (field) => field.key === "mobile_button_2_action_asset_flavor"
104
+ );
105
+
106
+ expect(actionAssetFlavorField.rules).toBe("all_conditions");
107
+
108
+ expect(actionAssetFlavorField.conditional_fields).toEqual([
109
+ {
110
+ key: "styles/mobile_button_2_button_enabled",
111
+ condition_value: true,
112
+ },
113
+ {
114
+ key: "styles/mobile_buttons_container_independent_styles",
115
+ condition_value: true,
116
+ },
117
+ {
118
+ key: "styles/mobile_button_2_asset_enabled",
119
+ condition_value: true,
120
+ },
121
+ ]);
122
+
123
+ const contentsAlignmentField = result.fields.find(
124
+ (field) => field.key === "mobile_button_2_contents_alignment"
125
+ );
126
+
127
+ expect(contentsAlignmentField.rules).toBe("all_conditions");
128
+
129
+ expect(contentsAlignmentField.conditional_fields).toEqual([
130
+ {
131
+ key: "styles/mobile_button_2_button_enabled",
132
+ condition_value: true,
133
+ },
134
+ {
135
+ key: "styles/mobile_buttons_container_independent_styles",
136
+ condition_value: true,
137
+ },
138
+ {
139
+ key: "styles/mobile_button_2_display_mode",
140
+ condition_value: ["fixed", "fill"],
141
+ },
142
+ ]);
143
+ });
144
+
145
+ it("keeps first-button fields independent-style agnostic", () => {
146
+ const result = mobileActionButton({
147
+ label: "Mobile Button 1",
148
+ description: "button 1",
149
+ defaults,
150
+ isFirstButton: true,
151
+ });
152
+
153
+ const widthField = result.fields.find(
154
+ (field) => field.key === "mobile_button_1_width"
155
+ );
156
+
157
+ expect(widthField.conditional_fields).toEqual([
158
+ {
159
+ key: "styles/mobile_button_1_button_enabled",
160
+ condition_value: true,
161
+ },
162
+ {
163
+ key: "styles/mobile_button_1_display_mode",
164
+ condition_value: "fixed",
165
+ },
166
+ ]);
167
+ });
168
+ });