@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.
Files changed (91) hide show
  1. package/README.md +4 -4
  2. package/dist/Card-415OfRhw.cjs.map +1 -1
  3. package/dist/{Card-D4PPD5qa.js → Card-DVMzYquF.js} +11 -8
  4. package/dist/Card-DVMzYquF.js.map +1 -0
  5. package/dist/{Card-qS17qnN0.js → Card-DaJtDL7m.js} +15 -6
  6. package/dist/Card-DaJtDL7m.js.map +1 -0
  7. package/dist/Card-Dxk_Ggxw.cjs.map +1 -1
  8. package/dist/LoadingDots-Ct3WmVlT.js.map +1 -1
  9. package/dist/LoadingDots-D9GGphgM.cjs.map +1 -1
  10. package/dist/{index-DGmVyxHy.js → index-BOBIiAuD.js} +87 -62
  11. package/dist/index-BOBIiAuD.js.map +1 -0
  12. package/dist/index-D7A8U8GQ.cjs.map +1 -1
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.js +1 -1
  15. package/package.json +3 -2
  16. package/src/components/ActionButton/ActionButton.stories.tsx +0 -2
  17. package/src/components/ActionButton/ActionButton.test.tsx +60 -55
  18. package/src/components/ActionButton/index.tsx +13 -14
  19. package/src/components/AttachmentCard/AttachmentCard.stories.tsx +1 -3
  20. package/src/components/AttachmentCard/MediaPlayer.tsx +9 -7
  21. package/src/components/AttachmentCard/index.tsx +6 -2
  22. package/src/components/Avatar/Avatar.stories.tsx +17 -5
  23. package/src/components/Avatar/index.tsx +13 -23
  24. package/src/components/ChannelView.test.tsx +6 -10
  25. package/src/components/CustomMessage/CustomMessage.stories.tsx +7 -2
  26. package/src/components/CustomMessage/CustomMessage.test.tsx +5 -3
  27. package/src/components/CustomMessage/LockedAttachment/LockedAttachment.test.tsx +5 -4
  28. package/src/components/CustomMessage/LockedAttachment/components/Text/Card.tsx +2 -4
  29. package/src/components/CustomMessage/LockedAttachment/components/_shared/CardBody.tsx +9 -6
  30. package/src/components/CustomMessage/LockedAttachment/components/_shared/CardThumbnail.tsx +1 -3
  31. package/src/components/CustomMessage/LockedAttachment/components/_shared/GalleryThumbnail.tsx +1 -4
  32. package/src/components/CustomMessage/LockedAttachment/components/_shared/PurchaseStatusBadge.tsx +0 -1
  33. package/src/components/CustomMessage/LockedAttachment/components/_shared/SingleThumbnail.tsx +1 -4
  34. package/src/components/CustomMessage/LockedAttachment/components/_shared/SkeletonTextLines.tsx +7 -2
  35. package/src/components/CustomMessage/LockedAttachment/types.ts +4 -3
  36. package/src/components/CustomMessage/MessageAttachmentConversations.stories.tsx +89 -86
  37. package/src/components/CustomMessage/MessageVoteButtons.tsx +4 -1
  38. package/src/components/CustomMessage/SentMessageDeliveryStatus.test.tsx +13 -10
  39. package/src/components/CustomMessage/SentMessageDeliveryStatus.tsx +1 -4
  40. package/src/components/CustomMessage/StreamAttachmentMessage.stories.tsx +4 -4
  41. package/src/components/CustomMessage/StreamAttachmentMessage.test.tsx +6 -7
  42. package/src/components/CustomMessage/context.tsx +7 -2
  43. package/src/components/CustomMessage/index.tsx +1 -4
  44. package/src/components/CustomMessageInput/CustomMessageInput.stories.tsx +33 -11
  45. package/src/components/CustomMessageInput/CustomMessageInput.test.tsx +3 -3
  46. package/src/components/CustomSystemMessage/CustomSystemMessage.test.tsx +6 -9
  47. package/src/components/CustomTypingIndicator/CustomTypingIndicator.stories.tsx +4 -1
  48. package/src/components/IconButton/index.tsx +21 -17
  49. package/src/components/LinkAttachment/LinkAttachment.test.tsx +4 -1
  50. package/src/components/LinkAttachment/components/_shared/CardCta.tsx +4 -1
  51. package/src/components/Loading/Loading.stories.tsx +1 -4
  52. package/src/components/MediaMessage/MediaMessage.stories.tsx +6 -18
  53. package/src/components/MediaMessage/MediaMessage.test.tsx +30 -16
  54. package/src/components/MediaMessage/index.tsx +51 -14
  55. package/src/components/MessageAttachment/Audio/AudioAttachment.stories.tsx +4 -2
  56. package/src/components/MessageAttachment/File/FileAttachment.stories.tsx +3 -1
  57. package/src/components/MessageAttachment/Image/ImageAttachment.stories.tsx +15 -4
  58. package/src/components/MessageAttachment/Image/index.tsx +6 -12
  59. package/src/components/MessageAttachment/MessageAttachment.test.tsx +59 -27
  60. package/src/components/MessageAttachment/Pdf/PdfAttachment.stories.tsx +3 -1
  61. package/src/components/MessageAttachment/Pdf/index.tsx +2 -3
  62. package/src/components/MessageAttachment/Video/VideoAttachment.stories.tsx +11 -3
  63. package/src/components/MessageAttachment/Video/index.tsx +9 -14
  64. package/src/components/MessageAttachment/_shared/CompactDocumentRow.tsx +1 -3
  65. package/src/components/MessageAttachment/_shared/ImageViewer.tsx +1 -3
  66. package/src/components/MessageAttachment/_shared/MediaStackGrid.tsx +5 -1
  67. package/src/components/MessageAttachment/_shared/PdfViewer.tsx +1 -3
  68. package/src/components/MessageAttachment/_shared/VideoViewer.tsx +1 -3
  69. package/src/components/MessageAttachment/_shared/fileMeta.test.ts +4 -6
  70. package/src/components/MessageAttachment/stories/StoryTable.tsx +1 -3
  71. package/src/components/MessageBubble/MessageBubble.stories.tsx +5 -1
  72. package/src/components/MessageBubble/MessageBubble.test.tsx +1 -3
  73. package/src/components/MessageBubble/components/_shared/bubbleTail.tsx +2 -1
  74. package/src/components/MessagingShell/LoadingState.stories.tsx +0 -1
  75. package/src/components/RichCard/RichCard.stories.tsx +14 -3
  76. package/src/components/RichCard/RichCard.test.tsx +16 -4
  77. package/src/components/RichCard/RichCardBody.tsx +11 -3
  78. package/src/components/RichCard/RichCardCard.tsx +1 -3
  79. package/src/components/RichCard/RichCardImages.tsx +2 -4
  80. package/src/components/SearchInput/SearchInput.stories.tsx +0 -1
  81. package/src/components/SearchInput/SearchInput.test.tsx +94 -65
  82. package/src/hooks/useChannelStar.ts +3 -1
  83. package/src/hooks/useMessaging.ts +4 -4
  84. package/src/stories/decorators/storyUser.tsx +2 -2
  85. package/src/stories/mocks.tsx +4 -1
  86. package/src/styles.css +23 -8
  87. package/src/types.ts +1 -2
  88. package/src/utils/cdnImageUrl.test.ts +3 -1
  89. package/dist/Card-D4PPD5qa.js.map +0 -1
  90. package/dist/Card-qS17qnN0.js.map +0 -1
  91. 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
