forked from Github/frigate
Accessibility features (#14518)
* Add screen reader aria labels to buttons and menu items * Fix sub_label score in search detail dialog
This commit is contained in:
@@ -167,7 +167,11 @@ export default function CameraInfoDialog({
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="select" onClick={() => onCopyFfprobe()}>
|
||||
<Button
|
||||
variant="select"
|
||||
aria-label="Copy"
|
||||
onClick={() => onCopyFfprobe()}
|
||||
>
|
||||
Copy
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
|
||||
@@ -98,7 +98,11 @@ export default function CreateUserDialog({
|
||||
)}
|
||||
/>
|
||||
<DialogFooter className="mt-4">
|
||||
<Button variant="select" disabled={isLoading}>
|
||||
<Button
|
||||
variant="select"
|
||||
aria-label="Create user"
|
||||
disabled={isLoading}
|
||||
>
|
||||
{isLoading && <ActivityIndicator className="mr-2 h-4 w-4" />}
|
||||
Create User
|
||||
</Button>
|
||||
|
||||
@@ -27,6 +27,7 @@ export default function DeleteUserDialog({
|
||||
<DialogFooter>
|
||||
<Button
|
||||
className="flex items-center gap-1"
|
||||
aria-label="Confirm delete"
|
||||
variant="destructive"
|
||||
size="sm"
|
||||
onClick={onDelete}
|
||||
|
||||
@@ -142,6 +142,7 @@ export default function ExportDialog({
|
||||
<Trigger asChild>
|
||||
<Button
|
||||
className="flex items-center gap-2"
|
||||
aria-label="Export"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
const now = new Date(latestTime * 1000);
|
||||
@@ -307,6 +308,7 @@ export function ExportContent({
|
||||
</div>
|
||||
<Button
|
||||
className={isDesktop ? "" : "w-full"}
|
||||
aria-label="Select or export"
|
||||
variant="select"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
@@ -420,6 +422,7 @@ function CustomTimeSelector({
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
className={`text-primary ${isDesktop ? "" : "text-xs"}`}
|
||||
aria-label="Start time"
|
||||
variant={startOpen ? "select" : "default"}
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
@@ -485,6 +488,7 @@ function CustomTimeSelector({
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
className={`text-primary ${isDesktop ? "" : "text-xs"}`}
|
||||
aria-label="End time"
|
||||
variant={endOpen ? "select" : "default"}
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
|
||||
@@ -59,8 +59,17 @@ export default function GPUInfoDialog({
|
||||
<ActivityIndicator />
|
||||
)}
|
||||
<DialogFooter>
|
||||
<Button onClick={() => setShowGpuInfo(false)}>Close</Button>
|
||||
<Button variant="select" onClick={() => onCopyInfo()}>
|
||||
<Button
|
||||
aria-label="Close GPU info"
|
||||
onClick={() => setShowGpuInfo(false)}
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
<Button
|
||||
aria-label="Copy GPU info"
|
||||
variant="select"
|
||||
onClick={() => onCopyInfo()}
|
||||
>
|
||||
Copy
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
@@ -88,8 +97,17 @@ export default function GPUInfoDialog({
|
||||
<ActivityIndicator />
|
||||
)}
|
||||
<DialogFooter>
|
||||
<Button onClick={() => setShowGpuInfo(false)}>Close</Button>
|
||||
<Button variant="select" onClick={() => onCopyInfo()}>
|
||||
<Button
|
||||
aria-label="Close GPU info"
|
||||
onClick={() => setShowGpuInfo(false)}
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
<Button
|
||||
aria-label="Copy GPU info"
|
||||
variant="select"
|
||||
onClick={() => onCopyInfo()}
|
||||
>
|
||||
Copy
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
|
||||
@@ -23,7 +23,11 @@ export default function MobileCameraDrawer({
|
||||
return (
|
||||
<Drawer open={cameraDrawer} onOpenChange={setCameraDrawer}>
|
||||
<DrawerTrigger asChild>
|
||||
<Button className="rounded-lg capitalize" size="sm">
|
||||
<Button
|
||||
className="rounded-lg capitalize"
|
||||
aria-label="Cameras"
|
||||
size="sm"
|
||||
>
|
||||
<FaVideo className="text-secondary-foreground" />
|
||||
</Button>
|
||||
</DrawerTrigger>
|
||||
|
||||
@@ -132,6 +132,7 @@ export default function MobileReviewSettingsDrawer({
|
||||
{features.includes("export") && (
|
||||
<Button
|
||||
className="flex w-full items-center justify-center gap-2"
|
||||
aria-label="Export"
|
||||
onClick={() => {
|
||||
setDrawerMode("export");
|
||||
setMode("select");
|
||||
@@ -144,6 +145,7 @@ export default function MobileReviewSettingsDrawer({
|
||||
{features.includes("calendar") && (
|
||||
<Button
|
||||
className="flex w-full items-center justify-center gap-2"
|
||||
aria-label="Calendar"
|
||||
variant={filter?.after ? "select" : "default"}
|
||||
onClick={() => setDrawerMode("calendar")}
|
||||
>
|
||||
@@ -156,6 +158,7 @@ export default function MobileReviewSettingsDrawer({
|
||||
{features.includes("filter") && (
|
||||
<Button
|
||||
className="flex w-full items-center justify-center gap-2"
|
||||
aria-label="Filter"
|
||||
variant={filter?.labels || filter?.zones ? "select" : "default"}
|
||||
onClick={() => setDrawerMode("filter")}
|
||||
>
|
||||
@@ -226,6 +229,7 @@ export default function MobileReviewSettingsDrawer({
|
||||
<SelectSeparator />
|
||||
<div className="flex items-center justify-center p-2">
|
||||
<Button
|
||||
aria-label="Reset"
|
||||
onClick={() => {
|
||||
onUpdateFilter({
|
||||
...filter,
|
||||
@@ -306,6 +310,7 @@ export default function MobileReviewSettingsDrawer({
|
||||
<DrawerTrigger asChild>
|
||||
<Button
|
||||
className="rounded-lg capitalize"
|
||||
aria-label="Filters"
|
||||
variant={
|
||||
filter?.labels || filter?.after || filter?.zones
|
||||
? "select"
|
||||
|
||||
@@ -22,7 +22,11 @@ export default function MobileTimelineDrawer({
|
||||
return (
|
||||
<Drawer open={drawer} onOpenChange={setDrawer}>
|
||||
<DrawerTrigger asChild>
|
||||
<Button className="rounded-lg capitalize" size="sm">
|
||||
<Button
|
||||
className="rounded-lg capitalize"
|
||||
aria-label="Select timeline or events list"
|
||||
size="sm"
|
||||
>
|
||||
<FaFlag className="text-secondary-foreground" />
|
||||
</Button>
|
||||
</DrawerTrigger>
|
||||
|
||||
@@ -28,6 +28,7 @@ export default function SaveExportOverlay({
|
||||
>
|
||||
<Button
|
||||
className="flex items-center gap-1 text-primary"
|
||||
aria-label="Cancel"
|
||||
size="sm"
|
||||
onClick={onCancel}
|
||||
>
|
||||
@@ -36,6 +37,7 @@ export default function SaveExportOverlay({
|
||||
</Button>
|
||||
<Button
|
||||
className="flex items-center gap-1"
|
||||
aria-label="Preview export"
|
||||
size="sm"
|
||||
onClick={onPreview}
|
||||
>
|
||||
@@ -44,6 +46,7 @@ export default function SaveExportOverlay({
|
||||
</Button>
|
||||
<Button
|
||||
className="flex items-center gap-1"
|
||||
aria-label="Save export"
|
||||
variant="select"
|
||||
size="sm"
|
||||
onClick={onSave}
|
||||
|
||||
@@ -36,6 +36,7 @@ export default function SetPasswordDialog({
|
||||
<DialogFooter>
|
||||
<Button
|
||||
className="flex items-center gap-1"
|
||||
aria-label="Save Password"
|
||||
variant="select"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
|
||||
@@ -207,12 +207,14 @@ export function AnnotationSettingsPane({
|
||||
<div className="flex flex-row gap-2 pt-5">
|
||||
<Button
|
||||
className="flex flex-1"
|
||||
aria-label="Apply"
|
||||
onClick={form.handleSubmit(onApply)}
|
||||
>
|
||||
Apply
|
||||
</Button>
|
||||
<Button
|
||||
variant="select"
|
||||
aria-label="Save"
|
||||
disabled={isLoading}
|
||||
className="flex flex-1"
|
||||
type="submit"
|
||||
|
||||
@@ -242,6 +242,7 @@ export default function ObjectLifecycle({
|
||||
<div className={cn("flex items-center gap-2")}>
|
||||
<Button
|
||||
className="mb-2 mt-3 flex items-center gap-2.5 rounded-lg md:mt-0"
|
||||
aria-label="Go back"
|
||||
size="sm"
|
||||
onClick={() => setPane("overview")}
|
||||
>
|
||||
@@ -346,6 +347,7 @@ export default function ObjectLifecycle({
|
||||
<Button
|
||||
variant={showControls ? "select" : "default"}
|
||||
className="size-7 p-1.5"
|
||||
aria-label="Adjust annotation settings"
|
||||
>
|
||||
<LuSettings
|
||||
className="size-5"
|
||||
|
||||
@@ -153,6 +153,7 @@ export default function ReviewDetailDialog({
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<Button
|
||||
aria-label="Share this review item"
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
shareOrCopy(`${baseUrl}review?id=${review.id}`)
|
||||
|
||||
@@ -296,7 +296,7 @@ function ObjectDetailsTab({
|
||||
}
|
||||
|
||||
if (search.sub_label) {
|
||||
return Math.round((search.data?.top_score ?? 0) * 100);
|
||||
return Math.round((search.data?.sub_label_score ?? 0) * 100);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
@@ -440,6 +440,7 @@ function ObjectDetailsTab({
|
||||
/>
|
||||
{config?.semantic_search.enabled && (
|
||||
<Button
|
||||
aria-label="Find similar tracked objects"
|
||||
onClick={() => {
|
||||
setSearch(undefined);
|
||||
|
||||
@@ -466,6 +467,7 @@ function ObjectDetailsTab({
|
||||
<div className="flex items-center">
|
||||
<Button
|
||||
className="rounded-r-none border-r-0"
|
||||
aria-label="Regenerate tracked object description"
|
||||
onClick={() => regenerateDescription("thumbnails")}
|
||||
>
|
||||
Regenerate
|
||||
@@ -473,19 +475,24 @@ function ObjectDetailsTab({
|
||||
{search.has_snapshot && (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button className="rounded-l-none border-l-0 px-2">
|
||||
<Button
|
||||
className="rounded-l-none border-l-0 px-2"
|
||||
aria-label="Expand regeneration menu"
|
||||
>
|
||||
<FaChevronDown className="size-3" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem
|
||||
className="cursor-pointer"
|
||||
aria-label="Regenerate from snapshot"
|
||||
onClick={() => regenerateDescription("snapshot")}
|
||||
>
|
||||
Regenerate from Snapshot
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
className="cursor-pointer"
|
||||
aria-label="Regenerate from thumbnails"
|
||||
onClick={() => regenerateDescription("thumbnails")}
|
||||
>
|
||||
Regenerate from Thumbnails
|
||||
@@ -495,7 +502,11 @@ function ObjectDetailsTab({
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<Button variant="select" onClick={updateDescription}>
|
||||
<Button
|
||||
variant="select"
|
||||
aria-label="Save"
|
||||
onClick={updateDescription}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
@@ -601,6 +612,7 @@ function ObjectSnapshotTab({
|
||||
<>
|
||||
<Button
|
||||
className="bg-success"
|
||||
aria-label="Confirm this label for Frigate Plus"
|
||||
onClick={() => {
|
||||
setState("uploading");
|
||||
onSubmitToPlus(false);
|
||||
@@ -610,6 +622,7 @@ function ObjectSnapshotTab({
|
||||
</Button>
|
||||
<Button
|
||||
className="text-white"
|
||||
aria-label="Do not confirm this label for Frigate Plus"
|
||||
variant="destructive"
|
||||
onClick={() => {
|
||||
setState("uploading");
|
||||
|
||||
@@ -131,9 +131,14 @@ export function FrigatePlusDialog({
|
||||
<DialogFooter className="flex flex-row justify-end gap-2">
|
||||
{state == "reviewing" && (
|
||||
<>
|
||||
{dialog && <Button onClick={onClose}>Cancel</Button>}
|
||||
{dialog && (
|
||||
<Button aria-label="Cancel" onClick={onClose}>
|
||||
Cancel
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
className="bg-success"
|
||||
aria-label="Confirm this label for Frigate Plus"
|
||||
onClick={() => {
|
||||
setState("uploading");
|
||||
onSubmitToPlus(false);
|
||||
@@ -143,6 +148,7 @@ export function FrigatePlusDialog({
|
||||
</Button>
|
||||
<Button
|
||||
className="text-white"
|
||||
aria-label="Do not confirm this label for Frigate Plus"
|
||||
variant="destructive"
|
||||
onClick={() => {
|
||||
setState("uploading");
|
||||
|
||||
@@ -76,6 +76,7 @@ export default function SearchFilterDialog({
|
||||
const trigger = (
|
||||
<Button
|
||||
className="flex items-center gap-2"
|
||||
aria-label="More Filters"
|
||||
size="sm"
|
||||
variant={moreFiltersSelected ? "select" : "default"}
|
||||
>
|
||||
@@ -141,6 +142,7 @@ export default function SearchFilterDialog({
|
||||
<div className="flex items-center justify-evenly p-2">
|
||||
<Button
|
||||
variant="select"
|
||||
aria-label="Apply"
|
||||
onClick={() => {
|
||||
if (currentFilter != filter) {
|
||||
onUpdateFilter(currentFilter);
|
||||
@@ -152,6 +154,7 @@ export default function SearchFilterDialog({
|
||||
Apply
|
||||
</Button>
|
||||
<Button
|
||||
aria-label="Reset filters to default values"
|
||||
onClick={() => {
|
||||
setCurrentFilter((prevFilter) => ({
|
||||
...prevFilter,
|
||||
@@ -256,6 +259,7 @@ function TimeRangeFilterContent({
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
className={`text-primary ${isDesktop ? "" : "text-xs"} `}
|
||||
aria-label="Select Start Time"
|
||||
variant={startOpen ? "select" : "default"}
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
@@ -293,6 +297,7 @@ function TimeRangeFilterContent({
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
className={`text-primary ${isDesktop ? "" : "text-xs"}`}
|
||||
aria-label="Select End Time"
|
||||
variant={endOpen ? "select" : "default"}
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
|
||||
Reference in New Issue
Block a user