diff --git a/src/components/settings/InactiveSettingsCard.tsx b/src/components/settings/InactiveSettingsCard.tsx index cc722142..ba750c0b 100644 --- a/src/components/settings/InactiveSettingsCard.tsx +++ b/src/components/settings/InactiveSettingsCard.tsx @@ -1,6 +1,7 @@ import { faPencil } from '@fortawesome/free-solid-svg-icons' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { Box, Button, Card, CardContent, Divider, Grid, Typography } from '@mui/material' +import { Box, Button, Card, CardContent, Divider, Typography } from '@mui/material' +import Grid from '@mui/material/Grid2' import { Stack } from '@mui/system' import { FC, useState } from 'react' import useAuth from '../../contexts/useAuth' @@ -68,7 +69,7 @@ const InactiveSettingsCard: FC = () => { {setting.value.description} - + Author @@ -76,7 +77,7 @@ const InactiveSettingsCard: FC = () => { {setting.value.author} - + Homepage @@ -84,7 +85,7 @@ const InactiveSettingsCard: FC = () => { {setting.value.homepage} - + E-Mail @@ -92,7 +93,7 @@ const InactiveSettingsCard: FC = () => { {setting.value.email} - + Twitter diff --git a/src/components/settings/InactiveSettingsForm.tsx b/src/components/settings/InactiveSettingsForm.tsx index 49c6d1da..f5cbe49f 100644 --- a/src/components/settings/InactiveSettingsForm.tsx +++ b/src/components/settings/InactiveSettingsForm.tsx @@ -1,4 +1,5 @@ -import { FormGroup, Grid, TextField } from '@mui/material' +import { FormGroup, TextField } from '@mui/material' +import Grid from '@mui/material/Grid2' import { useQueryClient } from '@tanstack/react-query' import { FC } from 'react' import { SubmitHandler, useForm } from 'react-hook-form' @@ -45,17 +46,17 @@ const InactiveSettingsForm: FC = ({ name, setting, callback }) => { return (
- + - + - + = ({ name, setting, callback }) => { /> - + - + - + - + diff --git a/src/components/settings/RefreshIntervalForm.tsx b/src/components/settings/RefreshIntervalForm.tsx index d8f1cc13..51bdfaf2 100644 --- a/src/components/settings/RefreshIntervalForm.tsx +++ b/src/components/settings/RefreshIntervalForm.tsx @@ -1,4 +1,5 @@ -import { FormGroup, Grid, TextField } from '@mui/material' +import { FormGroup, TextField } from '@mui/material' +import Grid from '@mui/material/Grid2' import { useQueryClient } from '@tanstack/react-query' import { FC } from 'react' import { SubmitHandler, useForm } from 'react-hook-form' @@ -33,7 +34,7 @@ const RefreshIntervalForm: FC = ({ name, setting, callback }) => { return ( - + = ({ callback, ticker }) => { return ( - + You need to create a application password in Bluesky. {errors.root?.authenticationFailed && ( - + {errors.root.authenticationFailed.message} )} - + } label="Active" /> - + - + diff --git a/src/components/ticker/MastodonForm.tsx b/src/components/ticker/MastodonForm.tsx index c0d45ac3..913b2e0e 100644 --- a/src/components/ticker/MastodonForm.tsx +++ b/src/components/ticker/MastodonForm.tsx @@ -1,4 +1,5 @@ -import { Checkbox, FormControlLabel, FormGroup, Grid, TextField, Typography } from '@mui/material' +import { Checkbox, FormControlLabel, FormGroup, TextField, Typography } from '@mui/material' +import Grid from '@mui/material/Grid2' import { useQueryClient } from '@tanstack/react-query' import { FC } from 'react' import { SubmitHandler, useForm } from 'react-hook-form' @@ -31,7 +32,7 @@ const MastodonForm: FC = ({ callback, ticker }) => { return ( - + You need to create a Application for Ticker in Mastodon. Go to your profile settings in Mastodon. You find a menu point "Developer" where you need to create an Application. After saving you see the required secrets and tokens. @@ -40,27 +41,27 @@ const MastodonForm: FC = ({ callback, ticker }) => { Required Scopes: read write write:media write:statuses - + } label="Active" /> - + - + - + - + diff --git a/src/components/ticker/SignalGroupAdminForm.tsx b/src/components/ticker/SignalGroupAdminForm.tsx index 0993625a..d8f2fc3c 100644 --- a/src/components/ticker/SignalGroupAdminForm.tsx +++ b/src/components/ticker/SignalGroupAdminForm.tsx @@ -1,6 +1,7 @@ import { faPhone } from '@fortawesome/free-solid-svg-icons' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { Alert, FormGroup, Grid, InputAdornment, TextField } from '@mui/material' +import { Alert, FormGroup, InputAdornment, TextField } from '@mui/material' +import Grid from '@mui/material/Grid2' import { FC } from 'react' import { useForm } from 'react-hook-form' import { Ticker, TickerSignalGroupAdminFormData, putTickerSignalGroupAdminApi } from '../../api/Ticker' @@ -43,10 +44,10 @@ const SignalGroupAdminForm: FC = ({ callback, ticker, setSubmitting }) => return ( - + Only do this if extra members with write access are needed. - + = ({ callback, ticker }) => { return ( - + Only public Telegram Channels are supported. The name of the Channel is prefixed with an @ (e.g. @channel). - + } label="Active" /> - + = ({ ticker, isLoading }) => { if (ticker === undefined || isLoading) { return ( - - {headline()} - - + {headline()} + @@ -60,15 +59,13 @@ const Ticker: FC = ({ ticker, isLoading }) => { return ( - - {headline()} - + {headline()} {!ticker.active ? ( - + This ticker is currently disabled. ) : null} - + {user?.roles.includes('admin') ? ( <> @@ -81,15 +78,15 @@ const Ticker: FC = ({ ticker, isLoading }) => { ) : null} - - + + - + diff --git a/src/components/ticker/TickerIntegrations.tsx b/src/components/ticker/TickerIntegrations.tsx index 3f5190ed..4c44bc29 100644 --- a/src/components/ticker/TickerIntegrations.tsx +++ b/src/components/ticker/TickerIntegrations.tsx @@ -1,4 +1,4 @@ -import { Grid } from '@mui/material' +import Grid from '@mui/material/Grid2' import { FC } from 'react' import { Ticker } from '../../api/Ticker' import BlueskyCard from './BlueskyCard' @@ -14,19 +14,19 @@ interface Props { const TickerIntegrations: FC = ({ ticker }) => { return ( - + - + - + - + - + diff --git a/src/components/ticker/WebsiteCard.test.tsx b/src/components/ticker/WebsiteCard.test.tsx index 62de0b16..151c55a6 100644 --- a/src/components/ticker/WebsiteCard.test.tsx +++ b/src/components/ticker/WebsiteCard.test.tsx @@ -9,7 +9,7 @@ import WebsiteCard from './WebsiteCard' const token = sign({ id: 1, email: 'user@example.org', roles: ['user'], exp: new Date().getTime() / 1000 + 600 }, 'secret') -describe('WebsiteForm', () => { +describe('WebsiteCard', () => { beforeAll(() => { localStorage.setItem('token', token) }) diff --git a/src/components/ticker/WebsiteForm.tsx b/src/components/ticker/WebsiteForm.tsx index cfd167b9..8519b1d2 100644 --- a/src/components/ticker/WebsiteForm.tsx +++ b/src/components/ticker/WebsiteForm.tsx @@ -1,5 +1,6 @@ import { Delete } from '@mui/icons-material' -import { Button, FormControl, FormGroup, FormHelperText, Grid, IconButton, InputAdornment, OutlinedInput, Typography } from '@mui/material' +import { Button, FormControl, FormGroup, FormHelperText, IconButton, InputAdornment, OutlinedInput, Typography } from '@mui/material' +import Grid from '@mui/material/Grid2' import { useQueryClient } from '@tanstack/react-query' import { FC } from 'react' import { SubmitHandler, useFieldArray, useForm } from 'react-hook-form' @@ -43,10 +44,10 @@ const WebsiteForm: FC = ({ callback, ticker }) => { return ( - + You can configure website origins for your ticker. The ticker will only be reachable from the configured websites. - + {fields.map((field, index) => ( diff --git a/src/components/ticker/form/TickerForm.test.tsx b/src/components/ticker/form/TickerForm.test.tsx new file mode 100644 index 00000000..2c409432 --- /dev/null +++ b/src/components/ticker/form/TickerForm.test.tsx @@ -0,0 +1,60 @@ +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { render, screen } from '@testing-library/react' +import sign from 'jwt-encode' +import { MemoryRouter } from 'react-router' +import { Ticker } from '../../../api/Ticker' +import { AuthProvider } from '../../../contexts/AuthContext' +import TickerForm from './TickerForm' + +const token = sign({ id: 1, email: 'user@example.org', roles: ['user'], exp: new Date().getTime() / 1000 + 600 }, 'secret') + +describe('TickerForm', () => { + beforeAll(() => { + localStorage.setItem('token', token) + }) + + const callback = vi.fn() + + const ticker = () => { + return { + id: 1, + title: 'Ticker', + active: true, + information: {}, + location: {}, + } as Ticker + } + + beforeEach(() => { + fetchMock.resetMocks() + }) + + function setup(ticker: Ticker) { + const client = new QueryClient({ + defaultOptions: { + queries: { + retry: false, + }, + }, + }) + return render( + + + +
+ + +
+
+
+
+ ) + } + + it('should render the component', async () => { + setup(ticker()) + + expect(screen.getByRole('textbox', { name: 'Title' })).toBeInTheDocument() + expect(screen.getByRole('checkbox', { name: 'Active' })).toBeInTheDocument() + }) +}) diff --git a/src/components/ticker/form/TickerForm.tsx b/src/components/ticker/form/TickerForm.tsx index 94cec93b..79d75911 100644 --- a/src/components/ticker/form/TickerForm.tsx +++ b/src/components/ticker/form/TickerForm.tsx @@ -1,4 +1,5 @@ -import { Alert, Button, FormGroup, Grid, Stack, Typography } from '@mui/material' +import { Alert, Button, FormGroup, Stack, Typography } from '@mui/material' +import Grid from '@mui/material/Grid2' import { useQueryClient } from '@tanstack/react-query' import React, { FC, useCallback, useEffect } from 'react' import { FormProvider, SubmitHandler, useForm } from 'react-hook-form' @@ -94,67 +95,67 @@ const TickerForm: FC = ({ callback, id, ticker }) => { - + - + </FormGroup> </Grid> - <Grid item xs={12}> + <Grid size={{ xs: 12 }}> <FormGroup> <Description /> </FormGroup> </Grid> - <Grid item xs={12}> + <Grid size={{ xs: 12 }}> <Typography component="h6" variant="h6"> Information </Typography> </Grid> - <Grid item sm={6} xs={12}> + <Grid size={{ sm: 6, xs: 12 }}> <FormGroup> <Author /> </FormGroup> </Grid> - <Grid item sm={6} xs={12}> + <Grid size={{ sm: 6, xs: 12 }}> <FormGroup> <Url /> </FormGroup> </Grid> - <Grid item sm={6} xs={12}> + <Grid size={{ sm: 6, xs: 12 }}> <FormGroup> <Email /> </FormGroup> </Grid> - <Grid item sm={6} xs={12}> + <Grid size={{ sm: 6, xs: 12 }}> <FormGroup> <Twitter /> </FormGroup> </Grid> - <Grid item sm={6} xs={12}> + <Grid size={{ sm: 6, xs: 12 }}> <FormGroup> <Facebook /> </FormGroup> </Grid> - <Grid item sm={6} xs={12}> + <Grid size={{ sm: 6, xs: 12 }}> <FormGroup> <Telegram /> </FormGroup> </Grid> - <Grid item sm={6} xs={12}> + <Grid size={{ sm: 6, xs: 12 }}> <FormGroup> <Mastodon /> </FormGroup> </Grid> - <Grid item sm={6} xs={12}> + <Grid size={{ sm: 6, xs: 12 }}> <FormGroup> <Bluesky /> </FormGroup> </Grid> - <Grid item xs={12}> + <Grid size={{ xs: 12 }}> <Typography component="h6" variant="h6"> Location </Typography> @@ -166,7 +167,7 @@ const TickerForm: FC<Props> = ({ callback, id, ticker }) => { </code> </Alert> </Grid> - <Grid item xs={12}> + <Grid size={{ xs: 12 }}> <Stack direction="row" spacing={1}> <LocationSearch callback={onLocationChange} /> <Button disabled={ticker?.location.lat === 0 && ticker.location.lon === 0} onClick={onLoctionReset} variant="outlined"> @@ -175,7 +176,7 @@ const TickerForm: FC<Props> = ({ callback, id, ticker }) => { </Stack> </Grid> {position.lat !== 0 && position.lon !== 0 ? ( - <Grid item xs={12}> + <Grid size={{ xs: 12 }}> <MapContainer center={[position.lat, position.lon]} scrollWheelZoom={false} style={{ height: 200 }} zoom={10}> <TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" /> <Marker position={[position.lat, position.lon]} /> diff --git a/src/components/user/UserChangePasswordForm.tsx b/src/components/user/UserChangePasswordForm.tsx index 66a3e27f..0c05c7cc 100644 --- a/src/components/user/UserChangePasswordForm.tsx +++ b/src/components/user/UserChangePasswordForm.tsx @@ -1,4 +1,5 @@ -import { Alert, FormGroup, Grid, TextField } from '@mui/material' +import { Alert, FormGroup, TextField } from '@mui/material' +import Grid from '@mui/material/Grid2' import { FC } from 'react' import { SubmitHandler, useForm } from 'react-hook-form' import { putMeApi } from '../../api/User' @@ -45,7 +46,7 @@ const UserChangePasswordForm: FC<Props> = ({ id, onClose }) => { return ( <form id={id} onSubmit={handleSubmit(onSubmit)}> <Grid columnSpacing={{ xs: 1, sm: 2, md: 3 }} container rowSpacing={1}> - <Grid item xs={12}> + <Grid size={{ xs: 12 }}> {errors.password && <Alert severity="error">{errors.password.message}</Alert>} <FormGroup> <TextField margin="normal" {...register('password')} label="Password" required type="password" /> diff --git a/src/components/user/UserForm.tsx b/src/components/user/UserForm.tsx index d6380fb8..8ee46287 100644 --- a/src/components/user/UserForm.tsx +++ b/src/components/user/UserForm.tsx @@ -1,4 +1,5 @@ -import { Checkbox, Divider, FormControlLabel, FormGroup, Grid, TextField, Typography } from '@mui/material' +import { Checkbox, Divider, FormControlLabel, FormGroup, TextField, Typography } from '@mui/material' +import Grid from '@mui/material/Grid2' import { useQueryClient } from '@tanstack/react-query' import { FC, useEffect } from 'react' import { SubmitHandler, useForm } from 'react-hook-form' @@ -68,7 +69,7 @@ const UserForm: FC<Props> = ({ id, user, callback }) => { return ( <form id={id} onSubmit={handleSubmit(onSubmit)}> <Grid columnSpacing={{ xs: 1, sm: 2, md: 3 }} container rowSpacing={1}> - <Grid item md={6} xs={12}> + <Grid size={{ md: 6, xs: 12 }}> <FormGroup> <TextField error={errors.email !== undefined} @@ -85,7 +86,7 @@ const UserForm: FC<Props> = ({ id, user, callback }) => { <FormControlLabel control={<Checkbox {...register('isSuperAdmin')} defaultChecked={user?.isSuperAdmin} />} label="Super Admin" /> </FormGroup> </Grid> - <Grid item md={6} xs={12}> + <Grid size={{ md: 6, xs: 12 }}> <FormGroup> <TextField error={errors.password !== undefined} @@ -117,7 +118,7 @@ const UserForm: FC<Props> = ({ id, user, callback }) => { </FormGroup> </Grid> {!isSuperAdminChecked ? ( - <Grid item xs={12}> + <Grid size={{ xs: 12 }}> <Divider /> <Typography component="h6" sx={{ my: 1 }} variant="h6"> Permissions diff --git a/src/views/LoginView.tsx b/src/views/LoginView.tsx index 31fa5166..3cda12c1 100644 --- a/src/views/LoginView.tsx +++ b/src/views/LoginView.tsx @@ -1,10 +1,11 @@ -import { Alert, Box, Button, Container, Grid, Paper, TextField, Typography } from '@mui/material' +import { Alert, Box, Button, Container, Paper, TextField, Typography } from '@mui/material' +import Grid from '@mui/material/Grid2' +import { useQueryClient } from '@tanstack/react-query' import { FC, useEffect } from 'react' import { SubmitHandler, useForm } from 'react-hook-form' import { useNavigate } from 'react-router' -import useAuth from '../contexts/useAuth' import logo from '../assets/logo.png' -import { useQueryClient } from '@tanstack/react-query' +import useAuth from '../contexts/useAuth' interface FormValues { email: string @@ -39,7 +40,7 @@ const LoginView: FC = () => { return ( <Container fixed maxWidth="sm" sx={{ mt: 5 }}> <Grid container spacing={2}> - <Grid item xs={12}> + <Grid size={{ xs: 12 }}> <Box sx={{ mb: 2, textAlign: 'center' }}> <img alt="Systemli Logo" src={logo} style={{ marginLeft: 'auto', marginRight: 'auto' }} /> <Typography component="h4" sx={{ mt: 1 }} variant="h4"> @@ -47,7 +48,7 @@ const LoginView: FC = () => { </Typography> </Box> </Grid> - <Grid item xs={12}> + <Grid size={{ xs: 12 }}> <Paper elevation={3} sx={{ p: 2 }}> <form onSubmit={handleSubmit(onSubmit)}> {error ? ( diff --git a/src/views/SettingsView.tsx b/src/views/SettingsView.tsx index 7f38afdb..28ce732c 100644 --- a/src/views/SettingsView.tsx +++ b/src/views/SettingsView.tsx @@ -1,24 +1,25 @@ +import { Stack, Typography } from '@mui/material' +import Grid from '@mui/material/Grid2' import { FC } from 'react' -import Layout from './Layout' -import { Grid, Stack, Typography } from '@mui/material' -import RefreshIntervalCard from '../components/settings/RefreshIntervalCard' import InactiveSettingsCard from '../components/settings/InactiveSettingsCard' +import RefreshIntervalCard from '../components/settings/RefreshIntervalCard' +import Layout from './Layout' const SettingsView: FC = () => { return ( <Layout> <Grid container spacing={2}> - <Grid item xs={12}> + <Grid size={{ xs: 12 }}> <Stack alignItems="center" direction="row" justifyContent="space-between" mb={2}> <Typography component="h2" gutterBottom variant="h3"> Settings </Typography> </Stack> </Grid> - <Grid item md={6} xs={12}> + <Grid size={{ md: 6, xs: 12 }}> <InactiveSettingsCard /> </Grid> - <Grid item md={6} xs={12}> + <Grid size={{ md: 6, xs: 12 }}> <RefreshIntervalCard /> </Grid> </Grid> diff --git a/src/views/TickerListView.tsx b/src/views/TickerListView.tsx index 714770b5..f0a87c81 100644 --- a/src/views/TickerListView.tsx +++ b/src/views/TickerListView.tsx @@ -1,6 +1,7 @@ import { faPlus } from '@fortawesome/free-solid-svg-icons' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { Button, Card, Grid, Stack, Typography } from '@mui/material' +import { Button, Card, Stack, Typography } from '@mui/material' +import Grid from '@mui/material/Grid2' import { FC, useState } from 'react' import TickerList from '../components/ticker/TickerList' import TickerModalForm from '../components/ticker/TickerModalForm' @@ -14,7 +15,7 @@ const TickerListView: FC = () => { return ( <Layout> <Grid container spacing={2}> - <Grid item xs={12}> + <Grid size={{ xs: 12 }}> <Stack alignItems="center" direction="row" justifyContent="space-between" mb={2}> <Typography component="h2" gutterBottom variant="h3"> Tickers @@ -40,7 +41,7 @@ const TickerListView: FC = () => { ) : null} </Stack> </Grid> - <Grid item xs={12}> + <Grid size={{ xs: 12 }}> <Card> <TickerList token={token} /> </Card> diff --git a/src/views/UsersView.tsx b/src/views/UsersView.tsx index 2b4350e6..009d3bb7 100644 --- a/src/views/UsersView.tsx +++ b/src/views/UsersView.tsx @@ -1,6 +1,7 @@ import { faPlus } from '@fortawesome/free-solid-svg-icons' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { Button, Card, Grid, Stack, Typography } from '@mui/material' +import { Button, Card, Stack, Typography } from '@mui/material' +import Grid from '@mui/material/Grid2' import { FC, useState } from 'react' import UserList from '../components/user/UserList' import UserModalForm from '../components/user/UserModalForm' @@ -12,7 +13,7 @@ const UsersView: FC = () => { return ( <Layout> <Grid container spacing={2}> - <Grid item xs={12}> + <Grid size={{ xs: 12 }}> <Stack alignItems="center" direction="row" justifyContent="space-between" mb={2}> <Typography component="h2" gutterBottom variant="h3"> Users @@ -34,7 +35,7 @@ const UsersView: FC = () => { /> </Stack> </Grid> - <Grid item xs={12}> + <Grid size={{ xs: 12 }}> <Card> <UserList /> </Card>