1
0
Fork 0
mirror of https://github.com/binwiederhier/ntfy.git synced 2025-06-05 12:34:34 +02:00

WIP: Accessibility of web app

This commit is contained in:
Philipp Heckel 2022-05-02 19:30:29 -04:00
parent 4a8678bf39
commit bb5e0e3fed
9 changed files with 170 additions and 77 deletions
web/src/components

View file

@ -34,11 +34,6 @@ import DialogActions from "@mui/material/DialogActions";
import userManager from "../app/UserManager";
import {playSound, shuffle, sounds, validUrl} from "../app/utils";
import {useTranslation} from "react-i18next";
import priority1 from "../img/priority-1.svg";
import priority2 from "../img/priority-2.svg";
import priority3 from "../img/priority-3.svg";
import priority4 from "../img/priority-4.svg";
import priority5 from "../img/priority-5.svg";
const Preferences = () => {
return (
@ -55,7 +50,7 @@ const Preferences = () => {
const Notifications = () => {
const { t } = useTranslation();
return (
<Card sx={{p: 3}}>
<Card sx={{p: 3}} aria-label={t("prefs_notifications_title")}>
<Typography variant="h5" sx={{marginBottom: 2}}>
{t("prefs_notifications_title")}
</Typography>
@ -70,6 +65,7 @@ const Notifications = () => {
const Sound = () => {
const { t } = useTranslation();
const labelId = "prefSound";
const sound = useLiveQuery(async () => prefs.sound());
const handleChange = async (ev) => {
await prefs.setSound(ev.target.value);
@ -84,15 +80,15 @@ const Sound = () => {
description = t("prefs_notifications_sound_description_some", { sound: sounds[sound].label });
}
return (
<Pref title={t("prefs_notifications_sound_title")} description={description}>
<Pref labelId={labelId} title={t("prefs_notifications_sound_title")} description={description}>
<div style={{ display: 'flex', width: '100%' }}>
<FormControl fullWidth variant="standard" sx={{ margin: 1 }}>
<Select value={sound} onChange={handleChange}>
<Select value={sound} onChange={handleChange} aria-labelledby={labelId}>
<MenuItem value={"none"}>{t("prefs_notifications_sound_no_sound")}</MenuItem>
{Object.entries(sounds).map(s => <MenuItem key={s[0]} value={s[0]}>{s[1].label}</MenuItem>)}
</Select>
</FormControl>
<IconButton onClick={() => playSound(sound)} disabled={sound === "none"}>
<IconButton onClick={() => playSound(sound)} disabled={sound === "none"} aria-label={t("prefs_notifications_sound_play")}>
<PlayArrowIcon />
</IconButton>
</div>
@ -102,6 +98,7 @@ const Sound = () => {
const MinPriority = () => {
const { t } = useTranslation();
const labelId = "prefMinPriority";
const minPriority = useLiveQuery(async () => prefs.minPriority());
const handleChange = async (ev) => {
await prefs.setMinPriority(ev.target.value);
@ -128,9 +125,9 @@ const MinPriority = () => {
});
}
return (
<Pref title={t("prefs_notifications_min_priority_title")} description={description}>
<Pref labelId={labelId} title={t("prefs_notifications_min_priority_title")} description={description}>
<FormControl fullWidth variant="standard" sx={{ m: 1 }}>
<Select value={minPriority} onChange={handleChange}>
<Select value={minPriority} onChange={handleChange} aria-labelledby={labelId}>
<MenuItem value={1}>{t("prefs_notifications_min_priority_any")}</MenuItem>
<MenuItem value={2}>{t("prefs_notifications_min_priority_low_and_higher")}</MenuItem>
<MenuItem value={3}>{t("prefs_notifications_min_priority_default_and_higher")}</MenuItem>
@ -144,6 +141,7 @@ const MinPriority = () => {
const DeleteAfter = () => {
const { t } = useTranslation();
const labelId = "prefDeleteAfter";
const deleteAfter = useLiveQuery(async () => prefs.deleteAfter());
const handleChange = async (ev) => {
await prefs.setDeleteAfter(ev.target.value);
@ -161,9 +159,9 @@ const DeleteAfter = () => {
}
})();
return (
<Pref title={t("prefs_notifications_delete_after_title")} description={description}>
<Pref labelId={labelId} title={t("prefs_notifications_delete_after_title")} description={description}>
<FormControl fullWidth variant="standard" sx={{ m: 1 }}>
<Select value={deleteAfter} onChange={handleChange}>
<Select value={deleteAfter} onChange={handleChange} aria-labelledby={labelId}>
<MenuItem value={0}>{t("prefs_notifications_delete_after_never")}</MenuItem>
<MenuItem value={10800}>{t("prefs_notifications_delete_after_three_hours")}</MenuItem>
<MenuItem value={86400}>{t("prefs_notifications_delete_after_one_day")}</MenuItem>
@ -177,7 +175,7 @@ const DeleteAfter = () => {
const PrefGroup = (props) => {
return (
<div>
<div role="table">
{props.children}
</div>
)
@ -185,28 +183,39 @@ const PrefGroup = (props) => {
const Pref = (props) => {
return (
<div style={{
display: "flex",
flexDirection: "row",
marginTop: "10px",
marginBottom: "20px",
}}>
<div style={{
flex: '1 0 40%',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
paddingRight: '30px'
}}>
<div
role="row"
style={{
display: "flex",
flexDirection: "row",
marginTop: "10px",
marginBottom: "20px",
}}
>
<div
role="cell"
id={props.labelId}
aria-label={props.title}
style={{
flex: '1 0 40%',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
paddingRight: '30px'
}}
>
<div><b>{props.title}</b></div>
{props.description && <div><em>{props.description}</em></div>}
</div>
<div style={{
flex: '1 0 calc(60% - 50px)',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center'
}}>
<div
role="cell"
style={{
flex: '1 0 calc(60% - 50px)',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center'
}}
>
{props.children}
</div>
</div>
@ -235,7 +244,7 @@ const Users = () => {
}
};
return (
<Card sx={{ padding: 1 }}>
<Card sx={{ padding: 1 }} aria-label={t("prefs_users_title")}>
<CardContent sx={{ paddingBottom: 1 }}>
<Typography variant="h5" sx={{marginBottom: 2}}>
{t("prefs_users_title")}
@ -291,7 +300,7 @@ const UserTable = (props) => {
}
};
return (
<Table size="small">
<Table size="small" aria-label={t("prefs_users_table")}>
<TableHead>
<TableRow>
<TableCell sx={{paddingLeft: 0}}>{t("prefs_users_table_user_header")}</TableCell>
@ -305,13 +314,13 @@ const UserTable = (props) => {
key={user.baseUrl}
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
>
<TableCell component="th" scope="row" sx={{paddingLeft: 0}}>{user.username}</TableCell>
<TableCell>{user.baseUrl}</TableCell>
<TableCell component="th" scope="row" sx={{paddingLeft: 0}} aria-label={t("prefs_users_table_user_header")}>{user.username}</TableCell>
<TableCell aria-label={t("prefs_users_table_base_url_header")}>{user.baseUrl}</TableCell>
<TableCell align="right">
<IconButton onClick={() => handleEditClick(user)}>
<IconButton onClick={() => handleEditClick(user)} aria-label={t("prefs_users_edit_button")}>
<EditIcon/>
</IconButton>
<IconButton onClick={() => handleDeleteClick(user)}>
<IconButton onClick={() => handleDeleteClick(user)} aria-label={t("prefs_users_delete_button")}>
<CloseIcon />
</IconButton>
</TableCell>
@ -371,6 +380,7 @@ const UserDialog = (props) => {
margin="dense"
id="baseUrl"
label={t("prefs_users_dialog_base_url_label")}
aria-label={t("prefs_users_dialog_base_url_label")}
value={baseUrl}
onChange={ev => setBaseUrl(ev.target.value)}
type="url"
@ -382,6 +392,7 @@ const UserDialog = (props) => {
margin="dense"
id="username"
label={t("prefs_users_dialog_username_label")}
aria-label={t("prefs_users_dialog_username_label")}
value={username}
onChange={ev => setUsername(ev.target.value)}
type="text"
@ -392,6 +403,7 @@ const UserDialog = (props) => {
margin="dense"
id="password"
label={t("prefs_users_dialog_password_label")}
aria-label={t("prefs_users_dialog_password_label")}
type="password"
value={password}
onChange={ev => setPassword(ev.target.value)}
@ -410,7 +422,7 @@ const UserDialog = (props) => {
const Appearance = () => {
const { t } = useTranslation();
return (
<Card sx={{p: 3}}>
<Card sx={{p: 3}} aria-label={t("prefs_appearance_title")}>
<Typography variant="h5" sx={{marginBottom: 2}}>
{t("prefs_appearance_title")}
</Typography>
@ -423,6 +435,7 @@ const Appearance = () => {
const Language = () => {
const { t, i18n } = useTranslation();
const labelId = "prefLanguage";
const randomFlags = shuffle(["🇬🇧", "🇺🇸", "🇪🇸", "🇫🇷", "🇧🇬", "🇨🇿", "🇩🇪", "🇮🇩", "🇯🇵", "🇷🇺", "🇹🇷"]).slice(0, 3);
const title = t("prefs_appearance_language_title") + " " + randomFlags.join(" ");
const lang = i18n.language ?? "en";
@ -432,9 +445,9 @@ const Language = () => {
// Better: Sidebar in Wikipedia: https://en.wikipedia.org/wiki/Bokm%C3%A5l
return (
<Pref title={title}>
<Pref labelId={labelId} title={title}>
<FormControl fullWidth variant="standard" sx={{ m: 1 }}>
<Select value={lang} onChange={(ev) => i18n.changeLanguage(ev.target.value)}>
<Select value={lang} onChange={(ev) => i18n.changeLanguage(ev.target.value)} aria-labelledby={labelId}>
<MenuItem value="en">English</MenuItem>
<MenuItem value="bg">Български</MenuItem>
<MenuItem value="cs">Čeština</MenuItem>