mirror of
https://github.com/navidrome/navidrome.git
synced 2026-10-15 05:47:13 +02:00
* fix: prevent disabled Show in Playlist menu item from triggering actions Fixed bug where clicking on the disabled 'Show in Playlist' menu item would unintentionally trigger music playback and replace the queue. The menu item now properly prevents event propagation when disabled and takes no action. This resolves the issue where users would accidentally start playing music when clicking on the greyed-out menu option. The fix includes: - Custom onClick handler that stops event propagation for disabled state - Proper styling to maintain visual disabled state while allowing event handling - Comprehensive test coverage for the disabled behavior * style: clean up disabled menu item styling code Simplified the arrow function for disabled onClick handler and changed inline style from empty object to undefined when not needed. Also added a CSS class for disabled menu items for potential future use. These changes improve code readability and follow React best practices by using undefined instead of empty objects for conditional styles.
107 lines
3 KiB
JavaScript
107 lines
3 KiB
JavaScript
import React from 'react'
|
|
import { render, fireEvent, screen, waitFor } from '@testing-library/react'
|
|
import { TestContext } from 'ra-test'
|
|
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
import { SongContextMenu } from './SongContextMenu'
|
|
|
|
vi.mock('../dataProvider', () => ({
|
|
httpClient: vi.fn(),
|
|
}))
|
|
|
|
vi.mock('react-redux', () => ({ useDispatch: () => vi.fn() }))
|
|
|
|
const getPlaylistsMock = vi.fn()
|
|
|
|
vi.mock('react-admin', async (importOriginal) => {
|
|
const actual = await importOriginal()
|
|
return {
|
|
...actual,
|
|
useRedirect: () => (url) => {
|
|
window.location.hash = `#${url}`
|
|
},
|
|
useDataProvider: () => ({
|
|
getPlaylists: getPlaylistsMock,
|
|
inspect: vi.fn().mockResolvedValue({
|
|
data: { rawTags: {} },
|
|
}),
|
|
}),
|
|
}
|
|
})
|
|
|
|
describe('SongContextMenu', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
window.location.hash = ''
|
|
getPlaylistsMock.mockResolvedValue({
|
|
data: [{ id: 'pl1', name: 'Pl 1' }],
|
|
})
|
|
})
|
|
|
|
it('navigates to playlist when selected', async () => {
|
|
render(
|
|
<TestContext>
|
|
<SongContextMenu record={{ id: 'song1', size: 1 }} resource="song" />
|
|
</TestContext>,
|
|
)
|
|
fireEvent.click(screen.getAllByRole('button')[1])
|
|
await waitFor(() =>
|
|
screen.getByText(/resources\.song\.actions\.showInPlaylist/),
|
|
)
|
|
fireEvent.click(
|
|
screen.getByText(/resources\.song\.actions\.showInPlaylist/),
|
|
)
|
|
await waitFor(() => screen.getByText('Pl 1'))
|
|
fireEvent.click(screen.getByText('Pl 1'))
|
|
expect(window.location.hash).toBe('#/playlist/pl1/show')
|
|
})
|
|
|
|
it('stops event propagation when playlist submenu is closed', async () => {
|
|
const mockOnClick = vi.fn()
|
|
render(
|
|
<TestContext>
|
|
<div onClick={mockOnClick}>
|
|
<SongContextMenu record={{ id: 'song1', size: 1 }} resource="song" />
|
|
</div>
|
|
</TestContext>,
|
|
)
|
|
|
|
// Open main menu
|
|
fireEvent.click(screen.getAllByRole('button')[1])
|
|
await waitFor(() =>
|
|
screen.getByText(/resources\.song\.actions\.showInPlaylist/),
|
|
)
|
|
|
|
// Open playlist submenu
|
|
fireEvent.click(
|
|
screen.getByText(/resources\.song\.actions\.showInPlaylist/),
|
|
)
|
|
await waitFor(() => screen.getByText('Pl 1'))
|
|
|
|
// Click outside the playlist submenu (should close it without triggering parent click)
|
|
fireEvent.click(document.body)
|
|
|
|
expect(mockOnClick).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('does nothing when "Show in Playlist" is disabled', async () => {
|
|
getPlaylistsMock.mockResolvedValue({ data: [] })
|
|
const mockOnClick = vi.fn()
|
|
render(
|
|
<TestContext>
|
|
<div onClick={mockOnClick}>
|
|
<SongContextMenu record={{ id: 'song1', size: 1 }} resource="song" />
|
|
</div>
|
|
</TestContext>,
|
|
)
|
|
|
|
fireEvent.click(screen.getAllByRole('button')[1])
|
|
await waitFor(() =>
|
|
screen.getByText(/resources\.song\.actions\.showInPlaylist/),
|
|
)
|
|
|
|
fireEvent.click(
|
|
screen.getByText(/resources\.song\.actions\.showInPlaylist/),
|
|
)
|
|
expect(mockOnClick).not.toHaveBeenCalled()
|
|
})
|
|
})
|