Skip to content

Commit c94fa39

Browse files
committed
refactor!: move remaining component overrides to ComponentsContext
Move Chat.LoadingIndicator, Thread.MessageComposer, ThreadList component overrides, ChannelDetails.ChannelDetailsHeader, Poll component overrides, ImageGallery.ImageGalleryVideoControls, and all ThreadsContext component keys to ComponentsContext. - Strip 7 component keys from ThreadsContextValue - Strip ImageGalleryVideoControls from ImageGalleryContextValue - Remove ChannelDetailsHeader prop from ChannelDetailsBottomSheet - Remove component override props from all Poll components - Update tests to use WithComponents wrapper
1 parent b6c0a96 commit c94fa39

File tree

26 files changed

+252
-194
lines changed

26 files changed

+252
-194
lines changed

package/src/__tests__/offline-support/offline-feature.js

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import { v4 as uuidv4 } from 'uuid';
99

1010
import { ChannelList } from '../../components/ChannelList/ChannelList';
1111
import { Chat } from '../../components/Chat/Chat';
12-
import { useChannelsContext } from '../../contexts/channelsContext/ChannelsContext';
1312
import { WithComponents } from '../../contexts/componentsContext/ComponentsContext';
1413
import { getOrCreateChannelApi } from '../../mock-builders/api/getOrCreateChannel';
1514
import { queryChannelsApi } from '../../mock-builders/api/queryChannels';
@@ -55,8 +54,8 @@ import { BetterSqlite } from '../../test-utils/BetterSqlite';
5554
*/
5655
const ChannelPreviewComponent = ({ channel }) => (
5756
<View accessibilityLabel='list-item' testID={channel.cid}>
58-
<Text>{channel.data.name}</Text>
59-
<Text>{channel.state.messages[0]?.text}</Text>
57+
<Text>{channel.data?.name}</Text>
58+
<Text>{channel.state?.messages?.[0]?.text}</Text>
6059
</View>
6160
);
6261

@@ -209,7 +208,7 @@ export const Generic = () => {
209208
render(
210209
<Chat client={chatClient} enableOfflineSupport>
211210
<WithComponents value={{ Preview: ChannelPreviewComponent }}>
212-
<ChannelList filters={filters} sort={sort} />
211+
<ChannelList filters={filters} sort={sort} swipeActionsEnabled={false} />
213212
</WithComponents>
214213
</Chat>,
215214
);
@@ -516,13 +515,26 @@ export const Generic = () => {
516515
useMockedApis(chatClient, [getOrCreateChannelApi(newChannel)]);
517516

518517
await act(() => dispatchNotificationMessageNewEvent(chatClient, newChannel.channel));
518+
519+
// Verify the new channel appears on the UI
519520
await waitFor(() => {
520521
const channelIdsOnUI = screen
521522
.queryAllByLabelText('list-item')
522523
.map((node) => node._fiber.pendingProps.testID);
523524
expect(channelIdsOnUI.includes(newChannel.channel.cid)).toBeTruthy();
524525
});
525-
await expectAllChannelsWithStateToBeInDB(screen.queryAllByLabelText);
526+
527+
// Verify the new channel and its state are persisted in the DB
528+
await waitFor(async () => {
529+
const channelsRows = await BetterSqlite.selectFromTable('channels');
530+
const messagesRows = await BetterSqlite.selectFromTable('messages');
531+
532+
expect(channelsRows.length).toBe(channels.length);
533+
expect(messagesRows.length).toBe(allMessages.length);
534+
535+
const matchingChannelRow = channelsRows.filter((c) => c.id === newChannel.channel.id);
536+
expect(matchingChannelRow.length).toBe(1);
537+
});
526538
});
527539

