editor: explain slot refusals on-page; auto-scroll while dragging\n\nDrag state now records the tagged slot under the pointer. When the\ndragged type does not fit it, a badge on the zone names the accepted\ntypes (from the slot schema) and the pointer chip turns refused -\npreviously a refused slot silently fell back to a container drop.\nHolding the pointer near a viewport edge scrolls the page (ramped\nrAF loop, re-targeting after each step) so long pages are droppable\nend to end. Helpers are unit-tested; harness covers refusal badge,\naccept path and the scroll step.\n\n🤖 Generated with Codebuff\nCo-Authored-By: Codebuff <noreply@codebuff.com>
This commit is contained in:
1 parent
8e8823421d
commit
40b33f89a4
6 files changed
+154
-12
No files matched your search
@@ -199,6 +199,67 @@ describe('block palette and drag & drop', () => {
|
||||
await h.settle()
|
||||
expect(kids('s1')).toEqual(['b1', 'h1', 't1'])
|
||||
})
|
||||
|
||||
it('a slot that refuses the type names what it takes (drop still falls back to the container)', async () => {
|
||||
h = await setup()
|
||||
await h.settle()
|
||||
place('s1', 0, 0, 600, 300)
|
||||
// The section schema declares a `sidebar` slot taking text/button/footer; the template marks it.
|
||||
byId('s1').insertAdjacentHTML('beforeend', '<div data-cms-slot="sidebar" data-cms-slot-parent="s1"></div>')
|
||||
const zone = byId('s1').querySelector<HTMLElement>('[data-cms-slot]')!
|
||||
zone.getBoundingClientRect = () => ({ x: 10, y: 190, left: 10, top: 190, right: 590, bottom: 250, width: 580, height: 60, toJSON() {} }) as DOMRect
|
||||
h.editor.ctrl.scan()
|
||||
h.editor.ctrl.beginPaletteDrag({ type: 'heading', label: 'heading' })
|
||||
await ptr('pointermove', 80, 200)
|
||||
expect(h.editor.ctrl.store.get().drag).toMatchObject({ slot: { parentId: 's1', handle: 'sidebar' }, target: { parentId: 's1' } })
|
||||
expect(h.shadow.querySelector('.cms-slotref')?.textContent).toContain('sidebar takes: text, button, footer')
|
||||
expect(h.shadow.querySelector('.cms-dragchip')?.className).toContain('is-refused')
|
||||
await ptr('pointerup', 80, 200)
|
||||
await h.settle()
|
||||
expect(h.editor.ctrl.store.get().drag).toBeNull()
|
||||
})
|
||||
|
||||
it('a slot that accepts the type shows no refusal', async () => {
|
||||
h = await setup()
|
||||
await h.settle()
|
||||
place('s1', 0, 0, 600, 300)
|
||||
byId('s1').insertAdjacentHTML('beforeend', '<div data-cms-slot="sidebar" data-cms-slot-parent="s1"></div>')
|
||||
const zone = byId('s1').querySelector<HTMLElement>('[data-cms-slot]')!
|
||||
zone.getBoundingClientRect = () => ({ x: 10, y: 190, left: 10, top: 190, right: 590, bottom: 250, width: 580, height: 60, toJSON() {} }) as DOMRect
|
||||
h.editor.ctrl.scan()
|
||||
h.editor.ctrl.beginPaletteDrag({ type: 'button', label: 'button' })
|
||||
await ptr('pointermove', 80, 200)
|
||||
expect(h.editor.ctrl.store.get().drag).toMatchObject({
|
||||
slot: { parentId: 's1', handle: 'sidebar' },
|
||||
target: { parentId: 's1', handle: 'sidebar' },
|
||||
})
|
||||
expect(h.shadow.querySelector('.cms-slotref')).toBeNull()
|
||||
await ptr('pointerup', 80, 200)
|
||||
await h.settle()
|
||||
})
|
||||
|
||||
it('holding the pointer at the viewport edge scrolls the page while dragging', async () => {
|
||||
h = await setup()
|
||||
await h.settle()
|
||||
place('s1', 0, 0, 600, 300)
|
||||
h.editor.ctrl.scan()
|
||||
const scrolled: Array<[number, number]> = []
|
||||
const realScrollBy = window.scrollBy
|
||||
window.scrollBy = ((dx: number, dy: number) => void scrolled.push([dx, dy])) as typeof window.scrollBy
|
||||
try {
|
||||
h.editor.ctrl.beginPaletteDrag({ type: 'heading', label: 'heading' })
|
||||
await ptr('pointermove', 400, window.innerHeight - 4)
|
||||
await act(async () => void (await new Promise((r) => setTimeout(r, 40)))) // let the rAF autoscroll step run
|
||||
expect(scrolled.length).toBeGreaterThan(0)
|
||||
expect(scrolled[0]).toEqual([0, expect.any(Number)])
|
||||
expect(scrolled[0][1]).toBeGreaterThan(0)
|
||||
await ptr('pointerup', 400, window.innerHeight - 4)
|
||||
await h.settle()
|
||||
} finally {
|
||||
window.scrollBy = realScrollBy
|
||||
}
|
||||
expect(h.editor.ctrl.store.get().drag).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('insertion slots (virtual "+" elements)', () => {
|
||||
|
||||
Reference in new issue
Block a user