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