From d20761586d502dc2a93acbaae2b174688add28a2 Mon Sep 17 00:00:00 2001 From: srijna Date: Mon, 8 Jun 2026 15:46:28 +0530 Subject: [PATCH 1/9] feat: migrate MessageSearch to PaginatedVirtualList --- .../MessageSearchTab/MessageSearchTab.tsx | 80 ++----------- .../components/MessageSearch.spec.tsx | 110 ++++++++++++++++++ .../components/MessageSearch.tsx | 93 +++++++++++++++ .../hooks/useMessageSearchQuery.ts | 41 ++++--- 4 files changed, 235 insertions(+), 89 deletions(-) create mode 100644 apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx create mode 100644 apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/MessageSearchTab.tsx b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/MessageSearchTab.tsx index c804515c61ac7..dabab41133660 100644 --- a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/MessageSearchTab.tsx +++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/MessageSearchTab.tsx @@ -1,5 +1,4 @@ -import { Callout, Box, MessageDivider, Throbber } from '@rocket.chat/fuselage'; -import { MessageTypes } from '@rocket.chat/message-types'; +import { Callout, Box, Throbber } from '@rocket.chat/fuselage'; import { ContextualbarClose, ContextualbarContent, @@ -8,43 +7,28 @@ import { ContextualbarIcon, ContextualbarSection, ContextualbarDialog, - VirtualizedScrollbars, - ContextualbarEmptyContent, } from '@rocket.chat/ui-client'; -import { useRoomToolbox, useUserPreference, useSetting } from '@rocket.chat/ui-contexts'; -import { useState, memo, Fragment, useId } from 'react'; +import { useRoomToolbox } from '@rocket.chat/ui-contexts'; +import { memo, useId, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { Virtuoso } from 'react-virtuoso'; +import MessageSearch from './components/MessageSearch'; import MessageSearchForm from './components/MessageSearchForm'; import { useMessageSearchProviderQuery } from './hooks/useMessageSearchProviderQuery'; import { useMessageSearchQuery } from './hooks/useMessageSearchQuery'; import ResultsLiveRegion from '../../../../components/ResultsLiveRegion'; -import RoomMessage from '../../../../components/message/variants/RoomMessage'; -import SystemMessage from '../../../../components/message/variants/SystemMessage'; -import { useFormatDate } from '../../../../hooks/useFormatDate'; -import MessageListErrorBoundary from '../../MessageList/MessageListErrorBoundary'; -import { isMessageNewDay } from '../../MessageList/lib/isMessageNewDay'; -import MessageListProvider from '../../MessageList/providers/MessageListProvider'; -import { useRoomSubscription } from '../../contexts/RoomContext'; // TODO: Refactor this component to isolate the data from the visual const MessageSearchTab = () => { const { t } = useTranslation(); const searchListId = useId(); - const formatDate = useFormatDate(); const { closeTab } = useRoomToolbox(); - const pageSize = useSetting('PageSize', 10); - - const [limit, setLimit] = useState(pageSize); - const subscription = useRoomSubscription(); - const showUserAvatar = !!useUserPreference('displayAvatars'); const providerQuery = useMessageSearchProviderQuery(); const [{ searchText, globalSearch }, handleSearch] = useState({ searchText: '', globalSearch: false }); - const { isSuccess, data: messageSearchData, isPending } = useMessageSearchQuery({ searchText, limit, globalSearch }); - const itemCount = messageSearchData?.length ?? 0; + const { isSuccess, data, isPending } = useMessageSearchQuery({ searchText, globalSearch }); + const itemCount = data?.itemCount ?? 0; return ( @@ -65,57 +49,7 @@ const MessageSearchTab = () => { {searchText && isPending && } {isSuccess && ( - {messageSearchData.length === 0 && } - {messageSearchData.length > 0 && ( - - - - - { - const previous = messageSearchData[index - 1]; - - const newDay = isMessageNewDay(message, previous); - - const system = MessageTypes.isSystemMessage(message); - - const unread = subscription?.tunread?.includes(message._id) ?? false; - const mention = subscription?.tunreadUser?.includes(message._id) ?? false; - const all = subscription?.tunreadGroup?.includes(message._id) ?? false; - - return ( - - {newDay && {formatDate(message.ts)}} - - {system ? ( - - ) : ( - - )} - - ); - }} - endReached={() => { - setLimit((limit) => limit + pageSize); - }} - /> - - - - - )} + )} diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx new file mode 100644 index 0000000000000..f56123f65543a --- /dev/null +++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx @@ -0,0 +1,110 @@ +import type { IMessage } from '@rocket.chat/core-typings'; +import { render, screen } from '@testing-library/react'; + +import MessageSearch from './MessageSearch'; + +const useMessageSearchQueryMock = jest.fn(); + +jest.mock('../hooks/useMessageSearchQuery', () => ({ + useMessageSearchQuery: (...args: unknown[]) => useMessageSearchQueryMock(...args), +})); + +jest.mock('../../../../../components/PaginatedVirtualList', () => ({ + PaginatedVirtualList: ({ items, renderItem }: { items: IMessage[]; renderItem: (item: IMessage, index: number) => React.ReactNode }) => ( +
+ {items.map((item, index) => ( +
{renderItem(item, index)}
+ ))} +
+ ), +})); + +jest.mock('../../../../../components/message/variants/RoomMessage', () => ({ message }: { message: IMessage }) => ( +
{message.msg}
+)); + +jest.mock('../../../../../components/message/variants/SystemMessage', () => ({ message }: { message: IMessage }) => ( +
{message.msg}
+)); + +jest.mock('../../../../../hooks/useFormatDate', () => ({ + useFormatDate: () => () => 'formatted-date', +})); + +jest.mock('../../../MessageList/MessageListErrorBoundary', () => ({ + __esModule: true, + default: ({ children }: { children: React.ReactNode }) => <>{children}, +})); + +jest.mock('../../../MessageList/providers/MessageListProvider', () => ({ + __esModule: true, + default: ({ children }: { children: React.ReactNode }) => <>{children}, +})); + +jest.mock('../../../contexts/RoomContext', () => ({ + useRoomSubscription: () => ({ + tunread: ['message-1'], + tunreadUser: ['message-1'], + tunreadGroup: ['message-1'], + }), +})); + +jest.mock('@rocket.chat/ui-contexts', () => ({ + ...jest.requireActual('@rocket.chat/ui-contexts'), + useTranslation: () => (key: string) => key, + useUserPreference: () => true, +})); + +const createMessage = (id: string, overrides: Partial = {}): IMessage => + ({ + _id: id, + rid: 'room-id', + msg: `Message ${id}`, + ts: new Date('2026-03-22T10:00:00.000Z'), + u: { _id: 'user-id', username: 'testuser', name: 'Test User' }, + _updatedAt: new Date('2026-03-22T10:00:00.000Z'), + ...overrides, + }) as IMessage; + +describe('MessageSearch', () => { + beforeEach(() => { + useMessageSearchQueryMock.mockReturnValue({ + isPending: false, + isSuccess: true, + data: { items: [], itemCount: 0 }, + fetchNextPage: jest.fn(), + }); + }); + + afterEach(() => { + useMessageSearchQueryMock.mockReset(); + }); + + it('renders the empty state when no messages are returned', () => { + render(); + + expect(screen.getByText('No_results_found')).toBeInTheDocument(); + }); + + it('renders room and system messages with date dividers', () => { + const roomMessage = createMessage('message-1'); + const systemMessage = createMessage('message-2', { + ts: new Date('2026-03-23T10:00:00.000Z'), + t: 'au', + msg: 'System event', + }); + + useMessageSearchQueryMock.mockReturnValue({ + isPending: false, + isSuccess: true, + data: { items: [roomMessage, systemMessage], itemCount: 2 }, + fetchNextPage: jest.fn(), + }); + + render(); + + expect(screen.getByTestId('room-message')).toHaveTextContent('Message message-1'); + expect(screen.getByTestId('system-message')).toHaveTextContent('System event'); + expect(screen.getAllByText('formatted-date')).toHaveLength(2); + }); +}); diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx new file mode 100644 index 0000000000000..5fa0947e4ab81 --- /dev/null +++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx @@ -0,0 +1,93 @@ +import { Box, MessageDivider } from '@rocket.chat/fuselage'; +import { MessageTypes } from '@rocket.chat/message-types'; +import { ContextualbarEmptyContent } from '@rocket.chat/ui-client'; +import { useTranslation, useUserPreference } from '@rocket.chat/ui-contexts'; +import type { ReactElement } from 'react'; +import { Fragment, memo } from 'react'; + +import { PaginatedVirtualList } from '../../../../../components/PaginatedVirtualList'; +import RoomMessage from '../../../../../components/message/variants/RoomMessage'; +import SystemMessage from '../../../../../components/message/variants/SystemMessage'; +import { useFormatDate } from '../../../../../hooks/useFormatDate'; +import MessageListErrorBoundary from '../../../MessageList/MessageListErrorBoundary'; +import { isMessageNewDay } from '../../../MessageList/lib/isMessageNewDay'; +import MessageListProvider from '../../../MessageList/providers/MessageListProvider'; +import { useRoomSubscription } from '../../../contexts/RoomContext'; +import type { MessageSearchItem } from '../hooks/useMessageSearchQuery'; +import { useMessageSearchQuery } from '../hooks/useMessageSearchQuery'; + +type MessageSearchProps = { + searchText: string; + globalSearch: boolean; +}; + +const MessageSearch = ({ searchText, globalSearch }: MessageSearchProps): ReactElement => { + const t = useTranslation(); + const formatDate = useFormatDate(); + const showUserAvatar = !!useUserPreference('displayAvatars'); + + const subscription = useRoomSubscription(); + const { isPending, isSuccess, data, fetchNextPage } = useMessageSearchQuery({ searchText, globalSearch }); + const items = data?.items || []; + const itemCount = data?.itemCount ?? 0; + + if (!isSuccess) { + return <>; + } + + return ( + <> + {items.length === 0 && } + {items.length > 0 && ( + + + + + { + const previous = items[index - 1]; + + const newDay = isMessageNewDay(message, previous); + + const system = MessageTypes.isSystemMessage(message); + + const unread = subscription?.tunread?.includes(message._id) ?? false; + const mention = subscription?.tunreadUser?.includes(message._id) ?? false; + const all = subscription?.tunreadGroup?.includes(message._id) ?? false; + + return ( + + {newDay && {formatDate(message.ts)}} + + {system ? ( + + ) : ( + + )} + + ); + }} + /> + + + + + )} + + ); +}; + +export default memo(MessageSearch); diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/hooks/useMessageSearchQuery.ts b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/hooks/useMessageSearchQuery.ts index ffa22b4ae69a6..3687ad8d724a7 100644 --- a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/hooks/useMessageSearchQuery.ts +++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/hooks/useMessageSearchQuery.ts @@ -1,30 +1,39 @@ -import { useMethod, useTranslation, useUserId } from '@rocket.chat/ui-contexts'; -import { keepPreviousData, useQuery } from '@tanstack/react-query'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; +import { useMethod, useSetting, useTranslation, useUserId } from '@rocket.chat/ui-contexts'; +import { keepPreviousData, useInfiniteQuery } from '@tanstack/react-query'; import { useRoom } from '../../../contexts/RoomContext'; -export const useMessageSearchQuery = ({ - searchText, - limit, - globalSearch, -}: { - searchText: string; - limit: number; - globalSearch: boolean; -}) => { +export type MessageSearchItem = NonNullable>['message']>['docs'][number]; + +export const useMessageSearchQuery = ({ searchText, globalSearch }: { searchText: string; globalSearch: boolean }) => { const uid = useUserId(); const room = useRoom(); + const pageSize = useSetting('PageSize', 10); const t = useTranslation(); const searchMessages = useMethod('rocketchatSearch.search'); - return useQuery({ - queryKey: ['rooms', room._id, 'message-search', { uid, rid: room._id, searchText, limit, globalSearch }] as const, - - queryFn: async () => { + return useInfiniteQuery({ + queryKey: ['rooms', room._id, 'message-search', { uid, rid: room._id, searchText, globalSearch }] as const, + queryFn: async ({ pageParam: limit }) => { const result = await searchMessages(searchText, { uid, rid: room._id }, { limit, searchAll: globalSearch }); - return result.message?.docs ?? []; + const items = result.message?.docs ?? []; + + return { + items, + itemCount: items.length >= limit ? items.length + 1 : items.length, + }; + }, + initialPageParam: pageSize, + getNextPageParam: (lastPage, _allPages, lastPageParam) => { + if (lastPage.items.length < lastPageParam) { + return undefined; + } + + return lastPageParam + pageSize; }, + select: ({ pages }) => pages.at(-1) ?? { items: [], itemCount: 0 }, placeholderData: keepPreviousData, meta: { errorToastMessage: t('Search_message_search_failed'), From 2d5502ba9457c7ffa166a1a2c00a3da58d3f5f33 Mon Sep 17 00:00:00 2001 From: srijna Date: Thu, 11 Jun 2026 18:19:27 +0530 Subject: [PATCH 2/9] fix: constrain message search list height --- .../MessageSearchTab/MessageSearchTab.tsx | 2 +- .../components/MessageSearch.spec.tsx | 13 +++++++++++++ .../MessageSearchTab/components/MessageSearch.tsx | 12 +++++++++++- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/MessageSearchTab.tsx b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/MessageSearchTab.tsx index dabab41133660..8abf7d80899c2 100644 --- a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/MessageSearchTab.tsx +++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/MessageSearchTab.tsx @@ -48,7 +48,7 @@ const MessageSearchTab = () => { <> {searchText && isPending && } {isSuccess && ( - + )} diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx index f56123f65543a..066a5845fe0f8 100644 --- a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx +++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx @@ -86,6 +86,19 @@ describe('MessageSearch', () => { expect(screen.getByText('No_results_found')).toBeInTheDocument(); }); + it('renders nothing until the search query succeeds', () => { + useMessageSearchQueryMock.mockReturnValue({ + isPending: true, + isSuccess: false, + data: undefined, + fetchNextPage: jest.fn(), + }); + + const { container } = render(); + + expect(container).toBeEmptyDOMElement(); + }); + it('renders room and system messages with date dividers', () => { const roomMessage = createMessage('message-1'); const systemMessage = createMessage('message-2', { diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx index 5fa0947e4ab81..0ceca44f54112 100644 --- a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx +++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx @@ -41,7 +41,17 @@ const MessageSearch = ({ searchText, globalSearch }: MessageSearchProps): ReactE {items.length > 0 && ( - + Date: Thu, 11 Jun 2026 20:16:10 +0530 Subject: [PATCH 3/9] fix: address message search review feedback --- .../MessageSearchTab/MessageSearchTab.tsx | 24 ++- .../components/MessageSearch.spec.tsx | 91 +++++----- .../components/MessageSearch.stories.tsx | 155 ++++++++++++++++++ .../components/MessageSearch.tsx | 40 +++-- 4 files changed, 236 insertions(+), 74 deletions(-) create mode 100644 apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.stories.tsx diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/MessageSearchTab.tsx b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/MessageSearchTab.tsx index 8abf7d80899c2..a94eb0e29431f 100644 --- a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/MessageSearchTab.tsx +++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/MessageSearchTab.tsx @@ -8,7 +8,7 @@ import { ContextualbarSection, ContextualbarDialog, } from '@rocket.chat/ui-client'; -import { useRoomToolbox } from '@rocket.chat/ui-contexts'; +import { useRoomToolbox, useUserPreference } from '@rocket.chat/ui-contexts'; import { memo, useId, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -17,8 +17,9 @@ import MessageSearchForm from './components/MessageSearchForm'; import { useMessageSearchProviderQuery } from './hooks/useMessageSearchProviderQuery'; import { useMessageSearchQuery } from './hooks/useMessageSearchQuery'; import ResultsLiveRegion from '../../../../components/ResultsLiveRegion'; +import { useFormatDate } from '../../../../hooks/useFormatDate'; +import { useRoomSubscription } from '../../contexts/RoomContext'; -// TODO: Refactor this component to isolate the data from the visual const MessageSearchTab = () => { const { t } = useTranslation(); const searchListId = useId(); @@ -27,8 +28,12 @@ const MessageSearchTab = () => { const providerQuery = useMessageSearchProviderQuery(); const [{ searchText, globalSearch }, handleSearch] = useState({ searchText: '', globalSearch: false }); - const { isSuccess, data, isPending } = useMessageSearchQuery({ searchText, globalSearch }); + const { isPending, isSuccess, data, fetchNextPage } = useMessageSearchQuery({ searchText, globalSearch }); + const items = data?.items || []; const itemCount = data?.itemCount ?? 0; + const subscription = useRoomSubscription(); + const showUserAvatar = !!useUserPreference('displayAvatars'); + const formatDate = useFormatDate(); return ( @@ -49,7 +54,18 @@ const MessageSearchTab = () => { {searchText && isPending && } {isSuccess && ( - + )} diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx index 066a5845fe0f8..a071e3b34d6c9 100644 --- a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx +++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx @@ -1,17 +1,20 @@ -import type { IMessage } from '@rocket.chat/core-typings'; +import type { IMessage, ISubscription } from '@rocket.chat/core-typings'; import { render, screen } from '@testing-library/react'; import MessageSearch from './MessageSearch'; - -const useMessageSearchQueryMock = jest.fn(); - -jest.mock('../hooks/useMessageSearchQuery', () => ({ - useMessageSearchQuery: (...args: unknown[]) => useMessageSearchQueryMock(...args), -})); +import type { MessageSearchItem } from '../hooks/useMessageSearchQuery'; jest.mock('../../../../../components/PaginatedVirtualList', () => ({ - PaginatedVirtualList: ({ items, renderItem }: { items: IMessage[]; renderItem: (item: IMessage, index: number) => React.ReactNode }) => ( -
+ PaginatedVirtualList: ({ + items, + totalCount, + renderItem, + }: { + items: MessageSearchItem[]; + totalCount: number; + renderItem: (item: MessageSearchItem, index: number) => React.ReactNode; + }) => ( +
{items.map((item, index) => (
{renderItem(item, index)}
))} @@ -27,10 +30,6 @@ jest.mock('../../../../../components/message/variants/SystemMessage', () => ({ m
{message.msg}
)); -jest.mock('../../../../../hooks/useFormatDate', () => ({ - useFormatDate: () => () => 'formatted-date', -})); - jest.mock('../../../MessageList/MessageListErrorBoundary', () => ({ __esModule: true, default: ({ children }: { children: React.ReactNode }) => <>{children}, @@ -41,60 +40,51 @@ jest.mock('../../../MessageList/providers/MessageListProvider', () => ({ default: ({ children }: { children: React.ReactNode }) => <>{children}, })); -jest.mock('../../../contexts/RoomContext', () => ({ - useRoomSubscription: () => ({ - tunread: ['message-1'], - tunreadUser: ['message-1'], - tunreadGroup: ['message-1'], - }), -})); - jest.mock('@rocket.chat/ui-contexts', () => ({ ...jest.requireActual('@rocket.chat/ui-contexts'), useTranslation: () => (key: string) => key, - useUserPreference: () => true, })); -const createMessage = (id: string, overrides: Partial = {}): IMessage => +const createMessage = (id: string, overrides: Partial = {}): MessageSearchItem => ({ _id: id, rid: 'room-id', + user: 'testuser', msg: `Message ${id}`, ts: new Date('2026-03-22T10:00:00.000Z'), u: { _id: 'user-id', username: 'testuser', name: 'Test User' }, _updatedAt: new Date('2026-03-22T10:00:00.000Z'), ...overrides, - }) as IMessage; + }) as MessageSearchItem; + +const subscription = { + tunread: ['message-1'], + tunreadUser: ['message-1'], + tunreadGroup: ['message-1'], +} as ISubscription; + +const defaultProps = { + items: [], + itemCount: 0, + isPending: false, + isSuccess: true, + fetchNextPage: jest.fn(), + subscription, + showUserAvatar: true, + formatDate: () => 'formatted-date', + searchText: 'hello', + noResultsTitle: 'No_results_found', +}; describe('MessageSearch', () => { - beforeEach(() => { - useMessageSearchQueryMock.mockReturnValue({ - isPending: false, - isSuccess: true, - data: { items: [], itemCount: 0 }, - fetchNextPage: jest.fn(), - }); - }); - - afterEach(() => { - useMessageSearchQueryMock.mockReset(); - }); - it('renders the empty state when no messages are returned', () => { - render(); + render(); expect(screen.getByText('No_results_found')).toBeInTheDocument(); }); it('renders nothing until the search query succeeds', () => { - useMessageSearchQueryMock.mockReturnValue({ - isPending: true, - isSuccess: false, - data: undefined, - fetchNextPage: jest.fn(), - }); - - const { container } = render(); + const { container } = render(); expect(container).toBeEmptyDOMElement(); }); @@ -107,14 +97,7 @@ describe('MessageSearch', () => { msg: 'System event', }); - useMessageSearchQueryMock.mockReturnValue({ - isPending: false, - isSuccess: true, - data: { items: [roomMessage, systemMessage], itemCount: 2 }, - fetchNextPage: jest.fn(), - }); - - render(); + render(); expect(screen.getByTestId('room-message')).toHaveTextContent('Message message-1'); expect(screen.getByTestId('system-message')).toHaveTextContent('System event'); diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.stories.tsx b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.stories.tsx new file mode 100644 index 0000000000000..c51494f9e5460 --- /dev/null +++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.stories.tsx @@ -0,0 +1,155 @@ +import type { IMessage } from '@rocket.chat/core-typings'; +import { Box } from '@rocket.chat/fuselage'; +import { mockAppRoot } from '@rocket.chat/mock-providers'; +import { Contextualbar } from '@rocket.chat/ui-client'; +import { action } from '@storybook/addon-actions'; +import type { Meta, StoryObj } from '@storybook/react'; +import type { UseInfiniteQueryResult } from '@tanstack/react-query'; + +import MessageSearch from './MessageSearch'; +import { createFakeMessageWithMd, createFakeRoom, createFakeSubscription } from '../../../../../../tests/mocks/data'; +import type { MessageSearchItem } from '../hooks/useMessageSearchQuery'; + +const room = createFakeRoom({ _id: 'room-id', t: 'c', name: 'general', fname: 'General' }); +const subscription = createFakeSubscription({ + rid: room._id, + tunread: ['message-2'], + tunreadUser: ['message-2'], + tunreadGroup: [], +}); + +const fetchNextPageAction = action('fetchNextPage'); +const fetchNextPage = (async (options) => { + fetchNextPageAction(options); + return {} as Awaited>; +}) satisfies UseInfiniteQueryResult['fetchNextPage']; + +const formatDate = (date: Date | string | number): string => new Intl.DateTimeFormat('en', { dateStyle: 'medium' }).format(new Date(date)); + +const createMessage = (overrides: Partial): MessageSearchItem => + createFakeMessageWithMd({ + rid: room._id, + u: { + _id: 'user-id', + username: 'ana.silva', + name: 'Ana Silva', + }, + ...overrides, + }) as MessageSearchItem; + +const messages = [ + createMessage({ + _id: 'message-1', + msg: 'Can you share the deployment checklist?', + ts: new Date('2026-06-09T14:15:00.000Z'), + }), + createMessage({ + _id: 'message-2', + msg: 'The checklist is attached to the release room topic.', + ts: new Date('2026-06-09T14:18:00.000Z'), + u: { + _id: 'user-id-2', + username: 'sam.chen', + name: 'Sam Chen', + }, + }), + createMessage({ + _id: 'message-3', + msg: 'I found the rollback notes as well.', + ts: new Date('2026-06-09T14:22:00.000Z'), + }), +]; + +const systemMessages = [ + createMessage({ + _id: 'system-message-1', + msg: 'Sam Chen joined the room', + t: 'uj', + ts: new Date('2026-06-09T09:00:00.000Z'), + }), + createMessage({ + _id: 'system-message-2', + msg: 'Room topic changed to Release coordination', + t: 'room_changed_topic', + ts: new Date('2026-06-09T09:05:00.000Z'), + }), +]; + +const multipleDateMessages = [ + createMessage({ + _id: 'date-message-1', + msg: 'Initial search result from Monday.', + ts: new Date('2026-06-08T10:00:00.000Z'), + }), + createMessage({ + _id: 'date-message-2', + msg: 'Follow-up result from Tuesday.', + ts: new Date('2026-06-09T10:00:00.000Z'), + }), + createMessage({ + _id: 'date-message-3', + msg: 'Final result from Wednesday.', + ts: new Date('2026-06-10T10:00:00.000Z'), + }), +]; + +const meta = { + component: MessageSearch, + parameters: { + layout: 'fullscreen', + actions: { argTypesRegex: '^on.*' }, + }, + decorators: [ + mockAppRoot().withJohnDoe().withRoom(room).withSubscription(subscription).buildStoryDecorator(), + (fn) => ( + + + {fn()} + + + ), + ], + args: { + itemCount: 0, + isPending: false, + isSuccess: true, + fetchNextPage, + subscription, + showUserAvatar: true, + formatDate, + searchText: 'release', + noResultsTitle: 'No results found', + }, +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Empty: Story = { + args: { + items: [], + itemCount: 0, + }, +}; + +export const Messages: Story = { + args: { + items: messages, + itemCount: messages.length, + }, +}; + +export const SystemMessages: Story = { + args: { + items: systemMessages, + itemCount: systemMessages.length, + }, +}; + +export const MultipleDateGroups: Story = { + args: { + items: multipleDateMessages, + itemCount: multipleDateMessages.length, + }, +}; diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx index 0ceca44f54112..645bb35e45e8c 100644 --- a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx +++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx @@ -1,43 +1,51 @@ +import type { ISubscription } from '@rocket.chat/core-typings'; import { Box, MessageDivider } from '@rocket.chat/fuselage'; import { MessageTypes } from '@rocket.chat/message-types'; import { ContextualbarEmptyContent } from '@rocket.chat/ui-client'; -import { useTranslation, useUserPreference } from '@rocket.chat/ui-contexts'; +import type { UseInfiniteQueryResult } from '@tanstack/react-query'; import type { ReactElement } from 'react'; import { Fragment, memo } from 'react'; import { PaginatedVirtualList } from '../../../../../components/PaginatedVirtualList'; import RoomMessage from '../../../../../components/message/variants/RoomMessage'; import SystemMessage from '../../../../../components/message/variants/SystemMessage'; -import { useFormatDate } from '../../../../../hooks/useFormatDate'; import MessageListErrorBoundary from '../../../MessageList/MessageListErrorBoundary'; import { isMessageNewDay } from '../../../MessageList/lib/isMessageNewDay'; import MessageListProvider from '../../../MessageList/providers/MessageListProvider'; -import { useRoomSubscription } from '../../../contexts/RoomContext'; import type { MessageSearchItem } from '../hooks/useMessageSearchQuery'; -import { useMessageSearchQuery } from '../hooks/useMessageSearchQuery'; type MessageSearchProps = { + items: MessageSearchItem[]; + itemCount: number; + isPending: boolean; + isSuccess: boolean; + fetchNextPage: UseInfiniteQueryResult['fetchNextPage']; + subscription: ISubscription | undefined; + showUserAvatar: boolean; + formatDate: (date: Date | string | number) => string; searchText: string; - globalSearch: boolean; + noResultsTitle: string; }; -const MessageSearch = ({ searchText, globalSearch }: MessageSearchProps): ReactElement => { - const t = useTranslation(); - const formatDate = useFormatDate(); - const showUserAvatar = !!useUserPreference('displayAvatars'); - - const subscription = useRoomSubscription(); - const { isPending, isSuccess, data, fetchNextPage } = useMessageSearchQuery({ searchText, globalSearch }); - const items = data?.items || []; - const itemCount = data?.itemCount ?? 0; - +const MessageSearch = ({ + items, + itemCount, + isPending, + isSuccess, + fetchNextPage, + subscription, + showUserAvatar, + formatDate, + searchText, + noResultsTitle, +}: MessageSearchProps): ReactElement => { if (!isSuccess) { return <>; } return ( <> - {items.length === 0 && } + {items.length === 0 && } {items.length > 0 && ( From 8f9a77b9de548aff44bf536e345703633b972312 Mon Sep 17 00:00:00 2001 From: MartinSchoeler Date: Mon, 15 Jun 2026 17:14:49 -0300 Subject: [PATCH 4/9] chore: update test --- .../components/MessageSearch.spec.tsx | 72 ++++++++++++------- 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx index a071e3b34d6c9..125273efc72e0 100644 --- a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx +++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx @@ -1,7 +1,12 @@ -import type { IMessage, ISubscription } from '@rocket.chat/core-typings'; +import type { IMessage, IRoom, ISubscription, IUser } from '@rocket.chat/core-typings'; +import { mockAppRoot } from '@rocket.chat/mock-providers'; +import type { SubscriptionWithRoom } from '@rocket.chat/ui-contexts'; import { render, screen } from '@testing-library/react'; +import type { ReactNode } from 'react'; import MessageSearch from './MessageSearch'; +import FakeRoomProvider from '../../../../../../tests/mocks/client/FakeRoomProvider'; +import { createFakeRoom, createFakeSubscription, createFakeUser } from '../../../../../../tests/mocks/data'; import type { MessageSearchItem } from '../hooks/useMessageSearchQuery'; jest.mock('../../../../../components/PaginatedVirtualList', () => ({ @@ -12,7 +17,7 @@ jest.mock('../../../../../components/PaginatedVirtualList', () => ({ }: { items: MessageSearchItem[]; totalCount: number; - renderItem: (item: MessageSearchItem, index: number) => React.ReactNode; + renderItem: (item: MessageSearchItem, index: number) => ReactNode; }) => (
{items.map((item, index) => ( @@ -22,27 +27,8 @@ jest.mock('../../../../../components/PaginatedVirtualList', () => ({ ), })); -jest.mock('../../../../../components/message/variants/RoomMessage', () => ({ message }: { message: IMessage }) => ( -
{message.msg}
-)); - -jest.mock('../../../../../components/message/variants/SystemMessage', () => ({ message }: { message: IMessage }) => ( -
{message.msg}
-)); - -jest.mock('../../../MessageList/MessageListErrorBoundary', () => ({ - __esModule: true, - default: ({ children }: { children: React.ReactNode }) => <>{children}, -})); - -jest.mock('../../../MessageList/providers/MessageListProvider', () => ({ - __esModule: true, - default: ({ children }: { children: React.ReactNode }) => <>{children}, -})); - -jest.mock('@rocket.chat/ui-contexts', () => ({ - ...jest.requireActual('@rocket.chat/ui-contexts'), - useTranslation: () => (key: string) => key, +jest.mock('../../../../../../app/utils/client', () => ({ + getURL: (url: string) => url, })); const createMessage = (id: string, overrides: Partial = {}): MessageSearchItem => @@ -76,9 +62,39 @@ const defaultProps = { noResultsTitle: 'No_results_found', }; +// TODO: Create a to mock the MessageListProvider +jest.mock('../../../MessageList/providers/MessageListProvider', () => ({ children }: { children: ReactNode }) => <>{children}); + +const getUserInfoMocked = jest.fn().mockResolvedValue({ user: createFakeUser({ _id: 'peer-uid', username: 'peer-username' }) }); + +const appRoot = (overrides: { user?: IUser | null; room?: IRoom; subscription?: SubscriptionWithRoom } = {}) => { + const { + user = createFakeUser({ _id: 'own-uid', username: 'own-username' }), + room = createFakeRoom({ uids: ['own-uid', 'peer-uid'] }), + subscription = createFakeSubscription(), + } = overrides; + + const root = mockAppRoot() + .withRoom(room) + .withEndpoint('GET', '/v1/users.info', getUserInfoMocked) + .wrap((children) => ( + + {children} + + )); + + if (user !== null) { + root.withUser(user); + } + + return root.build(); +}; + describe('MessageSearch', () => { it('renders the empty state when no messages are returned', () => { - render(); + render(, { + wrapper: appRoot(), + }); expect(screen.getByText('No_results_found')).toBeInTheDocument(); }); @@ -97,10 +113,12 @@ describe('MessageSearch', () => { msg: 'System event', }); - render(); + render(, { + wrapper: appRoot(), + }); - expect(screen.getByTestId('room-message')).toHaveTextContent('Message message-1'); - expect(screen.getByTestId('system-message')).toHaveTextContent('System event'); + expect(screen.getByText('Message message-1')).toBeInTheDocument(); + expect(screen.getByText('User_added_to')).toBeInTheDocument(); expect(screen.getAllByText('formatted-date')).toHaveLength(2); }); }); From 8848f240342407fd9e7e03e56fb2532de885f85e Mon Sep 17 00:00:00 2001 From: MartinSchoeler Date: Mon, 15 Jun 2026 17:29:41 -0300 Subject: [PATCH 5/9] chore simplify tests even further --- .../components/MessageSearch.spec.tsx | 108 +-- .../components/MessageSearch.stories.tsx | 24 +- .../__snapshots__/MessageSearch.spec.tsx.snap | 832 ++++++++++++++++++ 3 files changed, 862 insertions(+), 102 deletions(-) create mode 100644 apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/__snapshots__/MessageSearch.spec.tsx.snap diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx index 125273efc72e0..d4357aa47fe09 100644 --- a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx +++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx @@ -1,12 +1,9 @@ -import type { IMessage, IRoom, ISubscription, IUser } from '@rocket.chat/core-typings'; -import { mockAppRoot } from '@rocket.chat/mock-providers'; -import type { SubscriptionWithRoom } from '@rocket.chat/ui-contexts'; -import { render, screen } from '@testing-library/react'; +import { composeStories } from '@storybook/react'; +import { render } from '@testing-library/react'; +import { axe } from 'jest-axe'; import type { ReactNode } from 'react'; -import MessageSearch from './MessageSearch'; -import FakeRoomProvider from '../../../../../../tests/mocks/client/FakeRoomProvider'; -import { createFakeRoom, createFakeSubscription, createFakeUser } from '../../../../../../tests/mocks/data'; +import * as stories from './MessageSearch.stories'; import type { MessageSearchItem } from '../hooks/useMessageSearchQuery'; jest.mock('../../../../../components/PaginatedVirtualList', () => ({ @@ -31,94 +28,25 @@ jest.mock('../../../../../../app/utils/client', () => ({ getURL: (url: string) => url, })); -const createMessage = (id: string, overrides: Partial = {}): MessageSearchItem => - ({ - _id: id, - rid: 'room-id', - user: 'testuser', - msg: `Message ${id}`, - ts: new Date('2026-03-22T10:00:00.000Z'), - u: { _id: 'user-id', username: 'testuser', name: 'Test User' }, - _updatedAt: new Date('2026-03-22T10:00:00.000Z'), - ...overrides, - }) as MessageSearchItem; - -const subscription = { - tunread: ['message-1'], - tunreadUser: ['message-1'], - tunreadGroup: ['message-1'], -} as ISubscription; - -const defaultProps = { - items: [], - itemCount: 0, - isPending: false, - isSuccess: true, - fetchNextPage: jest.fn(), - subscription, - showUserAvatar: true, - formatDate: () => 'formatted-date', - searchText: 'hello', - noResultsTitle: 'No_results_found', -}; - // TODO: Create a to mock the MessageListProvider jest.mock('../../../MessageList/providers/MessageListProvider', () => ({ children }: { children: ReactNode }) => <>{children}); -const getUserInfoMocked = jest.fn().mockResolvedValue({ user: createFakeUser({ _id: 'peer-uid', username: 'peer-username' }) }); - -const appRoot = (overrides: { user?: IUser | null; room?: IRoom; subscription?: SubscriptionWithRoom } = {}) => { - const { - user = createFakeUser({ _id: 'own-uid', username: 'own-username' }), - room = createFakeRoom({ uids: ['own-uid', 'peer-uid'] }), - subscription = createFakeSubscription(), - } = overrides; - - const root = mockAppRoot() - .withRoom(room) - .withEndpoint('GET', '/v1/users.info', getUserInfoMocked) - .wrap((children) => ( - - {children} - - )); - - if (user !== null) { - root.withUser(user); - } - - return root.build(); -}; +const testCases = Object.values(composeStories(stories)).map((Story) => [Story.storyName || 'Story', Story]); -describe('MessageSearch', () => { - it('renders the empty state when no messages are returned', () => { - render(, { - wrapper: appRoot(), - }); - - expect(screen.getByText('No_results_found')).toBeInTheDocument(); - }); - - it('renders nothing until the search query succeeds', () => { - const { container } = render(); - - expect(container).toBeEmptyDOMElement(); - }); - - it('renders room and system messages with date dividers', () => { - const roomMessage = createMessage('message-1'); - const systemMessage = createMessage('message-2', { - ts: new Date('2026-03-23T10:00:00.000Z'), - t: 'au', - msg: 'System event', - }); +test.each(testCases)(`renders %s without crashing`, async (_storyname, Story) => { + const { baseElement } = render(); + expect(baseElement).toMatchSnapshot(); +}); - render(, { - wrapper: appRoot(), - }); +test.each(testCases)('%s should have no a11y violations', async (_storyname, Story) => { + const { container } = render(); - expect(screen.getByText('Message message-1')).toBeInTheDocument(); - expect(screen.getByText('User_added_to')).toBeInTheDocument(); - expect(screen.getAllByText('formatted-date')).toHaveLength(2); + const results = await axe(container, { + rules: { + 'nested-interactive': { enabled: false }, + 'aria-required-parent': { enabled: false }, + 'aria-required-children': { enabled: false }, + }, }); + expect(results).toHaveNoViolations(); }); diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.stories.tsx b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.stories.tsx index c51494f9e5460..090b9bb00a642 100644 --- a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.stories.tsx +++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.stories.tsx @@ -2,11 +2,11 @@ import type { IMessage } from '@rocket.chat/core-typings'; import { Box } from '@rocket.chat/fuselage'; import { mockAppRoot } from '@rocket.chat/mock-providers'; import { Contextualbar } from '@rocket.chat/ui-client'; -import { action } from '@storybook/addon-actions'; import type { Meta, StoryObj } from '@storybook/react'; import type { UseInfiniteQueryResult } from '@tanstack/react-query'; import MessageSearch from './MessageSearch'; +import FakeRoomProvider from '../../../../../../tests/mocks/client/FakeRoomProvider'; import { createFakeMessageWithMd, createFakeRoom, createFakeSubscription } from '../../../../../../tests/mocks/data'; import type { MessageSearchItem } from '../hooks/useMessageSearchQuery'; @@ -18,13 +18,11 @@ const subscription = createFakeSubscription({ tunreadGroup: [], }); -const fetchNextPageAction = action('fetchNextPage'); -const fetchNextPage = (async (options) => { - fetchNextPageAction(options); - return {} as Awaited>; -}) satisfies UseInfiniteQueryResult['fetchNextPage']; +const fetchNextPage = (async () => + ({}) as Awaited>) satisfies UseInfiniteQueryResult['fetchNextPage']; -const formatDate = (date: Date | string | number): string => new Intl.DateTimeFormat('en', { dateStyle: 'medium' }).format(new Date(date)); +const formatDate = (date: Date | string | number): string => + new Intl.DateTimeFormat('en-US', { dateStyle: 'medium', timeZone: 'UTC' }).format(new Date(date)); const createMessage = (overrides: Partial): MessageSearchItem => createFakeMessageWithMd({ @@ -102,11 +100,13 @@ const meta = { decorators: [ mockAppRoot().withJohnDoe().withRoom(room).withSubscription(subscription).buildStoryDecorator(), (fn) => ( - - - {fn()} - - + + + + {fn()} + + + ), ], args: { diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/__snapshots__/MessageSearch.spec.tsx.snap b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/__snapshots__/MessageSearch.spec.tsx.snap new file mode 100644 index 0000000000000..be92f861f5d64 --- /dev/null +++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/__snapshots__/MessageSearch.spec.tsx.snap @@ -0,0 +1,832 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`renders Empty without crashing 1`] = ` + +
+
+
+
+
+ +

+ No results found +

+
+
+
+
+
+ +`; + +exports[`renders Messages without crashing 1`] = ` + +
+
+
+
+
+
+
+
+
+
+
+ +`; + +exports[`renders MultipleDateGroups without crashing 1`] = ` + +
+
+
+
+
+
+
+
+
+
+
+ +`; + +exports[`renders SystemMessages without crashing 1`] = ` + +
+
+
+
+
+
+
+ +
+
+
+
+
+ +`; From c041d39f9385c12523ec91c518d4f961a17c1e84 Mon Sep 17 00:00:00 2001 From: MartinSchoeler Date: Mon, 15 Jun 2026 17:32:39 -0300 Subject: [PATCH 6/9] chore: compound stories into one --- .../components/MessageSearch.stories.tsx | 72 +-- .../__snapshots__/MessageSearch.spec.tsx.snap | 478 +++++++----------- 2 files changed, 218 insertions(+), 332 deletions(-) diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.stories.tsx b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.stories.tsx index 090b9bb00a642..93ea9b116ee0a 100644 --- a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.stories.tsx +++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.stories.tsx @@ -35,7 +35,29 @@ const createMessage = (overrides: Partial): MessageSearchItem => ...overrides, }) as MessageSearchItem; -const messages = [ +const searchResults = [ + createMessage({ + _id: 'date-message-1', + msg: 'Initial search result from Monday.', + ts: new Date('2026-06-08T10:00:00.000Z'), + }), + createMessage({ + _id: 'system-message-1', + msg: 'Sam Chen joined the room', + t: 'uj', + ts: new Date('2026-06-09T09:00:00.000Z'), + }), + createMessage({ + _id: 'system-message-2', + msg: 'Room topic changed to Release coordination', + t: 'room_changed_topic', + ts: new Date('2026-06-09T09:05:00.000Z'), + }), + createMessage({ + _id: 'date-message-2', + msg: 'Follow-up result from Tuesday.', + ts: new Date('2026-06-09T10:00:00.000Z'), + }), createMessage({ _id: 'message-1', msg: 'Can you share the deployment checklist?', @@ -56,34 +78,6 @@ const messages = [ msg: 'I found the rollback notes as well.', ts: new Date('2026-06-09T14:22:00.000Z'), }), -]; - -const systemMessages = [ - createMessage({ - _id: 'system-message-1', - msg: 'Sam Chen joined the room', - t: 'uj', - ts: new Date('2026-06-09T09:00:00.000Z'), - }), - createMessage({ - _id: 'system-message-2', - msg: 'Room topic changed to Release coordination', - t: 'room_changed_topic', - ts: new Date('2026-06-09T09:05:00.000Z'), - }), -]; - -const multipleDateMessages = [ - createMessage({ - _id: 'date-message-1', - msg: 'Initial search result from Monday.', - ts: new Date('2026-06-08T10:00:00.000Z'), - }), - createMessage({ - _id: 'date-message-2', - msg: 'Follow-up result from Tuesday.', - ts: new Date('2026-06-09T10:00:00.000Z'), - }), createMessage({ _id: 'date-message-3', msg: 'Final result from Wednesday.', @@ -133,23 +127,9 @@ export const Empty: Story = { }, }; -export const Messages: Story = { - args: { - items: messages, - itemCount: messages.length, - }, -}; - -export const SystemMessages: Story = { - args: { - items: systemMessages, - itemCount: systemMessages.length, - }, -}; - -export const MultipleDateGroups: Story = { +export const WithResults: Story = { args: { - items: multipleDateMessages, - itemCount: multipleDateMessages.length, + items: searchResults, + itemCount: searchResults.length, }, }; diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/__snapshots__/MessageSearch.spec.tsx.snap b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/__snapshots__/MessageSearch.spec.tsx.snap index be92f861f5d64..cda1af676124a 100644 --- a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/__snapshots__/MessageSearch.spec.tsx.snap +++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/__snapshots__/MessageSearch.spec.tsx.snap @@ -34,7 +34,7 @@ exports[`renders Empty without crashing 1`] = ` `; -exports[`renders Messages without crashing 1`] = ` +exports[`renders WithResults without crashing 1`] = `
- Jun 9, 2026 + Jun 8, 2026
@@ -116,7 +116,7 @@ exports[`renders Messages without crashing 1`] = `
- Can you share the deployment checklist? + Initial search result from Monday.
@@ -158,15 +158,146 @@ exports[`renders Messages without crashing 1`] = `
@@ -288,7 +415,7 @@ exports[`renders Messages without crashing 1`] = `
- I found the rollback notes as well. + Can you share the deployment checklist?
@@ -328,64 +455,17 @@ exports[`renders Messages without crashing 1`] = ` />
-
-
- -
-
-
- -`; - -exports[`renders MultipleDateGroups without crashing 1`] = ` - -
-
-
-
-
-
-
@@ -169,7 +168,6 @@ exports[`renders WithResults without crashing 1`] = ` > Jun 9, 2026
-
@@ -530,7 +528,7 @@ exports[`renders WithResults without crashing 1`] = ` role="document" >
- The checklist is attached to the + The checklist is attached to the release @@ -640,7 +638,6 @@ exports[`renders WithResults without crashing 1`] = ` > Jun 10, 2026
-
From 39b5842fbd3afc408d78777004887658207b5d6f Mon Sep 17 00:00:00 2001 From: srijna Date: Mon, 29 Jun 2026 15:17:01 +0530 Subject: [PATCH 9/9] test: align message search virtual list mock --- .../components/MessageSearch.spec.tsx | 7 ++-- .../__snapshots__/MessageSearch.spec.tsx.snap | 36 +++++++++---------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx index 1d90d24f5baa7..54590e34008e3 100644 --- a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx +++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx @@ -16,11 +16,11 @@ jest.mock('../../../../../components/PaginatedVirtualList', () => ({ totalCount: number; renderItem: (item: MessageSearchItem, index: number) => ReactNode; }) => ( -
+
    {items.map((item, index) => ( -
    {renderItem(item, index)}
    +
  • {renderItem(item, index)}
  • ))} -
+ ), })); @@ -64,7 +64,6 @@ test.each(testCases)('%s should have no a11y violations', async (_storyname, Sto rules: { 'nested-interactive': { enabled: false }, 'aria-required-parent': { enabled: false }, - 'aria-required-children': { enabled: false }, }, }); expect(results).toHaveNoViolations(); diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/__snapshots__/MessageSearch.spec.tsx.snap b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/__snapshots__/MessageSearch.spec.tsx.snap index e034b069585d6..90e2a4c39a0b7 100644 --- a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/__snapshots__/MessageSearch.spec.tsx.snap +++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/__snapshots__/MessageSearch.spec.tsx.snap @@ -51,11 +51,11 @@ exports[`renders WithResults without crashing 1`] = ` class="rcx-box rcx-box--full rcx-css-1cb6i7s" style="min-height: 0;" > -
-
+
  • -
  • -
    + +
  • -
    -
    + +
  • -
    -
    + +
  • -
  • -
    + +
  • -
  • -
    + +
  • -
  • -
    + +
  • -
  • -
    + +
  • -
  • -
    + +