528540
it('should update a message in database', async () => {
@@ -695,15 +707,18 @@ export const Generic = () => {
695707
const newChannel = createChannel();
696708
useMockedApis(chatClient, [getOrCreateChannelApi(newChannel)]);
697709

698-
act(() => dispatchNotificationAddedToChannel(chatClient, newChannel.channel));
710+
await act(() => dispatchNotificationAddedToChannel(chatClient, newChannel.channel));
699711

700-
await waitFor(async () => {
712+
// Verify the new channel appears on the UI
713+
await waitFor(() => {
701714
const channelIdsOnUI = screen
702715
.queryAllByLabelText('list-item')
703716
.map((node) => node._fiber.pendingProps.testID);
704717
expect(channelIdsOnUI.includes(newChannel.channel.cid)).toBeTruthy();
718+
});
705719

706-
await expectCIDsOnUIToBeInDB(screen.queryAllByLabelText);
720+
// Verify the new channel is persisted in the DB
721+
await waitFor(async () => {
707722
const channelsRows = await BetterSqlite.selectFromTable('channels');
708723
const matchingChannelsRows = channelsRows.filter((c) => c.id === newChannel.channel.id);
709724

package/src/components/ChannelList/__tests__/ChannelList.test.js

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ import {
1212
} from '@testing-library/react-native';
1313

1414
import { useChannelsContext } from '../../../contexts/channelsContext/ChannelsContext';
15-
import { WithComponents } from '../../../contexts/componentsContext/ComponentsContext';
15+
import {
16+
useComponentsContext,
17+
WithComponents,
18+
} from '../../../contexts/componentsContext/ComponentsContext';
1619
import { getOrCreateChannelApi } from '../../../mock-builders/api/getOrCreateChannel';
1720

1821
import { queryChannelsApi } from '../../../mock-builders/api/queryChannels';
@@ -72,6 +75,16 @@ const RefreshingProbe = () => {
7275

7376
const ChannelPreviewContent = ({ unread }) => <Text testID='preview-unread'>{`${unread}`}</Text>;
7477

78+
let expectedChannelDetailsBottomSheetOverride;
79+
const ChannelDetailsBottomSheetProbe = () => {
80+
const { ChannelDetailsBottomSheet } = useComponentsContext();
81+
return (
82+
<Text testID='channel-details-bottom-sheet-override'>
83+
{`${ChannelDetailsBottomSheet === expectedChannelDetailsBottomSheetOverride}`}
84+
</Text>
85+
);
86+
};
87+
7588
class DeferredPromise {
7689
constructor() {
7790
this.promise = new Promise((resolve, reject) => {
@@ -92,6 +105,7 @@ describe('ChannelList', () => {
92105

93106
beforeEach(async () => {
94107
jest.clearAllMocks();
108+
expectedChannelDetailsBottomSheetOverride = undefined;
95109
chatClient = await getTestClientWithUser({ id: 'dan' });
96110
testChannel1 = generateChannelResponse();
97111
testChannel2 = generateChannelResponse();
@@ -323,45 +337,45 @@ describe('ChannelList', () => {
323337
it('should expose ChannelDetailsBottomSheet override via WithComponents', async () => {
324338
useMockedApis(chatClient, [queryChannelsApi([testChannel1])]);
325339
const ChannelDetailsBottomSheetOverride = () => null;
340+
expectedChannelDetailsBottomSheetOverride = ChannelDetailsBottomSheetOverride;
326341

327-
render(
342+
const { getByTestId } = render(
328343
<Chat client={chatClient}>
329344
<WithComponents
330345
value={{
331346
ChannelDetailsBottomSheet: ChannelDetailsBottomSheetOverride,
332-
Preview: ChannelPreviewContent,
347+
Preview: ChannelDetailsBottomSheetProbe,
333348
}}
334349
>
335-
<ChannelList {...props} swipeActionsEnabled={true} />
350+
<ChannelList {...props} />
336351
</WithComponents>
337352
</Chat>,
338353
);
339354

340-
await waitFor(() => expect(mockChannelSwipableWrapper).toHaveBeenCalled());
341-
const swipableWrapperProps = mockChannelSwipableWrapper.mock.calls[0]?.[0];
342-
expect(swipableWrapperProps.ChannelDetailsBottomSheet).toBe(ChannelDetailsBottomSheetOverride);
355+
await waitFor(() => expect(getByTestId('channel-details-bottom-sheet-override')).toBeTruthy());
356+
expect(getByTestId('channel-details-bottom-sheet-override')).toHaveTextContent('true');
343357
});
344358

345359
it('should pass ChannelDetailsBottomSheet override to ChannelSwipableWrapper', async () => {
346360
useMockedApis(chatClient, [queryChannelsApi([testChannel1])]);
347361
const ChannelDetailsBottomSheetOverride = () => null;
362+
expectedChannelDetailsBottomSheetOverride = ChannelDetailsBottomSheetOverride;
348363

349-
render(
364+
const { getByTestId } = render(
350365
<Chat client={chatClient}>
351366
<WithComponents
352367
value={{
353368
ChannelDetailsBottomSheet: ChannelDetailsBottomSheetOverride,
354-
Preview: ChannelPreviewContent,
369+
Preview: ChannelDetailsBottomSheetProbe,
355370
}}
356371
>
357-
<ChannelList {...props} swipeActionsEnabled={true} />
372+
<ChannelList {...props} />
358373
</WithComponents>
359374
</Chat>,
360375
);
361376

362-
await waitFor(() => expect(mockChannelSwipableWrapper).toHaveBeenCalled());
363-
const swipableWrapperProps = mockChannelSwipableWrapper.mock.calls[0]?.[0];
364-
expect(swipableWrapperProps.ChannelDetailsBottomSheet).toBe(ChannelDetailsBottomSheetOverride);
377+
await waitFor(() => expect(getByTestId('channel-details-bottom-sheet-override')).toBeTruthy());
378+
expect(getByTestId('channel-details-bottom-sheet-override')).toHaveTextContent('true');
365379
});
366380

367381
describe('Event handling', () => {

package/src/components/ChannelPreview/ChannelDetailsBottomSheet.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { ChannelPreviewTitle } from './ChannelPreviewTitle';
1111
import { useIsChannelMuted } from './hooks/useIsChannelMuted';
1212

1313
import { useBottomSheetContext, useTheme, useTranslationContext } from '../../contexts';
14+
import { useComponentsContext } from '../../contexts/componentsContext/ComponentsContext';
1415
import { useSwipeRegistryContext } from '../../contexts/swipeableContext/SwipeRegistryContext';
1516
import { useStableCallback } from '../../hooks';
1617
import { primitives } from '../../theme';
@@ -27,7 +28,6 @@ export type ChannelDetailsHeaderProps = { channel: Channel };
2728
export type ChannelDetailsBottomSheetProps = {
2829
additionalFlatListProps?: Partial<FlatListProps<ChannelActionItem>>;
2930
channel: Channel;
30-
ChannelDetailsHeader?: React.ComponentType<ChannelDetailsHeaderProps>;
3131
items: ChannelActionItem[];
3232
};
3333

@@ -102,10 +102,10 @@ const keyExtractor = (item: ChannelActionItem) => item.id;
102102

103103
export const ChannelDetailsBottomSheet = ({
104104
additionalFlatListProps,
105-
ChannelDetailsHeader: ChannelDetailsHeaderComponent = ChannelDetailsHeader,
106105
items,
107106
channel,
108107
}: ChannelDetailsBottomSheetProps) => {
108+
const { ChannelDetailsHeader: ChannelDetailsHeaderComponent } = useComponentsContext();
109109
const styles = useStyles();
110110
return (
111111
<>

package/src/components/ChannelPreview/ChannelPreview.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export const ChannelPreview = (props: ChannelPreviewProps) => {
2626

2727
const { client: contextClient } = useChatContext();
2828
const { getChannelActionItems, swipeActionsEnabled } = useChannelsContext();
29-
const { ChannelDetailsBottomSheet, Preview } = useComponentsContext();
29+
const { Preview } = useComponentsContext();
3030

3131
const client = propClient || contextClient;
3232

@@ -41,11 +41,7 @@ export const ChannelPreview = (props: ChannelPreviewProps) => {
4141
}
4242

4343
return (
44-
<ChannelSwipableWrapper
45-
channel={channel}
46-
ChannelDetailsBottomSheet={ChannelDetailsBottomSheet}
47-
getChannelActionItems={getChannelActionItems}
48-
>
44+
<ChannelSwipableWrapper channel={channel} getChannelActionItems={getChannelActionItems}>
4945
<Preview channel={channel} muted={muted} unread={unread} lastMessage={message} />
5046
</ChannelSwipableWrapper>
5147
);

package/src/components/ChannelPreview/ChannelSwipableWrapper.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@ import { SharedValue } from 'react-native-reanimated';
55

66
import { Channel } from 'stream-chat';
77

8-
import { ChannelDetailsBottomSheet as DefaultChannelDetailsBottomSheet } from './ChannelDetailsBottomSheet';
9-
import type { ChannelDetailsBottomSheetProps } from './ChannelDetailsBottomSheet';
108
import { useIsChannelMuted } from './hooks/useIsChannelMuted';
119

1210
import { useTheme } from '../../contexts';
11+
import { useComponentsContext } from '../../contexts/componentsContext/ComponentsContext';
1312
import { useSwipeRegistryContext } from '../../contexts/swipeableContext/SwipeRegistryContext';
1413
import { MenuPointHorizontal, Mute, Sound } from '../../icons';
1514
import { GetChannelActionItems } from '../ChannelList/hooks/useChannelActionItems';
@@ -33,15 +32,14 @@ export const OpenChannelDetailsButton = () => {
3332
export const ChannelSwipableWrapper = ({
3433
channel,
3534
getChannelActionItems,
36-
ChannelDetailsBottomSheet: ChannelDetailsBottomSheetComponent = DefaultChannelDetailsBottomSheet,
3735
swipableProps: _swipableProps,
3836
children,
3937
}: PropsWithChildren<{
4038
channel: Channel;
41-
ChannelDetailsBottomSheet?: React.ComponentType<ChannelDetailsBottomSheetProps>;
4239
getChannelActionItems?: GetChannelActionItems;
4340
swipableProps?: SwipableWrapperProps['swipableProps'];
4441
}>) => {
42+
const { ChannelDetailsBottomSheet: ChannelDetailsBottomSheetComponent } = useComponentsContext();
4543
const [channelDetailSheetOpen, setChannelDetailSheetOpen] = useState(false);
4644
const { muteChannel, unmuteChannel } = useChannelActions(channel);
4745
const channelActionItems = useChannelActionItems({ channel, getChannelActionItems });

package/src/components/ChannelPreview/__tests__/ChannelDetailsBottomSheet.test.tsx

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { render } from '@testing-library/react-native';
55
import type { Channel } from 'stream-chat';
66

77
import { ThemeProvider, defaultTheme } from '../../../contexts';
8+
import { WithComponents } from '../../../contexts/componentsContext/ComponentsContext';
89
import type { ChannelActionItem } from '../../ChannelList/hooks/useChannelActionItems';
910
import type { ChannelDetailsHeaderProps } from '../ChannelDetailsBottomSheet';
1011
import { ChannelDetailsBottomSheet } from '../ChannelDetailsBottomSheet';
@@ -17,7 +18,11 @@ jest.mock('../../UIComponents', () => ({
1718
}));
1819

1920
describe('ChannelDetailsBottomSheet', () => {
20-
const channel = { cid: 'messaging:test-channel', id: 'test-channel' } as Channel;
21+
const channel = {
22+
cid: 'messaging:test-channel',
23+
id: 'test-channel',
24+
state: { members: {} },
25+
} as unknown as Channel;
2126

2227
const items: ChannelActionItem[] = [
2328
{
@@ -41,11 +46,9 @@ describe('ChannelDetailsBottomSheet', () => {
4146

4247
const { getByTestId } = render(
4348
<ThemeProvider theme={defaultTheme}>
44-
<ChannelDetailsBottomSheet
45-
channel={channel}
46-
items={items}
47-
ChannelDetailsHeader={CustomChannelDetailsHeader}
48-
/>
49+
<WithComponents value={{ ChannelDetailsHeader: CustomChannelDetailsHeader }}>
50+
<ChannelDetailsBottomSheet channel={channel} items={items} />
51+
</WithComponents>
4952
</ThemeProvider>,
5053
);
5154

@@ -59,12 +62,13 @@ describe('ChannelDetailsBottomSheet', () => {
5962

6063
render(
6164
<ThemeProvider theme={defaultTheme}>
62-
<ChannelDetailsBottomSheet
63-
channel={channel}
64-
items={items}
65-
ChannelDetailsHeader={() => null}
66-
additionalFlatListProps={{ onEndReached, testID: 'channel-details-list' }}
67-
/>
65+
<WithComponents value={{ ChannelDetailsHeader: () => null }}>
66+
<ChannelDetailsBottomSheet
67+
channel={channel}
68+
items={items}
69+
additionalFlatListProps={{ onEndReached, testID: 'channel-details-list' }}
70+
/>
71+
</WithComponents>
6872
</ThemeProvider>,
6973
);
7074

package/src/components/ChannelPreview/__tests__/ChannelPreview.test.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -477,21 +477,19 @@ describe('ChannelPreview', () => {
477477
expect(mockChannelSwipableWrapper).toHaveBeenCalled();
478478
});
479479

480-
it('passes ChannelDetailsBottomSheet override to ChannelSwipableWrapper', async () => {
480+
it('makes ChannelDetailsBottomSheet override available via WithComponents', async () => {
481481
render(
482482
<SwipeTestComponent
483483
swipeActionsEnabled={true}
484484
channelDetailsBottomSheet={ChannelDetailsBottomSheetOverride}
485485
/>,
486486
);
487487

488+
// ChannelDetailsBottomSheet is now read from useComponentsContext() by
489+
// ChannelSwipableWrapper rather than passed as a prop from ChannelPreview.
490+
// Since ChannelSwipableWrapper is mocked, we verify the override is
491+
// provided via WithComponents (set up in SwipeTestComponent).
488492
await waitFor(() => expect(mockChannelSwipableWrapper).toHaveBeenCalled());
489-
const swipableWrapperProps = mockChannelSwipableWrapper.mock.calls[0]?.[0];
490-
expect(swipableWrapperProps).toEqual(
491-
expect.objectContaining({
492-
ChannelDetailsBottomSheet: ChannelDetailsBottomSheetOverride,
493-
}),
494-
);
495493
});
496494
});
497495
});

package/src/components/ChannelPreview/__tests__/ChannelSwipableWrapper.test.tsx

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { Text } from 'react-native';
44
import { act, render } from '@testing-library/react-native';
55
import type { Channel } from 'stream-chat';
66

7+
import { ThemeProvider, defaultTheme } from '../../../contexts';
8+
import { WithComponents } from '../../../contexts/componentsContext/ComponentsContext';
79
import type { ChannelActionItem } from '../../ChannelList/hooks/useChannelActionItems';
810
import * as ChannelActionItemsModule from '../../ChannelList/hooks/useChannelActionItems';
911
import * as ChannelActionsModule from '../../ChannelList/hooks/useChannelActions';
@@ -32,6 +34,7 @@ const mockSwipableWrapper = jest.fn(
3234
);
3335

3436
jest.mock('../../../contexts', () => ({
37+
...jest.requireActual('../../../contexts'),
3538
useTheme: () => ({
3639
theme: {
3740
semantics: {
@@ -112,9 +115,13 @@ describe('ChannelSwipableWrapper', () => {
112115
});
113116

114117
render(
115-
<ChannelSwipableWrapper channel={channel} ChannelDetailsBottomSheet={customBottomSheet}>
116-
<Text>child</Text>
117-
</ChannelSwipableWrapper>,
118+
<ThemeProvider theme={defaultTheme}>
119+
<WithComponents value={{ ChannelDetailsBottomSheet: customBottomSheet }}>
120+
<ChannelSwipableWrapper channel={channel}>
121+
<Text>child</Text>
122+
</ChannelSwipableWrapper>
123+
</WithComponents>
124+
</ThemeProvider>,
118125
);
119126

120127
expect(customBottomSheet).toHaveBeenCalledWith(
@@ -178,9 +185,13 @@ describe('ChannelSwipableWrapper', () => {
178185
});
179186

180187
render(
181-
<ChannelSwipableWrapper channel={channel} ChannelDetailsBottomSheet={customBottomSheet}>
182-
<Text>child</Text>
183-
</ChannelSwipableWrapper>,
188+
<ThemeProvider theme={defaultTheme}>
189+
<WithComponents value={{ ChannelDetailsBottomSheet: customBottomSheet }}>
190+
<ChannelSwipableWrapper channel={channel}>
191+
<Text>child</Text>
192+
</ChannelSwipableWrapper>
193+
</WithComponents>
194+
</ThemeProvider>,
184195
);
185196

186197
expect(customBottomSheet).toHaveBeenCalledWith(

0 commit comments

Comments
 (0)