CommentForm.tsx

  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;