Add tests
This commit is contained in:
parent
a68aeb8464
commit
81bfc06990
|
@ -216,7 +216,12 @@ class DropdownMenu extends React.PureComponent<IDropdownMenu, IDropdownMenuState
|
||||||
// It should not be transformed when mounting because the resulting
|
// It should not be transformed when mounting because the resulting
|
||||||
// size will be used to determine the coordinate of the menu by
|
// size will be used to determine the coordinate of the menu by
|
||||||
// react-overlays
|
// react-overlays
|
||||||
<div className={`dropdown-menu ${placement}`} style={{ ...style, opacity: opacity, transform: mounted ? `scale(${scaleX}, ${scaleY})` : undefined }} ref={this.setRef}>
|
<div
|
||||||
|
className={`dropdown-menu ${placement}`}
|
||||||
|
style={{ ...style, opacity: opacity, transform: mounted ? `scale(${scaleX}, ${scaleY})` : undefined }}
|
||||||
|
ref={this.setRef}
|
||||||
|
data-testid='dropdown-menu'
|
||||||
|
>
|
||||||
<div className={`dropdown-menu__arrow ${placement}`} style={{ left: arrowOffsetLeft, top: arrowOffsetTop }} />
|
<div className={`dropdown-menu__arrow ${placement}`} style={{ left: arrowOffsetLeft, top: arrowOffsetTop }} />
|
||||||
<ul>
|
<ul>
|
||||||
{items.map((option, i) => this.renderItem(option, i))}
|
{items.map((option, i) => this.renderItem(option, i))}
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { ChatContext } from 'soapbox/contexts/chat-context';
|
||||||
|
import { IAccount } from 'soapbox/queries/accounts';
|
||||||
|
|
||||||
|
import { __stub } from '../../../../api';
|
||||||
|
import { queryClient, render, rootState, screen, waitFor } from '../../../../jest/test-helpers';
|
||||||
|
import { IChat, IChatMessage } from '../../../../queries/chats';
|
||||||
|
import ChatMessageList from '../chat-message-list';
|
||||||
|
|
||||||
|
const chat: IChat = {
|
||||||
|
id: '14',
|
||||||
|
unread: 5,
|
||||||
|
created_by_account: '2',
|
||||||
|
account: {
|
||||||
|
id: '1',
|
||||||
|
avatar: 'url',
|
||||||
|
acct: 'username',
|
||||||
|
} as IAccount,
|
||||||
|
last_message: null,
|
||||||
|
accepted: true,
|
||||||
|
} as IChat;
|
||||||
|
|
||||||
|
const chatMessages: IChatMessage[] = [
|
||||||
|
{
|
||||||
|
account_id: '1',
|
||||||
|
chat_id: '14',
|
||||||
|
content: 'this is the first chat',
|
||||||
|
created_at: new Date('2022-09-09T16:02:26.186Z'),
|
||||||
|
id: '1',
|
||||||
|
unread: false,
|
||||||
|
pending: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
account_id: '2',
|
||||||
|
chat_id: '14',
|
||||||
|
content: 'this is the second chat',
|
||||||
|
created_at: new Date('2022-09-09T16:04:26.186Z'),
|
||||||
|
id: '2',
|
||||||
|
unread: true,
|
||||||
|
pending: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Mock scrollIntoView function.
|
||||||
|
window.HTMLElement.prototype.scrollIntoView = function() { };
|
||||||
|
Object.assign(navigator, {
|
||||||
|
clipboard: {
|
||||||
|
writeText: () => {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const store = rootState.set('me', '1');
|
||||||
|
|
||||||
|
const renderComponentWithChatContext = () => render(
|
||||||
|
<ChatContext.Provider value={{ chat }}>
|
||||||
|
<ChatMessageList chat={chat} />
|
||||||
|
</ChatContext.Provider>,
|
||||||
|
undefined,
|
||||||
|
store,
|
||||||
|
);
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
queryClient.clear();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('<ChatMessageList />', () => {
|
||||||
|
describe('when the query is loading', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
__stub((mock) => {
|
||||||
|
mock.onGet(`/api/v1/pleroma/chats/${chat.id}/messages`).reply(200, chatMessages, {
|
||||||
|
link: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays the skeleton loader', async() => {
|
||||||
|
renderComponentWithChatContext();
|
||||||
|
|
||||||
|
expect(screen.queryAllByTestId('placeholder-chat-message')).toHaveLength(5);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByTestId('chat-message-list-intro')).toBeInTheDocument();
|
||||||
|
expect(screen.queryAllByTestId('placeholder-chat-message')).toHaveLength(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the query is finished loading', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
__stub((mock) => {
|
||||||
|
mock.onGet(`/api/v1/pleroma/chats/${chat.id}/messages`).reply(200, chatMessages, {
|
||||||
|
link: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays the intro', async() => {
|
||||||
|
renderComponentWithChatContext();
|
||||||
|
|
||||||
|
expect(screen.queryAllByTestId('chat-message-list-intro')).toHaveLength(0);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByTestId('chat-message-list-intro')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays the messages', async() => {
|
||||||
|
renderComponentWithChatContext();
|
||||||
|
|
||||||
|
expect(screen.queryAllByTestId('chat-message')).toHaveLength(0);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.queryAllByTestId('chat-message')).toHaveLength(chatMessages.length);
|
||||||
|
expect(screen.queryAllByTestId('chat-message')[0]).toHaveTextContent(chatMessages[0].content);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays the correct menu options depending on the owner of the message', async() => {
|
||||||
|
renderComponentWithChatContext();
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.queryAllByTestId('chat-message-menu')).toHaveLength(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
await userEvent.click(screen.queryAllByTestId('chat-message-menu')[0].querySelector('button') as any);
|
||||||
|
expect(screen.getByTestId('dropdown-menu')).toHaveTextContent('Delete');
|
||||||
|
expect(screen.getByTestId('dropdown-menu')).toHaveTextContent('Copy');
|
||||||
|
|
||||||
|
await userEvent.click(screen.queryAllByTestId('chat-message-menu')[1].querySelector('button') as any);
|
||||||
|
expect(screen.getByTestId('dropdown-menu')).not.toHaveTextContent('Delete');
|
||||||
|
expect(screen.getByTestId('dropdown-menu')).toHaveTextContent('Copy');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,83 @@
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { render, screen } from '../../../../jest/test-helpers';
|
||||||
|
import ChatPaneHeader from '../chat-pane-header';
|
||||||
|
|
||||||
|
describe('<ChatPaneHeader />', () => {
|
||||||
|
it('handles the onToggle prop', async() => {
|
||||||
|
const mockFn = jest.fn();
|
||||||
|
render(<ChatPaneHeader title='title' onToggle={mockFn} isOpen />);
|
||||||
|
|
||||||
|
await userEvent.click(screen.getByTestId('icon-button'));
|
||||||
|
expect(mockFn).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('the "title" prop', () => {
|
||||||
|
describe('when it is a string', () => {
|
||||||
|
it('renders the title', () => {
|
||||||
|
const title = 'Messages';
|
||||||
|
render(<ChatPaneHeader title={title} onToggle={jest.fn()} isOpen />);
|
||||||
|
|
||||||
|
expect(screen.getByTestId('title')).toHaveTextContent(title);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when it is a node', () => {
|
||||||
|
it('renders the title', () => {
|
||||||
|
const title = (
|
||||||
|
<div><p>hello world</p></div>
|
||||||
|
);
|
||||||
|
render(<ChatPaneHeader title={title} onToggle={jest.fn()} isOpen />);
|
||||||
|
|
||||||
|
expect(screen.getByTestId('title')).toHaveTextContent('hello world');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('the "unreadCount" prop', () => {
|
||||||
|
describe('when present', () => {
|
||||||
|
it('renders the unread count', () => {
|
||||||
|
const count = 14;
|
||||||
|
render(<ChatPaneHeader title='title' onToggle={jest.fn()} isOpen unreadCount={count} />);
|
||||||
|
|
||||||
|
expect(screen.getByTestId('unread-count')).toHaveTextContent(String(count));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when 0', () => {
|
||||||
|
it('does not render the unread count', () => {
|
||||||
|
const count = 0;
|
||||||
|
render(<ChatPaneHeader title='title' onToggle={jest.fn()} isOpen unreadCount={count} />);
|
||||||
|
|
||||||
|
expect(screen.queryAllByTestId('unread-count')).toHaveLength(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when unprovided', () => {
|
||||||
|
it('does not render the unread count', () => {
|
||||||
|
render(<ChatPaneHeader title='title' onToggle={jest.fn()} isOpen />);
|
||||||
|
|
||||||
|
expect(screen.queryAllByTestId('unread-count')).toHaveLength(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('secondaryAction prop', () => {
|
||||||
|
it('handles the secondaryAction callback', async() => {
|
||||||
|
const mockFn = jest.fn();
|
||||||
|
render(
|
||||||
|
<ChatPaneHeader
|
||||||
|
title='title'
|
||||||
|
onToggle={jest.fn()}
|
||||||
|
isOpen
|
||||||
|
secondaryAction={mockFn}
|
||||||
|
secondaryActionIcon='icon.svg'
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
await userEvent.click(screen.queryAllByTestId('icon-button')[0]);
|
||||||
|
expect(mockFn).toBeCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,65 @@
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { __stub } from 'soapbox/api';
|
||||||
|
import { ChatProvider } from 'soapbox/contexts/chat-context';
|
||||||
|
|
||||||
|
import { render, screen, waitFor } from '../../../../jest/test-helpers';
|
||||||
|
import ChatSearch from '../chat-search';
|
||||||
|
|
||||||
|
const renderComponent = () => render(
|
||||||
|
<ChatProvider>
|
||||||
|
<ChatSearch />
|
||||||
|
</ChatProvider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
describe('<ChatSearch />', () => {
|
||||||
|
it('renders correctly', () => {
|
||||||
|
renderComponent();
|
||||||
|
|
||||||
|
expect(screen.getByTestId('pane-header')).toHaveTextContent('Messages');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the pane is closed', () => {
|
||||||
|
it('does not render the search input', () => {
|
||||||
|
renderComponent();
|
||||||
|
expect(screen.queryAllByTestId('search')).toHaveLength(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the pane is open', () => {
|
||||||
|
beforeEach(async() => {
|
||||||
|
renderComponent();
|
||||||
|
await userEvent.click(screen.getByTestId('icon-button'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the search input', () => {
|
||||||
|
expect(screen.getByTestId('search')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when searching', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
__stub((mock) => {
|
||||||
|
mock.onGet('/api/v1/accounts/search').reply(200, [{
|
||||||
|
id: '1',
|
||||||
|
avatar: 'url',
|
||||||
|
verified: false,
|
||||||
|
display_name: 'steve',
|
||||||
|
acct: 'sjobs',
|
||||||
|
}]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders accounts', async() => {
|
||||||
|
renderComponent();
|
||||||
|
|
||||||
|
const user = userEvent.setup();
|
||||||
|
await user.type(screen.getByTestId('search'), 'ste');
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.queryAllByTestId('account')).toHaveLength(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,68 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { IChat } from 'soapbox/queries/chats';
|
||||||
|
|
||||||
|
import { render, screen } from '../../../../jest/test-helpers';
|
||||||
|
import Chat from '../chat';
|
||||||
|
|
||||||
|
const chat: any = {
|
||||||
|
id: '1',
|
||||||
|
unread: 5,
|
||||||
|
created_by_account: '2',
|
||||||
|
last_message: {
|
||||||
|
account_id: '2',
|
||||||
|
chat_id: '1',
|
||||||
|
content: 'hello world',
|
||||||
|
created_at: '2022-09-09T16:02:26.186Z',
|
||||||
|
discarded_at: null,
|
||||||
|
id: '12332423234',
|
||||||
|
unread: true,
|
||||||
|
},
|
||||||
|
created_at: new Date('2022-09-09T16:02:26.186Z'),
|
||||||
|
updated_at: new Date('2022-09-09T16:02:26.186Z'),
|
||||||
|
accepted: true,
|
||||||
|
discarded_at: null,
|
||||||
|
account: {
|
||||||
|
acct: 'username',
|
||||||
|
display_name: 'johnnie',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('<Chat />', () => {
|
||||||
|
it('renders correctly', () => {
|
||||||
|
render(<Chat chat={chat as IChat} onClick={jest.fn()} />);
|
||||||
|
|
||||||
|
expect(screen.getByTestId('chat')).toBeInTheDocument();
|
||||||
|
expect(screen.getByTestId('chat')).toHaveTextContent(chat.account.display_name);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('last message content', () => {
|
||||||
|
it('renders the last message', () => {
|
||||||
|
render(<Chat chat={chat as IChat} onClick={jest.fn()} />);
|
||||||
|
|
||||||
|
expect(screen.getByTestId('chat-last-message')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not render the last message', () => {
|
||||||
|
const changedChat = { ...chat, last_message: null };
|
||||||
|
render(<Chat chat={changedChat as IChat} onClick={jest.fn()} />);
|
||||||
|
|
||||||
|
expect(screen.queryAllByTestId('chat-last-message')).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('unread', () => {
|
||||||
|
it('renders the unread dot', () => {
|
||||||
|
render(<Chat chat={chat as IChat} onClick={jest.fn()} />);
|
||||||
|
|
||||||
|
expect(screen.getByTestId('chat-unread-indicator')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not render the unread dot', () => {
|
||||||
|
const changedChat = { ...chat, last_message: { ...chat.last_message, unread: false } };
|
||||||
|
render(<Chat chat={changedChat as IChat} onClick={jest.fn()} />);
|
||||||
|
|
||||||
|
expect(screen.queryAllByTestId('chat-unread-indicator')).toHaveLength(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -37,6 +37,7 @@ const ChatMessageListIntro = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack
|
<Stack
|
||||||
|
data-testid='chat-message-list-intro'
|
||||||
justifyContent='center'
|
justifyContent='center'
|
||||||
alignItems='center'
|
alignItems='center'
|
||||||
space={4}
|
space={4}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import classNames from 'clsx';
|
||||||
import { List as ImmutableList } from 'immutable';
|
import { List as ImmutableList } from 'immutable';
|
||||||
import escape from 'lodash/escape';
|
import escape from 'lodash/escape';
|
||||||
import throttle from 'lodash/throttle';
|
import throttle from 'lodash/throttle';
|
||||||
import React, { useState, useEffect, useRef, useLayoutEffect } from 'react';
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
import { useIntl, defineMessages } from 'react-intl';
|
import { useIntl, defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import { openModal } from 'soapbox/actions/modals';
|
import { openModal } from 'soapbox/actions/modals';
|
||||||
|
@ -81,7 +81,7 @@ const ChatMessageList: React.FC<IChatMessageList> = ({ chat, autosize }) => {
|
||||||
const formattedChatMessages = chatMessages || [];
|
const formattedChatMessages = chatMessages || [];
|
||||||
|
|
||||||
const me = useAppSelector((state) => state.me);
|
const me = useAppSelector((state) => state.me);
|
||||||
const isBlocked = useAppSelector((state) => state.getIn(['relationships', chat.account, 'blocked_by']));
|
const isBlocked = useAppSelector((state) => state.getIn(['relationships', chat.account.id, 'blocked_by']));
|
||||||
|
|
||||||
const node = useRef<HTMLDivElement>(null);
|
const node = useRef<HTMLDivElement>(null);
|
||||||
const messagesEnd = useRef<HTMLDivElement>(null);
|
const messagesEnd = useRef<HTMLDivElement>(null);
|
||||||
|
@ -240,7 +240,7 @@ const ChatMessageList: React.FC<IChatMessageList> = ({ chat, autosize }) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={chatMessage.id} className='group'>
|
<div key={chatMessage.id} className='group' data-testid='chat-message'>
|
||||||
<Stack
|
<Stack
|
||||||
space={1}
|
space={1}
|
||||||
className={classNames({
|
className={classNames({
|
||||||
|
@ -293,7 +293,7 @@ const ChatMessageList: React.FC<IChatMessageList> = ({ chat, autosize }) => {
|
||||||
>
|
>
|
||||||
{maybeRenderMedia(chatMessage)}
|
{maybeRenderMedia(chatMessage)}
|
||||||
<Text size='sm' theme='inherit' dangerouslySetInnerHTML={{ __html: parseContent(chatMessage) }} />
|
<Text size='sm' theme='inherit' dangerouslySetInnerHTML={{ __html: parseContent(chatMessage) }} />
|
||||||
<div className='chat-message__menu'>
|
<div className='chat-message__menu' data-testid='chat-message-menu'>
|
||||||
<DropdownMenuContainer
|
<DropdownMenuContainer
|
||||||
items={menu}
|
items={menu}
|
||||||
src={require('@tabler/icons/dots.svg')}
|
src={require('@tabler/icons/dots.svg')}
|
||||||
|
@ -395,7 +395,7 @@ const ChatMessageList: React.FC<IChatMessageList> = ({ chat, autosize }) => {
|
||||||
return (
|
return (
|
||||||
<Stack alignItems='center' justifyContent='center' className='h-full flex-grow'>
|
<Stack alignItems='center' justifyContent='center' className='h-full flex-grow'>
|
||||||
<Stack alignItems='center' space={2}>
|
<Stack alignItems='center' space={2}>
|
||||||
<Avatar src={chat.account.avatar_static} size={75} />
|
<Avatar src={chat.account.avatar} size={75} />
|
||||||
<Text align='center'>
|
<Text align='center'>
|
||||||
<>
|
<>
|
||||||
<Text tag='span'>You are blocked by</Text>
|
<Text tag='span'>You are blocked by</Text>
|
||||||
|
|
|
@ -21,6 +21,7 @@ const ChatPaneHeader = (props: IChatPaneHeader) => {
|
||||||
secondaryActionIcon,
|
secondaryActionIcon,
|
||||||
title,
|
title,
|
||||||
unreadCount,
|
unreadCount,
|
||||||
|
...rest
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const ButtonComp = isToggleable ? 'button' : 'div';
|
const ButtonComp = isToggleable ? 'button' : 'div';
|
||||||
|
@ -30,9 +31,10 @@ const ChatPaneHeader = (props: IChatPaneHeader) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HStack alignItems='center' justifyContent='between' className='rounded-t-xl h-16 py-3 px-4'>
|
<HStack {...rest} alignItems='center' justifyContent='between' className='rounded-t-xl h-16 py-3 px-4'>
|
||||||
<ButtonComp
|
<ButtonComp
|
||||||
className='flex-grow flex items-center flex-row space-x-1 h-16'
|
className='flex-grow flex items-center flex-row space-x-1 h-16'
|
||||||
|
data-testid='title'
|
||||||
{...buttonProps}
|
{...buttonProps}
|
||||||
>
|
>
|
||||||
{typeof title === 'string' ? (
|
{typeof title === 'string' ? (
|
||||||
|
@ -43,7 +45,7 @@ const ChatPaneHeader = (props: IChatPaneHeader) => {
|
||||||
|
|
||||||
{(typeof unreadCount !== 'undefined' && unreadCount > 0) && (
|
{(typeof unreadCount !== 'undefined' && unreadCount > 0) && (
|
||||||
<HStack alignItems='center' space={2}>
|
<HStack alignItems='center' space={2}>
|
||||||
<Text weight='semibold'>
|
<Text weight='semibold' data-testid='unread-count'>
|
||||||
({unreadCount})
|
({unreadCount})
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,7 @@ const ChatSearch = () => {
|
||||||
return (
|
return (
|
||||||
<Pane isOpen={isOpen} index={0} main>
|
<Pane isOpen={isOpen} index={0} main>
|
||||||
<ChatPaneHeader
|
<ChatPaneHeader
|
||||||
|
data-testid='pane-header'
|
||||||
title={
|
title={
|
||||||
<HStack alignItems='center' space={2}>
|
<HStack alignItems='center' space={2}>
|
||||||
<button onClick={() => setSearching(false)}>
|
<button onClick={() => setSearching(false)}>
|
||||||
|
@ -71,6 +72,7 @@ const ChatSearch = () => {
|
||||||
<Stack space={4} className='flex-grow h-full'>
|
<Stack space={4} className='flex-grow h-full'>
|
||||||
<div className='px-4'>
|
<div className='px-4'>
|
||||||
<Input
|
<Input
|
||||||
|
data-testid='search'
|
||||||
type='text'
|
type='text'
|
||||||
autoFocus
|
autoFocus
|
||||||
placeholder='Type a name'
|
placeholder='Type a name'
|
||||||
|
@ -100,6 +102,7 @@ const ChatSearch = () => {
|
||||||
handleClickOnSearchResult.mutate(account.id);
|
handleClickOnSearchResult.mutate(account.id);
|
||||||
clearValue();
|
clearValue();
|
||||||
}}
|
}}
|
||||||
|
data-testid='account'
|
||||||
>
|
>
|
||||||
<HStack alignItems='center' space={2}>
|
<HStack alignItems='center' space={2}>
|
||||||
<Avatar src={account.avatar} size={40} />
|
<Avatar src={account.avatar} size={40} />
|
||||||
|
|
|
@ -18,6 +18,7 @@ const Chat: React.FC<IChatInterface> = ({ chat, onClick }) => {
|
||||||
type='button'
|
type='button'
|
||||||
onClick={() => onClick(chat)}
|
onClick={() => onClick(chat)}
|
||||||
className='px-4 py-2 w-full flex flex-col hover:bg-gray-100 dark:hover:bg-gray-800'
|
className='px-4 py-2 w-full flex flex-col hover:bg-gray-100 dark:hover:bg-gray-800'
|
||||||
|
data-testid='chat'
|
||||||
>
|
>
|
||||||
<HStack alignItems='center' justifyContent='between' space={2} className='w-full'>
|
<HStack alignItems='center' justifyContent='between' space={2} className='w-full'>
|
||||||
<HStack alignItems='center' space={2}>
|
<HStack alignItems='center' space={2}>
|
||||||
|
@ -30,9 +31,16 @@ const Chat: React.FC<IChatInterface> = ({ chat, onClick }) => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{chat.last_message?.content && (
|
{chat.last_message?.content && (
|
||||||
<Text align='left' size='sm' weight='medium' theme='muted' truncate className='max-w-[200px]'>
|
<Text
|
||||||
{chat.last_message?.content}
|
align='left'
|
||||||
</Text>
|
size='sm'
|
||||||
|
weight='medium'
|
||||||
|
theme='muted'
|
||||||
|
truncate
|
||||||
|
className='max-w-[200px]'
|
||||||
|
data-testid='chat-last-message'
|
||||||
|
dangerouslySetInnerHTML={{ __html: chat.last_message?.content }}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
</HStack>
|
</HStack>
|
||||||
|
@ -40,7 +48,10 @@ const Chat: React.FC<IChatInterface> = ({ chat, onClick }) => {
|
||||||
{chat.last_message && (
|
{chat.last_message && (
|
||||||
<HStack alignItems='center' space={2}>
|
<HStack alignItems='center' space={2}>
|
||||||
{chat.last_message.unread && (
|
{chat.last_message.unread && (
|
||||||
<div className='w-2 h-2 rounded-full bg-secondary-500' />
|
<div
|
||||||
|
className='w-2 h-2 rounded-full bg-secondary-500'
|
||||||
|
data-testid='chat-unread-indicator'
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<RelativeTimestamp timestamp={chat.last_message.created_at} size='sm' />
|
<RelativeTimestamp timestamp={chat.last_message.created_at} size='sm' />
|
||||||
|
|
|
@ -13,6 +13,7 @@ const PlaceholderChatMessage = ({ isMyMessage = false }: { isMyMessage?: boolean
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack
|
<Stack
|
||||||
|
data-testid='placeholder-chat-message'
|
||||||
space={1}
|
space={1}
|
||||||
className={classNames({
|
className={classNames({
|
||||||
'max-w-[85%] animate-pulse': true,
|
'max-w-[85%] animate-pulse': true,
|
||||||
|
|
Loading…
Reference in New Issue