@capillarytech/creatives-library 9.0.59-alpha.1 → 9.0.59-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -20,12 +20,15 @@ import CapLabel from '@capillarytech/cap-ui-library/CapLabel';
|
|
|
20
20
|
import CapHeader from '@capillarytech/cap-ui-library/CapHeader';
|
|
21
21
|
import CapSlideBox from '@capillarytech/cap-ui-library/CapSlideBox';
|
|
22
22
|
import CapTooltip from '@capillarytech/cap-ui-library/CapTooltip';
|
|
23
|
+
import CapSpin from '@capillarytech/cap-ui-library/CapSpin';
|
|
23
24
|
import CommunicationFlow from './index';
|
|
24
25
|
import { CHANNELS, DEFAULT_COMMUNICATION_STRATEGY_OPTIONS, DYNAMIC_CONTROLS_CONFIG } from './constants';
|
|
25
26
|
import {
|
|
26
27
|
VIBER, SMS, EMAIL, WHATSAPP, RCS, ZALO,
|
|
27
28
|
} from '../CreativesContainer/constants';
|
|
28
29
|
import getContentBody from './utils/getContentBody';
|
|
30
|
+
import { getCommDefinition, getCommDefinitionVersion } from '../../services/api';
|
|
31
|
+
import { fetchAndMapCommDefinition } from './utils/mapCommDefinitionToStepData';
|
|
29
32
|
import messages from './messages';
|
|
30
33
|
import addCreativesIllustration from '../Assets/images/addCreativesIllustration.svg';
|
|
31
34
|
import './CommunicationFlow.scss';
|
|
@@ -90,6 +93,40 @@ const CommunicationFlowCard = ({
|
|
|
90
93
|
}
|
|
91
94
|
}, [initialData]);
|
|
92
95
|
|
|
96
|
+
// A consumer other than CapNotify (which always pre-fetches and passes full initialData
|
|
97
|
+
// itself) may hand this card only a commDefinitionId — no local content copy at all — and
|
|
98
|
+
// rely on CommunicationFlowCard to fetch + show the configured summary itself, instead of
|
|
99
|
+
// the empty "Add creatives" state. Mirrors CommunicationFlow.js's own fetch-by-id path
|
|
100
|
+
// (config.context.existingCommDefinitionId); doing it here too means the SUMMARY CARD
|
|
101
|
+
// reflects fetched content immediately, without the user having to open the slidebox and
|
|
102
|
+
// click Save first — CommunicationFlow's own fetch would only ever reach this card's
|
|
103
|
+
// savedData via that save round-trip, since nothing here listens for its internal onChange.
|
|
104
|
+
const [isFetchingCommDefinition, setIsFetchingCommDefinition] = useState(
|
|
105
|
+
() => !!config?.context?.existingCommDefinitionId && !initialData?.contentItems?.length,
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
useEffect(() => {
|
|
109
|
+
const existingCommDefinitionId = config?.context?.existingCommDefinitionId;
|
|
110
|
+
if (!existingCommDefinitionId || initialData?.contentItems?.length) return undefined;
|
|
111
|
+
let cancelled = false;
|
|
112
|
+
(async () => {
|
|
113
|
+
try {
|
|
114
|
+
const mapped = await fetchAndMapCommDefinition(existingCommDefinitionId, {
|
|
115
|
+
getCommDefinition,
|
|
116
|
+
getCommDefinitionVersion,
|
|
117
|
+
});
|
|
118
|
+
if (!cancelled && mapped) {
|
|
119
|
+
setSavedData((prevSavedData) => ({ ...prevSavedData, ...mapped }));
|
|
120
|
+
}
|
|
121
|
+
} catch (error) {
|
|
122
|
+
console.error('[CommunicationFlowCard] fetchAndMapCommDefinition error:', error);
|
|
123
|
+
} finally {
|
|
124
|
+
if (!cancelled) setIsFetchingCommDefinition(false);
|
|
125
|
+
}
|
|
126
|
+
})();
|
|
127
|
+
return () => { cancelled = true; };
|
|
128
|
+
}, []);
|
|
129
|
+
|
|
93
130
|
const handleSave = useCallback((data) => {
|
|
94
131
|
setSavedData(data);
|
|
95
132
|
setShowSlideBox(false);
|
|
@@ -199,7 +236,16 @@ const CommunicationFlowCard = ({
|
|
|
199
236
|
|
|
200
237
|
return (
|
|
201
238
|
<>
|
|
202
|
-
{
|
|
239
|
+
{isFetchingCommDefinition ? (
|
|
240
|
+
<CapCard
|
|
241
|
+
className="communication-flow-card-loading"
|
|
242
|
+
bodyStyle={{ padding: 0, height: 152 }}
|
|
243
|
+
>
|
|
244
|
+
<CapRow type="flex" justify="center" align="middle" style={{ height: '100%' }}>
|
|
245
|
+
<CapSpin spinning />
|
|
246
|
+
</CapRow>
|
|
247
|
+
</CapCard>
|
|
248
|
+
) : !firstItem ? (
|
|
203
249
|
<CapCard
|
|
204
250
|
className="communication-flow-card-empty"
|
|
205
251
|
bodyStyle={{ padding: 0, height: 152 }}
|
|
@@ -38,14 +38,25 @@ jest.mock('@capillarytech/cap-ui-library/CapIcon', () => function MockCapIcon({
|
|
|
38
38
|
|
|
39
39
|
jest.mock('../utils/getContentBody', () => jest.fn());
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
jest.mock('../../../services/api', () => ({
|
|
42
|
+
getCommDefinition: jest.fn(),
|
|
43
|
+
getCommDefinitionVersion: jest.fn(),
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
jest.mock('../utils/mapCommDefinitionToStepData', () => ({
|
|
47
|
+
fetchAndMapCommDefinition: jest.fn(),
|
|
48
|
+
}));
|
|
49
|
+
|
|
50
|
+
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
|
42
51
|
import '@testing-library/jest-dom';
|
|
43
52
|
import { IntlProvider } from 'react-intl';
|
|
44
53
|
import CommunicationFlowCard from '../CommunicationFlowCard';
|
|
45
54
|
import getContentBody from '../utils/getContentBody';
|
|
55
|
+
import { fetchAndMapCommDefinition } from '../utils/mapCommDefinitionToStepData';
|
|
46
56
|
|
|
47
57
|
beforeEach(() => {
|
|
48
58
|
getContentBody.mockReturnValue(<span data-testid="content-body">Content</span>);
|
|
59
|
+
fetchAndMapCommDefinition.mockReset();
|
|
49
60
|
});
|
|
50
61
|
|
|
51
62
|
const baseConfig = {
|
|
@@ -711,3 +722,52 @@ describe('CommunicationFlowCard', () => {
|
|
|
711
722
|
});
|
|
712
723
|
});
|
|
713
724
|
});
|
|
725
|
+
|
|
726
|
+
describe('CommunicationFlowCard — fetch-by-id (config.context.existingCommDefinitionId)', () => {
|
|
727
|
+
const configWithId = {
|
|
728
|
+
...baseConfig,
|
|
729
|
+
context: { existingCommDefinitionId: 'cd_1' },
|
|
730
|
+
};
|
|
731
|
+
|
|
732
|
+
it('shows a loading state (not the empty "Add creatives" state) while fetching', () => {
|
|
733
|
+
fetchAndMapCommDefinition.mockReturnValue(new Promise(() => {})); // never resolves
|
|
734
|
+
renderCard({ config: configWithId, initialData: null });
|
|
735
|
+
expect(screen.queryByText('Add creatives')).not.toBeInTheDocument();
|
|
736
|
+
expect(screen.queryByText('Message:')).not.toBeInTheDocument();
|
|
737
|
+
});
|
|
738
|
+
|
|
739
|
+
it('shows the configured summary card once the fetch resolves, without opening the slidebox', async () => {
|
|
740
|
+
fetchAndMapCommDefinition.mockResolvedValue(makeSavedData());
|
|
741
|
+
renderCard({ config: configWithId, initialData: null });
|
|
742
|
+
|
|
743
|
+
await waitFor(() => expect(screen.getByText('Message:')).toBeInTheDocument());
|
|
744
|
+
expect(screen.queryByText('Add creatives')).not.toBeInTheDocument();
|
|
745
|
+
expect(screen.queryByTestId('slide-box')).not.toBeInTheDocument();
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
it('falls back to the empty "Add creatives" state when the fetch resolves with nothing mappable', async () => {
|
|
749
|
+
fetchAndMapCommDefinition.mockResolvedValue(null);
|
|
750
|
+
renderCard({ config: configWithId, initialData: null });
|
|
751
|
+
|
|
752
|
+
await waitFor(() => expect(screen.getByText('Add creatives')).toBeInTheDocument());
|
|
753
|
+
});
|
|
754
|
+
|
|
755
|
+
it('falls back to the empty "Add creatives" state when the fetch rejects', async () => {
|
|
756
|
+
fetchAndMapCommDefinition.mockRejectedValue(new Error('network error'));
|
|
757
|
+
renderCard({ config: configWithId, initialData: null });
|
|
758
|
+
|
|
759
|
+
await waitFor(() => expect(screen.getByText('Add creatives')).toBeInTheDocument());
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
it('does not fetch when initialData already has content (e.g. CapNotify, which always pre-fetches itself)', () => {
|
|
763
|
+
renderCard({ config: configWithId, initialData: makeSavedData() });
|
|
764
|
+
expect(fetchAndMapCommDefinition).not.toHaveBeenCalled();
|
|
765
|
+
expect(screen.getByText('Message:')).toBeInTheDocument();
|
|
766
|
+
});
|
|
767
|
+
|
|
768
|
+
it('does not fetch when config.context.existingCommDefinitionId is absent', () => {
|
|
769
|
+
renderCard({ config: baseConfig, initialData: null });
|
|
770
|
+
expect(fetchAndMapCommDefinition).not.toHaveBeenCalled();
|
|
771
|
+
expect(screen.getByText('Add creatives')).toBeInTheDocument();
|
|
772
|
+
});
|
|
773
|
+
});
|