aboutsummaryrefslogtreecommitdiffstats
path: root/webui/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'webui/src/components')
-rw-r--r--webui/src/components/BackToListButton.tsx4
-rw-r--r--webui/src/components/BugTitleForm/BugTitleForm.tsx12
-rw-r--r--webui/src/components/CommentInput/CommentInput.tsx2
-rw-r--r--webui/src/components/Content/AnchorTag.tsx38
-rw-r--r--webui/src/components/Content/BlockQuoteTag.tsx21
-rw-r--r--webui/src/components/Content/ImageTag.tsx9
-rw-r--r--webui/src/components/Content/index.tsx14
-rw-r--r--webui/src/components/Header/Header.tsx21
-rw-r--r--webui/src/components/Label.tsx49
9 files changed, 116 insertions, 54 deletions
diff --git a/webui/src/components/BackToListButton.tsx b/webui/src/components/BackToListButton.tsx
index 7ca53ad0..41e1d68a 100644
--- a/webui/src/components/BackToListButton.tsx
+++ b/webui/src/components/BackToListButton.tsx
@@ -1,4 +1,5 @@
import React from 'react';
+import { Link } from 'react-router-dom';
import Button from '@material-ui/core/Button';
import { makeStyles } from '@material-ui/core/styles';
@@ -25,7 +26,8 @@ function BackToListButton() {
variant="contained"
className={classes.backButton}
aria-label="back to issue list"
- href="/"
+ component={Link}
+ to="/"
>
<ArrowBackIcon />
Back to List
diff --git a/webui/src/components/BugTitleForm/BugTitleForm.tsx b/webui/src/components/BugTitleForm/BugTitleForm.tsx
index a7d5a820..665ecd4c 100644
--- a/webui/src/components/BugTitleForm/BugTitleForm.tsx
+++ b/webui/src/components/BugTitleForm/BugTitleForm.tsx
@@ -1,4 +1,5 @@
import React, { useState } from 'react';
+import { Link } from 'react-router-dom';
import { Button, makeStyles, Typography } from '@material-ui/core';
@@ -78,6 +79,10 @@ function BugTitleForm({ bug }: Props) {
function submitNewTitle() {
if (!isFormValid()) return;
+ if (bug.title === issueTitleInput.value) {
+ cancelChange();
+ return;
+ }
setTitle({
variables: {
input: {
@@ -106,7 +111,7 @@ function BugTitleForm({ bug }: Props) {
function editableBugTitle() {
return (
- <form className={classes.headerTitle} onSubmit={submitNewTitle}>
+ <form className={classes.headerTitle}>
<BugTitleInput
inputRef={(node) => {
issueTitleInput = node;
@@ -123,7 +128,7 @@ function BugTitleForm({ bug }: Props) {
className={classes.saveButton}
size="small"
variant="contained"
- type="submit"
+ onClick={() => submitNewTitle()}
disabled={issueTitle.length === 0}
>
Save
@@ -157,7 +162,8 @@ function BugTitleForm({ bug }: Props) {
className={classes.greenButton}
size="small"
variant="contained"
- href="/new"
+ component={Link}
+ to="/new"
>
New bug
</Button>
diff --git a/webui/src/components/CommentInput/CommentInput.tsx b/webui/src/components/CommentInput/CommentInput.tsx
index c574538e..f12ee8d8 100644
--- a/webui/src/components/CommentInput/CommentInput.tsx
+++ b/webui/src/components/CommentInput/CommentInput.tsx
@@ -5,7 +5,7 @@ import Tabs from '@material-ui/core/Tabs';
import TextField from '@material-ui/core/TextField';
import { makeStyles } from '@material-ui/core/styles';
-import Content from 'src/components/Content';
+import Content from '../Content';
/**
* Styles
diff --git a/webui/src/components/Content/AnchorTag.tsx b/webui/src/components/Content/AnchorTag.tsx
new file mode 100644
index 00000000..47d5e2fa
--- /dev/null
+++ b/webui/src/components/Content/AnchorTag.tsx
@@ -0,0 +1,38 @@
+import React from 'react';
+import { Link } from 'react-router-dom';
+
+import { makeStyles } from '@material-ui/core/styles';
+
+const useStyles = makeStyles((theme) => ({
+ tag: {
+ color: theme.palette.text.secondary,
+ },
+}));
+
+const AnchorTag = ({ children, href }: React.HTMLProps<HTMLAnchorElement>) => {
+ const classes = useStyles();
+ const origin = window.location.origin;
+ const destination = href === undefined ? '' : href;
+ const isInternalLink =
+ destination.startsWith('/') || destination.startsWith(origin);
+ const internalDestination = destination.replace(origin, '');
+ const internalLink = (
+ <Link className={classes.tag} to={internalDestination}>
+ {children}
+ </Link>
+ );
+ const externalLink = (
+ <a
+ className={classes.tag}
+ href={destination}
+ target="_blank"
+ rel="noopener noreferrer"
+ >
+ {children}
+ </a>
+ );
+
+ return isInternalLink ? internalLink : externalLink;
+};
+
+export default AnchorTag;
diff --git a/webui/src/components/Content/BlockQuoteTag.tsx b/webui/src/components/Content/BlockQuoteTag.tsx
new file mode 100644
index 00000000..18c34d8a
--- /dev/null
+++ b/webui/src/components/Content/BlockQuoteTag.tsx
@@ -0,0 +1,21 @@
+import React from 'react';
+
+import { makeStyles } from '@material-ui/core/styles';
+
+const useStyles = makeStyles((theme) => ({
+ tag: {
+ color: theme.palette.text.secondary,
+ borderLeftWidth: '0.5ch',
+ borderLeftStyle: 'solid',
+ borderLeftColor: theme.palette.text.secondary,
+ marginLeft: 0,
+ paddingLeft: '0.5rem',
+ },
+}));
+
+const BlockQuoteTag = (props: React.HTMLProps<HTMLPreElement>) => {
+ const classes = useStyles();
+ return <blockquote className={classes.tag} {...props} />;
+};
+
+export default BlockQuoteTag;
diff --git a/webui/src/components/Content/ImageTag.tsx b/webui/src/components/Content/ImageTag.tsx
index 70ee1bc0..29b01da3 100644
--- a/webui/src/components/Content/ImageTag.tsx
+++ b/webui/src/components/Content/ImageTag.tsx
@@ -14,9 +14,12 @@ const ImageTag = ({
}: React.ImgHTMLAttributes<HTMLImageElement>) => {
const classes = useStyles();
return (
- <a href={props.src} target="_blank" rel="noopener noreferrer nofollow">
- <img className={classes.tag} alt={alt} {...props} />
- </a>
+ <>
+ <a href={props.src} target="_blank" rel="noopener noreferrer nofollow">
+ <img className={classes.tag} alt={alt} {...props} />
+ </a>
+ <br />
+ </>
);
};
diff --git a/webui/src/components/Content/index.tsx b/webui/src/components/Content/index.tsx
index 56e52e1e..e4018809 100644
--- a/webui/src/components/Content/index.tsx
+++ b/webui/src/components/Content/index.tsx
@@ -1,26 +1,32 @@
import React from 'react';
+import gemoji from 'remark-gemoji';
import html from 'remark-html';
import parse from 'remark-parse';
import remark2react from 'remark-react';
import unified from 'unified';
+import AnchorTag from './AnchorTag';
+import BlockQuoteTag from './BlockQuoteTag';
import ImageTag from './ImageTag';
import PreTag from './PreTag';
type Props = { markdown: string };
const Content: React.FC<Props> = ({ markdown }: Props) => {
- const processor = unified()
+ const content = unified()
.use(parse)
+ .use(gemoji)
.use(html)
.use(remark2react, {
remarkReactComponents: {
img: ImageTag,
pre: PreTag,
+ a: AnchorTag,
+ blockquote: BlockQuoteTag,
},
- });
+ })
+ .processSync(markdown).result;
- const contents: React.ReactNode = processor.processSync(markdown).contents;
- return <>{contents}</>;
+ return <>{content}</>;
};
export default Content;
diff --git a/webui/src/components/Header/Header.tsx b/webui/src/components/Header/Header.tsx
index 3064f6e4..63146cc9 100644
--- a/webui/src/components/Header/Header.tsx
+++ b/webui/src/components/Header/Header.tsx
@@ -67,14 +67,14 @@ const DisabledTabWithTooltip = (props: TabProps) => {
function Header() {
const classes = useStyles();
const location = useLocation();
- const [selectedTab, setTab] = React.useState(location.pathname);
- const handleTabClick = (
- event: React.ChangeEvent<{}>,
- newTabValue: string
- ) => {
- setTab(newTabValue);
- };
+ // Prevents error of invalid tab selection in <Tabs>
+ // Will return a valid tab path or false if path is unkown.
+ function highlightTab() {
+ const validTabs = ['/', '/code', '/pulls', '/settings'];
+ const tab = validTabs.find((tabPath) => tabPath === location.pathname);
+ return tab === undefined ? false : tab;
+ }
return (
<>
@@ -92,12 +92,7 @@ function Header() {
</Toolbar>
</AppBar>
<div className={classes.offset} />
- <Tabs
- centered
- value={selectedTab}
- onChange={handleTabClick}
- aria-label="nav tabs"
- >
+ <Tabs centered value={highlightTab()} aria-label="nav tabs">
<DisabledTabWithTooltip label="Code" value="/code" {...a11yProps(1)} />
<Tab label="Bugs" value="/" component={Link} to="/" {...a11yProps(2)} />
<DisabledTabWithTooltip
diff --git a/webui/src/components/Label.tsx b/webui/src/components/Label.tsx
index 111f6d7f..a1d3c6f9 100644
--- a/webui/src/components/Label.tsx
+++ b/webui/src/components/Label.tsx
@@ -1,56 +1,47 @@
import React from 'react';
+import { Chip } from '@material-ui/core';
import { common } from '@material-ui/core/colors';
-import { makeStyles } from '@material-ui/core/styles';
import {
- getContrastRatio,
darken,
+ getContrastRatio,
} from '@material-ui/core/styles/colorManipulator';
+import { Color } from '../gqlTypes';
import { LabelFragment } from '../graphql/fragments.generated';
-import { Color } from 'src/gqlTypes';
+
+const _rgb = (color: Color) =>
+ 'rgb(' + color.R + ',' + color.G + ',' + color.B + ')';
// Minimum contrast between the background and the text color
const contrastThreshold = 2.5;
-
// Guess the text color based on the background color
const getTextColor = (background: string) =>
getContrastRatio(background, common.white) >= contrastThreshold
? common.white // White on dark backgrounds
: common.black; // And black on light ones
-const _rgb = (color: Color) =>
- 'rgb(' + color.R + ',' + color.G + ',' + color.B + ')';
-
// Create a style object from the label RGB colors
-const createStyle = (color: Color) => ({
+const createStyle = (color: Color, maxWidth?: string) => ({
backgroundColor: _rgb(color),
color: getTextColor(_rgb(color)),
borderBottomColor: darken(_rgb(color), 0.2),
+ maxWidth: maxWidth,
});
-const useStyles = makeStyles((theme) => ({
- label: {
- ...theme.typography.body1,
- padding: '1px 6px 0.5px',
- fontSize: '0.9em',
- fontWeight: 500,
- margin: '0.05em 1px calc(-1.5px + 0.05em)',
- borderRadius: '3px',
- display: 'inline-block',
- borderBottom: 'solid 1.5px',
- verticalAlign: 'bottom',
- },
-}));
-
-type Props = { label: LabelFragment };
-function Label({ label }: Props) {
- const classes = useStyles();
+type Props = {
+ label: LabelFragment;
+ maxWidth?: string;
+ className?: string;
+};
+function Label({ label, maxWidth, className }: Props) {
return (
- <span className={classes.label} style={createStyle(label.color)}>
- {label.name}
- </span>
+ <Chip
+ size={'small'}
+ label={label.name}
+ className={className}
+ style={createStyle(label.color, maxWidth)}
+ />
);
}
-
export default Label;