fix: pan board from empty space
This commit is contained in:
@@ -24,7 +24,7 @@ async function waitForSave(page: Page, action: () => Promise<void>) {
|
|||||||
await response
|
await response
|
||||||
}
|
}
|
||||||
|
|
||||||
test('move, folder expansion, hand pan, desktop wheel zoom, mobile pinch, and reload persistence', async ({ page }) => {
|
test('move, folder expansion, empty-board pan, desktop wheel zoom, mobile pinch, and reload persistence', async ({ page }) => {
|
||||||
await page.goto('/?level=e2e-level&edit=1')
|
await page.goto('/?level=e2e-level&edit=1')
|
||||||
await expect(page.getByRole('heading', { name: 'Browser Safety Test' })).toBeVisible()
|
await expect(page.getByRole('heading', { name: 'Browser Safety Test' })).toBeVisible()
|
||||||
await expect(page.locator('.documents-panel')).toHaveClass(/\bclosed\b/)
|
await expect(page.locator('.documents-panel')).toHaveClass(/\bclosed\b/)
|
||||||
@@ -65,14 +65,13 @@ test('move, folder expansion, hand pan, desktop wheel zoom, mobile pinch, and re
|
|||||||
|
|
||||||
const folderBeforePan = await boardPosition(folder)
|
const folderBeforePan = await boardPosition(folder)
|
||||||
const transformBeforePan = await board.getAttribute('style')
|
const transformBeforePan = await board.getAttribute('style')
|
||||||
await page.getByRole('button', { name: 'HAND', exact: true }).click()
|
await expect(page.getByRole('button', { name: 'MOVE', exact: true })).toHaveClass(/\bactive\b/)
|
||||||
const viewportBox = await boardViewport.boundingBox()
|
const viewportBox = await boardViewport.boundingBox()
|
||||||
if (!viewportBox) throw new Error('Board viewport is not visible')
|
if (!viewportBox) throw new Error('Board viewport is not visible')
|
||||||
await waitForSave(page, async () => {
|
await waitForSave(page, async () => {
|
||||||
await page.mouse.move(viewportBox.x + 240, viewportBox.y + 230)
|
await boardViewport.dispatchEvent('pointerdown', { pointerId: 31, pointerType: 'mouse', isPrimary: true, button: 0, clientX: viewportBox.x + 240, clientY: viewportBox.y + 230 })
|
||||||
await page.mouse.down()
|
await boardViewport.dispatchEvent('pointermove', { pointerId: 31, pointerType: 'mouse', isPrimary: true, button: 0, clientX: viewportBox.x + 290, clientY: viewportBox.y + 195 })
|
||||||
await page.mouse.move(viewportBox.x + 290, viewportBox.y + 195, { steps: 5 })
|
await boardViewport.dispatchEvent('pointerup', { pointerId: 31, pointerType: 'mouse', isPrimary: true, button: 0, clientX: viewportBox.x + 290, clientY: viewportBox.y + 195 })
|
||||||
await page.mouse.up()
|
|
||||||
})
|
})
|
||||||
expect(await boardPosition(folder)).toEqual(folderBeforePan)
|
expect(await boardPosition(folder)).toEqual(folderBeforePan)
|
||||||
const transformAfterPan = await board.getAttribute('style')
|
const transformAfterPan = await board.getAttribute('style')
|
||||||
|
|||||||
+6
-1
@@ -518,7 +518,12 @@ function Board({ state, selected, linkFrom, recentlyCreatedExhibitId, recentlyCr
|
|||||||
const toggleFolder = (id: string) => update(s => ({ ...s, evidence: s.evidence.map(widget => widget.id === id ? { ...widget, config: { ...(widget.config || {}), open: !folderIsOpen(widget) } } : widget) }))
|
const toggleFolder = (id: string) => update(s => ({ ...s, evidence: s.evidence.map(widget => widget.id === id ? { ...widget, config: { ...(widget.config || {}), open: !folderIsOpen(widget) } } : widget) }))
|
||||||
const previewOrigin = linkFrom ? pointForId(linkFrom) : undefined
|
const previewOrigin = linkFrom ? pointForId(linkFrom) : undefined
|
||||||
return <div className={`board-viewport tool-${tool} ${linkFrom ? 'threading' : ''}`} ref={boardRef}
|
return <div className={`board-viewport tool-${tool} ${linkFrom ? 'threading' : ''}`} ref={boardRef}
|
||||||
onPointerDown={e => { if (!(e.target as HTMLElement).closest('.thread-tag')) setExpandedThreadTagId(null); if (e.pointerType === 'touch' || tool === 'hand' || e.button === 1) { e.preventDefault(); pointerDown(e) } }}
|
onPointerDown={e => {
|
||||||
|
const target = e.target as HTMLElement
|
||||||
|
if (!target.closest('.thread-tag')) setExpandedThreadTagId(null)
|
||||||
|
const emptyBoardDrag = e.button === 0 && !target.closest('.evidence-card, .source-file-widget, .thread-tag, button')
|
||||||
|
if (e.pointerType === 'touch' || tool === 'hand' || e.button === 1 || emptyBoardDrag) { e.preventDefault(); pointerDown(e) }
|
||||||
|
}}
|
||||||
onPointerMoveCapture={trackThreadPointer} onPointerMove={pointerMove} onPointerLeave={() => setThreadPointer(null)} onPointerUp={finishDrag} onPointerCancel={finishDrag} onAuxClick={e => { if (e.button === 1) e.preventDefault() }}>
|
onPointerMoveCapture={trackThreadPointer} onPointerMove={pointerMove} onPointerLeave={() => setThreadPointer(null)} onPointerUp={finishDrag} onPointerCancel={finishDrag} onAuxClick={e => { if (e.button === 1) e.preventDefault() }}>
|
||||||
<div className="board" style={{ width: BOARD_W, height: BOARD_H, transform: `translate(${state.viewport.x}px, ${state.viewport.y}px) scale(${state.viewport.zoom})` }}>
|
<div className="board" style={{ width: BOARD_W, height: BOARD_H, transform: `translate(${state.viewport.x}px, ${state.viewport.y}px) scale(${state.viewport.zoom})` }}>
|
||||||
<div className="board-stamp">AUTHORIZED CITIZEN SCIENTIST WORKSTATION <span>GU-NET / 04</span></div>
|
<div className="board-stamp">AUTHORIZED CITIZEN SCIENTIST WORKSTATION <span>GU-NET / 04</span></div>
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ button:focus-visible { outline: 2px solid #e99a44; outline-offset: 2px; }
|
|||||||
.case-number { border-left: 1px solid #415750; margin-left: 26px; padding-left: 17px; font: 8px IBM Plex Mono; color: #6d867f; line-height: 1.5; }
|
.case-number { border-left: 1px solid #415750; margin-left: 26px; padding-left: 17px; font: 8px IBM Plex Mono; color: #6d867f; line-height: 1.5; }
|
||||||
.case-number b { color: #bdc9c3; font-size: 13px; }
|
.case-number b { color: #bdc9c3; font-size: 13px; }
|
||||||
.board-viewport { position: absolute; inset: 0; overflow: hidden; touch-action: none; overscroll-behavior: contain; cursor: default; background-image: radial-gradient(#49615a55 1px, transparent 1px), linear-gradient(90deg, #18302a33 1px, transparent 1px), linear-gradient(#18302a33 1px, transparent 1px); background-size: 20px 20px, 100px 100px, 100px 100px; }
|
.board-viewport { position: absolute; inset: 0; overflow: hidden; touch-action: none; overscroll-behavior: contain; cursor: default; background-image: radial-gradient(#49615a55 1px, transparent 1px), linear-gradient(90deg, #18302a33 1px, transparent 1px), linear-gradient(#18302a33 1px, transparent 1px); background-size: 20px 20px, 100px 100px, 100px 100px; }
|
||||||
|
.board-viewport.tool-move { cursor: grab; }
|
||||||
|
.board-viewport.tool-move:active { cursor: grabbing; }
|
||||||
.board-viewport.tool-hand, .board-viewport.tool-hand .evidence-card, .board-viewport.tool-hand .source-file-widget { cursor: grab; }
|
.board-viewport.tool-hand, .board-viewport.tool-hand .evidence-card, .board-viewport.tool-hand .source-file-widget { cursor: grab; }
|
||||||
.board-viewport.tool-hand:active, .board-viewport.tool-hand .evidence-card:active, .board-viewport.tool-hand .source-file-widget:active { cursor: grabbing; }
|
.board-viewport.tool-hand:active, .board-viewport.tool-hand .evidence-card:active, .board-viewport.tool-hand .source-file-widget:active { cursor: grabbing; }
|
||||||
.board-viewport.threading, .board-viewport.threading .evidence-card, .board-viewport.threading .source-file-widget { cursor: crosshair; }
|
.board-viewport.threading, .board-viewport.threading .evidence-card, .board-viewport.threading .source-file-widget { cursor: crosshair; }
|
||||||
|
|||||||
Reference in New Issue
Block a user