@capillarytech/creatives-library 9.0.61-alpha.3 → 9.0.62

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 (59) hide show
  1. package/constants/unified.js +8 -2
  2. package/package.json +1 -1
  3. package/services/api.js +5 -0
  4. package/services/tests/api.test.js +44 -0
  5. package/utils/common.js +8 -0
  6. package/utils/downloadFile.js +57 -0
  7. package/utils/tests/downloadFile.test.js +219 -0
  8. package/v2Components/FormBuilder/Classic.js +43 -9
  9. package/v2Components/FormBuilder/Functional/FormBuilderShell.js +324 -69
  10. package/v2Components/FormBuilder/Functional/channels/mobilepush/buildSubmitPayload.js +10 -0
  11. package/v2Components/FormBuilder/Functional/channels/mobilepush/config.js +75 -0
  12. package/v2Components/FormBuilder/Functional/channels/mobilepush/getEditorErrorDescriptor.js +74 -0
  13. package/v2Components/FormBuilder/Functional/channels/mobilepush/index.js +28 -0
  14. package/v2Components/FormBuilder/Functional/channels/mobilepush/modals.js +21 -0
  15. package/v2Components/FormBuilder/Functional/channels/mobilepush/runSubmitPipeline.js +45 -0
  16. package/v2Components/FormBuilder/Functional/channels/mobilepush/tests/getEditorErrorDescriptor.test.js +162 -0
  17. package/v2Components/FormBuilder/Functional/channels/mobilepush/tests/modals.test.js +37 -0
  18. package/v2Components/FormBuilder/Functional/channels/mobilepush/tests/runSubmitPipeline.test.js +70 -0
  19. package/v2Components/FormBuilder/Functional/channels/mobilepush/tests/validate.test.js +274 -0
  20. package/v2Components/FormBuilder/Functional/channels/mobilepush/validate.js +251 -0
  21. package/v2Components/FormBuilder/Functional/channels/registry.js +2 -0
  22. package/v2Components/FormBuilder/Functional/constants.js +46 -8
  23. package/v2Components/FormBuilder/Functional/core/schema/initializeFormState.js +175 -28
  24. package/v2Components/FormBuilder/Functional/core/store/formReducer.js +75 -6
  25. package/v2Components/FormBuilder/Functional/core/store/toLegacyFormData.js +10 -20
  26. package/v2Components/FormBuilder/Functional/layout/FieldSlot.js +9 -1
  27. package/v2Components/FormBuilder/Functional/layout/SchemaForm.js +20 -3
  28. package/v2Components/FormBuilder/Functional/layout/Section.js +6 -1
  29. package/v2Components/FormBuilder/Functional/layout/TabsContainer.js +85 -0
  30. package/v2Components/FormBuilder/Functional/renderers/mpushRenderers.js +451 -0
  31. package/v2Components/FormBuilder/Functional/tests/fieldSlot.test.js +9 -2
  32. package/v2Components/FormBuilder/Functional/tests/manual.typingPerf.test.js +135 -0
  33. package/v2Components/FormBuilder/Functional/tests/mpush.crossFlowParity.test.js +430 -0
  34. package/v2Components/FormBuilder/Functional/tests/mpush.ctaFlows.parity.test.js +320 -0
  35. package/v2Components/FormBuilder/Functional/tests/mpush.editContainer.parity.test.js +148 -0
  36. package/v2Components/FormBuilder/Functional/tests/mpush.engine.test.js +306 -0
  37. package/v2Components/FormBuilder/Functional/tests/mpush.libraryValidity.parity.test.js +152 -0
  38. package/v2Components/FormBuilder/Functional/tests/mpush.shellEvents.test.js +200 -0
  39. package/v2Components/FormBuilder/Functional/tests/mpushRenderers.test.js +382 -0
  40. package/v2Components/FormBuilder/Functional/tests/schemaForm.test.js +17 -0
  41. package/v2Components/FormBuilder/Functional/tests/tabsContainer.test.js +112 -0
  42. package/v2Components/FormBuilder/_formBuilder.scss +11 -0
  43. package/v2Components/FormBuilder/index.js +27 -17
  44. package/v2Components/FormBuilder/tests/entryGate.test.js +90 -7
  45. package/v2Components/FormBuilder/tests/mpush.characterization.test.js +459 -0
  46. package/v2Containers/BeeEditor/index.js +8 -0
  47. package/v2Containers/Email/index.js +118 -16
  48. package/v2Containers/Email/initialSchema.js +25 -3
  49. package/v2Containers/Email/messages.js +16 -0
  50. package/v2Containers/Email/tests/index.test.js +597 -0
  51. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +55 -1
  52. package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +141 -0
  53. package/v2Containers/EmailWrapper/components/_emailHTMLEditor.scss +11 -0
  54. package/v2Containers/Templates/index.js +53 -0
  55. package/v2Containers/Templates/messages.js +8 -0
  56. package/v2Containers/Templates/tests/index.test.js +170 -0
  57. package/v2Containers/Viber/constants.js +0 -3
  58. package/v2Containers/Viber/sagas.js +3 -1
  59. package/v2Containers/Viber/tests/saga.test.js +21 -0
