Skip to content

Commit aa2c8c1

Browse files
authored
VIDEO 3731 Conversations UI - create chat window (#421)
1 parent 9cd4c9f commit aa2c8c1

File tree

16 files changed

+266
-18
lines changed

16 files changed

+266
-18
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import React from 'react';
2+
import { shallow } from 'enzyme';
3+
import { Button } from '@material-ui/core';
4+
5+
import ChatIcon from '../../../icons/ChatIcon';
6+
import ToggleChatButton from './ToggleChatButton';
7+
import useChatContext from '../../../hooks/useChatContext/useChatContext';
8+
9+
jest.mock('../../../hooks/useChatContext/useChatContext');
10+
const mockUseChatContext = useChatContext as jest.Mock<any>;
11+
12+
const mockToggleChatWindow = jest.fn();
13+
mockUseChatContext.mockImplementation(() => ({ setIsChatWindowOpen: mockToggleChatWindow, isChatWindowOpen: false }));
14+
15+
describe('the ToggleChatButton component', () => {
16+
it('should render correctly when chat is enabled', () => {
17+
const wrapper = shallow(<ToggleChatButton />);
18+
expect(wrapper.prop('startIcon')).toEqual(<ChatIcon />);
19+
expect(wrapper.text()).toBe('Chat');
20+
});
21+
22+
it('should call the correct toggle function when clicked', () => {
23+
const wrapper = shallow(<ToggleChatButton />);
24+
wrapper.find(Button).simulate('click');
25+
expect(mockToggleChatWindow).toHaveBeenCalledWith(true);
26+
});
27+
});
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import React from 'react';
2+
import Button from '@material-ui/core/Button';
3+
import ChatIcon from '../../../icons/ChatIcon';
4+
import useChatContext from '../../../hooks/useChatContext/useChatContext';
5+
6+
export default function ToggleVideoButton() {
7+
const { isChatWindowOpen, setIsChatWindowOpen } = useChatContext();
8+
9+
const toggleChatWindow = () => {
10+
setIsChatWindowOpen(!isChatWindowOpen);
11+
};
12+
13+
return (
14+
<Button onClick={toggleChatWindow} startIcon={<ChatIcon />}>
15+
Chat
16+
</Button>
17+
);
18+
}

src/components/Buttons/ToogleScreenShareButton/ToggleScreenShareButton.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ export const SHARE_NOT_SUPPORTED_TEXT = 'Screen sharing is not supported with th
1616
const useStyles = makeStyles((theme: Theme) =>
1717
createStyles({
1818
button: {
19-
margin: theme.spacing(1),
2019
'&[disabled]': {
2120
color: '#bbb',
2221
'& svg *': {

src/components/ChatProvider/index.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import React, { createContext, useState } from 'react';
2+
3+
type ChatContextType = { isChatWindowOpen: boolean; setIsChatWindowOpen: (isChatWindowOpen: boolean) => void };
4+
5+
export const ChatContext = createContext<ChatContextType>(null!);
6+
7+
type ChatProviderProps = {
8+
children: React.ReactNode;
9+
};
10+
11+
export function ChatProvider({ children }: ChatProviderProps) {
12+
const [isChatWindowOpen, setIsChatWindowOpen] = useState(false);
13+
14+
return <ChatContext.Provider value={{ isChatWindowOpen, setIsChatWindowOpen }}>{children}</ChatContext.Provider>;
15+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import React from 'react';
2+
import { makeStyles, createStyles, Theme } from '@material-ui/core/styles';
3+
import ChatWindowHeader from './ChatWindowHeader/ChatWindowHeader';
4+
import useChatContext from '../../hooks/useChatContext/useChatContext';
5+
6+
const useStyles = makeStyles((theme: Theme) =>
7+
createStyles({
8+
container: {
9+
overflowY: 'auto',
10+
background: '#FFFFFF',
11+
zIndex: 100,
12+
[theme.breakpoints.down('sm')]: {
13+
position: 'fixed',
14+
top: 0,
15+
left: 0,
16+
bottom: 0,
17+
right: 0,
18+
},
19+
},
20+
})
21+
);
22+
23+
export default function ChatWindow() {
24+
const classes = useStyles();
25+
const { isChatWindowOpen } = useChatContext();
26+
27+
//if chat window is not open, don't render this component
28+
if (!isChatWindowOpen) return null;
29+
30+
return (
31+
<aside className={classes.container}>
32+
<ChatWindowHeader />
33+
</aside>
34+
);
35+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React from 'react';
2+
import { shallow } from 'enzyme';
3+
4+
import CloseIcon from '../../../icons/CloseIcon';
5+
import ChatWindowHeader from './ChatWindowHeader';
6+
import useChatContext from '../../../hooks/useChatContext/useChatContext';
7+
8+
jest.mock('../../../hooks/useChatContext/useChatContext');
9+
10+
const mockUseChatContext = useChatContext as jest.Mock<any>;
11+
12+
const mockToggleChatWindow = jest.fn();
13+
mockUseChatContext.mockImplementation(() => ({ setIsChatWindowOpen: mockToggleChatWindow }));
14+
15+
describe('the CloseChatWindowHeader component', () => {
16+
it('should close the chat window when "X" is clicked on', () => {
17+
const wrapper = shallow(<ChatWindowHeader />);
18+
wrapper
19+
.find(CloseIcon)
20+
.parent()
21+
.simulate('click');
22+
expect(mockToggleChatWindow).toHaveBeenCalledWith(false);
23+
});
24+
});
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import React from 'react';
2+
import { makeStyles, createStyles } from '@material-ui/core/styles';
3+
import CloseIcon from '../../../icons/CloseIcon';
4+
5+
import useChatContext from '../../../hooks/useChatContext/useChatContext';
6+
7+
const useStyles = makeStyles(() =>
8+
createStyles({
9+
container: {
10+
height: '56px',
11+
background: '#F4F4F6',
12+
boxShadow: 'inset 0 -0.1em 0 #E4E7E9',
13+
display: 'flex',
14+
justifyContent: 'space-between',
15+
alignItems: 'center',
16+
padding: '0 1em',
17+
},
18+
text: {
19+
fontWeight: 'bold',
20+
},
21+
closeChatWindow: {
22+
cursor: 'pointer',
23+
display: 'flex',
24+
},
25+
})
26+
);
27+
28+
export default function ChatWindowHeader() {
29+
const classes = useStyles();
30+
const { setIsChatWindowOpen } = useChatContext();
31+
32+
return (
33+
<div className={classes.container}>
34+
<div className={classes.text}>Chat</div>
35+
<div className={classes.closeChatWindow} onClick={() => setIsChatWindowOpen(false)}>
36+
<CloseIcon />
37+
</div>
38+
</div>
39+
);
40+
}

src/components/MenuBar/MenuBar.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,12 @@ import { createStyles, makeStyles, Theme } from '@material-ui/core/styles';
33

44
import Button from '@material-ui/core/Button';
55
import EndCallButton from '../Buttons/EndCallButton/EndCallButton';
6-
import FlipCameraButton from './FlipCameraButton/FlipCameraButton';
76
import Menu from './Menu/Menu';
8-
97
import useRoomState from '../../hooks/useRoomState/useRoomState';
108
import useVideoContext from '../../hooks/useVideoContext/useVideoContext';
119
import { Typography, Grid, Hidden } from '@material-ui/core';
1210
import ToggleAudioButton from '../Buttons/ToggleAudioButton/ToggleAudioButton';
11+
import ToggleChatButton from '../Buttons/ToggleChatButton/ToggleChatButton';
1312
import ToggleVideoButton from '../Buttons/ToggleVideoButton/ToggleVideoButton';
1413
import ToggleScreenShareButton from '../Buttons/ToogleScreenShareButton/ToggleScreenShareButton';
1514

@@ -89,7 +88,7 @@ export default function MenuBar() {
8988
<ToggleAudioButton disabled={isReconnecting} />
9089
<ToggleVideoButton disabled={isReconnecting} />
9190
<Hidden smDown>{!isSharingScreen && <ToggleScreenShareButton disabled={isReconnecting} />}</Hidden>
92-
<FlipCameraButton />
91+
<ToggleChatButton />
9392
</Grid>
9493
</Grid>
9594
<Hidden smDown>

src/components/Room/Room.test.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React from 'react';
2+
import { shallow } from 'enzyme';
3+
4+
import Room from './Room';
5+
import useChatContext from '../../hooks/useChatContext/useChatContext';
6+
jest.mock('../../hooks/useChatContext/useChatContext');
7+
8+
const mockUseChatContext = useChatContext as jest.Mock<any>;
9+
10+
const mockToggleChatWindow = jest.fn();
11+
mockUseChatContext.mockImplementation(() => ({ setIsChatWindowOpen: mockToggleChatWindow }));
12+
13+
describe('the Room component', () => {
14+
it('should render correctly with chat window closed', () => {
15+
const wrapper = shallow(<Room />);
16+
expect(wrapper.prop('className')).not.toContain('chatWindowOpen');
17+
});
18+
19+
it('should render correctly with chat window open', () => {
20+
mockUseChatContext.mockImplementationOnce(() => ({ isChatWindowOpen: true }));
21+
const wrapper = shallow(<Room />);
22+
expect(wrapper.prop('className')).toContain('chatWindowOpen');
23+
});
24+
});

src/components/Room/Room.tsx

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,39 @@
11
import React from 'react';
2+
import clsx from 'clsx';
3+
import { makeStyles, Theme } from '@material-ui/core';
4+
import ChatWindow from '../ChatWindow/ChatWindow';
25
import ParticipantList from '../ParticipantList/ParticipantList';
3-
import { styled } from '@material-ui/core/styles';
46
import MainParticipant from '../MainParticipant/MainParticipant';
7+
import useChatContext from '../../hooks/useChatContext/useChatContext';
58

6-
const Container = styled('div')(({ theme }) => {
9+
const useStyles = makeStyles((theme: Theme) => {
710
const totalMobileSidebarHeight = `${theme.sidebarMobileHeight +
811
theme.sidebarMobilePadding * 2 +
912
theme.participantBorderWidth}px`;
10-
1113
return {
12-
position: 'relative',
13-
height: '100%',
14-
display: 'grid',
15-
gridTemplateColumns: `1fr ${theme.sidebarWidth}px`,
16-
gridTemplateRows: '100%',
17-
[theme.breakpoints.down('sm')]: {
18-
gridTemplateColumns: `100%`,
19-
gridTemplateRows: `calc(100% - ${totalMobileSidebarHeight}) ${totalMobileSidebarHeight}`,
14+
container: {
15+
position: 'relative',
16+
height: '100%',
17+
display: 'grid',
18+
gridTemplateColumns: `1fr ${theme.sidebarWidth}px`,
19+
gridTemplateRows: '100%',
20+
[theme.breakpoints.down('sm')]: {
21+
gridTemplateColumns: `100%`,
22+
gridTemplateRows: `calc(100% - ${totalMobileSidebarHeight}) ${totalMobileSidebarHeight}`,
23+
},
2024
},
25+
chatWindowOpen: { gridTemplateColumns: `1fr ${theme.sidebarWidth}px ${theme.chatWindowWidth}px` },
2126
};
2227
});
2328

2429
export default function Room() {
30+
const classes = useStyles();
31+
const { isChatWindowOpen } = useChatContext();
2532
return (
26-
<Container>
33+
<div className={clsx(classes.container, { [classes.chatWindowOpen]: isChatWindowOpen })}>
2734
<MainParticipant />
2835
<ParticipantList />
29-
</Container>
36+
<ChatWindow />
37+
</div>
3038
);
3139
}

0 commit comments

Comments
 (0)