@capillarytech/creatives-library 9.0.56-betaa.0 → 9.0.56

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.
Files changed (116) hide show
  1. package/constants/unified.js +9 -4
  2. package/package.json +1 -1
  3. package/services/api.js +25 -4
  4. package/utils/common.js +0 -12
  5. package/utils/templateVarUtils.js +16 -0
  6. package/utils/tests/templateVarUtils.test.js +44 -0
  7. package/v2Components/CapActionButton/index.js +73 -60
  8. package/v2Components/CapActionButton/index.scss +44 -28
  9. package/v2Components/CapActionButton/messages.js +7 -3
  10. package/v2Components/CapActionButton/tests/index.test.js +17 -1
  11. package/v2Components/CapWhatsappCTA/messages.js +4 -0
  12. package/v2Components/CapWhatsappCarouselButton/index.js +42 -33
  13. package/v2Components/CapWhatsappCarouselButton/index.scss +44 -2
  14. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +13 -0
  15. package/v2Components/CommonTestAndPreview/actions.js +13 -0
  16. package/v2Components/CommonTestAndPreview/constants.js +8 -0
  17. package/v2Components/CommonTestAndPreview/index.js +55 -3
  18. package/v2Components/CommonTestAndPreview/reducer.js +20 -0
  19. package/v2Components/CommonTestAndPreview/sagas.js +28 -0
  20. package/v2Components/CommonTestAndPreview/selectors.js +6 -0
  21. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +106 -0
  22. package/v2Components/CommonTestAndPreview/tests/actions.test.js +15 -0
  23. package/v2Components/CommonTestAndPreview/tests/constants.test.js +9 -1
  24. package/v2Components/CommonTestAndPreview/tests/index.test.js +101 -0
  25. package/v2Components/CommonTestAndPreview/tests/reducer.test.js +41 -0
  26. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +84 -0
  27. package/v2Components/CommonTestAndPreview/tests/selectors.test.js +12 -0
  28. package/v2Components/FormBuilder/Functional/FormBuilderShell.js +46 -320
  29. package/v2Components/FormBuilder/Functional/channels/registry.js +0 -2
  30. package/v2Components/FormBuilder/Functional/constants.js +8 -43
  31. package/v2Components/FormBuilder/Functional/core/schema/initializeFormState.js +28 -175
  32. package/v2Components/FormBuilder/Functional/core/store/formReducer.js +6 -75
  33. package/v2Components/FormBuilder/Functional/core/store/toLegacyFormData.js +20 -10
  34. package/v2Components/FormBuilder/Functional/layout/FieldSlot.js +1 -9
  35. package/v2Components/FormBuilder/Functional/layout/SchemaForm.js +3 -20
  36. package/v2Components/FormBuilder/Functional/layout/Section.js +1 -6
  37. package/v2Components/FormBuilder/Functional/tests/fieldSlot.test.js +2 -9
  38. package/v2Components/FormBuilder/Functional/tests/schemaForm.test.js +0 -17
  39. package/v2Components/FormBuilder/index.js +12 -34
  40. package/v2Components/FormBuilder/tests/entryGate.test.js +7 -94
  41. package/v2Components/TestAndPreviewSlidebox/index.js +9 -0
  42. package/v2Containers/CommunicationFlow/CommunicationFlow.js +140 -75
  43. package/v2Containers/CommunicationFlow/CommunicationFlow.scss +18 -2
  44. package/v2Containers/CommunicationFlow/CommunicationFlowCard.js +67 -28
  45. package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +503 -51
  46. package/v2Containers/CommunicationFlow/Tests/CommunicationFlowCard.test.js +98 -4
  47. package/v2Containers/CommunicationFlow/Tests/constants.test.js +85 -0
  48. package/v2Containers/CommunicationFlow/constants.js +65 -4
  49. package/v2Containers/CommunicationFlow/index.js +16 -23
  50. package/v2Containers/CommunicationFlow/messages.js +11 -3
  51. package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.js +27 -11
  52. package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.scss +4 -0
  53. package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/Tests/ChannelSelectionStep.test.js +176 -4
  54. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/DeliverySettingsSection.js +22 -4
  55. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/SenderDetails.js +14 -4
  56. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +11 -0
  57. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +51 -0
  58. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/deliverySettingsConfig.test.js +11 -4
  59. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/deliverySettingsConfig.js +19 -5
  60. package/v2Containers/CreativesContainer/SlideBoxContent.js +15 -0
  61. package/v2Containers/CreativesContainer/SlideBoxFooter.js +6 -2
  62. package/v2Containers/CreativesContainer/index.js +29 -3
  63. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +29 -0
  64. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +18 -5
  65. package/v2Containers/CreativesContainer/tests/index.test.js +12 -0
  66. package/v2Containers/MobilePush/Create/index.js +18 -1
  67. package/v2Containers/MobilePush/Create/test/contentValidity.test.js +96 -0
  68. package/v2Containers/MobilePush/Edit/index.js +18 -1
  69. package/v2Containers/MobilePush/Edit/test/contentValidity.test.js +116 -0
  70. package/v2Containers/MobilePush/commonMethods.js +49 -1
  71. package/v2Containers/MobilePushNew/index.js +29 -4
  72. package/v2Containers/MobilePushNew/tests/index.test.js +119 -0
  73. package/v2Containers/MobilePushNew/tests/utils.test.js +82 -0
  74. package/v2Containers/MobilePushNew/utils.js +34 -1
  75. package/v2Containers/MobilepushWrapper/index.js +3 -1
  76. package/v2Containers/Rcs/index.js +34 -0
  77. package/v2Containers/Rcs/index.scss +15 -0
  78. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +115 -19
  79. package/v2Containers/Rcs/tests/index.test.js +67 -0
  80. package/v2Containers/Sms/Create/index.js +3 -1
  81. package/v2Containers/Sms/Edit/index.js +25 -0
  82. package/v2Containers/Sms/Edit/tests/index.test.js +85 -0
  83. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +27 -3
  84. package/v2Containers/Viber/index.js +24 -1
  85. package/v2Containers/Viber/tests/index.test.js +103 -0
  86. package/v2Containers/WebPush/Create/index.js +19 -0
  87. package/v2Containers/WebPush/Create/preview/WebPushPreview.js +17 -12
  88. package/v2Containers/WebPush/Create/preview/tests/WebPushPreview.test.js +5 -0
  89. package/v2Containers/WebPush/Create/tests/contentValidity.test.js +294 -0
  90. package/v2Containers/Whatsapp/index.js +25 -4
  91. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +306 -34
  92. package/v2Containers/Whatsapp/tests/index.test.js +115 -0
  93. package/v2Containers/Zalo/index.js +28 -1
  94. package/v2Containers/Zalo/tests/index.test.js +99 -0
  95. package/v2Containers/mockdata.js +25 -0
  96. package/v2Components/FormBuilder/Functional/channels/mobilepush/buildSubmitPayload.js +0 -10
  97. package/v2Components/FormBuilder/Functional/channels/mobilepush/config.js +0 -82
  98. package/v2Components/FormBuilder/Functional/channels/mobilepush/getEditorErrorDescriptor.js +0 -74
  99. package/v2Components/FormBuilder/Functional/channels/mobilepush/index.js +0 -28
  100. package/v2Components/FormBuilder/Functional/channels/mobilepush/modals.js +0 -19
  101. package/v2Components/FormBuilder/Functional/channels/mobilepush/runSubmitPipeline.js +0 -45
  102. package/v2Components/FormBuilder/Functional/channels/mobilepush/tests/getEditorErrorDescriptor.test.js +0 -162
  103. package/v2Components/FormBuilder/Functional/channels/mobilepush/tests/modals.test.js +0 -37
  104. package/v2Components/FormBuilder/Functional/channels/mobilepush/tests/runSubmitPipeline.test.js +0 -70
  105. package/v2Components/FormBuilder/Functional/channels/mobilepush/tests/validate.test.js +0 -274
  106. package/v2Components/FormBuilder/Functional/channels/mobilepush/validate.js +0 -251
  107. package/v2Components/FormBuilder/Functional/layout/TabsContainer.js +0 -89
  108. package/v2Components/FormBuilder/Functional/renderers/mpushRenderers.js +0 -440
  109. package/v2Components/FormBuilder/Functional/tests/mpush.crossFlowParity.test.js +0 -430
  110. package/v2Components/FormBuilder/Functional/tests/mpush.ctaFlows.parity.test.js +0 -282
  111. package/v2Components/FormBuilder/Functional/tests/mpush.editContainer.parity.test.js +0 -141
  112. package/v2Components/FormBuilder/Functional/tests/mpush.engine.test.js +0 -306
  113. package/v2Components/FormBuilder/Functional/tests/mpush.shellEvents.test.js +0 -151
  114. package/v2Components/FormBuilder/Functional/tests/mpushRenderers.test.js +0 -342
  115. package/v2Components/FormBuilder/Functional/tests/tabsContainer.test.js +0 -110
  116. package/v2Components/FormBuilder/tests/mpush.characterization.test.js +0 -459