- buildCompactMetaLabel('application/pdf', 'notes.pdf', 388_658)
54
- ).toBe('PDF · 379.5 KB')
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 header="Brie" text={LONG} className="w-fit max-w-[520px]" />
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: variant === 'received' ? 'calc(-1 * 0.25rem)' : undefined,
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],
@@ -24,4 +24,3 @@ const Template: StoryFn<ComponentProps> = () => {
24
24
  }
25
25
 
26
26
  export const Default: StoryFn<ComponentProps> = Template.bind({})
27
-
@@ -167,7 +167,11 @@ export const ImagePresentation: StoryFn = () => (
167
167
  <tr key={side}>
168
168
  <RowLabel>{side}</RowLabel>
169
169
  <Cell>
170
- <Card {...PRESENTATION_BASE} images={[IMAGE]} imagePresentation="hero" />
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 {...FOOTER_BASE} actions={[primary('Set up welcome message')]} />
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 {...OFFICIAL_WELCOME} subtitle={undefined} actions={undefined} />
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(screen.getByText('Automatically greet new followers.')).toBeInTheDocument()
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={['https://cdn.example.com/a.jpg', 'https://cdn.example.com/b.jpg', 'https://cdn.example.com/c.jpg']}
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 title="Welcome" images={[HERO]} imagePresentation="hero" />
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('w-full')
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 = 'line-clamp-2 text-[14px] font-medium leading-5 tracking-[0.28px]'
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 className={classNames('min-w-0', TITLE_CLASS)} style={chinTextStyle}>
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 surface={ctaSurface} actions={actions} layout="stacked" />
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
- survivors.length >= 3 ? 3 : survivors.length === 2 ? 2 : 1
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]
@@ -31,4 +31,3 @@ export const CustomPlaceholder: StoryFn<ComponentProps> = Template.bind({})
31
31
  CustomPlaceholder.args = {
32
32
  placeholder: 'Find a conversation...',
33
33
  }
34
-
@@ -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(screen.getByPlaceholderText('Search messages...')).toBeInTheDocument();
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 searchQuery="test query" setSearchQuery={vi.fn()} placeholder="Search" />
23
- );
24
- const input = screen.getByRole('textbox');
25
- expect(input).toHaveValue('test query');
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 searchQuery="" setSearchQuery={handleChange} placeholder="Search" />
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); // Once per character
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 searchQuery="test" setSearchQuery={vi.fn()} placeholder="Search" />
49
- );
50
-
51
- const clearButton = screen.getByRole('button', { name: /clear/i });
52
- expect(clearButton).toBeInTheDocument();
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 searchQuery="" setSearchQuery={vi.fn()} placeholder="Search" />
58
- );
59
-
60
- const clearButton = screen.queryByLabelText(/clear/i);
61
- expect(clearButton).not.toBeInTheDocument();
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 searchQuery="test" setSearchQuery={handleChange} placeholder="Search" />
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 searchQuery="test" setSearchQuery={vi.fn()} placeholder="Search" />
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 ? !!event.member.pinned_at : !!channel.state.membership?.pinned_at
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 }>
@@ -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<string, Set<(...args: unknown[]) => void>>()
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 .str-chat__message--me .sent-message-status-row
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 .str-chat__message--me .sent-message-status-row
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 .str-chat__message--me .sent-message-status-row
710
- .sent-message-broadcast-badge svg {
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 .str-chat__message--me .sent-message-status-row
715
- .sent-message-broadcast-badge svg path {
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 .str-chat__message--me .sent-message-status-row
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 .str-chat__message--me .sent-message-status-row
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
- | MessagingProviderInjectedClientProps
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('https://d111111abcdef8.cloudfront.net/photo.jpg')
16
+ optimizeMessagingAttachmentUrl(
17
+ 'https://d111111abcdef8.cloudfront.net/photo.jpg'
18
+ )
17
19
  ).toBe('https://d111111abcdef8.cloudfront.net/photo.jpg')
18
20
  })
19
21