Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
312623d
Add chat svg icon from designs
olipyskoty Feb 11, 2021
1efbdcb
add chat button to menu bar
olipyskoty Feb 12, 2021
7f6e9a8
add chat window to room and refactor materialui code to make use of m…
olipyskoty Feb 12, 2021
324106a
add chat provider context to index.js
olipyskoty Feb 12, 2021
fdcf1e9
add chat window styles to theme
olipyskoty Feb 12, 2021
26835c9
add chat button
olipyskoty Feb 12, 2021
f8632cf
add chat window component
olipyskoty Feb 12, 2021
8a4e3f7
add chatProvider and useChatContext hook
olipyskoty Feb 12, 2021
2c22fa5
add button for closing chat window
olipyskoty Feb 12, 2021
cb06376
add component for chat window header
olipyskoty Feb 12, 2021
b480fd7
add chat window header to chat window component
olipyskoty Feb 12, 2021
4589592
correct formatting
olipyskoty Feb 12, 2021
6a23206
change close chat window button to div to remove hover state
olipyskoty Feb 13, 2021
4f883d9
correct styling for chat header
olipyskoty Feb 13, 2021
a645876
move flip camera button back to MenuBar for time being
olipyskoty Feb 13, 2021
f6f2b5e
remove FlipCameraButton from menubar
olipyskoty Feb 17, 2021
5d2adba
add unit tests for chat window and chat window button
olipyskoty Feb 17, 2021
178d409
remove chatWindowHeaderHeight from theme
olipyskoty Feb 18, 2021
87b8189
delete CloseChat button folder to move code into ChatWindowHeader com…
olipyskoty Feb 18, 2021
e380e46
rename icon to CloseChat icon
olipyskoty Feb 18, 2021
614f563
make ToggleChatButton tests more specific
olipyskoty Feb 18, 2021
4f5617a
fix typo to usechatContext
olipyskoty Feb 18, 2021
b696528
remove snapshot tests from Room tests
olipyskoty Feb 18, 2021
1f844db
move ChatWindowHeader component into ChatWindow component directory
olipyskoty Feb 18, 2021
d1773d7
fix hover styling/css to match other menu buttons
olipyskoty Feb 18, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions src/components/Buttons/ToggleChatButton/ToggleChatButton.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react';
import { shallow } from 'enzyme';
import { Button } from '@material-ui/core';

import ChatIcon from '../../../icons/ChatIcon';
import ToggleChatButton from './ToggleChatButton';
import useChatContext from '../../../hooks/useChatContext/useChatContext';

jest.mock('../../../hooks/useChatContext/useChatContext');
const mockUseChatContext = useChatContext as jest.Mock<any>;

const mockToggleChatWindow = jest.fn();
mockUseChatContext.mockImplementation(() => ({ setIsChatWindowOpen: mockToggleChatWindow, isChatWindowOpen: false }));

describe('the ToggleChatButton component', () => {
it('should render correctly when chat is enabled', () => {
const wrapper = shallow(<ToggleChatButton />);
expect(wrapper.prop('startIcon')).toEqual(<ChatIcon />);
expect(wrapper.text()).toBe('Chat');
});

it('should call the correct toggle function when clicked', () => {
const wrapper = shallow(<ToggleChatButton />);
wrapper.find(Button).simulate('click');
expect(mockToggleChatWindow).toHaveBeenCalledWith(true);
});
});
18 changes: 18 additions & 0 deletions src/components/Buttons/ToggleChatButton/ToggleChatButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react';
import Button from '@material-ui/core/Button';
import ChatIcon from '../../../icons/ChatIcon';
import useChatContext from '../../../hooks/useChatContext/useChatContext';

