@capillarytech/creatives-library 8.0.316-alpha.3 → 8.0.316
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/v2Containers/WebPush/Create/components/MessageSection.js +54 -18
- package/v2Containers/WebPush/Create/components/MessageSection.test.js +28 -0
- package/v2Containers/WebPush/Create/components/__snapshots__/MessageSection.test.js.snap +7 -3
- package/v2Containers/Whatsapp/index.js +4 -3
- package/v2Containers/Whatsapp/tests/index.test.js +172 -0
package/package.json
CHANGED
|
@@ -12,6 +12,52 @@ import CapAskAira from '@capillarytech/cap-ui-library/CapAskAira';
|
|
|
12
12
|
import { MESSAGE_MAX_LENGTH, SHOW_CHARACTER_COUNT } from '../../constants';
|
|
13
13
|
import { useAiraTriggerPosition } from '../hooks/useAiraTriggerPosition';
|
|
14
14
|
|
|
15
|
+
const InputWrapper = ({
|
|
16
|
+
children,
|
|
17
|
+
inputRef,
|
|
18
|
+
error,
|
|
19
|
+
value,
|
|
20
|
+
onChange,
|
|
21
|
+
isAiContentBotDisabled,
|
|
22
|
+
}) => {
|
|
23
|
+
const wrapperRef = useRef(null);
|
|
24
|
+
const airaRootStyle = useAiraTriggerPosition({ wrapperRef, inputRef, error });
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<div className="webpush-message-input-wrapper" ref={wrapperRef}>
|
|
28
|
+
{children}
|
|
29
|
+
{!isAiContentBotDisabled && (
|
|
30
|
+
<CapAskAira.ContentGenerationBot
|
|
31
|
+
text={value || ''}
|
|
32
|
+
setText={(text) => onChange(text)}
|
|
33
|
+
iconPlacement="float-br"
|
|
34
|
+
iconSize="1.6rem"
|
|
35
|
+
rootStyle={airaRootStyle}
|
|
36
|
+
/>
|
|
37
|
+
)}
|
|
38
|
+
</div>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
InputWrapper.propTypes = {
|
|
43
|
+
children: PropTypes.node.isRequired,
|
|
44
|
+
inputRef: PropTypes.oneOfType([
|
|
45
|
+
PropTypes.shape({ current: PropTypes.any }),
|
|
46
|
+
PropTypes.func,
|
|
47
|
+
]),
|
|
48
|
+
error: PropTypes.string,
|
|
49
|
+
value: PropTypes.string,
|
|
50
|
+
onChange: PropTypes.func.isRequired,
|
|
51
|
+
isAiContentBotDisabled: PropTypes.bool,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
InputWrapper.defaultProps = {
|
|
55
|
+
inputRef: null,
|
|
56
|
+
error: '',
|
|
57
|
+
value: '',
|
|
58
|
+
isAiContentBotDisabled: false,
|
|
59
|
+
};
|
|
60
|
+
|
|
15
61
|
/**
|
|
16
62
|
* MessageSection component - Message textarea with tags, emoji picker, and character count
|
|
17
63
|
*/
|
|
@@ -27,13 +73,6 @@ export const MessageSection = ({
|
|
|
27
73
|
handleMessageTextAreaRef,
|
|
28
74
|
isAiContentBotDisabled,
|
|
29
75
|
}) => {
|
|
30
|
-
const messageInputWrapperRef = useRef(null);
|
|
31
|
-
const airaRootStyle = useAiraTriggerPosition({
|
|
32
|
-
wrapperRef: messageInputWrapperRef,
|
|
33
|
-
inputRef: messageTextAreaRef,
|
|
34
|
-
error,
|
|
35
|
-
});
|
|
36
|
-
|
|
37
76
|
const renderCharacterCount = () => {
|
|
38
77
|
if (!SHOW_CHARACTER_COUNT) return null;
|
|
39
78
|
|
|
@@ -60,7 +99,13 @@ export const MessageSection = ({
|
|
|
60
99
|
<CapHeading type="h4" className="webpush-message">
|
|
61
100
|
<FormattedMessage {...messages.message} />
|
|
62
101
|
</CapHeading>
|
|
63
|
-
<
|
|
102
|
+
<InputWrapper
|
|
103
|
+
inputRef={messageTextAreaRef}
|
|
104
|
+
error={error}
|
|
105
|
+
value={value}
|
|
106
|
+
onChange={onChange}
|
|
107
|
+
isAiContentBotDisabled={isAiContentBotDisabled}
|
|
108
|
+
>
|
|
64
109
|
<CapEmojiPicker.Wrapper
|
|
65
110
|
value={value}
|
|
66
111
|
onChange={onChange}
|
|
@@ -84,16 +129,7 @@ export const MessageSection = ({
|
|
|
84
129
|
}
|
|
85
130
|
/>
|
|
86
131
|
</CapEmojiPicker.Wrapper>
|
|
87
|
-
|
|
88
|
-
<CapAskAira.ContentGenerationBot
|
|
89
|
-
text={value || ''}
|
|
90
|
-
setText={(text) => onChange(text)}
|
|
91
|
-
iconPlacement="float-br"
|
|
92
|
-
iconSize="1.6rem"
|
|
93
|
-
rootStyle={airaRootStyle}
|
|
94
|
-
/>
|
|
95
|
-
)}
|
|
96
|
-
</div>
|
|
132
|
+
</InputWrapper>
|
|
97
133
|
{renderCharacterCount()}
|
|
98
134
|
</CapRow>
|
|
99
135
|
<CapDivider className="webpush-message-divider" />
|
|
@@ -318,5 +318,33 @@ describe('MessageSection', () => {
|
|
|
318
318
|
expect(textArea.prop('value')).toBe(longValue);
|
|
319
319
|
});
|
|
320
320
|
});
|
|
321
|
+
|
|
322
|
+
describe('InputWrapper', () => {
|
|
323
|
+
it('should render the wrapper div with correct class', () => {
|
|
324
|
+
const wrapper = mountWithIntl(<MessageSection {...defaultProps} />);
|
|
325
|
+
const inputWrapper = wrapper.find('.webpush-message-input-wrapper');
|
|
326
|
+
expect(inputWrapper.exists()).toBe(true);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it('should render children inside InputWrapper', () => {
|
|
330
|
+
const wrapper = mountWithIntl(<MessageSection {...defaultProps} />);
|
|
331
|
+
const inputWrapper = wrapper.find('.webpush-message-input-wrapper');
|
|
332
|
+
expect(inputWrapper.find(CapEmojiPicker.Wrapper).exists()).toBe(true);
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it('should not render aiRA bot inside InputWrapper when disabled', () => {
|
|
336
|
+
const wrapper = mountWithIntl(<MessageSection {...defaultProps} isAiContentBotDisabled />);
|
|
337
|
+
const inputWrapper = wrapper.find('.webpush-message-input-wrapper');
|
|
338
|
+
expect(inputWrapper.find(CapAskAira.ContentGenerationBot).exists()).toBe(false);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it('should render aiRA bot inside InputWrapper when enabled', () => {
|
|
342
|
+
const wrapper = mountWithIntl(
|
|
343
|
+
<MessageSection {...defaultProps} isAiContentBotDisabled={false} />
|
|
344
|
+
);
|
|
345
|
+
const inputWrapper = wrapper.find('.webpush-message-input-wrapper');
|
|
346
|
+
expect(inputWrapper.find(CapAskAira.ContentGenerationBot).exists()).toBe(true);
|
|
347
|
+
});
|
|
348
|
+
});
|
|
321
349
|
});
|
|
322
350
|
|
|
@@ -18,8 +18,12 @@ exports[`MessageSection Rendering should render correctly with default props 1`]
|
|
|
18
18
|
values={Object {}}
|
|
19
19
|
/>
|
|
20
20
|
</CapHeading>
|
|
21
|
-
<
|
|
22
|
-
|
|
21
|
+
<InputWrapper
|
|
22
|
+
error=""
|
|
23
|
+
inputRef={null}
|
|
24
|
+
isAiContentBotDisabled={true}
|
|
25
|
+
onChange={[MockFunction]}
|
|
26
|
+
value="Test Message"
|
|
23
27
|
>
|
|
24
28
|
<InjectIntl(Wrapper)
|
|
25
29
|
onChange={[MockFunction]}
|
|
@@ -43,7 +47,7 @@ exports[`MessageSection Rendering should render correctly with default props 1`]
|
|
|
43
47
|
value="Test Message"
|
|
44
48
|
/>
|
|
45
49
|
</InjectIntl(Wrapper)>
|
|
46
|
-
</
|
|
50
|
+
</InputWrapper>
|
|
47
51
|
<CapLabel
|
|
48
52
|
className="webpush-character-count"
|
|
49
53
|
type="label2"
|
|
@@ -2957,12 +2957,13 @@ const isAuthenticationTemplate = isEqual(templateCategory, WHATSAPP_CATEGORIES.a
|
|
|
2957
2957
|
};
|
|
2958
2958
|
|
|
2959
2959
|
const isEditDoneDisabled = () => {
|
|
2960
|
+
const isBlankInput = (inputValue) => !String(inputValue ?? '').trim();
|
|
2960
2961
|
let carouselDisableCheck = false;
|
|
2961
2962
|
if (isMediaTypeCarousel) {
|
|
2962
2963
|
carouselDisableCheck = carouselData.some((data) => {
|
|
2963
2964
|
return (
|
|
2964
2965
|
data.carouselTagValidationErr ||
|
|
2965
|
-
Object.values(data.varMap).some((inputValue) => inputValue
|
|
2966
|
+
Object.values(data.varMap).some((inputValue) => isBlankInput(inputValue)) ||
|
|
2966
2967
|
computeTextLength(CAROUSEL_TEXT, data) > TEMPLATE_MESSAGE_MAX_LENGTH ||
|
|
2967
2968
|
(carouselMediaType === IMAGE.toLowerCase() && !data.imageSrc) ||
|
|
2968
2969
|
(carouselMediaType === VIDEO.toLowerCase() && !data.videoSrc) ||
|
|
@@ -2972,8 +2973,8 @@ const isAuthenticationTemplate = isEqual(templateCategory, WHATSAPP_CATEGORIES.a
|
|
|
2972
2973
|
}
|
|
2973
2974
|
return (isTagValidationError ||
|
|
2974
2975
|
isHeaderTagValidationError ||
|
|
2975
|
-
Object.values(varMap).some((inputValue) => inputValue
|
|
2976
|
-
Object.values(headerVarMappedData).some((inputValue) => inputValue
|
|
2976
|
+
Object.values(varMap).some((inputValue) => isBlankInput(inputValue)) ||
|
|
2977
|
+
Object.values(headerVarMappedData).some((inputValue) => isBlankInput(inputValue)) ||
|
|
2977
2978
|
computeTextLength(MESSAGE_TEXT) > TEMPLATE_MESSAGE_MAX_LENGTH ||
|
|
2978
2979
|
computeTextLength(HEADER_TEXT) > TEMPLATE_HEADER_MAX_LENGTH ||
|
|
2979
2980
|
(isBtnTypeCta && ctaData.some((btn) => btn?.url?.includes("{{1}}"))) || isMediatypeValid()) || carouselDisableCheck;
|
|
@@ -1354,3 +1354,175 @@ describe('Haptic carousel file handle mapping', () => {
|
|
|
1354
1354
|
expect(cd[0].karixFileHandle).toBe('');
|
|
1355
1355
|
});
|
|
1356
1356
|
});
|
|
1357
|
+
|
|
1358
|
+
describe('WhatsApp edit Done button whitespace validation', () => {
|
|
1359
|
+
let renderedComponent;
|
|
1360
|
+
|
|
1361
|
+
const createWhatsappTemplate = jest.fn();
|
|
1362
|
+
const getFormData = jest.fn();
|
|
1363
|
+
const clearCreateResponse = jest.fn();
|
|
1364
|
+
const getTemplateDetails = jest.fn();
|
|
1365
|
+
const resetEditTemplate = jest.fn();
|
|
1366
|
+
const fetchSchemaForEntity = jest.fn();
|
|
1367
|
+
const handleClose = jest.fn();
|
|
1368
|
+
const onCreateComplete = jest.fn();
|
|
1369
|
+
const formatMessage = jest.fn();
|
|
1370
|
+
const getMetaTags = jest.fn();
|
|
1371
|
+
const resetMetaTags = jest.fn();
|
|
1372
|
+
|
|
1373
|
+
const mountEditFlow = (editData, accountData = mockData.accountData1) => {
|
|
1374
|
+
renderedComponent = mountWithIntl(
|
|
1375
|
+
<Provider store={store}>
|
|
1376
|
+
<Whatsapp
|
|
1377
|
+
actions={{
|
|
1378
|
+
createWhatsappTemplate,
|
|
1379
|
+
clearCreateResponse,
|
|
1380
|
+
getTemplateDetails,
|
|
1381
|
+
resetEditTemplate,
|
|
1382
|
+
getMetaTags,
|
|
1383
|
+
resetMetaTags,
|
|
1384
|
+
}}
|
|
1385
|
+
globalActions={{ fetchSchemaForEntity }}
|
|
1386
|
+
onCreateComplete={onCreateComplete}
|
|
1387
|
+
handleClose={handleClose}
|
|
1388
|
+
getFormData={getFormData}
|
|
1389
|
+
params={{ id: editData?.templateDetails?._id || 'edit-whitespace-id' }}
|
|
1390
|
+
editData={editData}
|
|
1391
|
+
accountData={accountData}
|
|
1392
|
+
location={mockData.location}
|
|
1393
|
+
intl={{ formatMessage }}
|
|
1394
|
+
isFullMode={false}
|
|
1395
|
+
isEditFlow
|
|
1396
|
+
loadingTags={false}
|
|
1397
|
+
metaEntities={[]}
|
|
1398
|
+
getDefaultTags
|
|
1399
|
+
Templates={{
|
|
1400
|
+
senderDetails: {
|
|
1401
|
+
status: 'SUCCESS',
|
|
1402
|
+
domainProperties: [
|
|
1403
|
+
{
|
|
1404
|
+
domainProperties: {
|
|
1405
|
+
connectionProperties: {
|
|
1406
|
+
sourceAccountIdentifier: '2000222347',
|
|
1407
|
+
baseUrl: 'https://api.gupshup.io/whatsapp/v1',
|
|
1408
|
+
},
|
|
1409
|
+
},
|
|
1410
|
+
},
|
|
1411
|
+
],
|
|
1412
|
+
},
|
|
1413
|
+
}}
|
|
1414
|
+
/>
|
|
1415
|
+
</Provider>,
|
|
1416
|
+
);
|
|
1417
|
+
};
|
|
1418
|
+
|
|
1419
|
+
const getDoneButton = () => renderedComponent.find('CapButton.whatsapp-create-btn').at(0);
|
|
1420
|
+
|
|
1421
|
+
const getFirstVariableTextarea = () => renderedComponent
|
|
1422
|
+
.find('textarea.TextArea__StyledTextArea-sc-177dfyt-2')
|
|
1423
|
+
.at(0);
|
|
1424
|
+
|
|
1425
|
+
it('keeps Done disabled for whitespace-only message variable', () => {
|
|
1426
|
+
const editData = {
|
|
1427
|
+
templateDetails: {
|
|
1428
|
+
_id: 'edit-msg-whitespace',
|
|
1429
|
+
name: 'msg_whitespace',
|
|
1430
|
+
type: 'WHATSAPP',
|
|
1431
|
+
versions: {
|
|
1432
|
+
base: {
|
|
1433
|
+
content: {
|
|
1434
|
+
whatsapp: {
|
|
1435
|
+
status: 'approved',
|
|
1436
|
+
category: 'ALERT_UPDATE',
|
|
1437
|
+
mediaType: 'TEXT',
|
|
1438
|
+
buttonType: 'NONE',
|
|
1439
|
+
varMapped: {},
|
|
1440
|
+
languages: [{ language: 'en', content: 'Your code is {{1}}' }],
|
|
1441
|
+
},
|
|
1442
|
+
},
|
|
1443
|
+
},
|
|
1444
|
+
},
|
|
1445
|
+
},
|
|
1446
|
+
};
|
|
1447
|
+
|
|
1448
|
+
mountEditFlow(editData);
|
|
1449
|
+
getFirstVariableTextarea().simulate('change', { target: { value: ' ', id: '{{1}}_3' } });
|
|
1450
|
+
renderedComponent.update();
|
|
1451
|
+
|
|
1452
|
+
expect(getDoneButton().props().disabled).toBe(true);
|
|
1453
|
+
});
|
|
1454
|
+
|
|
1455
|
+
it('keeps Done disabled for whitespace-only header variable', () => {
|
|
1456
|
+
const editData = {
|
|
1457
|
+
templateDetails: {
|
|
1458
|
+
_id: 'edit-header-whitespace',
|
|
1459
|
+
name: 'header_whitespace',
|
|
1460
|
+
type: 'WHATSAPP',
|
|
1461
|
+
versions: {
|
|
1462
|
+
base: {
|
|
1463
|
+
content: {
|
|
1464
|
+
whatsapp: {
|
|
1465
|
+
status: 'approved',
|
|
1466
|
+
category: 'ALERT_UPDATE',
|
|
1467
|
+
mediaType: 'TEXT',
|
|
1468
|
+
buttonType: 'NONE',
|
|
1469
|
+
varMapped: {},
|
|
1470
|
+
whatsappMedia: {
|
|
1471
|
+
header: 'Hi {{1}}',
|
|
1472
|
+
footer: '',
|
|
1473
|
+
headerVarMapped: {},
|
|
1474
|
+
},
|
|
1475
|
+
languages: [{ language: 'en', content: 'Simple message without vars' }],
|
|
1476
|
+
},
|
|
1477
|
+
},
|
|
1478
|
+
},
|
|
1479
|
+
},
|
|
1480
|
+
},
|
|
1481
|
+
};
|
|
1482
|
+
|
|
1483
|
+
mountEditFlow(editData);
|
|
1484
|
+
getFirstVariableTextarea().simulate('change', { target: { value: ' ', id: '{{1}}_1' } });
|
|
1485
|
+
renderedComponent.update();
|
|
1486
|
+
|
|
1487
|
+
expect(getDoneButton().props().disabled).toBe(true);
|
|
1488
|
+
});
|
|
1489
|
+
|
|
1490
|
+
it('keeps Done disabled for whitespace-only carousel variable', () => {
|
|
1491
|
+
const editData = {
|
|
1492
|
+
templateDetails: {
|
|
1493
|
+
_id: 'edit-carousel-whitespace',
|
|
1494
|
+
name: 'carousel_whitespace',
|
|
1495
|
+
type: 'WHATSAPP',
|
|
1496
|
+
versions: {
|
|
1497
|
+
base: {
|
|
1498
|
+
content: {
|
|
1499
|
+
whatsapp: {
|
|
1500
|
+
status: 'approved',
|
|
1501
|
+
category: 'MARKETING',
|
|
1502
|
+
mediaType: 'CAROUSEL',
|
|
1503
|
+
buttonType: 'NONE',
|
|
1504
|
+
varMapped: {},
|
|
1505
|
+
languages: [{ language: 'en', content: 'Main message' }],
|
|
1506
|
+
carouselData: [
|
|
1507
|
+
{
|
|
1508
|
+
mediaType: 'image',
|
|
1509
|
+
imageUrl: 'https://cdn.example.com/card.jpg',
|
|
1510
|
+
bodyText: 'Card body {{1}}',
|
|
1511
|
+
varMap: {},
|
|
1512
|
+
buttons: [],
|
|
1513
|
+
},
|
|
1514
|
+
],
|
|
1515
|
+
},
|
|
1516
|
+
},
|
|
1517
|
+
},
|
|
1518
|
+
},
|
|
1519
|
+
},
|
|
1520
|
+
};
|
|
1521
|
+
|
|
1522
|
+
mountEditFlow(editData, mockData.accountData2);
|
|
1523
|
+
getFirstVariableTextarea().simulate('change', { target: { value: ' ', id: '{{1}}_2' } });
|
|
1524
|
+
renderedComponent.update();
|
|
1525
|
+
|
|
1526
|
+
expect(getDoneButton().props().disabled).toBe(true);
|
|
1527
|
+
});
|
|
1528
|
+
});
|