@capillarytech/creatives-library 9.0.29 → 9.0.31
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/constants/unified.js +29 -0
- package/global-styles.js +1 -1
- package/package.json +1 -1
- package/services/tests/api.test.js +35 -20
- package/utils/commonUtils.js +22 -1
- package/utils/rcsPayloadUtils.js +102 -0
- package/utils/templateVarUtils.js +198 -0
- package/utils/tests/rcsPayloadUtils.test.js +295 -0
- package/utils/tests/templateVarUtils.test.js +204 -0
- package/v2Components/CapActionButton/constants.js +21 -1
- package/v2Components/CapActionButton/index.js +261 -144
- package/v2Components/CapActionButton/index.scss +245 -14
- package/v2Components/CapActionButton/messages.js +32 -3
- package/v2Components/CapActionButton/tests/index.test.js +74 -19
- package/v2Components/CapImageUpload/index.js +2 -2
- package/v2Components/CapTagList/index.js +9 -0
- package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +70 -48
- package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +8 -2
- package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +214 -21
- package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +16 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +83 -9
- package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +30 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +58 -11
- package/v2Components/CommonTestAndPreview/SendTestMessage.js +10 -5
- package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +186 -22
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +381 -80
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +155 -8
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +11 -0
- package/v2Components/CommonTestAndPreview/constants.js +35 -2
- package/v2Components/CommonTestAndPreview/index.js +774 -231
- package/v2Components/CommonTestAndPreview/messages.js +45 -3
- package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
- package/v2Components/CommonTestAndPreview/sagas.js +25 -6
- package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +1 -0
- package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +412 -257
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +337 -63
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +118 -5
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +341 -0
- package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +8 -1
- package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +108 -15
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +281 -283
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -1
- package/v2Components/CommonTestAndPreview/tests/index.test.js +133 -4
- package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +31 -24
- package/v2Components/CommonTestAndPreview/tests/utils.test.js +151 -0
- package/v2Components/CommonTestAndPreview/utils.js +84 -0
- package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +91 -0
- package/v2Components/SmsFallback/constants.js +94 -0
- package/v2Components/SmsFallback/index.js +958 -0
- package/v2Components/SmsFallback/index.scss +266 -0
- package/v2Components/SmsFallback/messages.js +78 -0
- package/v2Components/SmsFallback/smsFallbackUtils.js +120 -0
- package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +50 -0
- package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +147 -0
- package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +304 -0
- package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +208 -0
- package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +309 -0
- package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +471 -0
- package/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
- package/v2Components/TemplatePreview/_templatePreview.scss +45 -25
- package/v2Components/TemplatePreview/constants.js +2 -0
- package/v2Components/TemplatePreview/index.js +147 -32
- package/v2Components/TemplatePreview/tests/index.test.js +142 -0
- package/v2Components/TestAndPreviewSlidebox/CustomValuesEditor.js +6 -6
- package/v2Components/TestAndPreviewSlidebox/index.js +13 -1
- package/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
- package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
- package/v2Components/VarSegmentMessageEditor/constants.js +2 -0
- package/v2Components/VarSegmentMessageEditor/index.js +125 -0
- package/v2Components/VarSegmentMessageEditor/index.scss +46 -0
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +4 -1
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +4 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +20 -20
- package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +27 -0
- package/v2Containers/CreativesContainer/SlideBoxContent.js +37 -4
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +14 -5
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +36 -5
- package/v2Containers/CreativesContainer/constants.js +11 -0
- package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +77 -0
- package/v2Containers/CreativesContainer/index.js +322 -106
- package/v2Containers/CreativesContainer/index.scss +102 -1
- package/v2Containers/CreativesContainer/tests/CreativesSlideBoxWrapper.test.js +58 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.smsDltPreview.test.js +73 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.test.js +37 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +103 -34
- package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +193 -15
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +88 -0
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +549 -57
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +20 -15
- package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
- package/v2Containers/CreativesContainer/tests/index.test.js +71 -9
- package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
- package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
- package/v2Containers/Rcs/carouselUtils.js +224 -0
- package/v2Containers/Rcs/components/CarouselCard.js +317 -0
- package/v2Containers/Rcs/components/CarouselCardButtons.js +113 -0
- package/v2Containers/Rcs/components/CarouselCardMedia.js +136 -0
- package/v2Containers/Rcs/components/CarouselCharacterCount.js +31 -0
- package/v2Containers/Rcs/components/CarouselDimensionSelection.js +80 -0
- package/v2Containers/Rcs/constants.js +132 -16
- package/v2Containers/Rcs/index.js +1822 -946
- package/v2Containers/Rcs/index.scss +443 -8
- package/v2Containers/Rcs/messages.js +45 -22
- package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +494 -0
- package/v2Containers/Rcs/tests/CarouselCard.test.js +464 -0
- package/v2Containers/Rcs/tests/CarouselCardButtons.test.js +211 -0
- package/v2Containers/Rcs/tests/CarouselCardMedia.test.js +160 -0
- package/v2Containers/Rcs/tests/CarouselCharacterCount.test.js +50 -0
- package/v2Containers/Rcs/tests/CarouselDimensionSelection.test.js +119 -0
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +74440 -39387
- package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +10 -74
- package/v2Containers/Rcs/tests/carouselUtils.test.js +916 -0
- package/v2Containers/Rcs/tests/index.test.js +275 -40
- package/v2Containers/Rcs/tests/mockData.js +38 -0
- package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +912 -0
- package/v2Containers/Rcs/tests/utils.test.js +682 -30
- package/v2Containers/Rcs/utils.js +514 -12
- package/v2Containers/Sms/Create/index.js +115 -48
- package/v2Containers/Sms/smsFormDataHelpers.js +67 -0
- package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
- package/v2Containers/SmsTrai/Create/index.js +9 -4
- package/v2Containers/SmsTrai/Edit/constants.js +2 -0
- package/v2Containers/SmsTrai/Edit/index.js +678 -169
- package/v2Containers/SmsTrai/Edit/index.scss +126 -0
- package/v2Containers/SmsTrai/Edit/messages.js +14 -4
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +5615 -3014
- package/v2Containers/SmsWrapper/index.js +37 -8
- package/v2Containers/TagList/index.js +5 -0
- package/v2Containers/Templates/TemplatesActionBar.js +101 -0
- package/v2Containers/Templates/_templates.scss +196 -12
- package/v2Containers/Templates/actions.js +11 -0
- package/v2Containers/Templates/constants.js +2 -0
- package/v2Containers/Templates/index.js +131 -59
- package/v2Containers/Templates/sagas.js +57 -13
- package/v2Containers/Templates/tests/TemplatesActionBar.test.js +129 -0
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1060 -1015
- package/v2Containers/Templates/tests/sagas.test.js +199 -16
- package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
- package/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
- package/v2Containers/TemplatesV2/index.js +88 -25
- package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
- package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
- package/v2Containers/Whatsapp/index.js +3 -20
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +578 -34
|
@@ -126,7 +126,7 @@ import { INAPP_LAYOUT_DETAILS, INAPP_MESSAGE_LAYOUT_TYPES } from '../InApp/const
|
|
|
126
126
|
import { ZALO_STATUS_OPTIONS, ZALO_STATUSES } from '../Zalo/constants';
|
|
127
127
|
import { getWhatsappContent, getWhatsappStatus, getWhatsappCategory, getWhatsappCta, getWhatsappQuickReply, getWhatsappAutoFill, getWhatsappCarouselButtonView, getWhatsappDocPreview } from '../Whatsapp/utils';
|
|
128
128
|
import { getRCSContent } from '../Rcs/utils';
|
|
129
|
-
import {RCS_STATUSES} from '../Rcs/constants';
|
|
129
|
+
import { RCS_STATUSES, HOST_INFOBIP } from '../Rcs/constants';
|
|
130
130
|
import zaloMessages from '../Zalo/messages';
|
|
131
131
|
import rcsMessages from '../Rcs/messages';
|
|
132
132
|
import inAppMessages from '../InApp/messages';
|
|
@@ -432,8 +432,13 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
432
432
|
channel = '';
|
|
433
433
|
}
|
|
434
434
|
this.setState({ channel, activeMode });
|
|
435
|
-
// Always reset archive mode and selection when mounting a new channel
|
|
436
|
-
|
|
435
|
+
// Always reset archive mode and selection when mounting a new channel.
|
|
436
|
+
// Skip for local-template instances (e.g. SmsFallback selector) — SET_ARCHIVED_MODE clears
|
|
437
|
+
// Templates.templates in the reducer, which would wipe the background RCS template list.
|
|
438
|
+
const useLocalTemplatesOnMount = get(this.props, 'localTemplatesConfig.useLocalTemplates', false);
|
|
439
|
+
if (!useLocalTemplatesOnMount) {
|
|
440
|
+
this.props.actions.setArchivedMode(false);
|
|
441
|
+
}
|
|
437
442
|
this.props.actions.clearTemplateSelection();
|
|
438
443
|
// Clear templates when entering account selection mode to prevent showing old channel templates
|
|
439
444
|
if (activeMode === ACCOUNT_SELECTION_MODE) {
|
|
@@ -466,7 +471,13 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
466
471
|
if (this.props.location.query.type === 'embedded') {
|
|
467
472
|
this.props.actions.resetAccount();
|
|
468
473
|
}
|
|
469
|
-
|
|
474
|
+
// When using local templates (e.g. SMS fallback selector), do not fetch from API or we overwrite global store and break background RCS list
|
|
475
|
+
const useLocalTemplates = get(
|
|
476
|
+
this.props,
|
|
477
|
+
'localTemplatesConfig.useLocalTemplates',
|
|
478
|
+
get(this.props, 'useLocalTemplates', false),
|
|
479
|
+
);
|
|
480
|
+
if (!useLocalTemplates && [LINE_LOWERCASE, VIBER_CHANNEL, FACEBOOK_CHANNEL, SMS_LOWERCASE, EMAIL_LOWERCASE, EBILL_LOWERCASE].includes((this.state.channel || '').toLowerCase())) {
|
|
470
481
|
const queryParams = {
|
|
471
482
|
// name: this.state.searchText,
|
|
472
483
|
// sortBy: this.state.sortBy,
|
|
@@ -549,7 +560,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
549
560
|
selectedTemplateForPreview: null,
|
|
550
561
|
testAndPreviewContent: null,
|
|
551
562
|
}, () => {
|
|
552
|
-
if ([
|
|
563
|
+
if ([LINE_LOWERCASE, VIBER_CHANNEL, FACEBOOK_CHANNEL, SMS_LOWERCASE, EMAIL_LOWERCASE, EBILL_LOWERCASE, RCS_LOWERCASE].includes(this.state.channel.toLowerCase()) || (this.state.channel.toLowerCase() === 'wechat' && !isEmpty(nextProps.Templates.selectedWeChatAccount))) {
|
|
553
564
|
if (this.state.channel.toLowerCase() === 'wechat' && !isEmpty(nextProps.Templates.selectedWeChatAccount)) {
|
|
554
565
|
params.wecrmId = (nextProps.Templates.selectedWeChatAccount.configs || {}).wecrm_app_id;
|
|
555
566
|
params.wecrmToken = (nextProps.Templates.selectedWeChatAccount.configs || {}).wecrm_token;
|
|
@@ -1012,8 +1023,16 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
1012
1023
|
|
|
1013
1024
|
componentWillUnmount() {
|
|
1014
1025
|
window.removeEventListener("message", this.handleFrameTasks);
|
|
1015
|
-
|
|
1016
|
-
|
|
1026
|
+
// When using local templates (e.g. SMS fallback selector), do not clear global store or background RCS list is wiped
|
|
1027
|
+
const useLocalTemplates = get(
|
|
1028
|
+
this.props,
|
|
1029
|
+
'localTemplatesConfig.useLocalTemplates',
|
|
1030
|
+
get(this.props, 'useLocalTemplates', false),
|
|
1031
|
+
);
|
|
1032
|
+
if (!useLocalTemplates) {
|
|
1033
|
+
this.props.actions.resetTemplateStoreData();
|
|
1034
|
+
this.props.globalActions.clearMetaEntities();
|
|
1035
|
+
}
|
|
1017
1036
|
// Clear any pending timeouts to prevent memory leaks
|
|
1018
1037
|
if (this._clearEditTimeout) {
|
|
1019
1038
|
clearTimeout(this._clearEditTimeout);
|
|
@@ -1870,12 +1889,20 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
1870
1889
|
}
|
|
1871
1890
|
|
|
1872
1891
|
filterRcsTemplates = (templates) => {
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1892
|
+
const selectedRcsAccountName = this.props?.Templates?.selectedRcsAccount?.name || '';
|
|
1893
|
+
const hostName = this.state?.hostName;
|
|
1894
|
+
let nextTemplates = templates || [];
|
|
1895
|
+
if (selectedRcsAccountName) {
|
|
1896
|
+
nextTemplates = nextTemplates.filter(
|
|
1897
|
+
(template) => get(template, 'versions.base.content.RCS.rcsContent.accountName', '') === selectedRcsAccountName
|
|
1898
|
+
);
|
|
1877
1899
|
}
|
|
1878
|
-
|
|
1900
|
+
if (!this.props.isFullMode && hostName !== HOST_INFOBIP) {
|
|
1901
|
+
return nextTemplates.filter(
|
|
1902
|
+
(template) => get(template, 'versions.base.content.RCS.rcsContent.cardContent[0].Status', 'unavailable') === RCS_STATUSES.approved
|
|
1903
|
+
);
|
|
1904
|
+
}
|
|
1905
|
+
return nextTemplates;
|
|
1879
1906
|
}
|
|
1880
1907
|
|
|
1881
1908
|
filterZaloTemplates = (templates) => {
|
|
@@ -2065,7 +2092,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
2065
2092
|
style={{ marginRight: "16px" }}
|
|
2066
2093
|
type="eye"
|
|
2067
2094
|
onClick={() => {
|
|
2068
|
-
if (!this.props.isFullMode || this.props.isDltFromRcs) {
|
|
2095
|
+
if (!this.props.isFullMode || this.props.isDltFromRcs || this.props.isSmsFallbackFromRcs) {
|
|
2069
2096
|
if (!get(template, "versions.base.content.zalo.previewUrl", "")) {
|
|
2070
2097
|
this.setState({ zaloPreviewItemId: template?._id });
|
|
2071
2098
|
}
|
|
@@ -2545,17 +2572,20 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
2545
2572
|
const name = get(template, "name", "");
|
|
2546
2573
|
const statusDisplay=getRcsStatusType(status);
|
|
2547
2574
|
templateData.title = (
|
|
2548
|
-
<
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2575
|
+
<CapColumn>
|
|
2576
|
+
<CapRow type='flex' align="middle" justify="start" gap={12}>
|
|
2577
|
+
{isCardArchiveEligible && this.renderCardSelectionCheckbox({ templateId: template._id, selectedIds: selectedIdsArrayForCard, isDisabled: isAnyArchiveInProgress })}
|
|
2578
|
+
<CapLabel className="whatsapp-rcs-template-name">{name}</CapLabel>
|
|
2579
|
+
</CapRow>
|
|
2580
|
+
{this.state.hostName !== HOST_INFOBIP && <CapColumn type="flex" align="middle" className="rcs-status-container zalo-status-color">
|
|
2552
2581
|
<CapStatus
|
|
2553
2582
|
type={statusDisplay}
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2583
|
+
text={statusDisplay && this.props.intl.formatMessage(rcsMessages?.[`${statusDisplay}_STATUS`])}
|
|
2584
|
+
labelType="label3"
|
|
2585
|
+
/>
|
|
2586
|
+
</CapColumn>
|
|
2587
|
+
}
|
|
2588
|
+
</CapColumn>
|
|
2559
2589
|
);
|
|
2560
2590
|
|
|
2561
2591
|
templateData.content = getRCSContent(template);
|
|
@@ -3131,6 +3161,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
3131
3161
|
let routeParams = {};
|
|
3132
3162
|
const {fbAdManager} = this.props;
|
|
3133
3163
|
const isLibraryMode = this.isEnabledInLibraryModule("callCreateFromProps");
|
|
3164
|
+
|
|
3134
3165
|
if (!isLibraryMode) {
|
|
3135
3166
|
timeTracker.startTimer(CHANNEL_CREATE_TRACK_MAPPING[channel]);
|
|
3136
3167
|
}
|
|
@@ -3480,6 +3511,13 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
3480
3511
|
this.setState({modeType});
|
|
3481
3512
|
}
|
|
3482
3513
|
const { _id: id } = template;
|
|
3514
|
+
const {
|
|
3515
|
+
localTemplatesConfig,
|
|
3516
|
+
fbAdManager,
|
|
3517
|
+
isDltFromRcs,
|
|
3518
|
+
isSmsFallbackFromRcs,
|
|
3519
|
+
onSelectTemplate,
|
|
3520
|
+
} = this.props;
|
|
3483
3521
|
const type = this.props.location.query.type;
|
|
3484
3522
|
const module = this.props.location.query.module;
|
|
3485
3523
|
const isLanguageSupport = (this.props.location.query.isLanguageSupport) ? this.props.location.query.isLanguageSupport : false;
|
|
@@ -3565,10 +3603,12 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
3565
3603
|
}
|
|
3566
3604
|
if (this.isEnabledInLibraryModule("callSelectFromProps")) {
|
|
3567
3605
|
let data = id;
|
|
3568
|
-
if (
|
|
3606
|
+
if (localTemplatesConfig?.useLocalTemplates) {
|
|
3607
|
+
data = template;
|
|
3608
|
+
} else if (fbAdManager || isDltFromRcs || isSmsFallbackFromRcs) {
|
|
3569
3609
|
data = this.selectTemplate(id);
|
|
3570
3610
|
}
|
|
3571
|
-
|
|
3611
|
+
onSelectTemplate(data, fbAdManager);
|
|
3572
3612
|
} else {
|
|
3573
3613
|
timeTracker.startTimer(CHANNEL_EDIT_TRACK_MAPPING[this.state.channel.toLowerCase()]);
|
|
3574
3614
|
if (this.state.channel.toLowerCase() === 'ebill') {
|
|
@@ -4546,9 +4586,11 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
4546
4586
|
const isWechatEmbedded = !this.props.isFullMode && channel.toUpperCase() === WECHAT;
|
|
4547
4587
|
const channelLowerCase = (channel || '').toLowerCase();
|
|
4548
4588
|
const isTraiDltFeature = this.checkDLTfeatureEnable();
|
|
4549
|
-
|
|
4550
4589
|
const createButton =
|
|
4551
|
-
(
|
|
4590
|
+
(
|
|
4591
|
+
[WHATSAPP_LOWERCASE, RCS_LOWERCASE].includes(channelLowerCase)
|
|
4592
|
+
&& !this.props.isFullMode
|
|
4593
|
+
)
|
|
4552
4594
|
? (
|
|
4553
4595
|
<CapLink
|
|
4554
4596
|
onClick={this.openCreativesFullMode}
|
|
@@ -4580,17 +4622,22 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
4580
4622
|
const _renderSelectedIdsArray = _renderSelectedIds && typeof _renderSelectedIds.toJS === 'function' ? _renderSelectedIds.toJS() : (Array.isArray(_renderSelectedIds) ? _renderSelectedIds : []);
|
|
4581
4623
|
const _renderHasSelection = _isArchivalEnabled && this.props.isFullMode && _renderSelectedIdsArray.length > 0;
|
|
4582
4624
|
|
|
4583
|
-
const
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4625
|
+
const useLocalTemplates = this.props.localTemplatesConfig?.useLocalTemplates;
|
|
4626
|
+
const builtFilterContent = ((isfilterContentVisisble || [WECHAT, MOBILE_PUSH, INAPP].includes(this.state.channel.toUpperCase())) && (
|
|
4627
|
+
<div className="action-container">
|
|
4628
|
+
<div className="action-container__toolbar-row">
|
|
4629
|
+
{isfilterContentVisisble ? (
|
|
4630
|
+
<CapInput.Search
|
|
4631
|
+
className="search-text"
|
|
4632
|
+
placeholder={this.props.intl.formatMessage(messages.searchText)}
|
|
4633
|
+
value={this.state.searchText}
|
|
4634
|
+
onChange={(e) => this.searchTemplate(e.target.value, this.state.channel)}
|
|
4635
|
+
onSearch={() => this.searchTemplate(this.state.searchText, this.state.channel)}
|
|
4636
|
+
onClear={() => this.searchTemplate('', this.state.channel)}
|
|
4637
|
+
onScroll={(e) => e.stopPropagation()}
|
|
4638
|
+
disabled={this.checkSearchDisabled()}
|
|
4639
|
+
/>
|
|
4640
|
+
) : null}
|
|
4594
4641
|
{
|
|
4595
4642
|
channel.toUpperCase() === WECHAT && <CapRadio.CapRadioGroup className="wechat-filters" defaultValue={wechatFilter} onChange={this.setWechatFilter}>
|
|
4596
4643
|
<CapRadio.Button value={WECHAT_FILTERS.ALL}><CapLabel type="label2">
|
|
@@ -4737,16 +4784,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
4737
4784
|
)
|
|
4738
4785
|
}
|
|
4739
4786
|
<div className="template-listing-header-actions">
|
|
4740
|
-
{!_isArchivedMode && !_renderHasSelection && (
|
|
4741
|
-
this.state?.channel?.toLowerCase() === WHATSAPP_LOWERCASE && (isWhatsappCountExeeded) ? (
|
|
4742
|
-
<CapTooltip title={whatsappCountExceedText}>
|
|
4743
|
-
<div className="button-disabled-tooltip-wrapper">
|
|
4744
|
-
{createButton}
|
|
4745
|
-
</div>
|
|
4746
|
-
</CapTooltip>
|
|
4747
|
-
)
|
|
4748
|
-
: isfilterContentVisisble && !isWechatEmbedded && !this.props.isDltFromRcs && createButton
|
|
4749
|
-
)}
|
|
4750
4787
|
{/* More (⋯) menu: full mode only, not archived mode, not Zalo (no archive support), not when selection active, archive flag enabled */}
|
|
4751
4788
|
{commonUtil.hasCreativesArchivalEnabled() && !_isArchivedMode && !_renderHasSelection && this.props.isFullMode && this.props.location.query.type !== EMBEDDED && channelLowerCase !== ZALO_LOWERCASE && (
|
|
4752
4789
|
<CapDropdown
|
|
@@ -4769,7 +4806,28 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
4769
4806
|
</CapDropdown>
|
|
4770
4807
|
)}
|
|
4771
4808
|
</div>
|
|
4772
|
-
</div>
|
|
4809
|
+
</div>
|
|
4810
|
+
<div>
|
|
4811
|
+
<div className="action-container__create-row">
|
|
4812
|
+
{
|
|
4813
|
+
isfilterContentVisisble && !isWechatEmbedded && !this.props.isDltFromRcs && !this.props.isSmsFallbackFromRcs && (
|
|
4814
|
+
this.state?.channel?.toLowerCase() === WHATSAPP_LOWERCASE && isWhatsappCountExeeded ? (
|
|
4815
|
+
<CapTooltip title={whatsappCountExceedText}>
|
|
4816
|
+
<div className="button-disabled-tooltip-wrapper">
|
|
4817
|
+
{createButton}
|
|
4818
|
+
</div>
|
|
4819
|
+
</CapTooltip>
|
|
4820
|
+
) : createButton
|
|
4821
|
+
)
|
|
4822
|
+
}
|
|
4823
|
+
</div>
|
|
4824
|
+
</div>
|
|
4825
|
+
</div>
|
|
4826
|
+
));
|
|
4827
|
+
const localTemplatesFilterContent = get(this.props, 'localTemplatesConfig.localTemplatesFilterContent', null);
|
|
4828
|
+
const filterContent = (useLocalTemplates && localTemplatesFilterContent) != null
|
|
4829
|
+
? localTemplatesFilterContent
|
|
4830
|
+
: builtFilterContent;
|
|
4773
4831
|
let htmlPreviewContent = "";
|
|
4774
4832
|
if (this.state.channel.toLowerCase() === 'ebill') {
|
|
4775
4833
|
htmlPreviewContent = this.state.previewTemplate && this.state.previewTemplate.versions && this.state.previewTemplate.versions.base && this.state.previewTemplate.versions.base['ebill-editor'];
|
|
@@ -4779,7 +4837,10 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
4779
4837
|
|
|
4780
4838
|
|
|
4781
4839
|
const creativesParams = this.getCreativesParams();
|
|
4782
|
-
const templates =
|
|
4840
|
+
const templates = useLocalTemplates
|
|
4841
|
+
? (this.props.localTemplatesConfig?.localTemplates || [])
|
|
4842
|
+
: (this.props.TemplatesList || []);
|
|
4843
|
+
const isLoadingWhenLocal = useLocalTemplates && !!this.props.localTemplatesConfig?.localTemplatesLoading;
|
|
4783
4844
|
const {route} = this.props;
|
|
4784
4845
|
const loadingTipMap = {
|
|
4785
4846
|
sendingFile: 'uploadingFile',
|
|
@@ -4794,9 +4855,11 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
4794
4855
|
(deleteRcsTemplateInProgress && 'deletingTemplate') ||
|
|
4795
4856
|
(this.props.EmailCreate.duplicateTemplateInProgress && 'duplicatingTemplate');
|
|
4796
4857
|
|
|
4797
|
-
const loadingTip =
|
|
4858
|
+
const loadingTip = useLocalTemplates && this.props.localTemplatesConfig?.localTemplatesLoadingTip
|
|
4859
|
+
? this.props.localTemplatesConfig.localTemplatesLoadingTip
|
|
4860
|
+
: (messages[loadingTipIntl] ? this.props.intl.formatMessage(messages[loadingTipIntl]) : this.props.intl.formatMessage(messages.gettingAllTemplates));
|
|
4798
4861
|
const showNoTemplatesFoundZalo = this.state.channel.toUpperCase() === ZALO && isEmpty(this.state.searchedZaloTemplates) && this.state.searchingZaloTemplate;
|
|
4799
|
-
const showNoTemplatesFoundOther = ![ZALO].includes(this.state.channel.toUpperCase()) && isEmpty(
|
|
4862
|
+
const showNoTemplatesFoundOther = ![ZALO].includes(this.state.channel.toUpperCase()) && isEmpty(templates) && (useLocalTemplates ? !isLoadingWhenLocal : !this.props.Templates.getAllTemplatesInProgress) && (useLocalTemplates || !isEmpty(this.state.searchText));
|
|
4800
4863
|
const showNoTemplatesFound = showNoTemplatesFoundZalo || showNoTemplatesFoundOther;
|
|
4801
4864
|
|
|
4802
4865
|
return (
|
|
@@ -4840,7 +4903,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
4840
4903
|
/>
|
|
4841
4904
|
) : null}
|
|
4842
4905
|
|
|
4843
|
-
{channel.toLowerCase() === RCS_LOWERCASE && !isFullMode ? (
|
|
4906
|
+
{channel.toLowerCase() === RCS_LOWERCASE && !isFullMode && this.state?.hostName !== HOST_INFOBIP ? (
|
|
4844
4907
|
<CapInfoNote
|
|
4845
4908
|
message={formatMessage(messages.rcsOnlyApprovedTemplates)}
|
|
4846
4909
|
/>
|
|
@@ -4853,22 +4916,22 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
4853
4916
|
) : null}
|
|
4854
4917
|
<CapRow useLegacy>
|
|
4855
4918
|
<Pagination
|
|
4856
|
-
templateInProgress={
|
|
4857
|
-
this.props.Templates.getAllTemplatesInProgress
|
|
4858
|
-
}
|
|
4919
|
+
templateInProgress={useLocalTemplates ? isLoadingWhenLocal : this.props.Templates.getAllTemplatesInProgress}
|
|
4859
4920
|
onPageChange={
|
|
4860
|
-
templates.length
|
|
4921
|
+
templates.length
|
|
4922
|
+
? (useLocalTemplates ? (this.props.localTemplatesConfig?.localTemplatesOnPageChange || (() => {})) : this.onPaginationChange)
|
|
4923
|
+
: () => {}
|
|
4861
4924
|
}
|
|
4862
4925
|
>
|
|
4863
4926
|
{this.getTemplateDataForGrid({
|
|
4864
4927
|
previewTemplateId: this.state.zaloPreviewItemId,
|
|
4865
|
-
isLoading,
|
|
4866
|
-
isInitialLoading,
|
|
4928
|
+
isLoading: useLocalTemplates ? isLoadingWhenLocal : isLoading,
|
|
4929
|
+
isInitialLoading: useLocalTemplates ? isLoadingWhenLocal && templates.length === 0 : isInitialLoading,
|
|
4867
4930
|
loadingTip,
|
|
4868
4931
|
channel: this.state.channel,
|
|
4869
4932
|
templates: this.state.searchingZaloTemplate
|
|
4870
4933
|
? this.state.searchedZaloTemplates
|
|
4871
|
-
:
|
|
4934
|
+
: templates,
|
|
4872
4935
|
filterContent,
|
|
4873
4936
|
handlers: {
|
|
4874
4937
|
handlePreviewClick: this.handlePreviewClick,
|
|
@@ -5052,6 +5115,15 @@ Templates.propTypes = {
|
|
|
5052
5115
|
WebPush: PropTypes.object,
|
|
5053
5116
|
smsRegister: PropTypes.any,
|
|
5054
5117
|
isDltFromRcs: PropTypes.bool,
|
|
5118
|
+
isSmsFallbackFromRcs: PropTypes.bool,
|
|
5119
|
+
localTemplatesConfig: PropTypes.shape({
|
|
5120
|
+
useLocalTemplates: PropTypes.bool,
|
|
5121
|
+
localTemplates: PropTypes.arrayOf(PropTypes.object),
|
|
5122
|
+
localTemplatesLoading: PropTypes.bool,
|
|
5123
|
+
localTemplatesLoadingTip: PropTypes.string,
|
|
5124
|
+
localTemplatesFilterContent: PropTypes.node,
|
|
5125
|
+
localTemplatesOnPageChange: PropTypes.func,
|
|
5126
|
+
}),
|
|
5055
5127
|
};
|
|
5056
5128
|
|
|
5057
5129
|
const mapStateToProps = createStructuredSelector({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
-
call, put, takeLatest, all,
|
|
2
|
+
call, put, takeLatest, takeEvery, all,
|
|
3
3
|
} from 'redux-saga/effects';
|
|
4
4
|
import get from 'lodash/get';
|
|
5
5
|
import { CapNotification } from '@capillarytech/cap-ui-library';
|
|
@@ -7,33 +7,70 @@ import { CapNotification } from '@capillarytech/cap-ui-library';
|
|
|
7
7
|
import * as Api from '../../services/api';
|
|
8
8
|
import * as types from './constants';
|
|
9
9
|
import { saveCdnConfigs, removeAllCdnLocalStorageItems, initCdnConfigFromEnv } from '../../utils/cdnTransformation';
|
|
10
|
-
import { COPY_OF } from '../../constants/unified';
|
|
10
|
+
import { COPY_OF, SMS as SMS_CHANNEL } from '../../constants/unified';
|
|
11
|
+
import { fetchSmsTemplatesFromQuery } from './utils/smsTemplatesListApi';
|
|
11
12
|
import { ZALO_TEMPLATE_INFO_REQUEST } from '../Zalo/constants';
|
|
12
13
|
import { getTemplateInfoById } from '../Zalo/saga';
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
export function* getAllTemplates(channel, queryParams) {
|
|
15
|
+
export function* getLocalSmsTemplates(action) {
|
|
16
16
|
try {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
const fetched = yield call(
|
|
18
|
+
fetchSmsTemplatesFromQuery,
|
|
19
|
+
action.queryParams,
|
|
20
|
+
action.intlCopyOf,
|
|
21
|
+
);
|
|
22
|
+
if (typeof action.onSuccess === 'function') {
|
|
23
|
+
yield call(action.onSuccess, fetched);
|
|
24
|
+
}
|
|
25
|
+
} catch (error) {
|
|
26
|
+
if (typeof action.onFailure === 'function') {
|
|
27
|
+
yield call(action.onFailure, error);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function* getAllTemplates(action) {
|
|
33
|
+
try {
|
|
34
|
+
if (action.channel && String(action.channel).toLowerCase() === SMS_CHANNEL) {
|
|
35
|
+
const fetched = yield call(
|
|
36
|
+
fetchSmsTemplatesFromQuery,
|
|
37
|
+
action.queryParams,
|
|
38
|
+
action.intlCopyOf,
|
|
39
|
+
);
|
|
40
|
+
yield put({
|
|
41
|
+
type: types.GET_ALL_TEMPLATES_SUCCESS,
|
|
42
|
+
data: fetched.channelTemplates,
|
|
43
|
+
weCRMTemplate: fetched.weCRMTemplate,
|
|
44
|
+
isReset: get(action, 'queryParams.page') === 1,
|
|
45
|
+
});
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const result = yield call(Api.getAllTemplates, action);
|
|
50
|
+
const channelTemplates = (action.channel === 'wechat')
|
|
51
|
+
? { templates: [...result.response.mapped, ...result.response.richmedia] }
|
|
52
|
+
: result.response;
|
|
53
|
+
if (action.channel === 'wechat' && action.queryParams && action.queryParams.sortBy && action.queryParams.sortBy.toLocaleLowerCase() === ("Most Recent").toLocaleLowerCase()) {
|
|
21
54
|
channelTemplates.templates.sort((a, b) => {
|
|
22
55
|
const dateA = new Date(a.updatedAt);
|
|
23
56
|
const dateB = new Date(b.updatedAt);
|
|
24
57
|
return dateB - dateA;
|
|
25
58
|
});
|
|
26
|
-
} else if (
|
|
59
|
+
} else if (action.channel === 'wechat' && action.queryParams && action.queryParams.sortBy && action.queryParams.sortBy.toLocaleLowerCase() === ("Alphabetically").toLocaleLowerCase()) {
|
|
27
60
|
channelTemplates.templates.sort((a, b) => b.name - a.name);
|
|
28
61
|
}
|
|
29
|
-
|
|
30
|
-
if (channel.intlCopyOf && channelTemplates?.templates) {
|
|
62
|
+
if (action.intlCopyOf && channelTemplates?.templates) {
|
|
31
63
|
channelTemplates.templates = channelTemplates.templates.map((template) => ({
|
|
32
64
|
...template,
|
|
33
|
-
name: template.name.replace(new RegExp(COPY_OF, 'g'),
|
|
65
|
+
name: template.name.replace(new RegExp(COPY_OF, 'g'), action.intlCopyOf),
|
|
34
66
|
}));
|
|
35
67
|
}
|
|
36
|
-
yield put({
|
|
68
|
+
yield put({
|
|
69
|
+
type: types.GET_ALL_TEMPLATES_SUCCESS,
|
|
70
|
+
data: channelTemplates,
|
|
71
|
+
weCRMTemplate: result.response.unMapped,
|
|
72
|
+
isReset: get(action, 'queryParams.page') === 1,
|
|
73
|
+
});
|
|
37
74
|
} catch (error) {
|
|
38
75
|
yield put({ type: types.GET_ALL_TEMPLATES_FAILURE, error });
|
|
39
76
|
}
|
|
@@ -270,6 +307,11 @@ export function* watchGetAllTemplates() {
|
|
|
270
307
|
yield takeLatest(types.GET_ALL_TEMPLATES_REQUEST, getAllTemplates);
|
|
271
308
|
}
|
|
272
309
|
|
|
310
|
+
|
|
311
|
+
export function* watchGetLocalSmsTemplates() {
|
|
312
|
+
yield takeEvery(types.GET_LOCAL_SMS_TEMPLATES_REQUEST, getLocalSmsTemplates);
|
|
313
|
+
}
|
|
314
|
+
|
|
273
315
|
export function* watchDeleteTemplate() {
|
|
274
316
|
yield takeLatest(types.DELETE_TEMPLATE_REQUEST, deleteTemplate);
|
|
275
317
|
}
|
|
@@ -323,6 +365,7 @@ export function* watchForGetTemplateInfoById() {
|
|
|
323
365
|
// All sagas to be loaded
|
|
324
366
|
export default [
|
|
325
367
|
watchGetAllTemplates,
|
|
368
|
+
watchGetLocalSmsTemplates,
|
|
326
369
|
watchDeleteTemplate,
|
|
327
370
|
watchDeleteRcsTemplate,
|
|
328
371
|
watchGetUserList,
|
|
@@ -339,6 +382,7 @@ export default [
|
|
|
339
382
|
export function* v2TemplateSaga() {
|
|
340
383
|
yield all([
|
|
341
384
|
watchGetAllTemplates(),
|
|
385
|
+
watchGetLocalSmsTemplates(),
|
|
342
386
|
watchDeleteTemplate(),
|
|
343
387
|
watchDeleteRcsTemplate(),
|
|
344
388
|
watchGetUserList(),
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
6
|
+
import '@testing-library/jest-dom';
|
|
7
|
+
import TemplatesActionBar from '../TemplatesActionBar';
|
|
8
|
+
|
|
9
|
+
jest.mock('@capillarytech/cap-ui-library/CapInput', () => {
|
|
10
|
+
const React = require('react');
|
|
11
|
+
function Search(props) {
|
|
12
|
+
return React.createElement('input', {
|
|
13
|
+
'data-testid': 'cap-input-search',
|
|
14
|
+
value: props?.value,
|
|
15
|
+
placeholder: props?.placeholder,
|
|
16
|
+
onChange: props?.onChange,
|
|
17
|
+
onKeyDown: (e) => {
|
|
18
|
+
if (e.key === 'Enter' && props.onPressEnter) {
|
|
19
|
+
props.onPressEnter(e);
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
onScroll: props?.onScroll,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function CapInput() {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
CapInput.Search = Search;
|
|
29
|
+
return { __esModule: true, default: CapInput };
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
jest.mock('@capillarytech/cap-ui-library/CapButton', () => {
|
|
33
|
+
const React = require('react');
|
|
34
|
+
return function CapButton(props) {
|
|
35
|
+
return React.createElement('button', {
|
|
36
|
+
type: 'button',
|
|
37
|
+
'data-testid': 'cta',
|
|
38
|
+
onClick: props?.onClick,
|
|
39
|
+
disabled: props?.disabled,
|
|
40
|
+
}, props?.children);
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe('TemplatesActionBar', () => {
|
|
45
|
+
it('renders search when searchPlaceholder is set', () => {
|
|
46
|
+
render(
|
|
47
|
+
<TemplatesActionBar
|
|
48
|
+
searchPlaceholder="Find templates"
|
|
49
|
+
searchValue="hi"
|
|
50
|
+
ctaLabel="Create"
|
|
51
|
+
/>,
|
|
52
|
+
);
|
|
53
|
+
expect(screen.getByTestId('cap-input-search')).toHaveAttribute('placeholder', 'Find templates');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('omits search when searchPlaceholder is empty', () => {
|
|
57
|
+
const { container } = render(
|
|
58
|
+
<TemplatesActionBar searchPlaceholder="" ctaLabel="Go" />,
|
|
59
|
+
);
|
|
60
|
+
expect(container.querySelector('[data-testid="cap-input-search"]')).toBeNull();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('fires onSearchChange and onCtaClick', () => {
|
|
64
|
+
const onSearchChange = jest.fn();
|
|
65
|
+
const onCtaClick = jest.fn();
|
|
66
|
+
render(
|
|
67
|
+
<TemplatesActionBar
|
|
68
|
+
searchPlaceholder="S"
|
|
69
|
+
onSearchChange={onSearchChange}
|
|
70
|
+
ctaLabel="New"
|
|
71
|
+
onCtaClick={onCtaClick}
|
|
72
|
+
/>,
|
|
73
|
+
);
|
|
74
|
+
fireEvent.change(screen.getByTestId('cap-input-search'), { target: { value: 'x' } });
|
|
75
|
+
fireEvent.click(screen.getByTestId('cta'));
|
|
76
|
+
expect(onSearchChange).toHaveBeenCalled();
|
|
77
|
+
expect(onCtaClick).toHaveBeenCalled();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('stops propagation on scroll within the search input', () => {
|
|
81
|
+
render(<TemplatesActionBar searchPlaceholder="S" ctaLabel="New" />);
|
|
82
|
+
const scrollEvent = new Event('scroll', { bubbles: true });
|
|
83
|
+
const stopPropagationSpy = jest.spyOn(scrollEvent, 'stopPropagation');
|
|
84
|
+
screen.getByTestId('cap-input-search').dispatchEvent(scrollEvent);
|
|
85
|
+
expect(stopPropagationSpy).toHaveBeenCalled();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('fires onSearch when Enter is pressed (antd Input has no native onSearch)', () => {
|
|
89
|
+
const onSearch = jest.fn();
|
|
90
|
+
render(
|
|
91
|
+
<TemplatesActionBar
|
|
92
|
+
searchPlaceholder="S"
|
|
93
|
+
searchValue="query"
|
|
94
|
+
onSearch={onSearch}
|
|
95
|
+
ctaLabel="New"
|
|
96
|
+
/>,
|
|
97
|
+
);
|
|
98
|
+
const input = screen.getByTestId('cap-input-search');
|
|
99
|
+
fireEvent.keyDown(input, { key: 'Enter', code: 'Enter' });
|
|
100
|
+
expect(onSearch).toHaveBeenCalledWith('query');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('renders ctaNode instead of default button when provided', () => {
|
|
104
|
+
render(
|
|
105
|
+
<TemplatesActionBar
|
|
106
|
+
searchPlaceholder="S"
|
|
107
|
+
ctaNode={<span data-testid="custom-cta">Custom</span>}
|
|
108
|
+
/>,
|
|
109
|
+
);
|
|
110
|
+
expect(screen.getByTestId('custom-cta')).toBeInTheDocument();
|
|
111
|
+
expect(screen.queryByTestId('cta')).toBeNull();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('hides CTA area when showCta is false', () => {
|
|
115
|
+
const { container } = render(
|
|
116
|
+
<TemplatesActionBar searchPlaceholder="S" showCta={false} ctaLabel="X" />,
|
|
117
|
+
);
|
|
118
|
+
expect(container.querySelector('[data-testid="cta"]')).toBeNull();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('renders children in toolbar row', () => {
|
|
122
|
+
render(
|
|
123
|
+
<TemplatesActionBar searchPlaceholder="S" ctaLabel="C">
|
|
124
|
+
<span data-testid="child">extra</span>
|
|
125
|
+
</TemplatesActionBar>,
|
|
126
|
+
);
|
|
127
|
+
expect(screen.getByTestId('child')).toBeInTheDocument();
|
|
128
|
+
});
|
|
129
|
+
});
|