Merge pull request #624 from GlancingMind/upstream-fix-and-improve-rendering-of-markdown-elements

WebUI: Fix and improve rendering of markdown elements
This commit is contained in:
Michael Muré 2021-04-12 19:26:29 +02:00 committed by GitHub
commit 62fb09a53c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 115 additions and 21 deletions

View File

@ -12325,6 +12325,11 @@
"resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
"integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc="
},
"gemoji": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/gemoji/-/gemoji-6.1.0.tgz",
"integrity": "sha512-MOlX3doQ1fsfzxQX8Y+u6bC5Ssc1pBUBIPVyrS69EzKt+5LIZAOm0G5XGVNhwXFgkBF3r+Yk88ONyrFHo8iNFA=="
},
"generic-names": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/generic-names/-/generic-names-2.0.1.tgz",
@ -12572,7 +12577,8 @@
"growly": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
"integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE="
"integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=",
"optional": true
},
"gzip-size": {
"version": "5.1.1",
@ -16118,6 +16124,7 @@
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.1.tgz",
"integrity": "sha512-BvEXF+UmsnAfYfoapKM9nGxnP+Wn7P91YfXmrKnfcYCx6VBeoN5Ez5Ogck6I8Bi5k4RlpqRYaw75pAwzX9OphA==",
"optional": true,
"requires": {
"growly": "^1.3.0",
"is-wsl": "^2.2.0",
@ -16131,6 +16138,7 @@
"version": "7.3.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz",
"integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==",
"optional": true,
"requires": {
"lru-cache": "^6.0.0"
}
@ -16138,12 +16146,14 @@
"uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"optional": true
},
"which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"optional": true,
"requires": {
"isexe": "^2.0.0"
}
@ -19227,6 +19237,15 @@
"fbjs": "^1.0.0"
}
},
"remark-gemoji": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/remark-gemoji/-/remark-gemoji-6.0.0.tgz",
"integrity": "sha512-LDW2h6QqNzAbAcOjscgfkJW9/8TGBasBe/ji+3mCxHlJdhF2IEXFSmm/3tdEPP1JJDZ4y+Ea+xlFQ4tOIU9WvA==",
"requires": {
"gemoji": "^6.0.0",
"unist-util-visit": "^2.0.0"
}
},
"remark-html": {
"version": "12.0.0",
"resolved": "https://registry.npmjs.org/remark-html/-/remark-html-12.0.0.tgz",
@ -20219,7 +20238,8 @@
"shellwords": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz",
"integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww=="
"integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==",
"optional": true
},
"side-channel": {
"version": "1.0.3",

View File

@ -22,6 +22,7 @@
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
"react-scripts": "^4.0.0-next.98",
"remark-gemoji": "^6.0.0",
"remark-html": "^12.0.0",
"remark-parse": "^8.0.3",
"remark-react": "^7.0.1",

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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 />
</>
);
};

View File

@ -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;

View File

@ -17,14 +17,6 @@ const useStyles = makeStyles<Theme, StyleProps>((theme) => ({
container: {
padding: theme.spacing(0, 2, 2, 2),
},
textarea: {},
tabContent: {
margin: theme.spacing(2, 0),
},
preview: {
borderBottom: `solid 3px ${theme.palette.grey['200']}`,
minHeight: '5rem',
},
actions: {
display: 'flex',
gap: '1em',

View File

@ -57,7 +57,8 @@ const useStyles = makeStyles((theme) => ({
},
body: {
...theme.typography.body2,
padding: '0.5rem',
paddingLeft: theme.spacing(1),
paddingRight: theme.spacing(1),
},
headerActions: {
color: theme.palette.info.contrastText,

View File

@ -22,6 +22,8 @@ import {
import CloseIcon from '@material-ui/icons/Close';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import Content from '../../components/Content';
import { AddCommentFragment } from './MessageCommentFragment.generated';
import { CreateFragment } from './MessageCreateFragment.generated';
import { useMessageHistoryQuery } from './MessageHistory.generated';
@ -108,6 +110,7 @@ const AccordionSummary = withStyles((theme) => ({
const AccordionDetails = withStyles((theme) => ({
root: {
display: 'block',
padding: theme.spacing(2),
},
}))(MuiAccordionDetails);
@ -214,6 +217,7 @@ function MessageHistoryDialog({ bugId, commentId, open, onClose }: Props) {
{history?.map((edit, index) => (
<Accordion
square
key={index}
expanded={expanded === 'panel' + index}
onChange={handleChange('panel' + index)}
>
@ -224,7 +228,9 @@ function MessageHistoryDialog({ bugId, commentId, open, onClose }: Props) {
>
<Typography>{getSummary(index, edit.date)}</Typography>
</AccordionSummary>
<AccordionDetails>{edit.message}</AccordionDetails>
<AccordionDetails>
<Content markdown={edit.message} />
</AccordionDetails>
</Accordion>
))}
</DialogContent>

6
webui/types/remark-gemoji/index.d.ts vendored Normal file
View File

@ -0,0 +1,6 @@
declare module 'remark-gemoji' {
import { Plugin } from 'unified';
const plugin: Plugin;
export default plugin;
}