@applicaster/zapp-react-native-utils 15.0.0-alpha.1107527841 → 15.0.0-alpha.1123384087

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 (41) hide show
  1. package/README.md +0 -6
  2. package/appUtils/focusManager/treeDataStructure/Tree/__tests__/Tree.test.js +46 -0
  3. package/appUtils/focusManager/treeDataStructure/Tree/index.js +18 -18
  4. package/appUtils/focusManagerAux/utils/index.ts +2 -4
  5. package/appUtils/focusManagerAux/utils/utils.ios.ts +6 -3
  6. package/appUtils/playerManager/playerNative.ts +2 -1
  7. package/manifestUtils/_internals/__tests__/index.test.js +41 -0
  8. package/manifestUtils/_internals/index.js +33 -0
  9. package/manifestUtils/defaultManifestConfigurations/player.js +0 -16
  10. package/manifestUtils/fieldUtils/__tests__/fieldUtils.test.js +49 -0
  11. package/manifestUtils/fieldUtils/index.js +54 -0
  12. package/manifestUtils/index.js +6 -0
  13. package/manifestUtils/keys.js +240 -0
  14. package/manifestUtils/mobileAction/button/__tests__/mobileActionButton.test.js +168 -0
  15. package/manifestUtils/mobileAction/button/index.js +140 -0
  16. package/manifestUtils/mobileAction/container/__tests__/mobileActionButtonsContainer.test.js +104 -0
  17. package/manifestUtils/mobileAction/container/index.js +79 -0
  18. package/manifestUtils/mobileAction/groups/__tests__/buildMobileActionButtonGroups.test.js +127 -0
  19. package/manifestUtils/mobileAction/groups/defaults.js +77 -0
  20. package/manifestUtils/mobileAction/groups/index.js +80 -0
  21. package/numberUtils/__tests__/toNumber.test.ts +27 -12
  22. package/numberUtils/__tests__/toPositiveNumber.test.ts +32 -4
  23. package/numberUtils/index.ts +5 -1
  24. package/package.json +2 -2
  25. package/reactHooks/feed/index.ts +0 -2
  26. package/reactHooks/feed/useInflatedUrl.ts +1 -1
  27. package/reactHooks/state/__tests__/useComponentScreenState.test.ts +246 -0
  28. package/reactHooks/state/index.ts +2 -0
  29. package/reactHooks/state/useComponentScreenState.ts +45 -0
  30. package/refreshUtils/RefreshCoordinator/__tests__/refreshCoordinator.test.ts +161 -0
  31. package/refreshUtils/RefreshCoordinator/index.ts +216 -0
  32. package/refreshUtils/RefreshCoordinator/utils/__tests__/getDataRefreshConfig.test.ts +104 -0
  33. package/refreshUtils/RefreshCoordinator/utils/index.ts +29 -0
  34. package/screenPickerUtils/index.ts +5 -0
  35. package/screenUtils/index.ts +3 -0
  36. package/utils/__tests__/clone.test.ts +158 -0
  37. package/utils/__tests__/path.test.ts +7 -0
  38. package/utils/clone.ts +7 -0
  39. package/utils/index.ts +2 -1
  40. package/reactHooks/feed/__tests__/useFeedRefresh.test.tsx +0 -75
  41. package/reactHooks/feed/useFeedRefresh.tsx +0 -65
package/README.md CHANGED
@@ -245,12 +245,6 @@ const connectionType = useConnectionInfo(true);
245
245
 
246
246
  `@applicaster/zapp-react-native/reactHooks`
247
247
 
248
- - `useFeedRefresh: ({ reloadData: function, component: { id: boolean | string, rules: {enable_data_refreshing: boolean, refreshing_interval: number} } }) => void` - Hook will call `reloadData` function, in the specified intervals if `enable_data_refreshing` is set to true;
249
-
250
- ```javascript
251
- useFeedRefresh({ reloadData, component });
252
- ```
253
-
254
248
  - `useFeedLoader: ({ feedUrl: string, pipesOptions?: { clearCache?: boolean, loadLocalFavourites?: boolean, silentRefresh?: boolean} }) => ({data: ?ApplicasterFeed, loading: boolean, url: string, error: Error,reloadData: (silentRefresh?: boolean) => void, loadNext: () => void})` - Hook will load data to the redux store and return a feed for the provided DSP URL. If the data for the provided url was already loaded, it will return that value
255
249
 
