test(editor): cover auto-layout padding editor

This commit is contained in:
Danila Poyarkov 2026-05-14 22:37:16 +03:00
parent 3eedd0740e
commit 031897c73a
5 changed files with 130 additions and 6 deletions

View file

@ -17,11 +17,15 @@ export function useCanvasVirtualReference(
const canvas = canvasRef.value
if (!point || !canvas) return null
const zoom = editor.state.zoom
const panX = editor.state.panX
const panY = editor.state.panY
return {
getBoundingClientRect() {
const rect = canvas.getBoundingClientRect()
const x = rect.left + point.x * editor.state.zoom + editor.state.panX
const y = rect.top + point.y * editor.state.zoom + editor.state.panY
const x = rect.left + point.x * zoom + panX
const y = rect.top + point.y * zoom + panY
return new DOMRect(x, y, 0, 0)
}
}

View file

@ -78,8 +78,6 @@ const paddingSideIcons = {
left: IconLucidePanelLeft
} satisfies Record<'top' | 'right' | 'bottom' | 'left', Component>
const paddingEditorRef = ref<HTMLElement | null>(null)
const paddingEditorAnchor = computed(() => {
const edit = autoLayoutPaddingEdit.value
if (!edit) return null
@ -102,7 +100,9 @@ const paddingEditorIcon = computed(() => {
watch(autoLayoutPaddingEdit, async (edit) => {
if (!edit) return
await nextTick()
const input = paddingEditorRef.value?.querySelector<HTMLInputElement>('input')
const input = document.querySelector<HTMLInputElement>(
'[data-test-id="auto-layout-padding-editor"] input'
)
input?.focus()
input?.select()
})
@ -145,7 +145,6 @@ const cursor = computed(() => toolCursor(store.state.activeTool, cursorOverride.
<PopoverPortal>
<PopoverContent
v-if="autoLayoutPaddingEdit && paddingEditorReference"
ref="paddingEditorRef"
:reference="paddingEditorReference"
side="top"
align="center"
@ -164,6 +163,12 @@ const cursor = computed(() => toolCursor(store.state.activeTool, cursorOverride.
data-test-id="auto-layout-padding-input"
@update:model-value="updateAutoLayoutPaddingEdit"
@commit="(value: number) => commitAutoLayoutPaddingEdit(value)"
@editing-change="
(editing: boolean) =>
!editing &&
autoLayoutPaddingEdit &&
commitAutoLayoutPaddingEdit(autoLayoutPaddingEdit.value)
"
>
<template #icon>
<component :is="paddingEditorIcon" class="size-3.5" />

View file

@ -0,0 +1,115 @@
import { expect, test, type Page } from '@playwright/test'
import { CanvasHelper } from '#tests/helpers/canvas'
async function dblclickCanvas(page: Page, x: number, y: number) {
const canvas = page.locator('[data-test-id="canvas-element"]')
const box = await canvas.boundingBox()
if (!box) throw new Error('Canvas has no bounding box')
await canvas.dispatchEvent('dblclick', {
clientX: box.x + x,
clientY: box.y + y,
bubbles: true,
cancelable: true
})
}
async function setupAutoLayoutFrame(page: Page) {
return page.evaluate(() => {
const store = window.openPencil?.getStore?.()
if (!store) throw new Error('OpenPencil store not initialized')
store.state.panX = 0
store.state.panY = 0
store.state.zoom = 1
const pageId = store.state.currentPageId
const frame = store.graph.createNode('FRAME', pageId, {
name: 'Padding editor frame',
x: 160,
y: 120,
width: 280,
height: 160,
layoutMode: 'VERTICAL',
itemSpacing: 8,
paddingTop: 20,
paddingRight: 20,
paddingBottom: 20,
paddingLeft: 20
})
store.graph.createNode('RECTANGLE', frame.id, {
name: 'Child',
x: 180,
y: 140,
width: 120,
height: 40
})
store.select([frame.id])
store.requestRender()
return frame.id
})
}
async function framePaddingTop(page: Page, frameId: string) {
return page.evaluate((id) => {
const store = window.openPencil?.getStore?.()
if (!store) throw new Error('OpenPencil store not initialized')
return store.graph.getNode(id)?.paddingTop ?? null
}, frameId)
}
test('double-clicking an auto-layout padding handle opens a scrub editor', async ({ page }) => {
await page.goto('/')
const canvas = new CanvasHelper(page)
await canvas.waitForInit()
await canvas.clearCanvas()
const frameId = await setupAutoLayoutFrame(page)
await canvas.waitForRender()
await canvas.hover(300, 130)
await dblclickCanvas(page, 300, 130)
const editor = page.locator('[data-test-id="auto-layout-padding-editor"]')
await expect(editor).toBeVisible()
await canvas.dragScrubInput(editor.locator('[data-test-id="auto-layout-padding-input"]'), 40)
await expect(editor).toHaveCount(0)
const changedPadding = await framePaddingTop(page, frameId)
expect(changedPadding).not.toBeNull()
expect(changedPadding).toBeGreaterThan(20)
await canvas.undo()
expect(await framePaddingTop(page, frameId)).toBe(20)
canvas.assertNoErrors()
})
test('auto-layout padding editor follows canvas pan while open', async ({ page }) => {
await page.goto('/')
const canvas = new CanvasHelper(page)
await canvas.waitForInit()
await canvas.clearCanvas()
await setupAutoLayoutFrame(page)
await canvas.waitForRender()
await canvas.hover(300, 130)
await dblclickCanvas(page, 300, 130)
const editor = page.locator('[data-test-id="auto-layout-padding-editor"]')
await expect(editor).toBeVisible()
const before = await editor.boundingBox()
if (!before) throw new Error('Padding editor has no bounding box')
await page.evaluate(() => {
const store = window.openPencil?.getStore?.()
if (!store) throw new Error('OpenPencil store not initialized')
store.pan(40, 0)
})
await canvas.waitForRender()
const after = await editor.boundingBox()
if (!after) throw new Error('Padding editor has no bounding box after pan')
expect(after.x).toBeGreaterThan(before.x + 20)
canvas.assertNoErrors()
})