feat(18.1-01): task 4 flat reorder in the admin SPA

- ReorderView reads flat from the reorder data; ReorderBranch takes a flat
  prop and shows only move up and move down
- ArrowRight and ArrowLeft do nothing, an inside drop lands after the row
  (tree.ts flatZone, flatMove) and flatten sends parent_id null
- vitest flat cases; docs describe the flat page; rebuilt dist
This commit is contained in:
Jakub Zych
2026-10-14 16:05:10 +02:00
parent 9edff8890f
commit c3834cb42c
8 changed files with 142 additions and 24 deletions

View File

@@ -3,7 +3,7 @@ import { ref } from 'vue'
import { enableAutoUnmount, flushPromises, mount, type VueWrapper } from '@vue/test-utils'
import ReorderBranch from '../../src/components/reorder/ReorderBranch.vue'
import { REORDER_PAGE, type ReorderPage } from '../../src/components/reorder/context'
import { applyDrop, applyMove, buildTree, canDrop, canMove, flatten, type TreeNode } from '../../src/components/reorder/tree'
import { applyDrop, applyMove, buildTree, canDrop, canMove, flatMove, flatZone, flatten, type TreeNode } from '../../src/components/reorder/tree'
import { setSettings } from '../../src/state/useSettings'
import ReorderView from '../../src/views/ReorderView.vue'
import { toasts } from '../../src/state/useToasts'
@@ -465,6 +465,90 @@ describe('reorder tree rules', () => {
})
})
// Phase 18.1 smoke specs for the flat (WinterCMS Sortable) reorder (D-17):
// the data's `flat` turns the page into a plain ordered list.
function flatTree(): Reply {
return {
body: {
data: {
title: 'Reorder links',
partition: null,
flat: true,
nodes: [
{ id: 1, name: 'Alpha', parent_id: null, sort_order: 1 },
{ id: 2, name: 'Beta', parent_id: null, sort_order: 2 },
{ id: 3, name: 'Gamma', parent_id: null, sort_order: 3 },
],
},
meta: {},
},
}
}
describe('ReorderView flat', () => {
it('offers no indent or outdent, ignores ArrowRight, drops a middle drag after the row and sends parent_id null', async () => {
let body: unknown = null
const { wrapper, calls } = await mountApp(
'/acme/companions/links/reorder',
{
[`GET ${REORDER}`]: flatTree(),
[`POST ${REORDER}`]: async (request) => {
body = await request.json()
return flatTree()
},
},
{ attach: true },
)
expect(shape(wrapper)).toBe('Alpha Beta Gamma')
expect(wrapper.find('[data-action="reorder-indent"]').exists()).toBe(false)
expect(wrapper.find('[data-action="reorder-outdent"]').exists()).toBe(false)
expect(wrapper.findAll('[data-action="reorder-up"]')).toHaveLength(3)
expect(wrapper.findAll('[data-action="reorder-down"]')).toHaveLength(3)
keydown(handle(wrapper, 2).element, 'ArrowRight')
keydown(handle(wrapper, 2).element, 'ArrowLeft')
await flushPromises()
expect(shape(wrapper)).toBe('Alpha Beta Gamma')
// A drag of Alpha onto the middle third of Beta's row lands after Beta.
const row = wrapper.find('[data-reorder-node="2"]')
vi.spyOn(row.element, 'getBoundingClientRect').mockReturnValue({ top: 0, height: 30, bottom: 30, left: 0, right: 100, width: 100, x: 0, y: 0, toJSON: () => ({}) } as DOMRect)
await handle(wrapper, 1).trigger('dragstart')
await row.trigger('dragover', { clientY: 15 })
expect(row.find('[data-reorder-line]').exists()).toBe(true)
await row.trigger('drop')
await flushPromises()
expect(shape(wrapper)).toBe('Beta Alpha Gamma')
await vi.waitFor(() => {
expect(requestsTo(calls, 'POST', REORDER)).toHaveLength(1)
})
expect(body).toEqual({
partition: null,
nodes: [
{ id: 2, parent_id: null, sort_order: 1 },
{ id: 1, parent_id: null, sort_order: 2 },
{ id: 3, parent_id: null, sort_order: 3 },
],
})
})
it('maps the inside zone to after, keeps sibling moves and flattens with null parents', () => {
expect(flatZone('inside', true)).toBe('after')
expect(flatZone('inside', false)).toBe('inside')
expect(flatZone('before', true)).toBe('before')
expect(flatMove('indent', true)).toBe(false)
expect(flatMove('outdent', true)).toBe(false)
expect(flatMove('up', true)).toBe(true)
expect(flatMove('indent', false)).toBe(true)
const nested: TreeNode[] = [{ id: 1, name: 'A', children: [{ id: 2, name: 'B', children: [] }] }]
expect(flatten(nested, true)).toEqual([
{ id: 1, parent_id: null, sort_order: 1 },
{ id: 2, parent_id: null, sort_order: 2 },
])
})
})
// The reorder modules imported by path (T-10-25): the page the route renders,
// its tree level and the injection key they share.
describe('reorder modules', () => {