@@ -21,6 +21,7 @@ import * as templatesActions from '../Templates/actions';
21
21
  import {makeSelectEmail} from './selectors';
22
22
  import { makeSelectTemplates } from '../Templates/selectors';
23
23
  import messages from './messages';
24
+ import { sanitizeFileName, exportEmailTemplateHtml, getExportHtmlDisabledTooltip } from '../../utils/downloadFile';
24
25
  import * as globalActions from '../Cap/actions';
25
26
  // import PageHeader from '../../v2Components/PageHeader';
26
27
  import './_email.scss';
@@ -37,7 +38,7 @@ import { FONT_COLOR_05 } from '@capillarytech/cap-ui-library/styled/variables';
37
38
  import { gtmPush } from '../../utils/gtmTrackers';
38
39
  const {CapCustomCardList} = CapCustomCard;
39
40
  import {storeS3FileSizeDetails, CREATIVES_S3_ASSET_FILESIZES} from '../../utils/cdnTransformation';
40
- import { CUSTOMER_BARCODE_TAG } from '../../constants/unified';
41
+ import { CUSTOMER_BARCODE_TAG, EXPORT_HTML_LABEL_ID, DEFAULT_LOCALE, DISPLAY_NONE } from '../../constants/unified';
41
42
 
42
43
  import { containsBase64Images } from '../../utils/content';
43
44
  import injectReducer from '../../utils/injectReducer';
@@ -49,6 +50,10 @@ import TestAndPreviewSlidebox from '../../v2Components/TestAndPreviewSlidebox';
49
50
  export class Email extends React.Component { // eslint-disable-line react/prefer-stateless-function
50
51
  constructor(props) {
51
52
  super(props);
53
+ // Baseline content (set in componentWillReceiveProps) to detect a real edit vs. a resync and permanently lock Export HTML.
54
+ this.originalContentCaptured = false;
55
+ this.originalBaseTemplateContent = undefined;
56
+
52
57
  this.state = {
53
58
  loadingStatus: 0,
54
59
  formData: {},
@@ -85,6 +90,7 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
85
90
  saveObj: {},
86
91
  showEdmEmailTemplates: false,
87
92
  editDataSet: false,
93
+ exportHtmlContentLocked: false,
88
94
  languageDataSet: false,
89
95
  addLanguageType: '',
90
96
  startValidation: false,
@@ -157,6 +163,9 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
157
163
  "switch-editor-label": {
158
164
  switchEditor: this.switchEditor,
159
165
  },
166
+ [EXPORT_HTML_LABEL_ID]: {
167
+ exportHtml: this.exportHtml,
168
+ },
160
169
  "pane": {
161
170
  onTabChange: this.onTabChange,
162
171
  },
@@ -459,6 +468,13 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
459
468
  }
460
469
  }
461
470
 
471
+ if (this.state.isEdit && !this.originalContentCaptured && !_.isEmpty(nextProps.Email.templateDetails)) {
472
+ this.originalContentCaptured = true;
473
+ const baseLanguageForCapture = nextProps.currentOrgDetails?.basic_details?.base_language || DEFAULT_LOCALE;
474
+ const baseVersionForCapture = nextProps.Email.templateDetails.versions?.base?.[baseLanguageForCapture] || {};
475
+ this.originalBaseTemplateContent = baseVersionForCapture['template-content'];
476
+ }
477
+
462
478
  if (this.state.isEdit && !this.state.editDataSet && !_.isEmpty(nextProps.Email.templateDetails) && !_.isEmpty(this.state.schema)) {
463
479
  this.setState({editDataSet: true}, () => {
464
480
  this.setEditData(nextProps.Email.templateDetails);
@@ -523,17 +539,16 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
523
539
  col.style.display = isDragDropForHide ? 'none' : '';
524
540
  }
525
541
  if (col.id === 'tab-options-popover') {
526
- col.colStyle.display = isDragDropForHide ? 'flex' : 'none';
527
542
  _.forEach(col.content.sections[0].inputFields, (tabPopoverSection) => {
528
543
  if (tabPopoverSection.cols[0].id === 'switch-editor-label') {
529
- tabPopoverSection.cols[0].style.display = isDragDropForHide ? '' : 'none';
544
+ tabPopoverSection.cols[0].style.display = (isDragDropForHide && hasSupportCKEditor()) ? '' : DISPLAY_NONE;
530
545
  }
531
546
  });
532
547
  if (col.value && col.value.sections) {
533
548
  _.forEach(col.value.sections[0].inputFields, (valueInputField) => {
534
549
  _.forEach(valueInputField.cols, (valueCol) => {
535
550
  if (valueCol.id === 'tab-option-icon') {
536
- valueCol.colStyle = { ...valueCol.colStyle, display: hasSupportCKEditor() ? 'flex' : 'none' };
551
+ valueCol.colStyle = { ...valueCol.colStyle, display: 'flex' };
537
552
  }
538
553
  });
539
554
  });
@@ -846,6 +861,11 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
846
861
  showTemplateName({formData, onFormDataChange: this.onFormDataChange});
847
862
  }
848
863
  });
864
+
865
+ const baseLanguage = this.props.currentOrgDetails?.basic_details?.base_language || DEFAULT_LOCALE;
866
+ if (!this.state.exportHtmlContentLocked && activeTab === baseLanguage && newContent !== this.originalBaseTemplateContent) {
867
+ this.lockExportHtml();
868
+ }
849
869
  }
850
870
  }
