@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.
Files changed (91) hide show
  1. package/README.md +4 -4
  2. package/dist/Card-415OfRhw.cjs.map +1 -1
  3. package/dist/{Card-DVMzYquF.js → Card-D4PPD5qa.js} +8 -11
  4. package/dist/Card-D4PPD5qa.js.map +1 -0
  5. package/dist/Card-Dxk_Ggxw.cjs.map +1 -1
  6. package/dist/{Card-DaJtDL7m.js → Card-qS17qnN0.js} +6 -15
  7. package/dist/Card-qS17qnN0.js.map +1 -0
  8. package/dist/LoadingDots-Ct3WmVlT.js.map +1 -1
  9. package/dist/LoadingDots-D9GGphgM.cjs.map +1 -1
  10. package/dist/index-D7A8U8GQ.cjs.map +1 -1
  11. package/dist/{index-BOBIiAuD.js → index-DGmVyxHy.js} +62 -87
  12. package/dist/index-DGmVyxHy.js.map +1 -0
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.js +1 -1
  15. package/package.json +2 -2
  16. package/src/components/ActionButton/ActionButton.stories.tsx +2 -0
  17. package/src/components/ActionButton/ActionButton.test.tsx +55 -60
  18. package/src/components/ActionButton/index.tsx +14 -13
  19. package/src/components/AttachmentCard/AttachmentCard.stories.tsx +3 -1
  20. package/src/components/AttachmentCard/MediaPlayer.tsx +7 -9
  21. package/src/components/AttachmentCard/index.tsx +2 -6
  22. package/src/components/Avatar/Avatar.stories.tsx +5 -17
  23. package/src/components/Avatar/index.tsx +23 -13
  24. package/src/components/ChannelView.test.tsx +10 -6
  25. package/src/components/CustomMessage/CustomMessage.stories.tsx +2 -7
  26. package/src/components/CustomMessage/CustomMessage.test.tsx +3 -5
  27. package/src/components/CustomMessage/LockedAttachment/LockedAttachment.test.tsx +4 -5
  28. package/src/components/CustomMessage/LockedAttachment/components/Text/Card.tsx +4 -2
  29. package/src/components/CustomMessage/LockedAttachment/components/_shared/CardBody.tsx +6 -9
  30. package/src/components/CustomMessage/LockedAttachment/components/_shared/CardThumbnail.tsx +3 -1
  31. package/src/components/CustomMessage/LockedAttachment/components/_shared/GalleryThumbnail.tsx +4 -1
  32. package/src/components/CustomMessage/LockedAttachment/components/_shared/PurchaseStatusBadge.tsx +1 -0
  33. package/src/components/CustomMessage/LockedAttachment/components/_shared/SingleThumbnail.tsx +4 -1
  34. package/src/components/CustomMessage/LockedAttachment/components/_shared/SkeletonTextLines.tsx +2 -7
  35. package/src/components/CustomMessage/LockedAttachment/types.ts +3 -4
  36. package/src/components/CustomMessage/MessageAttachmentConversations.stories.tsx +86 -89
  37. package/src/components/CustomMessage/MessageVoteButtons.tsx +1 -4
  38. package/src/components/CustomMessage/SentMessageDeliveryStatus.test.tsx +10 -13
  39. package/src/components/CustomMessage/SentMessageDeliveryStatus.tsx +4 -1
  40. package/src/components/CustomMessage/StreamAttachmentMessage.stories.tsx +4 -4
  41. package/src/components/CustomMessage/StreamAttachmentMessage.test.tsx +7 -6
  42. package/src/components/CustomMessage/context.tsx +2 -7
  43. package/src/components/CustomMessage/index.tsx +4 -1
  44. package/src/components/CustomMessageInput/CustomMessageInput.stories.tsx +11 -33
  45. package/src/components/CustomMessageInput/CustomMessageInput.test.tsx +3 -3
  46. package/src/components/CustomSystemMessage/CustomSystemMessage.test.tsx +9 -6
  47. package/src/components/CustomTypingIndicator/CustomTypingIndicator.stories.tsx +1 -4
  48. package/src/components/IconButton/index.tsx +17 -21
  49. package/src/components/LinkAttachment/LinkAttachment.test.tsx +1 -4
  50. package/src/components/LinkAttachment/components/_shared/CardCta.tsx +1 -4
  51. package/src/components/Loading/Loading.stories.tsx +4 -1
  52. package/src/components/MediaMessage/MediaMessage.stories.tsx +18 -6
  53. package/src/components/MediaMessage/MediaMessage.test.tsx +16 -30
  54. package/src/components/MediaMessage/index.tsx +14 -51
  55. package/src/components/MessageAttachment/Audio/AudioAttachment.stories.tsx +2 -4
  56. package/src/components/MessageAttachment/File/FileAttachment.stories.tsx +1 -3
  57. package/src/components/MessageAttachment/Image/ImageAttachment.stories.tsx +4 -15
  58. package/src/components/MessageAttachment/Image/index.tsx +12 -6
  59. package/src/components/MessageAttachment/MessageAttachment.test.tsx +27 -59
  60. package/src/components/MessageAttachment/Pdf/PdfAttachment.stories.tsx +1 -3
  61. package/src/components/MessageAttachment/Pdf/index.tsx +3 -2
  62. package/src/components/MessageAttachment/Video/VideoAttachment.stories.tsx +3 -11
  63. package/src/components/MessageAttachment/Video/index.tsx +14 -9
  64. package/src/components/MessageAttachment/_shared/CompactDocumentRow.tsx +3 -1
  65. package/src/components/MessageAttachment/_shared/ImageViewer.tsx +3 -1
  66. package/src/components/MessageAttachment/_shared/MediaStackGrid.tsx +1 -5
  67. package/src/components/MessageAttachment/_shared/PdfViewer.tsx +3 -1
  68. package/src/components/MessageAttachment/_shared/VideoViewer.tsx +3 -1
  69. package/src/components/MessageAttachment/_shared/fileMeta.test.ts +6 -4
  70. package/src/components/MessageAttachment/stories/StoryTable.tsx +3 -1
  71. package/src/components/MessageBubble/MessageBubble.stories.tsx +1 -5
  72. package/src/components/MessageBubble/MessageBubble.test.tsx +3 -1
  73. package/src/components/MessageBubble/components/_shared/bubbleTail.tsx +1 -2
  74. package/src/components/MessagingShell/LoadingState.stories.tsx +1 -0
  75. package/src/components/RichCard/RichCard.stories.tsx +3 -14
  76. package/src/components/RichCard/RichCard.test.tsx +4 -16
  77. package/src/components/RichCard/RichCardBody.tsx +3 -11
  78. package/src/components/RichCard/RichCardCard.tsx +3 -1
  79. package/src/components/RichCard/RichCardImages.tsx +4 -2
  80. package/src/components/SearchInput/SearchInput.stories.tsx +1 -0
  81. package/src/components/SearchInput/SearchInput.test.tsx +65 -94
  82. package/src/hooks/useChannelStar.ts +1 -3
  83. package/src/hooks/useMessaging.ts +4 -4
  84. package/src/stories/decorators/storyUser.tsx +2 -2
  85. package/src/stories/mocks.tsx +1 -4
  86. package/src/styles.css +8 -23
  87. package/src/types.ts +2 -1
  88. package/src/utils/cdnImageUrl.test.ts +1 -3
  89. package/dist/Card-DVMzYquF.js.map +0 -1
  90. package/dist/Card-DaJtDL7m.js.map +0 -1
  91. 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={items.length > 1 ? `${index + 1} / ${items.length}` : undefined}
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('DOCX')
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(buildCompactMetaLabel('application/pdf', 'notes.pdf', 388_658)).toBe(
51
- 'PDF · 379.5 KB'
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
- }) => <div className="min-h-screen w-full bg-[#F9F7F4] p-12">{children}</div>
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(<MessageBubble.Sent text="mine" />)
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],
@@ -24,3 +24,4 @@ const Template: StoryFn<ComponentProps> = () => {
24
24
  }
25
25
 
26
26
  export const Default: StoryFn<ComponentProps> = Template.bind({})
27
+
@@ -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 = groupPosition === 'single' || groupPosition === 'end'
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 = (survivors.length >= 3 ? 3 : survivors.length === 2 ? 2 : 1) as
88
- 1 | 2 | 3
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]
@@ -31,3 +31,4 @@ export const CustomPlaceholder: StoryFn<ComponentProps> = Template.bind({})
31
31
  CustomPlaceholder.args = {
32
32
  placeholder: 'Find a conversation...',
33
33
  }
34
+
@@ -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
- screen.getByPlaceholderText('Search messages...')
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
- 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
- })
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
- searchQuery=""
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
- 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
- })
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
- searchQuery=""
73
- setSearchQuery={vi.fn()}
74
- placeholder="Search"
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
- searchQuery="test"
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
- searchQuery="test"
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
- 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
- })
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 = (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,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
- .str-chat__message--me
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
- .str-chat__message--me
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
- MessagingProviderInjectedClientProps | MessagingProviderApiKeyProps
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