1import Button from '@material-ui/core/Button';
2import Paper from '@material-ui/core/Paper';
3import Tab from '@material-ui/core/Tab';
4import Tabs from '@material-ui/core/Tabs';
5import TextField from '@material-ui/core/TextField';
6import { makeStyles, Theme } from '@material-ui/core/styles';
7import React, { useState, useRef } from 'react';
8
9import Content from '../Content';
10
11import { useAddCommentMutation } from './CommentForm.generated';
12import { TimelineDocument } from './TimelineQuery.generated';
13
14type StyleProps = { loading: boolean };
15const useStyles = makeStyles<Theme, StyleProps>(theme => ({
16 container: {
17 margin: theme.spacing(2, 0),
18 padding: theme.spacing(0, 2, 2, 2),
19 },
20 textarea: {},
21 tabContent: {
22 margin: theme.spacing(2, 0),
23 },
24 preview: {
25 borderBottom: `solid 3px ${theme.palette.grey['200']}`,
26 minHeight: '5rem',
27 },
28 actions: {
29 display: 'flex',
30 justifyContent: 'flex-end',
31 },
32}));
33
34type TabPanelProps = {
35 children: React.ReactNode;
36 value: number;
37 index: number;
38} & React.HTMLProps<HTMLDivElement>;
39function TabPanel({ children, value, index, ...props }: TabPanelProps) {
40 return (
41 <div
42 role="tabpanel"
43 hidden={value !== index}
44 id={`editor-tabpanel-${index}`}
45 aria-labelledby={`editor-tab-${index}`}
46 {...props}
47 >
48 {value === index && children}
49 </div>
50 );
51}
52
53const a11yProps = (index: number) => ({
54 id: `editor-tab-${index}`,
55 'aria-controls': `editor-tabpanel-${index}`,
56});
57
58type Props = {
59 bugId: string;
60};
61
62function CommentForm({ bugId }: Props) {
63 const [addComment, { loading }] = useAddCommentMutation();
64 const [input, setInput] = useState<string>('');
65 const [tab, setTab] = useState(0);
66 const classes = useStyles({ loading });
67 const form = useRef<HTMLFormElement>(null);
68
69 const submit = () => {
70 addComment({
71 variables: {
72 input: {
73 prefix: bugId,
74 message: input,
75 },
76 },
77 refetchQueries: [
78 // TODO: update the cache instead of refetching
79 {
80 query: TimelineDocument,
81 variables: {
82 id: bugId,
83 first: 100,
84 },
85 },
86 ],
87 awaitRefetchQueries: true,
88 }).then(() => setInput(''));
89 };
90
91 const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
92 e.preventDefault();
93 submit();
94 };
95
96 const handleKeyDown = (e: React.KeyboardEvent<HTMLElement>) => {
97 // Submit on cmd/ctrl+enter
98 if ((e.metaKey || e.altKey) && e.keyCode === 13) {
99 submit();
100 }
101 };
102
103 return (
104 <Paper className={classes.container}>
105 <form onSubmit={handleSubmit} ref={form}>
106 <Tabs value={tab} onChange={(_, t) => setTab(t)}>
107 <Tab label="Write" {...a11yProps(0)} />
108 <Tab label="Preview" {...a11yProps(1)} />
109 </Tabs>
110 <div className={classes.tabContent}>
111 <TabPanel value={tab} index={0}>
112 <TextField
113 onKeyDown={handleKeyDown}
114 fullWidth
115 label="Comment"
116 placeholder="Leave a comment"
117 className={classes.textarea}
118 multiline
119 value={input}
120 variant="filled"
121 rows="4" // TODO: rowsMin support
122 onChange={(e: any) => setInput(e.target.value)}
123 disabled={loading}
124 />
125 </TabPanel>
126 <TabPanel value={tab} index={1} className={classes.preview}>
127 <Content markdown={input} />
128 </TabPanel>
129 </div>
130 <div className={classes.actions}>
131 <Button
132 variant="contained"
133 color="primary"
134 type="submit"
135 disabled={loading}
136 >
137 Comment
138 </Button>
139 </div>
140 </form>
141 </Paper>
142 );
143}
144
145export default CommentForm;