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:
@@ -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' })
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2,6 +2,7 @@ import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
||||
import { flushPromises } from '@vue/test-utils'
|
||||
import { setBundle } from '../../src/app/i18n'
|
||||
import { parseListQuery, toListQuery } from '../../src/app/listQuery'
|
||||
import { clearListStates } from '../../src/state/listState'
|
||||
import { clearUser } from '../../src/state/useAuth'
|
||||
import { setNavigation } from '../../src/state/useNavigation'
|
||||
import { clearToasts } from '../../src/state/useToasts'
|
||||
@@ -39,6 +40,8 @@ const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
|
||||
|
||||
beforeEach(() => {
|
||||
clearUser()
|
||||
// Lists remember their query (D-01); every test starts from a clean list.
|
||||
clearListStates()
|
||||
setNavigation([])
|
||||
clearToasts()
|
||||
setBundle(lang.data, 'pl')
|
||||
|
||||
Reference in New Issue
Block a user