851
871
 
@@ -1753,7 +1773,12 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
1753
1773
 
1754
1774
  temp.panes[langTab].sections[0].inputFields[0].cols[0].colStyle = {display: ""};
1755
1775
  temp.panes[langTab].sections[0].inputFields[0].cols[1].colStyle = {display: "none"};
1756
- temp.tabBarExtraContent.sections[0].inputFields[0].cols[4].colStyle.display = "none";
1776
+ // One-way Bee -> CKEditor switch: hide permanently once switched (see getBEEData).
1777
+ _.forEach(temp.tabBarExtraContent?.sections?.[0]?.inputFields?.[0]?.cols, (col) => {
1778
+ if (col.id === 'tab-options-popover') {
1779
+ this.hideSwitchEditorLabel(col);
1780
+ }
1781
+ });
1757
1782
  }
1758
1783
  });
1759
1784
  schema = this.showInsertImageButton(schema);
@@ -1799,20 +1824,18 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
1799
1824
  newContainerInputFieldCol.style.display = isDragDrop ? "none" : '';
1800
1825
  }
1801
1826
  if (containerInputFieldCol.id === 'tab-options-popover') {
1802
- // eslint-disable-next-line no-param-reassign
1803
- containerInputFieldCol.colStyle.display = isDragDrop ? "flex" : "none";
1804
1827
  _.forEach(containerInputFieldCol.content.sections[0].inputFields, (tabPopoverSection) => {
1805
1828
  if (tabPopoverSection.cols[0].id === 'switch-editor-label') {
1806
1829
  const newTabPopoverSection = tabPopoverSection;
1807
- newTabPopoverSection.cols[0].style.display = isDragDrop ? "" : "none";
1830
+ newTabPopoverSection.cols[0].style.display = (isDragDrop && hasSupportCKEditor()) ? "" : DISPLAY_NONE;
1808
1831
  }
1809
1832
  });
1810
- // Hide tab-option-icon (switch editor trigger) when SUPPORT_CK_EDITOR is disabled
1833
+ // tab-option-icon (popover trigger) is always visible regardless of editor type
1811
1834
  if (containerInputFieldCol.value && containerInputFieldCol.value.sections) {
1812
1835
  _.forEach(containerInputFieldCol.value.sections[0].inputFields, (valueInputField) => {
1813
1836
  _.forEach(valueInputField.cols, (valueCol) => {
1814
1837
  if (valueCol.id === 'tab-option-icon') {
1815
- valueCol.colStyle = { ...valueCol.colStyle, display: hasSupportCKEditor() ? 'flex' : 'none' };
1838
+ valueCol.colStyle = { ...valueCol.colStyle, display: 'flex' };
1816
1839
  }
1817
1840
  });
1818
1841
  });
@@ -1826,8 +1849,7 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
1826
1849
  }
1827
1850
 
1828
1851
  /**
1829
- * Hides the tab-option-icon (switch editor trigger) in schema when SUPPORT_CK_EDITOR is disabled,
1830
- * so users cannot switch editor if the feature is not enabled.
1852
+ * Ensures the tab-option-icon (popover trigger) is visible in schema, regardless of editor type.
1831
1853
  */
1832
1854
  applyTabOptionIconVisibility = (schema) => {
1833
1855
  if (!schema || !schema.containers) return;
@@ -1838,7 +1860,7 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
1838
1860
  _.forEach(col.value.sections[0].inputFields, (inputField) => {
1839
1861
  _.forEach(inputField.cols, (c) => {
1840
1862
  if (c.id === 'tab-option-icon') {
1841
- c.colStyle = { ...(c.colStyle || {}), display: hasSupportCKEditor() ? 'flex' : 'none' };
1863
+ c.colStyle = { ...(c.colStyle || {}), display: 'flex' };
1842
1864
  }
1843
1865
  });
1844
1866
  });
@@ -1995,6 +2017,67 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
1995
2017
  this.setState({showConfirmationModal: true, isGetBeeData: true});
1996
2018
  }
1997
2019
 
2020
+ // Finds a tab-options-popover menu item (e.g. switch-editor-label, EXPORT_HTML_LABEL_ID) by id.
2021
+ findTabPopoverLabelCol = (tabOptionsPopoverCol, targetId) => (
2022
+ _.find(tabOptionsPopoverCol?.content?.sections?.[0]?.inputFields, (field) => field.cols[0].id === targetId)?.cols[0]
2023
+ )
2024
+
2025
+ // One-way Bee -> CKEditor switch: hide the switch-editor-label permanently within a tab-options-popover col.
2026
+ hideSwitchEditorLabel = (tabOptionsPopoverCol) => {
2027
+ const switchEditorCol = this.findTabPopoverLabelCol(tabOptionsPopoverCol, 'switch-editor-label');
2028
+ if (switchEditorCol) {
2029
+ switchEditorCol.style.display = DISPLAY_NONE;
2030
+ }
2031
+ }
2032
+
2033
+ setExportHtmlLabelStyle = (tabOptionsPopoverCol, styleOverrides) => {
2034
+ const exportCol = this.findTabPopoverLabelCol(tabOptionsPopoverCol, EXPORT_HTML_LABEL_ID);
2035
+ if (exportCol) {
2036
+ exportCol.style = { ...exportCol.style, ...styleOverrides };
2037
+ }
2038
+ }
2039
+
2040
+ // Targeted schema mutation to permanently disable Export HTML, skipping injectEvents() to avoid its unrelated schema-rebuild work.
2041
+ // Only the active container is cloned (others are reused by reference) since it's the only one being mutated.
2042
+ lockExportHtml = () => {
2043
+ this.setState((prevState) => {
2044
+ const containers = _.map(prevState.schema.containers, (container) => {
2045
+ if (!container.isActive) return container;
2046
+ const activeContainer = _.cloneDeep(container);
2047
+ const tabOptionsPopoverCol = _.find(activeContainer?.tabBarExtraContent?.sections?.[0]?.inputFields?.[0]?.cols, { id: 'tab-options-popover' });
2048
+ if (tabOptionsPopoverCol) {
2049
+ this.setExportHtmlLabelStyle(tabOptionsPopoverCol, { opacity: 0.5, cursor: 'not-allowed', pointerEvents: 'none' });
2050
+ }
2051
+ return activeContainer;
2052
+ });
2053
+ return { exportHtmlContentLocked: true, schema: { ...prevState.schema, containers } };
2054
+ });
2055
+ }
2056
+
2057
+ // Bee's per-edit signal: deliberately never touches `schema` (unlike lockExportHtml) so it can't trigger Classic.js's formData rebuild and reinitialize the live Bee editor mid-edit.
2058
+ handleBeeContentChange = () => {
2059
+ if (this.state.exportHtmlContentLocked) return;
2060
+ const { formData, currentTab } = this.state;
2061
+ const activeTab = formData[currentTab - 1]?.activeTab;
2062
+ const baseLanguage = this.props.currentOrgDetails?.basic_details?.base_language || DEFAULT_LOCALE;
2063
+ if (activeTab !== baseLanguage) return;
2064
+ this.setState({ exportHtmlContentLocked: true });
2065
+ }
2066
+
2067
+ exportHtml = () => {
2068
+ const templateDetails = this.props.Email.templateDetails;
2069
+ if (_.isEmpty(templateDetails) || this.state.exportHtmlContentLocked) {
2070
+ CapNotification.error({ message: this.props.intl.formatMessage(messages.exportHtmlError) });
2071
+ return;
2072
+ }
2073
+ const filename = `${sanitizeFileName(templateDetails.name)}.html`;
2074
+ const baseLanguage = this.props.currentOrgDetails?.basic_details?.base_language || DEFAULT_LOCALE;
2075
+ exportEmailTemplateHtml(templateDetails, filename, baseLanguage).catch((error) => {
2076
+ console.error('Error exporting EMAIL template HTML:', error);
2077
+ CapNotification.error({ message: this.props.intl.formatMessage(messages.exportHtmlError) });
2078
+ });
2079
+ }
2080
+
1998
2081
  renameVersion = () => {
1999
2082
 
2000
2083
  }
@@ -2376,6 +2459,14 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
2376
2459
  newContainerInputFieldCol.colStyle.display = "none";
2377
2460
  }
