@linktr.ee/messaging-react 4.1.1-rc-1786774403 → 4.1.1-rc-1786776179
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 +2 -2
- package/src/components/ActionButton/ActionButton.stories.tsx +0 -1
- package/src/components/AttachmentCard/AttachmentCard.stories.tsx +1 -1
- package/src/components/Avatar/Avatar.stories.tsx +44 -5
- package/src/components/ChannelList/CustomChannelPreview.stories.tsx +332 -1
- package/src/components/CloseButton/CloseButton.stories.tsx +1 -1
- package/src/components/CustomDateSeparator/CustomDateSeparator.stories.tsx +1 -1
- package/src/components/CustomMessage/MessageTag.stories.tsx +4 -6
- package/src/components/FaqList/FaqList.stories.tsx +204 -0
- package/src/components/FaqList/FaqListItem.stories.tsx +0 -1
- package/src/components/IconButton/IconButton.stories.tsx +0 -1
- package/src/components/Loading/Loading.stories.tsx +0 -1
- package/src/components/MessagingShell/ErrorState.stories.tsx +0 -1
- package/src/components/MessagingShell/LoadingState.stories.tsx +0 -1
- package/src/components/SearchInput/SearchInput.stories.tsx +0 -1
- package/src/testing/createMockMessagingClient.stories.tsx +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linktr.ee/messaging-react",
|
|
3
|
-
"version": "4.1.1-rc-
|
|
3
|
+
"version": "4.1.1-rc-1786776179",
|
|
4
4
|
"description": "React messaging components built on messaging-core for web applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"chromatic:local": "yarn storybook:build && chromatic"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@linktr.ee/messaging-core": "2.4.0-rc-
|
|
53
|
+
"@linktr.ee/messaging-core": "2.4.0-rc-1786776179",
|
|
54
54
|
"@linktr.ee/messaging-taxonomy": "^0.10.0",
|
|
55
55
|
"@phosphor-icons/react": "^2.1.10"
|
|
56
56
|
},
|
|
@@ -8,7 +8,7 @@ type ComponentProps = React.ComponentProps<typeof AttachmentCard>
|
|
|
8
8
|
const meta: Meta<ComponentProps> = {
|
|
9
9
|
title: 'AttachmentCard',
|
|
10
10
|
component: AttachmentCard,
|
|
11
|
-
parameters: { layout: 'centered'
|
|
11
|
+
parameters: { layout: 'centered' },
|
|
12
12
|
argTypes: {
|
|
13
13
|
variant: { control: { type: 'inline-radio' }, options: ['light', 'dark'] },
|
|
14
14
|
},
|
|
@@ -22,17 +22,24 @@ const Template: StoryFn<ComponentProps> = (args) => {
|
|
|
22
22
|
)
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
// Loose single stories (Default/WithoutImage/Starred) are each a cell of the
|
|
26
|
+
// States grid; the per-size stories (SmallSize…ExtraLargeSize) are cells of the
|
|
27
|
+
// VariousSizes grid; AIActive is the size=40 cell of AIActiveVariousSizes.
|
|
28
|
+
// Keep them in Storybook for browsing but skip in Chromatic.
|
|
29
|
+
const skipInChromatic = { chromatic: { disableSnapshot: true } }
|
|
30
|
+
|
|
31
|
+
// Deterministic local avatar for the States grid so a pravatar network flake
|
|
32
|
+
// can't diff the snapshot. Individual stories keep their remote images (they're
|
|
33
|
+
// skipped in Chromatic anyway).
|
|
34
|
+
const LOCAL_AVATAR = '/image-thumbnail.jpg'
|
|
35
|
+
|
|
25
36
|
export const Default: StoryFn<ComponentProps> = Template.bind({})
|
|
26
37
|
Default.args = {
|
|
27
38
|
id: 'user-1',
|
|
28
39
|
name: 'Alice Johnson',
|
|
29
40
|
image: 'https://i.pravatar.cc/150?img=1',
|
|
30
41
|
}
|
|
31
|
-
|
|
32
|
-
// Per-size stories (SmallSize…ExtraLargeSize) are each a single cell of the
|
|
33
|
-
// VariousSizes grid; AIActive is the size=40 cell of AIActiveVariousSizes.
|
|
34
|
-
// Keep them in Storybook for browsing but skip in Chromatic.
|
|
35
|
-
const skipInChromatic = { chromatic: { disableSnapshot: true } }
|
|
42
|
+
Default.parameters = skipInChromatic
|
|
36
43
|
|
|
37
44
|
export const AIActive: StoryFn<ComponentProps> = Template.bind({})
|
|
38
45
|
AIActive.args = {
|
|
@@ -48,6 +55,7 @@ WithoutImage.args = {
|
|
|
48
55
|
id: 'user-2',
|
|
49
56
|
name: 'Bob Smith',
|
|
50
57
|
}
|
|
58
|
+
WithoutImage.parameters = skipInChromatic
|
|
51
59
|
|
|
52
60
|
export const Starred: StoryFn<ComponentProps> = Template.bind({})
|
|
53
61
|
Starred.args = {
|
|
@@ -55,6 +63,37 @@ Starred.args = {
|
|
|
55
63
|
name: 'Kaiya Korsgaard',
|
|
56
64
|
starred: true,
|
|
57
65
|
}
|
|
66
|
+
Starred.parameters = skipInChromatic
|
|
67
|
+
|
|
68
|
+
export const States: StoryFn = () => {
|
|
69
|
+
const cells: { label: string; props: ComponentProps }[] = [
|
|
70
|
+
{
|
|
71
|
+
label: 'Default (image)',
|
|
72
|
+
props: { id: 'user-1', name: 'Alice Johnson', image: LOCAL_AVATAR },
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
label: 'Without image',
|
|
76
|
+
props: { id: 'user-2', name: 'Bob Smith' },
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
label: 'Starred',
|
|
80
|
+
props: { id: 'user-9', name: 'Kaiya Korsgaard', starred: true },
|
|
81
|
+
},
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<div className="p-12">
|
|
86
|
+
<div className="flex gap-8 items-center">
|
|
87
|
+
{cells.map(({ label, props }) => (
|
|
88
|
+
<div key={label} className="flex flex-col items-center gap-2">
|
|
89
|
+
<Avatar {...props} />
|
|
90
|
+
<span className="text-xs text-stone">{label}</span>
|
|
91
|
+
</div>
|
|
92
|
+
))}
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
)
|
|
96
|
+
}
|
|
58
97
|
|
|
59
98
|
export const SmallSize: StoryFn<ComponentProps> = Template.bind({})
|
|
60
99
|
SmallSize.args = {
|
|
@@ -134,6 +134,17 @@ const Template: StoryFn<ComponentProps> = (args) => {
|
|
|
134
134
|
)
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
// Per-variant single-row stories stay in Storybook for browsing/design review
|
|
138
|
+
// but are individually covered by rows in the PreviewStates / MessageTypeStates
|
|
139
|
+
// / UrlAndAttachmentStates grids — skip in Chromatic so those grids are the
|
|
140
|
+
// single snapshot source of truth.
|
|
141
|
+
const skipInChromatic = { chromatic: { disableSnapshot: true } }
|
|
142
|
+
|
|
143
|
+
// Deterministic local avatar for the grids so a pravatar network flake can't
|
|
144
|
+
// diff a whole grid snapshot. Individual stories keep their remote images
|
|
145
|
+
// (they're skipped in Chromatic anyway).
|
|
146
|
+
const LOCAL_AVATAR = '/image-thumbnail.jpg'
|
|
147
|
+
|
|
137
148
|
export const Default: StoryFn<ComponentProps> = Template.bind({})
|
|
138
149
|
Default.args = {
|
|
139
150
|
channel: createMockChannel({
|
|
@@ -145,6 +156,7 @@ Default.args = {
|
|
|
145
156
|
lastMessageTime: now(),
|
|
146
157
|
}),
|
|
147
158
|
}
|
|
159
|
+
Default.parameters = skipInChromatic
|
|
148
160
|
|
|
149
161
|
export const Selected: StoryFn<ComponentProps> = Template.bind({})
|
|
150
162
|
Selected.args = {
|
|
@@ -163,6 +175,7 @@ Selected.args = {
|
|
|
163
175
|
participantImage: 'https://i.pravatar.cc/150?img=3',
|
|
164
176
|
}),
|
|
165
177
|
}
|
|
178
|
+
Selected.parameters = skipInChromatic
|
|
166
179
|
|
|
167
180
|
export const WithUnreadMessages: StoryFn<ComponentProps> = Template.bind({})
|
|
168
181
|
WithUnreadMessages.args = {
|
|
@@ -176,6 +189,7 @@ WithUnreadMessages.args = {
|
|
|
176
189
|
unreadCount: 3,
|
|
177
190
|
}),
|
|
178
191
|
}
|
|
192
|
+
WithUnreadMessages.parameters = skipInChromatic
|
|
179
193
|
|
|
180
194
|
export const ManyUnreadMessages: StoryFn<ComponentProps> = Template.bind({})
|
|
181
195
|
ManyUnreadMessages.args = {
|
|
@@ -189,6 +203,7 @@ ManyUnreadMessages.args = {
|
|
|
189
203
|
unreadCount: 127,
|
|
190
204
|
}),
|
|
191
205
|
}
|
|
206
|
+
ManyUnreadMessages.parameters = skipInChromatic
|
|
192
207
|
|
|
193
208
|
export const NoAvatar: StoryFn<ComponentProps> = Template.bind({})
|
|
194
209
|
NoAvatar.args = {
|
|
@@ -200,6 +215,7 @@ NoAvatar.args = {
|
|
|
200
215
|
lastMessageTime: minutesAgo(30),
|
|
201
216
|
}),
|
|
202
217
|
}
|
|
218
|
+
NoAvatar.parameters = skipInChromatic
|
|
203
219
|
|
|
204
220
|
export const NoMessages: StoryFn<ComponentProps> = Template.bind({})
|
|
205
221
|
NoMessages.args = {
|
|
@@ -211,6 +227,7 @@ NoMessages.args = {
|
|
|
211
227
|
lastMessageText: '',
|
|
212
228
|
}),
|
|
213
229
|
}
|
|
230
|
+
NoMessages.parameters = skipInChromatic
|
|
214
231
|
|
|
215
232
|
export const LongMessage: StoryFn<ComponentProps> = Template.bind({})
|
|
216
233
|
LongMessage.args = {
|
|
@@ -224,6 +241,7 @@ LongMessage.args = {
|
|
|
224
241
|
lastMessageTime: hoursAgo(2),
|
|
225
242
|
}),
|
|
226
243
|
}
|
|
244
|
+
LongMessage.parameters = skipInChromatic
|
|
227
245
|
|
|
228
246
|
export const LongName: StoryFn<ComponentProps> = Template.bind({})
|
|
229
247
|
LongName.args = {
|
|
@@ -235,6 +253,7 @@ LongName.args = {
|
|
|
235
253
|
lastMessageTime: minutesAgo(45),
|
|
236
254
|
}),
|
|
237
255
|
}
|
|
256
|
+
LongName.parameters = skipInChromatic
|
|
238
257
|
|
|
239
258
|
export const ChatbotMessage: StoryFn<ComponentProps> = Template.bind({})
|
|
240
259
|
ChatbotMessage.args = {
|
|
@@ -248,6 +267,7 @@ ChatbotMessage.args = {
|
|
|
248
267
|
lastMessageMetadata: { custom_type: 'MESSAGE_CHATBOT' },
|
|
249
268
|
}),
|
|
250
269
|
}
|
|
270
|
+
ChatbotMessage.parameters = skipInChromatic
|
|
251
271
|
|
|
252
272
|
export const TipMessage: StoryFn<ComponentProps> = Template.bind({})
|
|
253
273
|
TipMessage.args = {
|
|
@@ -261,6 +281,7 @@ TipMessage.args = {
|
|
|
261
281
|
lastMessageMetadata: { custom_type: 'MESSAGE_TIP', amount_text: '$5.00' },
|
|
262
282
|
}),
|
|
263
283
|
}
|
|
284
|
+
TipMessage.parameters = skipInChromatic
|
|
264
285
|
|
|
265
286
|
export const PaidMessage: StoryFn<ComponentProps> = Template.bind({})
|
|
266
287
|
PaidMessage.args = {
|
|
@@ -274,6 +295,7 @@ PaidMessage.args = {
|
|
|
274
295
|
lastMessageMetadata: { custom_type: 'MESSAGE_PAID', amount_text: '$10.00' },
|
|
275
296
|
}),
|
|
276
297
|
}
|
|
298
|
+
PaidMessage.parameters = skipInChromatic
|
|
277
299
|
|
|
278
300
|
export const PaidAttachment: StoryFn<ComponentProps> = Template.bind({})
|
|
279
301
|
PaidAttachment.args = {
|
|
@@ -290,6 +312,7 @@ PaidAttachment.args = {
|
|
|
290
312
|
},
|
|
291
313
|
}),
|
|
292
314
|
}
|
|
315
|
+
PaidAttachment.parameters = skipInChromatic
|
|
293
316
|
|
|
294
317
|
const renderMessagePreviewWithMarkers = (
|
|
295
318
|
message: LocalMessage | undefined,
|
|
@@ -396,6 +419,7 @@ SelectedWithUnread.args = {
|
|
|
396
419
|
participantId: 'participant-9',
|
|
397
420
|
}),
|
|
398
421
|
}
|
|
422
|
+
SelectedWithUnread.parameters = skipInChromatic
|
|
399
423
|
|
|
400
424
|
export const MultipleChannels: StoryFn = () => {
|
|
401
425
|
const [selectedChannelId, _setSelectedChannelId] = React.useState<
|
|
@@ -468,6 +492,7 @@ WithUrl.args = {
|
|
|
468
492
|
lastMessageTime: minutesAgo(8),
|
|
469
493
|
}),
|
|
470
494
|
}
|
|
495
|
+
WithUrl.parameters = skipInChromatic
|
|
471
496
|
|
|
472
497
|
export const WithVeryLongUrl: StoryFn<ComponentProps> = Template.bind({})
|
|
473
498
|
WithVeryLongUrl.args = {
|
|
@@ -481,6 +506,7 @@ WithVeryLongUrl.args = {
|
|
|
481
506
|
lastMessageTime: minutesAgo(20),
|
|
482
507
|
}),
|
|
483
508
|
}
|
|
509
|
+
WithVeryLongUrl.parameters = skipInChromatic
|
|
484
510
|
|
|
485
511
|
// Time-based stories
|
|
486
512
|
//
|
|
@@ -488,7 +514,6 @@ WithVeryLongUrl.args = {
|
|
|
488
514
|
// output matrix. The `TimeVariations` story further down already snapshots
|
|
489
515
|
// all of them together in a labeled table — kept here for Storybook
|
|
490
516
|
// browsing but skipped in Chromatic to avoid 9 redundant snapshots.
|
|
491
|
-
const skipInChromatic = { chromatic: { disableSnapshot: true } }
|
|
492
517
|
|
|
493
518
|
export const JustNow: StoryFn<ComponentProps> = Template.bind({})
|
|
494
519
|
JustNow.args = {
|
|
@@ -722,6 +747,7 @@ WithAttachmentAssetUrl.args = {
|
|
|
722
747
|
],
|
|
723
748
|
}),
|
|
724
749
|
}
|
|
750
|
+
WithAttachmentAssetUrl.parameters = skipInChromatic
|
|
725
751
|
|
|
726
752
|
export const WithAttachmentImageUrl: StoryFn<ComponentProps> = Template.bind({})
|
|
727
753
|
WithAttachmentImageUrl.args = {
|
|
@@ -762,6 +788,7 @@ WithAttachmentOgScrapeUrl.args = {
|
|
|
762
788
|
],
|
|
763
789
|
}),
|
|
764
790
|
}
|
|
791
|
+
WithAttachmentOgScrapeUrl.parameters = skipInChromatic
|
|
765
792
|
|
|
766
793
|
export const WithAttachmentThumbUrl: StoryFn<ComponentProps> = Template.bind({})
|
|
767
794
|
WithAttachmentThumbUrl.args = {
|
|
@@ -799,3 +826,307 @@ WithLongAttachmentUrl.args = {
|
|
|
799
826
|
}),
|
|
800
827
|
}
|
|
801
828
|
WithLongAttachmentUrl.parameters = skipInChromatic
|
|
829
|
+
|
|
830
|
+
interface GridRow {
|
|
831
|
+
label: string
|
|
832
|
+
channel: Channel
|
|
833
|
+
selectedChannel?: Channel
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
const StatesGrid: StoryFn<{ rows: GridRow[] }> = ({ rows }) => (
|
|
837
|
+
<div className="flex flex-col gap-4 p-6">
|
|
838
|
+
{rows.map(({ label, channel, selectedChannel }) => (
|
|
839
|
+
<div key={label} className="flex flex-col gap-1">
|
|
840
|
+
<span className="text-xs text-stone">{label}</span>
|
|
841
|
+
<div className="w-[360px] bg-chalk border border-sand rounded-lg overflow-hidden">
|
|
842
|
+
<CustomChannelPreview
|
|
843
|
+
channel={channel}
|
|
844
|
+
selectedChannel={selectedChannel}
|
|
845
|
+
onChannelSelect={(channel) => {
|
|
846
|
+
console.log('Channel selected:', channel.id)
|
|
847
|
+
}}
|
|
848
|
+
/>
|
|
849
|
+
</div>
|
|
850
|
+
</div>
|
|
851
|
+
))}
|
|
852
|
+
</div>
|
|
853
|
+
)
|
|
854
|
+
|
|
855
|
+
export const PreviewStates: StoryFn = () => {
|
|
856
|
+
const selected = createMockChannel({
|
|
857
|
+
id: 'channel-2',
|
|
858
|
+
participantName: 'Bob Smith',
|
|
859
|
+
participantId: 'participant-2',
|
|
860
|
+
participantImage: LOCAL_AVATAR,
|
|
861
|
+
lastMessageText: 'That sounds great!',
|
|
862
|
+
lastMessageTime: minutesAgo(5),
|
|
863
|
+
})
|
|
864
|
+
const selectedUnread = createMockChannel({
|
|
865
|
+
id: 'channel-9',
|
|
866
|
+
participantName: 'Helen Park',
|
|
867
|
+
participantId: 'participant-9',
|
|
868
|
+
participantImage: LOCAL_AVATAR,
|
|
869
|
+
lastMessageText: 'Important update!',
|
|
870
|
+
lastMessageTime: minutesAgo(10),
|
|
871
|
+
unreadCount: 5,
|
|
872
|
+
})
|
|
873
|
+
|
|
874
|
+
const rows: GridRow[] = [
|
|
875
|
+
{
|
|
876
|
+
label: 'Default',
|
|
877
|
+
channel: createMockChannel({
|
|
878
|
+
id: 'channel-1',
|
|
879
|
+
participantName: 'Alice Johnson',
|
|
880
|
+
participantId: 'participant-1',
|
|
881
|
+
participantImage: LOCAL_AVATAR,
|
|
882
|
+
lastMessageText: 'Hey! How are you doing?',
|
|
883
|
+
lastMessageTime: now(),
|
|
884
|
+
}),
|
|
885
|
+
},
|
|
886
|
+
{
|
|
887
|
+
label: 'Selected',
|
|
888
|
+
channel: selected,
|
|
889
|
+
selectedChannel: selected,
|
|
890
|
+
},
|
|
891
|
+
{
|
|
892
|
+
label: 'With unread messages',
|
|
893
|
+
channel: createMockChannel({
|
|
894
|
+
id: 'channel-3',
|
|
895
|
+
participantName: 'Carol Williams',
|
|
896
|
+
participantId: 'participant-3',
|
|
897
|
+
participantImage: LOCAL_AVATAR,
|
|
898
|
+
lastMessageText: 'Did you see my last message?',
|
|
899
|
+
lastMessageTime: minutesAgo(15),
|
|
900
|
+
unreadCount: 3,
|
|
901
|
+
}),
|
|
902
|
+
},
|
|
903
|
+
{
|
|
904
|
+
label: 'Many unread messages',
|
|
905
|
+
channel: createMockChannel({
|
|
906
|
+
id: 'channel-4',
|
|
907
|
+
participantName: 'David Brown',
|
|
908
|
+
participantId: 'participant-4',
|
|
909
|
+
participantImage: LOCAL_AVATAR,
|
|
910
|
+
lastMessageText: 'Please check this out!',
|
|
911
|
+
lastMessageTime: hoursAgo(1),
|
|
912
|
+
unreadCount: 127,
|
|
913
|
+
}),
|
|
914
|
+
},
|
|
915
|
+
{
|
|
916
|
+
label: 'Selected with unread',
|
|
917
|
+
channel: selectedUnread,
|
|
918
|
+
selectedChannel: selectedUnread,
|
|
919
|
+
},
|
|
920
|
+
{
|
|
921
|
+
label: 'No avatar',
|
|
922
|
+
channel: createMockChannel({
|
|
923
|
+
id: 'channel-5',
|
|
924
|
+
participantName: 'Emma Davis',
|
|
925
|
+
participantId: 'participant-5',
|
|
926
|
+
lastMessageText: 'Thanks for your help!',
|
|
927
|
+
lastMessageTime: minutesAgo(30),
|
|
928
|
+
}),
|
|
929
|
+
},
|
|
930
|
+
{
|
|
931
|
+
label: 'No messages',
|
|
932
|
+
channel: createMockChannel({
|
|
933
|
+
id: 'channel-6',
|
|
934
|
+
participantName: 'Frank Miller',
|
|
935
|
+
participantId: 'participant-6',
|
|
936
|
+
participantImage: LOCAL_AVATAR,
|
|
937
|
+
lastMessageText: '',
|
|
938
|
+
}),
|
|
939
|
+
},
|
|
940
|
+
{
|
|
941
|
+
label: 'Long message',
|
|
942
|
+
channel: createMockChannel({
|
|
943
|
+
id: 'channel-7',
|
|
944
|
+
participantName: 'Grace Lee',
|
|
945
|
+
participantId: 'participant-7',
|
|
946
|
+
participantImage: LOCAL_AVATAR,
|
|
947
|
+
lastMessageText:
|
|
948
|
+
'This is a very long message that should be truncated because it contains way too much text to display in the preview. We want to make sure the component handles this gracefully.',
|
|
949
|
+
lastMessageTime: hoursAgo(2),
|
|
950
|
+
}),
|
|
951
|
+
},
|
|
952
|
+
{
|
|
953
|
+
label: 'Long name',
|
|
954
|
+
channel: createMockChannel({
|
|
955
|
+
id: 'channel-8',
|
|
956
|
+
participantName: 'Alexander Christopher Wellington-Montgomery III',
|
|
957
|
+
participantId: 'participant-8',
|
|
958
|
+
lastMessageText: 'Nice to meet you!',
|
|
959
|
+
lastMessageTime: minutesAgo(45),
|
|
960
|
+
}),
|
|
961
|
+
},
|
|
962
|
+
]
|
|
963
|
+
|
|
964
|
+
return <StatesGrid rows={rows} />
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
export const MessageTypeStates: StoryFn = () => {
|
|
968
|
+
const rows: GridRow[] = [
|
|
969
|
+
{
|
|
970
|
+
label: 'Chatbot message',
|
|
971
|
+
channel: createMockChannel({
|
|
972
|
+
id: 'channel-chatbot',
|
|
973
|
+
participantName: 'Emily Watson',
|
|
974
|
+
participantId: 'participant-chatbot',
|
|
975
|
+
participantImage: LOCAL_AVATAR,
|
|
976
|
+
lastMessageText: 'Thanks for reaching out! How can I help you today?',
|
|
977
|
+
lastMessageTime: minutesAgo(2),
|
|
978
|
+
lastMessageMetadata: { custom_type: 'MESSAGE_CHATBOT' },
|
|
979
|
+
}),
|
|
980
|
+
},
|
|
981
|
+
{
|
|
982
|
+
label: 'Tip message',
|
|
983
|
+
channel: createMockChannel({
|
|
984
|
+
id: 'channel-tip',
|
|
985
|
+
participantName: 'Alice Johnson',
|
|
986
|
+
participantId: 'participant-tip',
|
|
987
|
+
participantImage: LOCAL_AVATAR,
|
|
988
|
+
lastMessageText: 'Take my money!',
|
|
989
|
+
lastMessageTime: minutesAgo(3),
|
|
990
|
+
lastMessageMetadata: {
|
|
991
|
+
custom_type: 'MESSAGE_TIP',
|
|
992
|
+
amount_text: '$5.00',
|
|
993
|
+
},
|
|
994
|
+
}),
|
|
995
|
+
},
|
|
996
|
+
{
|
|
997
|
+
label: 'Paid message',
|
|
998
|
+
channel: createMockChannel({
|
|
999
|
+
id: 'channel-paid',
|
|
1000
|
+
participantName: 'Bob Smith',
|
|
1001
|
+
participantId: 'participant-paid',
|
|
1002
|
+
participantImage: LOCAL_AVATAR,
|
|
1003
|
+
lastMessageText: 'I paid for this message!',
|
|
1004
|
+
lastMessageTime: minutesAgo(10),
|
|
1005
|
+
lastMessageMetadata: {
|
|
1006
|
+
custom_type: 'MESSAGE_PAID',
|
|
1007
|
+
amount_text: '$10.00',
|
|
1008
|
+
},
|
|
1009
|
+
}),
|
|
1010
|
+
},
|
|
1011
|
+
{
|
|
1012
|
+
label: 'Paid attachment',
|
|
1013
|
+
channel: createMockChannel({
|
|
1014
|
+
id: 'channel-paid-attachment',
|
|
1015
|
+
participantName: 'Carol Williams',
|
|
1016
|
+
participantId: 'participant-paid-attachment',
|
|
1017
|
+
participantImage: LOCAL_AVATAR,
|
|
1018
|
+
lastMessageText: 'Check out this exclusive photo!',
|
|
1019
|
+
lastMessageTime: minutesAgo(20),
|
|
1020
|
+
lastMessageMetadata: {
|
|
1021
|
+
custom_type: 'MESSAGE_ATTACHMENT',
|
|
1022
|
+
attachment_title: 'exclusive-photo.jpg',
|
|
1023
|
+
},
|
|
1024
|
+
}),
|
|
1025
|
+
},
|
|
1026
|
+
]
|
|
1027
|
+
|
|
1028
|
+
return <StatesGrid rows={rows} />
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
export const UrlAndAttachmentStates: StoryFn = () => {
|
|
1032
|
+
const rows: GridRow[] = [
|
|
1033
|
+
{
|
|
1034
|
+
label: 'With URL',
|
|
1035
|
+
channel: createMockChannel({
|
|
1036
|
+
id: 'channel-url',
|
|
1037
|
+
participantName: 'Ivan Rodriguez',
|
|
1038
|
+
participantId: 'participant-url',
|
|
1039
|
+
participantImage: LOCAL_AVATAR,
|
|
1040
|
+
lastMessageText: 'https://example.com/page',
|
|
1041
|
+
lastMessageTime: minutesAgo(8),
|
|
1042
|
+
}),
|
|
1043
|
+
},
|
|
1044
|
+
{
|
|
1045
|
+
label: 'With very long URL',
|
|
1046
|
+
channel: createMockChannel({
|
|
1047
|
+
id: 'channel-long-url',
|
|
1048
|
+
participantName: 'Julia Martinez',
|
|
1049
|
+
participantId: 'participant-long-url',
|
|
1050
|
+
participantImage: LOCAL_AVATAR,
|
|
1051
|
+
lastMessageText:
|
|
1052
|
+
'https://example.com/very/long/path/with/many/segments/and/query/parameters?param1=value1¶m2=value2¶m3=value3¶m4=value4¶m5=very-long-value-that-makes-the-url-extremely-long',
|
|
1053
|
+
lastMessageTime: minutesAgo(20),
|
|
1054
|
+
}),
|
|
1055
|
+
},
|
|
1056
|
+
{
|
|
1057
|
+
label: 'Attachment (asset_url)',
|
|
1058
|
+
channel: createMockChannel({
|
|
1059
|
+
id: 'channel-attachment-asset',
|
|
1060
|
+
participantName: 'Tom Wilson',
|
|
1061
|
+
participantId: 'participant-attachment-asset',
|
|
1062
|
+
participantImage: LOCAL_AVATAR,
|
|
1063
|
+
lastMessageText: '',
|
|
1064
|
+
lastMessageTime: minutesAgo(10),
|
|
1065
|
+
lastMessageAttachments: [
|
|
1066
|
+
{ asset_url: 'https://example.com/document.pdf' },
|
|
1067
|
+
],
|
|
1068
|
+
}),
|
|
1069
|
+
},
|
|
1070
|
+
{
|
|
1071
|
+
label: 'Attachment (image_url)',
|
|
1072
|
+
channel: createMockChannel({
|
|
1073
|
+
id: 'channel-attachment-image',
|
|
1074
|
+
participantName: 'Uma Patel',
|
|
1075
|
+
participantId: 'participant-attachment-image',
|
|
1076
|
+
participantImage: LOCAL_AVATAR,
|
|
1077
|
+
lastMessageText: '',
|
|
1078
|
+
lastMessageTime: minutesAgo(15),
|
|
1079
|
+
lastMessageAttachments: [
|
|
1080
|
+
{ image_url: 'https://example.com/image.jpg' },
|
|
1081
|
+
],
|
|
1082
|
+
}),
|
|
1083
|
+
},
|
|
1084
|
+
{
|
|
1085
|
+
label: 'Attachment (og_scrape_url)',
|
|
1086
|
+
channel: createMockChannel({
|
|
1087
|
+
id: 'channel-attachment-og',
|
|
1088
|
+
participantName: 'Victor Zhang',
|
|
1089
|
+
participantId: 'participant-attachment-og',
|
|
1090
|
+
participantImage: LOCAL_AVATAR,
|
|
1091
|
+
lastMessageText: '',
|
|
1092
|
+
lastMessageTime: minutesAgo(7),
|
|
1093
|
+
lastMessageAttachments: [
|
|
1094
|
+
{ og_scrape_url: 'https://example.com/article' },
|
|
1095
|
+
],
|
|
1096
|
+
}),
|
|
1097
|
+
},
|
|
1098
|
+
{
|
|
1099
|
+
label: 'Attachment (thumb_url)',
|
|
1100
|
+
channel: createMockChannel({
|
|
1101
|
+
id: 'channel-attachment-thumb',
|
|
1102
|
+
participantName: 'Wendy Kim',
|
|
1103
|
+
participantId: 'participant-attachment-thumb',
|
|
1104
|
+
participantImage: LOCAL_AVATAR,
|
|
1105
|
+
lastMessageText: '',
|
|
1106
|
+
lastMessageTime: minutesAgo(12),
|
|
1107
|
+
lastMessageAttachments: [
|
|
1108
|
+
{ thumb_url: 'https://example.com/thumb.jpg' },
|
|
1109
|
+
],
|
|
1110
|
+
}),
|
|
1111
|
+
},
|
|
1112
|
+
{
|
|
1113
|
+
label: 'Long attachment URL',
|
|
1114
|
+
channel: createMockChannel({
|
|
1115
|
+
id: 'channel-attachment-long',
|
|
1116
|
+
participantName: 'Xavier Cruz',
|
|
1117
|
+
participantId: 'participant-attachment-long',
|
|
1118
|
+
participantImage: LOCAL_AVATAR,
|
|
1119
|
+
lastMessageText: '',
|
|
1120
|
+
lastMessageTime: minutesAgo(20),
|
|
1121
|
+
lastMessageAttachments: [
|
|
1122
|
+
{
|
|
1123
|
+
asset_url:
|
|
1124
|
+
'https://example.com/very/long/path/to/file/with/many/segments/document-with-very-long-name-that-should-be-truncated.pdf',
|
|
1125
|
+
},
|
|
1126
|
+
],
|
|
1127
|
+
}),
|
|
1128
|
+
},
|
|
1129
|
+
]
|
|
1130
|
+
|
|
1131
|
+
return <StatesGrid rows={rows} />
|
|
1132
|
+
}
|
|
@@ -8,7 +8,7 @@ type ComponentProps = React.ComponentProps<typeof CloseButton>
|
|
|
8
8
|
const meta: Meta<ComponentProps> = {
|
|
9
9
|
title: 'CloseButton',
|
|
10
10
|
component: CloseButton,
|
|
11
|
-
parameters: { layout: 'centered'
|
|
11
|
+
parameters: { layout: 'centered' },
|
|
12
12
|
argTypes: {
|
|
13
13
|
onClick: { action: 'clicked' },
|
|
14
14
|
},
|
|
@@ -10,7 +10,7 @@ type ComponentProps = React.ComponentProps<typeof CustomDateSeparator>
|
|
|
10
10
|
const meta: Meta<ComponentProps> = {
|
|
11
11
|
title: 'CustomDateSeparator',
|
|
12
12
|
component: CustomDateSeparator,
|
|
13
|
-
parameters: { layout: 'fullscreen'
|
|
13
|
+
parameters: { layout: 'fullscreen' },
|
|
14
14
|
}
|
|
15
15
|
export default meta
|
|
16
16
|
|
|
@@ -179,9 +179,7 @@ export const AllVariants: StoryFn = () => {
|
|
|
179
179
|
</div>
|
|
180
180
|
)
|
|
181
181
|
}
|
|
182
|
-
// AllVariants is
|
|
183
|
-
// (
|
|
184
|
-
//
|
|
185
|
-
|
|
186
|
-
// problem that masks which variant actually drifted.
|
|
187
|
-
AllVariants.parameters = { chromatic: { disableSnapshot: true } }
|
|
182
|
+
// AllVariants is the single Chromatic snapshot for message tags: the per-variant
|
|
183
|
+
// stories above are skipped (kept for local browsing + controls) and this grid is
|
|
184
|
+
// the visual-regression guard — one snapshot covering every tag layout at once.
|
|
185
|
+
AllVariants.parameters = { chromatic: { disableSnapshot: false } }
|
|
@@ -22,6 +22,15 @@ const Template: StoryFn<ComponentProps> = (args) => {
|
|
|
22
22
|
)
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
// Per-variant stories stay in Storybook for browsing/design review but are
|
|
26
|
+
// individually covered by cells in AllStates — skip in Chromatic.
|
|
27
|
+
const skipInChromatic = { chromatic: { disableSnapshot: true } }
|
|
28
|
+
|
|
29
|
+
// Deterministic local avatar for the grid so a pravatar network flake can't
|
|
30
|
+
// diff the whole AllStates snapshot. Individual stories keep their remote
|
|
31
|
+
// images (they're skipped in Chromatic anyway).
|
|
32
|
+
const LOCAL_AVATAR = '/image-thumbnail.jpg'
|
|
33
|
+
|
|
25
34
|
export const Default: StoryFn<ComponentProps> = Template.bind({})
|
|
26
35
|
Default.args = {
|
|
27
36
|
faqs: [
|
|
@@ -52,6 +61,7 @@ Default.args = {
|
|
|
52
61
|
avatarName: 'Brie Parson',
|
|
53
62
|
avatarImage: 'https://i.pravatar.cc/150?img=47',
|
|
54
63
|
}
|
|
64
|
+
Default.parameters = skipInChromatic
|
|
55
65
|
|
|
56
66
|
export const WithoutHeader: StoryFn<ComponentProps> = Template.bind({})
|
|
57
67
|
WithoutHeader.args = {
|
|
@@ -73,6 +83,7 @@ WithoutHeader.args = {
|
|
|
73
83
|
],
|
|
74
84
|
onFaqClick: (id) => console.log('FAQ clicked:', id),
|
|
75
85
|
}
|
|
86
|
+
WithoutHeader.parameters = skipInChromatic
|
|
76
87
|
|
|
77
88
|
export const WithDisabledFaq: StoryFn<ComponentProps> = Template.bind({})
|
|
78
89
|
WithDisabledFaq.args = {
|
|
@@ -101,6 +112,7 @@ WithDisabledFaq.args = {
|
|
|
101
112
|
],
|
|
102
113
|
onFaqClick: (id) => console.log('FAQ clicked:', id),
|
|
103
114
|
}
|
|
115
|
+
WithDisabledFaq.parameters = skipInChromatic
|
|
104
116
|
|
|
105
117
|
export const WithLoading: StoryFn<ComponentProps> = Template.bind({})
|
|
106
118
|
WithLoading.args = {
|
|
@@ -131,12 +143,14 @@ WithLoading.args = {
|
|
|
131
143
|
loadingFaqId: '2',
|
|
132
144
|
headerText: 'Tap to send a suggested question:',
|
|
133
145
|
}
|
|
146
|
+
WithLoading.parameters = skipInChromatic
|
|
134
147
|
|
|
135
148
|
export const Empty: StoryFn<ComponentProps> = Template.bind({})
|
|
136
149
|
Empty.args = {
|
|
137
150
|
faqs: [],
|
|
138
151
|
onFaqClick: (id) => console.log('FAQ clicked:', id),
|
|
139
152
|
}
|
|
153
|
+
Empty.parameters = skipInChromatic
|
|
140
154
|
|
|
141
155
|
export const WithAvatarOnly: StoryFn<ComponentProps> = Template.bind({})
|
|
142
156
|
WithAvatarOnly.args = {
|
|
@@ -160,6 +174,7 @@ WithAvatarOnly.args = {
|
|
|
160
174
|
headerText: 'Frequently Asked Questions',
|
|
161
175
|
avatarName: 'Account Owner',
|
|
162
176
|
}
|
|
177
|
+
WithAvatarOnly.parameters = skipInChromatic
|
|
163
178
|
|
|
164
179
|
export const WithAvatarAndImage: StoryFn<ComponentProps> = Template.bind({})
|
|
165
180
|
WithAvatarAndImage.args = {
|
|
@@ -184,3 +199,192 @@ WithAvatarAndImage.args = {
|
|
|
184
199
|
avatarName: 'Sarah Johnson',
|
|
185
200
|
avatarImage: 'https://i.pravatar.cc/150?img=28',
|
|
186
201
|
}
|
|
202
|
+
WithAvatarAndImage.parameters = skipInChromatic
|
|
203
|
+
|
|
204
|
+
export const AllStates: StoryFn = () => {
|
|
205
|
+
const states: { label: string; props: ComponentProps }[] = [
|
|
206
|
+
{
|
|
207
|
+
label: 'Default (header + avatar + image)',
|
|
208
|
+
props: {
|
|
209
|
+
faqs: [
|
|
210
|
+
{
|
|
211
|
+
id: '1',
|
|
212
|
+
question: 'Book club slots',
|
|
213
|
+
answer: 'We ship worldwide...',
|
|
214
|
+
enabled: true,
|
|
215
|
+
order: 1,
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
id: '2',
|
|
219
|
+
question: 'Refunds',
|
|
220
|
+
answer: 'You can track...',
|
|
221
|
+
enabled: true,
|
|
222
|
+
order: 2,
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
id: '3',
|
|
226
|
+
question: 'Brand collabs',
|
|
227
|
+
answer: 'Returns are accepted...',
|
|
228
|
+
enabled: true,
|
|
229
|
+
order: 3,
|
|
230
|
+
},
|
|
231
|
+
],
|
|
232
|
+
onFaqClick: (id) => console.log('FAQ clicked:', id),
|
|
233
|
+
headerText: 'Tap to send a suggested question by Brie Parson:',
|
|
234
|
+
avatarName: 'Brie Parson',
|
|
235
|
+
avatarImage: LOCAL_AVATAR,
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
label: 'Without header',
|
|
240
|
+
props: {
|
|
241
|
+
faqs: [
|
|
242
|
+
{
|
|
243
|
+
id: '1',
|
|
244
|
+
question: 'Book club slots',
|
|
245
|
+
answer: 'We ship worldwide...',
|
|
246
|
+
enabled: true,
|
|
247
|
+
order: 1,
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
id: '2',
|
|
251
|
+
question: 'Refunds',
|
|
252
|
+
answer: 'You can track...',
|
|
253
|
+
enabled: true,
|
|
254
|
+
order: 2,
|
|
255
|
+
},
|
|
256
|
+
],
|
|
257
|
+
onFaqClick: (id) => console.log('FAQ clicked:', id),
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
label: 'With avatar only',
|
|
262
|
+
props: {
|
|
263
|
+
faqs: [
|
|
264
|
+
{
|
|
265
|
+
id: '1',
|
|
266
|
+
question: 'Book club slots',
|
|
267
|
+
answer: 'We ship worldwide...',
|
|
268
|
+
enabled: true,
|
|
269
|
+
order: 1,
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
id: '2',
|
|
273
|
+
question: 'Refunds',
|
|
274
|
+
answer: 'You can track...',
|
|
275
|
+
enabled: true,
|
|
276
|
+
order: 2,
|
|
277
|
+
},
|
|
278
|
+
],
|
|
279
|
+
onFaqClick: (id) => console.log('FAQ clicked:', id),
|
|
280
|
+
headerText: 'Frequently Asked Questions',
|
|
281
|
+
avatarName: 'Account Owner',
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
label: 'With avatar and image',
|
|
286
|
+
props: {
|
|
287
|
+
faqs: [
|
|
288
|
+
{
|
|
289
|
+
id: '1',
|
|
290
|
+
question: 'How do I contact support?',
|
|
291
|
+
answer: 'You can reach us...',
|
|
292
|
+
enabled: true,
|
|
293
|
+
order: 1,
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
id: '2',
|
|
297
|
+
question: 'What are your hours?',
|
|
298
|
+
answer: 'We are available...',
|
|
299
|
+
enabled: true,
|
|
300
|
+
order: 2,
|
|
301
|
+
},
|
|
302
|
+
],
|
|
303
|
+
onFaqClick: (id) => console.log('FAQ clicked:', id),
|
|
304
|
+
headerText: 'Questions for Sarah',
|
|
305
|
+
avatarName: 'Sarah Johnson',
|
|
306
|
+
avatarImage: LOCAL_AVATAR,
|
|
307
|
+
},
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
label: 'With disabled FAQ',
|
|
311
|
+
props: {
|
|
312
|
+
faqs: [
|
|
313
|
+
{
|
|
314
|
+
id: '1',
|
|
315
|
+
question: 'Enabled question',
|
|
316
|
+
answer: 'Answer...',
|
|
317
|
+
enabled: true,
|
|
318
|
+
order: 1,
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
id: '2',
|
|
322
|
+
question: 'Disabled question',
|
|
323
|
+
answer: 'Answer...',
|
|
324
|
+
enabled: false,
|
|
325
|
+
order: 2,
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
id: '3',
|
|
329
|
+
question: 'Another enabled',
|
|
330
|
+
answer: 'Answer...',
|
|
331
|
+
enabled: true,
|
|
332
|
+
order: 3,
|
|
333
|
+
},
|
|
334
|
+
],
|
|
335
|
+
onFaqClick: (id) => console.log('FAQ clicked:', id),
|
|
336
|
+
},
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
label: 'With loading',
|
|
340
|
+
props: {
|
|
341
|
+
faqs: [
|
|
342
|
+
{
|
|
343
|
+
id: '1',
|
|
344
|
+
question: 'Book club slots',
|
|
345
|
+
answer: 'We ship worldwide...',
|
|
346
|
+
enabled: true,
|
|
347
|
+
order: 1,
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
id: '2',
|
|
351
|
+
question: 'Refunds',
|
|
352
|
+
answer: 'You can track...',
|
|
353
|
+
enabled: true,
|
|
354
|
+
order: 2,
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
id: '3',
|
|
358
|
+
question: 'Brand collabs',
|
|
359
|
+
answer: 'Returns are accepted...',
|
|
360
|
+
enabled: true,
|
|
361
|
+
order: 3,
|
|
362
|
+
},
|
|
363
|
+
],
|
|
364
|
+
onFaqClick: (id) => console.log('FAQ clicked:', id),
|
|
365
|
+
loadingFaqId: '2',
|
|
366
|
+
headerText: 'Tap to send a suggested question:',
|
|
367
|
+
},
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
label: 'Empty (renders nothing)',
|
|
371
|
+
props: {
|
|
372
|
+
faqs: [],
|
|
373
|
+
onFaqClick: (id) => console.log('FAQ clicked:', id),
|
|
374
|
+
},
|
|
375
|
+
},
|
|
376
|
+
]
|
|
377
|
+
|
|
378
|
+
return (
|
|
379
|
+
<div className="flex flex-col gap-6 bg-white p-6">
|
|
380
|
+
{states.map(({ label, props }) => (
|
|
381
|
+
<div key={label} className="flex flex-col gap-1">
|
|
382
|
+
<span className="text-xs text-stone">{label}</span>
|
|
383
|
+
<div className="w-96">
|
|
384
|
+
<FaqList {...props} />
|
|
385
|
+
</div>
|
|
386
|
+
</div>
|
|
387
|
+
))}
|
|
388
|
+
</div>
|
|
389
|
+
)
|
|
390
|
+
}
|
|
@@ -14,7 +14,7 @@ import { createMockMessagingClient } from './createMockMessagingClient'
|
|
|
14
14
|
*/
|
|
15
15
|
const meta: Meta = {
|
|
16
16
|
title: 'testing/createMockMessagingClient',
|
|
17
|
-
parameters: { layout: 'fullscreen'
|
|
17
|
+
parameters: { layout: 'fullscreen' },
|
|
18
18
|
}
|
|
19
19
|
export default meta
|
|
20
20
|
|