diff --git a/src/__tests__/skills-index.test.tsx b/src/__tests__/skills-index.test.tsx index 2c9202286..260b3bcb4 100644 --- a/src/__tests__/skills-index.test.tsx +++ b/src/__tests__/skills-index.test.tsx @@ -178,6 +178,56 @@ describe('SkillsIndex', () => { }) }) + it('switches browse default sorting back to relevance when entering search', async () => { + searchMock = { sort: 'downloads' } + vi.useFakeTimers() + + render() + + const input = screen.getByPlaceholderText('Filter by name, slug, or summary…') + await act(async () => { + fireEvent.change(input, { target: { value: 'cli-design-framework' } }) + await vi.runAllTimersAsync() + }) + + expect(navigateMock).toHaveBeenCalled() + const lastCall = navigateMock.mock.calls.at(-1)?.[0] as { + replace?: boolean + search: (prev: Record) => Record + } + expect(lastCall.replace).toBe(true) + expect(lastCall.search({ sort: 'downloads' })).toEqual({ + q: 'cli-design-framework', + sort: undefined, + dir: undefined, + }) + }) + + it('preserves explicitly user-set downloads sort when entering search', async () => { + searchMock = { sort: 'downloads', dir: 'desc' } + vi.useFakeTimers() + + render() + + const input = screen.getByPlaceholderText('Filter by name, slug, or summary…') + await act(async () => { + fireEvent.change(input, { target: { value: 'cli-design-framework' } }) + await vi.runAllTimersAsync() + }) + + expect(navigateMock).toHaveBeenCalled() + const lastCall = navigateMock.mock.calls.at(-1)?.[0] as { + replace?: boolean + search: (prev: Record) => Record + } + expect(lastCall.replace).toBe(true) + expect(lastCall.search({ sort: 'downloads', dir: 'desc' })).toEqual({ + q: 'cli-design-framework', + sort: 'downloads', + dir: 'desc', + }) + }) + it('loads more results when search pagination is requested', async () => { searchMock = { q: 'remind' } vi.stubGlobal('IntersectionObserver', undefined) diff --git a/src/routes/skills/-useSkillsBrowseModel.ts b/src/routes/skills/-useSkillsBrowseModel.ts index 25a26de02..93c24c1e5 100644 --- a/src/routes/skills/-useSkillsBrowseModel.ts +++ b/src/routes/skills/-useSkillsBrowseModel.ts @@ -229,7 +229,22 @@ export function useSkillsBrowseModel({ const trimmed = next.trim() navigateTimer.current = window.setTimeout(() => { void navigate({ - search: (prev) => ({ ...prev, q: trimmed ? next : undefined }), + search: (prev) => { + const hadQuery = typeof prev.q === 'string' && prev.q.trim().length > 0 + const enteringSearch = Boolean(trimmed) && !hadQuery + const usesImplicitBrowseDefault = prev.sort === 'downloads' && prev.dir === undefined + + return { + ...prev, + q: trimmed ? next : undefined, + ...(enteringSearch && usesImplicitBrowseDefault + ? { + sort: undefined, + dir: undefined, + } + : null), + } + }, replace: true, }) }, 220)