Drag was already wired via @neodrag/svelte; this adds the missing keyboard path so reorder is accessible without a pointer device. - The drag handle becomes a real <button> (focusable). Disabled when canDrag is false so non-owners + unavailable tracks can't attempt a move. - ArrowUp / ArrowDown on the focused handle call onMove(row.position, row.position +/- 1). Bounds clamping already lives in the page-level handler (playlists/[id]/+page.svelte:39), so out-of-range targets resolve as no-ops. - Matches QueueTrackRow's pattern: aria-label "Reorder track …", aria-keyshortcuts, swallow Space/Enter to avoid scrolling. Test updates: drag-handle assertions retargeted to /reorder track/ + two new ArrowUp/ArrowDown handler tests.
This commit is contained in:
@@ -67,26 +67,46 @@ describe('PlaylistTrackRow', () => {
|
||||
expect(screen.getByText('2:17')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('shows drag handle and remove button for owner', () => {
|
||||
test('shows reorder handle and remove button for owner', () => {
|
||||
render(PlaylistTrackRow, {
|
||||
props: { row: live, isOwner: true, onRemove: vi.fn(), onPlay: vi.fn() }
|
||||
});
|
||||
expect(screen.getByLabelText('Drag handle')).toBeInTheDocument();
|
||||
expect(screen.getByLabelText(/reorder track/i)).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByLabelText(/Remove Roygbiv from playlist/i)
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('hides drag handle and remove button for non-owner', () => {
|
||||
test('hides reorder handle and remove button for non-owner', () => {
|
||||
render(PlaylistTrackRow, {
|
||||
props: { row: live, isOwner: false, onRemove: vi.fn(), onPlay: vi.fn() }
|
||||
});
|
||||
expect(screen.queryByLabelText('Drag handle')).not.toBeInTheDocument();
|
||||
expect(screen.queryByLabelText(/reorder track/i)).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByLabelText(/Remove Roygbiv from playlist/i)
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('ArrowDown on the reorder handle calls onMove(pos, pos+1)', async () => {
|
||||
const onMove = vi.fn();
|
||||
render(PlaylistTrackRow, {
|
||||
props: { row: live, isOwner: true, onRemove: vi.fn(), onPlay: vi.fn(), onMove }
|
||||
});
|
||||
const handle = screen.getByLabelText(/reorder track/i);
|
||||
await fireEvent.keyDown(handle, { key: 'ArrowDown' });
|
||||
expect(onMove).toHaveBeenCalledWith(2, 3);
|
||||
});
|
||||
|
||||
test('ArrowUp on the reorder handle calls onMove(pos, pos-1)', async () => {
|
||||
const onMove = vi.fn();
|
||||
render(PlaylistTrackRow, {
|
||||
props: { row: live, isOwner: true, onRemove: vi.fn(), onPlay: vi.fn(), onMove }
|
||||
});
|
||||
const handle = screen.getByLabelText(/reorder track/i);
|
||||
await fireEvent.keyDown(handle, { key: 'ArrowUp' });
|
||||
expect(onMove).toHaveBeenCalledWith(2, 1);
|
||||
});
|
||||
|
||||
test('greyed-out + strikethrough when track_id is null', () => {
|
||||
render(PlaylistTrackRow, {
|
||||
props: { row: removed, isOwner: true, onRemove: vi.fn(), onPlay: vi.fn() }
|
||||
|
||||
Reference in New Issue
Block a user