diff options
Diffstat (limited to 'webui/src')
24 files changed, 92 insertions, 87 deletions
diff --git a/webui/src/App.tsx b/webui/src/App.tsx index 4bf09606..173f35ff 100644 --- a/webui/src/App.tsx +++ b/webui/src/App.tsx @@ -1,4 +1,4 @@ -import { Route, Routes } from 'react-router-dom'; +import { Route, Routes } from 'react-router'; import Layout from './components/Header'; import BugPage from './pages/bug'; diff --git a/webui/src/components/Author.tsx b/webui/src/components/Author.tsx index 92e14d40..51ed336a 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 a4e4ea9c..234d1458 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 78b9e901..82185d4f 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 0fde1d95..4d4c3b63 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 e9edef95..da861bc2 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/Date.tsx b/webui/src/components/Date.tsx index 1454fac7..19efc081 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 5c03a5ec..69b3fdfb 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 e6a396a8..fb06898e 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 00000000..77aa6839 --- /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/Moment.tsx b/webui/src/components/Moment.tsx new file mode 100644 index 00000000..4bffbd31 --- /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/index.tsx b/webui/src/index.tsx index d203eb19..a7cbe559 100644 --- a/webui/src/index.tsx +++ b/webui/src/index.tsx @@ -1,19 +1,20 @@ 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'; -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 244b0836..704dd037 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/MessageHistoryDialog.tsx b/webui/src/pages/bug/MessageHistoryDialog.tsx index 77f82d86..7523d5c0 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 ab9e4cd6..3b55270c 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 a7c37a32..7c661dc2 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 19b80b1c..beced10d 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 382116ca..faa192f6 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 82582dbe..40f43613 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 6b3422be..0ba5e71d 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 5e774734..9b467e20 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 6b508e90..98a7df42 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 91e4905a..c0aa6b3f 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 00000000..012e25a1 --- /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; |