@@ -1,251 +0,0 @@
1
- /**
2
- * MOBILEPUSH validation — line-faithful port of Classic's validateForm MPUSH block
3
- * (Classic.js:935-1218) over the legacy formData; emitted errorData is byte-identical.
4
- * Every oddity below is a deliberate Classic quirk pinned by the characterization
5
- * suite — see FormBuilder-V3-MPUSH-Technical-Design.md §6.1 before "fixing" any.
6
- */
7
-
8
- import { validateTagsCore } from '../../../../../utils/tagValidations';
9
- import { hasPersonalizationTags } from '../../../../../utils/commonUtils';
10
- import { isUrl } from '../../../../../v2Containers/Line/Container/Wrapper/utils';
11
- import { DEFAULT as DEFAULT_MODULE } from '../../../../../constants/unified';
12
- import { TEMPLATE_NAME_FIELD } from '../../constants';
13
- import { ERROR_VALUE, TABS, CTA_RULES } from './config';
14
-
15
- const isTabKey = (key) => /^\d+$/.test(`${key}`);
16
-
17
- /** Truthy-value projection of a tab, excluding the given metadata keys. */
18
- const pickTruthy = (tab, excluded) => {
19
- const out = {};
20
- Object.keys(tab || {}).forEach((key) => {
21
- if (tab[key] && !excluded.includes(key)) out[key] = tab[key];
22
- });
23
- return out;
24
- };
25
-
26
- const isEmptyObject = (obj) => !obj || Object.keys(obj).length === 0;
27
-
28
- // Classic.js:752-758 — key absent => valid; present but empty/invalid URL => invalid.
29
- export const isValidExternalLink = (tab, key) => {
30
- if (key in (tab || {})) {
31
- const url = tab[key];
32
- return Boolean(url && url !== '' && isUrl(url));
33
- }
34
- return true;
35
- };
36
-
37
- // Classic's this.validateTags(content, tags, false /* isEmail */, isFullMode):
38
- // MPUSH consumes only .valid and .isBraceError from the core result.
39
- const runTagValidation = (content, { tags, currentModule, isFullMode }) => {
40
- const result = validateTagsCore({
41
- contentForBraceCheck: content,
42
- contentForUnsubscribeScan: content,
43
- tags,
44
- currentModule,
45
- isFullMode,
46
- initialMissingTags: [], // not email — no unsubscribe enforcement
47
- includeIsContentEmpty: true,
48
- });
49
- return result && typeof result === 'object'
50
- ? result
51
- : { valid: false, isBraceError: false };
52
- };
53
-
54
- // Classic seeds EVERY tab's error map from tab 0's keys, skipping
55
- // tabKey/base/selectedLanguages (initializeEditErrorData, Classic.js:1627-1656).
56
- const seedTabErrors = (tab0) => {
57
- const seeded = {};
58
- Object.keys(tab0 || {}).forEach((key) => {
59
- if (key !== 'tabKey' && key !== 'base' && key !== 'selectedLanguages') seeded[key] = false;
60
- });
61
- return seeded;
62
- };
63
-
64
- /**
65
- * @param {object} formData legacy formData ({ 'template-name', 0: {...}, 1: {...}, base })
66
- * @param {object} options { tags, isFullMode, isEmbedded, currentModule,
67
- * restrictPersonalization, isSave, currentTab (1-based) }
68
- * @returns {{ isValid, errorData, platform: { androidValid, iosValid, isOnlyAndroid,
69
- * isOnlyIos }, singlePlatformPrompt: 'android'|'ios'|null }}
70
- */
71
- export default function validateMobilePush(formData = {}, options = {}) {
72
- const {
73
- tags = [],
74
- isFullMode,
75
- isEmbedded = false,
76
- currentModule = DEFAULT_MODULE,
77
- restrictPersonalization = false,
78
- isSave = false,
79
- currentTab = 1,
80
- } = options;
81
-
82
- let isValid = true;
83
- let androidValid = true;
84
- let iosValid = true;
85
- let isOnlyAndroid = false;
86
- let isOnlyIos = false;
87
- const errorData = {};
88
- let singlePlatformPrompt = null;
89
-
90
- // Classic.js:941 — no Android tab => the WHOLE block (template-name included)
91
- // is skipped and validity stays true. Quirk, preserved.
92
- if (formData && formData[TABS.ANDROID]) {
93
- if (isEmptyObject(pickTruthy(formData[TABS.ANDROID], ['tabKey', 'base']))) isOnlyIos = true;
94
- if (isEmptyObject(pickTruthy(formData[TABS.IOS], ['tabKey', 'base']))) isOnlyAndroid = true;
95
-
96
- Object.keys(formData).forEach((index) => {
97
- const tab = formData[index];
98
- let isCurrentTabValid = true;
99
- // Embedded mode skips the tab that is not open (Classic.js:961-965).
100
- const skipValidation = isEmbedded
101
- && ((isOnlyAndroid && parseInt(index, 10) === 1) || (isOnlyIos && parseInt(index, 10) === 0))
102
- && isOnlyAndroid !== isOnlyIos;
103
-
104
- if (isTabKey(index) && tab && !skipValidation) {
105
- const tabIdx = parseInt(index, 10);
106
- const suffix = TABS.suffixFor(tabIdx);
107
- errorData[tabIdx] = errorData[tabIdx] || seedTabErrors(formData[TABS.ANDROID]);
108
- const tabErrors = errorData[tabIdx];
109
-
110
- // --- message-editor{suffix} (Classic.js:971-998) ---
111
- let message = tab[`message-editor${suffix}`];
112
- if (message) {
113
- message = message.trim();
114
- if (message === '') {
115
- tabErrors[`message-editor${suffix}`] = true;
116
- isValid = false;
117
- isCurrentTabValid = false;
118
- } else if (restrictPersonalization && hasPersonalizationTags(message)) {
119
- tabErrors[`message-editor${suffix}`] = true;
120
- isValid = false;
121
- isCurrentTabValid = false;
122
- } else {
123
- tabErrors[`message-editor${suffix}`] = false;
124
- const tagValidation = runTagValidation(message, { tags, currentModule, isFullMode });
125
- if (tagValidation.valid) {
126
- tabErrors[`message-editor${suffix}`] = false;
127
- } else {
128
- const { isBraceError } = tagValidation;
129
- tabErrors[`message-editor${suffix}`] = isBraceError ? ERROR_VALUE.BRACKET : true;
130
- tabErrors['bracket-error'] = isBraceError && ERROR_VALUE.BRACKET;
131
- isValid = false; // NOTE: isCurrentTabValid deliberately NOT cleared (Classic parity)
132
- }
133
- }
134
- } else {
135
- tabErrors[`message-editor${suffix}`] = true;
136
- isValid = false; // NOTE: isCurrentTabValid deliberately NOT cleared (Classic parity)
137
- }
138
-
139
- // --- message-title{suffix} (Classic.js:1002-1030) ---
140
- let title = tab[`message-title${suffix}`];
141
- if (title) {
142
- title = title.trim();
143
- if (title === '') {
144
- tabErrors[`message-title${suffix}`] = true;
145
- isValid = false;
146
- isCurrentTabValid = false;
147
- } else if (restrictPersonalization && hasPersonalizationTags(title)) {
148
- tabErrors[`message-title${suffix}`] = true;
149
- isValid = false;
150
- isCurrentTabValid = false;
151
- } else {
152
- tabErrors[`message-title${suffix}`] = false;
153
- const tagValidation = runTagValidation(title, { tags, currentModule, isFullMode });
154
- if (tagValidation.valid) {
155
- tabErrors[`message-title${suffix}`] = false;
156
- } else {
157
- const { isBraceError } = tagValidation;
158
- tabErrors[`message-title${suffix}`] = isBraceError ? ERROR_VALUE.BRACKET : true;
159
- tabErrors['bracket-error'] = isBraceError && ERROR_VALUE.BRACKET;
160
- isValid = false; // isCurrentTabValid NOT cleared (Classic parity)
161
- }
162
- }
163
- } else {
164
- // Unlike the editor, a missing title DOES clear the tab flag (Classic.js:1026-1029).
165
- tabErrors[`message-title${suffix}`] = true;
166
- isValid = false;
167
- isCurrentTabValid = false;
168
- }
169
-
170
- // --- fixed CTA/deeplink rules (Classic.js:1037-1135), unsuffixed by design ---
171
- CTA_RULES.forEach(({ key, kind }) => {
172
- const failed = kind === 'url'
173
- ? !isValidExternalLink(tab, key)
174
- : tab[key] === '';
175
- if (failed) {
176
- tabErrors[key] = true;
177
- isValid = false;
178
- isCurrentTabValid = false;
179
- } else {
180
- tabErrors[key] = false;
181
- }
182
- });
183
-
184
- // --- image: required only when the key exists (imageSchema) (Classic.js:1137-1143) ---
185
- if (typeof tab.image !== 'undefined' && tab.image === '') {
186
- tabErrors.image = true;
187
- isValid = false;
188
- isCurrentTabValid = false;
189
- } else {
190
- tabErrors.image = false;
191
- }
192
-
193
- // --- dynamic deeplink custom-value keys (Classic.js:1144-1155) ---
194
- Object.keys(tab).forEach((key) => {
195
- if (key.indexOf('custom-value') > -1) {
196
- if (tab[key] === '') {
197
- tabErrors[key] = true;
198
- isValid = false;
199
- isCurrentTabValid = false;
200
- } else {
201
- tabErrors[key] = false;
202
- }
203
- }
204
- });
205
- }
206
-
207
- // eslint-disable-next-line eqeqeq
208
- if (index == 0) androidValid = isCurrentTabValid;
209
- // eslint-disable-next-line eqeqeq
210
- else if (index == 1) iosValid = isCurrentTabValid;
211
- });
212
-
213
- // --- template-name: root, skipped in embedded mode; failure also suppresses
214
- // the single-platform modal by clearing both platform flags (Classic.js:1163-1170) ---
215
- if (!isEmbedded && (!formData[TEMPLATE_NAME_FIELD] || formData[TEMPLATE_NAME_FIELD] === '')) {
216
- errorData[TEMPLATE_NAME_FIELD] = true;
217
- isValid = false;
218
- androidValid = false;
219
- iosValid = false;
220
- } else {
221
- errorData[TEMPLATE_NAME_FIELD] = false;
222
- }
223
-
224
- // --- single-platform save prompt (Classic.js:1171-1204) ---
225
- if (isSave && (androidValid || iosValid)) {
226
- const androidData = pickTruthy(formData[TABS.ANDROID], ['tabKey', 'base']);
227
- // eslint-disable-next-line eqeqeq
228
- if (isEmptyObject(androidData) && currentTab == 2) {
229
- singlePlatformPrompt = 'ios';
230
- } else {
231
- // Classic's inner rebuild excludes only tabKey — NOT base (line 1191). Preserved.
232
- const iosData = pickTruthy(formData[TABS.IOS], ['tabKey']);
233
- // eslint-disable-next-line eqeqeq
234
- if (isEmptyObject(iosData) && currentTab == 1) {
235
- singlePlatformPrompt = 'android';
236
- }
237
- }
238
- }
239
- // Classic.js:1209-1215 — the three roll-up branches are `isValid = isValid && true`
240
- // no-ops; the accumulated isValid stands. isLiquidValid = isValid for MPUSH.
241
- }
242
-
243
- return {
244
- isValid,
245
- errorData,
246
- platform: {
247
- androidValid, iosValid, isOnlyAndroid, isOnlyIos,
248
- },
249
- singlePlatformPrompt,
250
- };
251
- }
@@ -1,89 +0,0 @@
1
- /**
2
- * TabsContainer — channel-agnostic renderer for a schema 'tabs' container
3
- * (MOBILEPUSH: the Android/iOS platform panes). Replaces the class component's
4
- * renderContainer 'tabs' case (Classic.js:4246-4328) with state-driven tab
5
- * activation — no DOM scans, no setTimeout — while keeping the exact container
6
- * contract: pane keys come from the bound tab's tabKey, unsupported panes are
7
- * skipped WITHOUT reserving an index (pane→tab binding shifts, matching the
8
- * class's paneIndex behavior), and the container's injected onTabChange still
9
- * receives the 1-based active index (bridged by the shell via onTabSwitch).
10
- */
11
-
12
- import React from 'react';
13
- import PropTypes from 'prop-types';
14
- import CapTab from '@capillarytech/cap-ui-library/CapTab';
15
- import Section from './Section';
16
-
17
- const JAPANESE_LOCALE = 'ja-JP';
18
-
19
- const hasTabKey = (key) => key !== undefined && key !== '';
20
-
21
- const TabsContainer = ({ container, renderContext }) => {
22
- if (!container?.panes?.length) return null;
23
-
24
- const tabs = renderContext?.state?.tabs || [];
25
- const activeTabIndex = renderContext?.activeTabIndex ?? 0;
26
- const hideHeaders = renderContext?.intl?.locale === JAPANESE_LOCALE;
27
-
28
- const renderedPanes = [];
29
- let supportedOrdinal = 0; // class parity: only supported panes advance the index
30
- container.panes.forEach((pane, paneIndex) => {
31
- const isPaneSupported = typeof pane.isSupported === 'boolean' ? pane.isSupported : true;
32
- if (!isPaneSupported) return;
33
- const tabIndex = supportedOrdinal;
34
- supportedOrdinal += 1;
35
-
36
- // Fields inside this pane read/write their OWN tab; everything else in the
37
- // context is shared. (paneTabIndex is consumed by Section.renderField.)
38
- const paneContext = { ...renderContext, paneTabIndex: tabIndex };
39
- const paneTabKey = tabs[tabIndex]?.tabKey;
40
- const headerSections = pane.sectionsHeaders || container.tabContent?.sections || [];
41
-
42
- renderedPanes.push({
43
- // Class parity: panes render eagerly (forceRender). Hidden-pane reconciliation
44
- // stays cheap through the FieldSlot memo boundaries. Lazy activation is a
45
- // deliberate post-rollout follow-up (see the MPUSH design doc §9).
46
- forceRender: true,
47
- tab: (
48
- <div className="form-tab-header" onInput={(e) => e.stopPropagation()}>
49
- {!hideHeaders && headerSections.map((section, index) => (
50
- <Section key={`hdr-${index}`} section={section} renderContext={paneContext} />
51
- ))}
52
- </div>
53
- ),
54
- key: hasTabKey(paneTabKey) ? `${paneTabKey}` : `${paneIndex}`,
55
- content: (pane.sections || []).map((section, index) => (
56
- <Section key={`sec-${index}`} section={section} renderContext={paneContext} />
57
- )),
58
- });
59
- });
60
-
61
- // Must resolve through the SAME fallback chain as the pane keys — a divergent
62
- // fallback leaves no pane matching activeKey and CapTab renders nothing.
63
- const activeKey = tabs[activeTabIndex]?.tabKey;
64
- const resolvedActiveKey = hasTabKey(activeKey)
65
- ? `${activeKey}`
66
- : (renderedPanes[activeTabIndex]?.key ?? renderedPanes[0]?.key ?? '1');
67
-
68
- return (
69
- <CapTab
70
- className={`cap-tabs-${container.id}`}
71
- activeKey={resolvedActiveKey}
72
- defaultActiveKey={resolvedActiveKey}
73
- onChange={(key) => renderContext?.onTabSwitch?.(container, key)}
74
- style={{ width: '100%' }}
75
- panes={renderedPanes}
76
- />
77
- );
78
- };
79
-
80
- TabsContainer.propTypes = {
81
- container: PropTypes.object,
82
- renderContext: PropTypes.object.isRequired,
83
- };
84
-
85
- TabsContainer.defaultProps = {
86
- container: null,
87
- };
88
-
89
- export default TabsContainer;