mirror of
https://github.com/scottlamb/moonfire-nvr.git
synced 2025-02-24 03:49:13 -05:00
upgrade material-ui to latest beta
This is a surprisingly complex upgrade. Some relevant changes from [their CHANGELOG](https://github.com/mui-org/material-ui/blob/v5.0.0-beta.3/CHANGELOG.md): * @material-ui/core/styles no longer re-exports some stuff from @material-ui/styles * there's no more defaultTheme, so tests need to provide one * select's onChange has a new type; match that. I haven't actually tried that the string form (apparently from autofill) works correctly. * checkboxes no longer default to the secondary color; explicitly request this in some places. * checkbox no longer has a checked argument; use event.target.checked instead. * date pickers have switched to the new style system, so I had to redo how I was overridding their spacing for desktop. * LoadingButton now has a loading property, not pending * createMuiTheme is no createTheme
This commit is contained in:
parent
c55032dfcd
commit
39a63e03ae
32790
ui/package-lock.json
generated
32790
ui/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -6,21 +6,22 @@
|
||||
"@emotion/react": "^11.1.5",
|
||||
"@emotion/styled": "^11.1.5",
|
||||
"@fontsource/roboto": "^4.2.1",
|
||||
"@material-ui/core": "^5.0.0-alpha.26",
|
||||
"@material-ui/icons": "^5.0.0-alpha.26",
|
||||
"@material-ui/lab": "^5.0.0-alpha.26",
|
||||
"@material-ui/core": "^5.0.0-beta.3",
|
||||
"@material-ui/icons": "^5.0.0-beta.1",
|
||||
"@material-ui/lab": "^5.0.0-alpha.42",
|
||||
"@material-ui/styles": "^5.0.0-beta.3",
|
||||
"@react-hook/resize-observer": "^1.2.0",
|
||||
"@types/jest": "^26.0.20",
|
||||
"@types/node": "^14.14.22",
|
||||
"@types/node": "^16.3.1",
|
||||
"@types/react": "^17.0.0",
|
||||
"@types/react-dom": "^17.0.0",
|
||||
"date-fns": "^2.18.0",
|
||||
"date-fns-tz": "^1.1.3",
|
||||
"gzipper": "^4.4.0",
|
||||
"gzipper": "^5.0.0",
|
||||
"react": "^17.0.1",
|
||||
"react-dom": "^17.0.1",
|
||||
"react-scripts": "4.0.1",
|
||||
"typescript": "^4.1.3"
|
||||
"react-scripts": "^4.0.3",
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
@ -64,10 +65,10 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/jest-dom": "^5.11.4",
|
||||
"@testing-library/react": "^11.1.0",
|
||||
"@testing-library/user-event": "^12.6.3",
|
||||
"http-proxy-middleware": "^1.0.6",
|
||||
"msw": "^0.26.1",
|
||||
"prettier": "2.2.1"
|
||||
"@testing-library/react": "^11.2.7",
|
||||
"@testing-library/user-event": "^12.8.3",
|
||||
"http-proxy-middleware": "^2.0.1",
|
||||
"msw": "^0.26.2",
|
||||
"prettier": "^2.2.1"
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,8 @@ import Button from "@material-ui/core/Button";
|
||||
import IconButton from "@material-ui/core/IconButton";
|
||||
import Menu from "@material-ui/core/Menu";
|
||||
import MenuItem from "@material-ui/core/MenuItem";
|
||||
import { createStyles, makeStyles, Theme } from "@material-ui/core/styles";
|
||||
import { Theme } from "@material-ui/core/styles";
|
||||
import { createStyles, makeStyles } from "@material-ui/styles";
|
||||
import Toolbar from "@material-ui/core/Toolbar";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import AccountCircle from "@material-ui/icons/AccountCircle";
|
||||
@ -38,10 +39,8 @@ interface Props {
|
||||
// https://material-ui.com/components/app-bar/
|
||||
function MoonfireMenu(props: Props) {
|
||||
const classes = useStyles();
|
||||
const [
|
||||
accountMenuAnchor,
|
||||
setAccountMenuAnchor,
|
||||
] = React.useState<null | HTMLElement>(null);
|
||||
const [accountMenuAnchor, setAccountMenuAnchor] =
|
||||
React.useState<null | HTMLElement>(null);
|
||||
|
||||
const handleMenu = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setAccountMenuAnchor(event.currentTarget);
|
||||
@ -94,7 +93,6 @@ function MoonfireMenu(props: Props) {
|
||||
</IconButton>
|
||||
<Menu
|
||||
anchorEl={accountMenuAnchor}
|
||||
getContentAnchorEl={null}
|
||||
keepMounted
|
||||
anchorOrigin={{
|
||||
vertical: "bottom",
|
||||
|
@ -4,12 +4,6 @@
|
||||
|
||||
import Avatar from "@material-ui/core/Avatar";
|
||||
import Container from "@material-ui/core/Container";
|
||||
import {
|
||||
createStyles,
|
||||
Theme,
|
||||
WithStyles,
|
||||
withStyles,
|
||||
} from "@material-ui/core/styles";
|
||||
import BugReportIcon from "@material-ui/icons/BugReport";
|
||||
import React from "react";
|
||||
|
||||
@ -17,16 +11,7 @@ interface State {
|
||||
error: any;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
avatar: {
|
||||
backgroundColor: theme.palette.secondary.main,
|
||||
float: "left",
|
||||
marginRight: "1em",
|
||||
},
|
||||
});
|
||||
|
||||
interface Props extends WithStyles<typeof styles> {
|
||||
interface Props {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
@ -55,7 +40,7 @@ class MoonfireErrorBoundary extends React.Component<Props, State> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { classes, children } = this.props;
|
||||
const { children } = this.props;
|
||||
|
||||
if (this.state.error !== null) {
|
||||
var error;
|
||||
@ -74,7 +59,13 @@ class MoonfireErrorBoundary extends React.Component<Props, State> {
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Avatar className={classes.avatar}>
|
||||
<Avatar
|
||||
sx={{
|
||||
float: "left",
|
||||
bgcolor: "secondary.main",
|
||||
marginRight: "1em",
|
||||
}}
|
||||
>
|
||||
<BugReportIcon color="primary" />
|
||||
</Avatar>
|
||||
<h1>Error</h1>
|
||||
@ -130,4 +121,4 @@ class MoonfireErrorBoundary extends React.Component<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
export default withStyles(styles)(MoonfireErrorBoundary);
|
||||
export default MoonfireErrorBoundary;
|
||||
|
@ -53,7 +53,13 @@ const DisplaySelector = (props: Props) => {
|
||||
id="split90k"
|
||||
size="small"
|
||||
value={props.split90k}
|
||||
onChange={(e) => props.setSplit90k(e.target.value)}
|
||||
onChange={(e) =>
|
||||
props.setSplit90k(
|
||||
typeof e.target.value === "string"
|
||||
? parseInt(e.target.value)
|
||||
: e.target.value
|
||||
)
|
||||
}
|
||||
displayEmpty
|
||||
>
|
||||
{DURATIONS.map(([l, d]) => (
|
||||
@ -72,10 +78,9 @@ const DisplaySelector = (props: Props) => {
|
||||
<Checkbox
|
||||
checked={props.trimStartAndEnd}
|
||||
size="small"
|
||||
onChange={(_, checked: boolean) =>
|
||||
props.setTrimStartAndEnd(checked)
|
||||
}
|
||||
onChange={(event) => props.setTrimStartAndEnd(event.target.checked)}
|
||||
name="trim-start-and-end"
|
||||
color="secondary"
|
||||
/>
|
||||
}
|
||||
label="Trim start and end"
|
||||
@ -87,8 +92,9 @@ const DisplaySelector = (props: Props) => {
|
||||
<Checkbox
|
||||
checked={props.timestampTrack}
|
||||
size="small"
|
||||
onChange={(_, checked: boolean) => props.setTimestampTrack(checked)}
|
||||
onChange={(event) => props.setTimestampTrack(event.target.checked)}
|
||||
name="timestamp-track"
|
||||
color="secondary"
|
||||
/>
|
||||
}
|
||||
label="Timestamp track"
|
||||
|
@ -5,7 +5,8 @@
|
||||
import Card from "@material-ui/core/Card";
|
||||
import { Camera, Stream, StreamType } from "../types";
|
||||
import Checkbox from "@material-ui/core/Checkbox";
|
||||
import { makeStyles, useTheme } from "@material-ui/core/styles";
|
||||
import { useTheme } from "@material-ui/core/styles";
|
||||
import { makeStyles } from "@material-ui/styles";
|
||||
|
||||
interface Props {
|
||||
cameras: Camera[];
|
||||
@ -86,14 +87,17 @@ const StreamMultiSelector = ({ cameras, selected, setSelected }: Props) => {
|
||||
function checkbox(st: StreamType) {
|
||||
const s = c.streams[st];
|
||||
if (s === undefined) {
|
||||
return <Checkbox className={classes.check} disabled />;
|
||||
return (
|
||||
<Checkbox className={classes.check} color="secondary" disabled />
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Checkbox
|
||||
className={classes.check}
|
||||
size="small"
|
||||
checked={selected.has(s)}
|
||||
onChange={(_, checked: boolean) => setStream(s, checked)}
|
||||
color="secondary"
|
||||
onChange={(event) => setStream(s, event.target.checked)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -3,7 +3,9 @@
|
||||
// SPDX-License-Identifier: GPL-v3.0-or-later WITH GPL-3.0-linking-exception
|
||||
|
||||
import { Stream } from "../types";
|
||||
import StaticDatePicker from "@material-ui/lab/StaticDatePicker";
|
||||
import StaticDatePicker, {
|
||||
StaticDatePickerProps,
|
||||
} from "@material-ui/lab/StaticDatePicker";
|
||||
import React, { useEffect } from "react";
|
||||
import { zonedTimeToUtc } from "date-fns-tz";
|
||||
import { addDays, addMilliseconds, differenceInMilliseconds } from "date-fns";
|
||||
@ -17,6 +19,7 @@ import Radio from "@material-ui/core/Radio";
|
||||
import RadioGroup from "@material-ui/core/RadioGroup";
|
||||
import TimePicker, { TimePickerProps } from "@material-ui/lab/TimePicker";
|
||||
import Collapse from "@material-ui/core/Collapse";
|
||||
import Box from "@material-ui/core/Box";
|
||||
|
||||
interface Props {
|
||||
selectedStreams: Set<Stream>;
|
||||
@ -39,6 +42,47 @@ const MyTimePicker = (
|
||||
/>
|
||||
);
|
||||
|
||||
const SmallStaticDatePicker = (props: StaticDatePickerProps<Date>) => {
|
||||
// The spacing defined at https://material.io/components/date-pickers#specs
|
||||
// seems plenty big enough (on desktop). Not sure why material-ui wants
|
||||
// to make it bigger but that doesn't work well with our layout.
|
||||
// This adjustment is a fragile hack but seems to work for now.
|
||||
const DATE_SIZE = 32;
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
"@media (pointer: fine)": {
|
||||
"& > div": {
|
||||
minWidth: 256,
|
||||
},
|
||||
"& > div > div, & > div > div > div, & .MuiCalendarPicker-root": {
|
||||
width: 256,
|
||||
},
|
||||
"& .MuiTypography-caption": {
|
||||
width: DATE_SIZE,
|
||||
margin: 0,
|
||||
},
|
||||
"& .PrivatePickersSlideTransition-root": {
|
||||
minHeight: DATE_SIZE * 6,
|
||||
},
|
||||
'& .PrivatePickersSlideTransition-root [role="row"]': {
|
||||
margin: 0,
|
||||
},
|
||||
"& .MuiPickersDay-dayWithMargin": {
|
||||
margin: 0,
|
||||
},
|
||||
"& .MuiPickersDay-root": {
|
||||
width: DATE_SIZE,
|
||||
height: DATE_SIZE,
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<StaticDatePicker {...props} />
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Combines the date-part of <tt>dayMillis</tt> and the time part of
|
||||
* <tt>time</tt>. If <tt>time</tt> is null, assume it reaches the end of the
|
||||
@ -260,7 +304,7 @@ const TimerangeSelector = ({
|
||||
<Card sx={{ padding: theme.spacing(1) }}>
|
||||
<div>
|
||||
<FormLabel component="legend">From</FormLabel>
|
||||
<StaticDatePicker
|
||||
<SmallStaticDatePicker
|
||||
displayStaticWrapperAs="desktop"
|
||||
value={startDate}
|
||||
shouldDisableDate={shouldDisableDate}
|
||||
@ -299,17 +343,17 @@ const TimerangeSelector = ({
|
||||
>
|
||||
<FormControlLabel
|
||||
value="same-day"
|
||||
control={<Radio size="small" />}
|
||||
control={<Radio size="small" color="secondary" />}
|
||||
label="Same day"
|
||||
/>
|
||||
<FormControlLabel
|
||||
value="other-day"
|
||||
control={<Radio size="small" />}
|
||||
control={<Radio size="small" color="secondary" />}
|
||||
label="Other day"
|
||||
/>
|
||||
</RadioGroup>
|
||||
<Collapse in={days.endType === "other-day"}>
|
||||
<StaticDatePicker
|
||||
<SmallStaticDatePicker
|
||||
displayStaticWrapperAs="desktop"
|
||||
value={endDate}
|
||||
shouldDisableDate={(d: Date | null) =>
|
||||
|
@ -5,7 +5,8 @@
|
||||
import Box from "@material-ui/core/Box";
|
||||
import Modal from "@material-ui/core/Modal";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
import { makeStyles, Theme } from "@material-ui/core/styles";
|
||||
import { Theme } from "@material-ui/core/styles";
|
||||
import { makeStyles } from "@material-ui/styles";
|
||||
import Table from "@material-ui/core/Table";
|
||||
import TableContainer from "@material-ui/core/TableContainer";
|
||||
import utcToZonedTime from "date-fns-tz/utcToZonedTime";
|
||||
|
@ -2,12 +2,13 @@
|
||||
// Copyright (C) 2021 The Moonfire NVR Authors; see AUTHORS and LICENSE.txt.
|
||||
// SPDX-License-Identifier: GPL-v3.0-or-later WITH GPL-3.0-linking-exception
|
||||
|
||||
import Select from "@material-ui/core/Select";
|
||||
import Select, { SelectChangeEvent } from "@material-ui/core/Select";
|
||||
import MenuItem from "@material-ui/core/MenuItem";
|
||||
import React, { useReducer, useState } from "react";
|
||||
import { Camera } from "../types";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import { makeStyles } from "@material-ui/styles";
|
||||
import useResizeObserver from "@react-hook/resize-observer";
|
||||
import { Theme } from "@material-ui/core/styles";
|
||||
|
||||
export interface Layout {
|
||||
className: string;
|
||||
@ -23,7 +24,7 @@ const LAYOUTS: Layout[] = [
|
||||
];
|
||||
const MAX_CAMERAS = 9;
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
const useStyles = makeStyles((theme: Theme) => ({
|
||||
root: {
|
||||
flex: "1 0 0",
|
||||
color: "white",
|
||||
@ -98,7 +99,13 @@ export const MultiviewChooser = (props: MultiviewChooserProps) => {
|
||||
<Select
|
||||
id="layout"
|
||||
value={props.layoutIndex}
|
||||
onChange={(e) => props.onChoice(e.target.value)}
|
||||
onChange={(e) =>
|
||||
props.onChoice(
|
||||
typeof e.target.value === "string"
|
||||
? parseInt(e.target.value)
|
||||
: e.target.value
|
||||
)
|
||||
}
|
||||
size="small"
|
||||
sx={{
|
||||
// Hacky attempt to style for the app menu.
|
||||
@ -238,10 +245,16 @@ interface MonoviewProps {
|
||||
|
||||
/** A single pane of a Multiview, including its camera chooser. */
|
||||
const Monoview = (props: MonoviewProps) => {
|
||||
const handleChange = (event: SelectChangeEvent<number | null>) => {
|
||||
const {
|
||||
target: { value },
|
||||
} = event;
|
||||
props.onSelect(typeof value === "string" ? parseInt(value) : value);
|
||||
};
|
||||
const chooser = (
|
||||
<Select
|
||||
value={props.cameraIndex == null ? undefined : props.cameraIndex}
|
||||
onChange={(e) => props.onSelect(e.target.value ?? null)}
|
||||
onChange={handleChange}
|
||||
displayEmpty
|
||||
size="small"
|
||||
sx={{
|
||||
|
@ -38,8 +38,8 @@ afterEach(() => {
|
||||
});
|
||||
|
||||
test("success", async () => {
|
||||
const handleClose = jest.fn();
|
||||
const onSuccess = jest.fn();
|
||||
const handleClose = jest.fn().mockName("handleClose");
|
||||
const onSuccess = jest.fn().mockName("handleOpen");
|
||||
renderWithCtx(
|
||||
<Login open={true} onSuccess={onSuccess} handleClose={handleClose} />
|
||||
);
|
||||
@ -54,8 +54,8 @@ test("success", async () => {
|
||||
// so the delay("infinite") request just sticks around, even though the fetch
|
||||
// has been aborted. Maybe https://github.com/mswjs/msw/pull/585 will fix it.
|
||||
xtest("close while pending", async () => {
|
||||
const handleClose = jest.fn();
|
||||
const onSuccess = jest.fn();
|
||||
const handleClose = jest.fn().mockName("handleClose");
|
||||
const onSuccess = jest.fn().mockName("handleOpen");
|
||||
const { rerender } = renderWithCtx(
|
||||
<Login open={true} onSuccess={onSuccess} handleClose={handleClose} />
|
||||
);
|
||||
@ -73,8 +73,8 @@ xtest("close while pending", async () => {
|
||||
});
|
||||
|
||||
test("bad credentials", async () => {
|
||||
const handleClose = jest.fn();
|
||||
const onSuccess = jest.fn();
|
||||
const handleClose = jest.fn().mockName("handleClose");
|
||||
const onSuccess = jest.fn().mockName("handleOpen");
|
||||
renderWithCtx(
|
||||
<Login open={true} onSuccess={onSuccess} handleClose={handleClose} />
|
||||
);
|
||||
@ -85,8 +85,8 @@ test("bad credentials", async () => {
|
||||
});
|
||||
|
||||
test("server error", async () => {
|
||||
const handleClose = jest.fn();
|
||||
const onSuccess = jest.fn();
|
||||
const handleClose = jest.fn().mockName("handleClose");
|
||||
const onSuccess = jest.fn().mockName("handleOpen");
|
||||
renderWithCtx(
|
||||
<Login open={true} onSuccess={onSuccess} handleClose={handleClose} />
|
||||
);
|
||||
@ -100,8 +100,8 @@ test("server error", async () => {
|
||||
});
|
||||
|
||||
test("network error", async () => {
|
||||
const handleClose = jest.fn();
|
||||
const onSuccess = jest.fn();
|
||||
const handleClose = jest.fn().mockName("handleClose");
|
||||
const onSuccess = jest.fn().mockName("handleOpen");
|
||||
renderWithCtx(
|
||||
<Login open={true} onSuccess={onSuccess} handleClose={handleClose} />
|
||||
);
|
||||
|
@ -8,7 +8,8 @@ import DialogActions from "@material-ui/core/DialogActions";
|
||||
import DialogTitle from "@material-ui/core/DialogTitle";
|
||||
import FormControl from "@material-ui/core/FormControl";
|
||||
import FormHelperText from "@material-ui/core/FormHelperText";
|
||||
import { makeStyles, Theme } from "@material-ui/core/styles";
|
||||
import { Theme } from "@material-ui/core/styles";
|
||||
import { makeStyles } from "@material-ui/styles";
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
import LockOutlinedIcon from "@material-ui/icons/LockOutlined";
|
||||
import LoadingButton from "@material-ui/lab/LoadingButton";
|
||||
@ -62,15 +63,15 @@ const Login = ({ open, onSuccess, handleClose }: Props) => {
|
||||
const passwordRef = React.useRef<HTMLInputElement>(null);
|
||||
|
||||
const [error, setError] = React.useState<string | null>(null);
|
||||
const [pending, setPending] = React.useState<api.LoginRequest | null>(null);
|
||||
const [loading, setLoading] = React.useState<api.LoginRequest | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (pending === null) {
|
||||
if (loading === null) {
|
||||
return;
|
||||
}
|
||||
let abort = new AbortController();
|
||||
const send = async (signal: AbortSignal) => {
|
||||
let response = await api.login(pending, { signal });
|
||||
let response = await api.login(loading, { signal });
|
||||
switch (response.status) {
|
||||
case "aborted":
|
||||
break;
|
||||
@ -83,10 +84,10 @@ const Login = ({ open, onSuccess, handleClose }: Props) => {
|
||||
key: "login-error",
|
||||
});
|
||||
}
|
||||
setPending(null);
|
||||
setLoading(null);
|
||||
break;
|
||||
case "success":
|
||||
setPending(null);
|
||||
setLoading(null);
|
||||
onSuccess();
|
||||
}
|
||||
};
|
||||
@ -94,16 +95,16 @@ const Login = ({ open, onSuccess, handleClose }: Props) => {
|
||||
return () => {
|
||||
abort.abort();
|
||||
};
|
||||
}, [pending, onSuccess, snackbars]);
|
||||
}, [loading, onSuccess, snackbars]);
|
||||
|
||||
const onSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
|
||||
// Suppress duplicate login attempts when latency is high.
|
||||
if (pending !== null) {
|
||||
if (loading !== null) {
|
||||
return;
|
||||
}
|
||||
setPending({
|
||||
setLoading({
|
||||
username: usernameRef.current!.value,
|
||||
password: passwordRef.current!.value,
|
||||
});
|
||||
@ -154,7 +155,7 @@ const Login = ({ open, onSuccess, handleClose }: Props) => {
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
pending={pending !== null}
|
||||
loading={loading !== null}
|
||||
>
|
||||
Log in
|
||||
</LoadingButton>
|
||||
|
@ -10,48 +10,3 @@ body,
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@media (pointer: fine) {
|
||||
/*
|
||||
* The spacing defined at https://material.io/components/date-pickers#specs
|
||||
* seems plenty big enough (on desktop). Not sure why material-ui wants
|
||||
* to make it bigger but that doesn't work well with our layout.
|
||||
*
|
||||
* Defining this here (in a global .css file) is the first way I could find
|
||||
* to override properties of .MuiPickerStaticWrapper-root. It's unfortunately
|
||||
* a _parent_ of the element that gets the <DatePicker>'s className applied,
|
||||
* and it doesn't seem to be exposed for a global style override
|
||||
* <https://next.material-ui.com/customization/theme-components/#global-style-overrides>.
|
||||
*/
|
||||
.MuiPickersStaticWrapper-root {
|
||||
min-width: 256px !important;
|
||||
}
|
||||
|
||||
/* Increased specificity here so it doesn't apply to the popup time picker. */
|
||||
.MuiPickersStaticWrapper-root .MuiPickerView-root {
|
||||
width: 256px !important;
|
||||
}
|
||||
|
||||
.MuiPickersCalendar-root {
|
||||
min-height: 160px !important;
|
||||
}
|
||||
|
||||
.MuiPickersCalendar-weekDayLabel {
|
||||
width: 32px !important;
|
||||
height: 32px !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.MuiPickersCalendar-week {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.MuiPickersDay-dayWithMargin {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.MuiPickersDay-root {
|
||||
width: 32px !important;
|
||||
height: 32px !important;
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
// SPDX-License-Identifier: GPL-v3.0-or-later WITH GPL-3.0-linking-exception
|
||||
|
||||
import CssBaseline from "@material-ui/core/CssBaseline";
|
||||
import { ThemeProvider, createMuiTheme } from "@material-ui/core/styles";
|
||||
import { ThemeProvider, createTheme } from "@material-ui/core/styles";
|
||||
import StyledEngineProvider from "@material-ui/core/StyledEngineProvider";
|
||||
import LocalizationProvider from "@material-ui/lab/LocalizationProvider";
|
||||
import "@fontsource/roboto";
|
||||
@ -15,7 +15,7 @@ import { SnackbarProvider } from "./snackbars";
|
||||
import AdapterDateFns from "@material-ui/lab/AdapterDateFns";
|
||||
import "./index.css";
|
||||
|
||||
const theme = createMuiTheme({
|
||||
const theme = createTheme({
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#000000",
|
||||
|
@ -93,7 +93,8 @@ const ctx = React.createContext<Snackbars | null>(null);
|
||||
// and I couldn't figure out a way to do that with hooks.
|
||||
export class SnackbarProvider
|
||||
extends React.Component<SnackbarProviderProps, State>
|
||||
implements Snackbars {
|
||||
implements Snackbars
|
||||
{
|
||||
constructor(props: SnackbarProviderProps) {
|
||||
super(props);
|
||||
this.state = { queue: [] };
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Copyright (C) 2021 The Moonfire NVR Authors; see AUTHORS and LICENSE.txt.
|
||||
// SPDX-License-Identifier: GPL-v3.0-or-later WITH GPL-3.0-linking-exception
|
||||
|
||||
import { createTheme, ThemeProvider } from "@material-ui/core/styles";
|
||||
import { render } from "@testing-library/react";
|
||||
import { SnackbarProvider } from "./snackbars";
|
||||
|
||||
@ -10,7 +11,9 @@ export function renderWithCtx(
|
||||
): Pick<ReturnType<typeof render>, "rerender"> {
|
||||
function wrapped(children: React.ReactElement): React.ReactElement {
|
||||
return (
|
||||
<SnackbarProvider autoHideDuration={5000}>{children}</SnackbarProvider>
|
||||
<ThemeProvider theme={createTheme()}>
|
||||
<SnackbarProvider autoHideDuration={5000}>{children}</SnackbarProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
const { rerender } = render(wrapped(children));
|
||||
|
Loading…
x
Reference in New Issue
Block a user