@capillarytech/creatives-library 9.0.41-alpha.0 → 9.0.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/v2Components/CommonTestAndPreview/UnifiedPreview/ViberCarouselPreviewCards.js +26 -31
- package/v2Components/CommonTestAndPreview/UnifiedPreview/ViberPreviewContent.js +1 -3
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_viberCarouselPreviewCards.scss +118 -116
- package/v2Components/CommonTestAndPreview/constants.js +2 -0
- package/v2Components/CommonTestAndPreview/index.js +4 -4
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/ViberPreviewContent.test.js +10 -10
- package/v2Components/CommonTestAndPreview/tests/utils.test.js +49 -0
- package/v2Components/CommonTestAndPreview/utils.js +20 -0
- package/v2Containers/Templates/_templates.scss +108 -125
- package/v2Containers/Templates/index.js +18 -24
package/package.json
CHANGED
|
@@ -3,18 +3,13 @@ import PropTypes from 'prop-types';
|
|
|
3
3
|
import CapRow from '@capillarytech/cap-ui-library/CapRow';
|
|
4
4
|
import CapLabel from '@capillarytech/cap-ui-library/CapLabel';
|
|
5
5
|
import CapImage from '@capillarytech/cap-ui-library/CapImage';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const getCarouselButtonSlotCount = (cards = []) => {
|
|
15
|
-
const buttonCounts = cards.map((card) => getCarouselButtonsWithTitles(card).length);
|
|
16
|
-
return Math.min(2, Math.max(0, ...buttonCounts, 0));
|
|
17
|
-
};
|
|
6
|
+
import {
|
|
7
|
+
getTrimmedText,
|
|
8
|
+
hasTrimmedText,
|
|
9
|
+
getCarouselButtonsWithTitles,
|
|
10
|
+
getCarouselButtonSlotCount,
|
|
11
|
+
} from '../utils';
|
|
12
|
+
import { DEFAULT_CAROUSEL_TITLE_LINE_HEIGHT_PX } from '../constants';
|
|
18
13
|
|
|
19
14
|
const ViberCarouselPreviewCards = ({ cards = [] }) => {
|
|
20
15
|
const previewCards = Array.isArray(cards) && cards.length ? cards : [{}];
|
|
@@ -29,13 +24,13 @@ const ViberCarouselPreviewCards = ({ cards = [] }) => {
|
|
|
29
24
|
|
|
30
25
|
useLayoutEffect(() => {
|
|
31
26
|
let maxLines = 1;
|
|
32
|
-
carouselTitleRefs.current
|
|
27
|
+
carouselTitleRefs.current?.forEach((node) => {
|
|
33
28
|
if (!node) {
|
|
34
29
|
return;
|
|
35
30
|
}
|
|
36
31
|
const { lineHeight } = window.getComputedStyle(node);
|
|
37
|
-
const parsedLineHeight = parseFloat(lineHeight) ||
|
|
38
|
-
const lines = Math.min(2, Math.max(1, Math.round(node
|
|
32
|
+
const parsedLineHeight = parseFloat(lineHeight) || DEFAULT_CAROUSEL_TITLE_LINE_HEIGHT_PX;
|
|
33
|
+
const lines = Math.min(2, Math.max(1, Math.round(node?.scrollHeight / parsedLineHeight)));
|
|
39
34
|
if (lines > maxLines) {
|
|
40
35
|
maxLines = lines;
|
|
41
36
|
}
|
|
@@ -44,41 +39,41 @@ const ViberCarouselPreviewCards = ({ cards = [] }) => {
|
|
|
44
39
|
}, [previewCards]);
|
|
45
40
|
|
|
46
41
|
return (
|
|
47
|
-
<CapRow type="flex" noWrap className="
|
|
48
|
-
{previewCards
|
|
42
|
+
<CapRow type="flex" noWrap className="scroll">
|
|
43
|
+
{previewCards?.map((card, index) => {
|
|
49
44
|
const cardButtons = getCarouselButtonsWithTitles(card);
|
|
50
|
-
const titleLineClass = `
|
|
45
|
+
const titleLineClass = `text-wrap--${carouselTitleLineCount}-line`;
|
|
51
46
|
|
|
52
47
|
return (
|
|
53
|
-
<CapRow type="flex-column" className="
|
|
48
|
+
<CapRow type="flex-column" className="card" key={`viber-carousel-card-${index}`}>
|
|
54
49
|
{hasTrimmedText(card?.mediaUrl) ? (
|
|
55
50
|
<CapImage
|
|
56
51
|
src={card?.mediaUrl}
|
|
57
|
-
className="
|
|
52
|
+
className="image"
|
|
58
53
|
alt="Viber carousel card"
|
|
59
54
|
/>
|
|
60
55
|
) : (
|
|
61
|
-
<CapRow className="
|
|
56
|
+
<CapRow className="image-placeholder" />
|
|
62
57
|
)}
|
|
63
|
-
<CapRow className="
|
|
64
|
-
<CapRow className={`
|
|
58
|
+
<CapRow className="card-body">
|
|
59
|
+
<CapRow className={`text-wrap ${titleLineClass}`}>
|
|
65
60
|
{hasTrimmedText(card?.text) ? (
|
|
66
61
|
<CapRow
|
|
67
|
-
className="
|
|
62
|
+
className="text-inner"
|
|
68
63
|
ref={(node) => {
|
|
69
64
|
carouselTitleRefs.current[index] = node;
|
|
70
65
|
}}
|
|
71
66
|
>
|
|
72
|
-
<CapLabel type="label15" className="
|
|
67
|
+
<CapLabel type="label15" className="text">
|
|
73
68
|
{card?.text}
|
|
74
69
|
</CapLabel>
|
|
75
70
|
</CapRow>
|
|
76
71
|
) : (
|
|
77
|
-
<CapLabel type="label15" className="
|
|
72
|
+
<CapLabel type="label15" className="text-placeholder" />
|
|
78
73
|
)}
|
|
79
74
|
</CapRow>
|
|
80
75
|
{carouselButtonSlotCount > 0 && (
|
|
81
|
-
<CapRow className="
|
|
76
|
+
<CapRow className="buttons">
|
|
82
77
|
{Array.from({ length: carouselButtonSlotCount }).map((_, btnIndex) => {
|
|
83
78
|
const cardButton = cardButtons[btnIndex];
|
|
84
79
|
const trimmedCardButtonTitle = cardButton
|
|
@@ -88,8 +83,8 @@ const ViberCarouselPreviewCards = ({ cards = [] }) => {
|
|
|
88
83
|
if (!trimmedCardButtonTitle) {
|
|
89
84
|
return (
|
|
90
85
|
<CapRow
|
|
91
|
-
className="
|
|
92
|
-
key={`viber-carousel-
|
|
86
|
+
className="button button-placeholder"
|
|
87
|
+
key={`viber-carousel-btn-placeholder-${index}-${btnIndex}`}
|
|
93
88
|
aria-hidden="true"
|
|
94
89
|
/>
|
|
95
90
|
);
|
|
@@ -97,8 +92,8 @@ const ViberCarouselPreviewCards = ({ cards = [] }) => {
|
|
|
97
92
|
|
|
98
93
|
return (
|
|
99
94
|
<CapLabel
|
|
100
|
-
className={`
|
|
101
|
-
key={`viber-carousel-
|
|
95
|
+
className={`button ${btnIndex === 1 ? 'button-secondary' : ''}`}
|
|
96
|
+
key={`viber-carousel-btn-${index}-${btnIndex}-${trimmedCardButtonTitle}`}
|
|
102
97
|
>
|
|
103
98
|
{trimmedCardButtonTitle}
|
|
104
99
|
</CapLabel>
|
|
@@ -24,15 +24,13 @@ import {
|
|
|
24
24
|
} from '../constants';
|
|
25
25
|
import messages from '../messages';
|
|
26
26
|
import videoPlay from '../../../assets/videoPlay.svg';
|
|
27
|
+
import { getTrimmedText, hasTrimmedText } from '../utils';
|
|
27
28
|
import ViberCarouselPreviewCards from './ViberCarouselPreviewCards';
|
|
28
29
|
|
|
29
30
|
// Import device mockup images (same as SMS)
|
|
30
31
|
const smsMobileAndroid = require('../../../assets/Android.png');
|
|
31
32
|
const smsMobileIos = require('../../../assets/iOS.png');
|
|
32
33
|
|
|
33
|
-
const getTrimmedText = (value = '') => (value ?? '').trim();
|
|
34
|
-
const hasTrimmedText = (value = '') => Boolean(getTrimmedText(value));
|
|
35
|
-
|
|
36
34
|
const ViberPreviewContent = ({
|
|
37
35
|
content,
|
|
38
36
|
device,
|
|
@@ -1,131 +1,133 @@
|
|
|
1
1
|
// Shared Viber carousel card row (Test & Preview device preview)
|
|
2
|
-
.viber-carousel-preview
|
|
3
|
-
|
|
4
|
-
flex-direction: row;
|
|
5
|
-
flex-wrap: nowrap;
|
|
6
|
-
align-items: stretch;
|
|
7
|
-
width: 100%;
|
|
8
|
-
max-width: 100%;
|
|
9
|
-
min-width: 0;
|
|
10
|
-
overflow-x: auto;
|
|
11
|
-
overflow-y: hidden;
|
|
12
|
-
padding-right: $CAP_SPACE_12;
|
|
13
|
-
scrollbar-width: none;
|
|
14
|
-
-webkit-overflow-scrolling: touch;
|
|
15
|
-
|
|
16
|
-
&::-webkit-scrollbar {
|
|
17
|
-
display: none;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
.viber-carousel-preview-card {
|
|
21
|
-
flex: 0 0 68%;
|
|
22
|
-
min-width: 80%;
|
|
23
|
-
margin-right: $CAP_SPACE_08;
|
|
24
|
-
background: $CAP_G09;
|
|
25
|
-
border: 1px solid $CAP_G07;
|
|
26
|
-
border-radius: $CAP_SPACE_12;
|
|
27
|
-
overflow: hidden;
|
|
2
|
+
.viber-carousel-preview {
|
|
3
|
+
.scroll {
|
|
28
4
|
display: flex;
|
|
29
|
-
flex-direction:
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
5
|
+
flex-direction: row;
|
|
6
|
+
flex-wrap: nowrap;
|
|
7
|
+
align-items: stretch;
|
|
8
|
+
width: 100%;
|
|
9
|
+
max-width: 100%;
|
|
10
|
+
min-width: 0;
|
|
11
|
+
overflow-x: auto;
|
|
12
|
+
overflow-y: hidden;
|
|
13
|
+
padding-right: $CAP_SPACE_12;
|
|
14
|
+
scrollbar-width: none;
|
|
15
|
+
-webkit-overflow-scrolling: touch;
|
|
16
|
+
|
|
17
|
+
&::-webkit-scrollbar {
|
|
18
|
+
display: none;
|
|
34
19
|
}
|
|
35
20
|
|
|
36
|
-
.
|
|
37
|
-
flex:
|
|
38
|
-
|
|
21
|
+
.card {
|
|
22
|
+
flex: 0 0 68%;
|
|
23
|
+
min-width: 80%;
|
|
24
|
+
margin-right: $CAP_SPACE_08;
|
|
25
|
+
background: $CAP_G09;
|
|
26
|
+
border: 1px solid $CAP_G07;
|
|
27
|
+
border-radius: $CAP_SPACE_12;
|
|
28
|
+
overflow: hidden;
|
|
39
29
|
display: flex;
|
|
40
30
|
flex-direction: column;
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
.viber-carousel-preview-text-wrap {
|
|
45
|
-
flex-shrink: 0;
|
|
46
|
-
width: 100%;
|
|
31
|
+
align-self: stretch;
|
|
47
32
|
|
|
48
|
-
|
|
49
|
-
|
|
33
|
+
&:last-child {
|
|
34
|
+
margin-right: 0;
|
|
50
35
|
}
|
|
51
36
|
|
|
52
|
-
|
|
53
|
-
|
|
37
|
+
.image {
|
|
38
|
+
width: 100%;
|
|
39
|
+
height: 10rem;
|
|
40
|
+
object-fit: cover;
|
|
41
|
+
border-radius: 0;
|
|
54
42
|
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
.viber-carousel-preview-text-inner {
|
|
58
|
-
width: 100%;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
.viber-carousel-preview-buttons {
|
|
62
|
-
display: flex;
|
|
63
|
-
flex-direction: column;
|
|
64
|
-
gap: $CAP_SPACE_06;
|
|
65
|
-
width: 100%;
|
|
66
|
-
flex-shrink: 0;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
.viber-carousel-preview-image {
|
|
70
|
-
width: 100%;
|
|
71
|
-
height: 10rem;
|
|
72
|
-
object-fit: cover;
|
|
73
|
-
border-radius: 0;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
.viber-carousel-preview-image-placeholder {
|
|
77
|
-
width: 100%;
|
|
78
|
-
height: 10rem;
|
|
79
|
-
border-radius: 0;
|
|
80
|
-
background: $CAP_G07;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
.viber-carousel-preview-text {
|
|
84
|
-
color: $CAP_G01;
|
|
85
|
-
line-height: 1.3;
|
|
86
|
-
white-space: normal;
|
|
87
|
-
word-break: break-word;
|
|
88
|
-
display: block;
|
|
89
|
-
width: 100%;
|
|
90
|
-
margin: 0;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
.viber-carousel-preview-text-placeholder {
|
|
94
|
-
width: 100%;
|
|
95
|
-
height: 0.875rem;
|
|
96
|
-
border-radius: $CAP_SPACE_04;
|
|
97
|
-
background: $CAP_G07;
|
|
98
|
-
min-height: 0.875rem;
|
|
99
|
-
display: block;
|
|
100
|
-
}
|
|
101
43
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
display: flex;
|
|
109
|
-
align-items: center;
|
|
110
|
-
justify-content: center;
|
|
111
|
-
min-height: 1.5rem;
|
|
112
|
-
padding: $CAP_SPACE_06 $CAP_SPACE_08;
|
|
113
|
-
white-space: normal;
|
|
114
|
-
box-sizing: border-box;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
.viber-carousel-preview-button-placeholder {
|
|
118
|
-
visibility: hidden;
|
|
119
|
-
pointer-events: none;
|
|
120
|
-
padding: 0;
|
|
121
|
-
background: transparent;
|
|
122
|
-
border: none;
|
|
123
|
-
}
|
|
44
|
+
.image-placeholder {
|
|
45
|
+
width: 100%;
|
|
46
|
+
height: 10rem;
|
|
47
|
+
border-radius: 0;
|
|
48
|
+
background: $CAP_G07;
|
|
49
|
+
}
|
|
124
50
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
51
|
+
.card-body {
|
|
52
|
+
flex: 1;
|
|
53
|
+
padding: $CAP_SPACE_08;
|
|
54
|
+
display: flex;
|
|
55
|
+
flex-direction: column;
|
|
56
|
+
min-height: 0;
|
|
57
|
+
|
|
58
|
+
.text-wrap {
|
|
59
|
+
flex-shrink: 0;
|
|
60
|
+
width: 100%;
|
|
61
|
+
|
|
62
|
+
&--1-line {
|
|
63
|
+
min-height: 1.3em;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
&--2-line {
|
|
67
|
+
min-height: 2.6em;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.text-inner {
|
|
71
|
+
width: 100%;
|
|
72
|
+
|
|
73
|
+
.text {
|
|
74
|
+
color: $CAP_G01;
|
|
75
|
+
line-height: 1.3;
|
|
76
|
+
white-space: normal;
|
|
77
|
+
word-break: break-word;
|
|
78
|
+
display: block;
|
|
79
|
+
width: 100%;
|
|
80
|
+
margin: 0;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.text-placeholder {
|
|
85
|
+
width: 100%;
|
|
86
|
+
height: 0.875rem;
|
|
87
|
+
border-radius: $CAP_SPACE_04;
|
|
88
|
+
background: $CAP_G07;
|
|
89
|
+
min-height: 0.875rem;
|
|
90
|
+
display: block;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.buttons {
|
|
95
|
+
display: flex;
|
|
96
|
+
flex-direction: column;
|
|
97
|
+
gap: $CAP_SPACE_06;
|
|
98
|
+
width: 100%;
|
|
99
|
+
flex-shrink: 0;
|
|
100
|
+
|
|
101
|
+
.button {
|
|
102
|
+
color: $CAP_WHITE;
|
|
103
|
+
background: $CAP_PURPLE01;
|
|
104
|
+
border-radius: $CAP_SPACE_12;
|
|
105
|
+
text-align: center;
|
|
106
|
+
width: 100%;
|
|
107
|
+
display: flex;
|
|
108
|
+
align-items: center;
|
|
109
|
+
justify-content: center;
|
|
110
|
+
min-height: 1.5rem;
|
|
111
|
+
padding: $CAP_SPACE_06 $CAP_SPACE_08;
|
|
112
|
+
white-space: normal;
|
|
113
|
+
box-sizing: border-box;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.button-placeholder {
|
|
117
|
+
visibility: hidden;
|
|
118
|
+
pointer-events: none;
|
|
119
|
+
padding: 0;
|
|
120
|
+
background: transparent;
|
|
121
|
+
border: none;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.button-secondary {
|
|
125
|
+
color: $CAP_PURPLE01;
|
|
126
|
+
background: transparent;
|
|
127
|
+
border: none;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
129
131
|
}
|
|
130
132
|
}
|
|
131
133
|
}
|
|
@@ -228,6 +228,8 @@ export const CHANNEL_NAME_MOBILE_PUSH = 'MOBILE_PUSH';
|
|
|
228
228
|
export const DEFAULT_SENDER = 'test1';
|
|
229
229
|
export const VIBER_API_SCENARIO_KEY = 'viber-api';
|
|
230
230
|
export const VIBER_USER_ID_PATTERN = '{{viber_user_id}}';
|
|
231
|
+
/** Fallback title line-height (px) when computed style is unavailable. */
|
|
232
|
+
export const DEFAULT_CAROUSEL_TITLE_LINE_HEIGHT_PX = 18;
|
|
231
233
|
export const UNSUBSCRIBE_TAG_NAME = 'unsubscribe';
|
|
232
234
|
export const IN_APP_CHANNEL_NAME = 'in-app';
|
|
233
235
|
export const MOBILE_PUSH_CHANNEL_NAME = 'mobile push';
|
|
@@ -703,10 +703,10 @@ const CommonTestAndPreview = (props) => {
|
|
|
703
703
|
contentObj = {};
|
|
704
704
|
}
|
|
705
705
|
}
|
|
706
|
-
const previewCards = formDataObj
|
|
707
|
-
?? formDataObj
|
|
708
|
-
?? contentObj
|
|
709
|
-
?? contentObj
|
|
706
|
+
const previewCards = formDataObj?.viberPreviewContent?.cards
|
|
707
|
+
?? formDataObj?.cards
|
|
708
|
+
?? contentObj?.viberPreviewContent?.cards
|
|
709
|
+
?? contentObj?.cards;
|
|
710
710
|
const isCarousel = Boolean(
|
|
711
711
|
formDataObj.type === MEDIA_TYPE_CAROUSEL
|
|
712
712
|
|| contentObj.type === MEDIA_TYPE_CAROUSEL
|
|
@@ -322,7 +322,7 @@ describe('ViberPreviewContent', () => {
|
|
|
322
322
|
</TestWrapper>
|
|
323
323
|
);
|
|
324
324
|
|
|
325
|
-
expect(container.querySelector('.viber-carousel-preview
|
|
325
|
+
expect(container.querySelector('.viber-carousel-preview .button')).toBeNull();
|
|
326
326
|
});
|
|
327
327
|
|
|
328
328
|
it('should show carousel shell when showCarouselEditorPreview is true even if cards are empty', () => {
|
|
@@ -347,7 +347,7 @@ describe('ViberPreviewContent', () => {
|
|
|
347
347
|
);
|
|
348
348
|
|
|
349
349
|
expect(screen.queryByText('No content available')).toBeNull();
|
|
350
|
-
expect(container.querySelector('.viber-carousel-preview
|
|
350
|
+
expect(container.querySelector('.viber-carousel-preview .scroll')).toBeTruthy();
|
|
351
351
|
expect(container.querySelector('.viber-carousel-message-box-placeholder')).toBeTruthy();
|
|
352
352
|
});
|
|
353
353
|
|
|
@@ -428,8 +428,8 @@ describe('ViberPreviewContent', () => {
|
|
|
428
428
|
</TestWrapper>
|
|
429
429
|
);
|
|
430
430
|
|
|
431
|
-
expect(container.querySelector('.viber-carousel-preview
|
|
432
|
-
expect(container.querySelector('.viber-carousel-preview
|
|
431
|
+
expect(container.querySelector('.viber-carousel-preview .image-placeholder')).toBeTruthy();
|
|
432
|
+
expect(container.querySelector('.viber-carousel-preview .image')).toBeNull();
|
|
433
433
|
});
|
|
434
434
|
|
|
435
435
|
it('should render at most two carousel buttons per card', () => {
|
|
@@ -459,7 +459,7 @@ describe('ViberPreviewContent', () => {
|
|
|
459
459
|
</TestWrapper>
|
|
460
460
|
);
|
|
461
461
|
|
|
462
|
-
expect(container.querySelectorAll('.viber-carousel-preview
|
|
462
|
+
expect(container.querySelectorAll('.viber-carousel-preview .button')).toHaveLength(2);
|
|
463
463
|
expect(screen.getByText('One')).toBeTruthy();
|
|
464
464
|
expect(screen.getByText('Two')).toBeTruthy();
|
|
465
465
|
expect(screen.queryByText('Three')).toBeNull();
|
|
@@ -491,10 +491,10 @@ describe('ViberPreviewContent', () => {
|
|
|
491
491
|
</TestWrapper>
|
|
492
492
|
);
|
|
493
493
|
|
|
494
|
-
const buttons = container.querySelectorAll('.viber-carousel-preview
|
|
495
|
-
expect(buttons[0].className).toContain('
|
|
496
|
-
expect(buttons[0].className).not.toContain('
|
|
497
|
-
expect(buttons[1].className).toContain('
|
|
494
|
+
const buttons = container.querySelectorAll('.viber-carousel-preview .button');
|
|
495
|
+
expect(buttons[0].className).toContain('button');
|
|
496
|
+
expect(buttons[0].className).not.toContain('button-secondary');
|
|
497
|
+
expect(buttons[1].className).toContain('button-secondary');
|
|
498
498
|
});
|
|
499
499
|
|
|
500
500
|
it('should show carousel when only a button title is present on a card', () => {
|
|
@@ -542,7 +542,7 @@ describe('ViberPreviewContent', () => {
|
|
|
542
542
|
</TestWrapper>
|
|
543
543
|
);
|
|
544
544
|
|
|
545
|
-
expect(container.querySelectorAll('.viber-carousel-preview
|
|
545
|
+
expect(container.querySelectorAll('.viber-carousel-preview .card')).toHaveLength(1);
|
|
546
546
|
});
|
|
547
547
|
|
|
548
548
|
it('should show no content when CAROUSEL has empty cards and no editor preview flag', () => {
|
|
@@ -6,6 +6,10 @@ import {
|
|
|
6
6
|
mapRcsSuggestionForTestMeta,
|
|
7
7
|
typeMatches,
|
|
8
8
|
unwrapEntity,
|
|
9
|
+
getTrimmedText,
|
|
10
|
+
hasTrimmedText,
|
|
11
|
+
getCarouselButtonsWithTitles,
|
|
12
|
+
getCarouselButtonSlotCount,
|
|
9
13
|
} from '../utils';
|
|
10
14
|
|
|
11
15
|
describe('CommonTestAndPreview/utils', () => {
|
|
@@ -148,4 +152,49 @@ describe('CommonTestAndPreview/utils', () => {
|
|
|
148
152
|
expect(unwrapEntity(response)).toBe(response);
|
|
149
153
|
});
|
|
150
154
|
});
|
|
155
|
+
|
|
156
|
+
describe('getTrimmedText / hasTrimmedText', () => {
|
|
157
|
+
it('trims string values', () => {
|
|
158
|
+
expect(getTrimmedText(' hello ')).toBe('hello');
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('treats null/undefined as empty', () => {
|
|
162
|
+
expect(getTrimmedText(null)).toBe('');
|
|
163
|
+
expect(getTrimmedText(undefined)).toBe('');
|
|
164
|
+
expect(hasTrimmedText(' ')).toBe(false);
|
|
165
|
+
expect(hasTrimmedText('ok')).toBe(true);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
describe('getCarouselButtonsWithTitles', () => {
|
|
170
|
+
it('returns up to 2 buttons with non-empty titles', () => {
|
|
171
|
+
expect(getCarouselButtonsWithTitles({
|
|
172
|
+
buttons: [
|
|
173
|
+
{ title: ' A ' },
|
|
174
|
+
{ title: '' },
|
|
175
|
+
{ title: 'B' },
|
|
176
|
+
{ title: 'C' },
|
|
177
|
+
],
|
|
178
|
+
})).toEqual([{ title: ' A ' }, { title: 'B' }]);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('returns empty array when card has no titled buttons', () => {
|
|
182
|
+
expect(getCarouselButtonsWithTitles({})).toEqual([]);
|
|
183
|
+
expect(getCarouselButtonsWithTitles(undefined)).toEqual([]);
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
describe('getCarouselButtonSlotCount', () => {
|
|
188
|
+
it('returns the max titled-button count across cards, capped at 2', () => {
|
|
189
|
+
expect(getCarouselButtonSlotCount([
|
|
190
|
+
{ buttons: [{ title: 'A' }] },
|
|
191
|
+
{ buttons: [{ title: 'A' }, { title: 'B' }, { title: 'C' }] },
|
|
192
|
+
])).toBe(2);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('returns 0 when no cards have titled buttons', () => {
|
|
196
|
+
expect(getCarouselButtonSlotCount([])).toBe(0);
|
|
197
|
+
expect(getCarouselButtonSlotCount([{ buttons: [{ title: ' ' }] }])).toBe(0);
|
|
198
|
+
});
|
|
199
|
+
});
|
|
151
200
|
});
|
|
@@ -116,3 +116,23 @@ export const unwrapEntity = (response) => {
|
|
|
116
116
|
if (response.entity === null) return null;
|
|
117
117
|
return response;
|
|
118
118
|
};
|
|
119
|
+
|
|
120
|
+
/** Trim string-like values; null/undefined become ''. */
|
|
121
|
+
export const getTrimmedText = (value = '') => (value ?? '').trim();
|
|
122
|
+
|
|
123
|
+
/** True when trimmed text is non-empty. */
|
|
124
|
+
export const hasTrimmedText = (value = '') => Boolean(getTrimmedText(value));
|
|
125
|
+
|
|
126
|
+
/** First up to 2 carousel buttons that have a non-empty title. */
|
|
127
|
+
export const getCarouselButtonsWithTitles = (card) => (
|
|
128
|
+
(card?.buttons ?? []).filter((button) => hasTrimmedText(button?.title)).slice(0, 2)
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Max button slots to reserve across carousel cards (0–2), so cards align
|
|
133
|
+
* even when some cards have fewer titled buttons.
|
|
134
|
+
*/
|
|
135
|
+
export const getCarouselButtonSlotCount = (cards = []) => {
|
|
136
|
+
const buttonCounts = cards.map((card) => getCarouselButtonsWithTitles(card).length);
|
|
137
|
+
return Math.min(2, Math.max(0, ...buttonCounts, 0));
|
|
138
|
+
};
|
|
@@ -160,103 +160,6 @@
|
|
|
160
160
|
line-clamp: 3;
|
|
161
161
|
}
|
|
162
162
|
}
|
|
163
|
-
.viber-carousel-static-content {
|
|
164
|
-
width: 100%;
|
|
165
|
-
overflow: visible;
|
|
166
|
-
display: flex;
|
|
167
|
-
flex-direction: column;
|
|
168
|
-
.viber-carousel-static-message-box {
|
|
169
|
-
width: 100%;
|
|
170
|
-
height: auto;
|
|
171
|
-
border-radius: $CAP_SPACE_04;
|
|
172
|
-
background: $CAP_WHITE;
|
|
173
|
-
margin-bottom: $CAP_SPACE_08;
|
|
174
|
-
display: flex;
|
|
175
|
-
align-items: flex-start;
|
|
176
|
-
}
|
|
177
|
-
.viber-carousel-static-message {
|
|
178
|
-
display: -webkit-box;
|
|
179
|
-
width: 100%;
|
|
180
|
-
color: $CAP_G01;
|
|
181
|
-
line-height: 1.3;
|
|
182
|
-
overflow: hidden;
|
|
183
|
-
text-overflow: ellipsis;
|
|
184
|
-
-webkit-line-clamp: 2;
|
|
185
|
-
line-clamp: 2;
|
|
186
|
-
-webkit-box-orient: vertical;
|
|
187
|
-
white-space: normal;
|
|
188
|
-
word-break: break-word;
|
|
189
|
-
}
|
|
190
|
-
.viber-carousel-static-cards {
|
|
191
|
-
display: flex;
|
|
192
|
-
gap: $CAP_SPACE_08;
|
|
193
|
-
overflow: visible;
|
|
194
|
-
}
|
|
195
|
-
.viber-carousel-static-card {
|
|
196
|
-
flex: 1;
|
|
197
|
-
min-width: 0;
|
|
198
|
-
width: 100%;
|
|
199
|
-
background: $CAP_WHITE;
|
|
200
|
-
border-radius: $CAP_SPACE_04;
|
|
201
|
-
overflow: visible;
|
|
202
|
-
display: flex;
|
|
203
|
-
flex-direction: column;
|
|
204
|
-
}
|
|
205
|
-
.viber-carousel-static-image {
|
|
206
|
-
width: 100%;
|
|
207
|
-
max-height: 11rem;
|
|
208
|
-
margin-bottom: $CAP_SPACE_04;
|
|
209
|
-
border-radius: $CAP_SPACE_04;
|
|
210
|
-
display: block;
|
|
211
|
-
flex-shrink: 0;
|
|
212
|
-
}
|
|
213
|
-
.viber-carousel-static-image-placeholder {
|
|
214
|
-
width: 100%;
|
|
215
|
-
height: 7.143rem;
|
|
216
|
-
border-radius: $CAP_SPACE_04;
|
|
217
|
-
}
|
|
218
|
-
.viber-carousel-static-image-placeholder {
|
|
219
|
-
background: $CAP_G07;
|
|
220
|
-
}
|
|
221
|
-
.viber-carousel-static-text {
|
|
222
|
-
display: block;
|
|
223
|
-
color: $CAP_G01;
|
|
224
|
-
margin: $CAP_SPACE_04 0;
|
|
225
|
-
line-height: 1.3;
|
|
226
|
-
overflow: hidden;
|
|
227
|
-
text-overflow: ellipsis;
|
|
228
|
-
white-space: normal;
|
|
229
|
-
-webkit-line-clamp: 2;
|
|
230
|
-
line-clamp: 2;
|
|
231
|
-
-webkit-box-orient: vertical;
|
|
232
|
-
display: -webkit-box;
|
|
233
|
-
flex-shrink: 0;
|
|
234
|
-
}
|
|
235
|
-
.viber-carousel-static-buttons {
|
|
236
|
-
display: flex;
|
|
237
|
-
flex-direction: column;
|
|
238
|
-
gap: $CAP_SPACE_04;
|
|
239
|
-
flex-shrink: 0;
|
|
240
|
-
width: 100%;
|
|
241
|
-
}
|
|
242
|
-
.viber-carousel-static-button {
|
|
243
|
-
display: block;
|
|
244
|
-
min-height: 1.5rem;
|
|
245
|
-
border-radius: $CAP_SPACE_12;
|
|
246
|
-
background: $CAP_PURPLE01;
|
|
247
|
-
color: $CAP_WHITE;
|
|
248
|
-
text-align: center;
|
|
249
|
-
padding: 0.179rem $CAP_SPACE_08;
|
|
250
|
-
white-space: normal;
|
|
251
|
-
}
|
|
252
|
-
.viber-carousel-static-button-secondary {
|
|
253
|
-
color: $CAP_PURPLE01;
|
|
254
|
-
background: transparent;
|
|
255
|
-
border: none;
|
|
256
|
-
box-shadow: none;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
163
|
}
|
|
261
164
|
|
|
262
165
|
&.no-image .ant-card-meta {
|
|
@@ -294,9 +197,9 @@
|
|
|
294
197
|
}
|
|
295
198
|
}
|
|
296
199
|
|
|
297
|
-
.
|
|
298
|
-
.
|
|
299
|
-
.
|
|
200
|
+
.content,
|
|
201
|
+
.cards,
|
|
202
|
+
.card {
|
|
300
203
|
overflow: visible;
|
|
301
204
|
height: auto;
|
|
302
205
|
}
|
|
@@ -1383,38 +1286,118 @@
|
|
|
1383
1286
|
line-clamp: unset;
|
|
1384
1287
|
-webkit-box-orient: unset;
|
|
1385
1288
|
}
|
|
1289
|
+
}
|
|
1386
1290
|
|
|
1387
|
-
|
|
1388
|
-
.
|
|
1389
|
-
.viber-carousel-static-buttons {
|
|
1390
|
-
display: flex;
|
|
1391
|
-
flex-direction: column;
|
|
1291
|
+
.viber-carousel-static {
|
|
1292
|
+
.content {
|
|
1392
1293
|
width: 100%;
|
|
1393
|
-
}
|
|
1394
|
-
|
|
1395
|
-
.viber-carousel-static-content,
|
|
1396
|
-
.viber-carousel-static-cards,
|
|
1397
|
-
.viber-carousel-static-card {
|
|
1398
1294
|
overflow: visible;
|
|
1295
|
+
display: flex;
|
|
1296
|
+
flex-direction: column;
|
|
1399
1297
|
height: auto;
|
|
1400
|
-
}
|
|
1401
1298
|
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1299
|
+
.message-box {
|
|
1300
|
+
width: 100%;
|
|
1301
|
+
height: auto;
|
|
1302
|
+
border-radius: $CAP_SPACE_04;
|
|
1303
|
+
background: $CAP_WHITE;
|
|
1304
|
+
margin-bottom: $CAP_SPACE_08;
|
|
1305
|
+
display: flex;
|
|
1306
|
+
align-items: flex-start;
|
|
1405
1307
|
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1308
|
+
.message {
|
|
1309
|
+
display: -webkit-box;
|
|
1310
|
+
width: 100%;
|
|
1311
|
+
color: $CAP_G01;
|
|
1312
|
+
line-height: 1.3;
|
|
1313
|
+
overflow: hidden;
|
|
1314
|
+
text-overflow: ellipsis;
|
|
1315
|
+
-webkit-line-clamp: 2;
|
|
1316
|
+
line-clamp: 2;
|
|
1317
|
+
-webkit-box-orient: vertical;
|
|
1318
|
+
white-space: normal;
|
|
1319
|
+
word-break: break-word;
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1412
1322
|
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1323
|
+
.cards {
|
|
1324
|
+
display: flex;
|
|
1325
|
+
gap: $CAP_SPACE_08;
|
|
1326
|
+
overflow: visible;
|
|
1327
|
+
width: 100%;
|
|
1328
|
+
height: auto;
|
|
1329
|
+
|
|
1330
|
+
.card {
|
|
1331
|
+
flex: 1;
|
|
1332
|
+
min-width: 0;
|
|
1333
|
+
width: 100%;
|
|
1334
|
+
background: $CAP_WHITE;
|
|
1335
|
+
border-radius: $CAP_SPACE_04;
|
|
1336
|
+
overflow: visible;
|
|
1337
|
+
display: flex;
|
|
1338
|
+
flex-direction: column;
|
|
1339
|
+
height: auto;
|
|
1340
|
+
|
|
1341
|
+
.image {
|
|
1342
|
+
width: 100%;
|
|
1343
|
+
max-height: 11rem;
|
|
1344
|
+
margin-bottom: $CAP_SPACE_04;
|
|
1345
|
+
border-radius: $CAP_SPACE_04;
|
|
1346
|
+
display: block;
|
|
1347
|
+
flex-shrink: 0;
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
.image-placeholder {
|
|
1351
|
+
width: 100%;
|
|
1352
|
+
height: 7.143rem;
|
|
1353
|
+
border-radius: $CAP_SPACE_04;
|
|
1354
|
+
background: $CAP_G07;
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
.text {
|
|
1358
|
+
display: -webkit-box;
|
|
1359
|
+
color: $CAP_G01;
|
|
1360
|
+
margin: $CAP_SPACE_04 0;
|
|
1361
|
+
line-height: 1.3;
|
|
1362
|
+
overflow: hidden;
|
|
1363
|
+
text-overflow: ellipsis;
|
|
1364
|
+
white-space: normal;
|
|
1365
|
+
-webkit-line-clamp: 2;
|
|
1366
|
+
line-clamp: 2;
|
|
1367
|
+
-webkit-box-orient: vertical;
|
|
1368
|
+
flex-shrink: 0;
|
|
1369
|
+
width: 100%;
|
|
1370
|
+
visibility: visible;
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
.buttons {
|
|
1374
|
+
display: flex;
|
|
1375
|
+
flex-direction: column;
|
|
1376
|
+
gap: $CAP_SPACE_04;
|
|
1377
|
+
flex-shrink: 0;
|
|
1378
|
+
width: 100%;
|
|
1379
|
+
visibility: visible;
|
|
1380
|
+
|
|
1381
|
+
.button {
|
|
1382
|
+
display: block;
|
|
1383
|
+
min-height: 1.5rem;
|
|
1384
|
+
border-radius: $CAP_SPACE_12;
|
|
1385
|
+
background: $CAP_PURPLE01;
|
|
1386
|
+
color: $CAP_WHITE;
|
|
1387
|
+
text-align: center;
|
|
1388
|
+
padding: 0.179rem $CAP_SPACE_08;
|
|
1389
|
+
white-space: normal;
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
.button-secondary {
|
|
1393
|
+
color: $CAP_PURPLE01;
|
|
1394
|
+
background: transparent;
|
|
1395
|
+
border: none;
|
|
1396
|
+
box-shadow: none;
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1418
1401
|
}
|
|
1419
1402
|
}
|
|
1420
1403
|
.whatsapp-doc {
|
|
@@ -2476,43 +2476,37 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
2476
2476
|
if (isViberCarousel) {
|
|
2477
2477
|
const previewCards = Array.isArray(cards) ? cards.slice(0, 1) : [];
|
|
2478
2478
|
// Bypass CapCustomCard Viber Meta + line-clamp (clips title/buttons for tall images).
|
|
2479
|
-
//
|
|
2479
|
+
// Parent class `viber-carousel-static` scopes listing styles in _templates.scss.
|
|
2480
|
+
// cardType hits the default content path; keep VIBER for channel card styles.
|
|
2480
2481
|
templateData.cardType = 'VIBER_CAROUSEL_PREVIEW';
|
|
2481
2482
|
templateData.className = 'viber-carousel-static VIBER';
|
|
2482
|
-
templateData.style = {
|
|
2483
|
-
height: 'auto',
|
|
2484
|
-
minHeight: '21.125rem',
|
|
2485
|
-
overflow: 'visible',
|
|
2486
|
-
};
|
|
2487
|
-
templateData.bodyStyle = {
|
|
2488
|
-
height: 'auto',
|
|
2489
|
-
overflow: 'visible',
|
|
2490
|
-
paddingBottom: '12px',
|
|
2491
|
-
};
|
|
2492
2483
|
templateData.content = (
|
|
2493
|
-
<CapRow vertical className="
|
|
2494
|
-
<CapRow className="
|
|
2495
|
-
<CapLabel type="label1" className="
|
|
2484
|
+
<CapRow vertical className="content">
|
|
2485
|
+
<CapRow className="message-box">
|
|
2486
|
+
<CapLabel type="label1" className="message">
|
|
2496
2487
|
{text}
|
|
2497
2488
|
</CapLabel>
|
|
2498
2489
|
</CapRow>
|
|
2499
|
-
<CapRow className="
|
|
2500
|
-
{previewCards
|
|
2501
|
-
<CapRow vertical className="
|
|
2490
|
+
<CapRow className="cards">
|
|
2491
|
+
{previewCards?.map((card, cardIdx) => (
|
|
2492
|
+
<CapRow vertical className="card" key={`viber-carousel-static-card-${cardIdx}`}>
|
|
2502
2493
|
{card?.mediaUrl ? (
|
|
2503
|
-
<CapImage src={card.mediaUrl} className="
|
|
2494
|
+
<CapImage src={card.mediaUrl} className="image" />
|
|
2504
2495
|
) : (
|
|
2505
|
-
<CapRow className="
|
|
2496
|
+
<CapRow className="image-placeholder" />
|
|
2506
2497
|
)}
|
|
2507
|
-
<CapLabel type="label1" className="
|
|
2498
|
+
<CapLabel type="label1" className="text">
|
|
2508
2499
|
{card?.text}
|
|
2509
2500
|
</CapLabel>
|
|
2510
|
-
<CapRow vertical className="
|
|
2511
|
-
{(Array.isArray(card?.buttons) && card.buttons.length
|
|
2501
|
+
<CapRow vertical className="buttons">
|
|
2502
|
+
{(Array.isArray(card?.buttons) && card.buttons.length
|
|
2503
|
+
? card.buttons
|
|
2504
|
+
: [{}, {}]
|
|
2505
|
+
).slice(0, 2).map((carouselButton, btnIdx) => (
|
|
2512
2506
|
<CapLabel
|
|
2513
|
-
key={`viber-static-btn-${cardIdx}-${btnIdx}`}
|
|
2507
|
+
key={`viber-carousel-static-btn-${cardIdx}-${btnIdx}`}
|
|
2514
2508
|
type="label1"
|
|
2515
|
-
className={`
|
|
2509
|
+
className={`button${btnIdx === 1 ? ' button-secondary' : ''}`}
|
|
2516
2510
|
>
|
|
2517
2511
|
{(carouselButton?.title || '').trim()}
|
|
2518
2512
|
</CapLabel>
|