chore(web/lint): clear svelte-check warnings + fix QueueDrawer test query
CI svelte-check was blocking on:
- 1 ERROR I introduced in the previous cleanup: QueueDrawer.test.ts
passed { hidden: true } to getByLabelText, but that option only
exists on getByRole. Fixed by switching to a direct
document.querySelector for the aria-hidden assertion.
- 13 WARNINGS pre-existing in the codebase from M7 #352/#372/#349
era, never surfaced because earlier CI runs failed before reaching
type-check. Now that CI gets that far, they accumulate. Cleared:
- FlagPopover, RemoveTrackPopover, AddToPlaylistMenu, TrackMenu:
interactive role divs gain tabindex="-1" + onkeydown that stops
propagation and closes on Escape (functional, not just warning-
suppression).
- FlagPopover state-from-props: $state(untrack(() => prop ?? default))
for explicit initial-snapshot semantics; const isUpdate switched
to $derived so it reacts to prop changes.
- PlaylistTrackRow drag-div: role="listitem" added.
- playlists/+page.svelte: autofocus replaced with bind:this + $effect.
This commit is contained in:
@@ -67,9 +67,11 @@
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
role="menu"
|
role="menu"
|
||||||
|
tabindex="-1"
|
||||||
aria-label="Add to playlist"
|
aria-label="Add to playlist"
|
||||||
class="absolute right-full bottom-0 z-30 mr-1 w-56 rounded-md border border-border bg-surface p-1 shadow-lg"
|
class="absolute right-full bottom-0 z-30 mr-1 w-56 rounded-md border border-border bg-surface p-1 shadow-lg"
|
||||||
onclick={(e) => e.stopPropagation()}
|
onclick={(e) => e.stopPropagation()}
|
||||||
|
onkeydown={(e) => { e.stopPropagation(); if (e.key === 'Escape') onClose?.(); }}
|
||||||
>
|
>
|
||||||
{#each ownPlaylists as p (p.id)}
|
{#each ownPlaylists as p (p.id)}
|
||||||
<TrackMenuItem
|
<TrackMenuItem
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { untrack } from 'svelte';
|
||||||
import { Flag } from 'lucide-svelte';
|
import { Flag } from 'lucide-svelte';
|
||||||
import { useQueryClient } from '@tanstack/svelte-query';
|
import { useQueryClient } from '@tanstack/svelte-query';
|
||||||
import { flagTrack } from '$lib/api/quarantine';
|
import { flagTrack } from '$lib/api/quarantine';
|
||||||
@@ -17,12 +18,12 @@
|
|||||||
initialNotes?: string;
|
initialNotes?: string;
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
let reason: LidarrQuarantineReason = $state(initialReason ?? 'bad_rip');
|
let reason: LidarrQuarantineReason = $state(untrack(() => initialReason ?? 'bad_rip'));
|
||||||
let notes = $state(initialNotes ?? '');
|
let notes = $state(untrack(() => initialNotes ?? ''));
|
||||||
let submitting = $state(false);
|
let submitting = $state(false);
|
||||||
let error = $state<string | null>(null);
|
let error = $state<string | null>(null);
|
||||||
|
|
||||||
const isUpdate = !!initialReason;
|
const isUpdate = $derived(!!initialReason);
|
||||||
const client = useQueryClient();
|
const client = useQueryClient();
|
||||||
|
|
||||||
async function submit() {
|
async function submit() {
|
||||||
@@ -42,10 +43,12 @@
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
role="dialog"
|
role="dialog"
|
||||||
|
tabindex="-1"
|
||||||
aria-modal="false"
|
aria-modal="false"
|
||||||
aria-labelledby="flag-popover-title"
|
aria-labelledby="flag-popover-title"
|
||||||
class="absolute right-0 z-30 mt-1 w-72 rounded-lg border border-border bg-surface p-3 shadow-xl"
|
class="absolute right-0 z-30 mt-1 w-72 rounded-lg border border-border bg-surface p-3 shadow-xl"
|
||||||
onclick={(e) => e.stopPropagation()}
|
onclick={(e) => e.stopPropagation()}
|
||||||
|
onkeydown={(e) => { e.stopPropagation(); if (e.key === 'Escape') onClose(); }}
|
||||||
>
|
>
|
||||||
<h4 id="flag-popover-title" class="text-sm font-medium text-text-primary">
|
<h4 id="flag-popover-title" class="text-sm font-medium text-text-primary">
|
||||||
Flag this track as broken
|
Flag this track as broken
|
||||||
|
|||||||
@@ -49,6 +49,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
role="listitem"
|
||||||
class="flex items-center gap-3 px-3 py-2 text-sm transition-colors hover:bg-surface-hover
|
class="flex items-center gap-3 px-3 py-2 text-sm transition-colors hover:bg-surface-hover
|
||||||
{isUnavailable ? 'text-text-muted' : 'text-text-primary'}"
|
{isUnavailable ? 'text-text-muted' : 'text-text-primary'}"
|
||||||
draggable={isOwner && !isUnavailable}
|
draggable={isOwner && !isUnavailable}
|
||||||
|
|||||||
@@ -79,8 +79,9 @@ describe('QueueDrawer', () => {
|
|||||||
openValue = false;
|
openValue = false;
|
||||||
queueValue = [t('a')];
|
queueValue = [t('a')];
|
||||||
render(QueueDrawer);
|
render(QueueDrawer);
|
||||||
const aside = screen.getByLabelText(/playback queue/i, { hidden: true });
|
const aside = document.querySelector('aside[aria-label="Playback queue"]');
|
||||||
expect(aside).toHaveAttribute('aria-hidden', 'true');
|
expect(aside).not.toBeNull();
|
||||||
|
expect(aside!.getAttribute('aria-hidden')).toBe('true');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('drawer has inert attribute when closed', () => {
|
it('drawer has inert attribute when closed', () => {
|
||||||
|
|||||||
@@ -55,10 +55,12 @@
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
role="dialog"
|
role="dialog"
|
||||||
|
tabindex="-1"
|
||||||
aria-modal="false"
|
aria-modal="false"
|
||||||
aria-labelledby="remove-track-popover-title"
|
aria-labelledby="remove-track-popover-title"
|
||||||
class="absolute right-0 z-30 mt-1 w-72 rounded-lg border border-border bg-surface p-3 shadow-xl"
|
class="absolute right-0 z-30 mt-1 w-72 rounded-lg border border-border bg-surface p-3 shadow-xl"
|
||||||
onclick={(e) => e.stopPropagation()}
|
onclick={(e) => e.stopPropagation()}
|
||||||
|
onkeydown={(e) => { e.stopPropagation(); if (e.key === 'Escape') onClose(); }}
|
||||||
>
|
>
|
||||||
<h4 id="remove-track-popover-title" class="text-sm font-medium text-text-primary">
|
<h4 id="remove-track-popover-title" class="text-sm font-medium text-text-primary">
|
||||||
Remove "{track.title}" from your library?
|
Remove "{track.title}" from your library?
|
||||||
|
|||||||
@@ -155,9 +155,11 @@
|
|||||||
{#if menuOpen}
|
{#if menuOpen}
|
||||||
<div
|
<div
|
||||||
role="menu"
|
role="menu"
|
||||||
|
tabindex="-1"
|
||||||
class="absolute right-0 z-20 w-56 rounded-md border border-border bg-surface p-1 shadow-lg
|
class="absolute right-0 z-20 w-56 rounded-md border border-border bg-surface p-1 shadow-lg
|
||||||
{direction === 'up' ? 'bottom-full mb-1' : 'top-full mt-1'}"
|
{direction === 'up' ? 'bottom-full mb-1' : 'top-full mt-1'}"
|
||||||
onclick={(e) => e.stopPropagation()}
|
onclick={(e) => e.stopPropagation()}
|
||||||
|
onkeydown={(e) => { e.stopPropagation(); if (e.key === 'Escape') menuOpen = false; }}
|
||||||
>
|
>
|
||||||
{#if !hideQueueActions}
|
{#if !hideQueueActions}
|
||||||
<TrackMenuItem icon={ListPlus} label="Play next" onclick={onPlayNext} />
|
<TrackMenuItem icon={ListPlus} label="Play next" onclick={onPlayNext} />
|
||||||
|
|||||||
@@ -16,6 +16,11 @@
|
|||||||
let newName = $state('');
|
let newName = $state('');
|
||||||
let creatingBusy = $state(false);
|
let creatingBusy = $state(false);
|
||||||
let createError = $state<string | null>(null);
|
let createError = $state<string | null>(null);
|
||||||
|
let inputEl: HTMLInputElement | undefined = $state();
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
inputEl?.focus();
|
||||||
|
});
|
||||||
|
|
||||||
async function submitCreate() {
|
async function submitCreate() {
|
||||||
if (!newName.trim()) {
|
if (!newName.trim()) {
|
||||||
@@ -59,9 +64,9 @@
|
|||||||
Name
|
Name
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
bind:this={inputEl}
|
||||||
bind:value={newName}
|
bind:value={newName}
|
||||||
placeholder="Saturday morning"
|
placeholder="Saturday morning"
|
||||||
autofocus
|
|
||||||
class="mt-1 w-full rounded border border-border bg-background px-3 py-1.5 text-sm text-text-primary"
|
class="mt-1 w-full rounded border border-border bg-background px-3 py-1.5 text-sm text-text-primary"
|
||||||
onkeydown={(e) => { if (e.key === 'Enter') submitCreate(); if (e.key === 'Escape') creating = false; }}
|
onkeydown={(e) => { if (e.key === 'Enter') submitCreate(); if (e.key === 'Escape') creating = false; }}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user