@linktr.ee/messaging-react 3.3.5 → 3.3.6-rc-1780987607
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/{Card-C-FCwjGa.cjs → Card-BlviN8Fb.cjs} +2 -2
- package/dist/{Card-C-FCwjGa.cjs.map → Card-BlviN8Fb.cjs.map} +1 -1
- package/dist/{Card-DzjYyrie.js → Card-C4ncqjxJ.js} +2 -2
- package/dist/{Card-DzjYyrie.js.map → Card-C4ncqjxJ.js.map} +1 -1
- package/dist/{Card-BRRlz4kq.cjs → Card-Cn7Zxc6U.cjs} +2 -2
- package/dist/{Card-BRRlz4kq.cjs.map → Card-Cn7Zxc6U.cjs.map} +1 -1
- package/dist/{Card-CVZzYmYW.js → Card-DE5bfj0l.js} +2 -2
- package/dist/{Card-CVZzYmYW.js.map → Card-DE5bfj0l.js.map} +1 -1
- package/dist/{Card-B9QrjooN.js → Card-IjOI7UXs.js} +3 -3
- package/dist/{Card-B9QrjooN.js.map → Card-IjOI7UXs.js.map} +1 -1
- package/dist/{Card-D_oLlfPw.cjs → Card-KgQxeR-B.cjs} +2 -2
- package/dist/{Card-D_oLlfPw.cjs.map → Card-KgQxeR-B.cjs.map} +1 -1
- package/dist/{LockedThumbnail-CJfXY_Ut.js → LockedThumbnail-4-54cyJG.js} +2 -2
- package/dist/{LockedThumbnail-CJfXY_Ut.js.map → LockedThumbnail-4-54cyJG.js.map} +1 -1
- package/dist/{LockedThumbnail-Cth1yWnH.cjs → LockedThumbnail-DL5NZzWJ.cjs} +2 -2
- package/dist/{LockedThumbnail-Cth1yWnH.cjs.map → LockedThumbnail-DL5NZzWJ.cjs.map} +1 -1
- package/dist/{index-D7eRkXoG.js → index-C2wfgpUU.js} +585 -551
- package/dist/index-C2wfgpUU.js.map +1 -0
- package/dist/index-nanry0Io.cjs +2 -0
- package/dist/index-nanry0Io.cjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/package.json +5 -2
- package/src/components/ActionButton/ActionButton.test.tsx +0 -25
- package/src/components/AttachmentCard/AttachmentCard.stories.tsx +122 -0
- package/src/components/Avatar/Avatar.stories.tsx +20 -0
- package/src/components/ChannelList/ChannelList.stories.tsx +5 -0
- package/src/components/ChannelList/CustomChannelPreview.stories.tsx +22 -0
- package/src/components/ChannelView.test.tsx +12 -1
- package/src/components/ChannelView.tsx +34 -17
- package/src/components/CustomMessage/CustomMessage.stories.tsx +0 -16
- package/src/components/CustomMessage/MessageAttachmentConversations.stories.tsx +13 -0
- package/src/components/CustomMessage/MessageTag.stories.tsx +18 -0
- package/src/components/CustomMessage/MessageVoteButtons.stories.tsx +9 -0
- package/src/components/CustomMessageInput/index.tsx +14 -4
- package/src/components/CustomSystemMessage/CustomSystemMessage.stories.tsx +54 -0
- package/src/components/CustomTypingIndicator/CustomTypingIndicator.stories.tsx +7 -0
- package/src/components/LinkAttachment/LinkAttachment.stories.tsx +11 -1
- package/src/components/LockedAttachment/LockedAttachment.stories.tsx +4 -0
- package/src/components/MediaMessage/MediaMessage.test.tsx +0 -38
- package/src/components/MessageAttachment/MessageAttachment.test.tsx +25 -84
- package/src/components/SearchInput/SearchInput.test.tsx +0 -8
- package/src/utils/formatRelativeTime.test.ts +1 -32
- package/dist/index-CBtOPvxW.cjs +0 -2
- package/dist/index-CBtOPvxW.cjs.map +0 -1
- package/dist/index-D7eRkXoG.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-nanry0Io.cjs");exports.ActionButton=e.ActionButton;exports.Avatar=e.Avatar;exports.ChannelEmptyState=e.ChannelEmptyState;exports.ChannelList=e.ChannelList;exports.ChannelView=e.ChannelView;exports.CustomMessageProvider=e.CustomMessageProvider;exports.FaqList=e.FaqList;exports.FaqListItem=e.FaqListItem;exports.LinkAttachment=e.LinkAttachment;exports.LockedAttachment=e.LockedAttachment;exports.MediaMessage=e.MediaMessage;exports.MessageAttachment=e.MessageAttachment;exports.MessageVoteButtons=e.MessageVoteButtons;exports.MessagingProvider=e.MessagingProvider;exports.MessagingShell=e.MessagingShell;exports.buildCompactMetaLabel=e.buildCompactMetaLabel;exports.formatFileSize=e.formatFileSize;exports.formatRelativeTime=e.formatRelativeTime;exports.getFileExtensionLabel=e.getFileExtensionLabel;exports.getMessageDisplayText=e.getMessageDisplayText;exports.isLinkAttachment=e.isLinkAttachment;exports.isUuidLike=e.isUuidLike;exports.messageAttachmentGroupPositionFromStream=e.bubbleGroupPositionFromStream;exports.normalizeLanguageCode=e.normalizeLanguageCode;exports.resolveLinkAttachment=e.resolveLinkAttachment;exports.resolveMediaFromMessage=e.resolveMediaFromMessage;exports.resolveParticipantDisplayName=e.resolveParticipantDisplayName;exports.useCustomMessage=e.useCustomMessage;exports.useMessageVote=e.useMessageVote;exports.useMessaging=e.useMessaging;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as e, b as t, C as i, c as n, d as o, e as m, F as g, f as l, L as r, h as M, M as u, i as L, j as c, k as h, l as d, m as p, n as v, o as A, p as C, q as F, s as k, t as b, u as f, v as x, w as y, x as P, y as S, z as q, B as z, D as B } from "./index-
|
|
1
|
+
import { a as e, b as t, C as i, c as n, d as o, e as m, F as g, f as l, L as r, h as M, M as u, i as L, j as c, k as h, l as d, m as p, n as v, o as A, p as C, q as F, s as k, t as b, u as f, v as x, w as y, x as P, y as S, z as q, B as z, D as B } from "./index-C2wfgpUU.js";
|
|
2
2
|
export {
|
|
3
3
|
e as ActionButton,
|
|
4
4
|
t as Avatar,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linktr.ee/messaging-react",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.6-rc-1780987607",
|
|
4
4
|
"description": "React messaging components built on messaging-core for web applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -32,7 +32,9 @@
|
|
|
32
32
|
"lint:fix": "npx --no-install eslint . --ext .ts,.tsx --fix",
|
|
33
33
|
"verify": "yarn type-check && yarn lint && yarn test:ci && yarn build",
|
|
34
34
|
"storybook": "storybook dev -p 6006",
|
|
35
|
-
"storybook:build": "storybook build"
|
|
35
|
+
"storybook:build": "storybook build --stats-json",
|
|
36
|
+
"chromatic:ci:feature": "chromatic --ci --exit-zero-on-changes --exit-once-uploaded",
|
|
37
|
+
"chromatic:ci:main": "chromatic --ci --auto-accept-changes"
|
|
36
38
|
},
|
|
37
39
|
"dependencies": {
|
|
38
40
|
"@linktr.ee/component-library": "11.8.6",
|
|
@@ -56,6 +58,7 @@
|
|
|
56
58
|
"@vitejs/plugin-react": "^4.2.1",
|
|
57
59
|
"@vitest/ui": "^1.0.4",
|
|
58
60
|
"autoprefixer": "^10.4.20",
|
|
61
|
+
"chromatic": "^11.20.0",
|
|
59
62
|
"classnames": "^2.3.2",
|
|
60
63
|
"jsdom": "^23.0.1",
|
|
61
64
|
"postcss": "^8.4.49",
|
|
@@ -10,24 +10,6 @@ describe('ActionButton', () => {
|
|
|
10
10
|
renderWithProviders(<ActionButton>Click me</ActionButton>);
|
|
11
11
|
expect(screen.getByRole('button', { name: 'Click me' })).toBeInTheDocument();
|
|
12
12
|
});
|
|
13
|
-
|
|
14
|
-
it('renders with default variant', () => {
|
|
15
|
-
renderWithProviders(<ActionButton>Default</ActionButton>);
|
|
16
|
-
const button = screen.getByRole('button');
|
|
17
|
-
expect(button).toHaveClass('text-charcoal');
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('renders with danger variant', () => {
|
|
21
|
-
renderWithProviders(<ActionButton variant="danger">Delete</ActionButton>);
|
|
22
|
-
const button = screen.getByRole('button');
|
|
23
|
-
expect(button).toHaveClass('text-danger');
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('has full width by default', () => {
|
|
27
|
-
renderWithProviders(<ActionButton>Full Width</ActionButton>);
|
|
28
|
-
const button = screen.getByRole('button');
|
|
29
|
-
expect(button).toHaveClass('w-full');
|
|
30
|
-
});
|
|
31
13
|
});
|
|
32
14
|
|
|
33
15
|
describe('Disabled State', () => {
|
|
@@ -52,13 +34,6 @@ describe('ActionButton', () => {
|
|
|
52
34
|
|
|
53
35
|
expect(handleClick).not.toHaveBeenCalled();
|
|
54
36
|
});
|
|
55
|
-
|
|
56
|
-
it('applies disabled styles', () => {
|
|
57
|
-
renderWithProviders(<ActionButton disabled>Disabled</ActionButton>);
|
|
58
|
-
const button = screen.getByRole('button');
|
|
59
|
-
expect(button).toHaveClass('disabled:opacity-60');
|
|
60
|
-
expect(button).toHaveClass('disabled:cursor-not-allowed');
|
|
61
|
-
});
|
|
62
37
|
});
|
|
63
38
|
|
|
64
39
|
describe('Click Handling', () => {
|
|
@@ -19,6 +19,10 @@ const IMAGE_URL = '/image-thumbnail.jpg'
|
|
|
19
19
|
|
|
20
20
|
const Template: StoryFn<ComponentProps> = (args) => <AttachmentCard {...args} />
|
|
21
21
|
|
|
22
|
+
// Per-variant stories stay in Storybook for browsing/design review but are
|
|
23
|
+
// individually covered by cells in AllVariants — skip in Chromatic.
|
|
24
|
+
const skipInChromatic = { chromatic: { disableSnapshot: true } }
|
|
25
|
+
|
|
22
26
|
export const LightImage: StoryFn<ComponentProps> = Template.bind({})
|
|
23
27
|
LightImage.args = {
|
|
24
28
|
variant: 'light',
|
|
@@ -34,6 +38,7 @@ LightImage.args = {
|
|
|
34
38
|
/>
|
|
35
39
|
),
|
|
36
40
|
}
|
|
41
|
+
LightImage.parameters = skipInChromatic
|
|
37
42
|
|
|
38
43
|
export const DarkImage: StoryFn<ComponentProps> = Template.bind({})
|
|
39
44
|
DarkImage.args = {
|
|
@@ -50,6 +55,7 @@ DarkImage.args = {
|
|
|
50
55
|
/>
|
|
51
56
|
),
|
|
52
57
|
}
|
|
58
|
+
DarkImage.parameters = skipInChromatic
|
|
53
59
|
|
|
54
60
|
export const PdfPlaceholder: StoryFn<ComponentProps> = Template.bind({})
|
|
55
61
|
PdfPlaceholder.args = {
|
|
@@ -59,6 +65,7 @@ PdfPlaceholder.args = {
|
|
|
59
65
|
detail: '120 KB',
|
|
60
66
|
thumbnail: <AttachmentThumbnail mimeType="application/pdf" variant="light" />,
|
|
61
67
|
}
|
|
68
|
+
PdfPlaceholder.parameters = skipInChromatic
|
|
62
69
|
|
|
63
70
|
export const AudioPosterOnly: StoryFn<ComponentProps> = Template.bind({})
|
|
64
71
|
AudioPosterOnly.args = {
|
|
@@ -75,6 +82,7 @@ AudioPosterOnly.args = {
|
|
|
75
82
|
/>
|
|
76
83
|
),
|
|
77
84
|
}
|
|
85
|
+
AudioPosterOnly.parameters = skipInChromatic
|
|
78
86
|
|
|
79
87
|
export const UntitledDark: StoryFn<ComponentProps> = Template.bind({})
|
|
80
88
|
UntitledDark.args = {
|
|
@@ -83,6 +91,7 @@ UntitledDark.args = {
|
|
|
83
91
|
detail: '1.1 MB',
|
|
84
92
|
thumbnail: <AttachmentThumbnail mimeType="image/png" variant="dark" />,
|
|
85
93
|
}
|
|
94
|
+
UntitledDark.parameters = skipInChromatic
|
|
86
95
|
|
|
87
96
|
export const WithTopBadges: StoryFn<ComponentProps> = Template.bind({})
|
|
88
97
|
WithTopBadges.args = {
|
|
@@ -102,3 +111,116 @@ WithTopBadges.args = {
|
|
|
102
111
|
</span>
|
|
103
112
|
),
|
|
104
113
|
}
|
|
114
|
+
WithTopBadges.parameters = skipInChromatic
|
|
115
|
+
|
|
116
|
+
export const AllVariants: StoryFn = () => {
|
|
117
|
+
const cards: { label: string; props: ComponentProps }[] = [
|
|
118
|
+
{
|
|
119
|
+
label: 'Light image',
|
|
120
|
+
props: {
|
|
121
|
+
variant: 'light',
|
|
122
|
+
title: 'sunset.jpg',
|
|
123
|
+
mimeType: 'image/jpeg',
|
|
124
|
+
detail: '2.4 MB',
|
|
125
|
+
thumbnail: (
|
|
126
|
+
<AttachmentThumbnail
|
|
127
|
+
mimeType="image/jpeg"
|
|
128
|
+
sourceUrl={IMAGE_URL}
|
|
129
|
+
title="sunset"
|
|
130
|
+
variant="light"
|
|
131
|
+
/>
|
|
132
|
+
),
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
label: 'Dark image',
|
|
137
|
+
props: {
|
|
138
|
+
variant: 'dark',
|
|
139
|
+
title: 'sunset.jpg',
|
|
140
|
+
mimeType: 'image/jpeg',
|
|
141
|
+
detail: '2.4 MB',
|
|
142
|
+
thumbnail: (
|
|
143
|
+
<AttachmentThumbnail
|
|
144
|
+
mimeType="image/jpeg"
|
|
145
|
+
sourceUrl={IMAGE_URL}
|
|
146
|
+
title="sunset"
|
|
147
|
+
variant="dark"
|
|
148
|
+
/>
|
|
149
|
+
),
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
label: 'PDF placeholder',
|
|
154
|
+
props: {
|
|
155
|
+
variant: 'light',
|
|
156
|
+
title: 'invoice.pdf',
|
|
157
|
+
mimeType: 'application/pdf',
|
|
158
|
+
detail: '120 KB',
|
|
159
|
+
thumbnail: (
|
|
160
|
+
<AttachmentThumbnail mimeType="application/pdf" variant="light" />
|
|
161
|
+
),
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
label: 'Audio poster',
|
|
166
|
+
props: {
|
|
167
|
+
variant: 'dark',
|
|
168
|
+
title: 'voice-memo.m4a',
|
|
169
|
+
mimeType: 'audio/m4a',
|
|
170
|
+
detail: '0:42',
|
|
171
|
+
thumbnail: (
|
|
172
|
+
<AttachmentThumbnail
|
|
173
|
+
mimeType="audio/m4a"
|
|
174
|
+
thumbnailUrl={IMAGE_URL}
|
|
175
|
+
title="voice memo"
|
|
176
|
+
variant="dark"
|
|
177
|
+
/>
|
|
178
|
+
),
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
label: 'Untitled (dark)',
|
|
183
|
+
props: {
|
|
184
|
+
variant: 'dark',
|
|
185
|
+
mimeType: 'image/png',
|
|
186
|
+
detail: '1.1 MB',
|
|
187
|
+
thumbnail: (
|
|
188
|
+
<AttachmentThumbnail mimeType="image/png" variant="dark" />
|
|
189
|
+
),
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
label: 'With top badges',
|
|
194
|
+
props: {
|
|
195
|
+
variant: 'light',
|
|
196
|
+
title: 'receipt.pdf',
|
|
197
|
+
mimeType: 'application/pdf',
|
|
198
|
+
detail: 'Paid',
|
|
199
|
+
thumbnail: (
|
|
200
|
+
<AttachmentThumbnail mimeType="application/pdf" variant="light" />
|
|
201
|
+
),
|
|
202
|
+
topLeft: (
|
|
203
|
+
<span className="rounded-full bg-black/80 px-2 py-0.5 text-xs font-medium text-white">
|
|
204
|
+
Locked
|
|
205
|
+
</span>
|
|
206
|
+
),
|
|
207
|
+
topRight: (
|
|
208
|
+
<span className="rounded-full bg-white/90 px-2 py-0.5 text-xs font-medium text-black">
|
|
209
|
+
$5.00
|
|
210
|
+
</span>
|
|
211
|
+
),
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
]
|
|
215
|
+
|
|
216
|
+
return (
|
|
217
|
+
<div className="grid grid-cols-2 gap-6 p-6">
|
|
218
|
+
{cards.map(({ label, props }) => (
|
|
219
|
+
<div key={label} className="flex flex-col gap-2">
|
|
220
|
+
<span className="text-xs text-stone">{label}</span>
|
|
221
|
+
<AttachmentCard {...props} />
|
|
222
|
+
</div>
|
|
223
|
+
))}
|
|
224
|
+
</div>
|
|
225
|
+
)
|
|
226
|
+
}
|
|
@@ -29,6 +29,11 @@ Default.args = {
|
|
|
29
29
|
image: 'https://i.pravatar.cc/150?img=1',
|
|
30
30
|
}
|
|
31
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 } }
|
|
36
|
+
|
|
32
37
|
export const AIActive: StoryFn<ComponentProps> = Template.bind({})
|
|
33
38
|
AIActive.args = {
|
|
34
39
|
id: 'ai-agent',
|
|
@@ -36,6 +41,7 @@ AIActive.args = {
|
|
|
36
41
|
image: 'https://i.pravatar.cc/150?img=12',
|
|
37
42
|
dmAgentEnabled: true,
|
|
38
43
|
}
|
|
44
|
+
AIActive.parameters = skipInChromatic
|
|
39
45
|
|
|
40
46
|
export const WithoutImage: StoryFn<ComponentProps> = Template.bind({})
|
|
41
47
|
WithoutImage.args = {
|
|
@@ -56,6 +62,7 @@ SmallSize.args = {
|
|
|
56
62
|
name: 'Carol Williams',
|
|
57
63
|
size: 20,
|
|
58
64
|
}
|
|
65
|
+
SmallSize.parameters = skipInChromatic
|
|
59
66
|
|
|
60
67
|
export const MediumSize: StoryFn<ComponentProps> = Template.bind({})
|
|
61
68
|
MediumSize.args = {
|
|
@@ -63,6 +70,7 @@ MediumSize.args = {
|
|
|
63
70
|
name: 'David Brown',
|
|
64
71
|
size: 32,
|
|
65
72
|
}
|
|
73
|
+
MediumSize.parameters = skipInChromatic
|
|
66
74
|
|
|
67
75
|
export const DefaultSize: StoryFn<ComponentProps> = Template.bind({})
|
|
68
76
|
DefaultSize.args = {
|
|
@@ -70,6 +78,7 @@ DefaultSize.args = {
|
|
|
70
78
|
name: 'Emma Davis',
|
|
71
79
|
size: 40,
|
|
72
80
|
}
|
|
81
|
+
DefaultSize.parameters = skipInChromatic
|
|
73
82
|
|
|
74
83
|
export const LargeSize: StoryFn<ComponentProps> = Template.bind({})
|
|
75
84
|
LargeSize.args = {
|
|
@@ -77,6 +86,7 @@ LargeSize.args = {
|
|
|
77
86
|
name: 'Frank Miller',
|
|
78
87
|
size: 56,
|
|
79
88
|
}
|
|
89
|
+
LargeSize.parameters = skipInChromatic
|
|
80
90
|
|
|
81
91
|
export const ExtraLargeSize: StoryFn<ComponentProps> = Template.bind({})
|
|
82
92
|
ExtraLargeSize.args = {
|
|
@@ -84,12 +94,17 @@ ExtraLargeSize.args = {
|
|
|
84
94
|
name: 'Grace Lee',
|
|
85
95
|
size: 80,
|
|
86
96
|
}
|
|
97
|
+
ExtraLargeSize.parameters = skipInChromatic
|
|
87
98
|
|
|
99
|
+
// Avatars don't render the participant name in the bubble — only the initial.
|
|
100
|
+
// LongName stays as a Storybook fixture for documentation but adds no visual
|
|
101
|
+
// regression value beyond what Default + WithoutImage already cover.
|
|
88
102
|
export const LongName: StoryFn<ComponentProps> = Template.bind({})
|
|
89
103
|
LongName.args = {
|
|
90
104
|
id: 'user-8',
|
|
91
105
|
name: 'Alexander Christopher Wellington-Montgomery III',
|
|
92
106
|
}
|
|
107
|
+
LongName.parameters = { chromatic: { disableSnapshot: true } }
|
|
93
108
|
|
|
94
109
|
export const DifferentColors: StoryFn = () => {
|
|
95
110
|
const users = [
|
|
@@ -117,6 +132,10 @@ export const DifferentColors: StoryFn = () => {
|
|
|
117
132
|
)
|
|
118
133
|
}
|
|
119
134
|
|
|
135
|
+
// Showcase grid that crosses image-present/absent with different users.
|
|
136
|
+
// Each individual case is already covered by `Default` (image) and
|
|
137
|
+
// `WithoutImage` (initial) — skip the showcase in Chromatic so a single
|
|
138
|
+
// pravatar avatar flake doesn't diff the whole grid.
|
|
120
139
|
export const MixedAvatars: StoryFn = () => {
|
|
121
140
|
const users = [
|
|
122
141
|
{ id: 'user-1', name: 'Alice Anderson', image: 'https://i.pravatar.cc/150?img=1' },
|
|
@@ -140,6 +159,7 @@ export const MixedAvatars: StoryFn = () => {
|
|
|
140
159
|
</div>
|
|
141
160
|
)
|
|
142
161
|
}
|
|
162
|
+
MixedAvatars.parameters = { chromatic: { disableSnapshot: true } }
|
|
143
163
|
|
|
144
164
|
export const VariousSizes: StoryFn = () => {
|
|
145
165
|
const sizes = [20, 32, 40, 56, 80]
|
|
@@ -8,11 +8,16 @@ import { ChannelList } from '.'
|
|
|
8
8
|
|
|
9
9
|
type ComponentProps = React.ComponentProps<typeof ChannelList>
|
|
10
10
|
|
|
11
|
+
// ChannelList renders a list of mocked channels driven by MockChatProvider —
|
|
12
|
+
// useful as a browsable Storybook fixture but the snapshot reflects the mock
|
|
13
|
+
// fixture rather than any reusable component contract. Skip the whole story
|
|
14
|
+
// file in Chromatic; per-channel-row visuals are covered by CustomChannelPreview.
|
|
11
15
|
const meta: Meta<ComponentProps> = {
|
|
12
16
|
title: 'ChannelList',
|
|
13
17
|
component: ChannelList,
|
|
14
18
|
parameters: {
|
|
15
19
|
layout: 'fullscreen',
|
|
20
|
+
chromatic: { disableSnapshot: true },
|
|
16
21
|
},
|
|
17
22
|
decorators: [
|
|
18
23
|
(Story) => (
|
|
@@ -483,6 +483,13 @@ WithVeryLongUrl.args = {
|
|
|
483
483
|
}
|
|
484
484
|
|
|
485
485
|
// Time-based stories
|
|
486
|
+
//
|
|
487
|
+
// Each story below covers exactly one row of the `formatRelativeTime`
|
|
488
|
+
// output matrix. The `TimeVariations` story further down already snapshots
|
|
489
|
+
// all of them together in a labeled table — kept here for Storybook
|
|
490
|
+
// browsing but skipped in Chromatic to avoid 9 redundant snapshots.
|
|
491
|
+
const skipInChromatic = { chromatic: { disableSnapshot: true } }
|
|
492
|
+
|
|
486
493
|
export const JustNow: StoryFn<ComponentProps> = Template.bind({})
|
|
487
494
|
JustNow.args = {
|
|
488
495
|
channel: createMockChannel({
|
|
@@ -494,6 +501,7 @@ JustNow.args = {
|
|
|
494
501
|
lastMessageTime: secondsAgo(30),
|
|
495
502
|
}),
|
|
496
503
|
}
|
|
504
|
+
JustNow.parameters = skipInChromatic
|
|
497
505
|
|
|
498
506
|
export const FiveMinutesAgo: StoryFn<ComponentProps> = Template.bind({})
|
|
499
507
|
FiveMinutesAgo.args = {
|
|
@@ -506,6 +514,7 @@ FiveMinutesAgo.args = {
|
|
|
506
514
|
lastMessageTime: minutesAgo(5),
|
|
507
515
|
}),
|
|
508
516
|
}
|
|
517
|
+
FiveMinutesAgo.parameters = skipInChromatic
|
|
509
518
|
|
|
510
519
|
export const ThreeHoursAgo: StoryFn<ComponentProps> = Template.bind({})
|
|
511
520
|
ThreeHoursAgo.args = {
|
|
@@ -518,6 +527,7 @@ ThreeHoursAgo.args = {
|
|
|
518
527
|
lastMessageTime: hoursAgo(3),
|
|
519
528
|
}),
|
|
520
529
|
}
|
|
530
|
+
ThreeHoursAgo.parameters = skipInChromatic
|
|
521
531
|
|
|
522
532
|
export const Yesterday: StoryFn<ComponentProps> = Template.bind({})
|
|
523
533
|
Yesterday.args = {
|
|
@@ -530,6 +540,7 @@ Yesterday.args = {
|
|
|
530
540
|
lastMessageTime: hoursAgo(30),
|
|
531
541
|
}),
|
|
532
542
|
}
|
|
543
|
+
Yesterday.parameters = skipInChromatic
|
|
533
544
|
|
|
534
545
|
export const ThreeDaysAgo: StoryFn<ComponentProps> = Template.bind({})
|
|
535
546
|
ThreeDaysAgo.args = {
|
|
@@ -542,6 +553,7 @@ ThreeDaysAgo.args = {
|
|
|
542
553
|
lastMessageTime: daysAgo(3),
|
|
543
554
|
}),
|
|
544
555
|
}
|
|
556
|
+
ThreeDaysAgo.parameters = skipInChromatic
|
|
545
557
|
|
|
546
558
|
export const LastWeek: StoryFn<ComponentProps> = Template.bind({})
|
|
547
559
|
LastWeek.args = {
|
|
@@ -554,6 +566,7 @@ LastWeek.args = {
|
|
|
554
566
|
lastMessageTime: daysAgo(7),
|
|
555
567
|
}),
|
|
556
568
|
}
|
|
569
|
+
LastWeek.parameters = skipInChromatic
|
|
557
570
|
|
|
558
571
|
export const TwoWeeksAgo: StoryFn<ComponentProps> = Template.bind({})
|
|
559
572
|
TwoWeeksAgo.args = {
|
|
@@ -566,6 +579,7 @@ TwoWeeksAgo.args = {
|
|
|
566
579
|
lastMessageTime: daysAgo(14),
|
|
567
580
|
}),
|
|
568
581
|
}
|
|
582
|
+
TwoWeeksAgo.parameters = skipInChromatic
|
|
569
583
|
|
|
570
584
|
export const OneMonthAgo: StoryFn<ComponentProps> = Template.bind({})
|
|
571
585
|
OneMonthAgo.args = {
|
|
@@ -578,6 +592,7 @@ OneMonthAgo.args = {
|
|
|
578
592
|
lastMessageTime: daysAgo(30),
|
|
579
593
|
}),
|
|
580
594
|
}
|
|
595
|
+
OneMonthAgo.parameters = skipInChromatic
|
|
581
596
|
|
|
582
597
|
export const SixMonthsAgo: StoryFn<ComponentProps> = Template.bind({})
|
|
583
598
|
SixMonthsAgo.args = {
|
|
@@ -590,6 +605,7 @@ SixMonthsAgo.args = {
|
|
|
590
605
|
lastMessageTime: daysAgo(180),
|
|
591
606
|
}),
|
|
592
607
|
}
|
|
608
|
+
SixMonthsAgo.parameters = skipInChromatic
|
|
593
609
|
|
|
594
610
|
export const TimeVariations: StoryFn = () => {
|
|
595
611
|
const [selectedChannelId, _setSelectedChannelId] = React.useState<
|
|
@@ -723,6 +739,10 @@ WithAttachmentImageUrl.args = {
|
|
|
723
739
|
],
|
|
724
740
|
}),
|
|
725
741
|
}
|
|
742
|
+
// Same fallback render as WithAttachmentAssetUrl ("📎 Sent an attachment") —
|
|
743
|
+
// kept as a separate story to document the field, skipped in Chromatic to
|
|
744
|
+
// avoid a redundant snapshot.
|
|
745
|
+
WithAttachmentImageUrl.parameters = skipInChromatic
|
|
726
746
|
|
|
727
747
|
export const WithAttachmentOgScrapeUrl: StoryFn<ComponentProps> = Template.bind(
|
|
728
748
|
{}
|
|
@@ -759,6 +779,7 @@ WithAttachmentThumbUrl.args = {
|
|
|
759
779
|
],
|
|
760
780
|
}),
|
|
761
781
|
}
|
|
782
|
+
WithAttachmentThumbUrl.parameters = skipInChromatic
|
|
762
783
|
|
|
763
784
|
export const WithLongAttachmentUrl: StoryFn<ComponentProps> = Template.bind({})
|
|
764
785
|
WithLongAttachmentUrl.args = {
|
|
@@ -777,3 +798,4 @@ WithLongAttachmentUrl.args = {
|
|
|
777
798
|
],
|
|
778
799
|
}),
|
|
779
800
|
}
|
|
801
|
+
WithLongAttachmentUrl.parameters = skipInChromatic
|
|
@@ -22,7 +22,18 @@ vi.mock('stream-chat-react', () => ({
|
|
|
22
22
|
Window: ({ children }: { children: React.ReactNode }) => (
|
|
23
23
|
<div data-testid="window">{children}</div>
|
|
24
24
|
),
|
|
25
|
-
MessageList: (
|
|
25
|
+
MessageList: ({
|
|
26
|
+
renderMessages,
|
|
27
|
+
}: {
|
|
28
|
+
renderMessages?: (options: Record<string, unknown>) => React.ReactNode[]
|
|
29
|
+
}) => (
|
|
30
|
+
<ul data-testid="message-list">
|
|
31
|
+
{renderMessages?.({ messages: [] }) ?? null}
|
|
32
|
+
</ul>
|
|
33
|
+
),
|
|
34
|
+
defaultRenderMessages: () => [
|
|
35
|
+
<li key="message-list-item" data-testid="message-list-item" />,
|
|
36
|
+
],
|
|
26
37
|
WithComponents: ({ children }: { children: React.ReactNode }) => (
|
|
27
38
|
<>{children}</>
|
|
28
39
|
),
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
useChannelStateContext,
|
|
11
11
|
WithComponents,
|
|
12
12
|
MessageUIComponentProps,
|
|
13
|
+
defaultRenderMessages,
|
|
13
14
|
} from 'stream-chat-react'
|
|
14
15
|
|
|
15
16
|
import { useChannelStar } from '../hooks/useChannelStar'
|
|
@@ -399,25 +400,41 @@ const ChannelViewInner: React.FC<{
|
|
|
399
400
|
key="lt-channel-message-list"
|
|
400
401
|
className="flex-1 overflow-hidden relative"
|
|
401
402
|
>
|
|
402
|
-
<MessageList
|
|
403
|
-
|
|
403
|
+
<MessageList
|
|
404
|
+
hideDeletedMessages
|
|
405
|
+
hideNewMessageSeparator={false}
|
|
406
|
+
renderMessages={(options) => {
|
|
407
|
+
const elements = defaultRenderMessages(options)
|
|
404
408
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
409
|
+
if (renderConversationFooter) {
|
|
410
|
+
elements.push(
|
|
411
|
+
<li key="lt-channel-conversation-footer">
|
|
412
|
+
{renderConversationFooter(channel)}
|
|
413
|
+
</li>
|
|
414
|
+
)
|
|
415
|
+
}
|
|
410
416
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
417
|
+
elements.push(
|
|
418
|
+
// Message Input
|
|
419
|
+
<li
|
|
420
|
+
key="lt-channel-message-input"
|
|
421
|
+
className="flex sticky bottom-0 p-4 pt-1 inset-x-0 mx-[calc(-1*var(--str-chat\_\_spacing-2))] md:mx-[calc(-1*min(var(--str-chat\_\_spacing-10),4%))]"
|
|
422
|
+
>
|
|
423
|
+
<div className="absolute bottom-0 inset-x-0 w-full h-4/5 backdrop-blur-[16px] [mask-image:linear-gradient(to_top,black_0%,transparent_100%)] [-webkit-mask-image:linear-gradient(to_top,black_0%,transparent_100%)]" />
|
|
424
|
+
<CustomMessageInput
|
|
425
|
+
className="isolate w-full"
|
|
426
|
+
renderActions={() => renderMessageInputActions?.(channel)}
|
|
427
|
+
renderFooter={() => renderMessageInputFooter?.(channel)}
|
|
428
|
+
disabled={composerDisabled}
|
|
429
|
+
disabledReason={composerDisabledReason}
|
|
430
|
+
/>
|
|
431
|
+
</li>
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
return elements
|
|
435
|
+
}}
|
|
436
|
+
/>
|
|
437
|
+
</div>
|
|
421
438
|
</Window>
|
|
422
439
|
</WithComponents>
|
|
423
440
|
</>
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import '../../stream-custom-data'
|
|
2
2
|
|
|
3
3
|
import type { Meta, StoryFn } from '@storybook/react'
|
|
4
|
-
import { expect, userEvent, within } from '@storybook/test'
|
|
5
4
|
import React, { useEffect } from 'react'
|
|
6
5
|
import {
|
|
7
6
|
Channel as ChannelType,
|
|
@@ -568,21 +567,6 @@ ConversationWithPaidAttachments.args = {
|
|
|
568
567
|
},
|
|
569
568
|
],
|
|
570
569
|
}
|
|
571
|
-
ConversationWithPaidAttachments.play = async ({ canvasElement }) => {
|
|
572
|
-
const canvas = within(canvasElement)
|
|
573
|
-
const attachmentMsg = canvasElement.querySelector('[data-message-id="msg-3"]')
|
|
574
|
-
await userEvent.hover(attachmentMsg!)
|
|
575
|
-
const toggle = await canvas.findByTestId('message-actions-toggle-button')
|
|
576
|
-
await userEvent.click(toggle)
|
|
577
|
-
const deleteButton = await canvas.findByRole('button', { name: 'Delete' })
|
|
578
|
-
await userEvent.click(deleteButton)
|
|
579
|
-
await expect(
|
|
580
|
-
canvas.getByRole('heading', { name: 'Delete attachment?' })
|
|
581
|
-
).toBeInTheDocument()
|
|
582
|
-
await expect(
|
|
583
|
-
canvas.getByText(/Deleting it will remove access for the buyer/)
|
|
584
|
-
).toBeInTheDocument()
|
|
585
|
-
}
|
|
586
570
|
ConversationWithPaidAttachments.parameters = {
|
|
587
571
|
docs: {
|
|
588
572
|
description: {
|
|
@@ -223,6 +223,14 @@ interface ConversationStoryArgs {
|
|
|
223
223
|
currentUser: StoryUser
|
|
224
224
|
}
|
|
225
225
|
|
|
226
|
+
// Single-attachment-type conversations (`ConversationWithImages`,
|
|
227
|
+
// `ConversationWithVideos`, etc.) are useful for design review but the
|
|
228
|
+
// per-attachment-type RENDERING is already covered by the dedicated
|
|
229
|
+
// `MessageAttachment/{Image,Video,Audio,Pdf,File}` stories. The
|
|
230
|
+
// "mixed" conversations below exercise composition that the per-type
|
|
231
|
+
// stories don't, so those keep their snapshots.
|
|
232
|
+
const skipInChromatic = { chromatic: { disableSnapshot: true } }
|
|
233
|
+
|
|
226
234
|
const HERO_PHOTO = 'https://picsum.photos/seed/portrait/720/720'
|
|
227
235
|
const STUDIO_PHOTOS = [
|
|
228
236
|
{ src: 'https://picsum.photos/seed/studio-1/720/720', alt: 'Studio shot 1' },
|
|
@@ -291,6 +299,7 @@ export const ConversationWithImages: StoryFn<ConversationStoryArgs> = ({
|
|
|
291
299
|
/>
|
|
292
300
|
)
|
|
293
301
|
}
|
|
302
|
+
ConversationWithImages.parameters = skipInChromatic
|
|
294
303
|
|
|
295
304
|
/**
|
|
296
305
|
* Video attachments — receiver sends a single clip with a caption,
|
|
@@ -355,6 +364,7 @@ export const ConversationWithVideos: StoryFn<ConversationStoryArgs> = ({
|
|
|
355
364
|
/>
|
|
356
365
|
)
|
|
357
366
|
}
|
|
367
|
+
ConversationWithVideos.parameters = skipInChromatic
|
|
358
368
|
|
|
359
369
|
/**
|
|
360
370
|
* Audio attachments — voice memo back-and-forth, plus a stacked
|
|
@@ -426,6 +436,7 @@ export const ConversationWithAudio: StoryFn<ConversationStoryArgs> = ({
|
|
|
426
436
|
/>
|
|
427
437
|
)
|
|
428
438
|
}
|
|
439
|
+
ConversationWithAudio.parameters = skipInChromatic
|
|
429
440
|
|
|
430
441
|
/**
|
|
431
442
|
* PDF attachments — single document with a caption and a stacked
|
|
@@ -498,6 +509,7 @@ export const ConversationWithPdfs: StoryFn<ConversationStoryArgs> = ({
|
|
|
498
509
|
/>
|
|
499
510
|
)
|
|
500
511
|
}
|
|
512
|
+
ConversationWithPdfs.parameters = skipInChromatic
|
|
501
513
|
|
|
502
514
|
/**
|
|
503
515
|
* Generic file attachments — non-PDF documents with a download
|
|
@@ -583,6 +595,7 @@ export const ConversationWithFiles: StoryFn<ConversationStoryArgs> = ({
|
|
|
583
595
|
/>
|
|
584
596
|
)
|
|
585
597
|
}
|
|
598
|
+
ConversationWithFiles.parameters = skipInChromatic
|
|
586
599
|
|
|
587
600
|
/**
|
|
588
601
|
* Mixed conversation — a realistic chat where the same thread carries
|