@capillarytech/creatives-library 8.0.319 → 8.0.321
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 +14 -0
- package/package.json +1 -1
- package/utils/templateVarUtils.js +172 -0
- package/utils/tests/tagValidations.test.js +34 -0
- package/utils/tests/templateVarUtils.test.js +160 -0
- package/v2Components/CapTagList/index.js +25 -22
- package/v2Components/CapTagList/style.scss +48 -0
- package/v2Components/CapTagListWithInput/__tests__/CapTagListWithInput.test.js +63 -0
- package/v2Components/CapTagListWithInput/index.js +4 -0
- package/v2Components/CapWhatsappCTA/index.js +2 -0
- package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +70 -49
- package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +8 -2
- package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +207 -21
- package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +16 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +85 -10
- package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +30 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +79 -11
- package/v2Components/CommonTestAndPreview/SendTestMessage.js +11 -5
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +20 -1
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +133 -4
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +12 -0
- package/v2Components/CommonTestAndPreview/constants.js +38 -0
- package/v2Components/CommonTestAndPreview/index.js +693 -155
- package/v2Components/CommonTestAndPreview/messages.js +41 -3
- package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
- package/v2Components/CommonTestAndPreview/sagas.js +15 -6
- package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +352 -0
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +269 -1
- 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/SendTestMessage.test.js +25 -4
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -1
- package/v2Components/CommonTestAndPreview/tests/index.test.js +132 -4
- package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +2 -2
- package/v2Components/FormBuilder/index.js +14 -1
- package/v2Components/HtmlEditor/HTMLEditor.js +6 -1
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +1 -0
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +927 -2
- package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +3 -0
- package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +87 -0
- package/v2Components/SmsFallback/constants.js +73 -0
- package/v2Components/SmsFallback/index.js +956 -0
- package/v2Components/SmsFallback/index.scss +265 -0
- package/v2Components/SmsFallback/messages.js +78 -0
- package/v2Components/SmsFallback/smsFallbackUtils.js +107 -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 +197 -0
- package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +261 -0
- package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +422 -0
- package/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
- package/v2Components/TestAndPreviewSlidebox/index.js +8 -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/BeeEditor/index.js +3 -0
- package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +43 -0
- package/v2Containers/CreativesContainer/SlideBoxContent.js +64 -5
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +10 -1
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +29 -4
- package/v2Containers/CreativesContainer/constants.js +9 -0
- package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +67 -0
- package/v2Containers/CreativesContainer/index.js +292 -99
- package/v2Containers/CreativesContainer/index.scss +51 -1
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +104 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +110 -0
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +8 -0
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +363 -0
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +20 -10
- 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/Email/index.js +1 -0
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +7 -1
- package/v2Containers/EmailWrapper/components/EmailWrapperView.js +3 -0
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +20 -2
- package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +16 -1
- package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +3 -0
- package/v2Containers/EmailWrapper/index.js +4 -0
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +1 -0
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +9 -0
- package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +19 -0
- package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +3 -0
- package/v2Containers/InAppWrapper/index.js +3 -0
- package/v2Containers/MobilePush/Create/index.js +2 -0
- package/v2Containers/MobilePush/Edit/index.js +2 -0
- package/v2Containers/MobilepushWrapper/index.js +3 -1
- package/v2Containers/Rcs/constants.js +32 -1
- package/v2Containers/Rcs/index.js +951 -873
- package/v2Containers/Rcs/index.scss +85 -6
- package/v2Containers/Rcs/messages.js +10 -1
- package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +205 -0
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +40834 -1963
- package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +0 -5
- package/v2Containers/Rcs/tests/index.test.js +41 -38
- package/v2Containers/Rcs/tests/mockData.js +38 -0
- package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +251 -0
- package/v2Containers/Rcs/tests/utils.test.js +379 -1
- package/v2Containers/Rcs/utils.js +358 -10
- package/v2Containers/Sms/Create/index.js +83 -36
- package/v2Containers/Sms/Edit/index.js +2 -0
- 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 +611 -128
- package/v2Containers/SmsTrai/Edit/index.scss +121 -0
- package/v2Containers/SmsTrai/Edit/messages.js +9 -4
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4327 -2374
- package/v2Containers/SmsWrapper/index.js +39 -8
- package/v2Containers/TagList/index.js +47 -2
- package/v2Containers/TagList/messages.js +4 -0
- package/v2Containers/TagList/tests/TagList.test.js +122 -20
- package/v2Containers/TagList/tests/mockdata.js +17 -0
- package/v2Containers/Templates/TemplatesActionBar.js +101 -0
- package/v2Containers/Templates/_templates.scss +61 -2
- package/v2Containers/Templates/actions.js +11 -0
- package/v2Containers/Templates/constants.js +2 -0
- package/v2Containers/Templates/index.js +90 -40
- package/v2Containers/Templates/sagas.js +57 -12
- package/v2Containers/Templates/tests/TemplatesActionBar.test.js +120 -0
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1043 -1079
- package/v2Containers/Templates/tests/sagas.test.js +193 -12
- 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 +86 -23
- package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
- package/v2Containers/Viber/index.js +5 -0
- package/v2Containers/WebPush/Create/hooks/useTagManagement.js +0 -2
- package/v2Containers/WebPush/Create/index.js +9 -1
- package/v2Containers/Whatsapp/index.js +8 -20
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +598 -34
- package/v2Containers/Zalo/index.js +2 -0
package/constants/unified.js
CHANGED
|
@@ -159,6 +159,20 @@ export const TAG_CONTENT_REGEX = /{{([^}]+)}}/g;
|
|
|
159
159
|
export const ENTRY_TRIGGER_TAG_REGEX = /\bentryTrigger\.\w+(?:\.\w+)?(?:\(\w+\))?/g;
|
|
160
160
|
export const SKIP_TAGS_REGEX_GROUPS = ["dynamic_expiry_date_after_\\d+_days.FORMAT_\\d", "unsubscribe\\(#[a-zA-Z\\d]{6}\\)", "Link_to_[a-zA-Z]", "SURVEY.*.TOKEN", "^[A-Za-z].*\\([a-zA-Z\\d]*\\)", "referral_unique_(code|url).*userid"];
|
|
161
161
|
|
|
162
|
+
// --- Template variable tokens (`{{var}}`, DLT `{#var#}`) ---
|
|
163
|
+
/** Global: all `{{…}}` placeholders in a string. */
|
|
164
|
+
export const DEFAULT_MUSTACHE_VAR_REGEX = /\{\{[^}]+\}\}/g;
|
|
165
|
+
/** Global: `{{…}}` or DLT `{#…#}` tokens (SMS combined mode). */
|
|
166
|
+
export const COMBINED_SMS_TEMPLATE_VAR_REGEX = /\{\{[^}]+\}\}|\{\#[^#]*\#\}/g;
|
|
167
|
+
/** Full-string check: one mustache token. */
|
|
168
|
+
export const MUSTACHE_VAR_TOKEN_FULL_STRING_REGEX = /^\{\{[^}]+\}\}$/;
|
|
169
|
+
/** Full-string check: one DLT hash token. */
|
|
170
|
+
export const DLT_HASH_VAR_TOKEN_FULL_STRING_REGEX = /^\{\#[^#]*\#\}$/;
|
|
171
|
+
/** Global with capture group: inner name inside `{{name}}`. */
|
|
172
|
+
export const MUSTACHE_VAR_NAME_CAPTURE_REGEX = /\{\{([^}]+)\}\}/g;
|
|
173
|
+
/** Global with capture group: inner body inside `{#body#}`. */
|
|
174
|
+
export const DLT_VAR_BODY_CAPTURE_REGEX = /\{\#([^#]*)\#\}/g;
|
|
175
|
+
|
|
162
176
|
export const GET_TRANSLATION_MAPPED = {
|
|
163
177
|
'en': 'en-US',
|
|
164
178
|
'zh-cn': 'zh',
|
package/package.json
CHANGED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utilities for templates containing {{var}} and DLT `{#var#}` tokens.
|
|
3
|
+
* Same split process used by WhatsApp/RCS: match vars with regex, then split content at each var.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
COMBINED_SMS_TEMPLATE_VAR_REGEX,
|
|
8
|
+
DEFAULT_MUSTACHE_VAR_REGEX,
|
|
9
|
+
DLT_HASH_VAR_TOKEN_FULL_STRING_REGEX,
|
|
10
|
+
DLT_VAR_BODY_CAPTURE_REGEX,
|
|
11
|
+
MUSTACHE_VAR_NAME_CAPTURE_REGEX,
|
|
12
|
+
MUSTACHE_VAR_TOKEN_FULL_STRING_REGEX,
|
|
13
|
+
} from '../constants/unified';
|
|
14
|
+
|
|
15
|
+
export { COMBINED_SMS_TEMPLATE_VAR_REGEX, DEFAULT_MUSTACHE_VAR_REGEX } from '../constants/unified';
|
|
16
|
+
|
|
17
|
+
const isMustacheVarToken = (s) =>
|
|
18
|
+
typeof s === 'string' && MUSTACHE_VAR_TOKEN_FULL_STRING_REGEX.test(s);
|
|
19
|
+
|
|
20
|
+
export const isDltHashVarToken = (s) =>
|
|
21
|
+
typeof s === 'string' && DLT_HASH_VAR_TOKEN_FULL_STRING_REGEX.test(s);
|
|
22
|
+
|
|
23
|
+
export const isAnyTemplateVarToken = (s) => isMustacheVarToken(s) || isDltHashVarToken(s);
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* `RegExp.prototype.exec` only advances `lastIndex` when the `g` and/or `y` flag is set.
|
|
27
|
+
* A non-global regex in a `while ((m = re.exec(s)) !== null)` loop never advances and can run forever.
|
|
28
|
+
*
|
|
29
|
+
* @param {RegExp} regex
|
|
30
|
+
* @returns {RegExp} Same instance if already global; otherwise a new RegExp with `g` appended to flags.
|
|
31
|
+
*/
|
|
32
|
+
function ensureGlobalRegexForExecLoop(regex) {
|
|
33
|
+
if (!regex || !(regex instanceof RegExp) || regex.global) {
|
|
34
|
+
return regex;
|
|
35
|
+
}
|
|
36
|
+
return new RegExp(regex.source, `${regex.flags}g`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Splits `content` into alternating plain-text segments and variable tokens, using the order of
|
|
41
|
+
* matches in `matchedVariableTokens` (e.g. from `String.prototype.match` with a global regex).
|
|
42
|
+
*
|
|
43
|
+
* @param {string[]} matchedVariableTokens - Matched tokens in left-to-right order
|
|
44
|
+
* @param {string} content - Full template string
|
|
45
|
+
* @returns {string[]}
|
|
46
|
+
*/
|
|
47
|
+
export const splitContentByOrderedVarTokens = (matchedVariableTokens, content) => {
|
|
48
|
+
const segmentList = [];
|
|
49
|
+
const tokenQueue = [...(matchedVariableTokens ?? [])];
|
|
50
|
+
let remainder = content ?? '';
|
|
51
|
+
while ((remainder?.length ?? 0) > 0) {
|
|
52
|
+
const nextVarToken = tokenQueue?.[0];
|
|
53
|
+
if (nextVarToken == null || nextVarToken === '') {
|
|
54
|
+
segmentList.push(remainder);
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
const varStartIndex = remainder.indexOf(nextVarToken);
|
|
58
|
+
if (varStartIndex !== -1) {
|
|
59
|
+
segmentList.push(remainder.substring(0, varStartIndex));
|
|
60
|
+
segmentList.push(nextVarToken);
|
|
61
|
+
const afterVar = varStartIndex + (nextVarToken?.length ?? 0);
|
|
62
|
+
remainder = remainder.substring(afterVar, remainder?.length ?? 0);
|
|
63
|
+
tokenQueue.shift();
|
|
64
|
+
} else {
|
|
65
|
+
segmentList.push(remainder);
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return segmentList;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Splits a template string into an array of text + {{var}} segments using the given regex.
|
|
74
|
+
*
|
|
75
|
+
* @param {string} str - Template string
|
|
76
|
+
* @param {RegExp} [varRegex] - Regex that matches var tokens; defaults to DEFAULT_MUSTACHE_VAR_REGEX
|
|
77
|
+
* @returns {string[]}
|
|
78
|
+
*/
|
|
79
|
+
export const splitTemplateVarString = (str = '', varRegex = DEFAULT_MUSTACHE_VAR_REGEX) => {
|
|
80
|
+
if (!str) return [];
|
|
81
|
+
const matchedVariableTokens = str.match(varRegex) || [];
|
|
82
|
+
return splitContentByOrderedVarTokens(matchedVariableTokens, str).filter(
|
|
83
|
+
(segment) => segment === 0 || segment
|
|
84
|
+
);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Extracts unique variable names from `{{var}}` (and, when using default capture, `{#var#}`) strings.
|
|
89
|
+
*
|
|
90
|
+
* @param {string} templateStr
|
|
91
|
+
* @param {RegExp} [captureRegex] - regex with a single capture group for the var name; if omitted, also scans DLT `{#…#}`.
|
|
92
|
+
* @returns {string[]}
|
|
93
|
+
*/
|
|
94
|
+
export const extractTemplateVariables = (templateStr = '', captureRegex) => {
|
|
95
|
+
if (!templateStr) return [];
|
|
96
|
+
const variables = [];
|
|
97
|
+
const add = (name) => {
|
|
98
|
+
const n = (name || '').trim();
|
|
99
|
+
if (n && !variables.includes(n)) variables.push(n);
|
|
100
|
+
};
|
|
101
|
+
const mustacheRe = ensureGlobalRegexForExecLoop(
|
|
102
|
+
captureRegex || MUSTACHE_VAR_NAME_CAPTURE_REGEX,
|
|
103
|
+
);
|
|
104
|
+
let match;
|
|
105
|
+
while ((match = mustacheRe.exec(templateStr)) !== null) {
|
|
106
|
+
add(match?.[1]);
|
|
107
|
+
}
|
|
108
|
+
if (!captureRegex) {
|
|
109
|
+
const dltRe = new RegExp(DLT_VAR_BODY_CAPTURE_REGEX.source, DLT_VAR_BODY_CAPTURE_REGEX.flags);
|
|
110
|
+
let dltMatch;
|
|
111
|
+
while ((dltMatch = dltRe.exec(templateStr)) !== null) {
|
|
112
|
+
add(dltMatch?.[1] || 'var');
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return variables;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* SMS / DLT template preview: replace `{{…}}` / `{#…#}` tokens using `varMapData` keys `${token}_${index}`.
|
|
120
|
+
* Used by SmsTraiEdit (RCS SMS fallback) and UnifiedPreview fallback SMS bubble.
|
|
121
|
+
* DLT `{#…#}`: empty / unset slot values show the raw token (matches DLT preview UX); mustache `{{…}}` still
|
|
122
|
+
* resolves to empty when the slot is cleared.
|
|
123
|
+
*/
|
|
124
|
+
export const getFallbackResolvedContent = (templateStr = '', varMapData = {}) => {
|
|
125
|
+
const fallbackVarSlotMap = varMapData ?? {};
|
|
126
|
+
const templateSegments = splitTemplateVarString(templateStr, COMBINED_SMS_TEMPLATE_VAR_REGEX);
|
|
127
|
+
return templateSegments
|
|
128
|
+
.map((segment, segmentIndex) => {
|
|
129
|
+
const isVariableToken = typeof segment === 'string' && isAnyTemplateVarToken(segment);
|
|
130
|
+
if (!isVariableToken) return segment;
|
|
131
|
+
const slotKey = `${segment}_${segmentIndex}`;
|
|
132
|
+
if (Object.prototype.hasOwnProperty.call(fallbackVarSlotMap, slotKey)) {
|
|
133
|
+
const slotValue = fallbackVarSlotMap[slotKey];
|
|
134
|
+
if (isDltHashVarToken(segment)) {
|
|
135
|
+
if (slotValue == null) return segment;
|
|
136
|
+
const str = String(slotValue);
|
|
137
|
+
if (str.trim() === '') return segment;
|
|
138
|
+
return str;
|
|
139
|
+
}
|
|
140
|
+
return slotValue == null ? '' : String(slotValue);
|
|
141
|
+
}
|
|
142
|
+
if (isDltHashVarToken(segment)) return segment;
|
|
143
|
+
return '';
|
|
144
|
+
})
|
|
145
|
+
.join('');
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* SMS fallback **card** (library list): show filled slot text; if a slot is empty, unset, or
|
|
150
|
+
* whitespace-only, show the raw `{{…}}` / `{#…#}` token so “save without labels” still shows
|
|
151
|
+
* `{#var#}` in the card. {@link getFallbackResolvedContent} keeps DLT placeholders in preview when
|
|
152
|
+
* slots are empty; mustache cleared slots can still render empty.
|
|
153
|
+
*/
|
|
154
|
+
export const getFallbackResolvedContentForCardDisplay = (templateStr = '', varMapData = {}) => {
|
|
155
|
+
const fallbackVarSlotMap = varMapData ?? {};
|
|
156
|
+
const templateSegments = splitTemplateVarString(templateStr, COMBINED_SMS_TEMPLATE_VAR_REGEX);
|
|
157
|
+
return templateSegments
|
|
158
|
+
.map((segment, segmentIndex) => {
|
|
159
|
+
const isVariableToken = typeof segment === 'string' && isAnyTemplateVarToken(segment);
|
|
160
|
+
if (!isVariableToken) return segment;
|
|
161
|
+
const slotKey = `${segment}_${segmentIndex}`;
|
|
162
|
+
if (Object.prototype.hasOwnProperty.call(fallbackVarSlotMap, slotKey)) {
|
|
163
|
+
const slotValue = fallbackVarSlotMap[slotKey];
|
|
164
|
+
if (slotValue == null) return segment;
|
|
165
|
+
const str = String(slotValue);
|
|
166
|
+
if (str.trim() === '') return segment;
|
|
167
|
+
return str;
|
|
168
|
+
}
|
|
169
|
+
return segment;
|
|
170
|
+
})
|
|
171
|
+
.join('');
|
|
172
|
+
};
|
|
@@ -242,6 +242,7 @@ describe("validateTags", () => {
|
|
|
242
242
|
tagsParam,
|
|
243
243
|
location,
|
|
244
244
|
tagModule,
|
|
245
|
+
waitEventContextTags: {},
|
|
245
246
|
});
|
|
246
247
|
|
|
247
248
|
expect(result.valid).toEqual(true);
|
|
@@ -273,6 +274,7 @@ describe("validateTags", () => {
|
|
|
273
274
|
tagsParam: tagsParamLocal,
|
|
274
275
|
location,
|
|
275
276
|
tagModule,
|
|
277
|
+
waitEventContextTags: {},
|
|
276
278
|
});
|
|
277
279
|
|
|
278
280
|
expect(result.valid).toEqual(true);
|
|
@@ -310,6 +312,7 @@ describe("validateTags", () => {
|
|
|
310
312
|
tagsParam: tagsParamLocal,
|
|
311
313
|
location,
|
|
312
314
|
tagModule,
|
|
315
|
+
waitEventContextTags: {},
|
|
313
316
|
});
|
|
314
317
|
|
|
315
318
|
expect(result.valid).toEqual(false);
|
|
@@ -335,6 +338,7 @@ describe("validateTags", () => {
|
|
|
335
338
|
tagsParam: tagsParamUnsubscribe,
|
|
336
339
|
location,
|
|
337
340
|
tagModule,
|
|
341
|
+
waitEventContextTags: {},
|
|
338
342
|
});
|
|
339
343
|
expect(resultMissing.missingTags).toContain("unsubscribe");
|
|
340
344
|
expect(resultMissing.valid).toBe(false);
|
|
@@ -345,6 +349,7 @@ describe("validateTags", () => {
|
|
|
345
349
|
tagsParam: tagsParamUnsubscribe,
|
|
346
350
|
location,
|
|
347
351
|
tagModule,
|
|
352
|
+
waitEventContextTags: {},
|
|
348
353
|
});
|
|
349
354
|
expect(resultSkipped.missingTags).not.toContain("unsubscribe");
|
|
350
355
|
expect(resultSkipped.valid).toBe(true);
|
|
@@ -360,6 +365,35 @@ describe("validateTags", () => {
|
|
|
360
365
|
expect(resultWhitespace.valid).toBe(true);
|
|
361
366
|
expect(resultWhitespace.unsupportedTags ?? []).toEqual([]);
|
|
362
367
|
});
|
|
368
|
+
|
|
369
|
+
it('should treat tags from waitEventContextTags as supported', () => {
|
|
370
|
+
const content = 'Hello {{waitEvent.orderId}}';
|
|
371
|
+
const tagsParam = [];
|
|
372
|
+
const injectedTagsParams = [];
|
|
373
|
+
const location = { query: { module: 'DEFAULT' } };
|
|
374
|
+
const tagModule = null;
|
|
375
|
+
const waitEventContextTags = {
|
|
376
|
+
block1: {
|
|
377
|
+
eventName: 'Order Placed',
|
|
378
|
+
blockName: 'Wait Block',
|
|
379
|
+
tags: [{ tagName: 'waitEvent.orderId', label: 'Order ID' }],
|
|
380
|
+
},
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
const result = validateTags({
|
|
384
|
+
content,
|
|
385
|
+
tagsParam,
|
|
386
|
+
injectedTagsParams,
|
|
387
|
+
location,
|
|
388
|
+
tagModule,
|
|
389
|
+
eventContextTags: [],
|
|
390
|
+
waitEventContextTags,
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
expect(result.valid).toEqual(true);
|
|
394
|
+
expect(result.missingTags).toEqual([]);
|
|
395
|
+
expect(result.isBraceError).toEqual(false);
|
|
396
|
+
});
|
|
363
397
|
});
|
|
364
398
|
|
|
365
399
|
describe('validateTags wrapper (v2 consumers)', () => {
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import {
|
|
2
|
+
extractTemplateVariables,
|
|
3
|
+
splitTemplateVarString,
|
|
4
|
+
splitContentByOrderedVarTokens,
|
|
5
|
+
getFallbackResolvedContent,
|
|
6
|
+
getFallbackResolvedContentForCardDisplay,
|
|
7
|
+
isDltHashVarToken,
|
|
8
|
+
isAnyTemplateVarToken,
|
|
9
|
+
} from '../templateVarUtils';
|
|
10
|
+
|
|
11
|
+
describe('templateVarUtils', () => {
|
|
12
|
+
describe('splitContentByOrderedVarTokens', () => {
|
|
13
|
+
it('pushes remainder when next token is not found in string', () => {
|
|
14
|
+
expect(splitContentByOrderedVarTokens(['{{b}}'], 'hello')).toEqual(['hello']);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('handles empty token queue by pushing remainder', () => {
|
|
18
|
+
expect(splitContentByOrderedVarTokens([], 'rest')).toEqual(['rest']);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('handles null matchedVariableTokens as empty queue', () => {
|
|
22
|
+
expect(splitContentByOrderedVarTokens(null, 'abc')).toEqual(['abc']);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('stops and pushes remainder when next token in queue is empty string', () => {
|
|
26
|
+
expect(splitContentByOrderedVarTokens(['{{a}}', ''], 'x{{a}}y')).toEqual(['x', '{{a}}', 'y']);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('splits multiple ordered tokens across content', () => {
|
|
30
|
+
expect(splitContentByOrderedVarTokens(['{{a}}', '{{b}}'], 'p{{a}}q{{b}}r')).toEqual([
|
|
31
|
+
'p',
|
|
32
|
+
'{{a}}',
|
|
33
|
+
'q',
|
|
34
|
+
'{{b}}',
|
|
35
|
+
'r',
|
|
36
|
+
]);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('returns empty segment list when content is null and queue is empty', () => {
|
|
40
|
+
expect(splitContentByOrderedVarTokens([], null)).toEqual([]);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe('splitTemplateVarString', () => {
|
|
45
|
+
it('returns empty array for falsy string', () => {
|
|
46
|
+
expect(splitTemplateVarString('')).toEqual([]);
|
|
47
|
+
expect(splitTemplateVarString(null)).toEqual([]);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('splits mustache segments', () => {
|
|
51
|
+
const parts = splitTemplateVarString('Hi {{name}}!');
|
|
52
|
+
expect(parts.some((p) => p.includes('name'))).toBe(true);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('omits empty segments after filtering', () => {
|
|
56
|
+
expect(splitTemplateVarString('a{{x}}b')).toEqual(['a', '{{x}}', 'b']);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('getFallbackResolvedContent', () => {
|
|
61
|
+
it('replaces var segments from varMapData', () => {
|
|
62
|
+
const out = getFallbackResolvedContent('{{a}}', { '{{a}}_0': 'X' });
|
|
63
|
+
expect(out).toBe('X');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('keeps DLT token string when slot empty', () => {
|
|
67
|
+
const out = getFallbackResolvedContent('{#v#}', {});
|
|
68
|
+
expect(out).toContain('#');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('returns empty string for mustache slot when map has no value', () => {
|
|
72
|
+
const out = getFallbackResolvedContent('Hi {{missing}}', {});
|
|
73
|
+
expect(out).toBe('Hi ');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('treats null as empty; explicit empty string is kept (cleared input)', () => {
|
|
77
|
+
expect(getFallbackResolvedContent('{{a}}', { '{{a}}_0': null })).toBe('');
|
|
78
|
+
expect(getFallbackResolvedContent('{{a}}', { '{{a}}_0': '' })).toBe('');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('keeps DLT token in preview when slot map has empty string (no tag yet)', () => {
|
|
82
|
+
expect(getFallbackResolvedContent('{#v#}', { '{#v#}_0': '' })).toBe('{#v#}');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('keeps DLT token in preview when slot map has null', () => {
|
|
86
|
+
expect(getFallbackResolvedContent('{#v#}', { '{#v#}_0': null })).toBe('{#v#}');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('uses slot replacement when value is non-empty string', () => {
|
|
90
|
+
expect(getFallbackResolvedContent('A{{b}}C', { '{{b}}_1': 'ok' })).toBe('AokC');
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('getFallbackResolvedContentForCardDisplay', () => {
|
|
95
|
+
it('shows raw tokens when map is empty or slots unset', () => {
|
|
96
|
+
expect(getFallbackResolvedContentForCardDisplay('Hi {{name}}', {})).toBe('Hi {{name}}');
|
|
97
|
+
expect(getFallbackResolvedContentForCardDisplay('{#var#}', {})).toBe('{#var#}');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('shows raw token when slot key exists but value is empty (no labels)', () => {
|
|
101
|
+
expect(getFallbackResolvedContentForCardDisplay('{#var#}', { '{#var#}_0': '' })).toBe('{#var#}');
|
|
102
|
+
expect(getFallbackResolvedContentForCardDisplay('{{a}}', { '{{a}}_0': '' })).toBe('{{a}}');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('replaces when slot has non-empty value', () => {
|
|
106
|
+
expect(getFallbackResolvedContentForCardDisplay('Hi {{name}}', { '{{name}}_1': 'Pat' })).toBe('Hi Pat');
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe('isDltHashVarToken / isAnyTemplateVarToken', () => {
|
|
111
|
+
it('detects DLT hash token', () => {
|
|
112
|
+
expect(isDltHashVarToken('{#x#}')).toBe(true);
|
|
113
|
+
expect(isDltHashVarToken('plain')).toBe(false);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('isAnyTemplateVarToken combines mustache and DLT', () => {
|
|
117
|
+
expect(isAnyTemplateVarToken('{{a}}')).toBe(true);
|
|
118
|
+
expect(isAnyTemplateVarToken('{#x#}')).toBe(true);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
describe('extractTemplateVariables', () => {
|
|
123
|
+
it('returns empty array for empty or missing template', () => {
|
|
124
|
+
expect(extractTemplateVariables('')).toEqual([]);
|
|
125
|
+
expect(extractTemplateVariables()).toEqual([]);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('includes DLT {#var#} names when no custom capture regex', () => {
|
|
129
|
+
const vars = extractTemplateVariables('Hello {#promo#} and {{name}}');
|
|
130
|
+
expect(vars).toEqual(expect.arrayContaining(['promo', 'name']));
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('skips DLT scan when captureRegex is provided', () => {
|
|
134
|
+
const re = /\{\{([^}]+)\}\}/g;
|
|
135
|
+
expect(extractTemplateVariables('{#x#} {{y}}', re)).toEqual(['y']);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('dedupes duplicate variable names', () => {
|
|
139
|
+
expect(extractTemplateVariables('{{a}} {{a}}')).toEqual(['a']);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('uses default name var when DLT body capture is empty', () => {
|
|
143
|
+
const vars = extractTemplateVariables('{##}');
|
|
144
|
+
expect(vars).toContain('var');
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
describe('extractTemplateVariables (regex global)', () => {
|
|
149
|
+
it('extracts all vars when caller passes a non-global capture regex (avoids infinite exec loop)', () => {
|
|
150
|
+
const nonGlobal = /\{\{([^}]+)\}\}/;
|
|
151
|
+
expect(nonGlobal.global).toBe(false);
|
|
152
|
+
expect(extractTemplateVariables('{{a}} and {{b}}', nonGlobal)).toEqual(['a', 'b']);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('still works when capture regex already has the global flag', () => {
|
|
156
|
+
const globalRe = /\{\{([^}]+)\}\}/g;
|
|
157
|
+
expect(extractTemplateVariables('{{x}} {{y}}', globalRe)).toEqual(['x', 'y']);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
});
|
|
@@ -211,6 +211,7 @@ class CapTagList extends React.Component { // eslint-disable-line react/prefer-s
|
|
|
211
211
|
} else if (info && info.selectedNodes && info.selectedNodes.length > 0 && !info.selectedNodes[0].props.isLeaf) {
|
|
212
212
|
this.handleOnExpand(selectedKeys[0]);
|
|
213
213
|
}
|
|
214
|
+
this.setState({expandedKeys: []})
|
|
214
215
|
}
|
|
215
216
|
};
|
|
216
217
|
|
|
@@ -233,6 +234,13 @@ class CapTagList extends React.Component { // eslint-disable-line react/prefer-s
|
|
|
233
234
|
}
|
|
234
235
|
};
|
|
235
236
|
|
|
237
|
+
/** Single-line ellipsis within popover width; full label on hover via CapTooltip. */
|
|
238
|
+
wrapTreeTitle = (displayNode, text) => (
|
|
239
|
+
<CapTooltip title={displayNode}>
|
|
240
|
+
<CapLabel.CapLabelInline type="label15" className="cap-tag-list-tree-title-wrap">{text || displayNode}</CapLabel.CapLabelInline>
|
|
241
|
+
</CapTooltip>
|
|
242
|
+
);
|
|
243
|
+
|
|
236
244
|
renderDynamicTagFlow = () => {
|
|
237
245
|
this.setState({showModal: true, visible: false});
|
|
238
246
|
};
|
|
@@ -280,7 +288,7 @@ class CapTagList extends React.Component { // eslint-disable-line react/prefer-s
|
|
|
280
288
|
if (temp?.length) {
|
|
281
289
|
const tagValue = (
|
|
282
290
|
<CapTreeNode
|
|
283
|
-
title={disabled ?
|
|
291
|
+
title={disabled ? this.wrapTreeTitle(loyaltyAttrDisableText, val?.name) : this.wrapTreeTitle(val?.name)}
|
|
284
292
|
tag={val}
|
|
285
293
|
key={val?.incentiveSeriesId ? `${key}(${val?.incentiveSeriesId})` : `${key}`}
|
|
286
294
|
disabled={disabled}
|
|
@@ -303,17 +311,9 @@ class CapTagList extends React.Component { // eslint-disable-line react/prefer-s
|
|
|
303
311
|
<CapTreeNode
|
|
304
312
|
title={
|
|
305
313
|
childDisabled ? (
|
|
306
|
-
|
|
307
|
-
title={
|
|
308
|
-
key === CUSTOMER_BARCODE_TAG
|
|
309
|
-
? customerBarcodeDisableText
|
|
310
|
-
: loyaltyAttrDisableText
|
|
311
|
-
}
|
|
312
|
-
>
|
|
313
|
-
{val?.desc || val?.name}
|
|
314
|
-
</CapTooltip>
|
|
314
|
+
this.wrapTreeTitle(key === CUSTOMER_BARCODE_TAG ? customerBarcodeDisableText : loyaltyAttrDisableText, val?.desc || val?.name)
|
|
315
315
|
) : (
|
|
316
|
-
val?.desc || val?.name
|
|
316
|
+
this.wrapTreeTitle(val?.desc || val?.name)
|
|
317
317
|
)
|
|
318
318
|
}
|
|
319
319
|
tag={val}
|
|
@@ -339,17 +339,9 @@ class CapTagList extends React.Component { // eslint-disable-line react/prefer-s
|
|
|
339
339
|
<CapTreeNode
|
|
340
340
|
title={
|
|
341
341
|
childDisabled ? (
|
|
342
|
-
|
|
343
|
-
title={
|
|
344
|
-
key === CUSTOMER_BARCODE_TAG
|
|
345
|
-
? customerBarcodeDisableText
|
|
346
|
-
: loyaltyAttrDisableText
|
|
347
|
-
}
|
|
348
|
-
>
|
|
349
|
-
{val?.desc || val?.name}
|
|
350
|
-
</CapTooltip>
|
|
342
|
+
this.wrapTreeTitle(key === CUSTOMER_BARCODE_TAG ? customerBarcodeDisableText : loyaltyAttrDisableText, val?.desc || val?.name)
|
|
351
343
|
) : (
|
|
352
|
-
val?.desc || val?.name
|
|
344
|
+
this.wrapTreeTitle(val?.desc || val?.name)
|
|
353
345
|
)
|
|
354
346
|
}
|
|
355
347
|
tag={val}
|
|
@@ -382,6 +374,9 @@ class CapTagList extends React.Component { // eslint-disable-line react/prefer-s
|
|
|
382
374
|
render() {
|
|
383
375
|
const {
|
|
384
376
|
hidePopover = false, intl = {}, moduleFilterEnabled, label, modalProps, channel, fetchingSchemaError = false,
|
|
377
|
+
overlayStyle,
|
|
378
|
+
overlayClassName,
|
|
379
|
+
getPopupContainer,
|
|
385
380
|
} = this.props;
|
|
386
381
|
const {formatMessage} = intl;
|
|
387
382
|
const {
|
|
@@ -404,7 +399,7 @@ class CapTagList extends React.Component { // eslint-disable-line react/prefer-s
|
|
|
404
399
|
},
|
|
405
400
|
];
|
|
406
401
|
const contentSection = (
|
|
407
|
-
<CapRow>
|
|
402
|
+
<CapRow className="cap-tag-list-popover-inner">
|
|
408
403
|
<CapSpin tip={formatMessage(messages.gettingTags)} spinning={shouldShowLoading}>
|
|
409
404
|
<Search
|
|
410
405
|
style={{ marginBottom: 8, width: '250px'}}
|
|
@@ -472,8 +467,12 @@ class CapTagList extends React.Component { // eslint-disable-line react/prefer-s
|
|
|
472
467
|
visible={fetchingSchemaError ? false : visible}
|
|
473
468
|
onVisibleChange={this.togglePopoverVisibility}
|
|
474
469
|
content={contentSection}
|
|
470
|
+
overlayClassName="cap-tag-list-popover-overlay"
|
|
475
471
|
trigger="click"
|
|
476
472
|
placement={this.props.popoverPlacement || (channel === EMAIL.toUpperCase() ? "leftTop" : "rightTop")}
|
|
473
|
+
overlayStyle={overlayStyle}
|
|
474
|
+
overlayClassName={overlayClassName}
|
|
475
|
+
getPopupContainer={getPopupContainer}
|
|
477
476
|
>
|
|
478
477
|
<CapTooltip
|
|
479
478
|
title={
|
|
@@ -545,6 +544,10 @@ CapTagList.propTypes = {
|
|
|
545
544
|
disableTooltipMsg: PropTypes.string,
|
|
546
545
|
fetchingSchemaError: PropTypes.bool,
|
|
547
546
|
popoverPlacement: PropTypes.string,
|
|
547
|
+
overlayStyle: PropTypes.object,
|
|
548
|
+
overlayClassName: PropTypes.string,
|
|
549
|
+
/** e.g. () => document.body — avoids overflow/stacking issues inside slideboxes */
|
|
550
|
+
getPopupContainer: PropTypes.func,
|
|
548
551
|
};
|
|
549
552
|
|
|
550
553
|
CapTagList.defaultValue = {
|
|
@@ -1,5 +1,53 @@
|
|
|
1
1
|
@import "~@capillarytech/cap-ui-library/styles/_variables";
|
|
2
2
|
|
|
3
|
+
// Tag list popover: keep overlay width aligned with search (250px); tree rows ellipsis + tooltip for full text
|
|
4
|
+
.cap-tag-list-popover-overlay.ant-popover {
|
|
5
|
+
.ant-popover-inner-content {
|
|
6
|
+
max-width: 20rem;
|
|
7
|
+
box-sizing: border-box;
|
|
8
|
+
overflow: hidden;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.cap-tag-list-popover-inner {
|
|
13
|
+
max-width: 20rem;
|
|
14
|
+
min-width: 0;
|
|
15
|
+
box-sizing: border-box;
|
|
16
|
+
|
|
17
|
+
.ant-tree.cap-tree-v2.ant-tree-icon-hide {
|
|
18
|
+
width: 100%;
|
|
19
|
+
max-width: 100%;
|
|
20
|
+
|
|
21
|
+
ul {
|
|
22
|
+
max-width: 100%;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
li {
|
|
26
|
+
overflow: hidden;
|
|
27
|
+
max-width: 100%;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
li .ant-tree-node-content-wrapper {
|
|
31
|
+
width: calc(100% - 3.5rem); // leave room for switcher (~24px)
|
|
32
|
+
max-width: calc(100% - 3.5rem);
|
|
33
|
+
overflow: hidden;
|
|
34
|
+
vertical-align: top;
|
|
35
|
+
box-sizing: border-box;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.cap-tag-list-tree-title-wrap {
|
|
39
|
+
display: inline-block;
|
|
40
|
+
width: 100%;
|
|
41
|
+
overflow: hidden;
|
|
42
|
+
text-overflow: ellipsis;
|
|
43
|
+
white-space: nowrap;
|
|
44
|
+
max-width: 100%;
|
|
45
|
+
vertical-align: top;
|
|
46
|
+
margin-top: 0.5rem;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
3
51
|
@media (max-height: 25rem) {
|
|
4
52
|
.ant-tree.cap-tree-v2.ant-tree-icon-hide {
|
|
5
53
|
height: 8.5714rem;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import '@testing-library/jest-dom';
|
|
4
|
+
import { IntlProvider } from 'react-intl';
|
|
5
|
+
import CapTagListWithInput from '../index';
|
|
6
|
+
|
|
7
|
+
const capturedTagListProps = { current: null };
|
|
8
|
+
|
|
9
|
+
jest.mock('../../../v2Containers/TagList', () => {
|
|
10
|
+
const React = require('react');
|
|
11
|
+
const Mock = (props) => {
|
|
12
|
+
capturedTagListProps.current = props;
|
|
13
|
+
return <div data-testid="mock-tag-list">TagList</div>;
|
|
14
|
+
};
|
|
15
|
+
return Mock;
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
jest.mock('@capillarytech/cap-ui-library/CapRow', () => ({ children }) => <div>{children}</div>);
|
|
19
|
+
jest.mock('@capillarytech/cap-ui-library/CapColumn', () => ({ children }) => <div>{children}</div>);
|
|
20
|
+
jest.mock('@capillarytech/cap-ui-library/CapHeading', () => () => null);
|
|
21
|
+
jest.mock('@capillarytech/cap-ui-library/CapInput', () => () => <input data-testid="cap-input" />);
|
|
22
|
+
|
|
23
|
+
const waitMap = {
|
|
24
|
+
b1: { eventName: 'Order Placed', blockName: 'Wait', tags: [] },
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
describe('CapTagListWithInput', () => {
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
capturedTagListProps.current = null;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('forwards waitEventContextTags to TagList', () => {
|
|
33
|
+
render(
|
|
34
|
+
<IntlProvider locale="en" messages={{}}>
|
|
35
|
+
<CapTagListWithInput
|
|
36
|
+
inputId="test-url"
|
|
37
|
+
inputOnChange={jest.fn()}
|
|
38
|
+
waitEventContextTags={waitMap}
|
|
39
|
+
onTagSelect={jest.fn()}
|
|
40
|
+
onContextChange={jest.fn()}
|
|
41
|
+
/>
|
|
42
|
+
</IntlProvider>
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
expect(screen.getByTestId('mock-tag-list')).toBeInTheDocument();
|
|
46
|
+
expect(capturedTagListProps.current.waitEventContextTags).toEqual(waitMap);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('uses default empty object for waitEventContextTags when omitted', () => {
|
|
50
|
+
render(
|
|
51
|
+
<IntlProvider locale="en" messages={{}}>
|
|
52
|
+
<CapTagListWithInput
|
|
53
|
+
inputId="test-url"
|
|
54
|
+
inputOnChange={jest.fn()}
|
|
55
|
+
onTagSelect={jest.fn()}
|
|
56
|
+
onContextChange={jest.fn()}
|
|
57
|
+
/>
|
|
58
|
+
</IntlProvider>
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
expect(capturedTagListProps.current.waitEventContextTags).toEqual({});
|
|
62
|
+
});
|
|
63
|
+
});
|