2378
2461
  }
2462
+ if (containerInputFieldCol.id === "tab-options-popover") {
2463
+ const canExportHtml = this.state.isEdit && !this.state.exportHtmlContentLocked;
2464
+ this.setExportHtmlLabelStyle(containerInputFieldCol, {
2465
+ opacity: canExportHtml ? 1 : 0.5,
2466
+ cursor: canExportHtml ? 'pointer' : 'not-allowed',
2467
+ pointerEvents: canExportHtml ? 'auto' : 'none',
2468
+ });
2469
+ }
2379
2470
  // if (containerInputFieldCol.id === "tab-options-popover") {
2380
2471
  // const newContainerInputFieldCol = containerInputFieldCol;
2381
2472
  // newContainerInputFieldCol.colStyle.display = "none";
@@ -2443,10 +2534,13 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
2443
2534
  _.forEach(section.inputFields, (inputFields) => {
2444
2535
  if (section.type === "multicols") {
2445
2536
  _.forEach(inputFields.cols, (col) => {
2446
- if ((col.id === "tab-options-popover" || col.id === "add-language-button") && !this.props.isFullMode) {
2537
+ if (col.id === "add-language-button" && !this.props.isFullMode) {
2447
2538
  const colStyle = col.colStyle;
2448
2539
  colStyle.display = "none";
2449
2540
  }
2541
+ if (col.id === "tab-options-popover" && !this.props.isFullMode) {
2542
+ this.hideSwitchEditorLabel(col);
2543
+ }
2450
2544
  });
2451
2545
  }
2452
2546
  });
@@ -3060,7 +3154,12 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
3060
3154
  const langTab = formData[`${currentTab - 1}`].selectedLanguages.indexOf(activeLangTab);
3061
3155
  container.panes[langTab].sections[0].inputFields[0].cols[0].colStyle = { display: "" };
3062
3156
  container.panes[langTab].sections[0].inputFields[0].cols[1].colStyle = { display: "none" };
3063
- container.tabBarExtraContent.sections[0].inputFields[0].cols[4].colStyle.display = "none";
3157
+ // One-way Bee -> CKEditor switch: hide permanently, since isDragDrop isn't re-fetched to drive the usual toggle.
3158
+ _.forEach(container.tabBarExtraContent?.sections?.[0]?.inputFields?.[0]?.cols, (col) => {
3159
+ if (col.id === 'tab-options-popover') {
3160
+ this.hideSwitchEditorLabel(col);
3161
+ }
3162
+ });
3064
3163
  }
