diff options
Diffstat (limited to 'webui/src/pages')
-rw-r--r-- | webui/src/pages/bug/CommentForm.tsx | 37 | ||||
-rw-r--r-- | webui/src/pages/bug/Message.tsx | 7 | ||||
-rw-r--r-- | webui/src/pages/bug/MessageHistoryDialog.tsx | 7 | ||||
-rw-r--r-- | webui/src/pages/list/BugRow.tsx | 2 | ||||
-rw-r--r-- | webui/src/pages/list/Filter.tsx | 44 | ||||
-rw-r--r-- | webui/src/pages/list/FilterToolbar.tsx | 13 |
6 files changed, 74 insertions, 36 deletions
diff --git a/webui/src/pages/bug/CommentForm.tsx b/webui/src/pages/bug/CommentForm.tsx index a8ce4319..6d917889 100644 --- a/webui/src/pages/bug/CommentForm.tsx +++ b/webui/src/pages/bug/CommentForm.tsx @@ -5,8 +5,10 @@ import Paper from '@material-ui/core/Paper'; import { makeStyles, Theme } from '@material-ui/core/styles'; import CommentInput from '../../components/CommentInput/CommentInput'; -import CloseBugButton from 'src/components/CloseBugButton/CloseBugButton'; -import ReopenBugButton from 'src/components/ReopenBugButton/ReopenBugButton'; +import CloseBugButton from 'src/components/CloseBugButton'; +import CloseBugWithCommentButton from 'src/components/CloseBugWithCommentButton'; +import ReopenBugButton from 'src/components/ReopenBugButton'; +import ReopenBugWithCommentButton from 'src/components/ReopenBugWithCommentButton'; import { BugFragment } from './Bug.generated'; import { useAddCommentMutation } from './CommentForm.generated'; @@ -77,12 +79,29 @@ function CommentForm({ bug }: Props) { if (issueComment.length > 0) submit(); }; - function getCloseButton() { - return <CloseBugButton bug={bug} disabled={issueComment.length > 0} />; - } - - function getReopenButton() { - return <ReopenBugButton bug={bug} disabled={issueComment.length > 0} />; + function getBugStatusButton() { + if (bug.status === 'OPEN' && issueComment.length > 0) { + return ( + <CloseBugWithCommentButton + bug={bug} + comment={issueComment} + postClick={resetForm} + /> + ); + } + if (bug.status === 'OPEN') { + return <CloseBugButton bug={bug} />; + } + if (bug.status === 'CLOSED' && issueComment.length > 0) { + return ( + <ReopenBugWithCommentButton + bug={bug} + comment={issueComment} + postClick={resetForm} + /> + ); + } + return <ReopenBugButton bug={bug} />; } return ( @@ -94,7 +113,7 @@ function CommentForm({ bug }: Props) { onChange={(comment: string) => setIssueComment(comment)} /> <div className={classes.actions}> - {bug.status === 'OPEN' ? getCloseButton() : getReopenButton()} + {getBugStatusButton()} <Button className={classes.greenButton} variant="contained" diff --git a/webui/src/pages/bug/Message.tsx b/webui/src/pages/bug/Message.tsx index 808bb525..51087faa 100644 --- a/webui/src/pages/bug/Message.tsx +++ b/webui/src/pages/bug/Message.tsx @@ -57,6 +57,7 @@ const useStyles = makeStyles((theme) => ({ marginLeft: '0.5rem', }, body: { + overflow: 'auto', ...theme.typography.body2, paddingLeft: theme.spacing(1), paddingRight: theme.spacing(1), @@ -156,7 +157,11 @@ function Message({ bug, op }: Props) { </IfLoggedIn> </header> <section className={classes.body}> - <Content markdown={comment.message} /> + {comment.message !== '' ? ( + <Content markdown={comment.message} /> + ) : ( + <Content markdown="*No description provided.*" /> + )} </section> </Paper> ); diff --git a/webui/src/pages/bug/MessageHistoryDialog.tsx b/webui/src/pages/bug/MessageHistoryDialog.tsx index 5879a373..df8915d9 100644 --- a/webui/src/pages/bug/MessageHistoryDialog.tsx +++ b/webui/src/pages/bug/MessageHistoryDialog.tsx @@ -111,6 +111,7 @@ const AccordionSummary = withStyles((theme) => ({ const AccordionDetails = withStyles((theme) => ({ root: { display: 'block', + overflow: 'auto', padding: theme.spacing(2), }, }))(MuiAccordionDetails); @@ -229,7 +230,11 @@ function MessageHistoryDialog({ bugId, commentId, open, onClose }: Props) { <Typography>{getSummary(index, edit.date)}</Typography> </AccordionSummary> <AccordionDetails> - <Content markdown={edit.message} /> + {edit.message !== '' ? ( + <Content markdown={edit.message} /> + ) : ( + <Content markdown="*No description provided.*" /> + )} </AccordionDetails> </Accordion> ))} diff --git a/webui/src/pages/list/BugRow.tsx b/webui/src/pages/list/BugRow.tsx index 562149f3..68a3b299 100644 --- a/webui/src/pages/list/BugRow.tsx +++ b/webui/src/pages/list/BugRow.tsx @@ -84,7 +84,9 @@ const useStyles = makeStyles((theme) => ({ }, commentCount: { fontSize: '1rem', + minWidth: '2rem', marginLeft: theme.spacing(0.5), + marginRight: theme.spacing(1), }, commentCountCell: { display: 'inline-flex', diff --git a/webui/src/pages/list/Filter.tsx b/webui/src/pages/list/Filter.tsx index 3559b3ce..496fb3ba 100644 --- a/webui/src/pages/list/Filter.tsx +++ b/webui/src/pages/list/Filter.tsx @@ -35,52 +35,50 @@ const ITEM_HEIGHT = 48; export type Query = { [key: string]: string[] }; function parse(query: string): Query { - // TODO: extract the rest of the query? const params: Query = {}; - - // TODO: support escaping without quotes - const re = /(\w+):([A-Za-z0-9-]+|(["'])(([^\3]|\\.)*)\3)+/g; + let re = new RegExp(/([^:\s]+)(:('[^']*'\S*|"[^"]*"\S*|\S*))?/, 'g'); let matches; while ((matches = re.exec(query)) !== null) { if (!params[matches[1]]) { params[matches[1]] = []; } - - let value; - if (matches[4]) { - value = matches[4]; + if (matches[3] !== undefined) { + params[matches[1]].push(matches[3]); } else { - value = matches[2]; + params[matches[1]].push(''); } - value = value.replace(/\\(.)/g, '$1'); - params[matches[1]].push(value); } return params; } function quote(value: string): string { - const hasSingle = value.includes("'"); - const hasDouble = value.includes('"'); const hasSpaces = value.includes(' '); - if (!hasSingle && !hasDouble && !hasSpaces) { - return value; - } + const isSingleQuotedRegEx = RegExp(/^'.*'$/); + const isDoubleQuotedRegEx = RegExp(/^".*"$/); + const isQuoted = () => + isDoubleQuotedRegEx.test(value) || isSingleQuotedRegEx.test(value); - if (!hasDouble) { - return `"${value}"`; + //Test if label name contains whitespace between quotes. If no quoates but + //whitespace, then quote string. + if (!isQuoted() && hasSpaces) { + value = `"${value}"`; } - if (!hasSingle) { - return `'${value}'`; + //Convert single quote (tick) to double quote. This way quoting is always + //uniform and can be relied upon by the label menu + const hasSingle = value.includes(`'`); + if (hasSingle) { + value = value.replace(/'/g, `"`); } - value = value.replace(/"/g, '\\"'); - return `"${value}"`; + return value; } function stringify(params: Query): string { const parts: string[][] = Object.entries(params).map(([key, values]) => { - return values.map((value) => `${key}:${quote(value)}`); + return values.map((value) => + value.length > 0 ? `${key}:${quote(value)}` : key + ); }); return new Array<string>().concat(...parts).join(' '); } diff --git a/webui/src/pages/list/FilterToolbar.tsx b/webui/src/pages/list/FilterToolbar.tsx index e109578d..4ac579f5 100644 --- a/webui/src/pages/list/FilterToolbar.tsx +++ b/webui/src/pages/list/FilterToolbar.tsx @@ -56,6 +56,16 @@ function CountingFilter({ query, children, ...props }: CountingFilterProps) { ); } +function quoteLabel(value: string) { + const hasUnquotedColon = RegExp(/^[^'"].*:.*[^'"]$/); + if (hasUnquotedColon.test(value)) { + //quote values which contain a colon but are not quoted. + //E.g. abc:abc becomes "abc:abc" + return `"${value}"`; + } + return value; +} + type Props = { query: string; queryLocation: (query: string) => LocationDescriptor; @@ -87,7 +97,7 @@ function FilterToolbar({ query, queryLocation }: Props) { labelsData.repository.validLabels.nodes ) { labels = labelsData.repository.validLabels.nodes.map((node) => [ - node.name, + quoteLabel(node.name), node.name, node.color, ]); @@ -131,7 +141,6 @@ function FilterToolbar({ query, queryLocation }: Props) { [key]: [], }); - // TODO: author/label filters return ( <Toolbar className={classes.toolbar}> <CountingFilter |