export default function ToggleVideoButton() {
const { isChatWindowOpen, setIsChatWindowOpen } = useChatContext();

const toggleChatWindow = () => {
setIsChatWindowOpen(!isChatWindowOpen);
};

return (
<Button onClick={toggleChatWindow} startIcon={<ChatIcon />}>
Chat
</Button>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ export const SHARE_NOT_SUPPORTED_TEXT = 'Screen sharing is not supported with th
const useStyles = makeStyles((theme: Theme) =>
createStyles({
button: {
margin: theme.spacing(1),
'&[disabled]': {
color: '#bbb',
'& svg *': {
Expand Down
15 changes: 15 additions & 0 deletions src/components/ChatProvider/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React, { createContext, useState } from 'react';

type ChatContextType = { isChatWindowOpen: boolean; setIsChatWindowOpen: (isChatWindowOpen: boolean) => void };

export const ChatContext = createContext<ChatContextType>(null!);

type ChatProviderProps = {
children: React.ReactNode;
};

export function ChatProvider({ children }: ChatProviderProps) {
const [isChatWindowOpen, setIsChatWindowOpen] = useState(false);

return <ChatContext.Provider value={{ isChatWindowOpen, setIsChatWindowOpen }}>{children}</ChatContext.Provider>;
}
35 changes: 35 additions & 0 deletions src/components/ChatWindow/ChatWindow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React from 'react';
import { makeStyles, createStyles, Theme } from '@material-ui/core/styles';
import ChatWindowHeader from './ChatWindowHeader/ChatWindowHeader';
import useChatContext from '../../hooks/useChatContext/useChatContext';

const useStyles = makeStyles((theme: Theme) =>
createStyles({
container: {
overflowY: 'auto',
background: '#FFFFFF',
zIndex: 100,
[theme.breakpoints.down('sm')]: {
position: 'fixed',
top: 0,
left: 0,
bottom: 0,
right: 0,
},
},
})
);

export default function ChatWindow() {
const classes = useStyles();
const { isChatWindowOpen } = useChatContext();

//if chat window is not open, don't render this component
if (!isChatWindowOpen) return null;

return (
<aside className={classes.container}>
<ChatWindowHeader />
</aside>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react';
import { shallow } from 'enzyme';

import CloseIcon from '../../../icons/CloseIcon';
import ChatWindowHeader from './ChatWindowHeader';
import useChatContext from '../../../hooks/useChatContext/useChatContext';

jest.mock('../../../hooks/useChatContext/useChatContext');

const mockUseChatContext = useChatContext as jest.Mock<any>;

const mockToggleChatWindow = jest.fn();
mockUseChatContext.mockImplementation(() => ({ setIsChatWindowOpen: mockToggleChatWindow }));

describe('the CloseChatWindowHeader component', () => {
it('should close the chat window when "X" is clicked on', () => {
const wrapper = shallow(<ChatWindowHeader />);
wrapper
.find(CloseIcon)
.parent()
.simulate('click');
expect(mockToggleChatWindow).toHaveBeenCalledWith(false);
});
});
40 changes: 40 additions & 0 deletions src/components/ChatWindow/ChatWindowHeader/ChatWindowHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react';
import { makeStyles, createStyles } from '@material-ui/core/styles';
import CloseIcon from '../../../icons/CloseIcon';

import useChatContext from '../../../hooks/useChatContext/useChatContext';

const useStyles = makeStyles(() =>
createStyles({
container: {
height: '56px',
background: '#F4F4F6',
boxShadow: 'inset 0 -0.1em 0 #E4E7E9',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
padding: '0 1em',
},
text: {
fontWeight: 'bold',
},
closeChatWindow: {
cursor: 'pointer',
display: 'flex',
},
})
);

export default function ChatWindowHeader() {
const classes = useStyles();
const { setIsChatWindowOpen } = useChatContext();

return (
<div className={classes.container}>
<div className={classes.text}>Chat</div>
<div className={classes.closeChatWindow} onClick={() => setIsChatWindowOpen(false)}>
<CloseIcon />
</div>
</div>
);
}
5 changes: 2 additions & 3 deletions src/components/MenuBar/MenuBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@ import { createStyles, makeStyles, Theme } from '@material-ui/core/styles';

import Button from '@material-ui/core/Button';
import EndCallButton from '../Buttons/EndCallButton/EndCallButton';
import FlipCameraButton from './FlipCameraButton/FlipCameraButton';
import Menu from './Menu/Menu';

import useRoomState from '../../hooks/useRoomState/useRoomState';
import useVideoContext from '../../hooks/useVideoContext/useVideoContext';
import { Typography, Grid, Hidden } from '@material-ui/core';
import ToggleAudioButton from '../Buttons/ToggleAudioButton/ToggleAudioButton';
import ToggleChatButton from '../Buttons/ToggleChatButton/ToggleChatButton';
import ToggleVideoButton from '../Buttons/ToggleVideoButton/ToggleVideoButton';
import ToggleScreenShareButton from '../Buttons/ToogleScreenShareButton/ToggleScreenShareButton';

Expand Down Expand Up @@ -89,7 +88,7 @@ export default function MenuBar() {
<ToggleAudioButton disabled={isReconnecting} />
<ToggleVideoButton disabled={isReconnecting} />
<Hidden smDown>{!isSharingScreen && <ToggleScreenShareButton disabled={isReconnecting} />}</Hidden>
<FlipCameraButton />
<ToggleChatButton />
</Grid>
</Grid>
<Hidden smDown>
Expand Down
24 changes: 24 additions & 0 deletions src/components/Room/Room.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react';
import { shallow } from 'enzyme';

import Room from './Room';
import useChatContext from '../../hooks/useChatContext/useChatContext';
jest.mock('../../hooks/useChatContext/useChatContext');

const mockUseChatContext = useChatContext as jest.Mock<any>;

const mockToggleChatWindow = jest.fn();
mockUseChatContext.mockImplementation(() => ({ setIsChatWindowOpen: mockToggleChatWindow }));

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like using snapshot tests - but I'm not sure if they are really needed here. I think that snapshot tests are great for testing lots of different things at once, but here we are testing only one thing. So I think that the expect(wrapper.prop('className')).toContain('chatWindowOpen'); test that you already have is sufficient. Just need to add expect(wrapper.prop('className')).not.toContain('chatWindowOpen'); to the first test.

describe('the Room component', () => {
it('should render correctly with chat window closed', () => {
const wrapper = shallow(<Room />);
expect(wrapper.prop('className')).not.toContain('chatWindowOpen');
});

it('should render correctly with chat window open', () => {
mockUseChatContext.mockImplementationOnce(() => ({ isChatWindowOpen: true }));
const wrapper = shallow(<Room />);
expect(wrapper.prop('className')).toContain('chatWindowOpen');
});
});
34 changes: 21 additions & 13 deletions src/components/Room/Room.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,39 @@
import React from 'react';
import clsx from 'clsx';
import { makeStyles, Theme } from '@material-ui/core';
import ChatWindow from '../ChatWindow/ChatWindow';
import ParticipantList from '../ParticipantList/ParticipantList';
import { styled } from '@material-ui/core/styles';
import MainParticipant from '../MainParticipant/MainParticipant';
import useChatContext from '../../hooks/useChatContext/useChatContext';

const Container = styled('div')(({ theme }) => {
const useStyles = makeStyles((theme: Theme) => {
const totalMobileSidebarHeight = `${theme.sidebarMobileHeight +
theme.sidebarMobilePadding * 2 +
theme.participantBorderWidth}px`;

return {
position: 'relative',
height: '100%',
display: 'grid',
gridTemplateColumns: `1fr ${theme.sidebarWidth}px`,
gridTemplateRows: '100%',
[theme.breakpoints.down('sm')]: {
gridTemplateColumns: `100%`,
gridTemplateRows: `calc(100% - ${totalMobileSidebarHeight}) ${totalMobileSidebarHeight}`,
container: {
position: 'relative',
height: '100%',
display: 'grid',
gridTemplateColumns: `1fr ${theme.sidebarWidth}px`,
gridTemplateRows: '100%',
[theme.breakpoints.down('sm')]: {
gridTemplateColumns: `100%`,
gridTemplateRows: `calc(100% - ${totalMobileSidebarHeight}) ${totalMobileSidebarHeight}`,
},
},
chatWindowOpen: { gridTemplateColumns: `1fr ${theme.sidebarWidth}px ${theme.chatWindowWidth}px` },
};
});

export default function Room() {
const classes = useStyles();
const { isChatWindowOpen } = useChatContext();
return (
<Container>
<div className={clsx(classes.container, { [classes.chatWindowOpen]: isChatWindowOpen })}>
<MainParticipant />
<ParticipantList />
</Container>
<ChatWindow />
</div>
);
}
9 changes: 9 additions & 0 deletions src/hooks/useChatContext/useChatContext.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import useChatContext from './useChatContext';
import { renderHook } from '@testing-library/react-hooks';

describe('the useChatContext hook', () => {
it('should throw an error if used outside of the ChatProvider', () => {
const { result } = renderHook(useChatContext);
expect(result.error.message).toBe('useChatContext must be used within a ChatProvider');
});
});
10 changes: 10 additions & 0 deletions src/hooks/useChatContext/useChatContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { useContext } from 'react';
import { ChatContext } from '../../components/ChatProvider';

export default function useChatContext() {
const context = useContext(ChatContext);
if (!context) {
throw new Error('useChatContext must be used within a ChatProvider');
}
return context;
}
14 changes: 14 additions & 0 deletions src/icons/ChatIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';

export default function ChatIcon() {
return (
<svg width="19" height="17" viewBox="0 0 19 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M0 1C0 0.723858 0.223858 0.5 0.5 0.5H18.5C18.7761 0.5 19 0.723858 19 1V13C19 13.2761 18.7761 13.5 18.5 13.5H9.3096L6.32421 16.361C6.1796 16.4996 5.96624 16.5385 5.78202 16.4599C5.59779 16.3813 5.47826 16.2003 5.47826 16V13.5H0.5C0.223858 13.5 0 13.2761 0 13V1ZM1 1.5V12.5H5.97826C6.2544 12.5 6.47826 12.7239 6.47826 13V14.8283L8.76274 12.639C8.85583 12.5498 8.97977 12.5 9.1087 12.5H18V1.5H1Z"
fill="#707578"
/>
</svg>
);
}
20 changes: 20 additions & 0 deletions src/icons/CloseIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';

export default function CloseIcon() {
return (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M2.64645 2.64645C2.84171 2.45118 3.15829 2.45118 3.35355 2.64645L21.3536 20.6464C21.5488 20.8417 21.5488 21.1583 21.3536 21.3536C21.1583 21.5488 20.8417 21.5488 20.6464 21.3536L2.64645 3.35355C2.45118 3.15829 2.45118 2.84171 2.64645 2.64645Z"
fill="#707578"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M21.3536 2.64645C21.5488 2.84171 21.5488 3.15829 21.3536 3.35355L3.35355 21.3536C3.15829 21.5488 2.84171 21.5488 2.64645 21.3536C2.45118 21.1583 2.45118 20.8417 2.64645 20.6464L20.6464 2.64645C20.8417 2.45118 21.1583 2.45118 21.3536 2.64645Z"
fill="#707578"
/>
</svg>
);
}
5 changes: 4 additions & 1 deletion src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import LoginPage from './components/LoginPage/LoginPage';
import PrivateRoute from './components/PrivateRoute/PrivateRoute';
import theme from './theme';
import './types';
import { ChatProvider } from './components/ChatProvider';
import { VideoProvider } from './components/VideoProvider';
import useConnectionOptions from './utils/useConnectionOptions/useConnectionOptions';
import UnsupportedBrowserWarning from './components/UnsupportedBrowserWarning/UnsupportedBrowserWarning';
Expand All @@ -24,7 +25,9 @@ const VideoApp = () => {
<UnsupportedBrowserWarning>
<VideoProvider options={connectionOptions} onError={setError}>
<ErrorDialog dismissError={() => setError(null)} error={error} />
<App />
<ChatProvider>
<App />
</ChatProvider>
</VideoProvider>
</UnsupportedBrowserWarning>
);
Expand Down
3 changes: 3 additions & 0 deletions src/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ declare module '@material-ui/core/styles/createMuiTheme' {
mobileFooterHeight: number;
sidebarMobilePadding: number;
participantBorderWidth: number;
chatWindowWidth: number;
}

// allow configuration using `createMuiTheme`
Expand All @@ -22,6 +23,7 @@ declare module '@material-ui/core/styles/createMuiTheme' {
mobileFooterHeight: number;
sidebarMobilePadding: number;
participantBorderWidth: number;
chatWindowWidth?: number;
}
}

Expand Down Expand Up @@ -121,4 +123,5 @@ export default createMuiTheme({
sidebarMobilePadding: 8,
participantBorderWidth: 2,
mobileTopBarHeight: 52,
chatWindowWidth: 320,
});