@weni/unnnic-system 3.25.5 → 3.25.7
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/dist/{es-ByCgA36w.mjs → es-Cez02syJ.mjs} +1 -1
- package/dist/{index-Cb-3tgt0.mjs → index-B-NJIYYT.mjs} +820 -761
- package/dist/index.d.ts +198 -2
- package/dist/{pt-br-InR8qOLr.mjs → pt-br-u8UEMjaT.mjs} +1 -1
- package/dist/style.css +1 -1
- package/dist/unnnic.mjs +1 -1
- package/dist/unnnic.umd.js +19 -19
- package/package.json +1 -1
- package/src/components/ChatsContact/ChatsContact.vue +88 -0
- package/src/components/ChatsContact/__tests__/ChatsContact.spec.js +164 -1
- package/src/components/ChatsContact/__tests__/__snapshots__/ChatsContact.spec.js.snap +1 -0
- package/src/stories/ChatsContact.stories.js +83 -0
package/package.json
CHANGED
|
@@ -67,6 +67,13 @@
|
|
|
67
67
|
</p>
|
|
68
68
|
<template v-else-if="lastMessageMedia.isMedia">
|
|
69
69
|
<section class="chats-contact__infos__media">
|
|
70
|
+
<p
|
|
71
|
+
v-if="lastMessage.isFromUser"
|
|
72
|
+
class="chats-contact__infos__media__sender-prefix"
|
|
73
|
+
data-testid="media-sender-prefix"
|
|
74
|
+
>
|
|
75
|
+
{{ i18n('you_message_prefix') }}
|
|
76
|
+
</p>
|
|
70
77
|
<UnnnicIcon
|
|
71
78
|
:icon="lastMessageMedia.mediaIcon"
|
|
72
79
|
scheme="fg-base"
|
|
@@ -106,6 +113,20 @@
|
|
|
106
113
|
<section
|
|
107
114
|
class="chats-contact__infos__unread-messages-container__pin-container"
|
|
108
115
|
>
|
|
116
|
+
<UnnnicTooltip
|
|
117
|
+
v-if="pendingResponse"
|
|
118
|
+
class="chats-contact__infos__pending-response"
|
|
119
|
+
:enabled="!!pendingResponseTooltip"
|
|
120
|
+
:text="pendingResponseTooltip"
|
|
121
|
+
side="top"
|
|
122
|
+
>
|
|
123
|
+
<UnnnicIcon
|
|
124
|
+
icon="reply"
|
|
125
|
+
size="sm"
|
|
126
|
+
scheme="fg-info"
|
|
127
|
+
data-testid="pending-response-icon"
|
|
128
|
+
/>
|
|
129
|
+
</UnnnicTooltip>
|
|
109
130
|
<button
|
|
110
131
|
v-if="isRenderPin"
|
|
111
132
|
data-testid="pin-button"
|
|
@@ -121,6 +142,18 @@
|
|
|
121
142
|
:scheme="schemePin"
|
|
122
143
|
/>
|
|
123
144
|
</button>
|
|
145
|
+
<UnnnicTooltip
|
|
146
|
+
v-else-if="newMessageIndicator"
|
|
147
|
+
class="chats-contact__infos__new-message-indicator-tooltip"
|
|
148
|
+
:enabled="!!newMessageIndicatorTooltip"
|
|
149
|
+
:text="newMessageIndicatorTooltip"
|
|
150
|
+
side="top"
|
|
151
|
+
>
|
|
152
|
+
<span
|
|
153
|
+
class="chats-contact__infos__new-message-indicator"
|
|
154
|
+
data-testid="new-message-indicator"
|
|
155
|
+
/>
|
|
156
|
+
</UnnnicTooltip>
|
|
124
157
|
<p
|
|
125
158
|
v-else-if="(unreadMessages && !selected) || forceShowUnreadMessages"
|
|
126
159
|
class="chats-contact__infos__unread-messages"
|
|
@@ -148,6 +181,7 @@ import UnnnicIcon from '../Icon.vue';
|
|
|
148
181
|
import TransitionRipple from '../TransitionRipple/TransitionRipple.vue';
|
|
149
182
|
import UnnnicI18n from '../../mixins/i18n';
|
|
150
183
|
import Checkbox from '../Checkbox/Checkbox.vue';
|
|
184
|
+
import UnnnicTooltip from '../ToolTip/ToolTip.vue';
|
|
151
185
|
|
|
152
186
|
import('moment/dist/locale/es.js');
|
|
153
187
|
import('moment/dist/locale/pt-br.js');
|
|
@@ -162,6 +196,7 @@ export default {
|
|
|
162
196
|
UnnnicIcon,
|
|
163
197
|
TransitionRipple,
|
|
164
198
|
Checkbox,
|
|
199
|
+
UnnnicTooltip,
|
|
165
200
|
},
|
|
166
201
|
|
|
167
202
|
mixins: [UnnnicI18n],
|
|
@@ -235,6 +270,22 @@ export default {
|
|
|
235
270
|
type: String,
|
|
236
271
|
default: ' fg-base',
|
|
237
272
|
},
|
|
273
|
+
pendingResponse: {
|
|
274
|
+
type: Boolean,
|
|
275
|
+
default: false,
|
|
276
|
+
},
|
|
277
|
+
pendingResponseTooltip: {
|
|
278
|
+
type: String,
|
|
279
|
+
default: '',
|
|
280
|
+
},
|
|
281
|
+
newMessageIndicator: {
|
|
282
|
+
type: Boolean,
|
|
283
|
+
default: false,
|
|
284
|
+
},
|
|
285
|
+
newMessageIndicatorTooltip: {
|
|
286
|
+
type: String,
|
|
287
|
+
default: '',
|
|
288
|
+
},
|
|
238
289
|
},
|
|
239
290
|
|
|
240
291
|
emits: ['click', 'clickPin'],
|
|
@@ -285,12 +336,18 @@ export default {
|
|
|
285
336
|
en: 'Video',
|
|
286
337
|
es: 'Video',
|
|
287
338
|
},
|
|
339
|
+
you_message_prefix: {
|
|
340
|
+
'pt-br': 'Você:',
|
|
341
|
+
en: 'You:',
|
|
342
|
+
es: 'Tú:',
|
|
343
|
+
},
|
|
288
344
|
},
|
|
289
345
|
};
|
|
290
346
|
},
|
|
291
347
|
|
|
292
348
|
computed: {
|
|
293
349
|
messageInfoAlign() {
|
|
350
|
+
if (this.newMessageIndicator) return 'space-between';
|
|
294
351
|
return this.unreadMessages && this.selected ? 'center' : 'flex-start';
|
|
295
352
|
},
|
|
296
353
|
messageInfoMarginTop() {
|
|
@@ -528,6 +585,10 @@ export default {
|
|
|
528
585
|
display: flex;
|
|
529
586
|
align-items: center;
|
|
530
587
|
gap: $unnnic-spacing-nano;
|
|
588
|
+
|
|
589
|
+
&__sender-prefix {
|
|
590
|
+
color: $unnnic-color-fg-base;
|
|
591
|
+
}
|
|
531
592
|
}
|
|
532
593
|
|
|
533
594
|
&__title,
|
|
@@ -547,6 +608,11 @@ export default {
|
|
|
547
608
|
color: $unnnic-color-fg-muted;
|
|
548
609
|
}
|
|
549
610
|
|
|
611
|
+
&__pending-response {
|
|
612
|
+
display: flex;
|
|
613
|
+
align-items: center;
|
|
614
|
+
}
|
|
615
|
+
|
|
550
616
|
&__unread-messages-container {
|
|
551
617
|
height: 100%;
|
|
552
618
|
min-width: max-content;
|
|
@@ -560,6 +626,7 @@ export default {
|
|
|
560
626
|
&__pin-container {
|
|
561
627
|
display: flex;
|
|
562
628
|
align-items: center;
|
|
629
|
+
gap: $unnnic-space-1;
|
|
563
630
|
position: relative;
|
|
564
631
|
z-index: 10;
|
|
565
632
|
}
|
|
@@ -617,6 +684,27 @@ export default {
|
|
|
617
684
|
color: $unnnic-color-weni-700;
|
|
618
685
|
}
|
|
619
686
|
|
|
687
|
+
&__new-message-indicator {
|
|
688
|
+
display: flex;
|
|
689
|
+
justify-content: center;
|
|
690
|
+
align-items: center;
|
|
691
|
+
|
|
692
|
+
width: $unnnic-space-5;
|
|
693
|
+
min-width: $unnnic-space-5;
|
|
694
|
+
height: $unnnic-space-5;
|
|
695
|
+
|
|
696
|
+
&::before {
|
|
697
|
+
content: '';
|
|
698
|
+
display: block;
|
|
699
|
+
|
|
700
|
+
width: $unnnic-space-2;
|
|
701
|
+
height: $unnnic-space-2;
|
|
702
|
+
|
|
703
|
+
border-radius: 50%;
|
|
704
|
+
background-color: $unnnic-color-bg-blue-strong;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
620
708
|
.ellipsis {
|
|
621
709
|
overflow: hidden;
|
|
622
710
|
text-overflow: ellipsis;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, afterEach,
|
|
1
|
+
import { beforeEach, describe, expect, afterEach, it, vi } from 'vitest';
|
|
2
2
|
import { mount } from '@vue/test-utils';
|
|
3
3
|
|
|
4
4
|
import UnnnicChatsContact from '../ChatsContact.vue';
|
|
@@ -258,4 +258,167 @@ describe('UnnnicChatsContact', () => {
|
|
|
258
258
|
expect(pinElement.exists()).toBe(false);
|
|
259
259
|
});
|
|
260
260
|
});
|
|
261
|
+
|
|
262
|
+
describe('Media "You:" prefix', () => {
|
|
263
|
+
const imageMedia = {
|
|
264
|
+
media: [
|
|
265
|
+
{
|
|
266
|
+
content_type: 'image/jpeg',
|
|
267
|
+
message: 'a997421d-6238-4bef-912d-e689c1c0db3f',
|
|
268
|
+
url: 'https://example.com/photo.jpg',
|
|
269
|
+
created_on: '2025-04-15T14:58:56.163027-03:00',
|
|
270
|
+
},
|
|
271
|
+
],
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
it('should not render the prefix on a media message when isFromUser is falsy', async () => {
|
|
275
|
+
await wrapper.setProps({ lastMessage: imageMedia });
|
|
276
|
+
|
|
277
|
+
const prefix = wrapper.find('[data-testid="media-sender-prefix"]');
|
|
278
|
+
expect(prefix.exists()).toBe(false);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('should render the EN "You:" prefix when isFromUser is true', async () => {
|
|
282
|
+
await wrapper.setProps({
|
|
283
|
+
lastMessage: { ...imageMedia, isFromUser: true },
|
|
284
|
+
locale: 'en',
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
const prefix = wrapper.find('[data-testid="media-sender-prefix"]');
|
|
288
|
+
expect(prefix.exists()).toBe(true);
|
|
289
|
+
expect(prefix.text()).toBe('You:');
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('should render the PT-BR "Você:" prefix when isFromUser is true', async () => {
|
|
293
|
+
await wrapper.setProps({
|
|
294
|
+
lastMessage: { ...imageMedia, isFromUser: true },
|
|
295
|
+
locale: 'pt-br',
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
const prefix = wrapper.find('[data-testid="media-sender-prefix"]');
|
|
299
|
+
expect(prefix.exists()).toBe(true);
|
|
300
|
+
expect(prefix.text()).toBe('Você:');
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it('should render the ES "Tú:" prefix when isFromUser is true', async () => {
|
|
304
|
+
await wrapper.setProps({
|
|
305
|
+
lastMessage: { ...imageMedia, isFromUser: true },
|
|
306
|
+
locale: 'es',
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
const prefix = wrapper.find('[data-testid="media-sender-prefix"]');
|
|
310
|
+
expect(prefix.exists()).toBe(true);
|
|
311
|
+
expect(prefix.text()).toBe('Tú:');
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('should not render the prefix for non-media messages even when isFromUser is true', async () => {
|
|
315
|
+
await wrapper.setProps({
|
|
316
|
+
lastMessage: { text: 'Hello there', isFromUser: true },
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
const prefix = wrapper.find('[data-testid="media-sender-prefix"]');
|
|
320
|
+
expect(prefix.exists()).toBe(false);
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
describe('New message indicator', () => {
|
|
325
|
+
it('should not render the dot by default', () => {
|
|
326
|
+
const indicator = wrapper.find('[data-testid="new-message-indicator"]');
|
|
327
|
+
expect(indicator.exists()).toBe(false);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it('should render the dot when newMessageIndicator is true', async () => {
|
|
331
|
+
await wrapper.setProps({ newMessageIndicator: true });
|
|
332
|
+
|
|
333
|
+
const indicator = wrapper.find('[data-testid="new-message-indicator"]');
|
|
334
|
+
expect(indicator.exists()).toBe(true);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
it('should hide the unread-count circle when newMessageIndicator and unreadMessages are both set', async () => {
|
|
338
|
+
await wrapper.setProps({
|
|
339
|
+
newMessageIndicator: true,
|
|
340
|
+
unreadMessages: 5,
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
const indicator = wrapper.find('[data-testid="new-message-indicator"]');
|
|
344
|
+
const unreadCount = wrapper.find(
|
|
345
|
+
'.chats-contact__infos__unread-messages',
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
expect(indicator.exists()).toBe(true);
|
|
349
|
+
expect(unreadCount.exists()).toBe(false);
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it('should keep tooltip disabled when newMessageIndicatorTooltip is empty', async () => {
|
|
353
|
+
await wrapper.setProps({
|
|
354
|
+
newMessageIndicator: true,
|
|
355
|
+
newMessageIndicatorTooltip: '',
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
const tooltip = wrapper.findComponent({ name: 'UnnnicTooltip' });
|
|
359
|
+
|
|
360
|
+
expect(tooltip.exists()).toBe(true);
|
|
361
|
+
expect(tooltip.props('enabled')).toBe(false);
|
|
362
|
+
expect(tooltip.props('text')).toBe('');
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it('should enable tooltip with the provided text when newMessageIndicatorTooltip is set', async () => {
|
|
366
|
+
const tooltipText = 'New chats received';
|
|
367
|
+
|
|
368
|
+
await wrapper.setProps({
|
|
369
|
+
newMessageIndicator: true,
|
|
370
|
+
newMessageIndicatorTooltip: tooltipText,
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
const tooltip = wrapper.findComponent({ name: 'UnnnicTooltip' });
|
|
374
|
+
|
|
375
|
+
expect(tooltip.exists()).toBe(true);
|
|
376
|
+
expect(tooltip.props('enabled')).toBe(true);
|
|
377
|
+
expect(tooltip.props('text')).toBe(tooltipText);
|
|
378
|
+
});
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
describe('Pending response', () => {
|
|
382
|
+
it('should not render the pending response icon by default', () => {
|
|
383
|
+
const icon = wrapper.find('[data-testid="pending-response-icon"]');
|
|
384
|
+
expect(icon.exists()).toBe(false);
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
it('should render the pending response icon when pendingResponse is true', async () => {
|
|
388
|
+
await wrapper.setProps({ pendingResponse: true });
|
|
389
|
+
|
|
390
|
+
const icon = wrapper.findComponent(
|
|
391
|
+
'[data-testid="pending-response-icon"]',
|
|
392
|
+
);
|
|
393
|
+
expect(icon.exists()).toBe(true);
|
|
394
|
+
expect(icon.props('icon')).toBe('reply');
|
|
395
|
+
expect(icon.props('scheme')).toBe('fg-info');
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
it('should keep tooltip disabled when pendingResponseTooltip is empty', async () => {
|
|
399
|
+
await wrapper.setProps({
|
|
400
|
+
pendingResponse: true,
|
|
401
|
+
pendingResponseTooltip: '',
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
const tooltip = wrapper.findComponent({ name: 'UnnnicTooltip' });
|
|
405
|
+
expect(tooltip.exists()).toBe(true);
|
|
406
|
+
expect(tooltip.props('enabled')).toBe(false);
|
|
407
|
+
expect(tooltip.props('text')).toBe('');
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
it('should enable tooltip with the provided text when pendingResponseTooltip is set', async () => {
|
|
411
|
+
const tooltipText = 'You did not respond to this contact';
|
|
412
|
+
|
|
413
|
+
await wrapper.setProps({
|
|
414
|
+
pendingResponse: true,
|
|
415
|
+
pendingResponseTooltip: tooltipText,
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
const tooltip = wrapper.findComponent({ name: 'UnnnicTooltip' });
|
|
419
|
+
expect(tooltip.exists()).toBe(true);
|
|
420
|
+
expect(tooltip.props('enabled')).toBe(true);
|
|
421
|
+
expect(tooltip.props('text')).toBe(tooltipText);
|
|
422
|
+
});
|
|
423
|
+
});
|
|
261
424
|
});
|
|
@@ -16,6 +16,7 @@ exports[`UnnnicChatsContact > should match snapshot 1`] = `
|
|
|
16
16
|
<!--v-if-->
|
|
17
17
|
<section data-v-8ce6efd5="" class="chats-contact__infos__unread-messages-container__pin-container">
|
|
18
18
|
<!--v-if-->
|
|
19
|
+
<!--v-if-->
|
|
19
20
|
</section>
|
|
20
21
|
</section>
|
|
21
22
|
<transition-group-stub data-v-17112bed="" data-v-8ce6efd5="" name="grow" tag="div" appear="false" persisted="false" css="true" class="ripples"></transition-group-stub>
|
|
@@ -135,6 +135,36 @@ export const Discussion = {
|
|
|
135
135
|
},
|
|
136
136
|
};
|
|
137
137
|
|
|
138
|
+
export const PendingResponse = {
|
|
139
|
+
args: {
|
|
140
|
+
...defaultArgs,
|
|
141
|
+
title: 'Aline',
|
|
142
|
+
projectName: 'Sector name',
|
|
143
|
+
lastMessage: {
|
|
144
|
+
text: 'I’d like to know if powdered dishwasher detergent is available',
|
|
145
|
+
},
|
|
146
|
+
lastInteractionTime: new Date().toISOString(),
|
|
147
|
+
pendingResponse: true,
|
|
148
|
+
pendingResponseTooltip: 'You did not respond to this contact',
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
export const PendingResponsePinned = {
|
|
153
|
+
args: {
|
|
154
|
+
...defaultArgs,
|
|
155
|
+
title: 'Milena',
|
|
156
|
+
projectName: 'Sector name',
|
|
157
|
+
lastMessage: {
|
|
158
|
+
text: 'Okay, I’ll wait for a response regarding the order status',
|
|
159
|
+
},
|
|
160
|
+
lastInteractionTime: new Date().toISOString(),
|
|
161
|
+
pendingResponse: true,
|
|
162
|
+
pendingResponseTooltip: 'You did not respond to this contact',
|
|
163
|
+
pinned: true,
|
|
164
|
+
activePin: true,
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
|
|
138
168
|
export const ContactList = {
|
|
139
169
|
args: {
|
|
140
170
|
...defaultArgs,
|
|
@@ -190,3 +220,56 @@ export const MediaLastMessage = {
|
|
|
190
220
|
`,
|
|
191
221
|
}),
|
|
192
222
|
};
|
|
223
|
+
|
|
224
|
+
const lastMessageMediaFromUser = Object.fromEntries(
|
|
225
|
+
Object.entries(lastMessageMedia).map(([mediaType, message]) => [
|
|
226
|
+
mediaType,
|
|
227
|
+
{ ...message, isFromUser: true },
|
|
228
|
+
]),
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
export const MediaLastMessageFromUser = {
|
|
232
|
+
args: {
|
|
233
|
+
...defaultArgs,
|
|
234
|
+
},
|
|
235
|
+
render: (args) => ({
|
|
236
|
+
setup() {
|
|
237
|
+
return { args, lastMessageMediaFromUser };
|
|
238
|
+
},
|
|
239
|
+
components: { unnnicChatsContact },
|
|
240
|
+
template: `
|
|
241
|
+
<div style="display: grid;">
|
|
242
|
+
<unnnic-chats-contact v-for="mediaType in ['geo', 'audio', 'image', 'video', 'document']" v-bind="args" :lastMessage="lastMessageMediaFromUser[mediaType]"/>
|
|
243
|
+
</div>
|
|
244
|
+
`,
|
|
245
|
+
}),
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
export const NewMessageIndicator = {
|
|
249
|
+
args: {
|
|
250
|
+
...defaultArgs,
|
|
251
|
+
title: 'Aline',
|
|
252
|
+
projectName: 'Sector name',
|
|
253
|
+
lastMessage: {
|
|
254
|
+
text: 'I’d like to know if powdered dishwasher detergent is available',
|
|
255
|
+
},
|
|
256
|
+
lastInteractionTime: new Date().toISOString(),
|
|
257
|
+
unreadMessages: 1,
|
|
258
|
+
newMessageIndicator: true,
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
export const NewMessageIndicatorWithTooltip = {
|
|
263
|
+
args: {
|
|
264
|
+
...defaultArgs,
|
|
265
|
+
title: 'Aline',
|
|
266
|
+
projectName: 'Sector name',
|
|
267
|
+
lastMessage: {
|
|
268
|
+
text: 'I’d like to know if powdered dishwasher detergent is available',
|
|
269
|
+
},
|
|
270
|
+
lastInteractionTime: new Date().toISOString(),
|
|
271
|
+
unreadMessages: 1,
|
|
272
|
+
newMessageIndicator: true,
|
|
273
|
+
newMessageIndicatorTooltip: 'New chats received',
|
|
274
|
+
},
|
|
275
|
+
};
|