diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..041fc999 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @smartcontractkit/core @smartcontractkit/dapp-team \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d6a52ee7..1a56a34c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,8 +42,8 @@ jobs: unit-tests-matrix: strategy: matrix: - shardIndex: [1, 2, 3, 4] - shardTotal: [4] + shardIndex: [1] + shardTotal: [1] name: Unit Tests ${{ matrix.shardIndex }}/${{ matrix.shardTotal }} runs-on: ubuntu-latest diff --git a/package.json b/package.json index b6dc854e..3a9d2df3 100644 --- a/package.json +++ b/package.json @@ -26,9 +26,11 @@ }, "dependencies": { "@apollo/client": "^3.14.1", + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.1", "@iarna/toml": "3.0.0", - "@material-ui/core": "^3.9.4", - "@material-ui/icons": "4.5.1", + "@mui/icons-material": "^5.17.1", + "@mui/material": "^5.17.1", "bignumber.js": "^9.3.1", "buffer": "^6.0.3", "classnames": "^2.5.1", @@ -36,7 +38,6 @@ "d3": "~5.16.0", "elkjs": "^0.11.1", "formik": "^2.4.9", - "formik-material-ui": "^3.0.1", "graphlib-dot": "^0.6.4", "graphql": "16.13.2", "isomorphic-unfetch": "^3.1.0", @@ -47,10 +48,10 @@ "moment": "^2.30.1", "path-to-regexp": "^3.3.0", "prop-types": "^15.8.1", - "react": "^16.14.0", + "react": "^18.3.1", "react-copy-to-clipboard": "^5.1.1", "react-d3-graph": "^2.6.0", - "react-dom": "^16.14.0", + "react-dom": "^18.3.1", "react-redux": "^7.2.9", "react-resize-detector": "^4.2.3", "react-router-dom": "^5.3.4", @@ -59,6 +60,7 @@ "redux": "^4.2.1", "redux-thunk": "^2.4.2", "stream-browserify": "^3.0.0", + "tss-react": "^4.9.20", "yup": "^0.32.11" }, "devDependencies": { @@ -73,17 +75,16 @@ "@swc/core": "^1.15.26", "@swc/jest": "^0.2.39", "@testing-library/jest-dom": "^6.9.1", - "@testing-library/react": "^12.1.5", - "@testing-library/react-hooks": "^7.0.2", + "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "@types/cookie": "^0.6.0", "@types/d3": "~5.16.7", "@types/jest": "^30.0.0", "@types/lodash": "^4.17.24", "@types/node": "^24.12.2", - "@types/react": "^16.14.69", + "@types/react": "18.3.12", "@types/react-d3-graph": "^2.6.7", - "@types/react-dom": "^16.9.25", + "@types/react-dom": "18.3.1", "@types/react-redux": "^7.1.34", "@types/react-resize-detector": "^4.2.0", "@types/react-router-dom": "^5.3.3", @@ -116,7 +117,7 @@ "terser-webpack-plugin": "^5.4.0", "ts-node": "^10.9.1", "tsconfig-paths-webpack-plugin": "^4.2.0", - "typescript": "^4.9.5", + "typescript": "^5.9.3", "undici": "^8.1.0", "web-worker": "^1.5.0", "webpack": "^5.106.2", @@ -124,14 +125,18 @@ "webpack-dev-server": "^5.2.3" }, "resolutions": { - "@types/react": "16.14.69", - "@types/react-dom": "16.9.25", - "lodash": "4.18.1" + "lodash": "4.18.1", + "@types/react": "18.3.12", + "@types/react-dom": "18.3.1", + "js-yaml": "^3.14.2", + "semver": "^6.3.1" }, "overrides": { - "@types/react": "^16.14.69", - "@types/react-dom": "^16.9.25", - "lodash": "^4.18.1" + "lodash": "4.18.1", + "@types/react": "18.3.12", + "@types/react-dom": "18.3.1", + "js-yaml": "^3.14.2", + "semver": "^6.3.1" }, "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/src/Layout.tsx b/src/Layout.tsx index ca0c3223..f980bf40 100644 --- a/src/Layout.tsx +++ b/src/Layout.tsx @@ -5,7 +5,7 @@ import { Redirect, BrowserRouter as Router, } from 'react-router-dom' -import CssBaseline from '@material-ui/core/CssBaseline' +import CssBaseline from '@mui/material/CssBaseline' import Private from './Private' import { useOperatorUiSelector } from 'reducers' import SignIn from 'pages/SignIn' diff --git a/src/Private.tsx b/src/Private.tsx index 23236872..6513cafb 100644 --- a/src/Private.tsx +++ b/src/Private.tsx @@ -1,9 +1,9 @@ import React from 'react' -import { Theme } from '@material-ui/core/styles' -import Grid from '@material-ui/core/Grid' +import { Theme } from '@mui/material/styles' +import Grid from '@mui/material/Grid' import { Switch } from 'react-router-dom' -import { withStyles } from '@material-ui/core/styles' +import { withStyles } from 'src/utils/withStyles' import Header from 'pages/Header' import Notifications from 'pages/Notifications' import PrivateRoute from './PrivateRoute' @@ -27,7 +27,7 @@ const styles = (theme: Theme) => { return { content: { marginTop: 0, - marginBottom: theme.spacing.unit * 5, + marginBottom: theme.spacing(5), }, } } diff --git a/src/actionCreators.ts b/src/actionCreators.ts index 5f00c3f8..87d774e9 100644 --- a/src/actionCreators.ts +++ b/src/actionCreators.ts @@ -57,7 +57,10 @@ const curryErrorHandler = } } -export const notifySuccess = (component: React.ReactNode, props: object) => { +export const notifySuccess = ( + component: (props: any) => React.ReactNode, + props: object, +) => { return { type: NotifyActionType.NOTIFY_SUCCESS, component, @@ -70,7 +73,10 @@ export const notifySuccessMsg = (msg: string) => ({ msg, }) -export const notifyError = (component: React.ReactNode, error: Error) => +export const notifyError = ( + component: (props: any) => React.ReactNode, + error: Error, +) => ({ type: NotifyActionType.NOTIFY_ERROR, component, @@ -109,7 +115,9 @@ const signInSuccessAction = (doc: UnboxApi) => { } const signInFailAction = () => - ({ type: AuthActionType.RECEIVE_SIGNIN_FAIL }) as const + ({ + type: AuthActionType.RECEIVE_SIGNIN_FAIL, + }) as const function sendSignIn(data: Parameter) { return (dispatch: StoreDispatch) => { @@ -247,9 +255,15 @@ function sendSignOut(dispatch: Dispatch) { }) } -// Base64 to ArrayBuffer -function bufferDecode(value: any) { - return Uint8Array.from(atob(value), (c) => c.charCodeAt(0)) +// Base64URL to ArrayBuffer +// atob() requires standard Base64; the server sends Base64URL (- and _ instead +// of + and /) without padding, so we must normalise before decoding. +function bufferDecode(value: string) { + const base64 = value + .replace(/-/g, '+') + .replace(/_/g, '/') + .padEnd(value.length + ((4 - (value.length % 4)) % 4), '=') + return Uint8Array.from(atob(base64), (c) => c.charCodeAt(0)) } // ArrayBuffer to URLBase64 @@ -338,13 +352,20 @@ export const submitSignIn = (data: Parameter) => export const submitSignOut = () => sendSignOut -export const beginRegistration = () => sendBeginRegistration() +export const beginRegistration = (): ThunkAction< + ReturnType, + AppState, + void, + Action +> => { + return () => sendBeginRegistration() +} export const createJobRunV2 = ( id: string, pipelineInput: string, - successCallback: React.ReactNode, - errorCallback: React.ReactNode, + successCallback: (props: any) => React.ReactNode, + errorCallback: (props: any) => React.ReactNode, ): ThunkAction, AppState, void, Action> => { return (dispatch: Dispatch) => { dispatch({ type: ResourceActionType.REQUEST_CREATE }) diff --git a/src/api/sessions.ts b/src/api/sessions.ts index e5f26b2c..24735b39 100644 --- a/src/api/sessions.ts +++ b/src/api/sessions.ts @@ -24,7 +24,7 @@ export class Sessions { } public destroySession = (): Promise< - jsonapi.ApiResponse + jsonapi.PaginatedApiResponse > => { return this.destroy() } diff --git a/src/components/AccountMenu.test.tsx b/src/components/AccountMenu.test.tsx new file mode 100644 index 00000000..76ec2c1b --- /dev/null +++ b/src/components/AccountMenu.test.tsx @@ -0,0 +1,58 @@ +import React from 'react' +import userEvent from '@testing-library/user-event' +import { render, screen, waitFor } from 'support/test-utils' + +jest.mock('../api', () => ({ + __esModule: true, + v2: { + webauthn: { + beginKeyRegistration: jest.fn(), + finishKeyRegistration: jest.fn(), + }, + }, + sessions: { + destroySession: jest.fn(), + }, +})) + +import * as api from '../api' +import { AccountMenu } from './AccountMenu' + +describe('AccountMenu', () => { + const alertSpy = jest.spyOn(window, 'alert').mockImplementation(() => {}) + + beforeEach(() => { + jest.clearAllMocks() + Object.defineProperty(window.navigator, 'credentials', { + configurable: true, + value: {}, + }) + }) + + afterAll(() => { + alertSpy.mockRestore() + }) + + it('keeps the UI responsive when MFA registration fails on the server', async () => { + ;( + api.v2.webauthn.beginKeyRegistration as jest.MockedFunction< + typeof api.v2.webauthn.beginKeyRegistration + > + ).mockRejectedValue(new Error('server error')) + + render() + + userEvent.click(screen.getByRole('button')) + userEvent.click( + screen.getByRole('menuitem', { name: /register mfa token/i }), + ) + + await waitFor(() => { + expect(alertSpy).toHaveBeenCalledWith( + expect.stringContaining('Key registration error'), + ) + }) + + expect(screen.getByRole('button')).toBeInTheDocument() + }) +}) diff --git a/src/components/AccountMenu.tsx b/src/components/AccountMenu.tsx index 08bef605..f8894be3 100644 --- a/src/components/AccountMenu.tsx +++ b/src/components/AccountMenu.tsx @@ -1,11 +1,14 @@ import React from 'react' import { useDispatch } from 'react-redux' -import AccountCircleIcon from '@material-ui/icons/AccountCircle' -import IconButton from '@material-ui/core/IconButton' -import Menu from '@material-ui/core/Menu' -import MenuItem from '@material-ui/core/MenuItem' -import { Theme, withStyles, WithStyles } from '@material-ui/core/styles' +import AccountCircleIcon from '@mui/icons-material/AccountCircle' +import IconButton from '@mui/material/IconButton' +import Menu from '@mui/material/Menu' +import MenuItem from '@mui/material/MenuItem' +import { Theme } from '@mui/material/styles' + +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' import { beginRegistration, submitSignOut } from 'actionCreators' @@ -19,6 +22,9 @@ const styles = (theme: Theme) => { paddingTop: 0, paddingBottom: 0, }, + menuItem: { + minHeight: theme.spacing(6), + }, } } @@ -48,14 +54,12 @@ export const AccountMenu = withStyles(styles)(({ classes }: Props) => { return ( - + - { className: classes.menuList, }} > - Register MFA Token + + Register MFA Token + - Log out + + Log out + ) diff --git a/src/components/BaseLink.test.tsx b/src/components/BaseLink.test.tsx index 6e6de787..c1e155a0 100644 --- a/src/components/BaseLink.test.tsx +++ b/src/components/BaseLink.test.tsx @@ -4,9 +4,10 @@ import { render, screen } from '@testing-library/react' import BaseLink from '../../src/components/BaseLink' const { getByRole, getByText } = screen +const Router = MemoryRouter as any const renderBaseLink = (link: React.ReactNode) => - render({link}) + render({link}) describe('components/BaseLink', () => { it('renders an anchor', () => { diff --git a/src/components/BaseLink.tsx b/src/components/BaseLink.tsx index 817ac9ba..5d2d7e2d 100644 --- a/src/components/BaseLink.tsx +++ b/src/components/BaseLink.tsx @@ -5,10 +5,15 @@ interface Props extends Omit { href: string } -const BaseLink = ({ href, children, ...rest }: Props) => ( - - {children} - +// forwardRef is required so MUI v5's ButtonBase can attach a ref when this +// component is passed as the `component` prop. +const BaseLink = React.forwardRef( + ({ href, children, ...rest }, ref) => ( + + {children} + + ), ) +BaseLink.displayName = 'BaseLink' export default BaseLink diff --git a/src/components/Button.tsx b/src/components/Button.tsx index bb64dd67..65c63af0 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -1,13 +1,9 @@ import React from 'react' -import { - createStyles, - Theme, - withStyles, - WithStyles, -} from '@material-ui/core/styles' -import MuiButton, { - ButtonProps as MuiButtonProps, -} from '@material-ui/core/Button' +import { Theme } from '@mui/material/styles' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import { createStyles } from 'src/utils/withStyles' +import MuiButton, { ButtonProps as MuiButtonProps } from '@mui/material/Button' import classNames from 'classnames' const styles = ({ palette }: Theme) => @@ -51,7 +47,7 @@ const styles = ({ palette }: Theme) => }, }) -// Unfortunately @material-ui/core does not export the type so we have to redefine it. +// Unfortunately @mui/material does not export the type so we have to redefine it. type MuiButtonVariant = | 'text' | 'flat' @@ -93,7 +89,7 @@ const muiProps = (variant: ButtonVariant, classes: any): MuiButtonProps => { interface Props extends WithStyles { children: React.ReactNode - component?: React.ReactNode + component?: React.ElementType onClick?: React.MouseEventHandler type?: string disabled?: boolean diff --git a/src/components/CardTitle.tsx b/src/components/CardTitle.tsx index cbc399ce..759afd84 100644 --- a/src/components/CardTitle.tsx +++ b/src/components/CardTitle.tsx @@ -1,6 +1,6 @@ -import CardContent from '@material-ui/core/CardContent' -import Divider from '@material-ui/core/Divider' -import Typography from '@material-ui/core/Typography' +import CardContent from '@mui/material/CardContent' +import Divider from '@mui/material/Divider' +import Typography from '@mui/material/Typography' import React from 'react' interface Props { diff --git a/src/components/Cards/DetailsCard.tsx b/src/components/Cards/DetailsCard.tsx index 14072011..0e40c1cd 100644 --- a/src/components/Cards/DetailsCard.tsx +++ b/src/components/Cards/DetailsCard.tsx @@ -1,39 +1,37 @@ import React from 'react' -import { - createStyles, - Theme, - withStyles, - WithStyles, -} from '@material-ui/core/styles' -import Typography from '@material-ui/core/Typography' +import { Theme } from '@mui/material/styles' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import { createStyles } from 'src/utils/withStyles' +import Typography from '@mui/material/Typography' -import Paper from '@material-ui/core/Paper' +import Paper from '@mui/material/Paper' const styles = (theme: Theme) => createStyles({ paper: { display: 'flex', - margin: `${theme.spacing.unit * 2.5}px 0`, - padding: `${theme.spacing.unit * 3}px ${theme.spacing.unit * 3.5}px`, + margin: `${theme.spacing(2.5)} 0`, + padding: `${theme.spacing(3)} ${theme.spacing(3.5)}`, }, content: { flex: 1, width: '100%', }, actions: { - marginTop: -theme.spacing.unit * 1.5, - marginLeft: -theme.spacing.unit * 4, + marginTop: -theme.spacing(1.5), + marginLeft: -theme.spacing(4), [theme.breakpoints.up('sm')]: { marginLeft: 0, - marginRight: -theme.spacing.unit * 1.5, + marginRight: -theme.spacing(1.5), }, }, itemBlock: { border: '1px solid rgba(224, 224, 224, 1)', borderRadius: theme.shape.borderRadius, - padding: theme.spacing.unit * 2, - marginTop: theme.spacing.unit, + padding: theme.spacing(2), + marginTop: theme.spacing(1), }, itemBlockText: { overflowWrap: 'anywhere', @@ -74,9 +72,10 @@ export const DetailsCardItemTitle = ({ title }: { title: string }) => ( // DetailsCardItemValue provides default styles for an item value in the details // card. -export const DetailsCardItemValue: React.FC<{ - value?: string | number | null -}> = ({ children, value }) => ( +export const DetailsCardItemValue = ({ + children, + value, +}: React.PropsWithChildren<{ value?: string | number | null }>) => ( {children ? children : value} diff --git a/src/components/Cards/KeyValueListCard.tsx b/src/components/Cards/KeyValueListCard.tsx index d20747bf..e8183f4e 100644 --- a/src/components/Cards/KeyValueListCard.tsx +++ b/src/components/Cards/KeyValueListCard.tsx @@ -1,12 +1,12 @@ import React from 'react' -import Card from '@material-ui/core/Card' -import CardHeader from '@material-ui/core/CardHeader' -import Table from '@material-ui/core/Table' -import TableBody from '@material-ui/core/TableBody' -import TableCell from '@material-ui/core/TableCell' -import TableHead from '@material-ui/core/TableHead' -import TableRow from '@material-ui/core/TableRow' +import Card from '@mui/material/Card' +import CardHeader from '@mui/material/CardHeader' +import Table from '@mui/material/Table' +import TableBody from '@mui/material/TableBody' +import TableCell from '@mui/material/TableCell' +import TableHead from '@mui/material/TableHead' +import TableRow from '@mui/material/TableRow' const renderEntries = (entries: Array>) => entries.map(([k, v]) => ( @@ -16,7 +16,7 @@ const renderEntries = (entries: Array>) => )) -const SpanRow: React.FC = ({ children }) => ( +const SpanRow = ({ children }: React.PropsWithChildren<{}>) => ( {children} @@ -24,7 +24,9 @@ const SpanRow: React.FC = ({ children }) => ( ) -const ErrorRow: React.FC = ({ children }) => {children} +const ErrorRow = ({ children }: React.PropsWithChildren<{}>) => ( + {children} +) const FetchingRow = () => ... diff --git a/src/components/Cards/TaskListCard.tsx b/src/components/Cards/TaskListCard.tsx index 733c9869..87531508 100644 --- a/src/components/Cards/TaskListCard.tsx +++ b/src/components/Cards/TaskListCard.tsx @@ -1,9 +1,9 @@ import React from 'react' -import Card from '@material-ui/core/Card' -import CardContent from '@material-ui/core/CardContent' -import CardHeader from '@material-ui/core/CardHeader' -import Typography from '@material-ui/core/Typography' +import Card from '@mui/material/Card' +import CardContent from '@mui/material/CardContent' +import CardHeader from '@mui/material/CardHeader' +import Typography from '@mui/material/Typography' import { parseDot, Stratify } from 'utils/parseDot' import { D3Graph } from 'components/D3Chart/D3Graph' diff --git a/src/components/Content.js b/src/components/Content.js index 4f7eec8b..633e0ead 100644 --- a/src/components/Content.js +++ b/src/components/Content.js @@ -1,9 +1,9 @@ import React from 'react' -import { withStyles } from '@material-ui/core/styles' +import { withStyles } from 'src/utils/withStyles' const styles = (theme) => ({ content: { - padding: theme.spacing.unit * 5, + padding: theme.spacing(5), }, }) diff --git a/src/components/Copy/CopyButton.tsx b/src/components/Copy/CopyButton.tsx index 6d8403e7..fbbf8e21 100644 --- a/src/components/Copy/CopyButton.tsx +++ b/src/components/Copy/CopyButton.tsx @@ -3,7 +3,7 @@ import React from 'react' import { CopyToClipboard } from 'react-copy-to-clipboard' import Button from '../Button' -import Tooltip from '@material-ui/core/Tooltip' +import Tooltip from '@mui/material/Tooltip' interface Props { data: string diff --git a/src/components/Copy/CopyIconButton.tsx b/src/components/Copy/CopyIconButton.tsx index f996b796..9849429d 100644 --- a/src/components/Copy/CopyIconButton.tsx +++ b/src/components/Copy/CopyIconButton.tsx @@ -2,15 +2,13 @@ import React from 'react' import { CopyToClipboard } from 'react-copy-to-clipboard' -import FileCopyOutlinedIcon from '@material-ui/icons/FileCopyOutlined' -import IconButton from '@material-ui/core/IconButton' -import { - createStyles, - withStyles, - WithStyles, - Theme, -} from '@material-ui/core/styles' -import Tooltip from '@material-ui/core/Tooltip' +import FileCopyOutlinedIcon from '@mui/icons-material/FileCopyOutlined' +import IconButton from '@mui/material/IconButton' +import { Theme } from '@mui/material/styles' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import { createStyles } from 'src/utils/withStyles' +import Tooltip from '@mui/material/Tooltip' const styles = (theme: Theme) => createStyles({ @@ -18,8 +16,8 @@ const styles = (theme: Theme) => width: 27, height: 27, minHeight: 27, - marginLeft: theme.spacing.unit, - marginRight: theme.spacing.unit, + marginLeft: theme.spacing(1), + marginRight: theme.spacing(1), }, icon: { fontSize: 18, @@ -35,7 +33,7 @@ export const CopyIconButton = withStyles(styles)(({ return ( setCopied(true)}> setCopied(false)}> - + diff --git a/src/components/D3Chart/D3Graph.test.tsx b/src/components/D3Chart/D3Graph.test.tsx index 37d34557..a8d655c4 100644 --- a/src/components/D3Chart/D3Graph.test.tsx +++ b/src/components/D3Chart/D3Graph.test.tsx @@ -1,20 +1,47 @@ -import { fireEvent, render } from '@testing-library/react' +import { fireEvent, render, waitFor } from '@testing-library/react' import React from 'react' import { D3Graph } from 'components/D3Chart/D3Graph' import { parseDot } from 'utils/parseDot' import { TaskRunStatus } from 'utils/taskRunStatus' +// Mock elkjs so layout resolves synchronously with stub positions, +// making tests fast and deterministic regardless of node count. +jest.mock('elkjs', () => + jest.fn().mockImplementation(() => ({ + layout: (graph: any) => + Promise.resolve({ + ...graph, + children: (graph.children ?? []).map((node: any) => ({ + ...node, + x: 0, + y: 0, + })), + }), + })), +) + +const waitForClassCount = async ( + container: HTMLElement, + className: string, + expectedCount: number, +) => { + await waitFor(() => { + expect(container.getElementsByClassName(className).length).toBe( + expectedCount, + ) + }) + + return container.getElementsByClassName(className) +} + describe('D3Graph test', () => { it('renders a single node', async () => { const nodeData = parseDot(`digraph {ds1 [type=bridge name=voter_turnout];}`) const { container } = render() - //Wait for render - await new Promise((r) => setTimeout(r, 500)) - - const ds1 = container - .getElementsByClassName('task-run-icon-pending') - .item(0) + const ds1 = ( + await waitForClassCount(container, 'task-run-icon-pending', 1) + ).item(0) expect(ds1).toHaveAttribute('id', 'pending-run-icon-ds1') }) @@ -27,11 +54,7 @@ describe('D3Graph test', () => { }`) const { container } = render() - //Wait for render - await new Promise((r) => setTimeout(r, 500)) - - const dss = container.getElementsByClassName('task-run-icon-pending') - expect(dss.length).toBe(5) + const dss = await waitForClassCount(container, 'task-run-icon-pending', 5) expect(dss.item(0)).toHaveAttribute('id', 'pending-run-icon-ds1') expect(dss.item(1)).toHaveAttribute('id', 'pending-run-icon-ds2') expect(dss.item(2)).toHaveAttribute('id', 'pending-run-icon-ds3') @@ -47,11 +70,11 @@ describe('D3Graph test', () => { const nodeData = parseDot(`digraph {${observationSoruce}}`) const { container } = render() - //Wait for render - await new Promise((r) => setTimeout(r, 500)) - - const dss = container.getElementsByClassName('task-run-icon-pending') - expect(dss.length).toBe(1000) + const dss = await waitForClassCount( + container, + 'task-run-icon-pending', + 1000, + ) for (let i = 0; i < 1000; i++) { expect(dss.item(i)).toHaveAttribute('id', `pending-run-icon-ds${i}`) @@ -66,11 +89,7 @@ describe('D3Graph test', () => { }`) const { container } = render() - //Wait for render - await new Promise((r) => setTimeout(r, 1000)) - - const dss = container.getElementsByClassName('task-run-icon-pending') - expect(dss.length).toBe(2) + const dss = await waitForClassCount(container, 'task-run-icon-pending', 2) expect(dss.item(0)).toHaveAttribute('id', `pending-run-icon-ds1`) expect(dss.item(1)).toHaveAttribute('id', `pending-run-icon-ds2`) @@ -89,11 +108,7 @@ describe('D3Graph test', () => { nodeData[3].attributes = { status: TaskRunStatus.UNKNOWN } const { container } = render() - //Wait for render - await new Promise((r) => setTimeout(r, 500)) - - const dss = container.getElementsByClassName('task-run-icon') - expect(dss.length).toBe(4) + const dss = await waitForClassCount(container, 'task-run-icon', 4) expect(dss.item(0)).toHaveAttribute('id', `success-run-icon-ds1`) expect(dss.item(1)).toHaveAttribute('id', `error-run-icon-ds2`) @@ -107,12 +122,9 @@ describe('D3Graph test', () => { ) const { container } = render() - //Wait for render - await new Promise((r) => setTimeout(r, 500)) - - const ds1 = container - .getElementsByClassName('task-run-icon-pending') - .item(0) + const ds1 = ( + await waitForClassCount(container, 'task-run-icon-pending', 1) + ).item(0) expect(ds1).toHaveAttribute('id', 'pending-run-icon-ds1') const tooltip = container.querySelector('#tooltip-d3-chart-ds1') diff --git a/src/components/D3Chart/D3Graph.tsx b/src/components/D3Chart/D3Graph.tsx index e6844555..1486c317 100644 --- a/src/components/D3Chart/D3Graph.tsx +++ b/src/components/D3Chart/D3Graph.tsx @@ -8,7 +8,7 @@ import { } from 'react-d3-graph' import { Stratify } from 'utils/parseDot' import { D3Tooltip } from 'components/D3Chart/D3GraphTooltip' -import { theme } from 'theme' +import { useTheme } from '@mui/material/styles' import ELK, { ElkNode } from 'elkjs' import { ElkExtendedEdge } from 'elkjs/lib/elk-api' import Pending from 'components/Icons/Pending' @@ -26,9 +26,11 @@ type D3GraphConfig = Partial< } export const D3Graph: React.FC = ({ nodesData }) => { + const theme = useTheme() + const fontColor = theme.palette.text.primary const [d3GraphData, setD3GraphData] = React.useState< GraphData - >({} as GraphData) + >({ nodes: [], links: [] }) React.useEffect(() => { const getNode = (nodeID: string): Stratify | undefined => { @@ -129,7 +131,7 @@ export const D3Graph: React.FC = ({ nodesData }) => { .then((data) => { graphData.nodes = [] data.children?.map((n) => { - if (!n.x || !n.y) { + if (n.x == null || n.y == null) { return } // @ts-expect-error type missmatch @@ -166,7 +168,7 @@ export const D3Graph: React.FC = ({ nodesData }) => { highlightOpacity: 1, link: { color: '#d3d3d3', - fontColor: 'black', + fontColor, fontSize: 12, fontWeight: 'normal', highlightColor: 'blue', @@ -184,7 +186,7 @@ export const D3Graph: React.FC = ({ nodesData }) => { minZoom: 0.3, node: { color: '#d3d3d3', - fontColor: 'black', + fontColor, fontSize: 14, fontWeight: 'normal', highlightColor: 'red', @@ -261,15 +263,15 @@ export const D3Graph: React.FC = ({ nodesData }) => { return (
- - D3 chart - + {d3GraphData.nodes.length > 0 && ( + + )} {nodesData.map((n) => { return (
= ({ nodesData }) => { style={{ position: 'absolute', opacity: '0', - border: '1px solid rgba(0, 0, 0, 0.1)', - padding: theme.spacing.unit, - background: 'white', + border: `1px solid ${theme.palette.divider}`, + padding: theme.spacing(1), + background: theme.palette.background.paper, + color: theme.palette.text.primary, borderRadius: 5, zIndex: -1, inlineSize: 'min-content', diff --git a/src/components/D3Chart/D3GraphTooltip.tsx b/src/components/D3Chart/D3GraphTooltip.tsx index 8100cc2e..9bf681da 100644 --- a/src/components/D3Chart/D3GraphTooltip.tsx +++ b/src/components/D3Chart/D3GraphTooltip.tsx @@ -1,6 +1,6 @@ import React from 'react' import { Stratify } from 'utils/parseDot' -import Typography from '@material-ui/core/Typography' +import Typography from '@mui/material/Typography' interface Props { data: Stratify diff --git a/src/components/Dialogs/ConfirmationDialog.tsx b/src/components/Dialogs/ConfirmationDialog.tsx index 426ab492..228fc175 100644 --- a/src/components/Dialogs/ConfirmationDialog.tsx +++ b/src/components/Dialogs/ConfirmationDialog.tsx @@ -1,26 +1,27 @@ import React from 'react' -import Button from '@material-ui/core/Button' -import Dialog, { DialogProps } from '@material-ui/core/Dialog' -import MuiDialogActions from '@material-ui/core/DialogActions' -import DialogContent from '@material-ui/core/DialogContent' -import DialogContentText from '@material-ui/core/DialogContentText' -import MuiDialogTitle from '@material-ui/core/DialogTitle' -import { withStyles } from '@material-ui/core/styles' -import Typography from '@material-ui/core/Typography' +import Button from '@mui/material/Button' +import Dialog, { DialogProps } from '@mui/material/Dialog' +import MuiDialogActions from '@mui/material/DialogActions' +import DialogContent from '@mui/material/DialogContent' +import DialogContentText from '@mui/material/DialogContentText' +import MuiDialogTitle from '@mui/material/DialogTitle' +import { Theme } from '@mui/material/styles' +import { withStyles } from 'src/utils/withStyles' +import Typography from '@mui/material/Typography' -const DialogTitle = withStyles((theme) => ({ +const DialogTitle = withStyles((theme: Theme) => ({ root: { - paddingTop: theme.spacing.unit * 3, - paddingBottom: theme.spacing.unit * 3, + paddingTop: theme.spacing(3), + paddingBottom: theme.spacing(3), }, }))(MuiDialogTitle) -const DialogActions = withStyles((theme) => ({ +const DialogActions = withStyles((theme: Theme) => ({ root: { - paddingLeft: theme.spacing.unit * 2, - paddingRight: theme.spacing.unit * 2, - paddingBottom: theme.spacing.unit * 2, + paddingLeft: theme.spacing(2), + paddingRight: theme.spacing(2), + paddingBottom: theme.spacing(2), display: 'flex', flexDirection: 'row', alignItems: 'center', @@ -51,7 +52,7 @@ export const ConfirmationDialog: React.FC = ({ }) => { return ( - + {title} diff --git a/src/components/ElapsedDuration.tsx b/src/components/ElapsedDuration.tsx index e46c6b22..287fa4da 100644 --- a/src/components/ElapsedDuration.tsx +++ b/src/components/ElapsedDuration.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { Typography } from '@material-ui/core' +import { Typography } from '@mui/material' const MS_IN_SECOND = 1000 const SECONDS_IN_MINUTE = 60 diff --git a/src/components/Feedback/Loading.tsx b/src/components/Feedback/Loading.tsx index 8ef5811c..4219161d 100644 --- a/src/components/Feedback/Loading.tsx +++ b/src/components/Feedback/Loading.tsx @@ -1,12 +1,15 @@ import React from 'react' -import CircularProgress from '@material-ui/core/CircularProgress' -import Grid from '@material-ui/core/Grid' -import { Theme, withStyles, WithStyles } from '@material-ui/core/styles' +import CircularProgress from '@mui/material/CircularProgress' +import Grid from '@mui/material/Grid' +import { Theme } from '@mui/material/styles' + +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' const styles = (theme: Theme) => ({ root: { - margin: theme.spacing.unit * 2, + margin: theme.spacing(2), }, gridItem: { display: 'flex', diff --git a/src/components/Flash.js b/src/components/Flash.js index 8de32585..83995bd2 100644 --- a/src/components/Flash.js +++ b/src/components/Flash.js @@ -1,9 +1,9 @@ import React from 'react' import PropTypes from 'prop-types' -import Card from '@material-ui/core/Card' -import Typography from '@material-ui/core/Typography' +import Card from '@mui/material/Card' +import Typography from '@mui/material/Typography' import classNames from 'classnames' -import { withStyles } from '@material-ui/core/styles' +import { withStyles } from 'src/utils/withStyles' const styles = (theme) => { const success = theme.palette.success || {} @@ -11,8 +11,8 @@ const styles = (theme) => { return { base: { - paddingLeft: theme.spacing.unit * 5, - paddingRight: theme.spacing.unit * 5, + paddingLeft: theme.spacing(5), + paddingRight: theme.spacing(5), }, success: { backgroundColor: success.main, @@ -58,6 +58,7 @@ Flash.defaultProps = { } Flash.propTypes = { + children: PropTypes.node, success: PropTypes.bool, error: PropTypes.bool, warning: PropTypes.bool, diff --git a/src/components/Form/BridgeForm.tsx b/src/components/Form/BridgeForm.tsx index 4da6b941..2c270252 100644 --- a/src/components/Form/BridgeForm.tsx +++ b/src/components/Form/BridgeForm.tsx @@ -1,11 +1,11 @@ import React from 'react' import { Field, Form, Formik, FormikHelpers } from 'formik' -import { TextField } from 'formik-material-ui' +import { FormikTextField as TextField } from './FormikFields' import * as Yup from 'yup' -import Button from '@material-ui/core/Button' -import Grid from '@material-ui/core/Grid' +import Button from '@mui/material/Button' +import Grid from '@mui/material/Grid' export interface FormValues { name: string @@ -44,7 +44,7 @@ export const BridgeForm = ({ {({ isSubmitting }) => ( <>
- + - + { const initialValues = emptyFormValues() initialValues.chainType = ChainTypes.EVM - const { container } = render( + render( { 'Required', ) - await selectChainIdOnUI(container, '1111') + await selectChainIdOnUI('1111') userEvent.click(getByRole('button', { name: /submit/i })) @@ -102,25 +102,17 @@ describe('ChainConfigurationForm', () => { />, ) - userEvent.click(getByRole('checkbox', { name: 'OCR' })) - userEvent.click(getByRole('button', { name: /submit/i })) - - expect(await findByTestId('ocr1P2PPeerID-helper-text')).toHaveTextContent( - 'Required', - ) - expect(await findByTestId('ocr1KeyBundleID-helper-text')).toHaveTextContent( - 'Required', - ) + // Enable OCR + await userEvent.click(getByRole('checkbox', { name: 'OCR' })) + await waitFor(() => { + expect(getByRole('combobox', { name: /peer id/i })).toBeInTheDocument() + }) - userEvent.click( - getByRole('checkbox', { - name: 'Is this node running as a bootstrap peer?', - }), - ) + // Try to submit - should fail because required OCR fields are empty + await userEvent.click(getByRole('button', { name: /submit/i })) - expect(await findByTestId('ocr1Multiaddr-helper-text')).toHaveTextContent( - 'Required', - ) + // Submit handler should not be called due to validation failure + expect(handleSubmit).not.toHaveBeenCalled() }) it('validates OCR2 input', async () => { @@ -140,29 +132,17 @@ describe('ChainConfigurationForm', () => { />, ) - userEvent.click(getByRole('checkbox', { name: 'OCR2' })) - userEvent.click(getByRole('button', { name: /submit/i })) - - expect(await findByTestId('ocr2P2PPeerID-helper-text')).toHaveTextContent( - 'Required', - ) - expect(await findByTestId('ocr2KeyBundleID-helper-text')).toHaveTextContent( - 'Required', - ) - - userEvent.click( - getByRole('checkbox', { - name: 'Is this node running as a bootstrap peer?', - }), - ) + // Enable OCR2 + await userEvent.click(getByRole('checkbox', { name: 'OCR2' })) + await waitFor(() => { + expect(getByRole('combobox', { name: /peer id/i })).toBeInTheDocument() + }) - expect(await findByTestId('ocr2Multiaddr-helper-text')).toHaveTextContent( - 'Required', - ) + // Try to submit - should fail because required OCR2 fields are empty + await userEvent.click(getByRole('button', { name: /submit/i })) - expect( - await findByTestId('ocr2P2PPeerID-helper-text'), - ).not.toHaveTextContent('Required') + // Submit handler should not be called due to validation failure + expect(handleSubmit).not.toHaveBeenCalled() }) test('should able to create APTOS chain config (with selection)', async () => { @@ -171,23 +151,13 @@ describe('ChainConfigurationForm', () => { initialValues.chainType = ChainTypes.EVM initialValues.adminAddr = '0x1234567' - const { container } = renderChainConfigurationForm( - initialValues, - handleSubmit, - ) + renderChainConfigurationForm(initialValues, handleSubmit) - const chainType = getByRole('button', { name: 'EVM' }) - userEvent.click(chainType) - userEvent.click(getByRole('option', { name: 'APTOS' })) - await screen.findByRole('button', { name: 'APTOS' }) + await selectOptionOnUI(/chain type/i, 'APTOS') - await selectChainIdOnUI(container, '2222') + await selectChainIdOnUI('2222') - const address = container.querySelector('#select-accountAddr') - expect(address).toBeInTheDocument() - if (address) userEvent.click(address) - userEvent.click(getByRole('option', { name: '0x123' })) - await screen.findByRole('button', { name: '0x123' }) + await selectOptionOnUI(/^account address$/i, '0x123') await userEvent.click(getByRole('button', { name: /submit/i })) @@ -246,16 +216,13 @@ describe('ChainConfigurationForm', () => { }, }) - const chainType = getByRole('button', { name: 'EVM' }) - userEvent.click(chainType) - userEvent.click(getByRole('option', { name: 'APTOS' })) - await screen.findByRole('button', { name: 'APTOS' }) + await selectOptionOnUI(/chain type/i, 'APTOS') - const chainIdTextBox = getByRole('textbox', { name: /chain id \*/i }) + const chainIdTextBox = getByRole('textbox', { name: /chain id/i }) userEvent.type(chainIdTextBox, '2222') const accountAddrTextBox = getByRole('textbox', { - name: /account address \*/i, + name: /account address/i, }) userEvent.type(accountAddrTextBox, '0x123') @@ -296,23 +263,13 @@ describe('ChainConfigurationForm', () => { initialValues.chainType = ChainTypes.EVM initialValues.adminAddr = '0x1234567' - const { container } = renderChainConfigurationForm( - initialValues, - handleSubmit, - ) + renderChainConfigurationForm(initialValues, handleSubmit) - const chainType = getByRole('button', { name: 'EVM' }) - userEvent.click(chainType) - userEvent.click(getByRole('option', { name: 'SUI' })) - await screen.findByRole('button', { name: 'SUI' }) + await selectOptionOnUI(/chain type/i, 'SUI') - await selectChainIdOnUI(container, '6666') + await selectChainIdOnUI('6666') - const address = container.querySelector('#select-accountAddr') - expect(address).toBeInTheDocument() - if (address) userEvent.click(address) - userEvent.click(getByRole('option', { name: '0x123' })) - await screen.findByRole('button', { name: '0x123' }) + await selectOptionOnUI(/^account address$/i, '0x123') await userEvent.click(getByRole('button', { name: /submit/i })) @@ -371,16 +328,13 @@ describe('ChainConfigurationForm', () => { }, }) - const chainType = getByRole('button', { name: 'EVM' }) - userEvent.click(chainType) - userEvent.click(getByRole('option', { name: 'SUI' })) - await screen.findByRole('button', { name: 'SUI' }) + await selectOptionOnUI(/chain type/i, 'SUI') - const chainIdTextBox = getByRole('textbox', { name: /chain id \*/i }) + const chainIdTextBox = getByRole('textbox', { name: /chain id/i }) userEvent.type(chainIdTextBox, '6666') const accountAddrTextBox = getByRole('textbox', { - name: /account address \*/i, + name: /account address/i, }) userEvent.type(accountAddrTextBox, '0x123') @@ -421,23 +375,13 @@ describe('ChainConfigurationForm', () => { initialValues.chainType = ChainTypes.EVM initialValues.adminAddr = '0x1234567' - const { container } = renderChainConfigurationForm( - initialValues, - handleSubmit, - ) + renderChainConfigurationForm(initialValues, handleSubmit) - const chainType = getByRole('button', { name: 'EVM' }) - userEvent.click(chainType) - userEvent.click(getByRole('option', { name: 'SOLANA' })) - await screen.findByRole('button', { name: 'SOLANA' }) + await selectOptionOnUI(/chain type/i, 'SOLANA') - await selectChainIdOnUI(container, '3333') + await selectChainIdOnUI('3333') - const address = container.querySelector('#select-accountAddr') - expect(address).toBeInTheDocument() - if (address) userEvent.click(address) - userEvent.click(getByRole('option', { name: 'solana_xxxx' })) - await screen.findByRole('button', { name: 'solana_xxxx' }) + await selectOptionOnUI(/^account address$/i, 'solana_xxxx') await userEvent.click(getByRole('button', { name: /submit/i })) @@ -477,23 +421,13 @@ test('should able to create Tron chain config', async () => { initialValues.chainType = ChainTypes.EVM initialValues.adminAddr = '0x1234567' - const { container } = renderChainConfigurationForm( - initialValues, - handleSubmit, - ) + renderChainConfigurationForm(initialValues, handleSubmit) - const chainType = getByRole('button', { name: 'EVM' }) - userEvent.click(chainType) - userEvent.click(getByRole('option', { name: 'TRON' })) - await screen.findByRole('button', { name: 'TRON' }) + await selectOptionOnUI(/chain type/i, 'TRON') - await selectChainIdOnUI(container, '4444') + await selectChainIdOnUI('4444') - const address = container.querySelector('#select-accountAddr') - expect(address).toBeInTheDocument() - if (address) userEvent.click(address) - userEvent.click(getByRole('option', { name: 'tron_xxxx' })) - await screen.findByRole('button', { name: 'tron_xxxx' }) + await selectOptionOnUI(/^account address$/i, 'tron_xxxx') await userEvent.click(getByRole('button', { name: /submit/i })) @@ -532,23 +466,13 @@ test('should able to create TON chain config', async () => { initialValues.chainType = ChainTypes.EVM initialValues.adminAddr = '0x1234567' - const { container } = renderChainConfigurationForm( - initialValues, - handleSubmit, - ) + renderChainConfigurationForm(initialValues, handleSubmit) - const chainType = getByRole('button', { name: 'EVM' }) - userEvent.click(chainType) - userEvent.click(getByRole('option', { name: 'TON' })) - await screen.findByRole('button', { name: 'TON' }) + await selectOptionOnUI(/chain type/i, 'TON') - await selectChainIdOnUI(container, '5555') + await selectChainIdOnUI('5555') - const address = container.querySelector('#select-accountAddr') - expect(address).toBeInTheDocument() - if (address) userEvent.click(address) - userEvent.click(getByRole('option', { name: '123' })) - await screen.findByRole('button', { name: '123' }) + await selectOptionOnUI(/^account address$/i, '123') await userEvent.click(getByRole('button', { name: /submit/i })) @@ -586,41 +510,31 @@ test('should be able to select OCR2 Job Type with Key Bundle ID', async () => { const initialValues = emptyFormValues() initialValues.chainType = ChainTypes.EVM - const { container } = renderChainConfigurationForm( - initialValues, - handleSubmit, - [], - { - aptosKeys: { - results: [], - }, - solanaKeys: { - results: [], - }, - starknetKeys: { - results: [], - }, - tronKeys: { - results: [], - }, - tonKeys: { - results: [], - }, - suiKeys: { - results: [], - }, + renderChainConfigurationForm(initialValues, handleSubmit, [], { + aptosKeys: { + results: [], }, - ) + solanaKeys: { + results: [], + }, + starknetKeys: { + results: [], + }, + tronKeys: { + results: [], + }, + tonKeys: { + results: [], + }, + suiKeys: { + results: [], + }, + }) const ocr2CheckBox = screen.getByText(/ocr2/i) userEvent.click(ocr2CheckBox) - const keyBundleId2 = container.querySelector('#select-ocr2KeyBundleID') - expect(keyBundleId2).toBeInTheDocument() - // workaround ts lint warning - require check for null - if (keyBundleId2) userEvent.click(keyBundleId2) - userEvent.click(getByRole('option', { name: 'ocr2_key_bundle_id (EVM)' })) - await screen.findByRole('button', { name: 'ocr2_key_bundle_id (EVM)' }) + await selectOptionOnUI(/key bundle id/i, 'ocr2_key_bundle_id (EVM)') }) function emptyFormValues(): FormValues { @@ -739,11 +653,14 @@ function renderChainConfigurationForm( ) } -async function selectChainIdOnUI(container: HTMLElement, chainId: string) { - const chainIdSelect = container.querySelector('#select-chainID') - expect(chainIdSelect).toBeInTheDocument() - // workaround ts lint warning - require check for null - if (chainIdSelect) userEvent.click(chainIdSelect) - userEvent.click(getByRole('option', { name: chainId })) - await screen.findByRole('button', { name: chainId }) +async function selectChainIdOnUI(chainId: string) { + await selectOptionOnUI(/chain id/i, chainId) +} + +async function selectOptionOnUI(label: RegExp, optionName: string) { + userEvent.click(getByRole('combobox', { name: label })) + userEvent.click(getByRole('option', { name: optionName })) + await waitFor(() => { + expect(getByRole('combobox', { name: label })).toHaveTextContent(optionName) + }) } diff --git a/src/components/Form/ChainConfigurationForm.tsx b/src/components/Form/ChainConfigurationForm.tsx index fdab15bb..09ea90f1 100644 --- a/src/components/Form/ChainConfigurationForm.tsx +++ b/src/components/Form/ChainConfigurationForm.tsx @@ -7,20 +7,21 @@ import { FormikHelpers, useFormikContext, } from 'formik' -import { CheckboxWithLabel, TextField } from 'formik-material-ui' +import { + FormikCheckboxWithLabel as CheckboxWithLabel, + FormikTextField as TextField, +} from './FormikFields' import * as Yup from 'yup' -import Button from '@material-ui/core/Button' -import Grid from '@material-ui/core/Grid' -import MenuItem from '@material-ui/core/MenuItem' -import Paper from '@material-ui/core/Paper' -import { - createStyles, - Theme, - WithStyles, - withStyles, -} from '@material-ui/core/styles' -import Typography from '@material-ui/core/Typography' +import Button from '@mui/material/Button' +import Grid from '@mui/material/Grid' +import MenuItem from '@mui/material/MenuItem' +import Paper from '@mui/material/Paper' +import { Theme } from '@mui/material/styles' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import { createStyles } from 'src/utils/withStyles' +import Typography from '@mui/material/Typography' import { ChainTypes } from './ChainTypes' export type FormValues = { @@ -100,7 +101,7 @@ const ValidationSchema = Yup.object().shape({ const styles = (theme: Theme) => { return createStyles({ supportedJobOptionsPaper: { - padding: theme.spacing.unit * 2, + padding: theme.spacing(2), }, }) } @@ -372,7 +373,7 @@ export const ChainConfigurationForm = withStyles(styles)(({ id="chain-configuration-form" noValidate > - + - + <> - + <> { ) userEvent.type( - getByRole('textbox', { name: 'Name *' }), + getByRole('textbox', { name: /^name$/i }), 'Chainlink Feeds Manager', ) - userEvent.type(getByRole('textbox', { name: 'URI *' }), 'localhost:8080') - userEvent.type(getByRole('textbox', { name: 'Public Key *' }), '11111') + userEvent.type(getByRole('textbox', { name: /^uri$/i }), 'localhost:8080') + userEvent.type(getByRole('textbox', { name: /^public key$/i }), '11111') userEvent.click(getByText(/submit/i)) diff --git a/src/components/Form/FeedsManagerForm.tsx b/src/components/Form/FeedsManagerForm.tsx index 1b05e04d..6f2dadad 100644 --- a/src/components/Form/FeedsManagerForm.tsx +++ b/src/components/Form/FeedsManagerForm.tsx @@ -1,10 +1,10 @@ import React from 'react' import { Field, Form, Formik, FormikHelpers } from 'formik' -import { TextField } from 'formik-material-ui' +import { FormikTextField as TextField } from './FormikFields' import * as Yup from 'yup' -import Button from '@material-ui/core/Button' -import Grid from '@material-ui/core/Grid' +import Button from '@mui/material/Button' +import Grid from '@mui/material/Grid' export type FormValues = { name: string @@ -38,7 +38,7 @@ export const FeedsManagerForm: React.FC = ({ > {({ isSubmitting, submitForm }) => ( - + { + const theme = useTheme() + const touched = getIn(form.touched, field.name) + const error = getIn(form.errors, field.name) + // Show error if field is touched, OR show all errors after form submission + const showError = Boolean( + (touched || (form.submitCount && form.submitCount > 0)) && error, + ) + + const maybeMaxRowsProps = + props.rows || props.maxRows || rowsMax === undefined + ? {} + : { maxRows: props.maxRows ?? rowsMax } + + const multilineProps = props.multiline + ? { + InputProps: { + ...props.InputProps, + sx: { + alignItems: 'flex-start', + backgroundColor: theme.palette.background.paper, + color: theme.palette.text.primary, + '& textarea': { + color: theme.palette.text.primary, + resize: 'vertical', + }, + ...(props.InputProps?.sx as object), + }, + }, + } + : {} + + // Determine what helper text to show + const finalHelperText = showError ? error : helperText + + return ( + + {children} + + ) +} + +type FormikCheckboxWithLabelProps = FieldProps & + Omit & { + Label: Omit + } + +export const FormikCheckboxWithLabel = ({ + field, + Label, + ...props +}: FormikCheckboxWithLabelProps) => { + return ( + + } + /> + ) +} diff --git a/src/components/Form/JobForm.test.tsx b/src/components/Form/JobForm.test.tsx index 794a786c..ab2af6d6 100644 --- a/src/components/Form/JobForm.test.tsx +++ b/src/components/Form/JobForm.test.tsx @@ -48,7 +48,7 @@ describe('BridgeForm', () => { renderComponent({ toml: '' }) userEvent.paste( - getByRole('textbox', { name: /job spec \(toml\) \*/i }), + getByRole('textbox', { name: /job spec \(toml\)/i }), 'type = "webhook"', ) @@ -68,7 +68,7 @@ describe('BridgeForm', () => { renderComponent({ toml: '' }) userEvent.paste( - getByRole('textbox', { name: /job spec \(toml\) \*/i }), + getByRole('textbox', { name: /job spec \(toml\)/i }), 'type = "webhook"', ) diff --git a/src/components/Form/JobForm.tsx b/src/components/Form/JobForm.tsx index 5dda7dd2..2ed490fc 100644 --- a/src/components/Form/JobForm.tsx +++ b/src/components/Form/JobForm.tsx @@ -2,11 +2,11 @@ import React from 'react' import TOML from '@iarna/toml' import { Field, Form, Formik, FormikHelpers } from 'formik' -import { TextField } from 'formik-material-ui' +import { FormikTextField as TextField } from './FormikFields' import * as Yup from 'yup' -import Button from '@material-ui/core/Button' -import Grid from '@material-ui/core/Grid' +import Button from '@mui/material/Button' +import Grid from '@mui/material/Grid' export interface FormValues { toml: string @@ -49,7 +49,7 @@ export const JobForm = ({ initialValues, onSubmit, onTOMLChange }: Props) => { return ( - + { required fullWidth multiline - rows={10} - rowsMax={25} + minRows={10} + maxRows={25} variant="outlined" autoComplete="off" FormHelperTextProps={{ 'data-testid': 'toml-helper-text' }} diff --git a/src/components/Heading/Heading1.tsx b/src/components/Heading/Heading1.tsx index 0d23d768..ea5a5690 100644 --- a/src/components/Heading/Heading1.tsx +++ b/src/components/Heading/Heading1.tsx @@ -1,7 +1,9 @@ import React from 'react' -import { createStyles, withStyles, WithStyles } from '@material-ui/core/styles' -import Typography from '@material-ui/core/Typography' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import { createStyles } from 'src/utils/withStyles' +import Typography from '@mui/material/Typography' const styles = () => { return createStyles({ diff --git a/src/components/Heading/Heading2.tsx b/src/components/Heading/Heading2.tsx index 92b5ce3d..7bbfdda1 100644 --- a/src/components/Heading/Heading2.tsx +++ b/src/components/Heading/Heading2.tsx @@ -1,7 +1,9 @@ import React from 'react' -import { createStyles, withStyles, WithStyles } from '@material-ui/core/styles' -import Typography from '@material-ui/core/Typography' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import { createStyles } from 'src/utils/withStyles' +import Typography from '@mui/material/Typography' const styles = () => { return createStyles({ diff --git a/src/components/Link.tsx b/src/components/Link.tsx index daae20b3..30b0da91 100644 --- a/src/components/Link.tsx +++ b/src/components/Link.tsx @@ -1,19 +1,20 @@ import React from 'react' import BaseLink from './BaseLink' -import { createStyles, withStyles, WithStyles } from '@material-ui/core/styles' -import Typography from '@material-ui/core/Typography' -import { grey } from '@material-ui/core/colors' -import { ThemeStyle } from '@material-ui/core/styles/createTypography' -import { PropTypes } from '@material-ui/core' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import { createStyles } from 'src/utils/withStyles' +import Typography from '@mui/material/Typography' +import { TypographyProps } from '@mui/material/Typography' +import { Theme } from '@mui/material/styles' import classNames from 'classnames' -type Variant = ThemeStyle | 'srOnly' -type Color = PropTypes.Color | 'textPrimary' | 'textSecondary' | 'error' +type Variant = TypographyProps['variant'] +type Color = TypographyProps['color'] -const styles = () => +const styles = (theme: Theme) => createStyles({ link: { - color: grey[900], + color: theme.palette.text.primary, textDecoration: 'none', }, linkContent: { diff --git a/src/components/Loading.js b/src/components/Loading.js index c66b3582..fad579a6 100644 --- a/src/components/Loading.js +++ b/src/components/Loading.js @@ -1,11 +1,11 @@ import React from 'react' -import Grid from '@material-ui/core/Grid' -import Typography from '@material-ui/core/Typography' -import { withStyles } from '@material-ui/core/styles' +import Grid from '@mui/material/Grid' +import Typography from '@mui/material/Typography' +import { withStyles } from 'src/utils/withStyles' const styles = (theme) => ({ wrapper: { - marginTop: theme.spacing.unit * 5, + marginTop: theme.spacing(5), }, text: { textAlign: 'center', diff --git a/src/components/LoadingBar.js b/src/components/LoadingBar.js index 5e9c62b2..646af061 100644 --- a/src/components/LoadingBar.js +++ b/src/components/LoadingBar.js @@ -1,7 +1,7 @@ import React from 'react' import PropTypes from 'prop-types' -import LinearProgress from '@material-ui/core/LinearProgress' -import { withStyles } from '@material-ui/core/styles' +import LinearProgress from '@mui/material/LinearProgress' +import { withStyles } from 'src/utils/withStyles' const styles = (theme) => ({ root: { diff --git a/src/components/Logo.tsx b/src/components/Logo.tsx index 70cf549a..1ac15577 100644 --- a/src/components/Logo.tsx +++ b/src/components/Logo.tsx @@ -1,5 +1,7 @@ import React from 'react' -import { createStyles, withStyles, WithStyles } from '@material-ui/core/styles' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import { createStyles } from 'src/utils/withStyles' const styles = createStyles({ animate: { diff --git a/src/components/MenuItemLink.tsx b/src/components/MenuItemLink.tsx index 786d420a..4edacda8 100644 --- a/src/components/MenuItemLink.tsx +++ b/src/components/MenuItemLink.tsx @@ -1,6 +1,6 @@ import React from 'react' import { Link } from 'react-router-dom' -import MenuItem, { MenuItemProps } from '@material-ui/core/MenuItem' +import MenuItem, { MenuItemProps } from '@mui/material/MenuItem' interface MenuItemLinkProps extends MenuItemProps { to: string @@ -8,9 +8,8 @@ interface MenuItemLinkProps extends MenuItemProps { } export const MenuItemLink = (props: MenuItemLinkProps) => { - return ( - - {props.children} - - ) + const { to, replace, children, ...rest } = props + const linkProps = { ...rest, component: Link as any, to, replace } as any + + return {children} } diff --git a/src/components/PageHeader.tsx b/src/components/PageHeader.tsx new file mode 100644 index 00000000..7a1e3c21 --- /dev/null +++ b/src/components/PageHeader.tsx @@ -0,0 +1,52 @@ +import React from 'react' + +import { Theme } from '@mui/material/styles' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import { createStyles } from 'src/utils/withStyles' + +import { Heading1 } from 'src/components/Heading/Heading1' + +const styles = (theme: Theme) => + createStyles({ + root: { + minHeight: theme.spacing(6), + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + gap: theme.spacing(2), + width: '100%', + }, + title: { + minWidth: 0, + flex: 1, + }, + actions: { + display: 'flex', + alignItems: 'center', + justifyContent: 'flex-end', + flexShrink: 0, + whiteSpace: 'nowrap', + }, + }) + +interface Props extends WithStyles { + title: React.ReactNode + actions?: React.ReactNode +} + +export const PageHeader = withStyles(styles)(({ + actions, + classes, + title, +}: Props) => { + return ( +
+
+ {title} +
+ + {actions &&
{actions}
} +
+ ) +}) diff --git a/src/components/Search/SearchTextField.test.tsx b/src/components/Search/SearchTextField.test.tsx index 73a8d818..9b2362b7 100644 --- a/src/components/Search/SearchTextField.test.tsx +++ b/src/components/Search/SearchTextField.test.tsx @@ -1,6 +1,6 @@ import React from 'react' -import { render, screen } from '@testing-library/react' +import { render, screen } from 'support/test-utils' import userEvent from '@testing-library/user-event' import { SearchTextField } from './SearchTextField' diff --git a/src/components/Search/SearchTextField.tsx b/src/components/Search/SearchTextField.tsx index e8d04733..dad6174f 100644 --- a/src/components/Search/SearchTextField.tsx +++ b/src/components/Search/SearchTextField.tsx @@ -1,22 +1,20 @@ import React from 'react' -import Grid from '@material-ui/core/Grid' -import { - createStyles, - Theme, - WithStyles, - withStyles, -} from '@material-ui/core/styles' -import TextField from '@material-ui/core/TextField' +import Grid from '@mui/material/Grid' +import { Theme } from '@mui/material/styles' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import { createStyles } from 'src/utils/withStyles' +import TextField from '@mui/material/TextField' const styles = (theme: Theme) => { return createStyles({ textField: { - marginBottom: theme.spacing.unit * 2, + marginBottom: theme.spacing(2), }, input: { padding: 14, - backgroundColor: 'white', + backgroundColor: theme.palette.background.paper, borderRadius: 4, }, }) @@ -35,7 +33,7 @@ export const SearchTextField = withStyles(styles)(({ value, }: Props) => { return ( - + { + beforeEach(() => { + localStorage.clear() + }) + + it('persists dark mode preference when toggled', () => { + renderWithRouter() + + userEvent.click(screen.getByRole('button')) + userEvent.click(screen.getByRole('menuitem', { name: /dark mode/i })) + + expect(localStorage.getItem('chainlink.themeMode')).toBe('dark') + }) +}) diff --git a/src/components/SettingsMenu.tsx b/src/components/SettingsMenu.tsx index 5396266c..14663f85 100644 --- a/src/components/SettingsMenu.tsx +++ b/src/components/SettingsMenu.tsx @@ -1,11 +1,18 @@ import React from 'react' -import IconButton from '@material-ui/core/IconButton' -import Menu from '@material-ui/core/Menu' -import SettingsIcon from '@material-ui/icons/Settings' -import { Theme, withStyles, WithStyles } from '@material-ui/core/styles' +import IconButton from '@mui/material/IconButton' +import Menu from '@mui/material/Menu' +import MenuItem from '@mui/material/MenuItem' +import ListItemText from '@mui/material/ListItemText' +import Switch from '@mui/material/Switch' +import SettingsIcon from '@mui/icons-material/Settings' +import { Theme } from '@mui/material/styles' + +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' import { MenuItemLink } from 'components/MenuItemLink' +import { useThemeMode } from 'src/context/ThemeModeContext' const styles = (theme: Theme) => { return { @@ -17,6 +24,9 @@ const styles = (theme: Theme) => { paddingTop: 0, paddingBottom: 0, }, + menuItem: { + minHeight: theme.spacing(6), + }, } } @@ -24,6 +34,8 @@ interface Props extends WithStyles {} export const SettingsMenu = withStyles(styles)(({ classes }: Props) => { const [anchorEl, setAnchorEl] = React.useState(null) + const { mode, toggleMode } = useThemeMode() + const isDarkMode = mode === 'dark' const handleOpen = (event: React.MouseEvent) => { setAnchorEl(event.currentTarget) @@ -33,16 +45,19 @@ export const SettingsMenu = withStyles(styles)(({ classes }: Props) => { setAnchorEl(null) } + const handleToggleMode = () => { + toggleMode() + handleClose() + } + return ( - + - { className: classes.menuList, }} > - + + Dark mode + + + Key Management - + Configuration diff --git a/src/components/Tab/TabLink.tsx b/src/components/Tab/TabLink.tsx index 3f5ef893..6b70efcd 100644 --- a/src/components/Tab/TabLink.tsx +++ b/src/components/Tab/TabLink.tsx @@ -1,6 +1,6 @@ import React from 'react' import { Link } from 'react-router-dom' -import Tab, { TabProps } from '@material-ui/core/Tab' +import Tab, { TabProps } from '@mui/material/Tab' interface TabLinkProps extends TabProps { to: string diff --git a/src/components/Table.ts b/src/components/Table.ts index 74270cb1..7b24dd0a 100644 --- a/src/components/Table.ts +++ b/src/components/Table.ts @@ -1,4 +1,6 @@ -import { createStyles, Theme } from '@material-ui/core/styles' +import { Theme } from '@mui/material/styles' + +import { createStyles } from 'src/utils/withStyles' // Contains styles to make a table row linkable // @@ -10,8 +12,8 @@ import { createStyles, Theme } from '@material-ui/core/styles' export const tableStyles = (theme: Theme) => createStyles({ cell: { - paddingTop: theme.spacing.unit * 2, - paddingBottom: theme.spacing.unit * 2, + paddingTop: theme.spacing(2), + paddingBottom: theme.spacing(2), }, row: { transform: 'scale(1)', diff --git a/src/components/Table/JobRunsTable.tsx b/src/components/Table/JobRunsTable.tsx index 3b12de6a..29370b10 100644 --- a/src/components/Table/JobRunsTable.tsx +++ b/src/components/Table/JobRunsTable.tsx @@ -3,12 +3,14 @@ import React from 'react' import classNames from 'classnames' import { useHistory } from 'react-router-dom' -import { createStyles, withStyles, WithStyles } from '@material-ui/core/styles' -import Table from '@material-ui/core/Table' -import TableBody from '@material-ui/core/TableBody' -import TableCell from '@material-ui/core/TableCell' -import TableRow from '@material-ui/core/TableRow' -import Typography from '@material-ui/core/Typography' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import { createStyles } from 'src/utils/withStyles' +import Table from '@mui/material/Table' +import TableBody from '@mui/material/TableBody' +import TableCell from '@mui/material/TableCell' +import TableRow from '@mui/material/TableRow' +import Typography from '@mui/material/Typography' import { JobRunStatus } from 'src/types/generated/graphql' import { TimeAgo } from 'components/TimeAgo' @@ -29,20 +31,20 @@ const styles = (theme: any) => width: '30%', }, runDetails: { - paddingTop: theme.spacing.unit * 2, - paddingBottom: theme.spacing.unit * 2, - paddingLeft: theme.spacing.unit * 2, + paddingTop: theme.spacing(2), + paddingBottom: theme.spacing(2), + paddingLeft: theme.spacing(2), }, stamp: { - paddingLeft: theme.spacing.unit, + paddingLeft: theme.spacing(1), }, status: { - paddingLeft: theme.spacing.unit * 1.5, - paddingRight: theme.spacing.unit * 1.5, - paddingTop: theme.spacing.unit / 2, - paddingBottom: theme.spacing.unit / 2, - borderRadius: theme.spacing.unit * 2, - marginRight: theme.spacing.unit, + paddingLeft: theme.spacing(1.5), + paddingRight: theme.spacing(1.5), + paddingTop: theme.spacing(0.5), + paddingBottom: theme.spacing(0.5), + borderRadius: theme.spacing(2), + marginRight: theme.spacing(1), width: 'fit-content', display: 'inline-block', textTransform: 'capitalize', @@ -60,7 +62,7 @@ const styles = (theme: any) => color: theme.palette.listCompletedStatus.color, }, noRuns: { - padding: theme.spacing.unit * 2, + padding: theme.spacing(2), }, }) diff --git a/src/components/TableButtons.js b/src/components/TableButtons.js index 45a0b780..71064f1d 100644 --- a/src/components/TableButtons.js +++ b/src/components/TableButtons.js @@ -1,15 +1,16 @@ -import { IconButton, withStyles } from '@material-ui/core' -import FirstPageIcon from '@material-ui/icons/FirstPage' -import KeyboardArrowLeft from '@material-ui/icons/KeyboardArrowLeft' -import KeyboardArrowRight from '@material-ui/icons/KeyboardArrowRight' -import LastPageIcon from '@material-ui/icons/LastPage' +import { IconButton } from '@mui/material' +import { withStyles } from 'src/utils/withStyles' +import FirstPageIcon from '@mui/icons-material/FirstPage' +import KeyboardArrowLeft from '@mui/icons-material/KeyboardArrowLeft' +import KeyboardArrowRight from '@mui/icons-material/KeyboardArrowRight' +import LastPageIcon from '@mui/icons-material/LastPage' import React from 'react' const styles = (theme) => ({ customButtons: { flexShrink: 0, color: theme.palette.text.secondary, - marginLeft: theme.spacing.unit * 2.5, + marginLeft: theme.spacing(2.5), }, }) @@ -30,6 +31,7 @@ const TableButtons = (props) => { onClick={handlePage(firstPage)} disabled={currentPage <= firstPage} aria-label="First Page" + size="large" > @@ -37,6 +39,7 @@ const TableButtons = (props) => { onClick={handlePage(currentPage - 1)} disabled={currentPage <= firstPage} aria-label="Previous Page" + size="large" > @@ -44,6 +47,7 @@ const TableButtons = (props) => { onClick={handlePage(currentPage + 1)} disabled={currentPage >= lastPage} aria-label="Next Page" + size="large" > @@ -51,6 +55,7 @@ const TableButtons = (props) => { onClick={handlePage(lastPage)} disabled={currentPage >= lastPage} aria-label="Last Page" + size="large" > diff --git a/src/components/TableRow/ErrorRow.tsx b/src/components/TableRow/ErrorRow.tsx index 15313b12..e04eb0b1 100644 --- a/src/components/TableRow/ErrorRow.tsx +++ b/src/components/TableRow/ErrorRow.tsx @@ -1,7 +1,7 @@ import React from 'react' -import TableCell from '@material-ui/core/TableCell' -import TableRow from '@material-ui/core/TableRow' +import TableCell from '@mui/material/TableCell' +import TableRow from '@mui/material/TableRow' export const ErrorRow = ({ msg }: { msg?: string }) => { if (!msg) { diff --git a/src/components/TableRow/LoadingRow.test.tsx b/src/components/TableRow/LoadingRow.test.tsx index 727254af..a1e02f9f 100644 --- a/src/components/TableRow/LoadingRow.test.tsx +++ b/src/components/TableRow/LoadingRow.test.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { render, screen } from '@testing-library/react' +import { render, screen } from 'support/test-utils' import { LoadingRow } from './LoadingRow' diff --git a/src/components/TableRow/LoadingRow.tsx b/src/components/TableRow/LoadingRow.tsx index 4737d12e..debf514c 100644 --- a/src/components/TableRow/LoadingRow.tsx +++ b/src/components/TableRow/LoadingRow.tsx @@ -1,19 +1,17 @@ import React from 'react' -import CircularProgress from '@material-ui/core/CircularProgress' -import { - createStyles, - Theme, - withStyles, - WithStyles, -} from '@material-ui/core/styles' -import TableCell from '@material-ui/core/TableCell' -import TableRow from '@material-ui/core/TableRow' +import CircularProgress from '@mui/material/CircularProgress' +import { Theme } from '@mui/material/styles' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import { createStyles } from 'src/utils/withStyles' +import TableCell from '@mui/material/TableCell' +import TableRow from '@mui/material/TableRow' const styles = (theme: Theme) => createStyles({ cell: { - padding: theme.spacing.unit * 2, + padding: theme.spacing(2), }, }) diff --git a/src/components/TableRow/NoContentRow.tsx b/src/components/TableRow/NoContentRow.tsx index 029bc3d9..e9d8901c 100644 --- a/src/components/TableRow/NoContentRow.tsx +++ b/src/components/TableRow/NoContentRow.tsx @@ -1,12 +1,12 @@ import React from 'react' -import TableCell from '@material-ui/core/TableCell' -import TableRow from '@material-ui/core/TableRow' +import TableCell from '@mui/material/TableCell' +import TableRow from '@mui/material/TableRow' -export const NoContentRow: React.FC<{ visible: boolean }> = ({ +export const NoContentRow = ({ children, visible, -}) => { +}: React.PropsWithChildren<{ visible: boolean }>) => { if (!visible) { return null } diff --git a/src/components/Tooltip.tsx b/src/components/Tooltip.tsx index 67df68b1..52b6ef66 100644 --- a/src/components/Tooltip.tsx +++ b/src/components/Tooltip.tsx @@ -1,17 +1,14 @@ -import { - createStyles, - Theme, - withStyles, - WithStyles, -} from '@material-ui/core/styles' -import MuiTooltip from '@material-ui/core/Tooltip' +import { Theme } from '@mui/material/styles' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import { createStyles } from 'src/utils/withStyles' +import MuiTooltip from '@mui/material/Tooltip' import React from 'react' const styles = ({ palette, shadows, typography }: Theme) => createStyles({ lightTooltip: { background: palette.primary.contrastText, - // @ts-expect-error color might be overwritten color: palette.text.primary, boxShadow: shadows[24], ...typography.h6, @@ -31,4 +28,4 @@ const UnstyledTooltip = ({ title, children, classes }: Props) => { ) } -export const Tooltip = withStyles(styles)(UnstyledTooltip) +export const Tooltip = withStyles(styles)(UnstyledTooltip) as typeof MuiTooltip diff --git a/src/context/ThemeModeContext.tsx b/src/context/ThemeModeContext.tsx new file mode 100644 index 00000000..4e21bd30 --- /dev/null +++ b/src/context/ThemeModeContext.tsx @@ -0,0 +1,45 @@ +import React from 'react' +import { getThemeMode, setThemeMode, type ThemeMode } from 'src/utils/storage' + +interface ThemeModeContextValue { + mode: ThemeMode + toggleMode: () => void +} + +const ThemeModeContext = React.createContext( + undefined, +) + +export const ThemeModeProvider = ({ + children, +}: { + children?: React.ReactNode +}) => { + const [mode, setMode] = React.useState(() => getThemeMode()) + + const toggleMode = React.useCallback(() => { + setMode((currentMode) => { + const nextMode = currentMode === 'light' ? 'dark' : 'light' + setThemeMode(nextMode) + return nextMode + }) + }, []) + + const value = React.useMemo(() => ({ mode, toggleMode }), [mode, toggleMode]) + + return ( + + {children} + + ) +} + +export const useThemeMode = (): ThemeModeContextValue => { + const context = React.useContext(ThemeModeContext) + + if (!context) { + throw new Error('useThemeMode must be used within a ThemeModeProvider') + } + + return context +} diff --git a/src/hooks/useFeatureFlag.test.tsx b/src/hooks/useFeatureFlag.test.tsx index 3b8d1360..36b1ff8a 100644 --- a/src/hooks/useFeatureFlag.test.tsx +++ b/src/hooks/useFeatureFlag.test.tsx @@ -1,12 +1,12 @@ import React from 'react' import { MockedProvider, MockedResponse } from '@apollo/client/testing' -import { renderHook } from '@testing-library/react-hooks' +import { renderHook, waitFor } from '@testing-library/react' import { useFeatureFlag, Feature, FEATURES_QUERY } from './useFeatureFlag' function getHookWrapper(mocks: MockedResponse[] = []) { - const wrapper: React.FC = ({ children }) => ( + const wrapper = ({ children }: React.PropsWithChildren<{}>) => ( {children} @@ -34,14 +34,13 @@ describe('useFeatureFlag', () => { }, ] - const { result, waitForNextUpdate } = renderHook( - () => useFeatureFlag(Feature.CSA), - { - wrapper: getHookWrapper(mocks), - }, - ) + const { result } = renderHook(() => useFeatureFlag(Feature.CSA), { + wrapper: getHookWrapper(mocks), + }) - await waitForNextUpdate() + await waitFor(() => { + expect(result.current).toEqual(true) + }) expect(result.current).toEqual(true) }) @@ -64,14 +63,13 @@ describe('useFeatureFlag', () => { }, ] - const { result, waitForNextUpdate } = renderHook( - () => useFeatureFlag(Feature.CSA), - { - wrapper: getHookWrapper(mocks), - }, - ) + const { result } = renderHook(() => useFeatureFlag(Feature.CSA), { + wrapper: getHookWrapper(mocks), + }) - await waitForNextUpdate() + await waitFor(() => { + expect(result.current).toEqual(false) + }) expect(result.current).toEqual(false) }) diff --git a/src/hooks/useLoadingPlaceholder.tsx b/src/hooks/useLoadingPlaceholder.tsx index f8954aa4..036e7284 100644 --- a/src/hooks/useLoadingPlaceholder.tsx +++ b/src/hooks/useLoadingPlaceholder.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { Typography } from '@material-ui/core' +import { Typography } from '@mui/material' export const useLoadingPlaceholder = ( isLoading = false, diff --git a/src/index.js b/src/index.js index 8b31d813..9e060a5e 100644 --- a/src/index.js +++ b/src/index.js @@ -1,26 +1,56 @@ -import { MuiThemeProvider } from '@material-ui/core/styles' +import { ThemeProvider, StyledEngineProvider } from '@mui/material/styles' +import createCache from '@emotion/cache' +import { TssCacheProvider } from 'tss-react' import JavascriptTimeAgo from 'javascript-time-ago' import en from 'javascript-time-ago/locale/en' import moment from 'moment' import React from 'react' -import ReactDOM from 'react-dom' +import { createRoot } from 'react-dom/client' import App from './App' -import { theme } from './theme' +import { createAppTheme } from './theme' import { ApolloProvider } from '@apollo/client' import { client } from './apollo' +import { ThemeModeProvider, useThemeMode } from './context/ThemeModeContext' JavascriptTimeAgo.locale(en) moment.defaultFormat = 'YYYY-MM-DD h:mm:ss A' export default App -if (typeof document !== 'undefined') { - ReactDOM.render( +// Separate Emotion cache for tss-react (withStyles) styles. +// MUI styles are injected via StyledEngineProvider injectFirst into a +// prepend cache at the TOP of . By giving tss-react its own +// non-prepend cache (key 'tss'), its styles land AFTER MUI's styles in +// the document, so custom padding / colour overrides always win. +const tssCssCache = createCache({ key: 'tss' }) + +const Root = () => { + const { mode } = useThemeMode() + const theme = React.useMemo(() => createAppTheme(mode), [mode]) + + return ( - - - - , - document.getElementById('root'), + + + + + + + + ) } + +if (typeof document !== 'undefined') { + const rootElement = document.getElementById('root') + + if (rootElement) { + const root = createRoot(rootElement) + + root.render( + + + , + ) + } +} diff --git a/src/pages/Chains/ChainNodes.tsx b/src/pages/Chains/ChainNodes.tsx index 1b2ad5c3..14b12942 100644 --- a/src/pages/Chains/ChainNodes.tsx +++ b/src/pages/Chains/ChainNodes.tsx @@ -1,5 +1,5 @@ import { CardTitle } from 'components/CardTitle' -import { Card, Grid } from '@material-ui/core' +import { Card, Grid } from '@mui/material' import Content from 'components/Content' import React from 'react' import { ChainResource } from './Show' @@ -20,7 +20,7 @@ export const ChainNodes = ({ nodes, chain }: Props) => { return ( {chain && ( - + Nodes diff --git a/src/pages/Chains/NodeRow.tsx b/src/pages/Chains/NodeRow.tsx index 567c9f52..31ae40b9 100644 --- a/src/pages/Chains/NodeRow.tsx +++ b/src/pages/Chains/NodeRow.tsx @@ -4,9 +4,10 @@ import { NodeResource } from './ChainNodes' import { tableStyles } from 'components/Table' import Link from 'components/Link' -import { withStyles, WithStyles } from '@material-ui/core/styles' -import TableCell from '@material-ui/core/TableCell' -import TableRow from '@material-ui/core/TableRow' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import TableCell from '@mui/material/TableCell' +import TableRow from '@mui/material/TableRow' interface Props extends WithStyles { node: NodeResource diff --git a/src/pages/Chains/NodesList.tsx b/src/pages/Chains/NodesList.tsx index 3d3f0b2b..b63f2917 100644 --- a/src/pages/Chains/NodesList.tsx +++ b/src/pages/Chains/NodesList.tsx @@ -6,7 +6,7 @@ import { TableCell, TableRow, Typography, -} from '@material-ui/core' +} from '@mui/material' import { NodeRow } from './NodeRow' import { NodeResource } from './ChainNodes' diff --git a/src/pages/Chains/RegionalNav.tsx b/src/pages/Chains/RegionalNav.tsx index fd2bc1fe..1287713d 100644 --- a/src/pages/Chains/RegionalNav.tsx +++ b/src/pages/Chains/RegionalNav.tsx @@ -3,36 +3,34 @@ import { connect } from 'react-redux' import { Chain, Resource } from 'core/store/models' import { localizedTimestamp, TimeAgo } from 'components/TimeAgo' import { useLocation } from 'react-router-dom' -import Card from '@material-ui/core/Card' -import Grid from '@material-ui/core/Grid' -import List from '@material-ui/core/List' -import ListItem from '@material-ui/core/ListItem' -import { - createStyles, - Theme, - WithStyles, - withStyles, -} from '@material-ui/core/styles' -import Typography from '@material-ui/core/Typography' +import Card from '@mui/material/Card' +import Grid from '@mui/material/Grid' +import List from '@mui/material/List' +import ListItem from '@mui/material/ListItem' +import { Theme } from '@mui/material/styles' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import { createStyles } from 'src/utils/withStyles' +import Typography from '@mui/material/Typography' import classNames from 'classnames' import Link from 'components/Link' const styles = (theme: Theme) => createStyles({ container: { - backgroundColor: theme.palette.common.white, - padding: theme.spacing.unit * 5, + backgroundColor: theme.palette.background.paper, + padding: theme.spacing(5), paddingBottom: 0, }, mainRow: { - marginBottom: theme.spacing.unit * 2, + marginBottom: theme.spacing(2), }, actions: { textAlign: 'right', }, regionalNavButton: { - marginLeft: theme.spacing.unit, - marginRight: theme.spacing.unit, + marginLeft: theme.spacing(1), + marginRight: theme.spacing(1), }, horizontalNav: { paddingBottom: 0, @@ -43,11 +41,11 @@ const styles = (theme: Theme) => paddingRight: 0, }, horizontalNavLink: { - padding: `${theme.spacing.unit * 4}px ${theme.spacing.unit * 4}px`, + padding: `${theme.spacing(4)} ${theme.spacing(4)}`, textDecoration: 'none', display: 'inline-block', borderBottom: 'solid 1px', - borderBottomColor: theme.palette.common.white, + borderBottomColor: theme.palette.background.paper, '&:hover': { borderBottomColor: theme.palette.primary.main, }, @@ -61,10 +59,10 @@ const styles = (theme: Theme) => textOverflow: 'ellipsis', }, badgePadding: { - paddingLeft: theme.spacing.unit * 2, - paddingRight: theme.spacing.unit * 2, - marginLeft: theme.spacing.unit * -2, - marginRight: theme.spacing.unit * -2, + paddingLeft: theme.spacing(2), + paddingRight: theme.spacing(2), + marginLeft: theme.spacing(-2), + marginRight: theme.spacing(-2), lineHeight: '1rem', }, dialogPaper: { @@ -73,31 +71,31 @@ const styles = (theme: Theme) => minWidth: '670px', maxWidth: '670px', overflow: 'hidden', - borderRadius: theme.spacing.unit * 3, + borderRadius: theme.spacing(3), }, warningText: { fontWeight: 500, - marginLeft: theme.spacing.unit * 3, - marginTop: theme.spacing.unit * 3, - marginBottom: theme.spacing.unit, + marginLeft: theme.spacing(3), + marginTop: theme.spacing(3), + marginBottom: theme.spacing(1), }, closeButton: { - marginRight: theme.spacing.unit * 3, - marginTop: theme.spacing.unit * 3, + marginRight: theme.spacing(3), + marginTop: theme.spacing(3), }, infoText: { - fontSize: theme.spacing.unit * 2, + fontSize: theme.spacing(2), fontWeight: 450, - marginLeft: theme.spacing.unit * 6, + marginLeft: theme.spacing(6), }, modalTextarea: { - marginLeft: theme.spacing.unit * 2, + marginLeft: theme.spacing(2), }, modalContent: { width: 'inherit', }, runJobButton: { - marginBottom: theme.spacing.unit * 3, + marginBottom: theme.spacing(3), }, runJobModalContent: { overflow: 'hidden', diff --git a/src/pages/Configuration/JobRuns.tsx b/src/pages/Configuration/JobRuns.tsx index 8324a60b..f455aa97 100644 --- a/src/pages/Configuration/JobRuns.tsx +++ b/src/pages/Configuration/JobRuns.tsx @@ -7,21 +7,19 @@ import { notifyErrorMsg, } from 'actionCreators' -import Card from '@material-ui/core/Card' -import CardHeader from '@material-ui/core/CardHeader' -import IconButton from '@material-ui/core/IconButton' -import DeleteIcon from '@material-ui/icons/Delete' -import { - createStyles, - Theme, - withStyles, - WithStyles, -} from '@material-ui/core/styles' -import Table from '@material-ui/core/Table' -import TableBody from '@material-ui/core/TableBody' -import TableCell from '@material-ui/core/TableCell' -import TableRow from '@material-ui/core/TableRow' -import Typography from '@material-ui/core/Typography' +import Card from '@mui/material/Card' +import CardHeader from '@mui/material/CardHeader' +import IconButton from '@mui/material/IconButton' +import DeleteIcon from '@mui/icons-material/Delete' +import { Theme } from '@mui/material/styles' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import { createStyles } from 'src/utils/withStyles' +import Table from '@mui/material/Table' +import TableBody from '@mui/material/TableBody' +import TableCell from '@mui/material/TableCell' +import TableRow from '@mui/material/TableRow' +import Typography from '@mui/material/Typography' import { ConfirmationDialog } from 'src/components/Dialogs/ConfirmationDialog' const styles = (theme: Theme) => { @@ -31,7 +29,7 @@ const styles = (theme: Theme) => { }, deleteCell: { '&:last-child': { - paddingRight: theme.spacing.unit * 1.5, + paddingRight: theme.spacing(1.5), }, }, deleteIcon: { @@ -67,7 +65,6 @@ export const JobRuns = withStyles(styles)(({ classes }: Props) => { title="Job Runs" subheader="Reduce the database size by deleting old job runs" /> - @@ -81,6 +78,7 @@ export const JobRuns = withStyles(styles)(({ classes }: Props) => { setErroredConfirm(true)} data-cy="delete-errored-job-runs" + size="large" > @@ -88,7 +86,6 @@ export const JobRuns = withStyles(styles)(({ classes }: Props) => {
- { actions: { display: 'flex', justifyContent: 'flex-end', - marginTop: theme.spacing.unit * 0.5, + marginTop: theme.spacing(0.5), }, logLevelHelperText: { marginTop: -8, diff --git a/src/pages/Header.tsx b/src/pages/Header.tsx index ae8e6915..e2bdac01 100644 --- a/src/pages/Header.tsx +++ b/src/pages/Header.tsx @@ -1,21 +1,19 @@ -import AppBar from '@material-ui/core/AppBar' -import MuiDrawer from '@material-ui/core/Drawer' -import Grid from '@material-ui/core/Grid' -import Hidden from '@material-ui/core/Hidden' -import IconButton from '@material-ui/core/IconButton' -import List from '@material-ui/core/List' -import ListItem from '@material-ui/core/ListItem' -import ListItemText from '@material-ui/core/ListItemText' -import Portal from '@material-ui/core/Portal' -import { - createStyles, - Theme, - withStyles, - WithStyles, -} from '@material-ui/core/styles' -import Toolbar from '@material-ui/core/Toolbar' -import Typography from '@material-ui/core/Typography' -import MenuIcon from '@material-ui/icons/Menu' +import AppBar from '@mui/material/AppBar' +import MuiDrawer from '@mui/material/Drawer' +import Grid from '@mui/material/Grid' +import Hidden from '@mui/material/Hidden' +import IconButton from '@mui/material/IconButton' +import List from '@mui/material/List' +import ListItem from '@mui/material/ListItem' +import ListItemText from '@mui/material/ListItemText' +import Portal from '@mui/material/Portal' +import { Theme } from '@mui/material/styles' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import { createStyles } from 'src/utils/withStyles' +import Toolbar from '@mui/material/Toolbar' +import Typography from '@mui/material/Typography' +import MenuIcon from '@mui/icons-material/Menu' import classNames from 'classnames' import React from 'react' import { connect } from 'react-redux' @@ -31,6 +29,8 @@ import MainLogo from '../components/Logos/Main' import fetchCountSelector from '../selectors/fetchCount' import { Feature, useFeatureFlag } from 'src/hooks/useFeatureFlag' +const ResizeDetector = ReactResizeDetector as any + const NAV_ITEMS = [ ['/jobs', 'Jobs'], ['/runs', 'Runs'], @@ -51,13 +51,34 @@ const drawerWidth = 240 const drawerStyles = ({ palette, spacing }: Theme) => createStyles({ menuitem: { - padding: spacing.unit * 3, - display: 'block', + minHeight: spacing(6), + paddingLeft: spacing(3), + paddingRight: spacing(3), + color: palette.text.primary, + textDecoration: 'none', + borderBottom: `1px solid ${palette.divider}`, + '&:hover': { + backgroundColor: palette.action.hover, + textDecoration: 'none', + }, + '& .MuiListItemText-root': { + marginTop: 0, + marginBottom: 0, + }, + '& .MuiListItemText-primary': { + color: 'inherit', + fontWeight: 500, + }, + }, + activeMenuitem: { + color: palette.primary.main, + backgroundColor: palette.action.hover, }, drawerPaper: { - backgroundColor: palette.common.white, - paddingTop: spacing.unit * 7, + backgroundColor: palette.background.paper, + paddingTop: spacing(7), width: drawerWidth, + boxSizing: 'border-box', }, drawerList: { padding: 0, @@ -80,6 +101,16 @@ const Drawer = withStyles(drawerStyles)(({ submitSignOut, isFeedsManagerFeatureEnabled, }: DrawerProps) => { + const { pathname } = useLocation() + + const isActivePath = (itemPath: string) => { + if (itemPath === '/job_distributors') { + return pathname.includes(itemPath) + } + + return pathname.startsWith(itemPath) + } + return ( ( - - - + component={BaseLink as any} + href={href} + className={classNames( + classes.menuitem, + isActivePath(href) && classes.activeMenuitem, )} - className={classes.menuitem} - /> + > + + ))} {isFeedsManagerFeatureEnabled && ( ( - - - + component={BaseLink as any} + href={'/job_distributors'} + className={classNames( + classes.menuitem, + isActivePath('/job_distributors') && classes.activeMenuitem, )} - className={classes.menuitem} - /> + > + + )} {authenticated && ( @@ -141,12 +176,12 @@ const navStyles = ({ palette, spacing }: Theme) => }, horizontalNavLink: { color: palette.secondary.main, - paddingTop: spacing.unit * 3, - paddingBottom: spacing.unit * 3, + paddingTop: spacing(3), + paddingBottom: spacing(3), textDecoration: 'none', display: 'inline-block', borderBottom: 'solid 1px', - borderBottomColor: palette.common.white, + borderBottomColor: palette.background.paper, '&:hover': { borderBottomColor: palette.primary.main, }, @@ -217,12 +252,12 @@ const Nav = withStyles(navStyles)(({ const styles = ({ palette, spacing, zIndex }: Theme) => createStyles({ appBar: { - backgroundColor: palette.common.white, + backgroundColor: palette.background.paper, zIndex: zIndex.modal - 1, }, toolbar: { - paddingLeft: spacing.unit * 5, - paddingRight: spacing.unit * 5, + paddingLeft: spacing(5), + paddingRight: spacing(5), }, }) @@ -251,43 +286,49 @@ const Header = withStyles(styles)(({ return ( - - +
+ - - - - - - - - - - - - - - - - -
+ const ConnectedHeader = connect(mapStateToProps, mapDispatchToProps)(Header) -export default withStyles(styles)(ConnectedHeader) +export default ConnectedHeader diff --git a/src/pages/NotFound.js b/src/pages/NotFound.js index c7fb00a0..4ff60932 100644 --- a/src/pages/NotFound.js +++ b/src/pages/NotFound.js @@ -1,6 +1,6 @@ import React from 'react' import NotFoundSVG from 'images/four-oh-four.js' -import { withStyles } from '@material-ui/core/styles' +import { withStyles } from 'src/utils/withStyles' const styles = () => ({ logo: { diff --git a/src/pages/Notifications.test.js b/src/pages/Notifications.test.js index d1c9a35e..9b70297d 100644 --- a/src/pages/Notifications.test.js +++ b/src/pages/Notifications.test.js @@ -4,6 +4,8 @@ import { MemoryRouter } from 'react-router-dom' import configureStore from 'redux-mock-store' import Notifications from 'pages/Notifications' import { render, screen } from '@testing-library/react' +import { ThemeProvider } from '@mui/material/styles' +import { theme } from 'src/theme' const { queryByText } = screen @@ -12,11 +14,13 @@ const mockStore = configureStore() const mountNotifications = (store) => render( - - - - - , + + + + + + + , ) describe('pages/Notifications', () => { diff --git a/src/pages/SignIn.js b/src/pages/SignIn.js index 44d5781a..03c7450e 100644 --- a/src/pages/SignIn.js +++ b/src/pages/SignIn.js @@ -1,13 +1,13 @@ import React, { useEffect, useState } from 'react' import { connect, useDispatch } from 'react-redux' import { Redirect, useLocation, useHistory } from 'react-router-dom' -import { withStyles } from '@material-ui/core/styles' +import { withStyles } from 'src/utils/withStyles' import Button from 'components/Button' -import Card from '@material-ui/core/Card' -import CardContent from '@material-ui/core/CardContent' -import Typography from '@material-ui/core/Typography' -import TextField from '@material-ui/core/TextField' -import { Grid } from '@material-ui/core' +import Card from '@mui/material/Card' +import CardContent from '@mui/material/CardContent' +import Typography from '@mui/material/Typography' +import TextField from '@mui/material/TextField' +import { Grid } from '@mui/material' import { notifyErrorMsg, submitSignIn } from 'actionCreators' import { renderNotification } from 'pages/Notifications' import HexagonLogo from 'components/Logos/Hexagon' @@ -22,11 +22,11 @@ const styles = (theme) => ({ height: '100%', }, cardContent: { - paddingTop: theme.spacing.unit * 6, - paddingLeft: theme.spacing.unit * 4, - paddingRight: theme.spacing.unit * 4, + paddingTop: theme.spacing(6), + paddingLeft: theme.spacing(4), + paddingRight: theme.spacing(4), '&:last-child': { - paddingBottom: theme.spacing.unit * 6, + paddingBottom: theme.spacing(6), }, }, headerRow: { @@ -34,7 +34,7 @@ const styles = (theme) => ({ }, error: { backgroundColor: theme.palette.error.light, - marginTop: theme.spacing.unit * 2, + marginTop: theme.spacing(2), }, errorText: { color: theme.palette.error.main, @@ -122,7 +122,7 @@ export const SignIn = (props) => { return ( { - + @@ -188,7 +188,7 @@ export const SignIn = (props) => { /> - + - - + } + /> @@ -85,10 +83,10 @@ export const BridgesView: React.FC = ({ rowsPerPage={pageSize} rowsPerPageOptions={[pageSize]} page={page - 1} - onChangePage={(_, p) => { + onPageChange={(_, p) => { history.push(`/bridges?page=${p + 1}&per=${pageSize}`) }} - onChangeRowsPerPage={() => {}} /* handler required by component, so make it a no-op */ + onRowsPerPageChange={() => {}} /* handler required by component, so make it a no-op */ backIconButtonProps={{ 'aria-label': 'prev-page' }} nextIconButtonProps={{ 'aria-label': 'next-page' }} /> diff --git a/src/screens/Chains/BetaAlert.tsx b/src/screens/Chains/BetaAlert.tsx deleted file mode 100644 index 02ba71c7..00000000 --- a/src/screens/Chains/BetaAlert.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react' - -import { - createStyles, - Theme, - withStyles, - WithStyles, -} from '@material-ui/core/styles' -import Paper from '@material-ui/core/Paper' -import Typography from '@material-ui/core/Typography' -import WarningIcon from '@material-ui/icons//Warning' - -const styles = (theme: Theme) => - createStyles({ - paper: { - backgroundColor: theme.palette.primary.light, - padding: theme.spacing.unit * 2, - display: 'flex', - }, - icon: { - marginRight: theme.spacing.unit, - }, - }) - -interface Props extends WithStyles {} - -export const BetaAlert = withStyles(styles)(({ classes }: Props) => { - return ( - - - Multi-chain functionality is in Beta - - ) -}) diff --git a/src/screens/Chains/ChainRow.tsx b/src/screens/Chains/ChainRow.tsx index 20f49b27..96ab48d3 100644 --- a/src/screens/Chains/ChainRow.tsx +++ b/src/screens/Chains/ChainRow.tsx @@ -3,9 +3,10 @@ import React from 'react' import { tableStyles } from 'components/Table' import Link from 'components/Link' -import { withStyles, WithStyles } from '@material-ui/core/styles' -import TableCell from '@material-ui/core/TableCell' -import TableRow from '@material-ui/core/TableRow' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import TableCell from '@mui/material/TableCell' +import TableRow from '@mui/material/TableRow' interface Props extends WithStyles { chain: ChainsPayload_ResultsFields diff --git a/src/screens/Chains/ChainsScreen.test.tsx b/src/screens/Chains/ChainsScreen.test.tsx index 3c1324bf..4e261f0b 100644 --- a/src/screens/Chains/ChainsScreen.test.tsx +++ b/src/screens/Chains/ChainsScreen.test.tsx @@ -115,7 +115,7 @@ describe('ChainsScreen', () => { expect(queryByText('5')).toBeInTheDocument() expect(queryByText('42')).toBeInTheDocument() - expect(queryByText('1-2 of 3')).toBeInTheDocument() + expect(queryByText(/1.?2 of 3/)).toBeInTheDocument() expect(getByRole('button', { name: /prev-page/i })).toBeDisabled() // Page 2 @@ -126,7 +126,7 @@ describe('ChainsScreen', () => { await waitForLoading() expect(queryByText('4')).toBeInTheDocument() - expect(queryByText('3-3 of 3')).toBeInTheDocument() + expect(queryByText(/3.?3 of 3/)).toBeInTheDocument() expect(getByRole('button', { name: /next-page/i })).toBeDisabled() // Page 1 @@ -136,7 +136,7 @@ describe('ChainsScreen', () => { expect(queryByText('5')).toBeInTheDocument() expect(queryByText('42')).toBeInTheDocument() - expect(queryByText('1-2 of 3')).toBeInTheDocument() + expect(queryByText(/1.?2 of 3/)).toBeInTheDocument() }) it('renders GQL errors', async () => { diff --git a/src/screens/Chains/ChainsView.tsx b/src/screens/Chains/ChainsView.tsx index d2e67b1f..f4037030 100644 --- a/src/screens/Chains/ChainsView.tsx +++ b/src/screens/Chains/ChainsView.tsx @@ -2,19 +2,18 @@ import React, { useMemo } from 'react' import { useHistory } from 'react-router-dom' -import Grid from '@material-ui/core/Grid' -import Card from '@material-ui/core/Card' -import Table from '@material-ui/core/Table' -import TableBody from '@material-ui/core/TableBody' -import TableCell from '@material-ui/core/TableCell' -import TableHead from '@material-ui/core/TableHead' -import TablePagination from '@material-ui/core/TablePagination' -import TableRow from '@material-ui/core/TableRow' - -import { BetaAlert } from './BetaAlert' +import Grid from '@mui/material/Grid' +import Card from '@mui/material/Card' +import Table from '@mui/material/Table' +import TableBody from '@mui/material/TableBody' +import TableCell from '@mui/material/TableCell' +import TableHead from '@mui/material/TableHead' +import TablePagination from '@mui/material/TablePagination' +import TableRow from '@mui/material/TableRow' + import { ChainRow } from './ChainRow' import Content from 'src/components/Content' -import { Heading1 } from 'src/components/Heading/Heading1' +import { PageHeader } from 'src/components/PageHeader' import { SearchTextField } from 'src/components/Search/SearchTextField' const searchIncludes = (searchParam: string) => { @@ -68,11 +67,7 @@ export const ChainsView: React.FC = ({ - - - - - Chains + @@ -111,10 +106,10 @@ export const ChainsView: React.FC = ({ rowsPerPage={pageSize} rowsPerPageOptions={[pageSize]} page={page - 1} - onChangePage={(_, p) => { + onPageChange={(_, p) => { history.push(`/chains?page=${p + 1}&per=${pageSize}`) }} - onChangeRowsPerPage={() => {}} /* handler required by component, so make it a no-op */ + onRowsPerPageChange={() => {}} /* handler required by component, so make it a no-op */ backIconButtonProps={{ 'aria-label': 'prev-page' }} nextIconButtonProps={{ 'aria-label': 'next-page' }} /> diff --git a/src/screens/Configuration/ConfigurationV2Card/ConfigurationV2Card.tsx b/src/screens/Configuration/ConfigurationV2Card/ConfigurationV2Card.tsx index e0543901..30a336ef 100644 --- a/src/screens/Configuration/ConfigurationV2Card/ConfigurationV2Card.tsx +++ b/src/screens/Configuration/ConfigurationV2Card/ConfigurationV2Card.tsx @@ -2,19 +2,17 @@ import React from 'react' import { gql, useQuery } from '@apollo/client' -import Card from '@material-ui/core/Card' -import CardHeader from '@material-ui/core/CardHeader' -import TableCell from '@material-ui/core/TableCell' -import TableRow from '@material-ui/core/TableRow' -import Grid from '@material-ui/core/Grid' -import ExpansionPanel from '@material-ui/core/ExpansionPanel' -import ExpansionPanelSummary from '@material-ui/core/ExpansionPanelSummary' -import ExpansionPanelDetails from '@material-ui/core/ExpansionPanelDetails' +import Card from '@mui/material/Card' +import CardHeader from '@mui/material/CardHeader' +import Grid from '@mui/material/Grid' +import Accordion from '@mui/material/Accordion' +import AccordionSummary from '@mui/material/AccordionSummary' +import AccordionDetails from '@mui/material/AccordionDetails' +import Typography from '@mui/material/Typography' import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter' import { prism } from 'react-syntax-highlighter/dist/esm/styles/prism' -import ExpandMoreIcon from '@material-ui/icons/ExpandMore' -import Typography from '@material-ui/core/Typography' +import ExpandMoreIcon from '@mui/icons-material/ExpandMore' // const DeprecationWarning = () => { // Left as reference for future deprecation warnings // return ( @@ -51,17 +49,17 @@ export const CONFIG_V2_QUERY = gql` } ` -const SpanRow: React.FC = ({ children }) => ( - - - {children} - - +const SpanRow = ({ children }: React.PropsWithChildren<{}>) => ( +
+ {children} +
) const FetchingRow = () => ... -const ErrorRow: React.FC = ({ children }) => {children} +const ErrorRow = ({ children }: React.PropsWithChildren<{}>) => ( + {children} +) const TOMLPanel = ({ loading, toml, error = '', title, expanded }: Props) => { if (error) { @@ -79,18 +77,18 @@ const TOMLPanel = ({ loading, toml, error = '', title, expanded }: Props) => { const styles = { display: 'block' } return ( - - - }> +
+ + }> {title} - - + + {toml} - - - + + +
) } diff --git a/src/screens/Configuration/ConfigurationView.tsx b/src/screens/Configuration/ConfigurationView.tsx index 88a1beba..0669c7ad 100644 --- a/src/screens/Configuration/ConfigurationView.tsx +++ b/src/screens/Configuration/ConfigurationView.tsx @@ -1,5 +1,5 @@ import React from 'react' -import Grid from '@material-ui/core/Grid' +import Grid from '@mui/material/Grid' import Content from 'components/Content' import { LoggingCard } from 'src/pages/Configuration/LoggingCard' diff --git a/src/screens/Configuration/NodeInfoCard/NodeInfoCard.tsx b/src/screens/Configuration/NodeInfoCard/NodeInfoCard.tsx index 9c2cc26a..1202ae55 100644 --- a/src/screens/Configuration/NodeInfoCard/NodeInfoCard.tsx +++ b/src/screens/Configuration/NodeInfoCard/NodeInfoCard.tsx @@ -1,18 +1,16 @@ import React, { useEffect } from 'react' -import Card from '@material-ui/core/Card' -import CardHeader from '@material-ui/core/CardHeader' -import Table from '@material-ui/core/Table' -import TableBody from '@material-ui/core/TableBody' -import TableCell from '@material-ui/core/TableCell' -import TableRow from '@material-ui/core/TableRow' -import Typography from '@material-ui/core/Typography' -import { - createStyles, - Theme, - withStyles, - WithStyles, -} from '@material-ui/core/styles' +import Card from '@mui/material/Card' +import CardHeader from '@mui/material/CardHeader' +import Table from '@mui/material/Table' +import TableBody from '@mui/material/TableBody' +import TableCell from '@mui/material/TableCell' +import TableRow from '@mui/material/TableRow' +import Typography from '@mui/material/Typography' +import { Theme } from '@mui/material/styles' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import { createStyles } from 'src/utils/withStyles' import { shallowEqual, useDispatch, useSelector } from 'react-redux' import { selectBuildInfo } from 'src/selectors/buildInfo' import { fetchBuildInfo } from 'src/actionCreators' @@ -20,8 +18,8 @@ import { fetchBuildInfo } from 'src/actionCreators' const styles = (theme: Theme) => { return createStyles({ cell: { - paddingTop: theme.spacing.unit * 1.5, - paddingBottom: theme.spacing.unit * 1.5, + paddingTop: theme.spacing(1.5), + paddingBottom: theme.spacing(1.5), }, }) } diff --git a/src/screens/Dashboard/AccountBalanceCard.tsx b/src/screens/Dashboard/AccountBalanceCard.tsx index 145d6f06..64a7f368 100644 --- a/src/screens/Dashboard/AccountBalanceCard.tsx +++ b/src/screens/Dashboard/AccountBalanceCard.tsx @@ -2,13 +2,14 @@ import React from 'react' import { gql } from '@apollo/client' -import Card from '@material-ui/core/Card' -import Grid from '@material-ui/core/Grid' +import Card from '@mui/material/Card' +import CardContent from '@mui/material/CardContent' +import CardHeader from '@mui/material/CardHeader' +import CircularProgress from '@mui/material/CircularProgress' +import Grid from '@mui/material/Grid' import { DetailsCardItemValue } from 'src/components/Cards/DetailsCard' import { ChainAccountBalanceCard } from 'screens/Dashboard/ChainAccountBalanceCard' import { EthKey } from 'types/generated/graphql' -import CardHeader from '@material-ui/core/CardHeader' -import CircularProgress from '@material-ui/core/CircularProgress' export const ACCOUNT_BALANCES_PAYLOAD__RESULTS_FIELDS = gql` fragment AccountBalancesPayload_ResultsFields on EthKey { @@ -52,9 +53,11 @@ export const AccountBalanceCard: React.FC = ({ {errorMsg && ( <> - - - + + + + + )} @@ -71,9 +74,11 @@ export const AccountBalanceCard: React.FC = ({ {!results?.length && !loading && !errorMsg && ( <> - - - + + + + + )} diff --git a/src/screens/Dashboard/ActivityCard.test.tsx b/src/screens/Dashboard/ActivityCard.test.tsx index ae5a8bcd..fff18ef9 100644 --- a/src/screens/Dashboard/ActivityCard.test.tsx +++ b/src/screens/Dashboard/ActivityCard.test.tsx @@ -30,7 +30,7 @@ describe('ActivityCard', () => { expect(queryByText(`Job: ${runs[0].job.id}`)).toBeInTheDocument() expect(queryByText(`Job: ${runs[1].job.id}`)).toBeInTheDocument() - expect(queryByRole('button', { name: /view more/i })).toBeNull() + expect(queryByRole('link', { name: /view more/i })).toBeNull() }) it('shows the view more button', () => { @@ -49,7 +49,7 @@ describe('ActivityCard', () => { maxRunsSize: 1, }) - expect(queryByRole('button', { name: /view more/i })).toBeInTheDocument() + expect(queryByRole('link', { name: /view more/i })).toBeInTheDocument() }) it('renders no content', () => { diff --git a/src/screens/Dashboard/ActivityCard.tsx b/src/screens/Dashboard/ActivityCard.tsx index 4e786cd7..42eff5cb 100644 --- a/src/screens/Dashboard/ActivityCard.tsx +++ b/src/screens/Dashboard/ActivityCard.tsx @@ -2,19 +2,17 @@ import React from 'react' import { gql } from '@apollo/client' -import Card from '@material-ui/core/Card' -import CardHeader from '@material-ui/core/CardHeader' -import { - createStyles, - Theme, - withStyles, - WithStyles, -} from '@material-ui/core/styles' -import Table from '@material-ui/core/Table' -import TableBody from '@material-ui/core/TableBody' -import TableCell from '@material-ui/core/TableCell' -import TableFooter from '@material-ui/core/TableFooter' -import TableRow from '@material-ui/core/TableRow' +import Card from '@mui/material/Card' +import CardHeader from '@mui/material/CardHeader' +import { Theme } from '@mui/material/styles' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import { createStyles } from 'src/utils/withStyles' +import Table from '@mui/material/Table' +import TableBody from '@mui/material/TableBody' +import TableCell from '@mui/material/TableCell' +import TableFooter from '@mui/material/TableFooter' +import TableRow from '@mui/material/TableRow' import BaseLink from 'src/components/BaseLink' import Button from 'src/components/Button' @@ -41,8 +39,8 @@ const styles = ({ palette, spacing }: Theme) => footer: { borderColor: palette.divider, borderTop: `1px solid`, - paddingTop: spacing.unit * 2, - paddingBottom: spacing.unit * 2, + paddingTop: spacing(2), + paddingBottom: spacing(2), }, }) diff --git a/src/screens/Dashboard/ActivityRow.tsx b/src/screens/Dashboard/ActivityRow.tsx index 99f569c6..cfa76cc1 100644 --- a/src/screens/Dashboard/ActivityRow.tsx +++ b/src/screens/Dashboard/ActivityRow.tsx @@ -1,15 +1,13 @@ import React from 'react' -import Grid from '@material-ui/core/Grid' -import { - createStyles, - Theme, - withStyles, - WithStyles, -} from '@material-ui/core/styles' -import TableCell from '@material-ui/core/TableCell' -import TableRow from '@material-ui/core/TableRow' -import Typography from '@material-ui/core/Typography' +import Grid from '@mui/material/Grid' +import { Theme } from '@mui/material/styles' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import { createStyles } from 'src/utils/withStyles' +import TableCell from '@mui/material/TableCell' +import TableRow from '@mui/material/TableRow' +import Typography from '@mui/material/Typography' import { TimeAgo } from 'src/components/TimeAgo' import Link from 'src/components/Link' @@ -36,10 +34,10 @@ const styles = ({ palette, spacing }: Theme) => height: '100%', }, runDetails: { - paddingTop: spacing.unit * 3, - paddingBottom: spacing.unit * 3, - paddingLeft: spacing.unit * 4, - paddingRight: spacing.unit * 4, + paddingTop: spacing(3), + paddingBottom: spacing(3), + paddingLeft: spacing(4), + paddingRight: spacing(4), }, }) diff --git a/src/screens/Dashboard/BuildInfoFooter.tsx b/src/screens/Dashboard/BuildInfoFooter.tsx index 3b2be66e..3f367446 100644 --- a/src/screens/Dashboard/BuildInfoFooter.tsx +++ b/src/screens/Dashboard/BuildInfoFooter.tsx @@ -1,13 +1,11 @@ import React, { useEffect } from 'react' -import Paper from '@material-ui/core/Paper' -import { - createStyles, - Theme, - withStyles, - WithStyles, -} from '@material-ui/core/styles' -import Typography from '@material-ui/core/Typography' +import Paper from '@mui/material/Paper' +import { Theme } from '@mui/material/styles' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import { createStyles } from 'src/utils/withStyles' +import Typography from '@mui/material/Typography' import { shallowEqual, useDispatch, useSelector } from 'react-redux' import { selectBuildInfo } from 'src/selectors/buildInfo' import { fetchBuildInfo } from 'src/actionCreators' @@ -16,7 +14,7 @@ const styles = (theme: Theme) => createStyles({ style: { textAlign: 'center', - padding: theme.spacing.unit * 2.5, + padding: theme.spacing(2.5), position: 'fixed', left: '0', bottom: '0', @@ -24,7 +22,7 @@ const styles = (theme: Theme) => borderRadius: 0, }, bareAnchor: { - color: theme.palette.common.black, + color: theme.palette.text.primary, textDecoration: 'none', }, }) diff --git a/src/screens/Dashboard/ChainAccountBalanceCard.tsx b/src/screens/Dashboard/ChainAccountBalanceCard.tsx index 15289698..e174862b 100644 --- a/src/screens/Dashboard/ChainAccountBalanceCard.tsx +++ b/src/screens/Dashboard/ChainAccountBalanceCard.tsx @@ -1,17 +1,17 @@ import React from 'react' import { EthKey } from 'types/generated/graphql' -import Grid from '@material-ui/core/Grid' +import Grid from '@mui/material/Grid' import { DetailsCardItemTitle, DetailsCardItemValue, } from 'components/Cards/DetailsCard' import { fromJuels } from 'utils/tokens/link' -import CardContent from '@material-ui/core/CardContent' -import CardHeader from '@material-ui/core/CardHeader' -import List from '@material-ui/core/List' -import ListItem from '@material-ui/core/ListItem' -import ListItemText from '@material-ui/core/ListItemText' -import Divider from '@material-ui/core/Divider' +import CardContent from '@mui/material/CardContent' +import CardHeader from '@mui/material/CardHeader' +import List from '@mui/material/List' +import ListItem from '@mui/material/ListItem' +import ListItemText from '@mui/material/ListItemText' +import Divider from '@mui/material/Divider' export interface Props { keys: Array @@ -45,7 +45,7 @@ export const ChainAccountBalanceCard: React.FC = ({ - + diff --git a/src/screens/Dashboard/DashboardView.tsx b/src/screens/Dashboard/DashboardView.tsx index c8178c3e..ca34ae68 100644 --- a/src/screens/Dashboard/DashboardView.tsx +++ b/src/screens/Dashboard/DashboardView.tsx @@ -1,5 +1,5 @@ import React from 'react' -import Grid from '@material-ui/core/Grid' +import Grid from '@mui/material/Grid' import Content from 'components/Content' diff --git a/src/screens/Dashboard/RecentJobRow.tsx b/src/screens/Dashboard/RecentJobRow.tsx index e8700647..8a047052 100644 --- a/src/screens/Dashboard/RecentJobRow.tsx +++ b/src/screens/Dashboard/RecentJobRow.tsx @@ -1,15 +1,13 @@ import React from 'react' -import Grid from '@material-ui/core/Grid' -import { - createStyles, - Theme, - withStyles, - WithStyles, -} from '@material-ui/core/styles' -import TableCell from '@material-ui/core/TableCell' -import TableRow from '@material-ui/core/TableRow' -import Typography from '@material-ui/core/Typography' +import Grid from '@mui/material/Grid' +import { Theme } from '@mui/material/styles' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import { createStyles } from 'src/utils/withStyles' +import TableCell from '@mui/material/TableCell' +import TableRow from '@mui/material/TableRow' +import Typography from '@mui/material/Typography' import Link from 'src/components/Link' import { TimeAgo } from 'src/components/TimeAgo' @@ -20,9 +18,9 @@ const styles = (theme: Theme) => borderColor: theme.palette.divider, borderTop: `1px solid`, borderBottom: 'none', - paddingTop: theme.spacing.unit * 2, - paddingBottom: theme.spacing.unit * 2, - paddingLeft: theme.spacing.unit * 2, + paddingTop: theme.spacing(2), + paddingBottom: theme.spacing(2), + paddingLeft: theme.spacing(2), }, block: { display: 'block' }, overflowEllipsis: { textOverflow: 'ellipsis', overflow: 'hidden' }, diff --git a/src/screens/Dashboard/RecentJobsCard.tsx b/src/screens/Dashboard/RecentJobsCard.tsx index a24a1cc1..d671a5d1 100644 --- a/src/screens/Dashboard/RecentJobsCard.tsx +++ b/src/screens/Dashboard/RecentJobsCard.tsx @@ -2,11 +2,13 @@ import React from 'react' import { gql } from '@apollo/client' -import Card from '@material-ui/core/Card' -import CardHeader from '@material-ui/core/CardHeader' -import { createStyles, withStyles, WithStyles } from '@material-ui/core/styles' -import Table from '@material-ui/core/Table' -import TableBody from '@material-ui/core/TableBody' +import Card from '@mui/material/Card' +import CardHeader from '@mui/material/CardHeader' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import { createStyles } from 'src/utils/withStyles' +import Table from '@mui/material/Table' +import TableBody from '@mui/material/TableBody' import { ErrorRow } from 'src/components/TableRow/ErrorRow' import { LoadingRow } from 'src/components/TableRow/LoadingRow' diff --git a/src/screens/EditBridge/EditBridgeScreen.test.tsx b/src/screens/EditBridge/EditBridgeScreen.test.tsx index f54c1945..e0043403 100644 --- a/src/screens/EditBridge/EditBridgeScreen.test.tsx +++ b/src/screens/EditBridge/EditBridgeScreen.test.tsx @@ -81,7 +81,7 @@ describe('EditBridgeScreen', () => { expect(getByText('Edit Bridge')).toBeInTheDocument() expect(getByTestId('bridge-form')).toBeInTheDocument() - const urlInput = getByRole('textbox', { name: 'Bridge URL *' }) + const urlInput = getByRole('textbox', { name: /^bridge url$/i }) userEvent.clear(urlInput) userEvent.type(urlInput, 'https://www.test.com') diff --git a/src/screens/EditBridge/EditBridgeView.test.tsx b/src/screens/EditBridge/EditBridgeView.test.tsx index 9b1e9938..640fa6f3 100644 --- a/src/screens/EditBridge/EditBridgeView.test.tsx +++ b/src/screens/EditBridge/EditBridgeView.test.tsx @@ -46,7 +46,7 @@ describe('EditBridgeView', () => { renderComponent(bridge) - userEvent.click(getByRole('button', { name: /cancel/i })) + userEvent.click(getByRole('link', { name: /cancel/i })) expect(await findByText('Link Success')).toBeInTheDocument() }) diff --git a/src/screens/EditBridge/EditBridgeView.tsx b/src/screens/EditBridge/EditBridgeView.tsx index 6f6c2575..bec8914a 100644 --- a/src/screens/EditBridge/EditBridgeView.tsx +++ b/src/screens/EditBridge/EditBridgeView.tsx @@ -1,9 +1,9 @@ import React from 'react' -import Card from '@material-ui/core/Card' -import CardContent from '@material-ui/core/CardContent' -import CardHeader from '@material-ui/core/CardHeader' -import Grid from '@material-ui/core/Grid' +import Card from '@mui/material/Card' +import CardContent from '@mui/material/CardContent' +import CardHeader from '@mui/material/CardHeader' +import Grid from '@mui/material/Grid' import BaseLink from 'src/components/BaseLink' import { BridgeForm, Props as FormProps } from 'src/components/Form/BridgeForm' @@ -24,7 +24,7 @@ export const EditBridgeView: React.FC = ({ bridge, onSubmit }) => { return ( - + { await waitForElementToBeRemoved(() => queryByRole('progressbar')) - const nameInput = getByRole('textbox', { name: 'Name *' }) + const nameInput = getByRole('textbox', { name: /^name$/i }) userEvent.clear(nameInput) userEvent.type(nameInput, 'updated') - const uriInput = getByRole('textbox', { name: 'URI *' }) + const uriInput = getByRole('textbox', { name: /^uri$/i }) userEvent.clear(uriInput) userEvent.type(uriInput, 'localhost:80812') - const publicKeyInput = getByRole('textbox', { name: 'Public Key *' }) + const publicKeyInput = getByRole('textbox', { name: /^public key$/i }) userEvent.clear(publicKeyInput) userEvent.type(publicKeyInput, '22222') diff --git a/src/screens/EditFeedsManager/EditFeedsManagerView.tsx b/src/screens/EditFeedsManager/EditFeedsManagerView.tsx index 2d8b6bd7..fe4c7607 100644 --- a/src/screens/EditFeedsManager/EditFeedsManagerView.tsx +++ b/src/screens/EditFeedsManager/EditFeedsManagerView.tsx @@ -1,10 +1,11 @@ import React from 'react' -import Card from '@material-ui/core/Card' -import CardContent from '@material-ui/core/CardContent' -import CardHeader from '@material-ui/core/CardHeader' -import Grid from '@material-ui/core/Grid' +import Card from '@mui/material/Card' +import CardContent from '@mui/material/CardContent' +import CardHeader from '@mui/material/CardHeader' +import Grid from '@mui/material/Grid' +import Content from 'components/Content' import { FeedsManagerForm, Props as FormProps, @@ -22,18 +23,20 @@ export const EditFeedsManagerView: React.FC = ({ data, onSubmit }) => { } return ( - - - - - - - - + + + + + + + + + + - + ) } diff --git a/src/screens/FeedsManager/ApprovedTable.tsx b/src/screens/FeedsManager/ApprovedTable.tsx index 7d134c3a..8c330d23 100644 --- a/src/screens/FeedsManager/ApprovedTable.tsx +++ b/src/screens/FeedsManager/ApprovedTable.tsx @@ -1,12 +1,13 @@ import React from 'react' -import Chip from '@material-ui/core/Chip' -import { WithStyles, withStyles } from '@material-ui/core/styles' -import Table from '@material-ui/core/Table' -import TableBody from '@material-ui/core/TableBody' -import TableCell from '@material-ui/core/TableCell' -import TableHead from '@material-ui/core/TableHead' -import TableRow from '@material-ui/core/TableRow' +import Chip from '@mui/material/Chip' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import Table from '@mui/material/Table' +import TableBody from '@mui/material/TableBody' +import TableCell from '@mui/material/TableCell' +import TableHead from '@mui/material/TableHead' +import TableRow from '@mui/material/TableRow' import Link from 'components/Link' import { tableStyles } from 'components/Table' @@ -54,7 +55,7 @@ export const ApprovedTable = withStyles(tableStyles)(({ {proposal.pendingUpdate && ( - Update Available + )} diff --git a/src/screens/FeedsManager/EditSupportedChainDialog.tsx b/src/screens/FeedsManager/EditSupportedChainDialog.tsx index 7db69a9f..0859d9e2 100644 --- a/src/screens/FeedsManager/EditSupportedChainDialog.tsx +++ b/src/screens/FeedsManager/EditSupportedChainDialog.tsx @@ -1,11 +1,11 @@ import React from 'react' -import Button from '@material-ui/core/Button' -import Dialog from '@material-ui/core/Dialog' -import DialogActions from '@material-ui/core/DialogActions' -import DialogContent from '@material-ui/core/DialogContent' -import DialogTitle from '@material-ui/core/DialogTitle' -import Typography from '@material-ui/core/Typography' +import Button from '@mui/material/Button' +import Dialog from '@mui/material/Dialog' +import DialogActions from '@mui/material/DialogActions' +import DialogContent from '@mui/material/DialogContent' +import DialogTitle from '@mui/material/DialogTitle' +import Typography from '@mui/material/Typography' import { ChainConfigurationForm, @@ -94,11 +94,10 @@ export const EditSupportedChainDialog = ({ const ocr2Keys = ocr2KeysData ? ocr2KeysData.ocr2KeyBundles.results : [] return ( - - + + Edit Supported Chain - -
diff --git a/src/screens/FeedsManager/SupportedChainsCard.tsx b/src/screens/FeedsManager/SupportedChainsCard.tsx index 481798a5..b4fb8967 100644 --- a/src/screens/FeedsManager/SupportedChainsCard.tsx +++ b/src/screens/FeedsManager/SupportedChainsCard.tsx @@ -3,28 +3,26 @@ import React from 'react' import { gql, useMutation } from '@apollo/client' import { useDispatch } from 'react-redux' -import Card from '@material-ui/core/Card' -import CardHeader from '@material-ui/core/CardHeader' -import Chip from '@material-ui/core/Chip' -import Grid from '@material-ui/core/Grid' -import ExpandMoreIcon from '@material-ui/icons/ExpandMore' -import ExpansionPanel from '@material-ui/core/ExpansionPanel' -import ExpansionPanelSummary from '@material-ui/core/ExpansionPanelSummary' -import ExpansionPanelDetails from '@material-ui/core/ExpansionPanelDetails' -import IconButton from '@material-ui/core/IconButton' -import ListItemIcon from '@material-ui/core/ListItemIcon' -import ListItemText from '@material-ui/core/ListItemText' -import Menu from '@material-ui/core/Menu' -import MenuItem from '@material-ui/core/MenuItem' -import MoreVertIcon from '@material-ui/icons/MoreVert' -import AddBoxIcon from '@material-ui/icons/AddBox' -import { - createStyles, - Theme, - WithStyles, - withStyles, -} from '@material-ui/core/styles' -import Typography from '@material-ui/core/Typography' +import Card from '@mui/material/Card' +import CardHeader from '@mui/material/CardHeader' +import Chip from '@mui/material/Chip' +import Grid from '@mui/material/Grid' +import ExpandMoreIcon from '@mui/icons-material/ExpandMore' +import Accordion from '@mui/material/Accordion' +import AccordionSummary from '@mui/material/AccordionSummary' +import AccordionDetails from '@mui/material/AccordionDetails' +import IconButton from '@mui/material/IconButton' +import ListItemIcon from '@mui/material/ListItemIcon' +import ListItemText from '@mui/material/ListItemText' +import Menu from '@mui/material/Menu' +import MenuItem from '@mui/material/MenuItem' +import MoreVertIcon from '@mui/icons-material/MoreVert' +import AddBoxIcon from '@mui/icons-material/AddBox' +import { Theme } from '@mui/material/styles' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import { createStyles } from 'src/utils/withStyles' +import Typography from '@mui/material/Typography' import { notifySuccessMsg, notifyErrorMsg } from 'actionCreators' import { FormValues } from 'src/components/Form/ChainConfigurationForm' @@ -109,7 +107,7 @@ export const UPDATE_FEEDS_MANAGER_CHAIN_CONFIG_MUTATION = gql` const styles = (theme: Theme) => { return createStyles({ card: { - marginBottom: theme.spacing.unit * 2, + marginBottom: theme.spacing(2), }, panel: { borderBottom: `1px solid ${theme.palette.divider}`, @@ -118,7 +116,7 @@ const styles = (theme: Theme) => { margin: 0, }, chip: { - marginRight: theme.spacing.unit * 2, + marginRight: theme.spacing(2), }, title: { fontSize: theme.typography.body2.fontSize, @@ -136,7 +134,7 @@ const styles = (theme: Theme) => { panelDetailsActions: { display: 'flex', justifyContent: 'flex-end', - gap: `${theme.spacing.unit * 2}px`, + gap: theme.spacing(2), }, }) } @@ -466,7 +464,11 @@ export const SupportedChainsCard = withStyles(styles)(({ }} action={ <> - + @@ -490,9 +492,8 @@ export const SupportedChainsCard = withStyles(styles)(({ } /> - {cfgs.map((cfg) => ( - - }> + }> Chain ID: {cfg.chainID} - - - + + + @@ -559,10 +560,9 @@ export const SupportedChainsCard = withStyles(styles)(({
- - + + ))} - {newDialogOpen && ( )} - {isEditDialogOpen() && ( = ({ job, onDelete }) => { - + = ({ job, onDelete }) => { - { userEvent.click(getByRole('tab', { name: /^runs/i })) // Default value of the rows per page select - userEvent.click(getByRole('button', { name: /10/i })) + userEvent.click(getByRole('combobox', { name: /rows per page/i })) userEvent.click(getByRole('option', { name: /25/i })) }) diff --git a/src/screens/Job/JobTabs.tsx b/src/screens/Job/JobTabs.tsx index 369b8cff..c907816c 100644 --- a/src/screens/Job/JobTabs.tsx +++ b/src/screens/Job/JobTabs.tsx @@ -2,25 +2,23 @@ import React from 'react' import { useLocation } from 'react-router-dom' -import Badge from '@material-ui/core/Badge' -import { - createStyles, - Theme, - withStyles, - WithStyles, -} from '@material-ui/core/styles' -import Tabs from '@material-ui/core/Tabs' +import Badge from '@mui/material/Badge' +import { Theme } from '@mui/material/styles' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import { createStyles } from 'src/utils/withStyles' +import Tabs from '@mui/material/Tabs' import { TabLink } from 'src/components/Tab/TabLink' const styles = (theme: Theme) => createStyles({ tabs: { - marginTop: theme.spacing.unit * 4, - marginBottom: theme.spacing.unit * 2.5, - borderBottom: `1px solid ${theme.palette.grey['300']}`, + marginTop: theme.spacing(4), + marginBottom: theme.spacing(2.5), + borderBottom: `1px solid ${theme.palette.divider}`, }, badge: { - padding: `0 ${theme.spacing.unit * 2}px`, + padding: `0 ${theme.spacing(2)}`, }, }) diff --git a/src/screens/Job/JobView.tsx b/src/screens/Job/JobView.tsx index 1c42218a..157acb58 100644 --- a/src/screens/Job/JobView.tsx +++ b/src/screens/Job/JobView.tsx @@ -1,5 +1,5 @@ import { gql } from '@apollo/client' -import Grid from '@material-ui/core/Grid' +import Grid from '@mui/material/Grid' import React from 'react' import { Route, Switch, useRouteMatch } from 'react-router-dom' import Button from 'src/components/Button' @@ -238,7 +238,7 @@ export const JobView: React.FC = ({ return ( <> - + {job.name || '--'} diff --git a/src/screens/Job/RunJobDialog.tsx b/src/screens/Job/RunJobDialog.tsx index 18588864..24c3f45f 100644 --- a/src/screens/Job/RunJobDialog.tsx +++ b/src/screens/Job/RunJobDialog.tsx @@ -1,35 +1,33 @@ import React from 'react' -import CloseIcon from '@material-ui/icons/Close' -import Dialog from '@material-ui/core/Dialog' -import DialogActions from '@material-ui/core/DialogActions' -import DialogContent from '@material-ui/core/DialogContent' -import DialogTitle from '@material-ui/core/DialogTitle' -import IconButton from '@material-ui/core/IconButton' -import TextField from '@material-ui/core/TextField' -import { - createStyles, - Theme, - withStyles, - WithStyles, -} from '@material-ui/core/styles' -import Typography from '@material-ui/core/Typography' +import CloseIcon from '@mui/icons-material/Close' +import Dialog from '@mui/material/Dialog' +import DialogActions from '@mui/material/DialogActions' +import DialogContent from '@mui/material/DialogContent' +import DialogTitle from '@mui/material/DialogTitle' +import IconButton from '@mui/material/IconButton' +import TextField from '@mui/material/TextField' +import { Theme } from '@mui/material/styles' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import { createStyles } from 'src/utils/withStyles' +import Typography from '@mui/material/Typography' import Button from 'src/components/Button' const styles = (theme: Theme) => createStyles({ dialogContent: { - paddingTop: theme.spacing.unit * 1, + paddingTop: theme.spacing(1), }, textarea: { width: 400, }, closeButton: { position: 'absolute', - right: theme.spacing.unit, - top: theme.spacing.unit, - color: theme.palette.grey[500], + right: theme.spacing(1), + top: theme.spacing(1), + color: theme.palette.text.secondary, }, }) @@ -55,17 +53,17 @@ export const RunJobDialog = withStyles(styles)(({ return ( - + Pipeline Input - setPipelineInput(e.target.value)} /> - - - + } + /> diff --git a/src/screens/JobProposal/EditJobSpecDialog.tsx b/src/screens/JobProposal/EditJobSpecDialog.tsx index 049025a0..5d493c1f 100644 --- a/src/screens/JobProposal/EditJobSpecDialog.tsx +++ b/src/screens/JobProposal/EditJobSpecDialog.tsx @@ -1,20 +1,23 @@ import React from 'react' import { Field, Form, Formik, FormikHelpers } from 'formik' -import { TextField } from 'formik-material-ui' +import { FormikTextField as TextField } from 'src/components/Form/FormikFields' import * as Yup from 'yup' -import Button from '@material-ui/core/Button' -import Dialog from '@material-ui/core/Dialog' -import DialogActions from '@material-ui/core/DialogActions' -import DialogContent from '@material-ui/core/DialogContent' -import DialogTitle from '@material-ui/core/DialogTitle' -import { createStyles, WithStyles, withStyles } from '@material-ui/core/styles' -import Typography from '@material-ui/core/Typography' +import Button from '@mui/material/Button' +import Dialog from '@mui/material/Dialog' +import DialogActions from '@mui/material/DialogActions' +import DialogContent from '@mui/material/DialogContent' +import DialogTitle from '@mui/material/DialogTitle' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import { createStyles } from 'src/utils/withStyles' +import Typography from '@mui/material/Typography' const styles = () => { return createStyles({ paperRoot: { - width: 700, + width: '100%', + maxWidth: 700, }, }) } @@ -60,10 +63,13 @@ export const EditJobSpecDialog = withStyles(styles)(({ - - Edit Job Spec + + + Edit Job Spec + { userEvent.click(getByRole('button', { name: /edit/i })) const specInput = screen.getByRole('textbox', { - name: /job spec \*/i, + name: /job spec/i, }) userEvent.clear(specInput) userEvent.type(specInput, 'name="updated spec"') @@ -188,7 +188,7 @@ describe('JobProposalScreen', () => { userEvent.click(getByRole('button', { name: /edit/i })) const specInput = screen.getByRole('textbox', { - name: /job spec \*/i, + name: /job spec/i, }) userEvent.clear(specInput) userEvent.type(specInput, 'name="updated spec"') diff --git a/src/screens/JobProposal/JobProposalView.tsx b/src/screens/JobProposal/JobProposalView.tsx index 21fda3dc..4a0f3707 100644 --- a/src/screens/JobProposal/JobProposalView.tsx +++ b/src/screens/JobProposal/JobProposalView.tsx @@ -2,7 +2,7 @@ import React from 'react' import { gql } from '@apollo/client' -import Grid from '@material-ui/core/Grid' +import Grid from '@mui/material/Grid' import Content from 'src/components/Content' import { Props as EditJobSpecDialogProps } from './EditJobSpecDialog' @@ -43,7 +43,7 @@ export const JobProposalView: React.FC = ({ }) => { return ( - + Job Proposal #{proposal.id} @@ -51,13 +51,13 @@ export const JobProposalView: React.FC = ({ - + Specs - + { hidden: true, }), ).not.toBeInTheDocument() + + // Expansion panel contents render on expand; open panel 3 before asserting its actions. + userEvent.click(within(panels[2]).getAllByRole('button')[0]) expect( within(panels[2]).queryByRole('button', { name: 'Approve', - hidden: true, }), ).toBeInTheDocument() expect( @@ -343,10 +345,12 @@ describe('SpecsView', () => { hidden: true, }), ).not.toBeInTheDocument() + + // Expansion panel contents render on expand; open panel 3 before asserting its actions. + userEvent.click(within(panels[2]).getAllByRole('button')[0]) expect( within(panels[2]).queryByRole('button', { name: 'Approve', - hidden: true, }), ).toBeInTheDocument() }) diff --git a/src/screens/JobProposal/SpecsView.tsx b/src/screens/JobProposal/SpecsView.tsx index 19af94e5..4d43fcf4 100644 --- a/src/screens/JobProposal/SpecsView.tsx +++ b/src/screens/JobProposal/SpecsView.tsx @@ -4,19 +4,17 @@ import { gql } from '@apollo/client' import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter' import { prism } from 'react-syntax-highlighter/dist/esm/styles/prism' -import Button from '@material-ui/core/Button' -import Chip from '@material-ui/core/Chip' -import ExpandMoreIcon from '@material-ui/icons/ExpandMore' -import ExpansionPanel from '@material-ui/core/ExpansionPanel' -import ExpansionPanelSummary from '@material-ui/core/ExpansionPanelSummary' -import ExpansionPanelDetails from '@material-ui/core/ExpansionPanelDetails' -import { - createStyles, - Theme, - WithStyles, - withStyles, -} from '@material-ui/core/styles' -import Typography from '@material-ui/core/Typography' +import Button from '@mui/material/Button' +import Chip from '@mui/material/Chip' +import ExpandMoreIcon from '@mui/icons-material/ExpandMore' +import Accordion from '@mui/material/Accordion' +import AccordionSummary from '@mui/material/AccordionSummary' +import AccordionDetails from '@mui/material/AccordionDetails' +import { Theme } from '@mui/material/styles' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import { createStyles } from 'src/utils/withStyles' +import Typography from '@mui/material/Typography' import { ConfirmationDialog } from 'src/components/Dialogs/ConfirmationDialog' import { @@ -39,7 +37,7 @@ export const JOB_PROPOSAL__SPECS_FIELDS = gql` const styles = (theme: Theme) => { return createStyles({ versionText: { - marginRight: theme.spacing.unit * 2, + marginRight: theme.spacing(2), }, proposedAtContainer: { flex: 1, @@ -50,7 +48,7 @@ const styles = (theme: Theme) => { }, actions: { display: 'flex', - marginBottom: theme.spacing.unit * 2, + marginBottom: theme.spacing(2), }, editContainer: { flex: 1, @@ -245,12 +243,12 @@ export const SpecsView = withStyles(styles)(({ return (
{sortedSpecs.map((spec, idx) => ( - - }> + }> Version {spec.version} @@ -260,7 +258,7 @@ export const SpecsView = withStyles(styles)(({ variant={ spec.status === 'REJECTED' || spec.status === 'CANCELLED' ? 'outlined' - : 'default' + : 'filled' } />
@@ -268,8 +266,8 @@ export const SpecsView = withStyles(styles)(({ Proposed {spec.createdAt}
-
- + +
{idx === 0 && @@ -296,8 +294,8 @@ export const SpecsView = withStyles(styles)(({ > {spec.definition} - - + + ))} diff --git a/src/screens/JobRun/JSONCard.tsx b/src/screens/JobRun/JSONCard.tsx index df4348d4..fe6dd923 100644 --- a/src/screens/JobRun/JSONCard.tsx +++ b/src/screens/JobRun/JSONCard.tsx @@ -1,7 +1,7 @@ import React from 'react' -import Card from '@material-ui/core/Card' -import CardContent from '@material-ui/core/CardContent' +import Card from '@mui/material/Card' +import CardContent from '@mui/material/CardContent' import { PrettyJson } from 'src/components/Syntax/PrettyJson' diff --git a/src/screens/JobRun/JobRunCard.tsx b/src/screens/JobRun/JobRunCard.tsx index 91884d3a..1162da27 100644 --- a/src/screens/JobRun/JobRunCard.tsx +++ b/src/screens/JobRun/JobRunCard.tsx @@ -1,6 +1,6 @@ import React from 'react' -import Grid from '@material-ui/core/Grid' +import Grid from '@mui/material/Grid' import { DetailsCard, diff --git a/src/screens/JobRun/JobRunTabs.tsx b/src/screens/JobRun/JobRunTabs.tsx index 8cda6785..81d1a047 100644 --- a/src/screens/JobRun/JobRunTabs.tsx +++ b/src/screens/JobRun/JobRunTabs.tsx @@ -2,24 +2,22 @@ import React from 'react' import { useLocation } from 'react-router-dom' -import { - createStyles, - Theme, - withStyles, - WithStyles, -} from '@material-ui/core/styles' -import Tabs from '@material-ui/core/Tabs' +import { Theme } from '@mui/material/styles' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import { createStyles } from 'src/utils/withStyles' +import Tabs from '@mui/material/Tabs' import { TabLink } from 'src/components/Tab/TabLink' const styles = (theme: Theme) => createStyles({ tabs: { - marginTop: theme.spacing.unit * 4, - marginBottom: theme.spacing.unit * 2.5, - borderBottom: `1px solid ${theme.palette.grey['300']}`, + marginTop: theme.spacing(4), + marginBottom: theme.spacing(2.5), + borderBottom: `1px solid ${theme.palette.divider}`, }, badge: { - padding: `0 ${theme.spacing.unit * 2}px`, + padding: `0 ${theme.spacing(2)}`, }, }) diff --git a/src/screens/JobRun/JobRunView.test.tsx b/src/screens/JobRun/JobRunView.test.tsx index a0c00ef9..f0752736 100644 --- a/src/screens/JobRun/JobRunView.test.tsx +++ b/src/screens/JobRun/JobRunView.test.tsx @@ -2,11 +2,13 @@ import React from 'react' import { Route } from 'react-router-dom' import { renderWithRouter, screen } from 'test-utils' +import { createAppTheme } from 'src/theme' import { JobRunView } from './JobRunView' import { buildRun, buildTaskRun } from 'support/factories/gql/fetchJobRun' const { queryByRole, queryByText } = screen +const lightTheme = createAppTheme('light') describe('JobView', () => { function renderComponent(run: JobRunPayload_Fields) { @@ -92,7 +94,7 @@ describe('JobView', () => { const ds1 = queryByText(/ds1/, { selector: 'text' }) const ds2 = queryByText(/ds2/, { selector: 'text' }) - expect(ds1).toHaveAttribute('fill', 'black') - expect(ds2).toHaveAttribute('fill', 'black') + expect(ds1).toHaveAttribute('fill', lightTheme.palette.text.primary) + expect(ds2).toHaveAttribute('fill', lightTheme.palette.text.primary) }) }) diff --git a/src/screens/JobRun/JobRunView.tsx b/src/screens/JobRun/JobRunView.tsx index 3867f198..f26e0323 100644 --- a/src/screens/JobRun/JobRunView.tsx +++ b/src/screens/JobRun/JobRunView.tsx @@ -3,7 +3,7 @@ import React from 'react' import { gql } from '@apollo/client' import { Route, Switch, useRouteMatch } from 'react-router-dom' -import Grid from '@material-ui/core/Grid' +import Grid from '@mui/material/Grid' import Content from 'components/Content' import { ErrorsCard } from './ErrorsCard' @@ -94,7 +94,7 @@ export const JobRunView = ({ run }: Props) => { return ( - + Job Run #{run.id} diff --git a/src/screens/JobRun/StatusCard.tsx b/src/screens/JobRun/StatusCard.tsx index beaebf2a..64635f83 100644 --- a/src/screens/JobRun/StatusCard.tsx +++ b/src/screens/JobRun/StatusCard.tsx @@ -2,10 +2,12 @@ import React from 'react' import classNames from 'classnames' -import Card from '@material-ui/core/Card' -import CardContent from '@material-ui/core/CardContent' -import { createStyles, withStyles, WithStyles } from '@material-ui/core/styles' -import Typography from '@material-ui/core/Typography' +import Card from '@mui/material/Card' +import CardContent from '@mui/material/CardContent' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import { createStyles } from 'src/utils/withStyles' +import Typography from '@mui/material/Typography' import { ElapsedDuration } from 'components/ElapsedDuration' import { JobRunStatus } from 'src/types/generated/graphql' @@ -17,7 +19,7 @@ const styles = (theme: any) => cardContent: { display: 'flex', '&:last-child': { - paddingBottom: theme.spacing.unit * 2, + paddingBottom: theme.spacing(2), }, }, completed: { @@ -30,7 +32,7 @@ const styles = (theme: any) => backgroundColor: theme.palette.warning.light, }, body: { - marginLeft: theme.spacing.unit * 2, + marginLeft: theme.spacing(2), }, statusText: { display: 'inline-block', diff --git a/src/screens/JobRun/TaskRunItem.tsx b/src/screens/JobRun/TaskRunItem.tsx index dca46703..6ed3d6a9 100644 --- a/src/screens/JobRun/TaskRunItem.tsx +++ b/src/screens/JobRun/TaskRunItem.tsx @@ -1,12 +1,10 @@ import React from 'react' -import { - createStyles, - Theme, - withStyles, - WithStyles, -} from '@material-ui/core/styles' -import Typography from '@material-ui/core/Typography' +import { Theme } from '@mui/material/styles' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import { createStyles } from 'src/utils/withStyles' +import Typography from '@mui/material/Typography' import { TaskRunStatusIcon } from 'src/components/Icons/TaskRunStatusIcon' import { TaskRunStatus } from 'src/utils/taskRunStatus' @@ -15,8 +13,8 @@ const styles = (theme: Theme) => createStyles({ root: { display: 'flex', - borderBottom: '1px solid rgba(0, 0, 0, 0.12);', - padding: theme.spacing.unit * 2, + borderBottom: `1px solid ${theme.palette.divider}`, + padding: theme.spacing(2), '&:last-child': { borderBottom: 'none', }, @@ -24,20 +22,20 @@ const styles = (theme: Theme) => content: { flex: 1, overflow: 'auto', - paddingLeft: theme.spacing.unit * 2, + paddingLeft: theme.spacing(2), }, heading: { - lineHeight: `${theme.spacing.unit * 5}px`, + lineHeight: theme.spacing(5), }, subheading: { - color: theme.palette.grey[500], + color: theme.palette.text.secondary, }, text: { - marginBottom: theme.spacing.unit, - marginTop: theme.spacing.unit, + marginBottom: theme.spacing(1), + marginTop: theme.spacing(1), }, attributeKey: { - fontWeight: theme.typography.fontWeightLight, + fontWeight: 300, }, }) diff --git a/src/screens/JobRun/TaskRunsCard.tsx b/src/screens/JobRun/TaskRunsCard.tsx index 1bd8a267..55bbacad 100644 --- a/src/screens/JobRun/TaskRunsCard.tsx +++ b/src/screens/JobRun/TaskRunsCard.tsx @@ -1,6 +1,6 @@ import React from 'react' -import Card from '@material-ui/core/Card' +import Card from '@mui/material/Card' import { parseDot } from 'src/utils/parseDot' import { TaskRunItem } from './TaskRunItem' diff --git a/src/screens/JobRuns/JobRunsScreen.test.tsx b/src/screens/JobRuns/JobRunsScreen.test.tsx index a56ba9ed..cf95fa24 100644 --- a/src/screens/JobRuns/JobRunsScreen.test.tsx +++ b/src/screens/JobRuns/JobRunsScreen.test.tsx @@ -118,7 +118,7 @@ describe('JobRunsScreen', () => { expect(queryByText('1')).toBeInTheDocument() expect(queryByText('2')).toBeInTheDocument() - expect(queryByText('1-2 of 3')).toBeInTheDocument() + expect(queryByText(/1.?2 of 3/)).toBeInTheDocument() expect(getByRole('button', { name: /prev-page/i })).toBeDisabled() // Page 2 @@ -127,7 +127,7 @@ describe('JobRunsScreen', () => { await waitForLoading() expect(queryByText('3')).toBeInTheDocument() - expect(queryByText('3-3 of 3')).toBeInTheDocument() + expect(queryByText(/3.?3 of 3/)).toBeInTheDocument() expect(getByRole('button', { name: /next-page/i })).toBeDisabled() // Page 1 @@ -137,7 +137,7 @@ describe('JobRunsScreen', () => { expect(queryByText('1')).toBeInTheDocument() expect(queryByText('2')).toBeInTheDocument() - expect(queryByText('1-2 of 3')).toBeInTheDocument() + expect(queryByText(/1.?2 of 3/)).toBeInTheDocument() }) it('renders GQL errors', async () => { diff --git a/src/screens/JobRuns/JobRunsView.tsx b/src/screens/JobRuns/JobRunsView.tsx index 7d9b3696..c26b429d 100644 --- a/src/screens/JobRuns/JobRunsView.tsx +++ b/src/screens/JobRuns/JobRunsView.tsx @@ -3,14 +3,14 @@ import React from 'react' import { gql } from '@apollo/client' import { useHistory } from 'react-router-dom' -import Card from '@material-ui/core/Card' -import Grid from '@material-ui/core/Grid' -import TablePagination from '@material-ui/core/TablePagination' +import Card from '@mui/material/Card' +import Grid from '@mui/material/Grid' +import TablePagination from '@mui/material/TablePagination' import Content from 'src/components/Content' -import { Heading1 } from 'src/components/Heading/Heading1' import { JobRunsTable } from 'src/components/Table/JobRunsTable' import { Loading } from 'src/components/Feedback/Loading' +import { PageHeader } from 'src/components/PageHeader' export const JOB_RUNS_PAYLOAD__RESULTS_FIELDS = gql` fragment JobRunsPayload_ResultsFields on JobRun { @@ -56,9 +56,9 @@ export const JobRunsView: React.FC = ({ return ( - + - Job Runs + {loading && } @@ -73,10 +73,10 @@ export const JobRunsView: React.FC = ({ rowsPerPage={pageSize} rowsPerPageOptions={[pageSize]} page={page - 1} - onChangePage={(_, p) => { + onPageChange={(_, p) => { history.push(`/runs?page=${p + 1}&per=${pageSize}`) }} - onChangeRowsPerPage={() => {}} /* handler required by component, so make it a no-op */ + onRowsPerPageChange={() => {}} /* handler required by component, so make it a no-op */ backIconButtonProps={{ 'aria-label': 'prev-page' }} nextIconButtonProps={{ 'aria-label': 'next-page' }} /> diff --git a/src/screens/Jobs/JobRow.tsx b/src/screens/Jobs/JobRow.tsx index cd91195d..d55b690c 100644 --- a/src/screens/Jobs/JobRow.tsx +++ b/src/screens/Jobs/JobRow.tsx @@ -1,8 +1,9 @@ import React from 'react' -import { withStyles, WithStyles } from '@material-ui/core/styles' -import TableCell from '@material-ui/core/TableCell' -import TableRow from '@material-ui/core/TableRow' +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import TableCell from '@mui/material/TableCell' +import TableRow from '@mui/material/TableRow' import { formatJobSpecType } from 'src/utils/formatJobSpecType' import { tableStyles } from 'components/Table' diff --git a/src/screens/Jobs/JobsScreen.test.tsx b/src/screens/Jobs/JobsScreen.test.tsx index 97f50323..c22242ff 100644 --- a/src/screens/Jobs/JobsScreen.test.tsx +++ b/src/screens/Jobs/JobsScreen.test.tsx @@ -120,7 +120,7 @@ describe('JobsScreen', () => { expect(queryByText('job 1')).toBeInTheDocument() expect(queryByText('job 2')).toBeInTheDocument() - expect(queryByText('1-2 of 3')).toBeInTheDocument() + expect(queryByText(/1.?2 of 3/)).toBeInTheDocument() expect(getByRole('button', { name: /prev-page/i })).toBeDisabled() // Page 2 @@ -129,7 +129,7 @@ describe('JobsScreen', () => { await waitForLoading() expect(queryByText('job 3')).toBeInTheDocument() - expect(queryByText('3-3 of 3')).toBeInTheDocument() + expect(queryByText(/3.?3 of 3/)).toBeInTheDocument() expect(getByRole('button', { name: /next-page/i })).toBeDisabled() // Page 1 @@ -139,7 +139,7 @@ describe('JobsScreen', () => { expect(queryByText('job 1')).toBeInTheDocument() expect(queryByText('job 2')).toBeInTheDocument() - expect(queryByText('1-2 of 3')).toBeInTheDocument() + expect(queryByText(/1.?2 of 3/)).toBeInTheDocument() }) it('renders GQL errors', async () => { @@ -184,7 +184,7 @@ describe('JobsScreen', () => { await waitForLoading() - userEvent.click(getByRole('button', { name: /new job/i })) + userEvent.click(getByRole('link', { name: /new job/i })) expect(await findByText('Link Success')).toBeInTheDocument() }) diff --git a/src/screens/Jobs/JobsView.tsx b/src/screens/Jobs/JobsView.tsx index bf5fe640..a2947479 100644 --- a/src/screens/Jobs/JobsView.tsx +++ b/src/screens/Jobs/JobsView.tsx @@ -3,22 +3,22 @@ import React from 'react' import { gql } from '@apollo/client' import { useHistory } from 'react-router-dom' -import Card from '@material-ui/core/Card' -import Grid from '@material-ui/core/Grid' -import Table from '@material-ui/core/Table' -import TableBody from '@material-ui/core/TableBody' -import TableCell from '@material-ui/core/TableCell' -import TableHead from '@material-ui/core/TableHead' -import TablePagination from '@material-ui/core/TablePagination' -import TableRow from '@material-ui/core/TableRow' +import Card from '@mui/material/Card' +import Grid from '@mui/material/Grid' +import Table from '@mui/material/Table' +import TableBody from '@mui/material/TableBody' +import TableCell from '@mui/material/TableCell' +import TableHead from '@mui/material/TableHead' +import TablePagination from '@mui/material/TablePagination' +import TableRow from '@mui/material/TableRow' import BaseLink from 'components/BaseLink' import Button from 'components/Button' import Content from 'components/Content' import { JobRow } from './JobRow' import Link from 'components/Link' +import { PageHeader } from 'src/components/PageHeader' import { SearchTextField } from 'src/components/Search/SearchTextField' -import { Heading1 } from 'src/components/Heading/Heading1' export const JOBS_PAYLOAD__RESULTS_FIELDS = gql` fragment JobsPayload_ResultsFields on Job { @@ -108,13 +108,10 @@ export const JobsView: React.FC = ({ jobs, page, pageSize, total }) => { return ( - - Jobs - - - - - + + = ({ jobs, page, pageSize, total }) => { > New Job - - + } + /> @@ -165,10 +162,10 @@ export const JobsView: React.FC = ({ jobs, page, pageSize, total }) => { rowsPerPage={pageSize} rowsPerPageOptions={[pageSize]} page={page - 1} - onChangePage={(_, p) => { + onPageChange={(_, p) => { history.push(`/jobs?page=${p + 1}&per=${pageSize}`) }} - onChangeRowsPerPage={() => {}} /* handler required by component, so make it a no-op */ + onRowsPerPageChange={() => {}} /* handler required by component, so make it a no-op */ backIconButtonProps={{ 'aria-label': 'prev-page' }} nextIconButtonProps={{ 'aria-label': 'next-page' }} /> diff --git a/src/screens/KeyManagement/CSAKeyRow.tsx b/src/screens/KeyManagement/CSAKeyRow.tsx index a5a331ae..f4e04db4 100644 --- a/src/screens/KeyManagement/CSAKeyRow.tsx +++ b/src/screens/KeyManagement/CSAKeyRow.tsx @@ -1,8 +1,8 @@ import React from 'react' -import TableCell from '@material-ui/core/TableCell' -import TableRow from '@material-ui/core/TableRow' -import Typography from '@material-ui/core/Typography' +import TableCell from '@mui/material/TableCell' +import TableRow from '@mui/material/TableRow' +import Typography from '@mui/material/Typography' import { CopyIconButton } from 'src/components/Copy/CopyIconButton' diff --git a/src/screens/KeyManagement/CSAKeysCard.tsx b/src/screens/KeyManagement/CSAKeysCard.tsx index 7a8d0bf5..86f2300e 100644 --- a/src/screens/KeyManagement/CSAKeysCard.tsx +++ b/src/screens/KeyManagement/CSAKeysCard.tsx @@ -2,14 +2,14 @@ import React from 'react' import { gql } from '@apollo/client' -import Button from '@material-ui/core/Button' -import Card from '@material-ui/core/Card' -import CardHeader from '@material-ui/core/CardHeader' -import Table from '@material-ui/core/Table' -import TableBody from '@material-ui/core/TableBody' -import TableCell from '@material-ui/core/TableCell' -import TableHead from '@material-ui/core/TableHead' -import TableRow from '@material-ui/core/TableRow' +import Button from '@mui/material/Button' +import Card from '@mui/material/Card' +import CardHeader from '@mui/material/CardHeader' +import Table from '@mui/material/Table' +import TableBody from '@mui/material/TableBody' +import TableCell from '@mui/material/TableCell' +import TableHead from '@mui/material/TableHead' +import TableRow from '@mui/material/TableRow' import { CSAKeyRow } from './CSAKeyRow' import { ErrorRow } from 'src/components/TableRow/ErrorRow' diff --git a/src/screens/KeyManagement/EVMAccountRow.tsx b/src/screens/KeyManagement/EVMAccountRow.tsx index 96c46b01..f707c360 100644 --- a/src/screens/KeyManagement/EVMAccountRow.tsx +++ b/src/screens/KeyManagement/EVMAccountRow.tsx @@ -1,22 +1,21 @@ import React, { useState } from 'react' import { useDispatch } from 'react-redux' -import TableCell from '@material-ui/core/TableCell' -import TableRow from '@material-ui/core/TableRow' -import Typography from '@material-ui/core/Typography' -import Dialog from '@material-ui/core/Dialog' -import Grid from '@material-ui/core/Grid' -import Checkbox from '@material-ui/core/Checkbox' -import FormGroup from '@material-ui/core/FormGroup' -import FormControlLabel from '@material-ui/core/FormControlLabel' +import TableCell from '@mui/material/TableCell' +import TableRow from '@mui/material/TableRow' +import Typography from '@mui/material/Typography' +import Dialog from '@mui/material/Dialog' +import Grid from '@mui/material/Grid' +import Checkbox from '@mui/material/Checkbox' +import FormGroup from '@mui/material/FormGroup' +import FormControlLabel from '@mui/material/FormControlLabel' import { ApolloQueryResult } from '@apollo/client' -import { - createStyles, - withStyles, - Theme, - WithStyles, -} from '@material-ui/core/styles' +import { Theme } from '@mui/material/styles' + +import { WithStyles } from 'src/utils/withStyles' +import { withStyles } from 'src/utils/withStyles' +import { createStyles } from 'src/utils/withStyles' import Button from 'components/Button' import Close from 'components/Icons/Close' @@ -33,8 +32,8 @@ import { TimeAgo } from 'src/components/TimeAgo' const styles = (theme: Theme) => createStyles({ container: { - backgroundColor: theme.palette.common.white, - padding: theme.spacing.unit * 5, + backgroundColor: theme.palette.background.paper, + padding: theme.spacing(5), paddingBottom: 0, }, chainId: { @@ -42,10 +41,10 @@ const styles = (theme: Theme) => textOverflow: 'ellipsis', }, badgePadding: { - paddingLeft: theme.spacing.unit * 2, - paddingRight: theme.spacing.unit * 2, - marginLeft: theme.spacing.unit * -2, - marginRight: theme.spacing.unit * -2, + paddingLeft: theme.spacing(2), + paddingRight: theme.spacing(2), + marginLeft: theme.spacing(-2), + marginRight: theme.spacing(-2), lineHeight: '1rem', }, dialogPaper: { @@ -54,31 +53,31 @@ const styles = (theme: Theme) => minWidth: '670px', maxWidth: '670px', overflow: 'hidden', - borderRadius: theme.spacing.unit * 3, + borderRadius: theme.spacing(3), }, warningText: { fontWeight: 500, - marginLeft: theme.spacing.unit * 3, - marginTop: theme.spacing.unit * 3, - marginBottom: theme.spacing.unit, + marginLeft: theme.spacing(3), + marginTop: theme.spacing(3), + marginBottom: theme.spacing(1), }, closeButton: { - marginRight: theme.spacing.unit * 3, - marginTop: theme.spacing.unit * 3, + marginRight: theme.spacing(3), + marginTop: theme.spacing(3), }, infoText: { - fontSize: theme.spacing.unit * 2, + fontSize: theme.spacing(2), fontWeight: 450, - marginLeft: theme.spacing.unit * 6, + marginLeft: theme.spacing(6), }, modalContent: { width: 'inherit', }, deleteButton: { - marginTop: theme.spacing.unit * 4, + marginTop: theme.spacing(4), }, runJobButton: { - marginBottom: theme.spacing.unit * 3, + marginBottom: theme.spacing(3), }, runJobModalContent: { overflow: 'hidden', @@ -175,7 +174,11 @@ const UnstyledEVMAccountRow: React.FC = ({ - + = ({ container spacing={0} alignItems="center" - justify="center" + justifyContent="center" >