@capillarytech/creatives-library 8.0.236-alpha.3 → 8.0.236-alpha.4

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capillarytech/creatives-library",
3
3
  "author": "meharaj",
4
- "version": "8.0.236-alpha.3",
4
+ "version": "8.0.236-alpha.4",
5
5
  "description": "Capillary creatives ui",
6
6
  "main": "./index.js",
7
7
  "module": "./index.es.js",
@@ -1376,4 +1376,228 @@ describe("validateCarouselCards", () => {
1376
1376
  expect(result.isValid).toBe(true);
1377
1377
  });
1378
1378
  });
1379
+
1380
+ describe('extractContent BEE Editor Logic (L404-L412)', () => {
1381
+ it('extracts content from beeHtml as string', () => {
1382
+ const platformData = {
1383
+ title: 'Test Title',
1384
+ isBEEeditor: true,
1385
+ beeHtml: '<p>BEE HTML Content</p>',
1386
+ ctas: [
1387
+ { text: 'Click Here', actionLink: 'https://example.com' },
1388
+ ],
1389
+ };
1390
+
1391
+ const result = extractContent(platformData);
1392
+
1393
+ expect(result).toContain('Test Title');
1394
+ expect(result).toContain('<p>BEE HTML Content</p>');
1395
+ expect(result).toContain('Click Here');
1396
+ });
1397
+
1398
+ it('extracts content from beeHtml as object with value property', () => {
1399
+ const platformData = {
1400
+ title: 'Test Title',
1401
+ isBEEeditor: true,
1402
+ beeHtml: { value: '<p>BEE HTML from Object</p>' },
1403
+ ctas: [
1404
+ { text: 'Button Text' },
1405
+ ],
1406
+ };
1407
+
1408
+ const result = extractContent(platformData);
1409
+
1410
+ expect(result).toContain('Test Title');
1411
+ expect(result).toContain('<p>BEE HTML from Object</p>');
1412
+ expect(result).toContain('Button Text');
1413
+ });
1414
+
1415
+ it('handles beeHtml as object without value property', () => {
1416
+ const platformData = {
1417
+ title: 'Test Title',
1418
+ isBEEeditor: true,
1419
+ beeHtml: { someOtherProperty: 'data' },
1420
+ ctas: [],
1421
+ };
1422
+
1423
+ const result = extractContent(platformData);
1424
+
1425
+ // Should extract title and empty beeHtml (since value is undefined)
1426
+ expect(result).toContain('Test Title');
1427
+ expect(result).not.toContain('someOtherProperty');
1428
+ });
1429
+
1430
+ it('extracts ctas with text property', () => {
1431
+ const platformData = {
1432
+ title: 'Title',
1433
+ isBEEeditor: true,
1434
+ beeHtml: '<p>Content</p>',
1435
+ ctas: [
1436
+ { text: 'CTA Text 1' },
1437
+ { text: 'CTA Text 2' },
1438
+ ],
1439
+ };
1440
+
1441
+ const result = extractContent(platformData);
1442
+
1443
+ expect(result).toContain('CTA Text 1');
1444
+ expect(result).toContain('CTA Text 2');
1445
+ });
1446
+
1447
+ it('extracts ctas with actionLink when text is missing', () => {
1448
+ const platformData = {
1449
+ title: 'Title',
1450
+ isBEEeditor: true,
1451
+ beeHtml: '<p>Content</p>',
1452
+ ctas: [
1453
+ { actionLink: 'https://link1.com' },
1454
+ { actionLink: 'https://link2.com' },
1455
+ ],
1456
+ };
1457
+
1458
+ const result = extractContent(platformData);
1459
+
1460
+ expect(result).toContain('https://link1.com');
1461
+ expect(result).toContain('https://link2.com');
1462
+ });
1463
+
1464
+ it('filters out falsy values with filter(Boolean)', () => {
1465
+ const platformData = {
1466
+ title: '',
1467
+ isBEEeditor: true,
1468
+ beeHtml: null,
1469
+ ctas: [
1470
+ { text: null, actionLink: null },
1471
+ { text: 'Valid Text' },
1472
+ ],
1473
+ };
1474
+
1475
+ const result = extractContent(platformData);
1476
+
1477
+ // Should only contain 'Valid Text' after filtering
1478
+ expect(result).toBe('Valid Text');
1479
+ });
1480
+
1481
+ it('handles empty ctas array', () => {
1482
+ const platformData = {
1483
+ title: 'Title Only',
1484
+ isBEEeditor: true,
1485
+ beeHtml: '<p>BEE Content</p>',
1486
+ ctas: [],
1487
+ };
1488
+
1489
+ const result = extractContent(platformData);
1490
+
1491
+ expect(result).toContain('Title Only');
1492
+ expect(result).toContain('<p>BEE Content</p>');
1493
+ });
1494
+
1495
+ it('handles undefined ctas', () => {
1496
+ const platformData = {
1497
+ title: 'Title',
1498
+ isBEEeditor: true,
1499
+ beeHtml: '<p>Content</p>',
1500
+ ctas: undefined,
1501
+ };
1502
+
1503
+ const result = extractContent(platformData);
1504
+
1505
+ expect(result).toContain('Title');
1506
+ expect(result).toContain('<p>Content</p>');
1507
+ });
1508
+
1509
+ it('joins all content with spaces', () => {
1510
+ const platformData = {
1511
+ title: 'Title',
1512
+ isBEEeditor: true,
1513
+ beeHtml: 'HTML',
1514
+ ctas: [
1515
+ { text: 'CTA1' },
1516
+ { text: 'CTA2' },
1517
+ ],
1518
+ };
1519
+
1520
+ const result = extractContent(platformData);
1521
+
1522
+ expect(result).toBe('Title HTML CTA1 CTA2');
1523
+ });
1524
+
1525
+ it('falls back to regular content when not BEE editor', () => {
1526
+ const platformData = {
1527
+ title: 'Title',
1528
+ message: 'Message',
1529
+ isBEEeditor: false,
1530
+ beeHtml: '<p>Should be ignored</p>',
1531
+ ctas: [
1532
+ { text: 'CTA' },
1533
+ ],
1534
+ };
1535
+
1536
+ const result = extractContent(platformData);
1537
+
1538
+ expect(result).toContain('Title');
1539
+ expect(result).toContain('Message');
1540
+ expect(result).toContain('CTA');
1541
+ expect(result).not.toContain('<p>Should be ignored</p>');
1542
+ });
1543
+
1544
+ it('handles null beeHtml', () => {
1545
+ const platformData = {
1546
+ title: 'Title',
1547
+ isBEEeditor: true,
1548
+ beeHtml: null,
1549
+ ctas: [{ text: 'CTA' }],
1550
+ };
1551
+
1552
+ const result = extractContent(platformData);
1553
+
1554
+ expect(result).toContain('Title');
1555
+ expect(result).toContain('CTA');
1556
+ });
1557
+
1558
+ it('handles undefined beeHtml', () => {
1559
+ const platformData = {
1560
+ title: 'Title',
1561
+ isBEEeditor: true,
1562
+ beeHtml: undefined,
1563
+ ctas: [],
1564
+ };
1565
+
1566
+ const result = extractContent(platformData);
1567
+
1568
+ expect(result).toBe('Title');
1569
+ });
1570
+
1571
+ it('handles complex ctas with both text and actionLink', () => {
1572
+ const platformData = {
1573
+ title: 'Title',
1574
+ isBEEeditor: true,
1575
+ beeHtml: 'Content',
1576
+ ctas: [
1577
+ { text: 'CTA Text', actionLink: 'https://example.com' },
1578
+ ],
1579
+ };
1580
+
1581
+ const result = extractContent(platformData);
1582
+
1583
+ // Should prefer text over actionLink
1584
+ expect(result).toContain('CTA Text');
1585
+ expect(result).toContain('Title');
1586
+ expect(result).toContain('Content');
1587
+ });
1588
+
1589
+ it('handles empty string beeHtml', () => {
1590
+ const platformData = {
1591
+ title: 'Title',
1592
+ isBEEeditor: true,
1593
+ beeHtml: '',
1594
+ ctas: [{ text: 'CTA' }],
1595
+ };
1596
+
1597
+ const result = extractContent(platformData);
1598
+
1599
+ expect(result).toBe('Title CTA');
1600
+ });
1601
+ });
1379
1602
  });
