@capillarytech/creatives-library 8.0.236-alpha.2 → 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 +1 -1
- package/utils/tests/commonUtil.test.js +224 -0
- package/v2Components/HtmlEditor/_htmlEditor.scss +2 -0
- package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +0 -1
- package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +98 -186
- package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +1 -1
- package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +1 -0
- package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +203 -0
- package/v2Components/HtmlEditor/hooks/useInAppContent.js +5 -4
- package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +23 -62
- package/v2Components/TemplatePreview/_templatePreview.scss +11 -1
- package/v2Containers/BeePopupEditor/index.js +5 -4
- package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +40 -74
- package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +2 -67
- package/v2Containers/InApp/constants.js +1 -0
- package/v2Containers/InApp/index.js +174 -115
- package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +4 -4
- package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +1 -1
- package/v2Containers/InappAdvance/index.js +36 -10
- package/v2Containers/InappAdvance/tests/index.test.js +32 -32
- package/v2Containers/Templates/_templates.scss +11 -0
package/package.json
CHANGED
|
@@ -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 {
|
|
@@ -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,30 +29,36 @@ 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
|
-
isFullscreenMode = false,
|
|
47
|
-
onLabelInsert,
|
|
48
49
|
forwardedRef,
|
|
49
|
-
//
|
|
50
|
+
// Tag-related props - tags are fetched and managed by parent component
|
|
50
51
|
tags = [],
|
|
51
52
|
injectedTags = {},
|
|
52
|
-
location
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
location,
|
|
54
|
+
eventContextTags = [],
|
|
55
|
+
selectedOfferDetails = [],
|
|
56
|
+
channel,
|
|
57
|
+
userLocale = 'en',
|
|
58
|
+
moduleFilterEnabled = true,
|
|
59
|
+
onTagContextChange,
|
|
56
60
|
}) => {
|
|
57
|
-
const { content
|
|
61
|
+
const { content } = useEditorContext();
|
|
58
62
|
const { content: contentValue, updateContent } = content;
|
|
59
63
|
const editorRef = useRef(null);
|
|
60
64
|
const viewRef = useRef(null);
|
|
@@ -130,178 +134,85 @@ const CodeEditorPaneComponent = ({
|
|
|
130
134
|
|
|
131
135
|
const handleTagSelect = (tagData) => {
|
|
132
136
|
// Get the unified editor
|
|
133
|
-
if (
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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;
|
|
146
153
|
}
|
|
147
|
-
|
|
148
|
-
// Call onLabelInsert with null position to indicate editor not ready
|
|
149
|
-
onLabelInsert(formattedTag, null);
|
|
150
|
-
}
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const view = viewRef.current;
|
|
155
|
-
const { state: { selection: { main: { head: pos } } } } = view;
|
|
156
|
-
|
|
157
|
-
// Extract tag text from the tagData object using destructuring
|
|
158
|
-
let tagText = '';
|
|
159
|
-
if (typeof tagData === 'string') {
|
|
160
|
-
tagText = tagData;
|
|
161
|
-
} else if (tagData) {
|
|
162
|
-
const {
|
|
163
|
-
text, name, label, value,
|
|
164
|
-
} = tagData;
|
|
165
|
-
tagText = text || name || label || value;
|
|
166
|
-
if (!tagText) {
|
|
154
|
+
} else {
|
|
167
155
|
console.warn('Invalid tag data:', tagData);
|
|
168
156
|
return;
|
|
169
157
|
}
|
|
170
|
-
} else {
|
|
171
|
-
console.warn('Invalid tag data:', tagData);
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
158
|
|
|
175
|
-
|
|
176
|
-
|
|
159
|
+
// For unified HTML editor, insert as template variable
|
|
160
|
+
const formattedTag = `{{${tagText}}}`;
|
|
177
161
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
162
|
+
// Insert the tag at cursor position directly
|
|
163
|
+
view.dispatch({
|
|
164
|
+
changes: { from: pos, insert: formattedTag },
|
|
165
|
+
selection: { anchor: pos + formattedTag.length },
|
|
166
|
+
});
|
|
183
167
|
|
|
184
|
-
|
|
185
|
-
|
|
168
|
+
// Focus back to editor
|
|
169
|
+
view.focus();
|
|
186
170
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
|
191
176
|
}
|
|
192
177
|
};
|
|
193
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
|
+
|
|
194
190
|
// Initialize CodeMirror effect
|
|
195
191
|
useEffect(() => {
|
|
196
192
|
if (editorRef.current && !viewRef.current) {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
highlightActiveLine: typeof highlightActiveLine,
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
// Use the comprehensive extensions from properSyntaxHighlighting.js
|
|
209
|
-
// This includes: html(), syntaxHighlighting(comprehensiveVSCodeTheme), cleanEditorTheme
|
|
210
|
-
// Note: Webpack configuration ensures CodeMirror packages are in a single shared chunk
|
|
211
|
-
// to prevent multiple instances that break instanceof checks
|
|
212
|
-
const robustExtensions = createRobustExtensions();
|
|
213
|
-
|
|
214
|
-
console.log('[CodeEditorPane] Robust extensions created:', {
|
|
215
|
-
extensionsCount: robustExtensions.length,
|
|
216
|
-
extensions: robustExtensions.map((ext) => {
|
|
217
|
-
try {
|
|
218
|
-
return typeof ext === 'function' ? 'function' : (ext?.constructor?.name || typeof ext);
|
|
219
|
-
} catch (e) {
|
|
220
|
-
return 'unknown';
|
|
221
|
-
}
|
|
222
|
-
}),
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
// Add additional extensions for line numbers, active line, and update listener
|
|
226
|
-
// IMPORTANT: Ensure all extensions come from the same CodeMirror instance
|
|
227
|
-
const lineNumbersExt = lineNumbers();
|
|
228
|
-
const highlightActiveLineExt = highlightActiveLine();
|
|
229
|
-
const updateListenerExt = EditorView.updateListener.of((update) => {
|
|
193
|
+
// Add additional extensions for line numbers, active line, and update listener
|
|
194
|
+
const extensions = [
|
|
195
|
+
lineNumbers(),
|
|
196
|
+
highlightActiveLine(),
|
|
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
|
|
200
|
+
EditorView.updateListener.of((update) => {
|
|
230
201
|
if (update.docChanged) {
|
|
231
202
|
updateContentRef.current(update.state.doc.toString());
|
|
232
203
|
}
|
|
233
|
-
})
|
|
234
|
-
|
|
235
|
-
// Flatten any nested arrays in robustExtensions (shouldn't happen, but be safe)
|
|
236
|
-
const flattenedRobustExtensions = robustExtensions.flat();
|
|
237
|
-
|
|
238
|
-
const extensions = [
|
|
239
|
-
lineNumbersExt,
|
|
240
|
-
highlightActiveLineExt,
|
|
241
|
-
...flattenedRobustExtensions, // Spread the robust extensions (html, syntax highlighting, theme)
|
|
242
|
-
updateListenerExt,
|
|
243
|
-
];
|
|
244
|
-
|
|
245
|
-
console.log('[CodeEditorPane] All extensions prepared:', {
|
|
246
|
-
totalExtensions: extensions.length,
|
|
247
|
-
extensionTypes: extensions.map((ext) => {
|
|
248
|
-
try {
|
|
249
|
-
return typeof ext === 'function' ? 'function' : (ext?.constructor?.name || typeof ext);
|
|
250
|
-
} catch (e) {
|
|
251
|
-
return 'unknown';
|
|
252
|
-
}
|
|
253
|
-
}),
|
|
254
|
-
extensionDetails: extensions.map((ext, idx) => {
|
|
255
|
-
try {
|
|
256
|
-
return {
|
|
257
|
-
index: idx,
|
|
258
|
-
type: typeof ext,
|
|
259
|
-
constructor: ext?.constructor?.name,
|
|
260
|
-
isArray: Array.isArray(ext),
|
|
261
|
-
isFunction: typeof ext === 'function',
|
|
262
|
-
hasTo: typeof ext?.to === 'function',
|
|
263
|
-
hasOf: typeof ext?.of === 'function',
|
|
264
|
-
// Check if it's a valid CodeMirror extension
|
|
265
|
-
isExtension: ext && (typeof ext === 'function' || typeof ext?.to === 'function' || typeof ext?.of === 'function'),
|
|
266
|
-
};
|
|
267
|
-
} catch (e) {
|
|
268
|
-
return { index: idx, error: e.message };
|
|
269
|
-
}
|
|
270
|
-
}),
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
// Validate extensions before creating EditorState
|
|
274
|
-
const invalidExtensions = extensions.filter((ext) => {
|
|
275
|
-
if (!ext) return true;
|
|
276
|
-
if (typeof ext === 'function') return false; // Functions are valid
|
|
277
|
-
if (typeof ext?.to === 'function') return false; // Extension objects with .to() are valid
|
|
278
|
-
if (typeof ext?.of === 'function') return false; // Extension objects with .of() are valid
|
|
279
|
-
if (Array.isArray(ext)) return true; // Arrays should be flattened
|
|
280
|
-
return true; // Unknown type
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
if (invalidExtensions.length > 0) {
|
|
284
|
-
console.error('[CodeEditorPane] Invalid extensions detected:', invalidExtensions);
|
|
285
|
-
throw new Error(`Invalid CodeMirror extensions detected. This usually means multiple instances of @codemirror packages are loaded.`);
|
|
286
|
-
}
|
|
204
|
+
}),
|
|
205
|
+
];
|
|
287
206
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
});
|
|
293
|
-
console.log('[CodeEditorPane] EditorState created successfully');
|
|
207
|
+
const state = EditorState.create({
|
|
208
|
+
doc: contentValue || '',
|
|
209
|
+
extensions,
|
|
210
|
+
});
|
|
294
211
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
} catch (error) {
|
|
300
|
-
// Log error for debugging - this should not happen if webpack config is correct
|
|
301
|
-
console.error('Error initializing CodeMirror editor:', error);
|
|
302
|
-
// Re-throw to prevent silent failures
|
|
303
|
-
throw error;
|
|
304
|
-
}
|
|
212
|
+
viewRef.current = new EditorView({
|
|
213
|
+
state,
|
|
214
|
+
parent: editorRef.current,
|
|
215
|
+
});
|
|
305
216
|
}
|
|
306
217
|
|
|
307
218
|
return () => {
|
|
@@ -343,28 +254,19 @@ const CodeEditorPaneComponent = ({
|
|
|
343
254
|
<TagList
|
|
344
255
|
key="html-editor-taglist"
|
|
345
256
|
label={intl.formatMessage(messages.addLabel)}
|
|
346
|
-
onTagSelect={
|
|
347
|
-
|
|
348
|
-
handleTagSelect(tag);
|
|
349
|
-
// Also call parent's onTagSelect callback if provided
|
|
350
|
-
if (onTagSelect) {
|
|
351
|
-
onTagSelect(tag);
|
|
352
|
-
}
|
|
353
|
-
}}
|
|
354
|
-
onContextChange={(context) => {
|
|
355
|
-
if (onContextChange) {
|
|
356
|
-
onContextChange(context);
|
|
357
|
-
}
|
|
358
|
-
}}
|
|
257
|
+
onTagSelect={handleTagSelect}
|
|
258
|
+
onContextChange={handleTagContextChange}
|
|
359
259
|
className="tag-list-trigger"
|
|
360
|
-
tags={tags}
|
|
361
|
-
injectedTags={injectedTags}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
location && location?.query && location?.query?.type !== "embedded"
|
|
366
|
-
}
|
|
260
|
+
tags={tags}
|
|
261
|
+
injectedTags={injectedTags}
|
|
262
|
+
moduleFilterEnabled={moduleFilterEnabled}
|
|
263
|
+
userLocale={userLocale}
|
|
264
|
+
channel={channel}
|
|
367
265
|
disabled={readOnly}
|
|
266
|
+
location={location}
|
|
267
|
+
selectedOfferDetails={selectedOfferDetails}
|
|
268
|
+
eventContextTags={eventContextTags}
|
|
269
|
+
popoverPlacement="rightTop"
|
|
368
270
|
/>
|
|
369
271
|
</CapRow>
|
|
370
272
|
</div>
|
|
@@ -387,6 +289,16 @@ CodeEditorPane.propTypes = {
|
|
|
387
289
|
className: PropTypes.string,
|
|
388
290
|
isFullscreenMode: PropTypes.bool,
|
|
389
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
|
|
390
302
|
};
|
|
391
303
|
|
|
392
304
|
// Export with injectIntl - ref forwarding is handled by forwardRef wrapper
|