diff --git a/frontend/src/components/BackgroundEffects/BackgroundGallery/BackgroundGallery.tsx b/frontend/src/components/BackgroundEffects/BackgroundGallery/BackgroundGallery.tsx index 07a58732d..d71d75041 100644 --- a/frontend/src/components/BackgroundEffects/BackgroundGallery/BackgroundGallery.tsx +++ b/frontend/src/components/BackgroundEffects/BackgroundGallery/BackgroundGallery.tsx @@ -110,9 +110,7 @@ const BackgroundGallery = ({ cursor: isSelected ? 'default' : 'pointer', backgroundColor: isSelected ? theme.colors.disabled : theme.colors.onBackground, '&:hover': { - backgroundColor: isSelected - ? theme.colors.disabled - : theme.colors.primaryHover, + backgroundColor: isSelected ? theme.colors.disabled : theme.colors.background, }, }} > diff --git a/frontend/src/components/DeviceAccessAlert/DeviceAccessAlert.tsx b/frontend/src/components/DeviceAccessAlert/DeviceAccessAlert.tsx index c8d7e658e..3c194e2b0 100644 --- a/frontend/src/components/DeviceAccessAlert/DeviceAccessAlert.tsx +++ b/frontend/src/components/DeviceAccessAlert/DeviceAccessAlert.tsx @@ -1,8 +1,12 @@ -import { AlertTitle, Box, Dialog, Stack, Alert } from '@mui/material'; import { ReactElement } from 'react'; import { useTranslation } from 'react-i18next'; import { DEVICE_ACCESS_STATUS } from '../../utils/constants'; import { isWebKit } from '../../utils/util'; +import Alert from '@ui/Alert'; +import Dialog from '@ui/Dialog'; +import Box from '@ui/Box'; +import Stack from '@ui/Stack'; +import AlertTitle from '@ui/AlertTitle'; export type DeviceAccessAlertProps = { accessStatus: string | null; diff --git a/frontend/src/components/HiddenParticipantsTile/HiddenParticipantsTile.tsx b/frontend/src/components/HiddenParticipantsTile/HiddenParticipantsTile.tsx index c0864e731..2155d78fa 100644 --- a/frontend/src/components/HiddenParticipantsTile/HiddenParticipantsTile.tsx +++ b/frontend/src/components/HiddenParticipantsTile/HiddenParticipantsTile.tsx @@ -1,11 +1,13 @@ import { ReactElement } from 'react'; -import { AvatarGroup } from '@mui/material'; import { Box } from 'opentok-layout-js'; import { SubscriberWrapper } from '@app-types/session'; import getBoxStyle from '@utils/helpers/getBoxStyle'; import useSessionContext from '@hooks/useSessionContext'; import useShouldShowParticipantList from '@Context/AppConfig/hooks/useShouldShowParticipantList'; import AvatarInitials from '../AvatarInitials'; +import AvatarGroup from '@ui/AvatarGroup'; +import ButtonBase from '@ui/ButtonBase'; +import useTheme from '@ui/theme'; export type HiddenParticipantsTileProps = { box: Box; @@ -28,24 +30,38 @@ const HiddenParticipantsTile = ({ const { toggleParticipantList } = useSessionContext(); const showParticipantList = useShouldShowParticipantList(); + const theme = useTheme(); const { height, width } = box; const diameter = Math.min(height, width) * 0.38; return ( - + ); }; diff --git a/frontend/src/components/Icons/PushPinOffIcon/PushPinOffIcon.tsx b/frontend/src/components/Icons/PushPinOffIcon/PushPinOffIcon.tsx index a82d2bc38..3a1a4e23b 100644 --- a/frontend/src/components/Icons/PushPinOffIcon/PushPinOffIcon.tsx +++ b/frontend/src/components/Icons/PushPinOffIcon/PushPinOffIcon.tsx @@ -1,4 +1,5 @@ -import { SvgIcon, SvgIconProps } from '@mui/material'; +import { SvgIconProps } from '@ui/SvgIconProps'; +import SvgIcon from '@ui/SvgIcon'; import { ReactElement } from 'react'; /** diff --git a/frontend/src/components/MeetingRoom/ArchivingButton/ArchivingButton.tsx b/frontend/src/components/MeetingRoom/ArchivingButton/ArchivingButton.tsx index 82d4e391e..617950455 100644 --- a/frontend/src/components/MeetingRoom/ArchivingButton/ArchivingButton.tsx +++ b/frontend/src/components/MeetingRoom/ArchivingButton/ArchivingButton.tsx @@ -1,5 +1,3 @@ -import RadioButtonCheckedIcon from '@mui/icons-material/RadioButtonChecked'; -import { Tooltip } from '@mui/material'; import { ReactElement, useState } from 'react'; import { useTranslation } from 'react-i18next'; import useRoomName from '@hooks/useRoomName'; @@ -8,6 +6,9 @@ import useSessionContext from '@hooks/useSessionContext'; import useAppConfig from '@Context/AppConfig/hooks/useAppConfig'; import ToolbarButton from '../ToolbarButton'; import PopupDialog, { DialogTexts } from '../PopupDialog'; +import Tooltip from '@ui/Tooltip'; +import useTheme from '@ui/theme'; +import VividIcon from '@components/VividIcon'; export type ArchivingButtonProps = { isOverflowButton?: boolean; @@ -31,6 +32,7 @@ const ArchivingButton = ({ }: ArchivingButtonProps): ReactElement | false => { const { t } = useTranslation(); const roomName = useRoomName(); + const theme = useTheme(); const { archiveId } = useSessionContext(); const allowArchiving = useAppConfig( ({ meetingRoomSettings }) => meetingRoomSettings.allowArchiving @@ -94,8 +96,10 @@ const ArchivingButton = ({ onClick={handleButtonClick} data-testid="archiving-button" icon={ - } sx={{ diff --git a/frontend/src/components/MeetingRoom/AudioIndicator/AudioIndicator.spec.tsx b/frontend/src/components/MeetingRoom/AudioIndicator/AudioIndicator.spec.tsx index 032d9d176..91934e66a 100644 --- a/frontend/src/components/MeetingRoom/AudioIndicator/AudioIndicator.spec.tsx +++ b/frontend/src/components/MeetingRoom/AudioIndicator/AudioIndicator.spec.tsx @@ -35,13 +35,13 @@ describe('AudioIndicator', () => { it('renders Mic icon when participant is unmuted but not speaking', () => { render(); - const micIcon = screen.getByTestId('MicIcon'); + const micIcon = screen.getByTestId('vivid-icon-microphone-2-solid'); expect(micIcon).toBeInTheDocument(); }); it('renders Mic off icon when participant is muted', () => { render(); - const micOffIcon = screen.getByTestId('MicOffIcon'); + const micOffIcon = screen.getByTestId('vivid-icon-mic-mute-solid'); expect(micOffIcon).toBeInTheDocument(); }); }); diff --git a/frontend/src/components/MeetingRoom/AudioIndicator/AudioIndicator.tsx b/frontend/src/components/MeetingRoom/AudioIndicator/AudioIndicator.tsx index e95b629f6..331ace432 100644 --- a/frontend/src/components/MeetingRoom/AudioIndicator/AudioIndicator.tsx +++ b/frontend/src/components/MeetingRoom/AudioIndicator/AudioIndicator.tsx @@ -1,16 +1,17 @@ -import { MicOff, Mic } from '@mui/icons-material'; import { ReactElement, useState } from 'react'; import { Stream } from '@vonage/client-sdk-video'; -import Tooltip from '@mui/material/Tooltip'; -import { IconButton } from '@mui/material'; import { useTranslation } from 'react-i18next'; import PopupDialog, { DialogTexts } from '../PopupDialog'; import VoiceIndicatorIcon from '../VoiceIndicator/VoiceIndicator'; import useSessionContext from '../../../hooks/useSessionContext'; +import IconButton from '@ui/IconButton'; +import Tooltip from '@ui/Tooltip'; +import VividIcon from '@components/VividIcon'; +import Box from '@ui/Box'; export type AudioIndicatorProps = { hasAudio: boolean | undefined; - indicatorStyle?: string; + indicatorStyle?: React.CSSProperties; indicatorColor?: string; stream?: Stream; audioLevel?: number; @@ -23,7 +24,7 @@ export type AudioIndicatorProps = { * This component displays an icon based on the audio status and handles muting another user. * @param {AudioIndicatorProps} hasAudio - Indicates whether the user has audio enabled. * @property {boolean | undefined} audioLevel - (optional) Indicates the current audio level of the video tile. - * @property {string} indicatorStyle - (optional) The styling props for the component. + * @property {React.CSSProperties} indicatorStyle - (optional) The MUI sx styling props for the component. * @property {string} indicatorColor - (optional) The color of the audio indicator button. * @property {Stream} stream - (optional) the stream that can be used to force mute a user. * @property {string} participantName - (optional) the name of the participant that can be muted. @@ -62,7 +63,6 @@ const AudioIndicator = ({ }; const sxProperties = { - fontSize: '18px', color: indicatorColor, cursor: hasAudio ? 'pointer' : 'default', }; @@ -72,7 +72,7 @@ const AudioIndicator = ({ } return ( -
+ - {hasAudio ? : } + {hasAudio ? ( + + ) : ( + + )} -
+ ); }; diff --git a/frontend/src/components/MeetingRoom/CaptionsButton/CaptionsBox/CaptionsBox.tsx b/frontend/src/components/MeetingRoom/CaptionsButton/CaptionsBox/CaptionsBox.tsx index 3907cdd4f..0ac211b6d 100644 --- a/frontend/src/components/MeetingRoom/CaptionsButton/CaptionsBox/CaptionsBox.tsx +++ b/frontend/src/components/MeetingRoom/CaptionsButton/CaptionsBox/CaptionsBox.tsx @@ -1,8 +1,9 @@ import { ReactElement } from 'react'; -import { Box } from '@mui/material'; +import Box from '@ui/Box'; import UserCaption from './UserCaption'; import useSessionContext from '../../../../hooks/useSessionContext'; import useIsSmallViewport from '../../../../hooks/useIsSmallViewport'; +import useTheme from '@ui/theme'; /** * CaptionsBox Component @@ -13,14 +14,15 @@ import useIsSmallViewport from '../../../../hooks/useIsSmallViewport'; const CaptionsBox = (): ReactElement => { const { subscriberWrappers, ownCaptions } = useSessionContext(); const isSmallViewPort = useIsSmallViewport(); + const theme = useTheme(); const sxBox = { position: 'absolute', bottom: isSmallViewPort ? 100 : 80, left: '50%', transform: 'translateX(-50%)', - backgroundColor: 'rgba(0, 0, 0, 0.7)', - color: 'white', + backgroundColor: theme.colors.darkGreyOpacity, + color: theme.colors.onDarkGrey, px: 2, py: isSmallViewPort ? 1 : 1.5, borderRadius: 2, diff --git a/frontend/src/components/MeetingRoom/CaptionsButton/CaptionsBox/UserCaption/UserCaption.tsx b/frontend/src/components/MeetingRoom/CaptionsButton/CaptionsBox/UserCaption/UserCaption.tsx index 16f21fea8..bc4b0d7ec 100644 --- a/frontend/src/components/MeetingRoom/CaptionsButton/CaptionsBox/UserCaption/UserCaption.tsx +++ b/frontend/src/components/MeetingRoom/CaptionsButton/CaptionsBox/UserCaption/UserCaption.tsx @@ -1,9 +1,10 @@ import { Subscriber } from '@vonage/client-sdk-video'; import { ReactElement, useState, useRef, useEffect } from 'react'; -import { Typography } from '@mui/material'; +import Typography from '@ui/Typography'; import { useTranslation } from 'react-i18next'; import useReceivingCaptions from '../../../../../hooks/useReceivingCaptions'; import { CAPTION_DISPLAY_DURATION_MS } from '../../../../../utils/constants'; +import useTheme from '@ui/theme'; export type UserCaptionProps = { subscriber: Subscriber | null; @@ -28,6 +29,7 @@ const UserCaption = ({ const { caption: captionText, isReceivingCaptions } = useReceivingCaptions({ subscriber, }); + const theme = useTheme(); const displayCaption = caption ?? captionText; const isActive = Boolean(caption ?? isReceivingCaptions); @@ -71,7 +73,7 @@ const UserCaption = ({ wordBreak: 'break-word', lineHeight: 1.4, textAlign: 'left', - color: 'white', + color: theme.colors.onDarkGrey, fontSize: isSmallViewPort ? '1rem' : '1.25rem', }} > diff --git a/frontend/src/components/MeetingRoom/CaptionsButton/CaptionsButton.tsx b/frontend/src/components/MeetingRoom/CaptionsButton/CaptionsButton.tsx index eefe07edf..9f783a6bf 100644 --- a/frontend/src/components/MeetingRoom/CaptionsButton/CaptionsButton.tsx +++ b/frontend/src/components/MeetingRoom/CaptionsButton/CaptionsButton.tsx @@ -1,5 +1,3 @@ -import { ClosedCaption, ClosedCaptionDisabled } from '@mui/icons-material'; -import { Tooltip } from '@mui/material'; import { Dispatch, ReactElement, useState, SetStateAction } from 'react'; import { AxiosError } from 'axios'; import { useTranslation } from 'react-i18next'; @@ -7,6 +5,9 @@ import useIsMeetingCaptionsAllowed from '@Context/AppConfig/hooks/useIsMeetingCa import { disableCaptions, enableCaptions } from '@api/captions'; import useRoomName from '@hooks/useRoomName'; import ToolbarButton from '../ToolbarButton'; +import Tooltip from '@ui/Tooltip'; +import VividIcon from '@components/VividIcon'; +import useTheme from '@ui/theme'; export type CaptionsState = { isUserCaptionsEnabled: boolean; @@ -43,6 +44,7 @@ const CaptionsButton = ({ const { isUserCaptionsEnabled, setIsUserCaptionsEnabled, setCaptionsErrorResponse } = captionsState; const title = isUserCaptionsEnabled ? t('captions.disable') : t('captions.enable'); + const theme = useTheme(); const handleClose = () => { if (isOverflowButton && handleClick) { @@ -105,12 +107,16 @@ const CaptionsButton = ({ data-testid="captions-button" icon={ !isUserCaptionsEnabled ? ( - + ) : ( - ) } diff --git a/frontend/src/components/MeetingRoom/CaptionsError/CaptionsError.tsx b/frontend/src/components/MeetingRoom/CaptionsError/CaptionsError.tsx index 9600b6cf0..6a787c2d7 100644 --- a/frontend/src/components/MeetingRoom/CaptionsError/CaptionsError.tsx +++ b/frontend/src/components/MeetingRoom/CaptionsError/CaptionsError.tsx @@ -1,8 +1,9 @@ -import { Snackbar, Alert } from '@mui/material'; import { Dispatch, ReactElement, SetStateAction } from 'react'; import { useTranslation } from 'react-i18next'; import { CAPTION_ERROR_DISPLAY_DURATION_MS } from '../../../utils/constants'; import useIsSmallViewport from '../../../hooks/useIsSmallViewport'; +import Snackbar from '@ui/Snackbar'; +import Alert from '@ui/Alert'; export type CaptionsErrorProps = { captionsErrorResponse: string | null; diff --git a/frontend/src/components/MeetingRoom/Chat/Chat.tsx b/frontend/src/components/MeetingRoom/Chat/Chat.tsx index bde5dd1e7..e16596849 100644 --- a/frontend/src/components/MeetingRoom/Chat/Chat.tsx +++ b/frontend/src/components/MeetingRoom/Chat/Chat.tsx @@ -1,5 +1,4 @@ import { ReactElement } from 'react'; -import { List } from '@mui/material'; import { useTranslation } from 'react-i18next'; import getInitials from '../../../utils/getInitials'; import getParticipantColor from '../../../utils/getParticipantColor'; @@ -7,6 +6,8 @@ import ChatMessage from '../ChatMessage'; import ChatInput from '../ChatInput'; import useSessionContext from '../../../hooks/useSessionContext'; import RightPanelTitle from '../RightPanel/RightPanelTitle'; +import List from '@ui/List'; +import Box from '@ui/Box'; export type ChatProps = { handleClose: () => void; @@ -24,13 +25,19 @@ export type ChatProps = { const Chat = ({ handleClose, isOpen }: ChatProps): ReactElement | false => { const { t } = useTranslation(); const { messages } = useSessionContext(); - const heightClass = '@apply h-[calc(100dvh_-_240px)]'; return ( isOpen && ( <> -
+ {messages.map((msg) => { return ( @@ -45,10 +52,18 @@ const Chat = ({ handleClose, isOpen }: ChatProps): ReactElement | false => { ); })} -
-
+ + -
+ ) ); diff --git a/frontend/src/components/MeetingRoom/ChatButton/ChatButton.spec.tsx b/frontend/src/components/MeetingRoom/ChatButton/ChatButton.spec.tsx index bef14563e..72dcfde22 100644 --- a/frontend/src/components/MeetingRoom/ChatButton/ChatButton.spec.tsx +++ b/frontend/src/components/MeetingRoom/ChatButton/ChatButton.spec.tsx @@ -39,12 +39,12 @@ describe('ChatButton', () => { it('should have a white icon when the list is closed', () => { render( {}} isOpen={false} />); - expect(screen.getByTestId('ChatIcon')).toHaveStyle('color: rgb(255, 255, 255)'); + expect(screen.getByTestId('vivid-icon-chat-solid')).toHaveStyle('color: rgb(255, 255, 255)'); }); it('should have a blue icon when the chat is open', () => { render( {}} isOpen />); - expect(screen.getByTestId('ChatIcon')).toHaveStyle('color: rgb(130, 177, 255)'); + expect(screen.getByTestId('vivid-icon-chat-solid')).toHaveStyle('color: rgb(0, 0, 0)'); }); it('should invoke callback on click', () => { diff --git a/frontend/src/components/MeetingRoom/ChatButton/ChatButton.tsx b/frontend/src/components/MeetingRoom/ChatButton/ChatButton.tsx index 4ad0f30de..75ef796ca 100644 --- a/frontend/src/components/MeetingRoom/ChatButton/ChatButton.tsx +++ b/frontend/src/components/MeetingRoom/ChatButton/ChatButton.tsx @@ -1,11 +1,11 @@ -import ChatIcon from '@mui/icons-material/Chat'; -import Tooltip from '@mui/material/Tooltip'; -import { blue } from '@mui/material/colors'; import { ReactElement } from 'react'; import { useTranslation } from 'react-i18next'; import useIsMeetingChatAllowed from '@Context/AppConfig/hooks/useIsMeetingChatAllowed'; import ToolbarButton from '../ToolbarButton'; import UnreadMessagesBadge from '../UnreadMessagesBadge'; +import Tooltip from '@ui/Tooltip'; +import useTheme from '@ui/theme'; +import VividIcon from '@components/VividIcon'; export type ChatButtonProps = { handleClick: () => void; @@ -30,8 +30,9 @@ const ChatButton = ({ isOverflowButton = false, }: ChatButtonProps): ReactElement | false => { const isMeetingChatAllowed = useIsMeetingChatAllowed(); - + const theme = useTheme(); const { t } = useTranslation(); + return ( isMeetingChatAllowed && ( @@ -43,7 +44,13 @@ const ChatButton = ({ marginRight: '0px', }} onClick={handleClick} - icon={} + icon={ + + } isOverflowButton={isOverflowButton} /> diff --git a/frontend/src/components/MeetingRoom/ChatInput/ChatInput.tsx b/frontend/src/components/MeetingRoom/ChatInput/ChatInput.tsx index cb811c861..c5d5204ed 100644 --- a/frontend/src/components/MeetingRoom/ChatInput/ChatInput.tsx +++ b/frontend/src/components/MeetingRoom/ChatInput/ChatInput.tsx @@ -1,10 +1,11 @@ -import TextField from '@mui/material/TextField'; -import SendIcon from '@mui/icons-material/Send'; -import { IconButton, InputAdornment } from '@mui/material'; import { KeyboardEvent, ReactElement, useState } from 'react'; -import { blue } from '@mui/material/colors'; import { useTranslation } from 'react-i18next'; import useSessionContext from '../../../hooks/useSessionContext'; +import IconButton from '@ui/IconButton'; +import InputAdornment from '@ui/InputAdornment'; +import TextField from '@ui/TextField'; +import useTheme from '@ui/theme'; +import VividIcon from '@components/VividIcon'; /** * ChatInput component @@ -15,6 +16,7 @@ import useSessionContext from '../../../hooks/useSessionContext'; */ const ChatInput = (): ReactElement => { const { t } = useTranslation(); + const theme = useTheme(); const [text, setText] = useState(''); const [isComposing, setIsComposing] = useState(false); const { sendChatMessage } = useSessionContext(); @@ -63,8 +65,8 @@ const ChatInput = (): ReactElement => { sx={{ margin: '16px', minHeight: '48px', - borderRadius: '25px', - backgroundColor: '#F1F3F4', + borderRadius: theme.shapes.borderRadiusExtraLarge, + backgroundColor: theme.colors.background, flexDirection: 'row', '&.MuiTextField-root': { paddingLeft: '24px', @@ -73,8 +75,12 @@ const ChatInput = (): ReactElement => { InputProps={{ endAdornment: ( - - + + ), diff --git a/frontend/src/components/MeetingRoom/ChatMessage/ChatMessage.tsx b/frontend/src/components/MeetingRoom/ChatMessage/ChatMessage.tsx index bc72c07d2..89caae126 100644 --- a/frontend/src/components/MeetingRoom/ChatMessage/ChatMessage.tsx +++ b/frontend/src/components/MeetingRoom/ChatMessage/ChatMessage.tsx @@ -1,8 +1,12 @@ -import { Avatar, ListItem, ListItemText, Typography } from '@mui/material'; import { ReactElement } from 'react'; import { useTranslation } from 'react-i18next'; import { getFormattedTime } from '../../../utils/dateTime'; import FormattedMessageBody from '../FormattedMessageBody'; +import ListItem from '@ui/ListItem'; +import ListItemText from '@ui/ListItemText'; +import Avatar from '@ui/Avatar'; +import Typography from '@ui/Typography'; +import useTheme from '@ui/theme'; export type ChatMessageProps = { avatarColor: string; @@ -32,6 +36,8 @@ const ChatMessage = ({ timestamp, }: ChatMessageProps): ReactElement => { const { i18n } = useTranslation(); + const theme = useTheme(); + return ( {name} @@ -60,7 +66,7 @@ const ChatMessage = ({ {getFormattedTime(i18n.language, timestamp)} @@ -68,7 +74,10 @@ const ChatMessage = ({ } secondary={ - + } diff --git a/frontend/src/components/MeetingRoom/ConnectionAlert/ConnectionAlert.tsx b/frontend/src/components/MeetingRoom/ConnectionAlert/ConnectionAlert.tsx index ceee5de84..b7bc393c0 100644 --- a/frontend/src/components/MeetingRoom/ConnectionAlert/ConnectionAlert.tsx +++ b/frontend/src/components/MeetingRoom/ConnectionAlert/ConnectionAlert.tsx @@ -1,4 +1,6 @@ -import { Alert, AlertTitle, SxProps } from '@mui/material'; +import Alert from '@ui/Alert'; +import AlertTitle from '@ui/AlertTitle'; +import { SxProps } from '@ui/SxProps'; import { ReactElement, useState } from 'react'; import useIsSmallViewport from '../../../hooks/useIsSmallViewport'; @@ -44,7 +46,7 @@ const ConnectionAlert = ({ { diff --git a/frontend/src/components/MeetingRoom/DeviceControlButton/DeviceControlButton.spec.tsx b/frontend/src/components/MeetingRoom/DeviceControlButton/DeviceControlButton.spec.tsx index 150a33a4c..3f85727e4 100644 --- a/frontend/src/components/MeetingRoom/DeviceControlButton/DeviceControlButton.spec.tsx +++ b/frontend/src/components/MeetingRoom/DeviceControlButton/DeviceControlButton.spec.tsx @@ -125,7 +125,7 @@ describe('DeviceControlButton', () => { expect(micButton).toBeInTheDocument(); expect(micButton).not.toBeDisabled(); - expect(screen.getByTestId('ArrowDropUpIcon')).toBeInTheDocument(); + expect(screen.getByTestId('vivid-icon-chevron-up-line')).toBeInTheDocument(); }); it('renders the button as disabled with greyed out icon and correct tooltip when allowMicrophoneControl is false', async () => { @@ -172,7 +172,7 @@ describe('DeviceControlButton', () => { expect(videoButton).toBeInTheDocument(); expect(videoButton).not.toBeDisabled(); - expect(screen.getByTestId('ArrowDropUpIcon')).toBeInTheDocument(); + expect(screen.getByTestId('vivid-icon-chevron-up-line')).toBeInTheDocument(); }); it('renders the button as disabled with greyed out icon and correct tooltip when allowCameraControl is false', async () => { diff --git a/frontend/src/components/MeetingRoom/DeviceControlButton/DeviceControlButton.tsx b/frontend/src/components/MeetingRoom/DeviceControlButton/DeviceControlButton.tsx index d0934c816..7b936802c 100644 --- a/frontend/src/components/MeetingRoom/DeviceControlButton/DeviceControlButton.tsx +++ b/frontend/src/components/MeetingRoom/DeviceControlButton/DeviceControlButton.tsx @@ -1,10 +1,3 @@ -import Mic from '@mui/icons-material/MicNone'; -import { IconButton } from '@mui/material'; -import VideocamIcon from '@mui/icons-material/Videocam'; -import VideocamOffIcon from '@mui/icons-material/VideocamOff'; -import Tooltip from '@mui/material/Tooltip'; -import ButtonGroup from '@mui/material/ButtonGroup'; -import { MicOff, ArrowDropUp, ArrowDropDown } from '@mui/icons-material'; import { useState, useRef, useCallback, ReactElement } from 'react'; import { useTranslation } from 'react-i18next'; import useIsMicrophoneControlAllowed from '@Context/AppConfig/hooks/useIsMicrophoneControlAllowed'; @@ -15,6 +8,11 @@ import getControlButtonTooltip from '@utils/getControlButtonTooltip'; import useTheme from '@ui/theme'; import DeviceSettingsMenu from '../DeviceSettingsMenu'; import MutedAlert from '../../MutedAlert'; +import ButtonGroup from '@ui/ButtonGroup'; +import IconButton from '@ui/IconButton'; +import Tooltip from '@ui/Tooltip'; +import VividIcon from '@components/VividIcon'; +import Box from '@ui/Box'; export type DeviceControlButtonProps = { deviceType: 'audio' | 'video'; @@ -71,21 +69,42 @@ const DeviceControlButton = ({ const renderControlIcon = () => { if (isAudio) { if (!isMicrophoneControlAllowed) { - return ; + return ( + + ); } if (isAudioEnabled) { - return ; + return ( + + ); } - return ; + return ( + + ); } if (!isCameraControlAllowed) { - return ; + return ; } if (isVideoEnabled) { - return ; + return ( + + ); } - return ; + return ; }; const handleDeviceStateChange = () => { @@ -101,9 +120,13 @@ const DeviceControlButton = ({ <> {isAudio && } {open ? ( - + ) : ( - + )} -
- {/* We add the div here so that the tooltip is present if the button is disabled */} + + {/* We add the Box here so that the tooltip is present if the button is disabled */} {renderControlIcon()} -
+
- {({ TransitionProps, placement }: PopperChildrenProps) => ( + {({ TransitionProps, placement }) => ( -
+ ({ @@ -135,7 +135,7 @@ const DeviceSettingsMenu = ({ {renderSettingsMenu()} -
+
)} diff --git a/frontend/src/components/MeetingRoom/DropdownSeparator/DropdownSeparator.tsx b/frontend/src/components/MeetingRoom/DropdownSeparator/DropdownSeparator.tsx index 34a7d766b..20566352f 100644 --- a/frontend/src/components/MeetingRoom/DropdownSeparator/DropdownSeparator.tsx +++ b/frontend/src/components/MeetingRoom/DropdownSeparator/DropdownSeparator.tsx @@ -1,4 +1,6 @@ import { ReactElement } from 'react'; +import Box from '@ui/Box'; +import useTheme from '@ui/theme'; /** * DropdownSeparator Component @@ -7,11 +9,16 @@ import { ReactElement } from 'react'; * @returns {ReactElement} The DropdownSeparator component. */ const DropdownSeparator = (): ReactElement => { + const theme = useTheme(); + return ( -
); }; diff --git a/frontend/src/components/MeetingRoom/Emoji/Emoji.spec.tsx b/frontend/src/components/MeetingRoom/Emoji/Emoji.spec.tsx index 4152bf32c..ea6075b4f 100644 --- a/frontend/src/components/MeetingRoom/Emoji/Emoji.spec.tsx +++ b/frontend/src/components/MeetingRoom/Emoji/Emoji.spec.tsx @@ -21,34 +21,32 @@ describe('Emoji component', () => { render(); const container = screen.getByTestId('emoji-string-container'); + const expectedDuration = EMOJI_DISPLAY_DURATION + 100; + expect(container).toHaveStyle({ position: 'absolute', animationName: 'moveEmoji', + animationDuration: `${expectedDuration}ms`, animationTimingFunction: 'linear', animationIterationCount: '1', maxWidth: '35%', zIndex: '1', }); - - const duration = container.style.animationDuration; - const expectedDuration = EMOJI_DISPLAY_DURATION + 100; - expect(parseInt(duration, 10)).toBe(expectedDuration); }); - it('applies expected Tailwind classes to container and Chip', () => { + it('applies expected MUI styles to container and Chip', () => { render(); const container = screen.getByTestId('emoji-string-container'); - expect(container).toHaveClass( - 'ml-5', - 'flex', - 'flex-col', - 'text-5xl', - 'md:ml-[15%]', - 'md:text-6xl' - ); + expect(container).toHaveStyle({ + display: 'flex', + }); const chip = screen.getByText(emojiWrapper.name).parentElement!; - expect(chip).toHaveClass('truncate', 'text-sm', 'md:text-lg'); + expect(chip).toHaveStyle({ + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + }); }); }); diff --git a/frontend/src/components/MeetingRoom/Emoji/Emoji.tsx b/frontend/src/components/MeetingRoom/Emoji/Emoji.tsx index 394e0e23b..0227ea3db 100644 --- a/frontend/src/components/MeetingRoom/Emoji/Emoji.tsx +++ b/frontend/src/components/MeetingRoom/Emoji/Emoji.tsx @@ -1,7 +1,10 @@ import { CSSProperties, ReactElement } from 'react'; -import { Chip } from '@mui/material'; import { EmojiWrapper } from '../../../hooks/useEmoji'; import { EMOJI_DISPLAY_DURATION } from '../../../utils/constants'; +import Chip from '@ui/Chip'; +import Box from '@ui/Box'; +import useTheme from '@ui/theme'; +import useIsSmallViewport from '@hooks/useIsSmallViewport'; export type EmojiProps = { emojiWrapper: EmojiWrapper; @@ -15,6 +18,8 @@ export type EmojiProps = { * @returns {ReactElement} - The Emoji Component. */ const Emoji = ({ emojiWrapper }: EmojiProps): ReactElement => { + const theme = useTheme(); + const isSmallViewport = useIsSmallViewport(); const { emoji, name } = emojiWrapper; const style: CSSProperties = { position: 'absolute', @@ -28,22 +33,35 @@ const Emoji = ({ emojiWrapper }: EmojiProps): ReactElement => { }; return ( -
{emoji} -
+ ); }; diff --git a/frontend/src/components/MeetingRoom/EmojiGrid/EmojiGridDesktop.tsx b/frontend/src/components/MeetingRoom/EmojiGrid/EmojiGridDesktop.tsx index 13ebebd7f..2626d52cb 100644 --- a/frontend/src/components/MeetingRoom/EmojiGrid/EmojiGridDesktop.tsx +++ b/frontend/src/components/MeetingRoom/EmojiGrid/EmojiGridDesktop.tsx @@ -1,9 +1,13 @@ -import { Grid, Grow, Paper, Popper, ClickAwayListener } from '@mui/material'; +import Grid from '@ui/Grid'; +import Grow from '@ui/Grow'; +import Paper from '@ui/Paper'; +import Popper from '@ui/Popper'; +import ClickAwayListener from '@ui/ClickAwayListener'; +import Box from '@ui/Box'; import { ReactElement, RefObject, useEffect, useState } from 'react'; import useTheme from '@ui/theme'; import SendEmojiButton from '../SendEmojiButton'; import emojiMap from '../../../utils/emojis'; -import { PopperChildrenProps } from '@mui/material/Popper/BasePopper.types'; export type EmojiGridDesktopProps = { handleClickAway: (event: MouseEvent | TouchEvent) => void; @@ -46,19 +50,27 @@ const EmojiGridDesktop = ({ disablePortal placement="bottom" > - {({ TransitionProps, placement }: PopperChildrenProps) => ( + {({ TransitionProps, placement }) => ( -
+ -
+
)} diff --git a/frontend/src/components/MeetingRoom/EmojiGrid/EmojiGridMobile.tsx b/frontend/src/components/MeetingRoom/EmojiGrid/EmojiGridMobile.tsx index 5082067fd..6b780ae93 100644 --- a/frontend/src/components/MeetingRoom/EmojiGrid/EmojiGridMobile.tsx +++ b/frontend/src/components/MeetingRoom/EmojiGrid/EmojiGridMobile.tsx @@ -1,7 +1,12 @@ -import { Box, Grid, Grow, Portal, ClickAwayListener } from '@mui/material'; +import Box from '@ui/Box'; +import Grid from '@ui/Grid'; +import Grow from '@ui/Grow'; +import Portal from '@ui/Portal'; +import ClickAwayListener from '@ui/ClickAwayListener'; import { ReactElement } from 'react'; import SendEmojiButton from '../SendEmojiButton'; import emojiMap from '../../../utils/emojis'; +import useTheme from '@ui/theme'; export type EmojiGridMobileProps = { handleClickAway: (event: MouseEvent | TouchEvent) => void; @@ -23,41 +28,45 @@ const EmojiGridMobile = ({ handleClickAway, isToolbarOpen, isEmojiGridOpen, -}: EmojiGridMobileProps): ReactElement => ( - - - - { + const theme = useTheme(); + + return ( + + + - - {Object.values(emojiMap).map((emoji) => ( - - ))} - - - - - -); + + {Object.values(emojiMap).map((emoji) => ( + + ))} + + + + + + ); +}; export default EmojiGridMobile; diff --git a/frontend/src/components/MeetingRoom/EmojiGridButton/EmojiGridButton.tsx b/frontend/src/components/MeetingRoom/EmojiGridButton/EmojiGridButton.tsx index 22a6f0e01..1db1a4f70 100644 --- a/frontend/src/components/MeetingRoom/EmojiGridButton/EmojiGridButton.tsx +++ b/frontend/src/components/MeetingRoom/EmojiGridButton/EmojiGridButton.tsx @@ -1,11 +1,11 @@ -import { Tooltip } from '@mui/material'; -import { EmojiEmotions } from '@mui/icons-material'; +import Tooltip from '@ui/Tooltip'; import { Dispatch, ReactElement, SetStateAction, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import useAppConfig from '@Context/AppConfig/hooks/useAppConfig'; import useTheme from '@ui/theme'; import ToolbarButton from '../ToolbarButton'; import EmojiGrid from '../EmojiGrid/EmojiGrid'; +import VividIcon from '@components/VividIcon'; export type EmojiGridProps = { isEmojiGridOpen: boolean; @@ -46,9 +46,11 @@ const EmojiGridButton = ({ } diff --git a/frontend/src/components/MeetingRoom/ExitButton/ExitButton.tsx b/frontend/src/components/MeetingRoom/ExitButton/ExitButton.tsx index 552668756..214dfa59c 100644 --- a/frontend/src/components/MeetingRoom/ExitButton/ExitButton.tsx +++ b/frontend/src/components/MeetingRoom/ExitButton/ExitButton.tsx @@ -1,10 +1,11 @@ -import CallEndIcon from '@mui/icons-material/CallEnd'; -import Tooltip from '@mui/material/Tooltip'; +import Tooltip from '@ui/Tooltip'; import { useNavigate } from 'react-router-dom'; import { ReactElement } from 'react'; import { useTranslation } from 'react-i18next'; import ToolbarButton from '../ToolbarButton'; import useRoomName from '../../../hooks/useRoomName'; +import useTheme from '@ui/theme'; +import VividIcon from '@components/VividIcon'; export type ExitButtonProps = { handleLeave: () => void; @@ -22,6 +23,7 @@ const ExitButton = ({ handleLeave }: ExitButtonProps): ReactElement => { const { t } = useTranslation(); const navigate = useNavigate(); const roomName = useRoomName(); + const theme = useTheme(); const handleExit = () => { handleLeave(); @@ -33,13 +35,18 @@ const ExitButton = ({ handleLeave }: ExitButtonProps): ReactElement => { } + icon={ + + } /> ); diff --git a/frontend/src/components/MeetingRoom/FormattedMessageBody/FormattedMessageBody.tsx b/frontend/src/components/MeetingRoom/FormattedMessageBody/FormattedMessageBody.tsx index 848d48558..fffb93735 100644 --- a/frontend/src/components/MeetingRoom/FormattedMessageBody/FormattedMessageBody.tsx +++ b/frontend/src/components/MeetingRoom/FormattedMessageBody/FormattedMessageBody.tsx @@ -1,5 +1,5 @@ import { ReactElement } from 'react'; -import { Link } from '@mui/material'; +import Link from '@ui/Link'; import linkGroupsParser from '../../../utils/linkGroupsParser'; export type FormattedMessageBodyProps = { diff --git a/frontend/src/components/MeetingRoom/InputDevices/InputDevices.spec.tsx b/frontend/src/components/MeetingRoom/InputAudioDevices/InputAudioDevices.spec.tsx similarity index 88% rename from frontend/src/components/MeetingRoom/InputDevices/InputDevices.spec.tsx rename to frontend/src/components/MeetingRoom/InputAudioDevices/InputAudioDevices.spec.tsx index ff7cc3041..a286d11de 100644 --- a/frontend/src/components/MeetingRoom/InputDevices/InputDevices.spec.tsx +++ b/frontend/src/components/MeetingRoom/InputAudioDevices/InputAudioDevices.spec.tsx @@ -9,7 +9,7 @@ import usePublisherContext from '@hooks/usePublisherContext'; import { AllMediaDevices } from '@app-types/room'; import { PublisherContextType } from '@Context/PublisherProvider'; import { allMediaDevices, defaultAudioDevice } from '@utils/mockData/device'; -import InputDevices from './InputDevices'; +import InputAudioDevices from './InputAudioDevices'; // Mocks vi.mock('@hooks/useDevices'); @@ -27,7 +27,7 @@ const mockUseDevices = useDevices as Mock< >; const mockUsePublisherContext = usePublisherContext as Mock<[], PublisherContextType>; -describe('InputDevices Component', () => { +describe('InputAudioDevices Component', () => { const mockHandleToggle = vi.fn(); const mockSetAudioSource = vi.fn(); const mockGetAudioSource = vi.fn(); @@ -64,7 +64,7 @@ describe('InputDevices Component', () => { }); it('renders all available audio input devices', () => { - render(); + render(); expect(screen.getByText('Microphone')).toBeInTheDocument(); @@ -75,7 +75,7 @@ describe('InputDevices Component', () => { }); it('changes audio input device on menu item click', () => { - render(); + render(); const micItem = screen.getByText('MacBook Pro Microphone (Built-in)'); fireEvent.click(micItem); @@ -87,7 +87,7 @@ describe('InputDevices Component', () => { }); it('does not call setAudioSource if selected device is not found', () => { - render(); + render(); const bogusItem = document.createElement('li'); bogusItem.textContent = 'Nonexistent Microphone'; @@ -100,7 +100,7 @@ describe('InputDevices Component', () => { publisherContext.publisher = null; mockUsePublisherContext.mockReturnValue(publisherContext); - render(); + render(); const micItem = screen.getByText('MacBook Pro Microphone (Built-in)'); fireEvent.click(micItem); @@ -110,15 +110,15 @@ describe('InputDevices Component', () => { }); it('shows check icon for selected device', () => { - render(); + render(); // The default audio device should be selected - const checkIcon = screen.getByTestId('CheckIcon'); + const checkIcon = screen.getByTestId('vivid-icon-check-line'); expect(checkIcon).toBeInTheDocument(); }); it('is not rendered when allowDeviceSelection is false', () => { - render(, { + render(, { appConfigOptions: { value: { meetingRoomSettings: { @@ -132,7 +132,7 @@ describe('InputDevices Component', () => { }); it('handles click event when audioDeviceId is found', () => { - render(); + render(); const micItem = screen.getByText('Soundcore Life A2 NC (Bluetooth)'); fireEvent.click(micItem); diff --git a/frontend/src/components/MeetingRoom/InputDevices/InputDevices.tsx b/frontend/src/components/MeetingRoom/InputAudioDevices/InputAudioDevices.tsx similarity index 71% rename from frontend/src/components/MeetingRoom/InputDevices/InputDevices.tsx rename to frontend/src/components/MeetingRoom/InputAudioDevices/InputAudioDevices.tsx index e7ef4e623..9ce1950bb 100644 --- a/frontend/src/components/MeetingRoom/InputDevices/InputDevices.tsx +++ b/frontend/src/components/MeetingRoom/InputAudioDevices/InputAudioDevices.tsx @@ -1,29 +1,31 @@ -import { Box, MenuItem, MenuList, Typography } from '@mui/material'; -import CheckIcon from '@mui/icons-material/Check'; +import Box from '@ui/Box'; +import Typography from '@ui/Typography'; +import MenuItem from '@ui/MenuItem'; +import MenuList from '@ui/MenuList'; import { Device } from '@vonage/client-sdk-video'; -import MicNoneIcon from '@mui/icons-material/MicNone'; import { MouseEvent as ReactMouseEvent, ReactElement, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import useAppConfig from '@Context/AppConfig/hooks/useAppConfig'; import useTheme from '@ui/theme'; +import VividIcon from '@components/VividIcon'; import useDevices from '../../../hooks/useDevices'; import usePublisherContext from '../../../hooks/usePublisherContext'; import { setStorageItem, STORAGE_KEYS } from '../../../utils/storage'; import cleanAndDedupeDeviceLabels from '../../../utils/cleanAndDedupeDeviceLabels'; -export type InputDevicesProps = { +export type InputAudioDevicesProps = { handleToggle: () => void; }; /** - * InputDevices Component + * InputAudioDevices Component * * Displays the audio input devices for a user. Handles switching audio input devices. - * @param {InputDevicesProps} props - The props for the component. + * @param {InputAudioDevicesProps} props - The props for the component. * @property {Function} handleToggle - The click handler to handle closing the menu. - * @returns {ReactElement | false} - The InputDevices component. + * @returns {ReactElement | false} - The InputAudioDevices component. */ -const InputDevices = ({ handleToggle }: InputDevicesProps): ReactElement | false => { +const InputAudioDevices = ({ handleToggle }: InputAudioDevicesProps): ReactElement | false => { const { t } = useTranslation(); const theme = useTheme(); const { publisher } = usePublisherContext(); @@ -63,13 +65,15 @@ const InputDevices = ({ handleToggle }: InputDevicesProps): ReactElement | false - - {t('devices.audio.microphone.full')} + + {t('devices.audio.microphone.full')} {options.map((option: string) => { @@ -86,7 +90,7 @@ const InputDevices = ({ handleToggle }: InputDevicesProps): ReactElement | false color: theme.colors.onBackground, }, '&:hover': { - backgroundColor: theme.colors.primaryHover, + backgroundColor: theme.colors.background, }, }} > @@ -96,16 +100,19 @@ const InputDevices = ({ handleToggle }: InputDevicesProps): ReactElement | false display: 'flex', mb: 0.5, overflow: 'hidden', + color: isSelected ? theme.colors.textPrimary : theme.colors.textSecondary, }} > {isSelected ? ( - + + + ) : ( // Placeholder when CheckIcon is not displayed )} @@ -120,4 +127,4 @@ const InputDevices = ({ handleToggle }: InputDevicesProps): ReactElement | false ); }; -export default InputDevices; +export default InputAudioDevices; diff --git a/frontend/src/components/MeetingRoom/InputAudioDevices/index.tsx b/frontend/src/components/MeetingRoom/InputAudioDevices/index.tsx new file mode 100644 index 000000000..c8bb39f76 --- /dev/null +++ b/frontend/src/components/MeetingRoom/InputAudioDevices/index.tsx @@ -0,0 +1,3 @@ +import InputDevices from './InputAudioDevices'; + +export default InputDevices; diff --git a/frontend/src/components/MeetingRoom/InputDevices/index.tsx b/frontend/src/components/MeetingRoom/InputDevices/index.tsx deleted file mode 100644 index e30c4b913..000000000 --- a/frontend/src/components/MeetingRoom/InputDevices/index.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import InputDevices from './InputDevices'; - -export default InputDevices; diff --git a/frontend/src/components/MeetingRoom/LayoutButton/LayoutButton.spec.tsx b/frontend/src/components/MeetingRoom/LayoutButton/LayoutButton.spec.tsx index baf170641..544c8e8db 100644 --- a/frontend/src/components/MeetingRoom/LayoutButton/LayoutButton.spec.tsx +++ b/frontend/src/components/MeetingRoom/LayoutButton/LayoutButton.spec.tsx @@ -22,7 +22,7 @@ describe('LayoutButton', () => { ); rerender(); - expect(screen.getByTestId('ViewSidebarIcon')).toBeInTheDocument(); + expect(screen.getByTestId('vivid-icon-layout-2-solid')).toBeInTheDocument(); }); it('should call the set layout mode function when triggered', async () => { @@ -47,7 +47,7 @@ describe('LayoutButton', () => { ); rerender(); - expect(screen.getByTestId('WindowIcon')).toBeInTheDocument(); + expect(screen.getByTestId('vivid-icon-apps-solid')).toBeInTheDocument(); }); it('should render the tooltip title that mentions switching to grid layout', async () => { diff --git a/frontend/src/components/MeetingRoom/LayoutButton/LayoutButton.tsx b/frontend/src/components/MeetingRoom/LayoutButton/LayoutButton.tsx index 51704000d..99b424ed4 100644 --- a/frontend/src/components/MeetingRoom/LayoutButton/LayoutButton.tsx +++ b/frontend/src/components/MeetingRoom/LayoutButton/LayoutButton.tsx @@ -1,10 +1,10 @@ -import ViewSidebarIcon from '@mui/icons-material/ViewSidebar'; -import Tooltip from '@mui/material/Tooltip'; -import WindowIcon from '@mui/icons-material/Window'; import { ReactElement } from 'react'; import { useTranslation } from 'react-i18next'; import useSessionContext from '../../../hooks/useSessionContext'; import ToolbarButton from '../ToolbarButton'; +import Tooltip from '@ui/Tooltip'; +import VividIcon from '@components/VividIcon'; +import useTheme from '@ui/theme'; export type LayoutButtonProps = { isScreenSharePresent: boolean; @@ -31,7 +31,7 @@ const LayoutButton = ({ const { layoutMode, setLayoutMode } = useSessionContext(); const isGrid = layoutMode === 'grid'; const isDisabled = isScreenSharePresent || isPinningPresent; - + const theme = useTheme(); const handleClick = () => { if (isDisabled) { return; @@ -56,14 +56,21 @@ const LayoutButton = ({ data-testid="layout-button" icon={ !isGrid ? ( - + ) : ( - + ) } sx={{ cursor: isDisabled ? 'not-allowed' : 'pointer', - // on the small view port devices we need to align the button marginTop: isOverflowButton ? '0px' : '4px', }} isOverflowButton={isOverflowButton} diff --git a/frontend/src/components/MeetingRoom/MutingDialog/MutingDialog.tsx b/frontend/src/components/MeetingRoom/MutingDialog/MutingDialog.tsx index b8096008e..0bead124d 100644 --- a/frontend/src/components/MeetingRoom/MutingDialog/MutingDialog.tsx +++ b/frontend/src/components/MeetingRoom/MutingDialog/MutingDialog.tsx @@ -1,12 +1,12 @@ -import Button from '@mui/material/Button'; -import Dialog from '@mui/material/Dialog'; -import DialogActions from '@mui/material/DialogActions'; -import DialogContent from '@mui/material/DialogContent'; -import DialogContentText from '@mui/material/DialogContentText'; import { Stream } from '@vonage/client-sdk-video'; import { ReactElement } from 'react'; import { useTranslation } from 'react-i18next'; import useSessionContext from '../../../hooks/useSessionContext'; +import Dialog from '@ui/Dialog'; +import DialogActions from '@ui/DialogActions'; +import DialogContent from '@ui/DialogContent'; +import DialogContentText from '@ui/DialogContentText'; +import Button from '@ui/Button'; type MutingDialogTexts = { contents: string; diff --git a/frontend/src/components/MeetingRoom/NameDisplay/NameDisplay.tsx b/frontend/src/components/MeetingRoom/NameDisplay/NameDisplay.tsx index f8c031bf0..8568ccc29 100644 --- a/frontend/src/components/MeetingRoom/NameDisplay/NameDisplay.tsx +++ b/frontend/src/components/MeetingRoom/NameDisplay/NameDisplay.tsx @@ -1,5 +1,7 @@ import { ReactElement } from 'react'; -import { TEXT_SHADOW } from '../../../utils/constants'; +import Box from '@ui/Box'; +import Typography from '@ui/Typography'; +import useTheme from '@ui/theme'; export type NameDisplayProps = { containerWidth: number; @@ -16,17 +18,26 @@ export type NameDisplayProps = { * @returns {ReactElement} The NameDisplay component. */ const NameDisplay = ({ name, containerWidth }: NameDisplayProps): ReactElement => { + const theme = useTheme(); const safeMaxWidth = typeof containerWidth === 'number' && Number.isFinite(containerWidth) ? containerWidth : 0; return ( -
- {name} -
+ + {name} + +
); }; diff --git a/frontend/src/components/MeetingRoom/OutputDevices/OutputDevices.tsx b/frontend/src/components/MeetingRoom/OutputAudioDevices/OutputAudioDevices.tsx similarity index 73% rename from frontend/src/components/MeetingRoom/OutputDevices/OutputDevices.tsx rename to frontend/src/components/MeetingRoom/OutputAudioDevices/OutputAudioDevices.tsx index def2338ea..aa7176c58 100644 --- a/frontend/src/components/MeetingRoom/OutputDevices/OutputDevices.tsx +++ b/frontend/src/components/MeetingRoom/OutputAudioDevices/OutputAudioDevices.tsx @@ -1,6 +1,3 @@ -import { Box, MenuItem, MenuList, Typography } from '@mui/material'; -import CheckIcon from '@mui/icons-material/Check'; -import VolumeUpIcon from '@mui/icons-material/VolumeUp'; import { MouseEvent, ReactElement, useMemo } from 'react'; import type { AudioOutputDevice } from '@vonage/client-sdk-video'; import { useTranslation } from 'react-i18next'; @@ -11,20 +8,25 @@ import useAudioOutputContext from '@hooks/useAudioOutputContext'; import { isGetActiveAudioOutputDeviceSupported } from '@utils/util'; import cleanAndDedupeDeviceLabels from '@utils/cleanAndDedupeDeviceLabels'; import DropdownSeparator from '../DropdownSeparator'; +import Box from '@ui/Box'; +import Typography from '@ui/Typography'; +import MenuList from '@ui/MenuList'; +import MenuItem from '@ui/MenuItem'; +import VividIcon from '@components/VividIcon'; -export type OutputDevicesProps = { +export type OutputAudioDevicesProps = { handleToggle: () => void; }; /** - * OutputDevices Component + * OutputAudioDevices Component * * Displays and switches audio output devices. - * @param {OutputDevicesProps} props - The props for the component. + * @param {OutputAudioDevicesProps} props - The props for the component. * @property {() => void} handleToggle - Function to close the menu. - * @returns {ReactElement | false} - The OutputDevices component. + * @returns {ReactElement | false} - The OutputAudioDevices component. */ -const OutputDevices = ({ handleToggle }: OutputDevicesProps): ReactElement | false => { +const OutputAudioDevices = ({ handleToggle }: OutputAudioDevicesProps): ReactElement | false => { const { t } = useTranslation(); const theme = useTheme(); const { currentAudioOutputDevice, setAudioOutputDevice } = useAudioOutputContext(); @@ -68,13 +70,15 @@ const OutputDevices = ({ handleToggle }: OutputDevicesProps): ReactElement | fal - - + + {t('devices.audio.speakers.full')} @@ -95,7 +99,7 @@ const OutputDevices = ({ handleToggle }: OutputDevicesProps): ReactElement | fal color: theme.colors.onBackground, }, '&:hover': { - backgroundColor: theme.colors.primaryHover, + backgroundColor: theme.colors.background, }, }} > @@ -105,16 +109,19 @@ const OutputDevices = ({ handleToggle }: OutputDevicesProps): ReactElement | fal display: 'flex', mb: 0.5, overflow: 'hidden', + color: isSelected ? theme.colors.textPrimary : theme.colors.textSecondary, }} > {isSelected ? ( - + + + ) : ( // Placeholder when CheckIcon is not displayed )} @@ -129,4 +136,4 @@ const OutputDevices = ({ handleToggle }: OutputDevicesProps): ReactElement | fal ); }; -export default OutputDevices; +export default OutputAudioDevices; diff --git a/frontend/src/components/MeetingRoom/OutputDevices/OutputDevices.spec.tsx b/frontend/src/components/MeetingRoom/OutputAudioDevices/OutputDevices.spec.tsx similarity index 86% rename from frontend/src/components/MeetingRoom/OutputDevices/OutputDevices.spec.tsx rename to frontend/src/components/MeetingRoom/OutputAudioDevices/OutputDevices.spec.tsx index 623d8c516..cb957f8fd 100644 --- a/frontend/src/components/MeetingRoom/OutputDevices/OutputDevices.spec.tsx +++ b/frontend/src/components/MeetingRoom/OutputAudioDevices/OutputDevices.spec.tsx @@ -8,7 +8,7 @@ import { allMediaDevices } from '@utils/mockData/device'; import * as util from '@utils/util'; import { AllMediaDevices } from '@app-types/room'; import { AppConfigProviderWrapperOptions, makeAppConfigProviderWrapper } from '@test/providers'; -import OutputDevices from './OutputDevices'; +import OutputAudioDevices from './OutputAudioDevices'; // Mocks vi.mock('@hooks/useDevices'); @@ -20,7 +20,7 @@ const mockUseDevices = useDevices as Mock< >; const mockUseAudioOutputContext = useAudioOutputContext as Mock<[], AudioOutputContextType>; -describe('OutputDevices Component', () => { +describe('OutputAudioDevices Component', () => { const mockHandleToggle = vi.fn(); const mockSetAudioOutputDevice = vi.fn(); let audioOutputContext: AudioOutputContextType; @@ -47,7 +47,7 @@ describe('OutputDevices Component', () => { }); it('renders all available audio output devices when supported', () => { - render(); + render(); expect(screen.getByText('Speakers')).toBeInTheDocument(); expect(screen.getByTestId('output-devices')).toBeInTheDocument(); @@ -61,7 +61,7 @@ describe('OutputDevices Component', () => { it('renders only default device when audio output is not supported', () => { (util.isGetActiveAudioOutputDeviceSupported as Mock).mockReturnValue(false); - render(); + render(); expect(screen.getByText('Speakers')).toBeInTheDocument(); expect(screen.getByText('System Default')).toBeInTheDocument(); @@ -71,7 +71,7 @@ describe('OutputDevices Component', () => { }); it('changes audio output device on menu item click when supported', () => { - render(); + render(); const speakerItem = screen.getByText('Soundcore Life A2 NC (Bluetooth)'); fireEvent.click(speakerItem); @@ -85,7 +85,7 @@ describe('OutputDevices Component', () => { it('does not call setAudioOutputDevice when audio output is not supported', () => { (util.isGetActiveAudioOutputDeviceSupported as Mock).mockReturnValue(false); - render(); + render(); const defaultItem = screen.getByText('System Default'); fireEvent.click(defaultItem); @@ -95,25 +95,25 @@ describe('OutputDevices Component', () => { }); it('shows check icon for selected device', () => { - render(); + render(); // The device with deviceId 'default' should be selected - const checkIcon = screen.getByTestId('CheckIcon'); + const checkIcon = screen.getByTestId('vivid-icon-check-line'); expect(checkIcon).toBeInTheDocument(); }); it('shows check icon for default device when only one device available', () => { (util.isGetActiveAudioOutputDeviceSupported as Mock).mockReturnValue(false); - render(); + render(); // When only default device is available, it should be selected - const checkIcon = screen.getByTestId('CheckIcon'); + const checkIcon = screen.getByTestId('vivid-icon-check-line'); expect(checkIcon).toBeInTheDocument(); }); it('is not rendered when allowDeviceSelection is false', () => { - render(, { + render(, { appConfigOptions: { value: { meetingRoomSettings: { diff --git a/frontend/src/components/MeetingRoom/OutputAudioDevices/index.tsx b/frontend/src/components/MeetingRoom/OutputAudioDevices/index.tsx new file mode 100644 index 000000000..d224c1783 --- /dev/null +++ b/frontend/src/components/MeetingRoom/OutputAudioDevices/index.tsx @@ -0,0 +1,3 @@ +import OutputDevices from './OutputAudioDevices'; + +export default OutputDevices; diff --git a/frontend/src/components/MeetingRoom/OutputDevices/index.tsx b/frontend/src/components/MeetingRoom/OutputDevices/index.tsx deleted file mode 100644 index 07fc276a0..000000000 --- a/frontend/src/components/MeetingRoom/OutputDevices/index.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import OutputDevices from './OutputDevices'; - -export default OutputDevices; diff --git a/frontend/src/components/MeetingRoom/ParticipantList/ParticipantList.spec.tsx b/frontend/src/components/MeetingRoom/ParticipantList/ParticipantList.spec.tsx index 58c4ab15c..745e5d5f5 100644 --- a/frontend/src/components/MeetingRoom/ParticipantList/ParticipantList.spec.tsx +++ b/frontend/src/components/MeetingRoom/ParticipantList/ParticipantList.spec.tsx @@ -103,12 +103,12 @@ describe('ParticipantList', () => { render( {}} />); - const copyButton = screen.getByTestId('ContentCopyIcon'); + const copyButton = screen.getByTestId('vivid-icon-copy-line'); fireEvent.click(copyButton); await waitFor(() => { expect(navigator.clipboard.writeText).toHaveBeenCalledWith('https://example.com/room123'); - expect(screen.getByTestId('CheckIcon')).toBeInTheDocument(); + expect(screen.getByTestId('vivid-icon-check-sent-line')).toBeInTheDocument(); }); }); diff --git a/frontend/src/components/MeetingRoom/ParticipantList/ParticipantList.tsx b/frontend/src/components/MeetingRoom/ParticipantList/ParticipantList.tsx index bc400dd3f..78629943d 100644 --- a/frontend/src/components/MeetingRoom/ParticipantList/ParticipantList.tsx +++ b/frontend/src/components/MeetingRoom/ParticipantList/ParticipantList.tsx @@ -1,7 +1,4 @@ -import { Fade, IconButton, List, Tooltip } from '@mui/material'; -import { ContentCopy } from '@mui/icons-material'; -import CheckIcon from '@mui/icons-material/Check'; -import { ReactElement, useState } from 'react'; +import { ReactElement, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import useSessionContext from '../../../hooks/useSessionContext'; import useUserContext from '../../../hooks/useUserContext'; @@ -13,6 +10,15 @@ import getParticipantColor from '../../../utils/getParticipantColor'; import useRoomShareUrl from '../../../hooks/useRoomShareUrl'; import RightPanelTitle from '../RightPanel/RightPanelTitle'; import usePublisherContext from '../../../hooks/usePublisherContext'; +import IconButton from '@ui/IconButton'; +import Tooltip from '@ui/Tooltip'; +import Fade from '@ui/Fade'; +import List from '@ui/List'; +import Box from '@ui/Box'; +import Typography from '@ui/Typography'; +import useTheme from '@ui/theme'; +import Stack from '@ui/Stack'; +import VividIcon from '@components/VividIcon'; const compareNameAlphabetically = (a: SubscriberWrapper, b: SubscriberWrapper) => { const nameA = a.subscriber?.stream?.name; @@ -43,6 +49,7 @@ export type ParticipantListProps = { */ const ParticipantList = ({ handleClose, isOpen }: ParticipantListProps): ReactElement | false => { const { t } = useTranslation(); + const theme = useTheme(); const { subscriberWrappers } = useSessionContext(); const publisherAudio = useAudioLevels(); const [isCopied, setIsCopied] = useState(false); @@ -54,6 +61,11 @@ const ParticipantList = ({ handleClose, isOpen }: ParticipantListProps): ReactEl const roomShareUrl = useRoomShareUrl(); const { isAudioEnabled } = usePublisherContext(); + const participantCount = useMemo( + () => 1 + subscriberWrappers.filter(({ isScreenshare }) => !isScreenshare).length, + [subscriberWrappers] + ); + const copyUrl = () => { navigator.clipboard.writeText(roomShareUrl); @@ -67,35 +79,60 @@ const ParticipantList = ({ handleClose, isOpen }: ParticipantListProps): ReactEl return ( isOpen && ( <> - -
-
- - {t('chat.meetingUrl')} - {' '} -
- + + + {t('chat.meetingUrl')} + {window.location.href} - -
+ + - {isCopied ? : } + {isCopied ? ( + + ) : ( + + )} -
+
void; @@ -32,8 +32,9 @@ const ParticipantListButton = ({ isOverflowButton = false, }: ParticipantListButtonProps): ReactElement | false => { const showParticipantList = useShouldShowParticipantList(); - + const theme = useTheme(); const { t } = useTranslation(); + return ( showParticipantList && ( } + icon={ + + } isOverflowButton={isOverflowButton} /> diff --git a/frontend/src/components/MeetingRoom/ParticipantListButton/ParticipantsListButton.spec.tsx b/frontend/src/components/MeetingRoom/ParticipantListButton/ParticipantsListButton.spec.tsx index 69cce6482..bea03a3ba 100644 --- a/frontend/src/components/MeetingRoom/ParticipantListButton/ParticipantsListButton.spec.tsx +++ b/frontend/src/components/MeetingRoom/ParticipantListButton/ParticipantsListButton.spec.tsx @@ -11,11 +11,11 @@ describe('ParticipantListButton', () => { }); it('should have a white icon when the list is closed', () => { render( {}} isOpen={false} participantCount={10} />); - expect(screen.getByTestId('PeopleIcon')).toHaveStyle('color: rgb(255, 255, 255)'); + expect(screen.getByTestId('vivid-icon-group-solid')).toHaveStyle('color: rgb(255, 255, 255)'); }); it('should have a blue icon when the list is open', () => { render( {}} isOpen participantCount={10} />); - expect(screen.getByTestId('PeopleIcon')).toHaveStyle('color: rgb(130, 177, 255)'); + expect(screen.getByTestId('vivid-icon-group-solid')).toHaveStyle('color: rgb(0, 0, 0)'); }); it('should invoke callback on click', () => { const handleClick = vi.fn(); diff --git a/frontend/src/components/MeetingRoom/ParticipantListItem/ParticipantListItem.spec.tsx b/frontend/src/components/MeetingRoom/ParticipantListItem/ParticipantListItem.spec.tsx index 1076f8f5d..72da44c28 100644 --- a/frontend/src/components/MeetingRoom/ParticipantListItem/ParticipantListItem.spec.tsx +++ b/frontend/src/components/MeetingRoom/ParticipantListItem/ParticipantListItem.spec.tsx @@ -108,7 +108,7 @@ describe('ParticipantListItem', () => { const audioIndicator = screen.getByTestId('audio-indicator'); expect(audioIndicator).toBeInTheDocument(); - const micIcon = screen.getByTestId('MicIcon'); + const micIcon = screen.getByTestId('vivid-icon-microphone-2-solid'); expect(micIcon).toBeInTheDocument(); }); @@ -121,7 +121,7 @@ describe('ParticipantListItem', () => { const audioIndicator = screen.getByTestId('audio-indicator'); expect(audioIndicator).toBeInTheDocument(); - const micOffIcon = screen.getByTestId('MicOffIcon'); + const micOffIcon = screen.getByTestId('vivid-icon-mic-mute-solid'); expect(micOffIcon).toBeInTheDocument(); }); }); diff --git a/frontend/src/components/MeetingRoom/ParticipantListItem/ParticipantListItem.tsx b/frontend/src/components/MeetingRoom/ParticipantListItem/ParticipantListItem.tsx index efc8629bb..b937e0a00 100644 --- a/frontend/src/components/MeetingRoom/ParticipantListItem/ParticipantListItem.tsx +++ b/frontend/src/components/MeetingRoom/ParticipantListItem/ParticipantListItem.tsx @@ -1,10 +1,15 @@ import { ReactElement } from 'react'; -import { Avatar, Badge, ListItem, Typography } from '@mui/material'; -import PushPinIcon from '@mui/icons-material/PushPin'; import { Stream } from '@vonage/client-sdk-video'; import AudioIndicator from '../AudioIndicator'; import ParticipantListItemMenu from '../ParticipantListItemMenu'; import { SubscriberWrapper } from '../../../types/session'; +import ListItem from '@ui/ListItem'; +import Avatar from '@ui/Avatar'; +import Typography from '@ui/Typography'; +import Badge from '@ui/Badge'; +import useTheme from '@ui/theme'; +import VividIcon from '@components/VividIcon'; +import Box from '@ui/Box'; export type ParticipantListItemProps = { stream?: Stream; @@ -40,24 +45,26 @@ const ParticipantListItem = ({ stream, subscriberWrapper, }: ParticipantListItemProps): ReactElement => { + const theme = useTheme(); + return ( + {subscriberWrapper && ( )} -
+ } > @@ -91,7 +99,7 @@ const ParticipantListItem = ({ diff --git a/frontend/src/components/MeetingRoom/ParticipantListItemMenu/ParticipantListItemMenu.tsx b/frontend/src/components/MeetingRoom/ParticipantListItemMenu/ParticipantListItemMenu.tsx index bfed06d70..c2985a923 100644 --- a/frontend/src/components/MeetingRoom/ParticipantListItemMenu/ParticipantListItemMenu.tsx +++ b/frontend/src/components/MeetingRoom/ParticipantListItemMenu/ParticipantListItemMenu.tsx @@ -1,8 +1,12 @@ import { useState, MouseEvent, ReactElement } from 'react'; -import { ClickAwayListener, IconButton, Paper, Popper } from '@mui/material'; -import MoreVertIcon from '@mui/icons-material/MoreVert'; import { SubscriberWrapper } from '../../../types/session'; import ParticipantPinMenuItem from './ParticipantPinMenuItem'; +import IconButton from '@ui/IconButton'; +import Popper from '@ui/Popper'; +import ClickAwayListener from '@ui/ClickAwayListener'; +import Paper from '@ui/Paper'; +import VividIcon from '@components/VividIcon'; +import useTheme from '@ui/theme'; export type ParticipantListItemMenuProps = { participantName: string; @@ -21,6 +25,7 @@ const ParticipantListItemMenu = ({ participantName, subscriberWrapper, }: ParticipantListItemMenuProps): ReactElement => { + const theme = useTheme(); const [anchorEl, setAnchorEl] = useState(null); const isOpen = !!anchorEl; const handleClick = (event: MouseEvent) => { @@ -33,7 +38,7 @@ const ParticipantListItemMenu = ({ return ( <> - + diff --git a/frontend/src/components/MeetingRoom/ParticipantListItemMenu/ParticipantPinMenuItem.tsx b/frontend/src/components/MeetingRoom/ParticipantListItemMenu/ParticipantPinMenuItem.tsx index 4b11c07c3..8f33a512d 100644 --- a/frontend/src/components/MeetingRoom/ParticipantListItemMenu/ParticipantPinMenuItem.tsx +++ b/frontend/src/components/MeetingRoom/ParticipantListItemMenu/ParticipantPinMenuItem.tsx @@ -1,10 +1,12 @@ import { ReactElement } from 'react'; -import { ListItemIcon, ListItemText, MenuItem } from '@mui/material'; -import PushPinIcon from '@mui/icons-material/PushPin'; import { useTranslation } from 'react-i18next'; import { SubscriberWrapper } from '../../../types/session'; -import PushPinOffIcon from '../../Icons/PushPinOffIcon'; import useSessionContext from '../../../hooks/useSessionContext'; +import MenuItem from '@ui/MenuItem'; +import ListItemIcon from '@ui/ListItemIcon'; +import ListItemText from '@ui/ListItemText'; +import VividIcon from '@components/VividIcon'; +import useTheme from '@ui/theme'; export type ParticipantPinMenuItemProps = { handleClick: () => void; @@ -27,6 +29,7 @@ const ParticipantPinMenuItem = ({ subscriberWrapper, }: ParticipantPinMenuItemProps): ReactElement => { const { t } = useTranslation(); + const theme = useTheme(); const { isPinned, id } = subscriberWrapper; const { isMaxPinned, pinSubscriber } = useSessionContext(); const isDisabled = !isPinned && isMaxPinned; @@ -55,9 +58,21 @@ const ParticipantPinMenuItem = ({ > {isPinned ? ( - + ) : ( - + )} { const { t } = useTranslation(); + const theme = useTheme(); const isDisabled = isMaxPinned && !isPinned; const anchorRef = useRef(null); const [isHoveringButton, setIsHoveringButton] = useState(false); const iconSx = { - fontSize: '18px', - color: isDisabled ? 'rgba(255,255,255,.54)' : 'white', + color: isDisabled ? theme.colors.disabled : theme.colors.surface, cursor: 'pointer', }; @@ -72,9 +74,20 @@ const PinButton = ({ const shouldShowIcon = isTileHovered || isPinned; return ( shouldShowIcon && ( -
setIsHoveringButton(true)} onPointerLeave={() => setIsHoveringButton(false)} @@ -89,18 +102,18 @@ const PinButton = ({ width: 24, borderRadius: '50%', cursor: 'pointer', - backgroundColor: isTileHovered ? 'rgb(32, 33, 36, .55)' : 'none', - '&:hover, &.Mui-focusVisible': { backgroundColor: 'rgb(32, 33, 36, .75)' }, + backgroundColor: isTileHovered ? theme.colors.darkGrey : 'none', + '&:hover, &.Mui-focusVisible': { backgroundColor: theme.colors.darkGreyHover }, }} > {isTileHovered && isPinned ? ( - + ) : ( - + )} -
+ ) ); }; diff --git a/frontend/src/components/MeetingRoom/PopupDialog/PopupDialog.tsx b/frontend/src/components/MeetingRoom/PopupDialog/PopupDialog.tsx index 0dfce37f6..7acc70868 100644 --- a/frontend/src/components/MeetingRoom/PopupDialog/PopupDialog.tsx +++ b/frontend/src/components/MeetingRoom/PopupDialog/PopupDialog.tsx @@ -1,9 +1,9 @@ -import Button from '@mui/material/Button'; -import Dialog from '@mui/material/Dialog'; -import DialogActions from '@mui/material/DialogActions'; -import DialogContent from '@mui/material/DialogContent'; -import DialogContentText from '@mui/material/DialogContentText'; -import DialogTitle from '@mui/material/DialogTitle'; +import Dialog from '@ui/Dialog'; +import DialogActions from '@ui/DialogActions'; +import DialogContent from '@ui/DialogContent'; +import DialogContentText from '@ui/DialogContentText'; +import DialogTitle from '@ui/DialogTitle'; +import Button from '@ui/Button'; import { ReactElement } from 'react'; export type DialogTexts = { diff --git a/frontend/src/components/MeetingRoom/ReduceNoiseTestSpeakers/ReduceNoiseTestSpeakers.tsx b/frontend/src/components/MeetingRoom/ReduceNoiseTestSpeakers/ReduceNoiseTestSpeakers.tsx index e8cf48653..70b43cc63 100644 --- a/frontend/src/components/MeetingRoom/ReduceNoiseTestSpeakers/ReduceNoiseTestSpeakers.tsx +++ b/frontend/src/components/MeetingRoom/ReduceNoiseTestSpeakers/ReduceNoiseTestSpeakers.tsx @@ -1,9 +1,5 @@ -import { Typography, MenuItem, IconButton, MenuList } from '@mui/material'; import { useState, useEffect, ReactElement } from 'react'; -import Grow from '@mui/material/Grow'; -import VolumeUpIcon from '@mui/icons-material/VolumeUp'; import { hasMediaProcessorSupport } from '@vonage/client-sdk-video'; -import HeadsetIcon from '@mui/icons-material/Headset'; import ToggleOffIcon from '@mui/icons-material/ToggleOff'; import ToggleOnIcon from '@mui/icons-material/ToggleOn'; import { useTranslation } from 'react-i18next'; @@ -13,6 +9,13 @@ import usePublisherContext from '@hooks/usePublisherContext'; import { setStorageItem, STORAGE_KEYS } from '@utils/storage'; import DropdownSeparator from '../DropdownSeparator'; import SoundTest from '../../SoundTest'; +import MenuList from '@ui/MenuList'; +import MenuItem from '@ui/MenuItem'; +import IconButton from '@ui/IconButton'; +import Typography from '@ui/Typography'; +import Grow from '@ui/Grow'; +import VividIcon from '@components/VividIcon'; +import Box from '@ui/Box'; /** * ReduceNoiseTestSpeakers Component @@ -66,14 +69,19 @@ const ReduceNoiseTestSpeakers = (): ReactElement | false => { - - + + + + {t('devices.audio.noiseSuppression')} @@ -81,7 +89,7 @@ const ReduceNoiseTestSpeakers = (): ReactElement | false => { @@ -90,7 +98,7 @@ const ReduceNoiseTestSpeakers = (): ReactElement | false => { fontSize="large" sx={{ position: 'absolute', - color: theme.colors.background, + color: theme.colors.secondary, }} /> @@ -98,7 +106,13 @@ const ReduceNoiseTestSpeakers = (): ReactElement | false => { )} - + + + diff --git a/frontend/src/components/MeetingRoom/ReportIssue/FeedbackForm/FeedbackForm.tsx b/frontend/src/components/MeetingRoom/ReportIssue/FeedbackForm/FeedbackForm.tsx index e1fcc4c13..dbc0642b1 100644 --- a/frontend/src/components/MeetingRoom/ReportIssue/FeedbackForm/FeedbackForm.tsx +++ b/frontend/src/components/MeetingRoom/ReportIssue/FeedbackForm/FeedbackForm.tsx @@ -1,4 +1,3 @@ -import { TextField, Button, Typography, Box, CircularProgress } from '@mui/material'; import { FormEvent, ChangeEvent, ReactElement } from 'react'; import { useTranslation } from 'react-i18next'; import FilePicker from './FilePicker'; @@ -9,6 +8,12 @@ import { } from '../../../../utils/constants'; import HelperText from './HelperText'; import useIsSmallViewport from '../../../../hooks/useIsSmallViewport'; +import Box from '@ui/Box'; +import TextField from '@ui/TextField'; +import Typography from '@ui/Typography'; +import Button from '@ui/Button'; +import CircularProgress from '@ui/CircularProgress'; +import useTheme from '@ui/theme'; export type FormType = { title: string; @@ -33,7 +38,7 @@ export type FeedbackFormType = { const getStyleTypography = () => { return { - marginBottom: '5px', + marginBottom: '4px', textAlign: 'left', }; }; @@ -61,6 +66,7 @@ const FeedbackForm = ({ onFileSelect, }: FeedbackFormType): ReactElement => { const { t } = useTranslation(); + const theme = useTheme(); const isSmallViewport = useIsSmallViewport(); // 224px = 64px panel header + 96px toolbar if normal viewport + (40px submit button + 24px submit button margin) // 208px = 64px panel header + 80px toolbar if small viewport + (40px submit button + 24px submit button margin) @@ -70,7 +76,7 @@ const FeedbackForm = ({ const width = isSmallViewport ? 'calc(100dvw - 50px)' : '310px'; const getColorStyle = (value: string, maxLength: number) => { - return value.length >= maxLength || value.length === 0 ? 'red' : 'inherit'; + return value.length >= maxLength || value.length === 0 ? theme.colors.error : 'inherit'; }; return loading ? ( @@ -82,22 +88,18 @@ const FeedbackForm = ({ ) : (
- + {t('feedbackForm.field.title.label')} - + {t('feedbackForm.field.name.label')} {t('feedbackForm.field.issue.label')} @@ -194,11 +190,8 @@ const FeedbackForm = ({ {t('feedbackForm.disclaiamer.label')} @@ -208,8 +201,6 @@ const FeedbackForm = ({ color="textPrimary" sx={{ ...getStyleTypography(), - fontSize: '0.8rem', - textAlign: 'left', }} > {t('feedbackForm.disclaiamer.screenshot')} @@ -223,7 +214,6 @@ const FeedbackForm = ({ fullWidth sx={{ textTransform: 'none', - fontSize: '1rem', margin: '12px 24px', width, }} diff --git a/frontend/src/components/MeetingRoom/ReportIssue/FeedbackForm/FilePicker/FilePicker.tsx b/frontend/src/components/MeetingRoom/ReportIssue/FeedbackForm/FilePicker/FilePicker.tsx index 656eb1d48..dbd71980c 100644 --- a/frontend/src/components/MeetingRoom/ReportIssue/FeedbackForm/FilePicker/FilePicker.tsx +++ b/frontend/src/components/MeetingRoom/ReportIssue/FeedbackForm/FilePicker/FilePicker.tsx @@ -1,9 +1,14 @@ import { ChangeEvent, useRef, useState, ReactElement } from 'react'; -import { Button, IconButton, Tooltip, Typography } from '@mui/material'; -import { Delete } from '@mui/icons-material'; import { useTranslation } from 'react-i18next'; +import Box from '@ui/Box'; import captureScreenshot from '../../../../../utils/captureScreenshot'; import { isMobile } from '@utils/util'; +import Button from '@ui/Button'; +import IconButton from '@ui/IconButton'; +import Tooltip from '@ui/Tooltip'; +import Typography from '@ui/Typography'; +import useTheme from '@ui/theme'; +import VividIcon from '@components/VividIcon'; // Setting the maximum file size to 20MB const maxFileSize = 2e7; @@ -25,6 +30,7 @@ const FilePicker = ({ const [imageSrc, setImageSrc] = useState(''); const imageRef = useRef(null); const [maximumSizeError, setMaximumSizeError] = useState(false); + const theme = useTheme(); const checkIfSizeAllowed = (file: File) => { if (file.size > maxFileSize) { @@ -75,22 +81,25 @@ const FilePicker = ({ <> {imageSrc && ( {t('filePicker.attachedScreenshot')} )} -
+ {maximumSizeError && ( {t('filePicker.sizeLimit')} @@ -119,8 +128,6 @@ const FilePicker = ({ component="label" > {t('filePicker.addScreenshot')} - {/* - */} ) : ( -
-
- + + - + - + -
-
+
+ )} -
+
); }; diff --git a/frontend/src/components/MeetingRoom/ReportIssue/FeedbackForm/HelperText/HelperText.tsx b/frontend/src/components/MeetingRoom/ReportIssue/FeedbackForm/HelperText/HelperText.tsx index 616f1b443..d8af23e78 100644 --- a/frontend/src/components/MeetingRoom/ReportIssue/FeedbackForm/HelperText/HelperText.tsx +++ b/frontend/src/components/MeetingRoom/ReportIssue/FeedbackForm/HelperText/HelperText.tsx @@ -1,13 +1,11 @@ -import { Box } from '@mui/material'; import { ReactElement } from 'react'; import { useTranslation } from 'react-i18next'; - -export type ColorStyle = 'inherit' | 'red'; +import Box from '@ui/Box'; export type HelperTextProps = { isError: boolean; errorType: string; - colorStyle: ColorStyle; + colorStyle: string; textLimit: number; formText: string; testId: string; @@ -21,7 +19,7 @@ export type HelperTextProps = { * @param {HelperTextProps} props - The props for the component. * @property {boolean} isError - Whether to display the error or not. * @property {string} errorType - What kind of error this is. - * @property {ColorStyle} colorStyle - Whether to use plain or red text. + * @property {string} colorStyle - Whether to use plain or red text. * @property {number} textLimit - How many characters can exist for the form type. * @property {string} formText - What is present in the form's field. * @property {string} testId - The testId to assign to the error name diff --git a/frontend/src/components/MeetingRoom/ReportIssue/FormSubmitted/FormSubmitted.tsx b/frontend/src/components/MeetingRoom/ReportIssue/FormSubmitted/FormSubmitted.tsx index 3a95a9d7f..49dc9bd53 100644 --- a/frontend/src/components/MeetingRoom/ReportIssue/FormSubmitted/FormSubmitted.tsx +++ b/frontend/src/components/MeetingRoom/ReportIssue/FormSubmitted/FormSubmitted.tsx @@ -1,6 +1,10 @@ -import { Typography, Button } from '@mui/material'; import { ReactElement } from 'react'; import { useTranslation } from 'react-i18next'; +import Box from '@ui/Box'; +import Button from '@ui/Button'; +import Typography from '@ui/Typography'; +import Link from '@ui/Link'; +import useTheme from '@ui/theme'; export type TicketResponseType = { message: string; @@ -27,12 +31,21 @@ const FormSubmitted = ({ ticketResponse, }: FormSubmittedProps): ReactElement => { const { t } = useTranslation(); + const theme = useTheme(); + return ( <> -
- + {ticketResponse.ticketUrl ? ( - <> - {t('feedbackForm.submitted.thanks')} -
- {t('feedbackForm.submitted.content')} -
- {ticketResponse.message}
- + + {t('feedbackForm.submitted.thanks')} + + + {t('feedbackForm.submitted.content')} + + + {ticketResponse.message} + + {t('feedbackForm.submitted.track')} - - + +
) : ( - + {t('feedbackForm.submitted.error')} -
-
+
)} - -
-
+ + + -
+ ); }; diff --git a/frontend/src/components/MeetingRoom/ReportIssueButton/ReportIssueButton.spec.tsx b/frontend/src/components/MeetingRoom/ReportIssueButton/ReportIssueButton.spec.tsx index 6de421444..11ae7b1a0 100644 --- a/frontend/src/components/MeetingRoom/ReportIssueButton/ReportIssueButton.spec.tsx +++ b/frontend/src/components/MeetingRoom/ReportIssueButton/ReportIssueButton.spec.tsx @@ -6,11 +6,13 @@ import ReportIssueButton from './ReportIssueButton'; describe('ReportIssueButton', () => { it('should have a white icon when the form is closed', () => { render( {}} isOpen={false} />); - expect(screen.getByTestId('FeedbackIcon')).toHaveStyle('color: rgb(255, 255, 255)'); + expect(screen.getByTestId('vivid-icon-feedback-solid')).toHaveStyle( + 'color: rgb(255, 255, 255)' + ); }); it('should have a blue icon when the form is open', () => { render( {}} isOpen />); - expect(screen.getByTestId('FeedbackIcon')).toHaveStyle('color: rgb(130, 177, 255)'); + expect(screen.getByTestId('vivid-icon-feedback-solid')).toHaveStyle('color: rgb(0, 0, 0)'); }); it('should invoke callback on click', () => { const handleClick = vi.fn(); diff --git a/frontend/src/components/MeetingRoom/ReportIssueButton/ReportIssueButton.tsx b/frontend/src/components/MeetingRoom/ReportIssueButton/ReportIssueButton.tsx index 6e87d3492..0d84763bb 100644 --- a/frontend/src/components/MeetingRoom/ReportIssueButton/ReportIssueButton.tsx +++ b/frontend/src/components/MeetingRoom/ReportIssueButton/ReportIssueButton.tsx @@ -1,9 +1,9 @@ -import { Tooltip } from '@mui/material'; +import Tooltip from '@ui/Tooltip'; import { ReactElement, useRef } from 'react'; -import FeedbackIcon from '@mui/icons-material/Feedback'; -import { blue } from '@mui/material/colors'; import { useTranslation } from 'react-i18next'; import ToolbarButton from '../ToolbarButton'; +import useTheme from '@ui/theme'; +import VividIcon from '@components/VividIcon'; export type ReportIssueButtonProps = { handleClick: () => void; @@ -27,6 +27,7 @@ const ReportIssueButton = ({ isOverflowButton = false, }: ReportIssueButtonProps): ReactElement => { const { t } = useTranslation(); + const theme = useTheme(); const anchorRef = useRef(null); return ( @@ -41,7 +42,13 @@ const ReportIssueButton = ({ marginRight: '12px', }} onClick={handleClick} - icon={} + icon={ + + } ref={anchorRef} isOverflowButton={isOverflowButton} /> diff --git a/frontend/src/components/MeetingRoom/RightPanel/RightPanel.tsx b/frontend/src/components/MeetingRoom/RightPanel/RightPanel.tsx index 830a0d505..650c24c4a 100644 --- a/frontend/src/components/MeetingRoom/RightPanel/RightPanel.tsx +++ b/frontend/src/components/MeetingRoom/RightPanel/RightPanel.tsx @@ -5,6 +5,8 @@ import ReportIssue from '../ReportIssue'; import type { RightPanelActiveTab } from '../../../hooks/useRightPanel'; import useIsSmallViewport from '../../../hooks/useIsSmallViewport'; import BackgroundEffectsLayout from '../../BackgroundEffects/BackgroundEffectsLayout'; +import Box from '@ui/Box'; +import useTheme from '@ui/theme'; export type RightPanelProps = { handleClose: () => void; @@ -22,15 +24,26 @@ export type RightPanelProps = { */ const RightPanel = ({ activeTab, handleClose }: RightPanelProps): ReactElement => { const isSmallViewport = useIsSmallViewport(); - const width = isSmallViewport ? 'w-dvw' : 'w-[360px]'; - const margins = isSmallViewport ? 'm-0' : 'mr-4 mt-4'; - const height = isSmallViewport - ? '@apply h-[calc(100dvh_-_80px)]' - : '@apply h-[calc(100dvh_-_96px)]'; - const className = `${height} absolute top-0 ${margins} ${width} overflow-hidden rounded bg-white transition-[right] ${activeTab === 'closed' ? 'right-[-380px] hidden' : 'right-0'}`; + const theme = useTheme(); return ( -
+ -
+ ); }; diff --git a/frontend/src/components/MeetingRoom/RightPanel/RightPanelTitle.tsx b/frontend/src/components/MeetingRoom/RightPanel/RightPanelTitle.tsx index eef972763..c54d8cf95 100644 --- a/frontend/src/components/MeetingRoom/RightPanel/RightPanelTitle.tsx +++ b/frontend/src/components/MeetingRoom/RightPanel/RightPanelTitle.tsx @@ -1,6 +1,9 @@ -import { Close } from '@mui/icons-material'; -import { IconButton } from '@mui/material'; +import IconButton from '@ui/IconButton'; import { ReactElement } from 'react'; +import VividIcon from '@components/VividIcon'; +import Box from '@ui/Box'; +import Typography from '@ui/Typography'; +import useTheme from '@ui/theme'; export type RightPanelTitleProps = { handleClose: () => void; @@ -17,17 +20,33 @@ export type RightPanelTitleProps = { * @returns {ReactElement} - RightPanelTitle component */ const RightPanelTitle = ({ handleClose, title }: RightPanelTitleProps): ReactElement => { + const theme = useTheme(); return ( -
- {title} - - + + {title} + + + -
+ ); }; diff --git a/frontend/src/components/MeetingRoom/ScreenSharePublisher/ScreenSharePublisher.spec.tsx b/frontend/src/components/MeetingRoom/ScreenSharePublisher/ScreenSharePublisher.spec.tsx index e301888fd..da108b8cd 100644 --- a/frontend/src/components/MeetingRoom/ScreenSharePublisher/ScreenSharePublisher.spec.tsx +++ b/frontend/src/components/MeetingRoom/ScreenSharePublisher/ScreenSharePublisher.spec.tsx @@ -34,9 +34,9 @@ describe('ScreenSharePublisher component', () => { render(); expect(screen.getByText('Test Stream')).toBeInTheDocument(); - expect(element.classList).toContain('w-full'); - expect(element.classList).toContain('absolute'); - expect(element.classList).toContain('rounded-xl'); - expect(element.classList).toContain('object-contain'); + expect(element.style.width).toBe('100%'); + expect(element.style.position).toBe('absolute'); + expect(element.style.borderRadius).toBe('12px'); + expect(element.style.objectFit).toBe('contain'); }); }); diff --git a/frontend/src/components/MeetingRoom/ScreenSharePublisher/ScreenSharePublisher.tsx b/frontend/src/components/MeetingRoom/ScreenSharePublisher/ScreenSharePublisher.tsx index 7ccd90c3a..b2ec515c1 100644 --- a/frontend/src/components/MeetingRoom/ScreenSharePublisher/ScreenSharePublisher.tsx +++ b/frontend/src/components/MeetingRoom/ScreenSharePublisher/ScreenSharePublisher.tsx @@ -3,6 +3,7 @@ import { Box } from 'opentok-layout-js'; import { Publisher } from '@vonage/client-sdk-video'; import VideoTile from '../VideoTile'; import ScreenShareNameDisplay from '../../ScreenShareNameDisplay'; +import useTheme from '@ui/theme'; export type ScreenSharePublisherProps = { box: Box | undefined; @@ -24,10 +25,16 @@ const ScreenSharePublisher = ({ element, publisher, }: ScreenSharePublisherProps): ReactElement | undefined => { + const theme = useTheme(); const containerRef = useRef(null); useEffect(() => { if (element && containerRef.current) { - element.classList.add('w-full', 'absolute', 'rounded-xl', 'object-contain'); + Object.assign(element.style, { + width: '100%', + position: 'absolute', + borderRadius: theme.shapes.borderRadiusLarge, + objectFit: 'contain', + }); containerRef.current.appendChild(element); } }, [element]); diff --git a/frontend/src/components/MeetingRoom/ScreenshareVideoTile/ScreenshareVideoTile.spec.tsx b/frontend/src/components/MeetingRoom/ScreenshareVideoTile/ScreenshareVideoTile.spec.tsx index 2a5fb024a..99b3c776e 100644 --- a/frontend/src/components/MeetingRoom/ScreenshareVideoTile/ScreenshareVideoTile.spec.tsx +++ b/frontend/src/components/MeetingRoom/ScreenshareVideoTile/ScreenshareVideoTile.spec.tsx @@ -66,10 +66,10 @@ describe('ScreenshareVideoTile', () => { }); it('renders with custom className', () => { - render(); + render(); const tile = screen.getByTestId('screenshare-tile'); - expect(tile).toHaveClass('bg-red-500'); + expect(tile).toHaveClass('test'); }); it('calls onMouseEnter and onMouseLeave handlers', () => { @@ -303,11 +303,17 @@ describe('ScreenshareVideoTile', () => { }); describe('Component structure', () => { - it('has correct CSS classes', () => { + it('has correct CSS styles', () => { render(); const tile = screen.getByTestId('screenshare-tile'); - expect(tile).toHaveClass('absolute', 'm-1', 'flex', 'items-center', 'justify-center'); + expect(tile).toHaveStyle({ + position: 'absolute', + margin: '8px', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }); }); it('has correct id attribute', () => { diff --git a/frontend/src/components/MeetingRoom/ScreenshareVideoTile/ScreenshareVideoTile.tsx b/frontend/src/components/MeetingRoom/ScreenshareVideoTile/ScreenshareVideoTile.tsx index 834cfbc8f..d2d75f1a4 100644 --- a/frontend/src/components/MeetingRoom/ScreenshareVideoTile/ScreenshareVideoTile.tsx +++ b/frontend/src/components/MeetingRoom/ScreenshareVideoTile/ScreenshareVideoTile.tsx @@ -13,6 +13,8 @@ import { hasMediaProcessorSupport } from '@vonage/client-sdk-video'; import getBoxStyle from '../../../utils/helpers/getBoxStyle'; import ZoomIndicator from '../ZoomIndicator'; import { MAX_ZOOM, MIN_ZOOM, ZOOM_STEP } from '../../../utils/constants'; +import useTheme from '@ui/theme'; +import CustomBox from '@ui/Box'; export type ScreenshareVideoTileProps = { 'data-testid': string; @@ -44,6 +46,7 @@ const ScreenshareVideoTile = forwardRef( }: ScreenshareVideoTileProps, ref: ForwardedRef ): ReactElement => { + const theme = useTheme(); // Zoom state management const [zoomLevel, setZoomLevel] = useState(1); const [panOffset, setPanOffset] = useState<{ x: number; y: number }>({ @@ -160,15 +163,19 @@ const ScreenshareVideoTile = forwardRef( }; return ( -
onMouseEnter?.()} onMouseLeave={() => { setIsDragging(false); @@ -179,17 +186,33 @@ const ScreenshareVideoTile = forwardRef( onMouseMove={handleMouseMove} onMouseUp={handleMouseUp} > -
-
{children} @@ -201,7 +224,7 @@ const ScreenshareVideoTile = forwardRef( zoomOut={zoomOut} /> )} -
+ ); } ); diff --git a/frontend/src/components/MeetingRoom/SendEmojiButton/SendEmojiButton.tsx b/frontend/src/components/MeetingRoom/SendEmojiButton/SendEmojiButton.tsx index 2d90345c3..7dd8adc33 100644 --- a/frontend/src/components/MeetingRoom/SendEmojiButton/SendEmojiButton.tsx +++ b/frontend/src/components/MeetingRoom/SendEmojiButton/SendEmojiButton.tsx @@ -1,8 +1,9 @@ -import { Button, Grid, GridSize } from '@mui/material'; import { ReactElement } from 'react'; import useTheme from '@ui/theme'; import useSessionContext from '../../../hooks/useSessionContext'; import useIsSmallViewport from '../../../hooks/useIsSmallViewport'; +import Grid from '@ui/Grid'; +import Button from '@ui/Button'; export type SendEmojiButtonProps = { emoji: string; @@ -19,17 +20,17 @@ const SendEmojiButton = ({ emoji }: SendEmojiButtonProps): ReactElement => { const { sendEmoji } = useSessionContext(); const theme = useTheme(); const isSmallViewport = useIsSmallViewport(); - const xs: GridSize = isSmallViewport ? 2 : 3; + const xs = isSmallViewport ? 2 : 3; const size = isSmallViewport ? 'small' : 'large'; return ( - +