1603
+
@@ -28,6 +28,7 @@
28
28
  padding: 0;
29
29
  min-height: 3.25rem;
30
30
  height: 3.25rem;
31
+ position: relative;
31
32
 
32
33
  // Right-align toolbar actions
33
34
  &__right {
@@ -128,6 +129,7 @@
128
129
  padding: 0;
129
130
  min-height: 3.25rem; // 52px = 3.25rem
130
131
  height: 3.25rem;
132
+ position: relative;
131
133
 
132
134
  // Right-align toolbar actions
133
135
  &__right {
@@ -198,7 +198,6 @@
198
198
  }
199
199
 
200
200
  .cm-gutters {
201
- background-color: var(--editor-gutter-bg, $CAP_G02);
202
201
  border-right: 0.0625rem solid var(--editor-border, $CAP_G04);
203
202
  color: var(--editor-gutter-foreground, $FONT_COLOR_02);
204
203
  }
@@ -9,7 +9,7 @@
9
9
  */
10
10
 
11
11
  import React, {
12
- forwardRef, useImperativeHandle, useRef, useEffect,
12
+ forwardRef, useImperativeHandle, useRef, useEffect, useCallback,
13
13
  } from 'react';
14
14
  import PropTypes from 'prop-types';
15
15
 
@@ -17,13 +17,11 @@ import PropTypes from 'prop-types';
17
17
  import { EditorState } from '@codemirror/state';
18
18
  import { EditorView, lineNumbers, highlightActiveLine } from '@codemirror/view';
19
19
 
20
- // Import our comprehensive syntax highlighting solution
21
- import { injectIntl, intlShape } from 'react-intl';
22
- import CapRow from '@capillarytech/cap-ui-library/CapRow';
23
- import { createRobustExtensions } from '../../utils/properSyntaxHighlighting';
24
20
 
21
+ import { injectIntl, intlShape } from 'react-intl';
25
22
 
26
23
  // Messages
24
+ import CapRow from '@capillarytech/cap-ui-library/CapRow';
27
25
  import messages from '../../messages';
28
26
 
29
27
  // Cap UI Components
@@ -31,27 +29,34 @@ import messages from '../../messages';
31
29
  // Components
32
30
  import TagList from '../../../../v2Containers/TagList';
33
31
 
32
+ // Constants - removed unused imports since tag fetching is handled by parent
33
+
34
34
  // Context
35
35
  import { useEditorContext } from '../common/EditorContext';
36
36
 
37
37
  // Styles
38
38
  import './_codeEditorPane.scss';
39
39
 
40
+ // Define Theme and Highlighting inline to avoid "multiple instances of @codemirror/state" error
41
+
42
+
40
43
  // Legacy CodeMirrorEditor removed - using enhanced implementation only
41
44
 
42
45
  const CodeEditorPaneComponent = ({
43
46
  intl,
44
47
  readOnly = false,
45
48
  className = '',
46
- onLabelInsert,
47
49
  forwardedRef,
48
- // Tags-related props
50
+ // Tag-related props - tags are fetched and managed by parent component
49
51
  tags = [],
50
52
  injectedTags = {},
51
- location = {},
52
- selectedOfferDetails = null,
53
- onTagSelect = null,
54
- onContextChange = null,
53
+ location,
54
+ eventContextTags = [],
55
+ selectedOfferDetails = [],
56
+ channel,
57
+ userLocale = 'en',
58
+ moduleFilterEnabled = true,
59
+ onTagContextChange,
55
60
  }) => {
56
61
  const { content } = useEditorContext();
57
62
  const { content: contentValue, updateContent } = content;
@@ -129,77 +134,69 @@ const CodeEditorPaneComponent = ({
129
134
 
130
135
  const handleTagSelect = (tagData) => {
131
136
  // Get the unified editor
132
- if (!viewRef.current) {
133
- console.warn('CodeEditorPane: Editor view not initialized. Cannot insert tag.');
134
- // Try to notify parent if onLabelInsert is available
135
- if (onLabelInsert) {
136
- // Extract tag text for notification purposes
137
- let tagText = '';
138
- if (typeof tagData === 'string') {
139
- tagText = tagData;
140
- } else if (tagData) {
141
- const { text, name, label, value } = tagData;
142
- tagText = text || name || label || value || '';
137
+ if (viewRef.current) {
138
+ const view = viewRef.current;
139
+ const { state: { selection: { main: { head: pos } } } } = view;
140
+
141
+ // Extract tag text from the tagData object using destructuring
142
+ let tagText = '';
143
+ if (typeof tagData === 'string') {
144
+ tagText = tagData;
145
+ } else if (tagData) {
146
+ const {
147
+ text, name, label, value,
148
+ } = tagData;
149
+ tagText = text || name || label || value;
150
+ if (!tagText) {
151
+ console.warn('Invalid tag data:', tagData);
152
+ return;
143
153
  }
144
- const formattedTag = tagText ? `{{${tagText}}}` : '';
145
- // Call onLabelInsert with null position to indicate editor not ready
146
- onLabelInsert(formattedTag, null);
147
- }
148
- return;
149
- }
150
-
151
- const view = viewRef.current;
152
- const { state: { selection: { main: { head: pos } } } } = view;
153
-
154
- // Extract tag text from the tagData object using destructuring
155
- let tagText = '';
156
- if (typeof tagData === 'string') {
157
- tagText = tagData;
158
- } else if (tagData) {
159
- const {
160
- text, name, label, value,
161
- } = tagData;
162
- tagText = text || name || label || value;
163
- if (!tagText) {
154
+ } else {
164
155
  console.warn('Invalid tag data:', tagData);
165
156
  return;
166
157
  }
167
- } else {
168
- console.warn('Invalid tag data:', tagData);
169
- return;
170
- }
171
158
 
172
- // For unified HTML editor, insert as template variable
173
- const formattedTag = `{{${tagText}}}`;
159
+ // For unified HTML editor, insert as template variable
160
+ const formattedTag = `{{${tagText}}}`;
174
161
 
175
- // Insert the tag at cursor position
176
- view.dispatch({
177
- changes: { from: pos, insert: formattedTag },
178
- selection: { anchor: pos + formattedTag.length },
179
- });
162
+ // Insert the tag at cursor position directly
163
+ view.dispatch({
164
+ changes: { from: pos, insert: formattedTag },
165
+ selection: { anchor: pos + formattedTag.length },
166
+ });
180
167
 
181
- // Focus back to editor
182
- view.focus();
168
+ // Focus back to editor
169
+ view.focus();
183
170
 
184
- // Call the parent's handleLabelInsert for notification purposes only
185
- // The tag has already been inserted above, so this is just for parent component awareness
186
- if (onLabelInsert) {
187
- onLabelInsert(formattedTag, pos);
171
+ // Note: We don't call onLabelInsert here because:
172
+ // 1. The tag is already inserted directly into the editor
173
+ // 2. onLabelInsert (handleLabelInsert from HTMLEditor) would try to insert again
174
+ // 3. This causes "Editor method not available" error
175
+ // The direct insertion via view.dispatch is sufficient
188
176
  }
189
177
  };
190
178
 
179
+ // Handle tag context change - delegate to parent component
180
+ // Tags are fetched in parent components (EmailHTMLEditor, INAPP, etc.)
181
+ // This component just passes the context change event up
182
+ const handleTagContextChange = useCallback((data) => {
183
+ if (onTagContextChange) {
184
+ // Parent component handles tag fetching and updates
185
+ onTagContextChange(data);
186
+ }
187
+ // No fallback - tags must be managed by parent component
188
+ }, [onTagContextChange]);
189
+
191
190
  // Initialize CodeMirror effect
192
191
  useEffect(() => {
193
192
  if (editorRef.current && !viewRef.current) {
194
- // Use the comprehensive extensions from properSyntaxHighlighting.js
195
- // This includes: html(), syntaxHighlighting(comprehensiveVSCodeTheme), cleanEditorTheme
196
- const robustExtensions = createRobustExtensions();
197
-
198
193
  // Add additional extensions for line numbers, active line, and update listener
199
194
  const extensions = [
200
195
  lineNumbers(),
201
196
  highlightActiveLine(),
202
- ...robustExtensions, // Spread the robust extensions (html, syntax highlighting, theme)
197
+ // html(), // 1. HTML language support - TEMPORARILY DISABLED due to version conflict
198
+ // syntaxHighlighting(comprehensiveVSCodeTheme), // 2. Syntax highlighting - TEMPORARILY DISABLED
199
+ // cleanEditorTheme, // 3. Theme - TEMPORARILY DISABLED
203
200
  EditorView.updateListener.of((update) => {
204
201
  if (update.docChanged) {
205
202
  updateContentRef.current(update.state.doc.toString());
@@ -257,28 +254,19 @@ const CodeEditorPaneComponent = ({
257
254
  <TagList
258
255
  key="html-editor-taglist"
259
256
  label={intl.formatMessage(messages.addLabel)}
260
- onTagSelect={(tag) => {
261
- // Always use handleTagSelect to insert tag at cursor position
262
- handleTagSelect(tag);
263
- // Also call parent's onTagSelect callback if provided
264
- if (onTagSelect) {
265
- onTagSelect(tag);
266
- }
267
- }}
268
- onContextChange={(context) => {
269
- if (onContextChange) {
270
- onContextChange(context);
271
- }
272
- }}
257
+ onTagSelect={handleTagSelect}
258
+ onContextChange={handleTagContextChange}
273
259
  className="tag-list-trigger"
274
- tags={tags} // Use passed tags from parent component
275
- injectedTags={injectedTags} // Use passed injectedTags from parent component
276
- location={location} // Use passed location for context
277
- selectedOfferDetails={selectedOfferDetails} // Use passed selectedOfferDetails
278
- moduleFilterEnabled={
279
- location && location?.query && location?.query?.type !== "embedded"
280
- }
260
+ tags={tags}
261
+ injectedTags={injectedTags}
262
+ moduleFilterEnabled={moduleFilterEnabled}
263
+ userLocale={userLocale}
264
+ channel={channel}
281
265
  disabled={readOnly}
266
+ location={location}
267
+ selectedOfferDetails={selectedOfferDetails}
268
+ eventContextTags={eventContextTags}
269
+ popoverPlacement="rightTop"
282
270
  />
283
271
  </CapRow>
284
272
  </div>
@@ -301,6 +289,16 @@ CodeEditorPane.propTypes = {
301
289
  className: PropTypes.string,
302
290
  isFullscreenMode: PropTypes.bool,
303
291
  onLabelInsert: PropTypes.func,
292
+ // Tag-related props - tags are fetched and managed by parent component
293
+ tags: PropTypes.array,
294
+ injectedTags: PropTypes.object,
295
+ location: PropTypes.object,
296
+ eventContextTags: PropTypes.array,
297
+ selectedOfferDetails: PropTypes.array,
298
+ channel: PropTypes.string,
299
+ userLocale: PropTypes.string,
300
+ moduleFilterEnabled: PropTypes.bool,
301
+ onTagContextChange: PropTypes.func, // Required - parent must handle tag fetching
304
302
  };
305
303
 
306
304
  // Export with injectIntl - ref forwarding is handled by forwardRef wrapper
@@ -9,7 +9,7 @@
9
9
  .html-editor .device-toggle {
10
10
  display: flex;
11
11
  align-items: center;
12
- gap: 1rem;
12
+ margin-left: 2.5rem;
13
13
  padding: 0;
14
14
  background-color: $CAP_G10;
15
15
  border-radius: 0.25rem 0.25rem 0 0;
@@ -49,6 +49,7 @@ body .ant-modal-mask+.ant-modal-wrap .ant-modal.html-editor-fullscreen-modal .an
49
49
  padding: 0;
50
50
  min-height: 3.25rem;
51
51
  height: 3.25rem;
52
+ position: relative;
52
53
 
53
54
  &__right {
54
55
  margin-left: auto;