@linktr.ee/messaging-react 3.37.0-rc-1786190741 → 3.37.0
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/README.md +4 -4
- package/dist/Card-415OfRhw.cjs.map +1 -1
- package/dist/{Card-DVMzYquF.js → Card-D4PPD5qa.js} +8 -11
- package/dist/Card-D4PPD5qa.js.map +1 -0
- package/dist/Card-Dxk_Ggxw.cjs.map +1 -1
- package/dist/{Card-DaJtDL7m.js → Card-qS17qnN0.js} +6 -15
- package/dist/Card-qS17qnN0.js.map +1 -0
- package/dist/LoadingDots-Ct3WmVlT.js.map +1 -1
- package/dist/LoadingDots-D9GGphgM.cjs.map +1 -1
- package/dist/index-D7A8U8GQ.cjs.map +1 -1
- package/dist/{index-BOBIiAuD.js → index-DGmVyxHy.js} +62 -87
- package/dist/index-DGmVyxHy.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/src/components/ActionButton/ActionButton.stories.tsx +2 -0
- package/src/components/ActionButton/ActionButton.test.tsx +55 -60
- package/src/components/ActionButton/index.tsx +14 -13
- package/src/components/AttachmentCard/AttachmentCard.stories.tsx +3 -1
- package/src/components/AttachmentCard/MediaPlayer.tsx +7 -9
- package/src/components/AttachmentCard/index.tsx +2 -6
- package/src/components/Avatar/Avatar.stories.tsx +5 -17
- package/src/components/Avatar/index.tsx +23 -13
- package/src/components/ChannelView.test.tsx +10 -6
- package/src/components/CustomMessage/CustomMessage.stories.tsx +2 -7
- package/src/components/CustomMessage/CustomMessage.test.tsx +3 -5
- package/src/components/CustomMessage/LockedAttachment/LockedAttachment.test.tsx +4 -5
- package/src/components/CustomMessage/LockedAttachment/components/Text/Card.tsx +4 -2
- package/src/components/CustomMessage/LockedAttachment/components/_shared/CardBody.tsx +6 -9
- package/src/components/CustomMessage/LockedAttachment/components/_shared/CardThumbnail.tsx +3 -1
- package/src/components/CustomMessage/LockedAttachment/components/_shared/GalleryThumbnail.tsx +4 -1
- package/src/components/CustomMessage/LockedAttachment/components/_shared/PurchaseStatusBadge.tsx +1 -0
- package/src/components/CustomMessage/LockedAttachment/components/_shared/SingleThumbnail.tsx +4 -1
- package/src/components/CustomMessage/LockedAttachment/components/_shared/SkeletonTextLines.tsx +2 -7
- package/src/components/CustomMessage/LockedAttachment/types.ts +3 -4
- package/src/components/CustomMessage/MessageAttachmentConversations.stories.tsx +86 -89
- package/src/components/CustomMessage/MessageVoteButtons.tsx +1 -4
- package/src/components/CustomMessage/SentMessageDeliveryStatus.test.tsx +10 -13
- package/src/components/CustomMessage/SentMessageDeliveryStatus.tsx +4 -1
- package/src/components/CustomMessage/StreamAttachmentMessage.stories.tsx +4 -4
- package/src/components/CustomMessage/StreamAttachmentMessage.test.tsx +7 -6
- package/src/components/CustomMessage/context.tsx +2 -7
- package/src/components/CustomMessage/index.tsx +4 -1
- package/src/components/CustomMessageInput/CustomMessageInput.stories.tsx +11 -33
- package/src/components/CustomMessageInput/CustomMessageInput.test.tsx +3 -3
- package/src/components/CustomSystemMessage/CustomSystemMessage.test.tsx +9 -6
- package/src/components/CustomTypingIndicator/CustomTypingIndicator.stories.tsx +1 -4
- package/src/components/IconButton/index.tsx +17 -21
- package/src/components/LinkAttachment/LinkAttachment.test.tsx +1 -4
- package/src/components/LinkAttachment/components/_shared/CardCta.tsx +1 -4
- package/src/components/Loading/Loading.stories.tsx +4 -1
- package/src/components/MediaMessage/MediaMessage.stories.tsx +18 -6
- package/src/components/MediaMessage/MediaMessage.test.tsx +16 -30
- package/src/components/MediaMessage/index.tsx +14 -51
- package/src/components/MessageAttachment/Audio/AudioAttachment.stories.tsx +2 -4
- package/src/components/MessageAttachment/File/FileAttachment.stories.tsx +1 -3
- package/src/components/MessageAttachment/Image/ImageAttachment.stories.tsx +4 -15
- package/src/components/MessageAttachment/Image/index.tsx +12 -6
- package/src/components/MessageAttachment/MessageAttachment.test.tsx +27 -59
- package/src/components/MessageAttachment/Pdf/PdfAttachment.stories.tsx +1 -3
- package/src/components/MessageAttachment/Pdf/index.tsx +3 -2
- package/src/components/MessageAttachment/Video/VideoAttachment.stories.tsx +3 -11
- package/src/components/MessageAttachment/Video/index.tsx +14 -9
- package/src/components/MessageAttachment/_shared/CompactDocumentRow.tsx +3 -1
- package/src/components/MessageAttachment/_shared/ImageViewer.tsx +3 -1
- package/src/components/MessageAttachment/_shared/MediaStackGrid.tsx +1 -5
- package/src/components/MessageAttachment/_shared/PdfViewer.tsx +3 -1
- package/src/components/MessageAttachment/_shared/VideoViewer.tsx +3 -1
- package/src/components/MessageAttachment/_shared/fileMeta.test.ts +6 -4
- package/src/components/MessageAttachment/stories/StoryTable.tsx +3 -1
- package/src/components/MessageBubble/MessageBubble.stories.tsx +1 -5
- package/src/components/MessageBubble/MessageBubble.test.tsx +3 -1
- package/src/components/MessageBubble/components/_shared/bubbleTail.tsx +1 -2
- package/src/components/MessagingShell/LoadingState.stories.tsx +1 -0
- package/src/components/RichCard/RichCard.stories.tsx +3 -14
- package/src/components/RichCard/RichCard.test.tsx +4 -16
- package/src/components/RichCard/RichCardBody.tsx +3 -11
- package/src/components/RichCard/RichCardCard.tsx +3 -1
- package/src/components/RichCard/RichCardImages.tsx +4 -2
- package/src/components/SearchInput/SearchInput.stories.tsx +1 -0
- package/src/components/SearchInput/SearchInput.test.tsx +65 -94
- package/src/hooks/useChannelStar.ts +1 -3
- package/src/hooks/useMessaging.ts +4 -4
- package/src/stories/decorators/storyUser.tsx +2 -2
- package/src/stories/mocks.tsx +1 -4
- package/src/styles.css +8 -23
- package/src/types.ts +2 -1
- package/src/utils/cdnImageUrl.test.ts +1 -3
- package/dist/Card-DVMzYquF.js.map +0 -1
- package/dist/Card-DaJtDL7m.js.map +0 -1
- package/dist/index-BOBIiAuD.js.map +0 -1
|
@@ -65,7 +65,9 @@ const VideoViewer: React.FC<VideoViewerProps> = ({
|
|
|
65
65
|
open={open}
|
|
66
66
|
onClose={onClose}
|
|
67
67
|
ariaLabel={filename}
|
|
68
|
-
counter={
|
|
68
|
+
counter={
|
|
69
|
+
items.length > 1 ? `${index + 1} / ${items.length}` : undefined
|
|
70
|
+
}
|
|
69
71
|
data-testid="video-viewer"
|
|
70
72
|
>
|
|
71
73
|
{/* No `<track>` is rendered — we don't author caption sidecars
|
|
@@ -30,7 +30,9 @@ describe('formatFileSize', () => {
|
|
|
30
30
|
describe('getFileExtensionLabel', () => {
|
|
31
31
|
it('prefers the filename extension when present', () => {
|
|
32
32
|
expect(getFileExtensionLabel(undefined, 'notes.pdf')).toBe('PDF')
|
|
33
|
-
expect(getFileExtensionLabel('application/pdf', 'oops.docx')).toBe(
|
|
33
|
+
expect(getFileExtensionLabel('application/pdf', 'oops.docx')).toBe(
|
|
34
|
+
'DOCX'
|
|
35
|
+
)
|
|
34
36
|
})
|
|
35
37
|
|
|
36
38
|
it('falls back to MIME-derived label when filename has no extension', () => {
|
|
@@ -47,9 +49,9 @@ describe('getFileExtensionLabel', () => {
|
|
|
47
49
|
|
|
48
50
|
describe('buildCompactMetaLabel', () => {
|
|
49
51
|
it('joins extension and size with a middle dot', () => {
|
|
50
|
-
expect(
|
|
51
|
-
'
|
|
52
|
-
)
|
|
52
|
+
expect(
|
|
53
|
+
buildCompactMetaLabel('application/pdf', 'notes.pdf', 388_658)
|
|
54
|
+
).toBe('PDF · 379.5 KB')
|
|
53
55
|
})
|
|
54
56
|
|
|
55
57
|
it('drops the size when missing', () => {
|
|
@@ -9,7 +9,9 @@ import React from 'react'
|
|
|
9
9
|
*/
|
|
10
10
|
export const StoryPage: React.FC<{ children: React.ReactNode }> = ({
|
|
11
11
|
children,
|
|
12
|
-
}) =>
|
|
12
|
+
}) => (
|
|
13
|
+
<div className="min-h-screen w-full bg-[#F9F7F4] p-12">{children}</div>
|
|
14
|
+
)
|
|
13
15
|
|
|
14
16
|
export const StoryHeading: React.FC<{
|
|
15
17
|
title: string
|
|
@@ -46,11 +46,7 @@ export const Gallery: StoryFn = () => (
|
|
|
46
46
|
<MessageBubble.Received text={SHORT} className="w-fit" />
|
|
47
47
|
</Row>
|
|
48
48
|
<Row label="Received — with header (sender name)" align="start">
|
|
49
|
-
<MessageBubble.Received
|
|
50
|
-
header="Brie"
|
|
51
|
-
text={LONG}
|
|
52
|
-
className="w-fit max-w-[520px]"
|
|
53
|
-
/>
|
|
49
|
+
<MessageBubble.Received header="Brie" text={LONG} className="w-fit max-w-[520px]" />
|
|
54
50
|
</Row>
|
|
55
51
|
<Row label="Received — no tail" align="start">
|
|
56
52
|
<MessageBubble.Received text={SHORT} tail={false} className="w-fit" />
|
|
@@ -46,7 +46,9 @@ describe('MessageBubble', () => {
|
|
|
46
46
|
})
|
|
47
47
|
|
|
48
48
|
it('applies the sent and received surface treatments from theme vars', () => {
|
|
49
|
-
const { rerender } = renderWithProviders(
|
|
49
|
+
const { rerender } = renderWithProviders(
|
|
50
|
+
<MessageBubble.Sent text="mine" />
|
|
51
|
+
)
|
|
50
52
|
const sent = screen.getByText('mine').parentElement as HTMLElement
|
|
51
53
|
expect(sent.style.backgroundColor).toBe(
|
|
52
54
|
'var(--str-chat__own-message-bubble-background-color, #1e2330)'
|
|
@@ -37,8 +37,7 @@ export const BubbleTail = ({ variant }: { variant: MessageBubbleVariant }) => (
|
|
|
37
37
|
className="pointer-events-none absolute bottom-0 z-[-1] h-[1.4375rem] w-5"
|
|
38
38
|
style={{
|
|
39
39
|
insetInlineEnd: variant === 'sent' ? 'calc(-1 * 0.25rem)' : undefined,
|
|
40
|
-
insetInlineStart:
|
|
41
|
-
variant === 'received' ? 'calc(-1 * 0.25rem)' : undefined,
|
|
40
|
+
insetInlineStart: variant === 'received' ? 'calc(-1 * 0.25rem)' : undefined,
|
|
42
41
|
transform: variant === 'received' ? 'scaleX(-1)' : undefined,
|
|
43
42
|
transformOrigin: 'center',
|
|
44
43
|
backgroundColor: BG_VAR_BY_VARIANT[variant],
|
|
@@ -167,11 +167,7 @@ export const ImagePresentation: StoryFn = () => (
|
|
|
167
167
|
<tr key={side}>
|
|
168
168
|
<RowLabel>{side}</RowLabel>
|
|
169
169
|
<Cell>
|
|
170
|
-
<Card
|
|
171
|
-
{...PRESENTATION_BASE}
|
|
172
|
-
images={[IMAGE]}
|
|
173
|
-
imagePresentation="hero"
|
|
174
|
-
/>
|
|
170
|
+
<Card {...PRESENTATION_BASE} images={[IMAGE]} imagePresentation="hero" />
|
|
175
171
|
</Cell>
|
|
176
172
|
<Cell>
|
|
177
173
|
<Card
|
|
@@ -237,10 +233,7 @@ export const FooterLayout: StoryFn = () => (
|
|
|
237
233
|
<tr key={side}>
|
|
238
234
|
<RowLabel>{side}</RowLabel>
|
|
239
235
|
<Cell>
|
|
240
|
-
<Card
|
|
241
|
-
{...FOOTER_BASE}
|
|
242
|
-
actions={[primary('Set up welcome message')]}
|
|
243
|
-
/>
|
|
236
|
+
<Card {...FOOTER_BASE} actions={[primary('Set up welcome message')]} />
|
|
244
237
|
</Cell>
|
|
245
238
|
<Cell>
|
|
246
239
|
<Card
|
|
@@ -363,11 +356,7 @@ export const ContentEdges: StoryFn = () => (
|
|
|
363
356
|
/>
|
|
364
357
|
</Cell>
|
|
365
358
|
<Cell>
|
|
366
|
-
<Card
|
|
367
|
-
{...OFFICIAL_WELCOME}
|
|
368
|
-
subtitle={undefined}
|
|
369
|
-
actions={undefined}
|
|
370
|
-
/>
|
|
359
|
+
<Card {...OFFICIAL_WELCOME} subtitle={undefined} actions={undefined} />
|
|
371
360
|
</Cell>
|
|
372
361
|
<Cell>
|
|
373
362
|
<Card {...OFFICIAL_WELCOME} groupPosition="middle" />
|
|
@@ -20,9 +20,7 @@ describe('RichCard', () => {
|
|
|
20
20
|
)
|
|
21
21
|
expect(await screen.findByTestId('rich-card')).toBeInTheDocument()
|
|
22
22
|
expect(screen.getByText('Set up your welcome message')).toBeInTheDocument()
|
|
23
|
-
expect(
|
|
24
|
-
screen.getByText('Automatically greet new followers.')
|
|
25
|
-
).toBeInTheDocument()
|
|
23
|
+
expect(screen.getByText('Automatically greet new followers.')).toBeInTheDocument()
|
|
26
24
|
})
|
|
27
25
|
|
|
28
26
|
it('renders a single hero image for the hero presentation', async () => {
|
|
@@ -99,11 +97,7 @@ describe('RichCard', () => {
|
|
|
99
97
|
rerender(
|
|
100
98
|
<RichCard.Received
|
|
101
99
|
title="Collection"
|
|
102
|
-
images={[
|
|
103
|
-
'https://cdn.example.com/a.jpg',
|
|
104
|
-
'https://cdn.example.com/b.jpg',
|
|
105
|
-
'https://cdn.example.com/c.jpg',
|
|
106
|
-
]}
|
|
100
|
+
images={['https://cdn.example.com/a.jpg', 'https://cdn.example.com/b.jpg', 'https://cdn.example.com/c.jpg']}
|
|
107
101
|
imagePresentation="fan"
|
|
108
102
|
/>
|
|
109
103
|
)
|
|
@@ -120,11 +114,7 @@ describe('RichCard', () => {
|
|
|
120
114
|
|
|
121
115
|
it('drops a failed hero image to the placeholder rather than a broken image', async () => {
|
|
122
116
|
const { container } = renderWithProviders(
|
|
123
|
-
<RichCard.Received
|
|
124
|
-
title="Welcome"
|
|
125
|
-
images={[HERO]}
|
|
126
|
-
imagePresentation="hero"
|
|
127
|
-
/>
|
|
117
|
+
<RichCard.Received title="Welcome" images={[HERO]} imagePresentation="hero" />
|
|
128
118
|
)
|
|
129
119
|
const img = await screen.findByRole('img')
|
|
130
120
|
// The hero errors → useChinPalette clears the URL → CardThumbnail falls
|
|
@@ -193,9 +183,7 @@ describe('RichCard', () => {
|
|
|
193
183
|
/>
|
|
194
184
|
)
|
|
195
185
|
// Primary → stacked, full-width button.
|
|
196
|
-
expect(await screen.findByText('Set up welcome message')).toHaveClass(
|
|
197
|
-
'w-full'
|
|
198
|
-
)
|
|
186
|
+
expect(await screen.findByText('Set up welcome message')).toHaveClass('w-full')
|
|
199
187
|
|
|
200
188
|
rerender(
|
|
201
189
|
<RichCard.Received
|
|
@@ -21,8 +21,7 @@ export interface RichCardBodyProps {
|
|
|
21
21
|
// Mirror LinkAttachment's `CardBody` type scales so the two cards read
|
|
22
22
|
// identically (14/20 medium title, 12/16 subtitle). Colour is always supplied
|
|
23
23
|
// via `chinTextColor`, so no per-variant text class is needed here.
|
|
24
|
-
const TITLE_CLASS =
|
|
25
|
-
'line-clamp-2 text-[14px] font-medium leading-5 tracking-[0.28px]'
|
|
24
|
+
const TITLE_CLASS = 'line-clamp-2 text-[14px] font-medium leading-5 tracking-[0.28px]'
|
|
26
25
|
const SUBTITLE_CLASS = 'text-[12px] leading-4 tracking-[0.24px]'
|
|
27
26
|
|
|
28
27
|
/**
|
|
@@ -110,10 +109,7 @@ const RichCardBody: React.FC<RichCardBodyProps> = ({
|
|
|
110
109
|
{hasTitle && (
|
|
111
110
|
<div className="flex min-w-0 items-center gap-2">
|
|
112
111
|
{appIcon ? <span className="shrink-0">{appIcon}</span> : null}
|
|
113
|
-
<p
|
|
114
|
-
className={classNames('min-w-0', TITLE_CLASS)}
|
|
115
|
-
style={chinTextStyle}
|
|
116
|
-
>
|
|
112
|
+
<p className={classNames('min-w-0', TITLE_CLASS)} style={chinTextStyle}>
|
|
117
113
|
{title}
|
|
118
114
|
</p>
|
|
119
115
|
</div>
|
|
@@ -143,11 +139,7 @@ const RichCardBody: React.FC<RichCardBodyProps> = ({
|
|
|
143
139
|
) : (
|
|
144
140
|
<>
|
|
145
141
|
{textBlock}
|
|
146
|
-
<RichCardFooter
|
|
147
|
-
surface={ctaSurface}
|
|
148
|
-
actions={actions}
|
|
149
|
-
layout="stacked"
|
|
150
|
-
/>
|
|
142
|
+
<RichCardFooter surface={ctaSurface} actions={actions} layout="stacked" />
|
|
151
143
|
</>
|
|
152
144
|
)}
|
|
153
145
|
</div>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { resolveRichCardImagePresentation } from '@linktr.ee/messaging-taxonomy'
|
|
2
2
|
import React from 'react'
|
|
3
3
|
|
|
4
|
+
|
|
4
5
|
import BubbleTail from '../LinkAttachment/components/_shared/BubbleTail'
|
|
5
6
|
import CardShell from '../LinkAttachment/components/_shared/CardShell'
|
|
6
7
|
import type { LinkAttachmentVariant } from '../LinkAttachment/components/_shared/CardShell'
|
|
@@ -50,7 +51,8 @@ const RichCardCard: React.FC<RichCardCardProps> = ({
|
|
|
50
51
|
thumbnailUrl: isHero ? images[0] : undefined,
|
|
51
52
|
})
|
|
52
53
|
|
|
53
|
-
const tailVisible =
|
|
54
|
+
const tailVisible =
|
|
55
|
+
groupPosition === 'single' || groupPosition === 'end'
|
|
54
56
|
|
|
55
57
|
return (
|
|
56
58
|
<BubbleTail
|
|
@@ -2,6 +2,7 @@ import type { RichCardImagePresentation } from '@linktr.ee/messaging-taxonomy'
|
|
|
2
2
|
import classNames from 'classnames'
|
|
3
3
|
import React from 'react'
|
|
4
4
|
|
|
5
|
+
|
|
5
6
|
import { optimizeMessagingAttachmentUrl } from '../../utils/cdnImageUrl'
|
|
6
7
|
// Reuse LinkAttachment's card chrome primitives — the rich card is a sibling
|
|
7
8
|
// renderer over the same building blocks, not a from-scratch card.
|
|
@@ -84,8 +85,9 @@ const RichCardFan: React.FC<{
|
|
|
84
85
|
.map((src, index) => ({ src, index }))
|
|
85
86
|
.filter(({ index }) => !failed[index])
|
|
86
87
|
|
|
87
|
-
const count = (
|
|
88
|
-
|
|
88
|
+
const count = (
|
|
89
|
+
survivors.length >= 3 ? 3 : survivors.length === 2 ? 2 : 1
|
|
90
|
+
) as 1 | 2 | 3
|
|
89
91
|
const rotations = FAN_ROTATION[count]
|
|
90
92
|
const offsets = FAN_OFFSET_PX[count]
|
|
91
93
|
const zorder = FAN_ZORDER[count]
|
|
@@ -1,131 +1,102 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from 'vitest'
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
2
|
|
|
3
|
-
import { renderWithProviders, screen, userEvent } from '../../test/utils'
|
|
3
|
+
import { renderWithProviders, screen, userEvent } from '../../test/utils';
|
|
4
4
|
|
|
5
|
-
import { SearchInput } from './index'
|
|
5
|
+
import { SearchInput } from './index';
|
|
6
6
|
|
|
7
7
|
describe('SearchInput', () => {
|
|
8
8
|
describe('Rendering', () => {
|
|
9
9
|
it('renders with placeholder', () => {
|
|
10
10
|
renderWithProviders(
|
|
11
|
-
<SearchInput
|
|
12
|
-
searchQuery=""
|
|
13
|
-
setSearchQuery={vi.fn()}
|
|
14
|
-
placeholder="Search messages..."
|
|
11
|
+
<SearchInput
|
|
12
|
+
searchQuery=""
|
|
13
|
+
setSearchQuery={vi.fn()}
|
|
14
|
+
placeholder="Search messages..."
|
|
15
15
|
/>
|
|
16
|
-
)
|
|
17
|
-
expect(
|
|
18
|
-
|
|
19
|
-
).toBeInTheDocument()
|
|
20
|
-
})
|
|
16
|
+
);
|
|
17
|
+
expect(screen.getByPlaceholderText('Search messages...')).toBeInTheDocument();
|
|
18
|
+
});
|
|
21
19
|
|
|
22
20
|
it('displays the current value', () => {
|
|
23
21
|
renderWithProviders(
|
|
24
|
-
<SearchInput
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const input = screen.getByRole('textbox')
|
|
31
|
-
expect(input).toHaveValue('test query')
|
|
32
|
-
})
|
|
33
|
-
})
|
|
22
|
+
<SearchInput searchQuery="test query" setSearchQuery={vi.fn()} placeholder="Search" />
|
|
23
|
+
);
|
|
24
|
+
const input = screen.getByRole('textbox');
|
|
25
|
+
expect(input).toHaveValue('test query');
|
|
26
|
+
});
|
|
27
|
+
});
|
|
34
28
|
|
|
35
29
|
describe('User Interaction', () => {
|
|
36
30
|
it('calls setSearchQuery when user types', async () => {
|
|
37
|
-
const handleChange = vi.fn()
|
|
38
|
-
const user = userEvent.setup()
|
|
39
|
-
|
|
31
|
+
const handleChange = vi.fn();
|
|
32
|
+
const user = userEvent.setup();
|
|
33
|
+
|
|
40
34
|
renderWithProviders(
|
|
41
|
-
<SearchInput
|
|
42
|
-
|
|
43
|
-
setSearchQuery={handleChange}
|
|
44
|
-
placeholder="Search"
|
|
45
|
-
/>
|
|
46
|
-
)
|
|
35
|
+
<SearchInput searchQuery="" setSearchQuery={handleChange} placeholder="Search" />
|
|
36
|
+
);
|
|
47
37
|
|
|
48
|
-
const input = screen.getByRole('textbox')
|
|
49
|
-
await user.type(input, 'hello')
|
|
38
|
+
const input = screen.getByRole('textbox');
|
|
39
|
+
await user.type(input, 'hello');
|
|
50
40
|
|
|
51
|
-
expect(handleChange).toHaveBeenCalledTimes(5) // Once per character
|
|
52
|
-
})
|
|
53
|
-
})
|
|
41
|
+
expect(handleChange).toHaveBeenCalledTimes(5); // Once per character
|
|
42
|
+
});
|
|
43
|
+
});
|
|
54
44
|
|
|
55
45
|
describe('Clear Functionality', () => {
|
|
56
46
|
it('shows clear button when there is a value', () => {
|
|
57
47
|
renderWithProviders(
|
|
58
|
-
<SearchInput
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const clearButton = screen.getByRole('button', { name: /clear/i })
|
|
66
|
-
expect(clearButton).toBeInTheDocument()
|
|
67
|
-
})
|
|
48
|
+
<SearchInput searchQuery="test" setSearchQuery={vi.fn()} placeholder="Search" />
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const clearButton = screen.getByRole('button', { name: /clear/i });
|
|
52
|
+
expect(clearButton).toBeInTheDocument();
|
|
53
|
+
});
|
|
68
54
|
|
|
69
55
|
it('does not show clear button when value is empty', () => {
|
|
70
56
|
renderWithProviders(
|
|
71
|
-
<SearchInput
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const clearButton = screen.queryByLabelText(/clear/i)
|
|
79
|
-
expect(clearButton).not.toBeInTheDocument()
|
|
80
|
-
})
|
|
57
|
+
<SearchInput searchQuery="" setSearchQuery={vi.fn()} placeholder="Search" />
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const clearButton = screen.queryByLabelText(/clear/i);
|
|
61
|
+
expect(clearButton).not.toBeInTheDocument();
|
|
62
|
+
});
|
|
81
63
|
|
|
82
64
|
it('calls setSearchQuery with empty string when clear is clicked', async () => {
|
|
83
|
-
const handleChange = vi.fn()
|
|
84
|
-
const user = userEvent.setup()
|
|
85
|
-
|
|
65
|
+
const handleChange = vi.fn();
|
|
66
|
+
const user = userEvent.setup();
|
|
67
|
+
|
|
86
68
|
renderWithProviders(
|
|
87
|
-
<SearchInput
|
|
88
|
-
|
|
89
|
-
setSearchQuery={handleChange}
|
|
90
|
-
placeholder="Search"
|
|
91
|
-
/>
|
|
92
|
-
)
|
|
69
|
+
<SearchInput searchQuery="test" setSearchQuery={handleChange} placeholder="Search" />
|
|
70
|
+
);
|
|
93
71
|
|
|
94
|
-
const clearButton = screen.getByRole('button', { name: /clear/i })
|
|
95
|
-
await user.click(clearButton)
|
|
72
|
+
const clearButton = screen.getByRole('button', { name: /clear/i });
|
|
73
|
+
await user.click(clearButton);
|
|
96
74
|
|
|
97
|
-
expect(handleChange).toHaveBeenCalledWith('')
|
|
98
|
-
})
|
|
99
|
-
})
|
|
75
|
+
expect(handleChange).toHaveBeenCalledWith('');
|
|
76
|
+
});
|
|
77
|
+
});
|
|
100
78
|
|
|
101
79
|
describe('Accessibility', () => {
|
|
102
80
|
it('clear button has accessible label', () => {
|
|
103
81
|
renderWithProviders(
|
|
104
|
-
<SearchInput
|
|
105
|
-
|
|
106
|
-
setSearchQuery={vi.fn()}
|
|
107
|
-
placeholder="Search"
|
|
108
|
-
/>
|
|
109
|
-
)
|
|
82
|
+
<SearchInput searchQuery="test" setSearchQuery={vi.fn()} placeholder="Search" />
|
|
83
|
+
);
|
|
110
84
|
|
|
111
|
-
const clearButton = screen.getByRole('button', { name: /clear search/i })
|
|
112
|
-
expect(clearButton).toBeInTheDocument()
|
|
113
|
-
})
|
|
85
|
+
const clearButton = screen.getByRole('button', { name: /clear search/i });
|
|
86
|
+
expect(clearButton).toBeInTheDocument();
|
|
87
|
+
});
|
|
114
88
|
|
|
115
89
|
it('is keyboard navigable', async () => {
|
|
116
|
-
const user = userEvent.setup()
|
|
117
|
-
|
|
90
|
+
const user = userEvent.setup();
|
|
91
|
+
|
|
118
92
|
renderWithProviders(
|
|
119
|
-
<SearchInput
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
})
|
|
130
|
-
})
|
|
131
|
-
})
|
|
93
|
+
<SearchInput searchQuery="" setSearchQuery={vi.fn()} placeholder="Search" />
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
await user.tab();
|
|
97
|
+
const input = screen.getByRole('textbox');
|
|
98
|
+
expect(input).toHaveFocus();
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
@@ -19,9 +19,7 @@ export const useChannelStar = (channel?: Channel) => {
|
|
|
19
19
|
|
|
20
20
|
const handleMemberUpdate = (event: Event) => {
|
|
21
21
|
setIsChannelStarred(
|
|
22
|
-
event?.member
|
|
23
|
-
? !!event.member.pinned_at
|
|
24
|
-
: !!channel.state.membership?.pinned_at
|
|
22
|
+
event?.member ? !!event.member.pinned_at : !!channel.state.membership?.pinned_at
|
|
25
23
|
)
|
|
26
24
|
}
|
|
27
25
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { useMessagingContext } from '../providers/MessagingProvider'
|
|
2
|
-
import type { MessagingContextValue } from '../providers/MessagingProvider'
|
|
1
|
+
import { useMessagingContext } from '../providers/MessagingProvider';
|
|
2
|
+
import type { MessagingContextValue } from '../providers/MessagingProvider';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Hook to access messaging service and state
|
|
6
6
|
*/
|
|
7
7
|
export const useMessaging = (): MessagingContextValue => {
|
|
8
|
-
return useMessagingContext()
|
|
9
|
-
}
|
|
8
|
+
return useMessagingContext();
|
|
9
|
+
};
|
|
@@ -15,7 +15,7 @@ export const storyUsers = {
|
|
|
15
15
|
|
|
16
16
|
export type StoryRole = keyof typeof storyUsers
|
|
17
17
|
|
|
18
|
-
export type StoryUser =
|
|
18
|
+
export type StoryUser = typeof storyUsers[keyof typeof storyUsers]
|
|
19
19
|
|
|
20
20
|
const DEFAULT_ROLE: StoryRole = 'creator'
|
|
21
21
|
|
|
@@ -34,4 +34,4 @@ export const currentUserArgType: ArgTypes<{ currentUser?: StoryUser }> = {
|
|
|
34
34
|
},
|
|
35
35
|
table: { defaultValue: { summary: DEFAULT_ROLE } },
|
|
36
36
|
},
|
|
37
|
-
} satisfies ArgTypes<{ currentUser?: StoryUser }>
|
|
37
|
+
} satisfies ArgTypes<{ currentUser?: StoryUser }>
|
package/src/stories/mocks.tsx
CHANGED
|
@@ -38,10 +38,7 @@ export const MockChatProviderWithChannels: React.FC<{
|
|
|
38
38
|
// Create mock channels
|
|
39
39
|
const mockChannels = Array.from({ length: channelCount }, (_, i) => {
|
|
40
40
|
const participant = mockParticipants[i % mockParticipants.length]
|
|
41
|
-
const eventListeners = new Map<
|
|
42
|
-
string,
|
|
43
|
-
Set<(...args: unknown[]) => void>
|
|
44
|
-
>()
|
|
41
|
+
const eventListeners = new Map<string, Set<(...args: unknown[]) => void>>()
|
|
45
42
|
|
|
46
43
|
const latestMessage = {
|
|
47
44
|
id: `msg-${i}-1`,
|
package/src/styles.css
CHANGED
|
@@ -665,9 +665,7 @@
|
|
|
665
665
|
line-height: 1.4;
|
|
666
666
|
color: var(--str-chat__text-low-emphasis-color, rgba(0, 0, 0, 0.5));
|
|
667
667
|
}
|
|
668
|
-
.str-chat__li
|
|
669
|
-
.str-chat__message--me
|
|
670
|
-
.sent-message-status-row
|
|
668
|
+
.str-chat__li .str-chat__message--me .sent-message-status-row
|
|
671
669
|
.str-chat__message-status {
|
|
672
670
|
grid-area: auto;
|
|
673
671
|
justify-content: inherit;
|
|
@@ -696,9 +694,7 @@
|
|
|
696
694
|
.str-chat__li .str-chat__message--me .sent-message-status-row svg path {
|
|
697
695
|
fill: currentColor;
|
|
698
696
|
}
|
|
699
|
-
.str-chat__li
|
|
700
|
-
.str-chat__message--me
|
|
701
|
-
.sent-message-status-row
|
|
697
|
+
.str-chat__li .str-chat__message--me .sent-message-status-row
|
|
702
698
|
.sent-message-broadcast-badge {
|
|
703
699
|
display: inline-flex;
|
|
704
700
|
width: 16px;
|
|
@@ -710,31 +706,20 @@
|
|
|
710
706
|
background: var(--sent-message-broadcast-badge-color);
|
|
711
707
|
color: #fff;
|
|
712
708
|
}
|
|
713
|
-
.str-chat__li
|
|
714
|
-
.
|
|
715
|
-
.sent-message-status-row
|
|
716
|
-
.sent-message-broadcast-badge
|
|
717
|
-
svg {
|
|
709
|
+
.str-chat__li .str-chat__message--me .sent-message-status-row
|
|
710
|
+
.sent-message-broadcast-badge svg {
|
|
718
711
|
width: 10px;
|
|
719
712
|
height: 10px;
|
|
720
713
|
}
|
|
721
|
-
.str-chat__li
|
|
722
|
-
.
|
|
723
|
-
.sent-message-status-row
|
|
724
|
-
.sent-message-broadcast-badge
|
|
725
|
-
svg
|
|
726
|
-
path {
|
|
714
|
+
.str-chat__li .str-chat__message--me .sent-message-status-row
|
|
715
|
+
.sent-message-broadcast-badge svg path {
|
|
727
716
|
fill: currentColor;
|
|
728
717
|
}
|
|
729
|
-
.str-chat__li
|
|
730
|
-
.str-chat__message--me
|
|
731
|
-
.sent-message-status-row
|
|
718
|
+
.str-chat__li .str-chat__message--me .sent-message-status-row
|
|
732
719
|
.sent-message-broadcast-name {
|
|
733
720
|
color: var(--sent-message-broadcast-name-color);
|
|
734
721
|
}
|
|
735
|
-
.str-chat__li
|
|
736
|
-
.str-chat__message--me
|
|
737
|
-
.sent-message-status-row
|
|
722
|
+
.str-chat__li .str-chat__message--me .sent-message-status-row
|
|
738
723
|
.sent-message-broadcast-link
|
|
739
724
|
.sent-message-broadcast-name {
|
|
740
725
|
text-decoration: underline;
|
package/src/types.ts
CHANGED
|
@@ -535,4 +535,5 @@ interface MessagingProviderApiKeyProps extends MessagingProviderBaseProps {
|
|
|
535
535
|
* neither.
|
|
536
536
|
*/
|
|
537
537
|
export type MessagingProviderProps =
|
|
538
|
-
|
|
538
|
+
| MessagingProviderInjectedClientProps
|
|
539
|
+
| MessagingProviderApiKeyProps
|
|
@@ -13,9 +13,7 @@ describe('optimizeMessagingAttachmentUrl', () => {
|
|
|
13
13
|
|
|
14
14
|
it('does not optimize unrelated CloudFront hosts', () => {
|
|
15
15
|
expect(
|
|
16
|
-
optimizeMessagingAttachmentUrl(
|
|
17
|
-
'https://d111111abcdef8.cloudfront.net/photo.jpg'
|
|
18
|
-
)
|
|
16
|
+
optimizeMessagingAttachmentUrl('https://d111111abcdef8.cloudfront.net/photo.jpg')
|
|
19
17
|
).toBe('https://d111111abcdef8.cloudfront.net/photo.jpg')
|
|
20
18
|
})
|
|
21
19
|
|