mirror of
https://github.com/navidrome/navidrome.git
synced 2026-10-18 07:30:29 +00:00
* feat(ui): add perPageStore helper for items-per-page persistence * feat(ui): persist items-per-page selection in localStorage * feat(ui): enable per-page persistence on album grid, missing files and playlist tracks * feat(ui): restore saved album grid page size on fresh load * feat(ui): restore saved items-per-page on list load * fix(ui): move defaultRowsPerPageOptions to perPageStore to satisfy react-refresh lint * fix(ui): correct defaultRowsPerPageOptions import in List and add render test * refactor(ui): default getStoredPerPage fallback to the first option The fallback equalled options[0] at three of four call sites; default it so those sites stop restating it. SongList and useAlbumsPerPage still pass an explicit fallback where it legitimately differs from the first option. * fix(ui): persist only user-selected page sizes, validate album size against width Persisting on every pagination context value let a URL-injected perPage (e.g. the perPage=15 album link in NowPlayingPanel) or a forced single-option mobile grid overwrite the saved preference. Persist only a value that is an actual option in a multi-option selector. Also validate the album grid's redux session value against the current width so a size chosen at a wider breakpoint can't leave an out-of-range selector. * fix(ui): restore saved items-per-page on the radio list RadioList passed a hard-coded perPage that overrode List's stored seed via the props spread, so a radio-list page size was persisted but never restored. Seed it from storage like the other list views. * fix(ui): persist page size only on an actual selector change Watching the pagination context value meant any valid value persisted itself: loading a list at a breakpoint where the saved size is invalid stored the responsive fallback, and opening a URL with a valid ?perPage= stored that too, either way discarding the user's real preference. Inject a wrapped setPerPage instead, so only the rows-per-page selector writes. This also drops the option-validation heuristics, which the new trigger makes unnecessary.
62 lines
1.9 KiB
JavaScript
62 lines
1.9 KiB
JavaScript
import { renderHook } from '@testing-library/react-hooks'
|
|
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
|
import { useAlbumsPerPage } from './useAlbumsPerPage'
|
|
import { setStoredPerPage } from './perPageStore'
|
|
|
|
vi.mock('react-redux', () => ({
|
|
useSelector: vi.fn(),
|
|
}))
|
|
|
|
describe('useAlbumsPerPage', () => {
|
|
let mockUseSelector
|
|
|
|
beforeEach(async () => {
|
|
vi.clearAllMocks()
|
|
localStorage.clear()
|
|
const { useSelector } = await import('react-redux')
|
|
mockUseSelector = vi.mocked(useSelector)
|
|
})
|
|
|
|
const setReduxPerPage = (value) =>
|
|
mockUseSelector.mockImplementation((selector) =>
|
|
selector({
|
|
admin: {
|
|
resources: { album: { list: { params: { perPage: value } } } },
|
|
},
|
|
}),
|
|
)
|
|
|
|
it('prefers the redux session value over the stored one', () => {
|
|
setReduxPerPage(36)
|
|
setStoredPerPage('album', 72)
|
|
const { result } = renderHook(() => useAlbumsPerPage('lg'))
|
|
expect(result.current[0]).toEqual(36)
|
|
})
|
|
|
|
it('falls back to the stored value on fresh load', () => {
|
|
setReduxPerPage(undefined)
|
|
setStoredPerPage('album', 72)
|
|
const { result } = renderHook(() => useAlbumsPerPage('lg'))
|
|
expect(result.current[0]).toEqual(72)
|
|
})
|
|
|
|
it('ignores stored values invalid for the current width', () => {
|
|
setReduxPerPage(undefined)
|
|
setStoredPerPage('album', 72) // valid for lg, not for md
|
|
const { result } = renderHook(() => useAlbumsPerPage('md'))
|
|
expect(result.current[0]).toEqual(12)
|
|
})
|
|
|
|
it('returns the responsive default when nothing is stored', () => {
|
|
setReduxPerPage(undefined)
|
|
const { result } = renderHook(() => useAlbumsPerPage('xl'))
|
|
expect(result.current).toEqual([36, [18, 36, 72]])
|
|
})
|
|
|
|
it('ignores a redux value invalid for the current width', () => {
|
|
setReduxPerPage(72) // valid for lg, not for md
|
|
const { result } = renderHook(() => useAlbumsPerPage('md'))
|
|
expect(result.current[0]).toEqual(12)
|
|
})
|
|
})
|