3065
3164
  });
3066
3165
 
@@ -3076,7 +3175,7 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
3076
3175
  this.setState((prevState) => {
3077
3176
  const { currentTab, formData } = _.cloneDeep(prevState);
3078
3177
  const activeTab = formData[currentTab - 1].activeTab;
3079
-
3178
+
3080
3179
  // Update content in both formData and base
3081
3180
  formData[currentTab - 1][activeTab]['template-content'] = html;
3082
3181
  formData[currentTab - 1][activeTab]['json-content'] = json;
@@ -3175,6 +3274,9 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
3175
3274
  isLoyaltyModule={this.props?.isLoyaltyModule}
3176
3275
  isGetBeeData={this.state.isGetBeeData}
3177
3276
  getBEEData={this.getBEEData}
3277
+ onContentChange={this.handleBeeContentChange}
3278
+ exportHtmlLocked={this.state.exportHtmlContentLocked}
3279
+ exportHtmlTooltip={getExportHtmlDisabledTooltip(this.state.isEdit, this.state.exportHtmlContentLocked, this.props.intl.formatMessage)}
3178
3280
  isTestAndPreviewMode={this.state.isTestAndPreviewMode} // Add flag to prevent validation
3179
3281
  /> : ''}
3180
3282
  </CapColumn>
@@ -2,6 +2,7 @@ import React from 'react';
2
2
  import {CapButton} from '@capillarytech/cap-ui-library';
3
3
  import {FormattedMessage} from 'react-intl';
4
4
  import messages from './messages';
5
+ import { EXPORT_HTML_LABEL_ID } from '../../constants/unified';
5
6
  export const response = {
6
7
  metaEntities: [
7
8
  {
@@ -285,6 +286,7 @@ export const response = {
285
286
  fontStyle: "normal",
286
287
  fontWeight: "600",
287
288
  justifyContent: "flex-end",
289
+ marginLeft: "31.25rem",
288
290
  },
289
291
  width: 3,
290
292
  offset: 10,
@@ -358,7 +360,7 @@ export const response = {
358
360
  colStyle: {
359
361
  textAlign: "right",
360
362
  flex: 0,
361
- display: "none",
363
+ display: "flex",
362
364
  alignItems: "center",
363
365
  },
364
366
  type: "popover",
@@ -395,6 +397,25 @@ export const response = {
395
397
  },
396
398
  ],
397
399
  },
400
+ {
401
+ cols: [
402
+ {
403
+ id: EXPORT_HTML_LABEL_ID,
404
+ metaType: "label",
405
+ type: "div",
406
+ primitive: true,
407
+ value: "Export HTML",
408
+ onlyDisplay: true,
409
+ customComponent: false,
410
+ order: 5,
411
+ fluid: false,
412
+ className: "version-popover",
413
+ submitAction: "exportHtml",
414
+ supportedEvents: ["exportHtml"],
415
+ style: { display: '' },
416
+ },
417
+ ],
418
+ },
398
419
  ],
399
420
  actionFields: [],
400
421
  },
@@ -410,10 +431,11 @@ export const response = {
410
431
  {
411
432
  id: "tab-option-icon",
412
433
  metaType: "display",
413
- type: "icon",
434
+ type: "cap-icon",
414
435
  onlyDisplay: true,
415
436
  primitive: false,
416
- value: "more_vert",
437
+ value: "more",
438
+ rotate: 90,
417
439
  customComponent: false,
418
440
  order: 3,
419
441
  fluid: false,
@@ -18,6 +18,22 @@ export default defineMessages({
18
18
  id: 'creatives.containersV2.Email.mostRecentOption',
19
19
  defaultMessage: "Most recent",
20
20
  },
21
+ "exportHtmlError": {
22
+ id: 'creatives.containersV2.Email.exportHtmlError',
23
+ defaultMessage: "Failed to export template HTML. Please try again.",
24
+ },
25
+ "exportHtmlButton": {
26
+ id: 'creatives.containersV2.Email.exportHtmlButton',
27
+ defaultMessage: "Export HTML",
28
+ },
29
+ "exportHtmlDisabledCreateTooltip": {
30
+ id: 'creatives.containersV2.Email.exportHtmlDisabledCreateTooltip',
31
+ defaultMessage: "HTML export isn’t available while creating a template.",
32
+ },
33
+ "exportHtmlDisabledEditedTooltip": {
34
+ id: 'creatives.containersV2.Email.exportHtmlDisabledEditedTooltip',
35
+ defaultMessage: "HTML export is unavailable because the template has been edited in this session.",
36
+ },
21
37
  "alphabeticallyOption": {
22
38
  id: 'creatives.containersV2.Email.alphabeticallyOption',
23
39
  defaultMessage: "Alphabetically",