fix(admin): keep list state in the URL and browser storage

- listState.ts stores each list's query (search, sort, dir, page, per_page,
  filter[name]) under summer-admin.list:<controller>; an empty query is
  never stored, malformed stored values are dropped like a hand-edited URL
- ListView restores the stored query into the URL before its first list
  request when entered without list params; a URL with list params wins
- a 422 forgets the stored query; an out-of-range page clamps to last_page
- logout clears every stored list query
- check-phase10.sh allows browser storage in useSidebar.ts and listState.ts
- list smoke tests start from a clean list state
- rebuilt modules/boardwalk/dist
This commit is contained in:
2026-10-09 21:04:00 +02:00
parent bd8626e5fc
commit 9cac45c389
10 changed files with 186 additions and 18 deletions

View File

@@ -3,6 +3,7 @@ import { enableAutoUnmount, flushPromises } from '@vue/test-utils'
import { setBundle } from '../../src/app/i18n'
import ListView from '../../src/views/ListView.vue'
import { toasts } from '../../src/state/useToasts'
import { readListState, writeListState } from '../../src/state/listState'
import { OWNER_ATTRIBUTE } from '../../src/app/pluginAssets'
import {
clone,
@@ -492,3 +493,25 @@ describe('bulk actions on the list (UI-SPEC S1, D-09)', () => {
expect(wrapper.find('[data-selected-pill]').exists()).toBe(false)
})
})
describe('list state in the URL and browser storage (D-01)', () => {
it('restores the stored query before the first list request when the URL has no list params', async () => {
writeListState('acme.demo.widgets', { sort: 'code', dir: 'desc', filter: { active: 'true' } })
const { calls, router } = await mountApp('/acme/demo/widgets', routes())
await flushPromises()
const [first] = requestsTo(calls, 'GET', BASE)
const query = queryOf(first!)
expect(query.get('sort')).toBe('code')
expect(query.get('dir')).toBe('desc')
expect(query.get('filter[active]')).toBe('true')
expect(router.currentRoute.value.query).toMatchObject({ sort: 'code', dir: 'desc', 'filter[active]': 'true' })
})
it('lets a URL with list params win and stores its query', async () => {
writeListState('acme.demo.widgets', { sort: 'code', dir: 'desc' })
const { calls } = await mountApp('/acme/demo/widgets?search=blue', routes())
await flushPromises()
expect(queryOf(requestsTo(calls, 'GET', BASE)[0]!).get('sort')).toBeNull()
expect(readListState('acme.demo.widgets')).toEqual({ search: 'blue' })
})
})