256
250
  ```javascript
@@ -373,3 +373,49 @@ describe("addNode", () => {
373
373
  checkParents(tree.root);
374
374
  });
375
375
  });
376
+
377
+ describe("findInTree", () => {
378
+ function createNode(id, children) {
379
+ return {
380
+ id,
381
+ children,
382
+ };
383
+ }
384
+
385
+ it("returns a direct child match from root children", () => {
386
+ const tree = new Tree(treeLoaded);
387
+ const direct = createNode("direct-node");
388
+
389
+ tree.root.children = [direct];
390
+
391
+ expect(tree.findInTree("direct-node")).toEqual(direct);
392
+ });
393
+
394
+ it("returns a nested descendant match", () => {
395
+ const tree = new Tree(treeLoaded);
396
+ const nested = createNode("nested-node");
397
+ const intermediate = createNode("intermediate-node", [nested]);
398
+ const rootNode = createNode("root-node", [intermediate]);
399
+
400
+ tree.root.children = [rootNode];
401
+
402
+ expect(tree.findInTree("nested-node")).toEqual(nested);
403
+ });
404
+
405
+ it("returns null when node id does not exist", () => {
406
+ const tree = new Tree(treeLoaded);
407
+ const leaf = createNode("leaf-node");
408
+ const rootNode = createNode("root-node", [leaf]);
409
+
410
+ tree.root.children = [rootNode];
411
+
412
+ expect(tree.findInTree("missing-node")).toEqual(null);
413
+ });
414
+
415
+ it("returns null when tree has no children", () => {
416
+ const tree = new Tree(treeLoaded);
417
+ tree.root.children = null;
418
+
419
+ expect(tree.findInTree("any-node")).toEqual(null);
420
+ });
421
+ });
@@ -205,31 +205,31 @@ export class Tree {
205
205
  * @returns founded node or null
206
206
  */
207
207
  findInTree(id) {
208
- const retVal = null;
209
-
210
- return this.findInArray(id, this.root.children, retVal);
208
+ return this.findInArray(id, this.root.children);
211
209
  }
212
210
 
213
- findInArray(id, children, retVal) {
214
- if (!retVal && children) {
215
- retVal = children.find((obj) => obj.id === id);
211
+ findInArray(id, children) {
212
+ if (!children) {
213
+ return null;
214
+ }
216
215
 
217
- if (!retVal) {
218
- children.forEach((child) => {
219
- if (child.children) {
220
- retVal = this.findInArray(id, child.children, retVal);
216
+ const directMatch = children.find((obj) => obj.id === id);
221
217
 
222
- if (retVal) {
223
- return retVal;
224
- }
225
- }
226
- });
227
- } else {
228
- return retVal;
218
+ if (directMatch) {
219
+ return directMatch;
220
+ }
221
+
222
+ for (const child of children) {
223
+ if (child.children) {
224
+ const nestedMatch = this.findInArray(id, child.children);
225
+
226
+ if (nestedMatch) {
227
+ return nestedMatch;
228
+ }
229
229
  }
230
230
  }
231
231
 
232
- return retVal;
232
+ return null;
233
233
  }
234
234
 
235
235
  /**
@@ -13,7 +13,7 @@ import {
13
13
 
14
14
  import {
15
15
  getFocusableId,
16
- SCREEN_PICKER_CONTAINER,
16
+ isTabsScreenContentContainerId,
17
17
  } from "@applicaster/zapp-react-native-utils/screenPickerUtils";
18
18
 
19
19
  // run check each 300 ms
@@ -24,8 +24,6 @@ const isTopMenu = (node) => startsWith(QUICK_BRICK_NAVBAR, node?.id);
24
24
  const isContent = (node) => startsWith(QUICK_BRICK_CONTENT, node?.id);
25
25
  const isRoot = (node) => node?.id === "root";
26
26
 
27
- const isScrenPicker = (node) => startsWith(SCREEN_PICKER_CONTAINER, node?.id);
28
-
29
27
  type Props = {
30
28
  maxTimeout: number;
31
29
  conditionFn: () => boolean;
@@ -136,7 +134,7 @@ export const isTabsScreenOnContentFocused = (node) => {
136
134
  return false;
137
135
  }
138
136
 
139
- if (isScrenPicker(node)) {
137
+ if (isTabsScreenContentContainerId(node?.id)) {
140
138
  return true;
141
139
  }
142
140
 
@@ -106,9 +106,8 @@ const focusableNativeViewRegistration = ({ focusableView, focusableGroup }) => {
106
106
  );
107
107
  };
108
108
 
109
- export const firstFocusableViewRegistrationFactory = () =>
109
+ export const firstFocusableViewInContentRegistrationFactory = () =>
110
110
  focusableViewRegistrationSubject$.pipe(
111
- take(1), // we care about only first FocusableView registration
112
111
  switchMap((focusableView) =>
113
112
  // start waiting registration of its parent FocusableGroup
114
113
  focusableGroupRegistrationSubject$.pipe(
@@ -126,7 +125,11 @@ export const firstFocusableViewRegistrationFactory = () =>
126
125
  focusableView,
127
126
  focusableGroup,
128
127
  })
129
- )
128
+ ),
129
+ filter(({ focusableView }) =>
130
+ isPartOfContent(focusManager.focusableTree, focusableView.id)
131
+ ),
132
+ take(1) // we care about only first FocusableView registration
130
133
  );
131
134
 
132
135
  // registration on RN level(into RN focusManager)
@@ -229,8 +229,9 @@ export class PlayerNative extends Player {
229
229
  };
230
230
 
231
231
  closeNativePlayer = () => {
232
- // TODO: Delete does not work
232
+ // TODO: Delete, does not work (component is null)
233
233
  this.currentPlayerComponent()?.closeNativePlayer?.();
234
+ this.getPlayerModule()?.stopBackgroundPlayback?.();
234
235
  };
235
236
 
236
237
  togglePlayPause = () => {
@@ -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
  };
@@ -707,22 +707,6 @@ function getPlayerConfiguration({ platform, version }) {
707
707
  }
708
708
 
709
709
  if (isMobile(platform)) {
710
- localizations.fields.push(
711
- {
712
- type: "text_input",
713
- label: "Restrict playback on mobile networks alert title",
714
- key: "mobile_connection_restricted_alert_title",
715
- initial_value: "Restricted Connection Type",
716
- },
717
- {
718
- type: "text_input",
719
- label: "Restrict playback on mobile networks alert message",
720
- key: "mobile_connection_restricted_alert_message",
721
- initial_value:
722
- "This content can only be viewed over a Wi-Fi or LAN network.",
723
- }
724
- );
725
-
726
710
  general.fields.push(
727
711
  {
728
712
  section: "Default Timestamp Type",
@@ -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,