openpencil/tests/e2e/canvas/split-views.spec.ts
Danila Poyarkov 7a02ee2c62
feat(editor): model split canvas panes (#519)
* refactor(editor): separate canvas view state

- Classify shared and view-local editor state explicitly

- Let canvas surfaces render supplied view state and report their viewport

- Preserve the existing one-canvas behavior by default

* fix(canvas): preserve loading render state

* feat(editor): model split canvas panes

- Add pure recursive split-tree operations with validated sizes

- Add explicit pane registry and independent view-state cloning

- Cap visible panes and cover close and split behavior

* refactor(editor): group state ownership modules

- Move shared and view state into the editor state namespace

- Model the partition with explicit interfaces and default factories

- Derive runtime view keys from the default view object

* feat(editor): model split canvas panes

- Add pure recursive split-tree operations with validated sizes

- Add explicit pane registry and independent view-state cloning

- Cap visible panes and cover close and split behavior

* feat(editor): add split canvas views

- Render recursive pane layouts with Reka UI splitters and pane-local headers
- Route canvas input, selection, viewport state, and close actions to the active pane
- Repaint every canvas during shared document previews and cover split lifecycle in tests

* fix(editor): clean up inactive pane interactions

- Cancel drag, padding preview, and text editing state when pane focus changes
- Remove duplicate changelog entries introduced while updating master
2026-08-15 10:31:52 +03:00

55 lines
2.4 KiB
TypeScript

import { expect, test } from '@playwright/test'
import { CanvasHelper } from '#tests/helpers/canvas'
test.describe('split canvas views', () => {
test('splits, resizes, activates, and closes independent canvas views', async ({ page }) => {
await page.goto('/')
const canvas = new CanvasHelper(page)
await canvas.waitForInit()
await expect(page.locator('[data-slot="canvas-pane-header"]')).toHaveCount(0)
await canvas.drawRect(140, 180, 180, 120)
await page.getByRole('menuitem', { name: 'View', exact: true }).click()
await page.getByRole('menuitem', { name: 'Split right' }).click()
const headers = page.locator('[data-slot="canvas-pane-header"]')
await expect(headers).toHaveCount(2)
await expect(headers.nth(1)).toHaveAttribute('data-active', 'true')
await expect(headers.nth(1)).toContainText('Page 1')
await expect(headers.nth(1)).toContainText('100%')
const panes = page.locator('[data-active-pane]')
await expect(panes).toHaveCount(2)
await expect(panes.nth(1)).toHaveAttribute('data-active-pane', 'true')
await expect(page.locator('canvas[data-ready="1"]')).toHaveCount(4)
const firstBox = await panes.nth(0).boundingBox()
const secondBox = await panes.nth(1).boundingBox()
expect(firstBox?.width).toBeGreaterThan(300)
expect(secondBox?.width).toBeGreaterThan(300)
await panes.nth(0).click({ position: { x: 100, y: 100 } })
await expect(panes.nth(0)).toHaveAttribute('data-active-pane', 'true')
await expect(headers.nth(0)).toHaveAttribute('data-active', 'true')
const handle = page.locator('[data-split-id]').first()
const handleBox = await handle.boundingBox()
if (!handleBox) throw new Error('Expected split handle')
await page.mouse.move(handleBox.x, handleBox.y + handleBox.height / 2)
await page.mouse.down()
await page.mouse.move(handleBox.x + 100, handleBox.y + handleBox.height / 2)
await page.mouse.up()
const resizedFirstBox = await panes.nth(0).boundingBox()
const resizedSecondBox = await panes.nth(1).boundingBox()
expect(resizedFirstBox?.width).toBeGreaterThan(firstBox?.width ?? 0)
expect(resizedSecondBox?.width).toBeLessThan(secondBox?.width ?? Number.POSITIVE_INFINITY)
await headers.nth(0).getByRole('button', { name: 'Close view' }).click()
await expect(panes).toHaveCount(1)
await expect(headers).toHaveCount(0)
await expect(page.locator('canvas[data-ready="1"]')).toHaveCount(2)
})
})