diff options
Diffstat (limited to 'webui/src')
29 files changed, 199 insertions, 123 deletions
diff --git a/webui/src/App.tsx b/webui/src/App.tsx index 4bf09606b..a8712a945 100644 --- a/webui/src/App.tsx +++ b/webui/src/App.tsx @@ -1,4 +1,5 @@ -import { Route, Routes } from 'react-router-dom'; +import * as React from 'react'; +import { Route, Routes } from 'react-router'; import Layout from './components/Header'; import BugPage from './pages/bug'; @@ -9,14 +10,16 @@ import NotFoundPage from './pages/notfound/NotFoundPage'; export default function App() { return ( - <Layout> - <Routes> - <Route path="/" element={<ListPage />} /> - <Route path="/new" element={<NewBugPage />} /> - <Route path="/bug/:id" element={<BugPage />} /> - <Route path="/user/:id" element={<IdentityPage />} /> - <Route element={<NotFoundPage />} /> - </Routes> - </Layout> + <React.StrictMode> + <Layout> + <Routes> + <Route path="/" element={<ListPage />} /> + <Route path="/new" element={<NewBugPage />} /> + <Route path="/bug/:id" element={<BugPage />} /> + <Route path="/user/:id" element={<IdentityPage />} /> + <Route element={<NotFoundPage />} /> + </Routes> + </Layout> + </React.StrictMode> ); } diff --git a/webui/src/components/Author.tsx b/webui/src/components/Author.tsx index 92e14d40f..51ed336ab 100644 --- a/webui/src/components/Author.tsx +++ b/webui/src/components/Author.tsx @@ -1,7 +1,7 @@ import MAvatar from '@mui/material/Avatar'; import Link from '@mui/material/Link'; import Tooltip from '@mui/material/Tooltip/Tooltip'; -import { Link as RouterLink } from 'react-router-dom'; +import { Link as RouterLink } from 'react-router'; import { AuthoredFragment } from '../graphql/fragments.generated'; diff --git a/webui/src/components/BackToListButton.tsx b/webui/src/components/BackToListButton.tsx index a4e4ea9c8..234d14588 100644 --- a/webui/src/components/BackToListButton.tsx +++ b/webui/src/components/BackToListButton.tsx @@ -1,7 +1,7 @@ import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import Button from '@mui/material/Button'; import makeStyles from '@mui/styles/makeStyles'; -import { Link } from 'react-router-dom'; +import { Link } from 'react-router'; const useStyles = makeStyles((theme) => ({ backButton: { diff --git a/webui/src/components/BugTitleForm/BugTitleForm.tsx b/webui/src/components/BugTitleForm/BugTitleForm.tsx index 78b9e901b..82185d4fd 100644 --- a/webui/src/components/BugTitleForm/BugTitleForm.tsx +++ b/webui/src/components/BugTitleForm/BugTitleForm.tsx @@ -1,7 +1,7 @@ import { Button, Typography } from '@mui/material'; import makeStyles from '@mui/styles/makeStyles'; import { useRef, useState } from 'react'; -import { Link } from 'react-router-dom'; +import { Link } from 'react-router'; import { TimelineDocument } from '../../pages/bug/TimelineQuery.generated'; import IfLoggedIn from '../IfLoggedIn/IfLoggedIn'; @@ -71,7 +71,7 @@ function BugTitleForm({ bug }: Props) { const [setTitle, { loading, error }] = useSetTitleMutation(); const [issueTitle, setIssueTitle] = useState(bug.title); const classes = useStyles(); - const issueTitleInput = useRef<HTMLInputElement>(); + const issueTitleInput = useRef<HTMLInputElement>(null); function isFormValid() { if (issueTitleInput.current) { diff --git a/webui/src/components/CommentInput/CommentInput.tsx b/webui/src/components/CommentInput/CommentInput.tsx index 0fde1d95b..4d4c3b633 100644 --- a/webui/src/components/CommentInput/CommentInput.tsx +++ b/webui/src/components/CommentInput/CommentInput.tsx @@ -102,7 +102,7 @@ function CommentInput({ inputProps, inputText, loading, onChange }: Props) { multiline value={input} variant="filled" - rows="4" // TODO: rowsMin support + minRows={4} onChange={(e: any) => setInput(e.target.value)} disabled={loading} /> diff --git a/webui/src/components/Content/AnchorTag.tsx b/webui/src/components/Content/AnchorTag.tsx index e9edef954..da861bc2d 100644 --- a/webui/src/components/Content/AnchorTag.tsx +++ b/webui/src/components/Content/AnchorTag.tsx @@ -1,6 +1,6 @@ import makeStyles from '@mui/styles/makeStyles'; import * as React from 'react'; -import { Link } from 'react-router-dom'; +import { Link } from 'react-router'; const useStyles = makeStyles((theme) => ({ tag: { diff --git a/webui/src/components/Content/index.tsx b/webui/src/components/Content/index.tsx index 9bf9ff7a6..3cfa9eb89 100644 --- a/webui/src/components/Content/index.tsx +++ b/webui/src/components/Content/index.tsx @@ -1,11 +1,24 @@ -import { createElement, Fragment, useEffect, useState } from 'react'; +import type { Root as HastRoot } from 'hast'; +import type { Root as MdRoot } from 'mdast'; +import { useEffect, useState } from 'react'; import * as React from 'react'; +import * as production from 'react/jsx-runtime'; +import rehypeHighlight, { + Options as RehypeHighlightOpts, +} from 'rehype-highlight'; import rehypeReact from 'rehype-react'; -import gemoji from 'remark-gemoji'; -import html from 'remark-html'; -import parse from 'remark-parse'; +import rehypeSanitize from 'rehype-sanitize'; +import remarkBreaks from 'remark-breaks'; +import remarkGemoji from 'remark-gemoji'; +import remarkGfm from 'remark-gfm'; +import remarkParse from 'remark-parse'; import remarkRehype from 'remark-rehype'; +import type { Options as RemarkRehypeOptions } from 'remark-rehype'; import { unified } from 'unified'; +import type { Plugin, Processor } from 'unified'; +import { Node as UnistNode } from 'unified/lib'; + +import { ThemeContext } from '../Themer'; import AnchorTag from './AnchorTag'; import BlockQuoteTag from './BlockQuoteTag'; @@ -13,32 +26,71 @@ import ImageTag from './ImageTag'; import PreTag from './PreTag'; type Props = { markdown: string }; + +// @lygaret 2025/05/16 +// type inference for some of this doesn't work, but the pipeline is fine +// this might get better when we upgrade typescript + +type RemarkPlugin = Plugin<[], MdRoot, HastRoot>; +type RemarkRehypePlugin = Plugin<RemarkRehypeOptions[], MdRoot, HastRoot>; +type RehypePlugin<Options extends unknown[] = []> = Plugin< + Options, + HastRoot, + HastRoot +>; + +const markdownPipeline: Processor< + UnistNode, + undefined, + undefined, + HastRoot, + React.JSX.Element +> = unified() + .use(remarkParse) + .use(remarkGemoji as unknown as RemarkPlugin) + .use(remarkBreaks as unknown as RemarkPlugin) + .use(remarkGfm) + .use(remarkRehype as unknown as RemarkRehypePlugin, { + allowDangerousHtml: true, + }) + .use(rehypeSanitize as unknown as RehypePlugin) + .use(rehypeHighlight as unknown as RehypePlugin<RehypeHighlightOpts[]>, { + detect: true, + subset: ['text'], + }) + .use(rehypeReact, { + ...production, + components: { + a: AnchorTag, + blockquote: BlockQuoteTag, + img: ImageTag, + pre: PreTag, + }, + }); + const Content: React.FC<Props> = ({ markdown }: Props) => { - const [Content, setContent] = useState(<></>); + const theme = React.useContext(ThemeContext); + const [content, setContent] = useState(<></>); useEffect(() => { - unified() - .use(parse) - .use(gemoji) - .use(html) - .use(remarkRehype) - .use(rehypeReact, { - createElement, - Fragment, - components: { - img: ImageTag, - pre: PreTag, - a: AnchorTag, - blockquote: BlockQuoteTag, - }, - }) + markdownPipeline .process(markdown) - .then((file) => { - setContent(file.result); + .then((file) => setContent(file.result)) + .catch((err: any) => { + setContent( + <> + <span className="error">{err}</span> + <pre>{markdown}</pre> + </> + ); }); }, [markdown]); - return Content; + return ( + <div className={'highlight-theme'} data-theme={theme.mode}> + {content} + </div> + ); }; export default Content; diff --git a/webui/src/components/Date.tsx b/webui/src/components/Date.tsx index 1454fac77..19efc081e 100644 --- a/webui/src/components/Date.tsx +++ b/webui/src/components/Date.tsx @@ -1,6 +1,7 @@ import Tooltip from '@mui/material/Tooltip/Tooltip'; import moment from 'moment'; -import Moment from 'react-moment'; + +import Moment from './Moment'; const HOUR = 1000 * 3600; const DAY = 24 * HOUR; diff --git a/webui/src/components/Header/Header.tsx b/webui/src/components/Header/Header.tsx index 5c03a5ecf..69b3fdfb2 100644 --- a/webui/src/components/Header/Header.tsx +++ b/webui/src/components/Header/Header.tsx @@ -1,13 +1,11 @@ import AppBar from '@mui/material/AppBar'; -import Tab, { TabProps } from '@mui/material/Tab'; -import Tabs from '@mui/material/Tabs'; import Toolbar from '@mui/material/Toolbar'; -import Tooltip from '@mui/material/Tooltip/Tooltip'; import { alpha } from '@mui/material/styles'; import makeStyles from '@mui/styles/makeStyles'; -import { Link, useLocation } from 'react-router-dom'; +import { Link } from 'react-router'; import CurrentIdentity from '../Identity/CurrentIdentity'; +import CurrentRepository from '../Identity/CurrentRepository'; import { LightSwitch } from '../Themer'; const useStyles = makeStyles((theme) => ({ @@ -29,7 +27,7 @@ const useStyles = makeStyles((theme) => ({ alignItems: 'center', }, lightSwitch: { - marginRight: '20px', + marginRight: theme.spacing(2), color: alpha(theme.palette.primary.contrastText, 0.5), }, logo: { @@ -38,43 +36,8 @@ const useStyles = makeStyles((theme) => ({ }, })); -function a11yProps(index: any) { - return { - id: `nav-tab-${index}`, - 'aria-controls': `nav-tabpanel-${index}`, - }; -} - -const DisabledTabWithTooltip = (props: TabProps) => { - /*The span elements around disabled tabs are needed, as the tooltip - * won't be triggered by disabled elements. - * See: https://material-ui.com/components/tooltips/#disabled-elements - * This must be done in a wrapper component, otherwise the TabS component - * cannot pass it styles down to the Tab component. Resulting in (console) - * warnings. This wrapper accepts the passed down TabProps and pass it around - * the span element to the Tab component. - */ - const msg = `This feature doesn't exist yet. Come help us build it.`; - return ( - <Tooltip title={msg}> - <span> - <Tab disabled {...props} /> - </span> - </Tooltip> - ); -}; - function Header() { const classes = useStyles(); - const location = useLocation(); - - // Prevents error of invalid tab selection in <Tabs> - // Will return a valid tab path or false if path is unknown. - function highlightTab() { - const validTabs = ['/', '/code', '/pulls', '/settings']; - const tab = validTabs.find((tabPath) => tabPath === location.pathname); - return tab === undefined ? false : tab; - } return ( <> @@ -82,28 +45,14 @@ function Header() { <Toolbar> <Link to="/" className={classes.appTitle}> <img src="/logo.svg" className={classes.logo} alt="git-bug logo" /> - git-bug + <CurrentRepository default="git-bug" /> </Link> <div className={classes.filler} /> <LightSwitch className={classes.lightSwitch} /> <CurrentIdentity /> </Toolbar> </AppBar> - <div className={classes.offset} /> - <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 - label="Pull Requests" - value="/pulls" - {...a11yProps(3)} - /> - <DisabledTabWithTooltip - label="Settings" - value="/settings" - {...a11yProps(4)} - /> - </Tabs> + <div className={classes.offset}></div> </> ); } diff --git a/webui/src/components/Identity/CurrentIdentity.tsx b/webui/src/components/Identity/CurrentIdentity.tsx index e6a396a8e..fb06898ec 100644 --- a/webui/src/components/Identity/CurrentIdentity.tsx +++ b/webui/src/components/Identity/CurrentIdentity.tsx @@ -12,7 +12,7 @@ import { import Avatar from '@mui/material/Avatar'; import makeStyles from '@mui/styles/makeStyles'; import { useState, useRef } from 'react'; -import { Link as RouterLink } from 'react-router-dom'; +import { Link as RouterLink } from 'react-router'; import { useCurrentIdentityQuery } from './CurrentIdentity.generated'; diff --git a/webui/src/components/Identity/CurrentRepository.tsx b/webui/src/components/Identity/CurrentRepository.tsx new file mode 100644 index 000000000..77aa6839b --- /dev/null +++ b/webui/src/components/Identity/CurrentRepository.tsx @@ -0,0 +1,19 @@ +import { useCurrentIdentityQuery } from './CurrentIdentity.generated'; + +// same as in multi_repo_cache.go +const defaultRepoName = '__default'; + +const CurrentRepository = (props: { default: string }) => { + const { loading, error, data } = useCurrentIdentityQuery(); + + if (error || loading || !data?.repository?.name) return null; + + let name = data.repository.name; + if (name === defaultRepoName) { + name = props.default; + } + + return <>{name}</>; +}; + +export default CurrentRepository; diff --git a/webui/src/components/Label.tsx b/webui/src/components/Label.tsx index 709aceff9..a63398487 100644 --- a/webui/src/components/Label.tsx +++ b/webui/src/components/Label.tsx @@ -26,14 +26,16 @@ const createStyle = (color: Color, maxWidth?: string) => ({ type Props = { label: LabelFragment; + inline?: boolean; maxWidth?: string; className?: string; }; -function Label({ label, maxWidth, className }: Props) { +function Label({ label, inline, maxWidth, className }: Props) { return ( <Chip size={'small'} label={label.name} + component={inline ? 'span' : 'div'} className={className} style={createStyle(label.color, maxWidth)} /> diff --git a/webui/src/components/Moment.tsx b/webui/src/components/Moment.tsx new file mode 100644 index 000000000..4bffbd318 --- /dev/null +++ b/webui/src/components/Moment.tsx @@ -0,0 +1,28 @@ +import moment from 'moment'; + +type Props = { + date: moment.MomentInput; + format: string; + fromNowDuring?: number; +}; + +const Moment = ({ date, format, fromNowDuring }: Props) => { + let dateString: string | undefined; + const dateMoment = moment(date); + + if (fromNowDuring) { + const diff = moment().diff(dateMoment, 'ms'); + if (diff < fromNowDuring) { + dateString = dateMoment.fromNow(); + } + } + + // we either are out of range or didn't get asked for fromNow + if (dateString === undefined) { + dateString = dateMoment.format(format); + } + + return <span>{dateString}</span>; +}; + +export default Moment; diff --git a/webui/src/components/Themer.tsx b/webui/src/components/Themer.tsx index 9934888d1..f03881b4d 100644 --- a/webui/src/components/Themer.tsx +++ b/webui/src/components/Themer.tsx @@ -11,7 +11,7 @@ declare module '@mui/styles/defaultTheme' { interface DefaultTheme extends Theme {} } -const ThemeContext = createContext({ +export const ThemeContext = createContext({ toggleMode: () => {}, mode: '', }); diff --git a/webui/src/index.tsx b/webui/src/index.tsx index d203eb199..7667c7068 100644 --- a/webui/src/index.tsx +++ b/webui/src/index.tsx @@ -1,19 +1,21 @@ import { ApolloProvider } from '@apollo/client'; -import ReactDOM from 'react-dom'; -import { BrowserRouter } from 'react-router-dom'; +import React from 'react'; +import { createRoot } from 'react-dom/client'; +import { BrowserRouter } from 'react-router'; import App from './App'; import apolloClient from './apollo'; import Themer from './components/Themer'; import { defaultLightTheme, defaultDarkTheme } from './themes/index'; +import './themes/highlight-theme.scss'; -ReactDOM.render( +const root = createRoot(document.getElementById('root') as HTMLElement); +root.render( <ApolloProvider client={apolloClient}> <BrowserRouter> <Themer lightTheme={defaultLightTheme} darkTheme={defaultDarkTheme}> <App /> </Themer> </BrowserRouter> - </ApolloProvider>, - document.getElementById('root') + </ApolloProvider> ); diff --git a/webui/src/pages/bug/BugQuery.tsx b/webui/src/pages/bug/BugQuery.tsx index 244b08360..704dd0376 100644 --- a/webui/src/pages/bug/BugQuery.tsx +++ b/webui/src/pages/bug/BugQuery.tsx @@ -1,6 +1,6 @@ import CircularProgress from '@mui/material/CircularProgress'; import * as React from 'react'; -import { useParams } from 'react-router-dom'; +import { useParams } from 'react-router'; import NotFoundPage from '../notfound/NotFoundPage'; @@ -16,7 +16,8 @@ const BugQuery: React.FC = () => { }); if (loading) return <CircularProgress />; if (!data?.repository?.bug) return <NotFoundPage />; - if (error) return <p>Error: {error}</p>; + if (error) return <p>Error: {error.message}</p>; + return <Bug bug={data.repository.bug} />; }; diff --git a/webui/src/pages/bug/LabelChange.tsx b/webui/src/pages/bug/LabelChange.tsx index 6b356d14a..28fc47433 100644 --- a/webui/src/pages/bug/LabelChange.tsx +++ b/webui/src/pages/bug/LabelChange.tsx @@ -35,12 +35,12 @@ function LabelChange({ op }: Props) { <Author author={op.author} className={classes.author} /> {added.length > 0 && <span> added the </span>} {added.map((label, index) => ( - <Label key={index} label={label} className={classes.label} /> + <Label inline key={index} label={label} className={classes.label} /> ))} {added.length > 0 && removed.length > 0 && <span> and</span>} {removed.length > 0 && <span> removed the </span>} {removed.map((label, index) => ( - <Label key={index} label={label} className={classes.label} /> + <Label inline key={index} label={label} className={classes.label} /> ))} <span> {' '} diff --git a/webui/src/pages/bug/MessageHistoryDialog.tsx b/webui/src/pages/bug/MessageHistoryDialog.tsx index 77f82d86f..7523d5c08 100644 --- a/webui/src/pages/bug/MessageHistoryDialog.tsx +++ b/webui/src/pages/bug/MessageHistoryDialog.tsx @@ -17,9 +17,9 @@ import createStyles from '@mui/styles/createStyles'; import withStyles from '@mui/styles/withStyles'; import moment from 'moment'; import * as React from 'react'; -import Moment from 'react-moment'; import Content from '../../components/Content'; +import Moment from '../../components/Moment'; import { AddCommentFragment } from './MessageCommentFragment.generated'; import { CreateFragment } from './MessageCreateFragment.generated'; @@ -159,7 +159,7 @@ function MessageHistoryDialog({ bugId, commentId, open, onClose }: Props) { Something went wrong... </DialogTitle> <DialogContent dividers> - <p>Error: {error}</p> + <p>Error: {error.message}</p> </DialogContent> </Dialog> ); diff --git a/webui/src/pages/bug/TimelineQuery.tsx b/webui/src/pages/bug/TimelineQuery.tsx index ab9e4cd6f..3b55270cb 100644 --- a/webui/src/pages/bug/TimelineQuery.tsx +++ b/webui/src/pages/bug/TimelineQuery.tsx @@ -17,7 +17,7 @@ const TimelineQuery = ({ bug }: Props) => { }); if (loading) return <CircularProgress />; - if (error) return <p>Error: {error}</p>; + if (error) return <p>Error: {error.message}</p>; const nodes = data?.repository?.bug?.timeline.nodes; if (!nodes) { diff --git a/webui/src/pages/identity/BugList.tsx b/webui/src/pages/identity/BugList.tsx index a7c37a32c..7c661dc20 100644 --- a/webui/src/pages/identity/BugList.tsx +++ b/webui/src/pages/identity/BugList.tsx @@ -34,7 +34,7 @@ function BugList({ id }: Props) { }); if (loading) return <CircularProgress />; - if (error) return <p>Error: {error}</p>; + if (error) return <p>Error: {error.message}</p>; const bugs = data?.repository?.allBugs.nodes; return ( diff --git a/webui/src/pages/identity/Identity.tsx b/webui/src/pages/identity/Identity.tsx index 19b80b1c2..beced10d8 100644 --- a/webui/src/pages/identity/Identity.tsx +++ b/webui/src/pages/identity/Identity.tsx @@ -5,7 +5,7 @@ import Avatar from '@mui/material/Avatar'; import CircularProgress from '@mui/material/CircularProgress'; import Grid from '@mui/material/Grid'; import makeStyles from '@mui/styles/makeStyles'; -import { Link as RouterLink } from 'react-router-dom'; +import { Link as RouterLink } from 'react-router'; import { IdentityFragment } from '../../components/Identity/IdentityFragment.generated'; @@ -56,7 +56,7 @@ const Identity = ({ identity }: Props) => { }); if (loading) return <CircularProgress />; - if (error) return <p>Error: {error}</p>; + if (error) return <p>Error: {error.message}</p>; const statistic = data?.repository; const authoredCount = statistic?.authored?.totalCount; const participatedCount = statistic?.participated?.totalCount; diff --git a/webui/src/pages/identity/IdentityQuery.tsx b/webui/src/pages/identity/IdentityQuery.tsx index 382116ca5..faa192f6b 100644 --- a/webui/src/pages/identity/IdentityQuery.tsx +++ b/webui/src/pages/identity/IdentityQuery.tsx @@ -1,6 +1,6 @@ import CircularProgress from '@mui/material/CircularProgress'; import * as React from 'react'; -import { useParams } from 'react-router-dom'; +import { useParams } from 'react-router'; import { useGetUserByIdQuery } from '../../components/Identity/UserIdentity.generated'; @@ -14,7 +14,7 @@ const UserQuery: React.FC = () => { variables: { userId: params.id }, }); if (loading) return <CircularProgress />; - if (error) return <p>Error: {error}</p>; + if (error) return <p>Error: {error.message}</p>; if (!data?.repository?.identity) return <p>404.</p>; return <Identity identity={data.repository.identity} />; }; diff --git a/webui/src/pages/list/BugRow.tsx b/webui/src/pages/list/BugRow.tsx index 82582dbe2..40f436130 100644 --- a/webui/src/pages/list/BugRow.tsx +++ b/webui/src/pages/list/BugRow.tsx @@ -6,7 +6,7 @@ import TableRow from '@mui/material/TableRow/TableRow'; import Tooltip from '@mui/material/Tooltip/Tooltip'; import makeStyles from '@mui/styles/makeStyles'; import * as React from 'react'; -import { Link } from 'react-router-dom'; +import { Link } from 'react-router'; import Author from 'src/components/Author'; import Date from 'src/components/Date'; diff --git a/webui/src/pages/list/Filter.tsx b/webui/src/pages/list/Filter.tsx index 6b3422bec..0ba5e71d0 100644 --- a/webui/src/pages/list/Filter.tsx +++ b/webui/src/pages/list/Filter.tsx @@ -10,7 +10,7 @@ import withStyles from '@mui/styles/withStyles'; import clsx from 'clsx'; import * as React from 'react'; import { useRef, useState, useEffect } from 'react'; -import { Location, Link } from 'react-router-dom'; +import { Location, Link } from 'react-router'; import { Color } from '../../gqlTypes'; diff --git a/webui/src/pages/list/FilterToolbar.tsx b/webui/src/pages/list/FilterToolbar.tsx index 5e774734e..9b467e20c 100644 --- a/webui/src/pages/list/FilterToolbar.tsx +++ b/webui/src/pages/list/FilterToolbar.tsx @@ -4,7 +4,7 @@ import ErrorOutline from '@mui/icons-material/ErrorOutline'; import Toolbar from '@mui/material/Toolbar'; import makeStyles from '@mui/styles/makeStyles'; import * as React from 'react'; -import { Location } from 'react-router-dom'; +import { Location } from 'react-router'; import { Filter, @@ -131,8 +131,8 @@ function FilterToolbar({ query, queryLocation }: Props) { params[key] && params[key].includes(value) ? values.filter((v) => v !== value) : values - ? [...values, value] - : [value], + ? [...values, value] + : [value], }; }; const clearParam = diff --git a/webui/src/pages/list/ListQuery.tsx b/webui/src/pages/list/ListQuery.tsx index 6b508e905..98a7df42d 100644 --- a/webui/src/pages/list/ListQuery.tsx +++ b/webui/src/pages/list/ListQuery.tsx @@ -13,7 +13,7 @@ import { Theme } from '@mui/material/styles'; import makeStyles from '@mui/styles/makeStyles'; import * as React from 'react'; import { useState, useEffect, useRef } from 'react'; -import { useLocation, useNavigate, Link } from 'react-router-dom'; +import { useLocation, useNavigate, Link } from 'react-router'; import { useCurrentIdentityQuery } from '../../components/Identity/CurrentIdentity.generated'; import IfLoggedIn from 'src/components/IfLoggedIn/IfLoggedIn'; diff --git a/webui/src/pages/new/NewBugPage.tsx b/webui/src/pages/new/NewBugPage.tsx index 91e4905a0..c0aa6b3f9 100644 --- a/webui/src/pages/new/NewBugPage.tsx +++ b/webui/src/pages/new/NewBugPage.tsx @@ -2,7 +2,7 @@ import { Button, Paper } from '@mui/material'; import { Theme } from '@mui/material/styles'; import makeStyles from '@mui/styles/makeStyles'; import { FormEvent, useRef, useState } from 'react'; -import { useNavigate } from 'react-router-dom'; +import { useNavigate } from 'react-router'; import BugTitleInput from '../../components/BugTitleForm/BugTitleInput'; import CommentInput from '../../components/CommentInput/CommentInput'; diff --git a/webui/src/setupTests.js b/webui/src/setupTests.js new file mode 100644 index 000000000..012e25a15 --- /dev/null +++ b/webui/src/setupTests.js @@ -0,0 +1,6 @@ +import { TextEncoder } from 'util'; + +// jsdom, used to run tests, doesn't support text-encoder +// https://github.com/remix-run/react-router/issues/12363 + +global.TextEncoder = TextEncoder; diff --git a/webui/src/themes/highlight-theme.scss b/webui/src/themes/highlight-theme.scss new file mode 100644 index 000000000..e6f145e9b --- /dev/null +++ b/webui/src/themes/highlight-theme.scss @@ -0,0 +1,13 @@ +@use 'sass:meta'; + +// if highlight.js gets updated to support proper mixins, +// meta.load-css won't be necessary; see https://sass-lang.com/documentation/breaking-changes/import/#nested-imports + +.highlight-theme[data-theme='light'], +.highlight-theme:not([data-theme='dark']) { + @include meta.load-css('~highlight.js/scss/base16/classic-light.scss'); +} + +.highlight-theme[data-theme='dark'] { + @include meta.load-css('~highlight.js/scss/base16/classic-dark.scss'); +} |