From 2ea17fabc19f94f2891bc3d571f21e7f94a43a5e Mon Sep 17 00:00:00 2001 From: martincupela Date: Tue, 21 May 2024 11:26:52 +0200 Subject: [PATCH 01/13] refactor: remove theme v1 related components --- .../Attachment/AttachmentContainer.tsx | 16 +- src/components/Attachment/Audio.tsx | 85 +-- src/components/Attachment/Card.tsx | 128 +--- src/components/Attachment/FileAttachment.tsx | 37 +- .../Attachment/UnsupportedAttachment.tsx | 2 - src/components/Attachment/VoiceRecording.tsx | 4 +- .../Attachment/__tests__/Audio.test.js | 280 ++++--- .../Attachment/__tests__/Card.test.js | 705 +++++------------- .../Attachment/__tests__/File.test.js | 18 +- .../__snapshots__/Audio.test.js.snap | 69 -- .../__tests__/__snapshots__/Card.test.js.snap | 67 +- .../__tests__/__snapshots__/File.test.js.snap | 40 +- src/components/Attachment/utils.tsx | 249 +------ .../AutoCompleteTextarea/Header.tsx | 44 -- src/components/AutoCompleteTextarea/Item.jsx | 37 +- src/components/AutoCompleteTextarea/List.jsx | 17 +- .../AutoCompleteTextarea/Textarea.jsx | 10 +- src/components/AutoCompleteTextarea/index.ts | 2 - src/components/Avatar/Avatar.tsx | 39 +- .../Avatar/__tests__/Avatar.test.js | 94 +-- src/components/Channel/Channel.tsx | 3 - .../__snapshots__/Channel.test.js.snap | 8 +- .../hooks/useChannelContainerClasses.ts | 2 +- .../ChannelHeader/ChannelHeader.tsx | 15 +- .../__tests__/ChannelHeader.test.js | 32 +- src/components/ChannelList/ChannelList.tsx | 14 +- .../ChannelList/ChannelListMessenger.tsx | 17 +- .../ChannelList/__tests__/ChannelList.test.js | 290 +++---- .../ChannelPreviewMessenger.tsx | 8 +- .../ChannelPreviewMessenger.test.js.snap | 22 +- .../ChannelSearch/ChannelSearch.tsx | 55 +- .../ChannelSearch/SearchResults.tsx | 25 +- .../__tests__/ChannelSearch.test.js | 2 +- .../ChannelSearch/__tests__/SearchBar.test.js | 5 - .../__tests__/SearchResults.test.js | 176 +++-- .../ChannelSearch/hooks/useChannelSearch.ts | 9 +- src/components/Chat/Chat.tsx | 39 +- .../Chat/hooks/useCreateChatContext.ts | 2 - src/components/Chat/hooks/useCustomStyles.ts | 133 ---- src/components/Chat/index.ts | 1 - .../ChatAutoComplete/ChatAutoComplete.tsx | 3 - .../__tests__/ChatAutocomplete.test.js | 20 +- src/components/ChatDown/ChatDown.tsx | 42 -- .../ChatDown/__tests__/ChatDown.test.js | 58 -- .../__snapshots__/ChatDown.test.js.snap | 102 --- src/components/ChatDown/icons.tsx | 27 - src/components/ChatDown/index.ts | 1 - src/components/Emojis/EmojiPicker.tsx | 37 +- .../EmptyStateIndicator.tsx | 3 - .../__tests__/EmptyStateIndicator.test.js | 3 +- .../Gallery/__tests__/Image.test.js | 2 +- .../__snapshots__/Gallery.test.js.snap | 2 +- .../AudioRecorder/AudioRecorder.tsx | 4 +- .../__tests__/AudioRecorder.test.js | 2 - src/components/Message/FixedHeightMessage.tsx | 2 - .../Message/MessageRepliesCountButton.tsx | 8 +- src/components/Message/MessageSimple.tsx | 28 +- src/components/Message/MessageStatus.tsx | 63 +- src/components/Message/MessageText.tsx | 15 +- src/components/Message/QuotedMessage.tsx | 13 +- .../MessageRepliesCountButton.test.js | 24 +- .../Message/__tests__/MessageSimple.test.js | 15 +- .../Message/__tests__/MessageText.test.js | 111 +-- src/components/Message/icons.tsx | 29 - .../CustomMessageActionsList.tsx | 7 - .../MessageInput/AttachmentPreviewList.tsx | 81 +- .../MessageInput/EditMessageForm.tsx | 119 +-- .../MessageInput/MessageInputFlat.tsx | 186 ++--- .../MessageInput/MessageInputSmall.tsx | 148 ---- .../MessageInput/QuotedMessagePreview.tsx | 41 +- src/components/MessageInput/SendButton.tsx | 31 +- .../MessageInput/UploadsPreview.tsx | 69 -- .../__tests__/LinkPreviewList.test.js | 59 +- .../__tests__/MessageInput.test.js | 540 +++++++------- .../AttachmentPreviewList.test.js.snap | 7 +- .../MessageInput/hooks/useCommandTrigger.ts | 4 +- .../MessageInput/hooks/useEmojiTrigger.ts | 59 +- .../hooks/useMessageInputState.ts | 7 +- .../MessageInput/hooks/usePasteHandler.ts | 8 +- .../MessageInput/hooks/useUserTrigger.ts | 4 +- src/components/MessageInput/icons.tsx | 120 +-- src/components/MessageInput/index.ts | 2 - src/components/MessageList/MessageList.tsx | 10 +- .../MessageList/MessageListMainPanel.tsx | 12 +- .../MessageList/ScrollToBottomButton.tsx | 2 - .../VirtualizedMessageListComponents.tsx | 2 +- .../MessageList/__tests__/MessageList.test.js | 40 +- .../VirtualizedMessageList.test.js.snap | 86 ++- src/components/Modal/Modal.tsx | 17 +- .../ReactFileUtilities/FileIcon/FileIcon.tsx | 25 +- .../{FileIconSet/v2.tsx => FileIconSet.tsx} | 87 +-- .../FileIcon/FileIconSet/v1.tsx | 185 ----- .../ReactFileUtilities/FileIcon/iconMap.ts | 107 +-- .../ReactFileUtilities/FilePreviewer.tsx | 68 -- .../ReactFileUtilities/FileUploadButton.tsx | 48 -- .../ReactFileUtilities/IconButton.tsx | 24 - .../ReactFileUtilities/ImagePreviewer.tsx | 83 --- .../ReactFileUtilities/ImageUploadButton.tsx | 41 - .../ReactFileUtilities/Thumbnail.tsx | 39 - .../ThumbnailPlaceholder.tsx | 21 - .../icons/AttachmentIcon.tsx | 11 - .../ReactFileUtilities/icons/CloseIcon.tsx | 27 - .../icons/FilePlaceholderIcon.tsx | 20 - .../ReactFileUtilities/icons/PictureIcon.tsx | 13 - .../ReactFileUtilities/icons/RetryIcon.tsx | 11 - .../ReactFileUtilities/icons/index.ts | 5 - src/components/ReactFileUtilities/index.ts | 7 - src/components/Reactions/ReactionsList.tsx | 1 + .../Reactions/ReactionsListModal.tsx | 5 +- .../Reactions/SimpleReactionsList.tsx | 34 +- .../__tests__/SimpleReactionsList.test.js | 40 +- .../SimpleReactionsList.test.js.snap | 43 +- src/components/Thread/Thread.tsx | 14 +- src/components/Thread/ThreadHeader.tsx | 2 +- .../Thread/__tests__/Thread.test.js | 81 +- .../TypingIndicator/TypingIndicator.tsx | 73 +- .../__tests__/TypingIndicator.test.js | 145 ++-- src/components/UserItem/UserItem.tsx | 12 +- .../UserItem/__tests__/UserItem.test.js | 16 +- .../UtilityComponents/NullComponent.tsx | 1 + src/components/UtilityComponents/index.ts | 1 + src/components/index.ts | 1 - src/context/ChatContext.tsx | 6 - src/context/ComponentContext.tsx | 4 +- src/i18n/de.json | 1 + src/i18n/en.json | 1 + src/i18n/es.json | 1 + src/i18n/fr.json | 1 + src/i18n/hi.json | 1 + src/i18n/it.json | 1 + src/i18n/ja.json | 1 + src/i18n/ko.json | 1 + src/i18n/nl.json | 1 + src/i18n/pt.json | 37 +- src/i18n/ru.json | 1 + src/i18n/tr.json | 1 + src/utils/generateRandomId.ts | 6 - src/utils/index.ts | 1 - 138 files changed, 1629 insertions(+), 4803 deletions(-) delete mode 100644 src/components/Attachment/__tests__/__snapshots__/Audio.test.js.snap delete mode 100644 src/components/AutoCompleteTextarea/Header.tsx delete mode 100644 src/components/Chat/hooks/useCustomStyles.ts delete mode 100644 src/components/ChatDown/ChatDown.tsx delete mode 100644 src/components/ChatDown/__tests__/ChatDown.test.js delete mode 100644 src/components/ChatDown/__tests__/__snapshots__/ChatDown.test.js.snap delete mode 100644 src/components/ChatDown/icons.tsx delete mode 100644 src/components/ChatDown/index.ts delete mode 100644 src/components/MessageInput/MessageInputSmall.tsx delete mode 100644 src/components/MessageInput/UploadsPreview.tsx rename src/components/ReactFileUtilities/FileIcon/{FileIconSet/v2.tsx => FileIconSet.tsx} (94%) delete mode 100644 src/components/ReactFileUtilities/FileIcon/FileIconSet/v1.tsx delete mode 100644 src/components/ReactFileUtilities/FilePreviewer.tsx delete mode 100644 src/components/ReactFileUtilities/FileUploadButton.tsx delete mode 100644 src/components/ReactFileUtilities/IconButton.tsx delete mode 100644 src/components/ReactFileUtilities/ImagePreviewer.tsx delete mode 100644 src/components/ReactFileUtilities/ImageUploadButton.tsx delete mode 100644 src/components/ReactFileUtilities/Thumbnail.tsx delete mode 100644 src/components/ReactFileUtilities/ThumbnailPlaceholder.tsx delete mode 100644 src/components/ReactFileUtilities/icons/AttachmentIcon.tsx delete mode 100644 src/components/ReactFileUtilities/icons/CloseIcon.tsx delete mode 100644 src/components/ReactFileUtilities/icons/FilePlaceholderIcon.tsx delete mode 100644 src/components/ReactFileUtilities/icons/PictureIcon.tsx delete mode 100644 src/components/ReactFileUtilities/icons/RetryIcon.tsx delete mode 100644 src/components/ReactFileUtilities/icons/index.ts create mode 100644 src/components/UtilityComponents/NullComponent.tsx create mode 100644 src/components/UtilityComponents/index.ts delete mode 100644 src/utils/generateRandomId.ts diff --git a/src/components/Attachment/AttachmentContainer.tsx b/src/components/Attachment/AttachmentContainer.tsx index 0f559f16b1..5665d10b98 100644 --- a/src/components/Attachment/AttachmentContainer.tsx +++ b/src/components/Attachment/AttachmentContainer.tsx @@ -10,14 +10,15 @@ import { VoiceRecording as DefaultVoiceRecording } from './VoiceRecording'; import { Gallery as DefaultGallery, ImageComponent as DefaultImage } from '../Gallery'; import { Card as DefaultCard } from './Card'; import { FileAttachment as DefaultFile } from './FileAttachment'; -import { NullComponent as DefaultUnsupportedAttachment } from './UnsupportedAttachment'; import { - AttachmentContainerProps, + AttachmentComponentType, + GalleryAttachment, isGalleryAttachmentType, isSvgAttachment, RenderAttachmentProps, RenderGalleryProps, } from './utils'; +import { NullComponent as DefaultUnsupportedAttachment } from '../UtilityComponents'; import { useChannelStateContext } from '../../context/ChannelStateContext'; @@ -26,7 +27,14 @@ import type { ImageAttachmentConfiguration, VideoAttachmentConfiguration, } from '../../types/types'; +import type { Attachment } from '../../../../stream-chat-js'; +export type AttachmentContainerProps< + StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics +> = { + attachment: Attachment | GalleryAttachment; + componentType: AttachmentComponentType; +}; export const AttachmentWithinContainer = < StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics >({ @@ -53,7 +61,7 @@ export const AttachmentWithinContainer = < [`str-chat__message-attachment--${attachment?.type}`]: attachment?.type, [`str-chat__message-attachment--${componentType}--${extra}`]: componentType && extra, 'str-chat__message-attachment--svg-image': isSvgAttachment(attachment), - 'str-chat__message-attachment-with-actions': extra === 'actions', // added for theme V2 (better readability) + 'str-chat__message-attachment-with-actions': extra === 'actions', }, ); @@ -298,7 +306,7 @@ export const MediaContainer = < return attachment.actions?.length ? ( -
+
{content}
diff --git a/src/components/Attachment/Audio.tsx b/src/components/Attachment/Audio.tsx index b8da296f27..baff2d7ef6 100644 --- a/src/components/Attachment/Audio.tsx +++ b/src/components/Attachment/Audio.tsx @@ -5,80 +5,23 @@ import type { Attachment } from 'stream-chat'; import { DownloadButton, FileSizeIndicator, PlayButton, ProgressBar } from './components'; import { useAudioController } from './hooks/useAudioController'; -import { useChatContext } from '../../context/ChatContext'; - import type { DefaultStreamChatGenerics } from '../../types/types'; export type AudioProps< StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics > = { + // fixme: rename og to attachment og: Attachment; }; -const AudioV1 = ({ og }: AudioProps) => { - // fixme: pass mimeType if available - const { asset_url, description, image_url, mime_type, text, title } = og; - const { audioRef, isPlaying, progress, togglePlay } = useAudioController({ mimeType: mime_type }); - - return ( -
-
- -
-
- {!isPlaying ? ( - - ) : ( - - )} -
- {image_url && {`${description}`}} -
-
- - {title} - - {text} -
-
-
-
-
-
- ); -}; - -const AudioV2 = ({ og }: AudioProps) => { - const { asset_url, file_size, mime_type, title } = og; +const UnMemoizedAudio = < + StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics +>( + props: AudioProps, +) => { + const { + og: { asset_url, file_size, mime_type, title }, + } = props; const { audioRef, isPlaying, progress, seek, togglePlay } = useAudioController({ mimeType: mime_type, }); @@ -110,16 +53,6 @@ const AudioV2 = ({ og }: AudioProps) => { ); }; -const UnMemoizedAudio = < - StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics ->( - props: AudioProps, -) => { - const { themeVersion } = useChatContext('Audio'); - - return themeVersion === '1' ? : ; -}; - /** * Audio attachment with play/pause button and progress bar */ diff --git a/src/components/Attachment/Card.tsx b/src/components/Attachment/Card.tsx index 66bfe58206..93d7e39aaa 100644 --- a/src/components/Attachment/Card.tsx +++ b/src/components/Attachment/Card.tsx @@ -7,8 +7,6 @@ import { ImageComponent } from '../Gallery'; import { SafeAnchor } from '../SafeAnchor'; import { PlayButton, ProgressBar } from './components'; import { useAudioController } from './hooks/useAudioController'; - -import { useChatContext } from '../../context/ChatContext'; import { useChannelStateContext } from '../../context/ChannelStateContext'; import { useTranslationContext } from '../../context/TranslationContext'; @@ -43,82 +41,6 @@ const UnableToRenderCard = ({ type }: { type?: CardProps['type'] }) => { ); }; -interface CardV1Props { - asset_url?: Attachment['asset_url']; - giphy?: Attachment['giphy']; - /** The url of the full sized image */ - image_url?: string; - /** The scraped url, used as a fallback if the OG-data doesn't include a link */ - og_scrape_url?: string; - /** Description returned by the OG scraper */ - text?: string; - /** The url for thumbnail sized image */ - thumb_url?: string; - /** Title returned by the OG scraper */ - title?: string; - /** Link returned by the OG scraper */ - title_link?: string; - /** The card type used in the className attribute */ - type?: string; -} - -const CardV1 = (props: CardV1Props) => { - const { - asset_url, - giphy, - image_url, - og_scrape_url, - text, - thumb_url, - title, - title_link, - type, - } = props; - const { giphyVersion: giphyVersionName } = useChannelStateContext('Card'); - - let image = thumb_url || image_url; - const dimensions: Dimensions = {}; - - if (type === 'giphy' && typeof giphy !== 'undefined') { - const giphyVersion = giphy[giphyVersionName as keyof NonNullable]; - image = giphyVersion.url; - dimensions.height = giphyVersion.height; - dimensions.width = giphyVersion.width; - } - - if (!title && !title_link && !asset_url && !image) { - return ; - } - - if (!title_link && !og_scrape_url) { - return null; - } - - return ( -
- - {type !== 'video' && ( -
-
- {title &&
{title}
} - {text &&
{text}
} - {(title_link || og_scrape_url) && ( - - {getHostFromURL(title_link || og_scrape_url)} - - )} -
-
- )} -
- ); -}; - const SourceLink = ({ author_name, url }: Pick & { url: string }) => (
{ ); }; -const CardV2 = (props: CardProps) => { - const { asset_url, giphy, image_url, thumb_url, title, title_link, type } = props; - const { giphyVersion: giphyVersionName } = useChannelStateContext('CardHeader'); - - let image = thumb_url || image_url; - const dimensions: { height?: string; width?: string } = {}; - - if (type === 'giphy' && typeof giphy !== 'undefined') { - const giphyVersion = giphy[giphyVersionName as keyof NonNullable]; - image = giphyVersion.url; - dimensions.height = giphyVersion.height; - dimensions.width = giphyVersion.width; - } - - if (!title && !title_link && !asset_url && !image) { - return ; - } - - return ( -
- - -
- ); -}; - export const CardAudio = ({ og: { asset_url, author_name, mime_type, og_scrape_url, text, title, title_link }, }: AudioProps) => { @@ -255,9 +151,29 @@ export const CardAudio = ({ export type CardProps = RenderAttachmentProps['attachment']; const UnMemoizedCard = (props: CardProps) => { - const { themeVersion } = useChatContext('Card'); + const { asset_url, giphy, image_url, thumb_url, title, title_link, type } = props; + const { giphyVersion: giphyVersionName } = useChannelStateContext('CardHeader'); - return themeVersion === '2' ? : ; + let image = thumb_url || image_url; + const dimensions: { height?: string; width?: string } = {}; + + if (type === 'giphy' && typeof giphy !== 'undefined') { + const giphyVersion = giphy[giphyVersionName as keyof NonNullable]; + image = giphyVersion.url; + dimensions.height = giphyVersion.height; + dimensions.width = giphyVersion.width; + } + + if (!title && !title_link && !asset_url && !image) { + return ; + } + + return ( +
+ + +
+ ); }; /** diff --git a/src/components/Attachment/FileAttachment.tsx b/src/components/Attachment/FileAttachment.tsx index b9bee3f312..0e66e74a73 100644 --- a/src/components/Attachment/FileAttachment.tsx +++ b/src/components/Attachment/FileAttachment.tsx @@ -3,9 +3,6 @@ import { FileIcon } from '../ReactFileUtilities'; import type { Attachment } from 'stream-chat'; import { DownloadButton, FileSizeIndicator } from './components'; -import { SafeAnchor } from '../SafeAnchor/SafeAnchor'; - -import { useChatContext } from '../../context/ChatContext'; import type { DefaultStreamChatGenerics } from '../../types/types'; @@ -15,29 +12,13 @@ export type FileAttachmentProps< attachment: Attachment; }; -const UnMemoizedFileAttachmentV1 = < - StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics ->({ - attachment, -}: FileAttachmentProps) => ( -
- -
- - {attachment.title} - - -
-
-); - -const UnMemoizedFileAttachmentV2 = < +const UnMemoizedFileAttachment = < StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics >({ attachment, }: FileAttachmentProps) => (
- +
@@ -50,20 +31,6 @@ const UnMemoizedFileAttachmentV2 = <
); -const UnMemoizedFileAttachment = < - StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics ->({ - attachment, -}: FileAttachmentProps) => { - const { themeVersion } = useChatContext('FileAttachment'); - - return themeVersion === '2' ? ( - - ) : ( - - ); -}; - export const FileAttachment = React.memo( UnMemoizedFileAttachment, ) as typeof UnMemoizedFileAttachment; diff --git a/src/components/Attachment/UnsupportedAttachment.tsx b/src/components/Attachment/UnsupportedAttachment.tsx index af46b4897f..ea05d1eebc 100644 --- a/src/components/Attachment/UnsupportedAttachment.tsx +++ b/src/components/Attachment/UnsupportedAttachment.tsx @@ -20,5 +20,3 @@ export const UnsupportedAttachment = < {JSON.stringify(attachment, null, 4)};
); - -export const NullComponent = () => null; diff --git a/src/components/Attachment/VoiceRecording.tsx b/src/components/Attachment/VoiceRecording.tsx index 8baf03208a..2b1d62fe6a 100644 --- a/src/components/Attachment/VoiceRecording.tsx +++ b/src/components/Attachment/VoiceRecording.tsx @@ -78,7 +78,7 @@ export const VoiceRecordingPlayer = ({ attachment, playbackRates }: VoiceRecordi {playbackRate.toFixed(1)}x ) : ( - + )}
@@ -114,7 +114,7 @@ export const QuotedVoiceRecording = ({ attachment }: QuotedVoiceRecordingProps)
- + ); }; diff --git a/src/components/Attachment/__tests__/Audio.test.js b/src/components/Attachment/__tests__/Audio.test.js index 26d5eb8c58..ff53883253 100644 --- a/src/components/Attachment/__tests__/Audio.test.js +++ b/src/components/Attachment/__tests__/Audio.test.js @@ -4,7 +4,7 @@ import '@testing-library/jest-dom'; import { Audio } from '../Audio'; -import { ChannelActionProvider, ChatContext } from '../../../context'; +import { ChannelActionProvider } from '../../../context'; import { generateAudioAttachment } from '../../../mock-builders'; import { prettifyFileSize } from '../../MessageInput/hooks/utils'; @@ -25,18 +25,15 @@ const defaultChannelActionContext = { addNotification: addNotificationSpy }; const renderComponent = ( props = { channelActionContext: defaultChannelActionContext, - chatContext: { themeVersion: '1' }, og: AUDIO, }, ) => render( - - - - , + + , ); const playButtonTestId = 'play-audio'; @@ -56,18 +53,8 @@ describe('Audio', () => { jest.resetAllMocks(); }); - it('in v1 should render title and render the image with description as alt tag', () => { - const { container } = renderComponent({ - chatContext: { themeVersion: '1' }, - og: { ...AUDIO, title: 'deterministic' }, - }); - - expect(container).toMatchSnapshot(); - }); - - it('in v2 uploaded should render title and file size', () => { + it('should render title and file size', () => { const { container, getByText } = renderComponent({ - chatContext: { themeVersion: '2' }, og: AUDIO, }); @@ -76,8 +63,8 @@ describe('Audio', () => { expect(container.querySelector('img')).not.toBeInTheDocument(); }); - it('in v2 should show the correct progress after clicking to the middle of a progress bar (seeking)', async () => { - const { getByTestId } = renderComponent({ chatContext: { themeVersion: '2' }, og: AUDIO }); + it('should show the correct progress after clicking to the middle of a progress bar (seeking)', async () => { + const { getByTestId } = renderComponent({ og: AUDIO }); jest .spyOn(HTMLDivElement.prototype, 'getBoundingClientRect') @@ -97,166 +84,159 @@ describe('Audio', () => { }); }); - describe.each([['1'], ['2']])('version %s', (themeVersion) => { - it('should render an audio element with the right source', () => { - const { getByTestId } = renderComponent({ chatContext: { themeVersion }, og: AUDIO }); + it('should render an audio element with the right source', () => { + const { getByTestId } = renderComponent({ og: AUDIO }); + + const source = getByTestId('audio-source'); - const source = getByTestId('audio-source'); + expect(source).toBeInTheDocument(); + expect(source.src).toBe(AUDIO.asset_url); + expect(source.parentElement).toBeInstanceOf(HTMLAudioElement); + }); - expect(source).toBeInTheDocument(); - expect(source.src).toBe(AUDIO.asset_url); - expect(source.parentElement).toBeInstanceOf(HTMLAudioElement); + it('should show the correct button if the song is paused/playing', async () => { + const { container } = renderComponent({ + og: { ...AUDIO, mime_type: undefined }, }); + const audioPausedMock = jest.spyOn(container.querySelector('audio'), 'paused', 'get'); + expect(await playButton()).toBeInTheDocument(); + expect(await pauseButton()).not.toBeInTheDocument(); - it('should show the correct button if the song is paused/playing', async () => { - const { container } = renderComponent({ - chatContext: { themeVersion }, - og: { ...AUDIO, mime_type: undefined }, - }); - const audioPausedMock = jest.spyOn(container.querySelector('audio'), 'paused', 'get'); - expect(await playButton()).toBeInTheDocument(); - expect(await pauseButton()).not.toBeInTheDocument(); + audioPausedMock.mockReturnValueOnce(true); + await act(async () => { + await fireEvent.click(playButton()); + }); - audioPausedMock.mockReturnValueOnce(true); - await act(async () => { - await fireEvent.click(playButton()); - }); + expect(await playButton()).not.toBeInTheDocument(); + expect(await pauseButton()).toBeInTheDocument(); - expect(await playButton()).not.toBeInTheDocument(); - expect(await pauseButton()).toBeInTheDocument(); + audioPausedMock.mockReturnValueOnce(false); + await act(async () => { + await fireEvent.click(pauseButton()); + }); - audioPausedMock.mockReturnValueOnce(false); - await act(async () => { - await fireEvent.click(pauseButton()); - }); + expect(await playButton()).toBeInTheDocument(); + expect(await pauseButton()).not.toBeInTheDocument(); + expect(addNotificationSpy).not.toHaveBeenCalled(); + audioPausedMock.mockRestore(); + }); - expect(await playButton()).toBeInTheDocument(); - expect(await pauseButton()).not.toBeInTheDocument(); - expect(addNotificationSpy).not.toHaveBeenCalled(); - audioPausedMock.mockRestore(); + it('should pause the audio if the playback has not started in 2000ms', async () => { + jest.useFakeTimers('modern'); + const { container } = renderComponent({ + og: { ...AUDIO, mime_type: undefined }, }); - it('should pause the audio if the playback has not started in 2000ms', async () => { - jest.useFakeTimers('modern'); - const { container } = renderComponent({ - chatContext: { themeVersion }, - og: { ...AUDIO, mime_type: undefined }, - }); + const audio = container.querySelector('audio'); + const audioPlayMock = jest.spyOn(audio, 'play').mockImplementation(() => delay(3000)); + const audioPauseMock = jest.spyOn(audio, 'pause'); - const audio = container.querySelector('audio'); - const audioPlayMock = jest.spyOn(audio, 'play').mockImplementation(() => delay(3000)); - const audioPauseMock = jest.spyOn(audio, 'pause'); + expect(await playButton()).toBeInTheDocument(); + expect(await pauseButton()).not.toBeInTheDocument(); - expect(await playButton()).toBeInTheDocument(); - expect(await pauseButton()).not.toBeInTheDocument(); + await act(async () => { + await fireEvent.click(playButton()); + }); + expect(await playButton()).toBeInTheDocument(); + expect(await pauseButton()).not.toBeInTheDocument(); - await act(async () => { - await fireEvent.click(playButton()); - }); + jest.advanceTimersByTime(2000); + + await waitFor(async () => { + expect(audioPauseMock).toHaveBeenCalledWith(); expect(await playButton()).toBeInTheDocument(); expect(await pauseButton()).not.toBeInTheDocument(); + expect(addNotificationSpy).not.toHaveBeenCalled(); + }); - jest.advanceTimersByTime(2000); - - await waitFor(async () => { - expect(audioPauseMock).toHaveBeenCalledWith(); - expect(await playButton()).toBeInTheDocument(); - expect(await pauseButton()).not.toBeInTheDocument(); - expect(addNotificationSpy).not.toHaveBeenCalled(); - }); + jest.useRealTimers(); + audioPlayMock.mockRestore(); + audioPauseMock.mockRestore(); + }); - jest.useRealTimers(); - audioPlayMock.mockRestore(); - audioPauseMock.mockRestore(); + it('should register error if pausing the audio after 2000ms of inactivity failed', async () => { + jest.useFakeTimers('modern'); + const { container } = renderComponent({ + og: { ...AUDIO, mime_type: undefined }, + }); + const audio = container.querySelector('audio'); + const audioPlayMock = jest.spyOn(audio, 'play').mockImplementation(() => delay(3000)); + const audioPauseMock = jest.spyOn(audio, 'pause').mockImplementationOnce(() => { + throw new Error(''); }); - it('should register error if pausing the audio after 2000ms of inactivity failed', async () => { - jest.useFakeTimers('modern'); - const { container } = renderComponent({ - chatContext: { themeVersion }, - og: { ...AUDIO, mime_type: undefined }, - }); - const audio = container.querySelector('audio'); - const audioPlayMock = jest.spyOn(audio, 'play').mockImplementation(() => delay(3000)); - const audioPauseMock = jest.spyOn(audio, 'pause').mockImplementationOnce(() => { - throw new Error(''); - }); + await act(() => { + fireEvent.click(playButton()); + }); + jest.advanceTimersByTime(2000); + await waitFor(() => { + expect(audioPauseMock).toHaveBeenCalledWith(); + expect(addNotificationSpy).toHaveBeenCalledWith('Failed to play the recording', 'error'); + }); - await act(() => { - fireEvent.click(playButton()); - }); - jest.advanceTimersByTime(2000); - await waitFor(() => { - expect(audioPauseMock).toHaveBeenCalledWith(); - expect(addNotificationSpy).toHaveBeenCalledWith('Failed to play the recording', 'error'); - }); + jest.useRealTimers(); + audioPlayMock.mockRestore(); + audioPauseMock.mockRestore(); + }); - jest.useRealTimers(); - audioPlayMock.mockRestore(); - audioPauseMock.mockRestore(); + it('should register error if playing the audio failed', async () => { + const errorText = 'Test error'; + const { container } = renderComponent({ + og: AUDIO, }); + const audio = container.querySelector('audio'); + const audioPlayMock = jest.spyOn(audio, 'play').mockRejectedValueOnce(new Error(errorText)); + const audioCanPlayTypeMock = jest.spyOn(audio, 'canPlayType').mockReturnValue('maybe'); - it('should register error if playing the audio failed', async () => { - const errorText = 'Test error'; - const { container } = renderComponent({ - chatContext: { themeVersion }, - og: AUDIO, - }); - const audio = container.querySelector('audio'); - const audioPlayMock = jest.spyOn(audio, 'play').mockRejectedValueOnce(new Error(errorText)); - const audioCanPlayTypeMock = jest.spyOn(audio, 'canPlayType').mockReturnValue('maybe'); - - expect(await playButton()).toBeInTheDocument(); - expect(await pauseButton()).not.toBeInTheDocument(); + expect(await playButton()).toBeInTheDocument(); + expect(await pauseButton()).not.toBeInTheDocument(); - await act(async () => { - await fireEvent.click(playButton()); - }); - expect(await playButton()).toBeInTheDocument(); - expect(await pauseButton()).not.toBeInTheDocument(); - expect(addNotificationSpy).toHaveBeenCalledWith(errorText, 'error'); - audioPlayMock.mockRestore(); - audioCanPlayTypeMock.mockRestore(); + await act(async () => { + await fireEvent.click(playButton()); }); + expect(await playButton()).toBeInTheDocument(); + expect(await pauseButton()).not.toBeInTheDocument(); + expect(addNotificationSpy).toHaveBeenCalledWith(errorText, 'error'); + audioPlayMock.mockRestore(); + audioCanPlayTypeMock.mockRestore(); + }); - it('should register error if the audio MIME type is not playable', async () => { - const { container } = renderComponent({ - chatContext: { themeVersion }, - og: AUDIO, - }); - const audio = container.querySelector('audio'); - const audioPlayMock = jest.spyOn(audio, 'play'); - const audioCanPlayTypeMock = jest.spyOn(audio, 'canPlayType').mockReturnValue(''); - - expect(await playButton()).toBeInTheDocument(); - expect(await pauseButton()).not.toBeInTheDocument(); + it('should register error if the audio MIME type is not playable', async () => { + const { container } = renderComponent({ + og: AUDIO, + }); + const audio = container.querySelector('audio'); + const audioPlayMock = jest.spyOn(audio, 'play'); + const audioCanPlayTypeMock = jest.spyOn(audio, 'canPlayType').mockReturnValue(''); - await act(async () => { - await fireEvent.click(playButton()); - }); - expect(audioPlayMock).not.toHaveBeenCalled(); - expect(addNotificationSpy).toHaveBeenCalledWith( - 'Recording format is not supported and cannot be reproduced', - 'error', - ); - expect(await playButton()).toBeInTheDocument(); - expect(await pauseButton()).not.toBeInTheDocument(); + expect(await playButton()).toBeInTheDocument(); + expect(await pauseButton()).not.toBeInTheDocument(); - audioPlayMock.mockRestore(); - audioCanPlayTypeMock.mockRestore(); + await act(async () => { + await fireEvent.click(playButton()); }); + expect(audioPlayMock).not.toHaveBeenCalled(); + expect(addNotificationSpy).toHaveBeenCalledWith( + 'Recording format is not supported and cannot be reproduced', + 'error', + ); + expect(await playButton()).toBeInTheDocument(); + expect(await pauseButton()).not.toBeInTheDocument(); + + audioPlayMock.mockRestore(); + audioCanPlayTypeMock.mockRestore(); + }); - it('should show the correct progress', async () => { - const { container } = renderComponent({ chatContext: { themeVersion }, og: AUDIO }); + it('should show the correct progress', async () => { + const { container } = renderComponent({ og: AUDIO }); - jest.spyOn(HTMLAudioElement.prototype, 'duration', 'get').mockImplementationOnce(() => 100); - jest.spyOn(HTMLAudioElement.prototype, 'currentTime', 'get').mockImplementationOnce(() => 50); - const audioElement = container.querySelector('audio'); - fireEvent.timeUpdate(audioElement); + jest.spyOn(HTMLAudioElement.prototype, 'duration', 'get').mockImplementationOnce(() => 100); + jest.spyOn(HTMLAudioElement.prototype, 'currentTime', 'get').mockImplementationOnce(() => 50); + const audioElement = container.querySelector('audio'); + fireEvent.timeUpdate(audioElement); - await waitFor(() => { - expect(screen.getByTestId('audio-progress')).toHaveAttribute('data-progress', '50'); - }); + await waitFor(() => { + expect(screen.getByTestId('audio-progress')).toHaveAttribute('data-progress', '50'); }); }); }); diff --git a/src/components/Attachment/__tests__/Card.test.js b/src/components/Attachment/__tests__/Card.test.js index 4b2b1072be..826f0a87d7 100644 --- a/src/components/Attachment/__tests__/Card.test.js +++ b/src/components/Attachment/__tests__/Card.test.js @@ -4,6 +4,7 @@ import '@testing-library/jest-dom'; import { Card } from '../Card'; +import { ChannelActionProvider, TranslationContext } from '../../../context'; import { ChannelStateProvider } from '../../../context/ChannelStateContext'; import { ChatProvider } from '../../../context/ChatContext'; import { ComponentProvider } from '../../../context/ComponentContext'; @@ -18,12 +19,15 @@ import { mockTranslationContext, useMockedApis, } from '../../../mock-builders'; -import { TranslationContext } from '../../../context'; let chatClient; let channel; const user = generateUser({ id: 'userId', name: 'username' }); +jest.spyOn(window.HTMLMediaElement.prototype, 'pause').mockImplementation(); +const addNotificationSpy = jest.fn(); +const channelActionContext = { addNotification: addNotificationSpy }; + const mockedChannel = generateChannel({ members: [generateMember({ user })], messages: [], @@ -32,13 +36,15 @@ const mockedChannel = generateChannel({ const renderCard = ({ cardProps, chatContext, theRenderer = render }) => theRenderer( - + - - - - - + + + + + + + , ); @@ -53,324 +59,80 @@ describe('Card', () => { afterEach(cleanup); - describe('theme V1', () => { - it('should render Card with default props', async () => { - const { container } = await renderCard({ chatContext: { chatClient } }); - expect(container.firstChild).toMatchInlineSnapshot(` -
-
-
- this content could not be displayed -
-
-
- `); - }); - - it('should render Card with default props and image_url', async () => { - const { container } = await renderCard({ - cardProps: { image_url: 'test.jpg' }, - chatContext: { chatClient }, - }); - expect(container.firstChild).toMatchInlineSnapshot(`null`); - }); - - it('should render Card with default props and title', async () => { - const { container } = await renderCard({ - cardProps: { title: 'test' }, - chatContext: { chatClient }, - }); - expect(container.firstChild).toMatchInlineSnapshot(`null`); - }); - - it('should render Card with default props and og_scrape_url', async () => { - const { container } = await renderCard({ - cardProps: { og_scrape_url: 'https://google.com' }, - chatContext: { chatClient }, - }); - expect(container.firstChild).toMatchInlineSnapshot(` -
-
-
- this content could not be displayed -
-
-
- `); - }); - - it('should render Card with default props and title and og_scrape_url', async () => { - const { container } = await renderCard({ - cardProps: { - og_scrape_url: 'https://google.com', - title: 'test', - }, - chatContext: { chatClient }, - }); - expect(container.firstChild).toMatchInlineSnapshot(` -
-
-
-
- test -
- - google.com - -
-
-
- `); - }); - - it('should render Card with default props and title, og_scrape_url, image_url', async () => { - const { container } = await renderCard({ - cardProps: { - image_url: 'test.jpg', - og_scrape_url: 'https://google.com', - title: 'test', - }, - chatContext: { chatClient }, - }); - expect(container.firstChild).toMatchInlineSnapshot(` -
-
- test -
-
-
-
- test -
- - google.com - -
-
-
- `); - }); - - it('should render Card with default props and title, og_scrape_url, image_url, text', async () => { - const { container } = await renderCard({ - cardProps: { - image_url: 'test.jpg', - og_scrape_url: 'https://google.com', - text: 'test text', - title: 'test', + const dummyAttachment = { + asset_url: 'dummyAttachment_asset_url', + author_name: 'dummyAttachment_author_name', + image_url: 'dummyAttachment_image_url', + og_scrape_url: 'dummyAttachment_og_scrape_url', + text: 'dummyAttachment_text', + thumb_url: 'dummyAttachment_thumb_url', + title: 'dummyAttachment_title', + title_link: 'dummyAttachment_title_link', + }; + + const attachmentTypes = ['audio', 'image', 'video']; + + const cases = attachmentTypes.reduce((acc, type) => { + const attachment = { ...dummyAttachment, type }; + acc[type] = [ + { + attachment: { ...attachment, og_scrape_url: undefined, title_link: undefined }, + props: 'og_scrape_url neither title_link is available', + render: `card without caption`, + }, + { + attachment: { + ...attachment, + asset_url: undefined, + image_url: undefined, + thumb_url: undefined, + title: undefined, + title_link: undefined, }, - chatContext: { chatClient }, - }); - - expect(container.firstChild).toMatchInlineSnapshot(` -
-
- test -
-
-
-
- test -
-
- test text -
- - google.com - -
-
-
- `); - }); - - it('should render trimmed url', async () => { - const { getByText } = await renderCard({ - cardProps: { - og_scrape_url: - 'https://www.theverge.com/2020/6/15/21291288/sony-ps5-software-user-interface-ui-design-dashboard-teaser-video', - title: 'test', + props: 'neither image and asset urls nor title_link are available', + render: 'unable-to-display card', + }, + { + attachment: { ...attachment, title_link: undefined }, + props: 'title_link is not available', + render: `${type} with caption using og_scrape_url and with asset in header`, + }, + { + attachment: { ...attachment, title: undefined }, + props: 'title is not available', + render: `${type} without title`, + }, + { + attachment: { ...attachment, title: undefined, title_link: undefined }, + props: 'title_link neither title is available', + render: `${type} without title and with caption using og_scrape_url and with image in header`, + }, + ]; + if (type === 'audio') { + acc[type].push( + { + attachment: { + ...attachment, + image_url: undefined, + thumb_url: undefined, + }, + props: 'og image URLs are not available', + render: `audio widget with title & text in Card content and without Card header`, }, - chatContext: { chatClient }, - }); - expect(getByText('theverge.com')).toBeInTheDocument(); - }); - - it('should return null if no og_scrape_url && no title_link', async () => { - const { container } = await renderCard({ - cardProps: { title: 'test card' }, - chatContext: { chatClient }, - }); - expect(container).toBeEmptyDOMElement(); - }); - - it('should fall back to render image for video card if no asset_url', async () => { - const { container } = await renderCard({ - cardProps: { - image_url: 'test.jpg', - og_scrape_url: 'https://google.com', - text: 'test text', - title: 'test', - type: 'video', + { + attachment: { ...attachment, asset_url: undefined, image_url: undefined }, + props: 'thumb_url is available, but not asset_url, image_url', + render: `image loaded from thumb_url not ${type} widget`, }, - chatContext: { chatClient }, - }); - - expect(container.firstChild).toMatchInlineSnapshot(` -
-
- test -
-
- `); - }); - - it('should render card with video player', async () => { - const { container } = await renderCard({ - cardProps: { - asset_url: 'https://example.com', - image_url: 'test.jpg', - og_scrape_url: 'https://google.com', - text: 'test text', - title: 'test', - type: 'video', + { + attachment: { ...attachment, asset_url: undefined, thumb_url: undefined }, + props: 'image_url is available, but not asset_url, thumb_url', + render: `image loaded from image_url not ${type} widget`, }, - chatContext: { chatClient }, - }); - - expect(container.firstChild).toMatchInlineSnapshot(` -
-
-
-
-
- `); - }); - }); - - describe('theme V2', () => { - beforeAll(() => { - jest.spyOn(window.HTMLMediaElement.prototype, 'pause').mockImplementation(() => {}); - }); - - afterAll(jest.restoreAllMocks); - - const dummyAttachment = { - asset_url: 'dummyAttachment_asset_url', - author_name: 'dummyAttachment_author_name', - image_url: 'dummyAttachment_image_url', - og_scrape_url: 'dummyAttachment_og_scrape_url', - text: 'dummyAttachment_text', - thumb_url: 'dummyAttachment_thumb_url', - title: 'dummyAttachment_title', - title_link: 'dummyAttachment_title_link', - }; - - const attachmentTypes = ['audio', 'image', 'video']; - - const cases = attachmentTypes.reduce((acc, type) => { - const attachment = { ...dummyAttachment, type }; - acc[type] = [ { - attachment: { ...attachment, og_scrape_url: undefined, title_link: undefined }, - props: 'og_scrape_url neither title_link is available', - render: `card without caption`, + attachment, + props: 'all props are available', + render: `audio widget with image loaded from thumb_url and title & text in Card content`, }, { attachment: { @@ -378,206 +140,151 @@ describe('Card', () => { asset_url: undefined, image_url: undefined, thumb_url: undefined, - title: undefined, - title_link: undefined, }, - props: 'neither image and asset urls nor title_link are available', - render: 'unable-to-display card', + props: 'asset and neither og image URL is available', + render: 'content part with title and text only and without the header part of the Card', }, + ); + } else if (type === 'video') { + acc[type].push( { - attachment: { ...attachment, title_link: undefined }, - props: 'title_link is not available', - render: `${type} with caption using og_scrape_url and with asset in header`, + attachment: { + ...attachment, + image_url: undefined, + thumb_url: undefined, + }, + props: 'og image URLs are not available', + render: `video widget in header and title & text in Card content`, }, { - attachment: { ...attachment, title: undefined }, - props: 'title is not available', - render: `${type} without title`, + attachment: { ...attachment, asset_url: undefined, image_url: undefined }, + props: 'thumb_url is available, but not asset_url, image_url', + render: `image loaded from thumb_url not ${type} widget`, }, { - attachment: { ...attachment, title: undefined, title_link: undefined }, - props: 'title_link neither title is available', - render: `${type} without title and with caption using og_scrape_url and with image in header`, + attachment: { ...attachment, asset_url: undefined, thumb_url: undefined }, + props: 'image_url is available, but not asset_url, thumb_url', + render: `image loaded from image_url not ${type} widget`, }, - ]; - if (type === 'audio') { - acc[type].push( - { - attachment: { - ...attachment, - image_url: undefined, - thumb_url: undefined, - }, - props: 'og image URLs are not available', - render: `audio widget with title & text in Card content and without Card header`, - }, - { - attachment: { ...attachment, asset_url: undefined, image_url: undefined }, - props: 'thumb_url is available, but not asset_url, image_url', - render: `image loaded from thumb_url not ${type} widget`, - }, - { - attachment: { ...attachment, asset_url: undefined, thumb_url: undefined }, - props: 'image_url is available, but not asset_url, thumb_url', - render: `image loaded from image_url not ${type} widget`, - }, - { - attachment, - props: 'all props are available', - render: `audio widget with image loaded from thumb_url and title & text in Card content`, - }, - { - attachment: { - ...attachment, - asset_url: undefined, - image_url: undefined, - thumb_url: undefined, - }, - props: 'asset and neither og image URL is available', - render: 'content part with title and text only and without the header part of the Card', - }, - ); - } else if (type === 'video') { - acc[type].push( - { - attachment: { - ...attachment, - image_url: undefined, - thumb_url: undefined, - }, - props: 'og image URLs are not available', - render: `video widget in header and title & text in Card content`, - }, - { - attachment: { ...attachment, asset_url: undefined, image_url: undefined }, - props: 'thumb_url is available, but not asset_url, image_url', - render: `image loaded from thumb_url not ${type} widget`, - }, - { - attachment: { ...attachment, asset_url: undefined, thumb_url: undefined }, - props: 'image_url is available, but not asset_url, thumb_url', - render: `image loaded from image_url not ${type} widget`, - }, - { - attachment, - props: 'all props are available', - render: `video widget in header and title & text in Card content`, - }, - { - attachment: { - ...attachment, - asset_url: undefined, - image_url: undefined, - thumb_url: undefined, - }, - props: 'scraped media URL is not available', - render: `content part with title and text only and without the header part of the Card`, - }, - ); - } else if (type === 'image') { - acc[type].push( - { - attachment: { - ...attachment, - image_url: undefined, - thumb_url: undefined, - }, - props: 'og image URLs are not available', - render: `card with title and text only and without the image in the header part of the Card`, - }, - { - attachment: { ...attachment, image_url: undefined }, - props: 'thumb_url is available, but not image_url', - render: `image loaded from thumb_url`, + { + attachment, + props: 'all props are available', + render: `video widget in header and title & text in Card content`, + }, + { + attachment: { + ...attachment, + asset_url: undefined, + image_url: undefined, + thumb_url: undefined, }, - { - attachment: { ...attachment, thumb_url: undefined }, - props: 'image_url is available, but not thumb_url', - render: `image loaded from image_url`, + props: 'scraped media URL is not available', + render: `content part with title and text only and without the header part of the Card`, + }, + ); + } else if (type === 'image') { + acc[type].push( + { + attachment: { + ...attachment, + image_url: undefined, + thumb_url: undefined, }, - ); - } - return acc; - }, {}); - - it.each` - num | render | type | props | attachment - ${1} | ${cases.audio[0].render} | ${cases.audio[0].attachment.type} | ${cases.audio[0].props} | ${cases.audio[0].attachment} - ${2} | ${cases.video[0].render} | ${cases.video[0].attachment.type} | ${cases.video[0].props} | ${cases.video[0].attachment} - ${3} | ${cases.image[0].render} | ${cases.image[0].attachment.type} | ${cases.image[0].props} | ${cases.image[0].attachment} - ${4} | ${cases.audio[1].render} | ${cases.audio[1].attachment.type} | ${cases.audio[1].props} | ${cases.audio[1].attachment} - ${5} | ${cases.video[1].render} | ${cases.video[1].attachment.type} | ${cases.video[1].props} | ${cases.video[1].attachment} - ${6} | ${cases.image[1].render} | ${cases.image[1].attachment.type} | ${cases.image[1].props} | ${cases.image[1].attachment} - ${7} | ${cases.audio[2].render} | ${cases.audio[2].attachment.type} | ${cases.audio[2].props} | ${cases.audio[2].attachment} - ${8} | ${cases.video[2].render} | ${cases.video[2].attachment.type} | ${cases.video[2].props} | ${cases.video[2].attachment} - ${9} | ${cases.image[2].render} | ${cases.image[2].attachment.type} | ${cases.image[2].props} | ${cases.image[2].attachment} - ${10} | ${cases.audio[3].render} | ${cases.audio[3].attachment.type} | ${cases.audio[3].props} | ${cases.audio[3].attachment} - ${11} | ${cases.video[3].render} | ${cases.video[3].attachment.type} | ${cases.video[3].props} | ${cases.video[3].attachment} - ${12} | ${cases.image[3].render} | ${cases.image[3].attachment.type} | ${cases.image[3].props} | ${cases.image[3].attachment} - ${13} | ${cases.audio[4].render} | ${cases.audio[4].attachment.type} | ${cases.audio[4].props} | ${cases.audio[4].attachment} - ${14} | ${cases.video[4].render} | ${cases.video[4].attachment.type} | ${cases.video[4].props} | ${cases.video[4].attachment} - ${15} | ${cases.image[4].render} | ${cases.image[4].attachment.type} | ${cases.image[4].props} | ${cases.image[4].attachment} - ${16} | ${cases.audio[5].render} | ${cases.audio[5].attachment.type} | ${cases.audio[5].props} | ${cases.audio[5].attachment} - ${17} | ${cases.video[5].render} | ${cases.video[5].attachment.type} | ${cases.video[5].props} | ${cases.video[5].attachment} - ${18} | ${cases.image[5].render} | ${cases.image[5].attachment.type} | ${cases.image[5].props} | ${cases.image[5].attachment} - ${19} | ${cases.audio[6].render} | ${cases.audio[6].attachment.type} | ${cases.audio[6].props} | ${cases.audio[6].attachment} - ${20} | ${cases.video[6].render} | ${cases.video[6].attachment.type} | ${cases.video[6].props} | ${cases.video[6].attachment} - ${21} | ${cases.image[6].render} | ${cases.image[6].attachment.type} | ${cases.image[6].props} | ${cases.image[6].attachment} - ${22} | ${cases.audio[7].render} | ${cases.audio[7].attachment.type} | ${cases.audio[7].props} | ${cases.audio[7].attachment} - ${23} | ${cases.video[7].render} | ${cases.video[7].attachment.type} | ${cases.video[7].props} | ${cases.video[7].attachment} - ${24} | ${cases.image[7].render} | ${cases.image[7].attachment.type} | ${cases.image[7].props} | ${cases.image[7].attachment} - ${25} | ${cases.audio[8].render} | ${cases.audio[8].attachment.type} | ${cases.audio[8].props} | ${cases.audio[8].attachment} - ${26} | ${cases.video[8].render} | ${cases.video[8].attachment.type} | ${cases.video[8].props} | ${cases.video[8].attachment} - ${27} | ${cases.audio[9].render} | ${cases.audio[9].attachment.type} | ${cases.audio[9].props} | ${cases.audio[9].attachment} - ${28} | ${cases.video[9].render} | ${cases.video[9].attachment.type} | ${cases.video[9].props} | ${cases.video[9].attachment} - `( - '($num) should render $render if attachment type is $type and $props', - async ({ attachment }) => { - const { container } = await renderCard({ - cardProps: attachment, - chatContext: { chatClient, themeVersion: '2' }, - render, - }); - - await waitFor(() => { - expect(container).toMatchSnapshot(); - }); - }, - ); - - it('should render giphy image if type is giphy', async () => { + props: 'og image URLs are not available', + render: `card with title and text only and without the image in the header part of the Card`, + }, + { + attachment: { ...attachment, image_url: undefined }, + props: 'thumb_url is available, but not image_url', + render: `image loaded from thumb_url`, + }, + { + attachment: { ...attachment, thumb_url: undefined }, + props: 'image_url is available, but not thumb_url', + render: `image loaded from image_url`, + }, + ); + } + return acc; + }, {}); + + it.each` + num | render | type | props | attachment + ${1} | ${cases.audio[0].render} | ${cases.audio[0].attachment.type} | ${cases.audio[0].props} | ${cases.audio[0].attachment} + ${2} | ${cases.video[0].render} | ${cases.video[0].attachment.type} | ${cases.video[0].props} | ${cases.video[0].attachment} + ${3} | ${cases.image[0].render} | ${cases.image[0].attachment.type} | ${cases.image[0].props} | ${cases.image[0].attachment} + ${4} | ${cases.audio[1].render} | ${cases.audio[1].attachment.type} | ${cases.audio[1].props} | ${cases.audio[1].attachment} + ${5} | ${cases.video[1].render} | ${cases.video[1].attachment.type} | ${cases.video[1].props} | ${cases.video[1].attachment} + ${6} | ${cases.image[1].render} | ${cases.image[1].attachment.type} | ${cases.image[1].props} | ${cases.image[1].attachment} + ${7} | ${cases.audio[2].render} | ${cases.audio[2].attachment.type} | ${cases.audio[2].props} | ${cases.audio[2].attachment} + ${8} | ${cases.video[2].render} | ${cases.video[2].attachment.type} | ${cases.video[2].props} | ${cases.video[2].attachment} + ${9} | ${cases.image[2].render} | ${cases.image[2].attachment.type} | ${cases.image[2].props} | ${cases.image[2].attachment} + ${10} | ${cases.audio[3].render} | ${cases.audio[3].attachment.type} | ${cases.audio[3].props} | ${cases.audio[3].attachment} + ${11} | ${cases.video[3].render} | ${cases.video[3].attachment.type} | ${cases.video[3].props} | ${cases.video[3].attachment} + ${12} | ${cases.image[3].render} | ${cases.image[3].attachment.type} | ${cases.image[3].props} | ${cases.image[3].attachment} + ${13} | ${cases.audio[4].render} | ${cases.audio[4].attachment.type} | ${cases.audio[4].props} | ${cases.audio[4].attachment} + ${14} | ${cases.video[4].render} | ${cases.video[4].attachment.type} | ${cases.video[4].props} | ${cases.video[4].attachment} + ${15} | ${cases.image[4].render} | ${cases.image[4].attachment.type} | ${cases.image[4].props} | ${cases.image[4].attachment} + ${16} | ${cases.audio[5].render} | ${cases.audio[5].attachment.type} | ${cases.audio[5].props} | ${cases.audio[5].attachment} + ${17} | ${cases.video[5].render} | ${cases.video[5].attachment.type} | ${cases.video[5].props} | ${cases.video[5].attachment} + ${18} | ${cases.image[5].render} | ${cases.image[5].attachment.type} | ${cases.image[5].props} | ${cases.image[5].attachment} + ${19} | ${cases.audio[6].render} | ${cases.audio[6].attachment.type} | ${cases.audio[6].props} | ${cases.audio[6].attachment} + ${20} | ${cases.video[6].render} | ${cases.video[6].attachment.type} | ${cases.video[6].props} | ${cases.video[6].attachment} + ${21} | ${cases.image[6].render} | ${cases.image[6].attachment.type} | ${cases.image[6].props} | ${cases.image[6].attachment} + ${22} | ${cases.audio[7].render} | ${cases.audio[7].attachment.type} | ${cases.audio[7].props} | ${cases.audio[7].attachment} + ${23} | ${cases.video[7].render} | ${cases.video[7].attachment.type} | ${cases.video[7].props} | ${cases.video[7].attachment} + ${24} | ${cases.image[7].render} | ${cases.image[7].attachment.type} | ${cases.image[7].props} | ${cases.image[7].attachment} + ${25} | ${cases.audio[8].render} | ${cases.audio[8].attachment.type} | ${cases.audio[8].props} | ${cases.audio[8].attachment} + ${26} | ${cases.video[8].render} | ${cases.video[8].attachment.type} | ${cases.video[8].props} | ${cases.video[8].attachment} + ${27} | ${cases.audio[9].render} | ${cases.audio[9].attachment.type} | ${cases.audio[9].props} | ${cases.audio[9].attachment} + ${28} | ${cases.video[9].render} | ${cases.video[9].attachment.type} | ${cases.video[9].props} | ${cases.video[9].attachment} + `( + '($num) should render $render if attachment type is $type and $props', + async ({ attachment }) => { const { container } = await renderCard({ - cardProps: { attachment: generateGiphyAttachment() }, - chatContext: { chatClient, themeVersion: '2' }, + cardProps: attachment, + chatContext: { chatClient }, render, }); await waitFor(() => { expect(container).toMatchSnapshot(); }); + }, + ); + + it('should render giphy image if type is giphy', async () => { + const { container } = await renderCard({ + cardProps: { attachment: generateGiphyAttachment() }, + chatContext: { chatClient }, + render, }); - it('should not render giphy image if url is not available', async () => { - const { queryByTestId } = await renderCard({ - cardProps: { attachment: generateGiphyAttachment({ giphy: undefined }) }, - chatContext: { chatClient, themeVersion: '2' }, - render, - }); - await waitFor(() => { - expect(queryByTestId('card-header')).not.toBeInTheDocument(); - }); + await waitFor(() => { + expect(container).toMatchSnapshot(); + }); + }); + it('should not render giphy image if url is not available', async () => { + const { queryByTestId } = await renderCard({ + cardProps: { attachment: generateGiphyAttachment({ giphy: undefined }) }, + chatContext: { chatClient }, + render, }); - it('should display trimmed URL in caption if author_name is not available', async () => { - const { getByText } = await renderCard({ - cardProps: { - og_scrape_url: - 'https://www.theverge.com/2020/6/15/21291288/sony-ps5-software-user-interface-ui-design-dashboard-teaser-video', - title: 'test', - }, - chatContext: { chatClient }, - }); - expect(getByText('theverge.com')).toBeInTheDocument(); + await waitFor(() => { + expect(queryByTestId('card-header')).not.toBeInTheDocument(); + }); + }); + + it('should display trimmed URL in caption if author_name is not available', async () => { + const { getByText } = await renderCard({ + cardProps: { + og_scrape_url: + 'https://www.theverge.com/2020/6/15/21291288/sony-ps5-software-user-interface-ui-design-dashboard-teaser-video', + title: 'test', + }, + chatContext: { chatClient }, }); + expect(getByText('theverge.com')).toBeInTheDocument(); }); }); diff --git a/src/components/Attachment/__tests__/File.test.js b/src/components/Attachment/__tests__/File.test.js index 5f5f75394d..31df22475e 100644 --- a/src/components/Attachment/__tests__/File.test.js +++ b/src/components/Attachment/__tests__/File.test.js @@ -3,17 +3,13 @@ import '@testing-library/jest-dom'; import renderer from 'react-test-renderer'; import { FileAttachment } from '../FileAttachment'; - -import { ChatContext } from '../../../context/ChatContext'; import { TranslationContext } from '../../../context'; import { mockTranslationContext } from '../../../mock-builders'; -const getComponent = ({ attachment, chatContext }) => ( - - - - - +const getComponent = ({ attachment }) => ( + + + ); const file = { @@ -26,10 +22,8 @@ const file = { }; describe('File', () => { - it.each([['1'], ['2']])('should render File component in V%s', (themeVersion) => { - const tree = renderer - .create(getComponent({ attachment: file, chatContext: { themeVersion } })) - .toJSON(); + it('should render File component', () => { + const tree = renderer.create(getComponent({ attachment: file })).toJSON(); expect(tree).toMatchSnapshot(); }); }); diff --git a/src/components/Attachment/__tests__/__snapshots__/Audio.test.js.snap b/src/components/Attachment/__tests__/__snapshots__/Audio.test.js.snap deleted file mode 100644 index ec86ca8f31..0000000000 --- a/src/components/Attachment/__tests__/__snapshots__/Audio.test.js.snap +++ /dev/null @@ -1,69 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Audio in v1 should render title and render the image with description as alt tag 1`] = ` -
-
-
- -
-
- -
-
-
- - - deterministic - - - -
-
-
-
-
-
-
-`; diff --git a/src/components/Attachment/__tests__/__snapshots__/Card.test.js.snap b/src/components/Attachment/__tests__/__snapshots__/Card.test.js.snap index 8ab0afbc48..eedea8c26b 100644 --- a/src/components/Attachment/__tests__/__snapshots__/Card.test.js.snap +++ b/src/components/Attachment/__tests__/__snapshots__/Card.test.js.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Card theme V2 (1) should render card without caption if attachment type is audio and og_scrape_url neither title_link is available 1`] = ` +exports[`Card (1) should render card without caption if attachment type is audio and og_scrape_url neither title_link is available 1`] = `
`; -exports[`Card theme V2 (2) should render card without caption if attachment type is video and og_scrape_url neither title_link is available 1`] = ` +exports[`Card (2) should render card without caption if attachment type is video and og_scrape_url neither title_link is available 1`] = `
-
+ />
`; -exports[`Card theme V2 (3) should render card without caption if attachment type is image and og_scrape_url neither title_link is available 1`] = ` +exports[`Card (3) should render card without caption if attachment type is image and og_scrape_url neither title_link is available 1`] = `
`; -exports[`Card theme V2 (4) should render unable-to-display card if attachment type is audio and neither image and asset urls nor title_link are available 1`] = ` +exports[`Card (4) should render unable-to-display card if attachment type is audio and neither image and asset urls nor title_link are available 1`] = `
`; -exports[`Card theme V2 (5) should render unable-to-display card if attachment type is video and neither image and asset urls nor title_link are available 1`] = ` +exports[`Card (5) should render unable-to-display card if attachment type is video and neither image and asset urls nor title_link are available 1`] = `
`; -exports[`Card theme V2 (6) should render unable-to-display card if attachment type is image and neither image and asset urls nor title_link are available 1`] = ` +exports[`Card (6) should render unable-to-display card if attachment type is image and neither image and asset urls nor title_link are available 1`] = `
`; -exports[`Card theme V2 (7) should render audio with caption using og_scrape_url and with asset in header if attachment type is audio and title_link is not available 1`] = ` +exports[`Card (7) should render audio with caption using og_scrape_url and with asset in header if attachment type is audio and title_link is not available 1`] = `
`; -exports[`Card theme V2 (8) should render video with caption using og_scrape_url and with asset in header if attachment type is video and title_link is not available 1`] = ` +exports[`Card (8) should render video with caption using og_scrape_url and with asset in header if attachment type is video and title_link is not available 1`] = `
`; -exports[`Card theme V2 (9) should render image with caption using og_scrape_url and with asset in header if attachment type is image and title_link is not available 1`] = ` +exports[`Card (9) should render image with caption using og_scrape_url and with asset in header if attachment type is image and title_link is not available 1`] = `
`; -exports[`Card theme V2 (10) should render audio without title if attachment type is audio and title is not available 1`] = ` +exports[`Card (10) should render audio without title if attachment type is audio and title is not available 1`] = `
`; -exports[`Card theme V2 (11) should render video without title if attachment type is video and title is not available 1`] = ` +exports[`Card (11) should render video without title if attachment type is video and title is not available 1`] = `
`; -exports[`Card theme V2 (12) should render image without title if attachment type is image and title is not available 1`] = ` +exports[`Card (12) should render image without title if attachment type is image and title is not available 1`] = `
`; -exports[`Card theme V2 (13) should render audio without title and with caption using og_scrape_url and with image in header if attachment type is audio and title_link neither title is available 1`] = ` +exports[`Card (13) should render audio without title and with caption using og_scrape_url and with image in header if attachment type is audio and title_link neither title is available 1`] = `
`; -exports[`Card theme V2 (14) should render video without title and with caption using og_scrape_url and with image in header if attachment type is video and title_link neither title is available 1`] = ` +exports[`Card (14) should render video without title and with caption using og_scrape_url and with image in header if attachment type is video and title_link neither title is available 1`] = `
`; -exports[`Card theme V2 (15) should render image without title and with caption using og_scrape_url and with image in header if attachment type is image and title_link neither title is available 1`] = ` +exports[`Card (15) should render image without title and with caption using og_scrape_url and with image in header if attachment type is image and title_link neither title is available 1`] = `
`; -exports[`Card theme V2 (16) should render audio widget with title & text in Card content and without Card header if attachment type is audio and og image URLs are not available 1`] = ` +exports[`Card (16) should render audio widget with title & text in Card content and without Card header if attachment type is audio and og image URLs are not available 1`] = `
`; -exports[`Card theme V2 (17) should render video widget in header and title & text in Card content if attachment type is video and og image URLs are not available 1`] = ` +exports[`Card (17) should render video widget in header and title & text in Card content if attachment type is video and og image URLs are not available 1`] = `
`; -exports[`Card theme V2 (18) should render card with title and text only and without the image in the header part of the Card if attachment type is image and og image URLs are not available 1`] = ` +exports[`Card (18) should render card with title and text only and without the image in the header part of the Card if attachment type is image and og image URLs are not available 1`] = `
`; -exports[`Card theme V2 (19) should render image loaded from thumb_url not audio widget if attachment type is audio and thumb_url is available, but not asset_url, image_url 1`] = ` +exports[`Card (19) should render image loaded from thumb_url not audio widget if attachment type is audio and thumb_url is available, but not asset_url, image_url 1`] = `
`; -exports[`Card theme V2 (20) should render image loaded from thumb_url not video widget if attachment type is video and thumb_url is available, but not asset_url, image_url 1`] = ` +exports[`Card (20) should render image loaded from thumb_url not video widget if attachment type is video and thumb_url is available, but not asset_url, image_url 1`] = `
`; -exports[`Card theme V2 (21) should render image loaded from thumb_url if attachment type is image and thumb_url is available, but not image_url 1`] = ` +exports[`Card (21) should render image loaded from thumb_url if attachment type is image and thumb_url is available, but not image_url 1`] = `
`; -exports[`Card theme V2 (22) should render image loaded from image_url not audio widget if attachment type is audio and image_url is available, but not asset_url, thumb_url 1`] = ` +exports[`Card (22) should render image loaded from image_url not audio widget if attachment type is audio and image_url is available, but not asset_url, thumb_url 1`] = `
`; -exports[`Card theme V2 (23) should render image loaded from image_url not video widget if attachment type is video and image_url is available, but not asset_url, thumb_url 1`] = ` +exports[`Card (23) should render image loaded from image_url not video widget if attachment type is video and image_url is available, but not asset_url, thumb_url 1`] = `
`; -exports[`Card theme V2 (24) should render image loaded from image_url if attachment type is image and image_url is available, but not thumb_url 1`] = ` +exports[`Card (24) should render image loaded from image_url if attachment type is image and image_url is available, but not thumb_url 1`] = `
`; -exports[`Card theme V2 (25) should render audio widget with image loaded from thumb_url and title & text in Card content if attachment type is audio and all props are available 1`] = ` +exports[`Card (25) should render audio widget with image loaded from thumb_url and title & text in Card content if attachment type is audio and all props are available 1`] = `
`; -exports[`Card theme V2 (26) should render video widget in header and title & text in Card content if attachment type is video and all props are available 1`] = ` +exports[`Card (26) should render video widget in header and title & text in Card content if attachment type is video and all props are available 1`] = `
`; -exports[`Card theme V2 (27) should render content part with title and text only and without the header part of the Card if attachment type is audio and asset and neither og image URL is available 1`] = ` +exports[`Card (27) should render content part with title and text only and without the header part of the Card if attachment type is audio and asset and neither og image URL is available 1`] = `
`; -exports[`Card theme V2 (28) should render content part with title and text only and without the header part of the Card if attachment type is video and scraped media URL is not available 1`] = ` +exports[`Card (28) should render content part with title and text only and without the header part of the Card if attachment type is video and scraped media URL is not available 1`] = `
`; -exports[`Card theme V2 should render giphy image if type is giphy 1`] = ` +exports[`Card should render giphy image if type is giphy 1`] = `
- - - -
- - Nice file - - - 1.31 kB - -
-
-`; - -exports[`File should render File component in V2 1`] = ` +exports[`File should render File component 1`] = `
= { - attachment: Attachment | GalleryAttachment; - componentType: AttachmentComponentType; -}; - export type RenderAttachmentProps< StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics > = Omit, 'attachments'> & { @@ -104,238 +89,6 @@ export const isSvgAttachment = (attachment: Attachment) => { return filename.toLowerCase().endsWith('.svg'); }; -/** - * @deprecated will be removed in the next major release, - * replaced with the proper component equivalent `AttachmentContainer/AttachmentWithinContainer` - */ -export const renderAttachmentWithinContainer = < - StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics ->( - props: PropsWithChildren>, -) => { - const { attachment, children, componentType } = props; - const isGAT = isGalleryAttachmentType(attachment); - let extra = ''; - - if (!isGAT) { - extra = - componentType === 'card' && !attachment?.image_url && !attachment?.thumb_url - ? 'no-image' - : attachment?.actions?.length - ? 'actions' - : ''; - } - - const classNames = clsx('str-chat__message-attachment', { - [`str-chat__message-attachment--${componentType}`]: componentType, - [`str-chat__message-attachment--${attachment?.type}`]: attachment?.type, - [`str-chat__message-attachment--${componentType}--${extra}`]: componentType && extra, - 'str-chat__message-attachment--svg-image': isSvgAttachment(attachment), - 'str-chat__message-attachment-with-actions': extra === 'actions', // added for theme V2 (better readability) - }); - - return
{children}
; -}; - -/** - * @deprecated will be removed in the next major release, - * replaced with the proper component equivalent `AttachmentContainer/AttachmentActionsContainer` - */ -export const renderAttachmentActions = < - StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics ->( - props: RenderAttachmentProps, -) => { - const { actionHandler, attachment, AttachmentActions = DefaultAttachmentActions } = props; - - if (!attachment.actions?.length) return null; - - return ( - actionHandler?.(event, name, value)} - actions={attachment.actions} - id={attachment.id || ''} - key={`key-actions-${attachment.id}`} - text={attachment.text || ''} - /> - ); -}; - -/** - * @deprecated will be removed in the next major release, - * replaced with the proper component equivalent `AttachmentContainer/GalleryContainer` - */ -export const renderGallery = < - StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics ->( - props: RenderGalleryProps, -) => { - const { attachment, Gallery = DefaultGallery } = props; - - return renderAttachmentWithinContainer({ - attachment, - children: , - componentType: 'gallery', - }); -}; - -/** - * @deprecated will be removed in the next major release, - * replaced with the proper component equivalent `AttachmentContainer/ImageContainer` - */ -export const renderImage = < - StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics ->( - props: RenderAttachmentProps, -) => { - const { attachment, Image = DefaultImage } = props; - - if (attachment.actions && attachment.actions.length) { - return renderAttachmentWithinContainer({ - attachment, - children: ( -
- {} - {renderAttachmentActions(props)} -
- ), - componentType: 'image', - }); - } - - return renderAttachmentWithinContainer({ - attachment, - children: , - componentType: 'image', - }); -}; - -/** - * @deprecated will be removed in the next major release, - * replaced with the proper component equivalent `AttachmentContainer/CardContainer` - */ -export const renderCard = < - StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics ->( - props: RenderAttachmentProps, -) => { - const { attachment, Card = DefaultCard } = props; - - if (attachment.actions && attachment.actions.length) { - return renderAttachmentWithinContainer({ - attachment, - children: ( -
- - {renderAttachmentActions(props)} -
- ), - componentType: 'card', - }); - } - - return renderAttachmentWithinContainer({ - attachment, - children: , - componentType: 'card', - }); -}; - -/** - * @deprecated will be removed in the next major release, - * replaced with the proper component equivalent `AttachmentContainer/FileContainer` - */ -export const renderFile = < - StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics ->( - props: RenderAttachmentProps, -) => { - const { attachment, File = DefaultFile } = props; - - if (!attachment.asset_url) return null; - - return renderAttachmentWithinContainer({ - attachment, - children: , - componentType: 'file', - }); -}; - -/** - * @deprecated will be removed in the next major release, - * replaced with the proper component equivalent `AttachmentContainer/AudioContainer` - */ -export const renderAudio = < - StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics ->( - props: RenderAttachmentProps, -) => { - const { attachment, Audio = DefaultAudio } = props; - - return renderAttachmentWithinContainer({ - attachment, - children: ( -
-
- ), - componentType: 'audio', - }); -}; - -/** - * @deprecated will be removed in the next major release, - * replaced with the proper component equivalent `AttachmentContainer/MediaContainer` - */ -export const renderMedia = < - StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics ->( - props: RenderAttachmentProps, -) => { - const { attachment, Media = ReactPlayer } = props; - - if (attachment.actions?.length) { - return renderAttachmentWithinContainer({ - attachment, - children: ( -
-
- -
- {renderAttachmentActions(props)} -
- ), - componentType: 'media', - }); - } - - return renderAttachmentWithinContainer({ - attachment, - children: ( -
- -
- ), - componentType: 'media', - }); -}; - export const divMod = (num: number, divisor: number) => [Math.floor(num / divisor), num % divisor]; export const displayDuration = (totalSeconds?: number) => { diff --git a/src/components/AutoCompleteTextarea/Header.tsx b/src/components/AutoCompleteTextarea/Header.tsx deleted file mode 100644 index 4a9498d274..0000000000 --- a/src/components/AutoCompleteTextarea/Header.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import React from 'react'; - -import { useTranslationContext } from '../../context/TranslationContext'; - -export type CurrentTrigger = '/' | '@' | ':' | T; - -export type SuggestionListHeaderProps = { - currentTrigger: CurrentTrigger; - value: string; -}; - -export const DefaultSuggestionListHeader = (props: SuggestionListHeaderProps) => { - const { currentTrigger, value } = props; - - const { t } = useTranslationContext('DefaultSuggestionListHeader'); - - const triggerIndex = value.lastIndexOf(currentTrigger); - - if (currentTrigger === '/') { - return ( - <> - {t('Commands matching')} {value.slice(triggerIndex + 1)} - - ); - } - - if (currentTrigger === ':') { - return ( - <> - {t('Emoji matching')} {value.slice(triggerIndex + 1)} - - ); - } - - if (currentTrigger === '@') { - return ( - <> - {t('People matching')} {value.slice(triggerIndex + 1)} - - ); - } - - return null; -}; diff --git a/src/components/AutoCompleteTextarea/Item.jsx b/src/components/AutoCompleteTextarea/Item.jsx index bbbb9f57ba..19cf35b047 100644 --- a/src/components/AutoCompleteTextarea/Item.jsx +++ b/src/components/AutoCompleteTextarea/Item.jsx @@ -1,8 +1,6 @@ import React, { useCallback } from 'react'; import clsx from 'clsx'; -import { useChatContext } from '../../context/ChatContext'; - export const Item = React.forwardRef(function Item(props, innerRef) { const { className, @@ -14,42 +12,23 @@ export const Item = React.forwardRef(function Item(props, innerRef) { style, } = props; - const { themeVersion } = useChatContext('SuggestionItem'); - const handleSelect = useCallback(() => onSelectHandler(item), [item, onSelectHandler]); const handleClick = useCallback((event) => onClickHandler(event, item), [item, onClickHandler]); - if (themeVersion === '2') - return ( -
  • - - - -
  • - ); - return ( -
  • - + +
  • ); }); diff --git a/src/components/AutoCompleteTextarea/List.jsx b/src/components/AutoCompleteTextarea/List.jsx index 55e0c6a3dd..d58acb02f8 100644 --- a/src/components/AutoCompleteTextarea/List.jsx +++ b/src/components/AutoCompleteTextarea/List.jsx @@ -2,10 +2,8 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import clsx from 'clsx'; import { useComponentContext } from '../../context/ComponentContext'; -import { useChatContext } from '../../context/ChatContext'; import { Item } from './Item'; -import { DefaultSuggestionListHeader } from './Header'; import { escapeRegExp } from '../Message/renderText'; export const List = ({ @@ -15,7 +13,6 @@ export const List = ({ dropdownScroll, getSelectedItem, getTextToReplace, - Header: PropHeader, itemClassName, itemStyle, onSelect, @@ -25,13 +22,8 @@ export const List = ({ value: propValue, values, }) => { - const { AutocompleteSuggestionHeader, AutocompleteSuggestionItem } = useComponentContext( - 'SuggestionList', - ); - const { themeVersion } = useChatContext('SuggestionList'); + const { AutocompleteSuggestionItem } = useComponentContext('SuggestionList'); const SuggestionItem = PropSuggestionItem || AutocompleteSuggestionItem || Item; - const SuggestionHeader = - PropHeader || AutocompleteSuggestionHeader || DefaultSuggestionListHeader; const [selectedItemIndex, setSelectedItemIndex] = useState(undefined); @@ -143,12 +135,7 @@ export const List = ({ const restructuredValues = useMemo(() => values.map(restructureItem), [values, restructureItem]); return ( -