mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-21 08:17:10 +08:00
Close [BS-3351](https://linear.app/affine-design/issue/BS-3351/无法通过拖拽frame-title来拖拽frame) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Improved rendering performance and consistency for widgets within frames. - Frame titles are now directly associated with individual frames and are draggable. - **Bug Fixes** - Selection logic for frames has been refined to better handle locked states and title area interactions. - **Refactor** - Frame title widget and related components have been simplified for clarity and maintainability. - Removed dynamic positioning and click toggling from frame titles for a cleaner interaction model. - **Tests** - Added a test to verify that frame titles are draggable. - Temporarily disabled tests related to frame title stacking and selection due to ongoing changes. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
130 lines
3.7 KiB
TypeScript
130 lines
3.7 KiB
TypeScript
import type { FrameBlockComponent } from '@blocksuite/affine/blocks/frame';
|
|
import type { EdgelessRootBlockComponent } from '@blocksuite/affine/blocks/root';
|
|
import type { FrameBlockModel } from '@blocksuite/affine/model';
|
|
import type { AffineFrameTitleWidget } from '@blocksuite/affine/widgets/frame-title';
|
|
import { assertType } from '@blocksuite/global/utils';
|
|
import { Text } from '@blocksuite/store';
|
|
import { beforeEach, describe, expect, test } from 'vitest';
|
|
|
|
import { wait } from '../utils/common.js';
|
|
import { getDocRootBlock } from '../utils/edgeless.js';
|
|
import { setupEditor } from '../utils/setup.js';
|
|
|
|
describe('frame', () => {
|
|
let service!: EdgelessRootBlockComponent['service'];
|
|
|
|
beforeEach(async () => {
|
|
const cleanup = await setupEditor('edgeless');
|
|
service = getDocRootBlock(window.doc, window.editor, 'edgeless').service;
|
|
|
|
return cleanup;
|
|
});
|
|
|
|
test('frame should have title', async () => {
|
|
const frame = service.doc.addBlock(
|
|
'affine:frame',
|
|
{
|
|
xywh: '[0,0,300,300]',
|
|
title: new Text('Frame 1'),
|
|
},
|
|
service.surface.id
|
|
);
|
|
await wait();
|
|
|
|
const getFrameTitle = (frameId: string) => {
|
|
const frameTitleWidget = service.std.view.getWidget(
|
|
'affine-frame-title-widget',
|
|
frameId
|
|
) as AffineFrameTitleWidget | null;
|
|
return frameTitleWidget?.shadowRoot?.querySelector('affine-frame-title');
|
|
};
|
|
|
|
const frameTitle = getFrameTitle(frame);
|
|
const rect = frameTitle?.getBoundingClientRect();
|
|
|
|
expect(frameTitle).toBeTruthy();
|
|
expect(rect).toBeTruthy();
|
|
expect(rect!.width).toBeGreaterThan(0);
|
|
expect(rect!.height).toBeGreaterThan(0);
|
|
|
|
const [titleX, titleY] = service.viewport.toModelCoord(rect!.x, rect!.y);
|
|
expect(titleX).toBeCloseTo(0);
|
|
expect(titleY).toBeLessThan(0);
|
|
|
|
const nestedFrame = service.doc.addBlock(
|
|
'affine:frame',
|
|
{
|
|
xywh: '[20,20,200,200]',
|
|
title: new Text('Frame 2'),
|
|
},
|
|
service.surface.id
|
|
);
|
|
await wait();
|
|
|
|
const nestedTitle = getFrameTitle(nestedFrame);
|
|
expect(nestedTitle).toBeTruthy();
|
|
if (!nestedTitle) return;
|
|
|
|
const nestedTitleRect = nestedTitle.getBoundingClientRect()!;
|
|
const [nestedTitleX, nestedTitleY] = service.viewport.toModelCoord(
|
|
nestedTitleRect.x,
|
|
nestedTitleRect.y
|
|
);
|
|
|
|
expect(nestedTitleX).toBeGreaterThan(20);
|
|
expect(nestedTitleY).toBeGreaterThan(20);
|
|
});
|
|
|
|
test('frame should have externalXYWH after moving viewport to contains frame', async () => {
|
|
const frameId = service.doc.addBlock(
|
|
'affine:frame',
|
|
{
|
|
xywh: '[1800,1800,200,200]',
|
|
title: new Text('Frame 1'),
|
|
},
|
|
service.surface.id
|
|
);
|
|
await wait();
|
|
|
|
const frame = service.doc.getBlock(frameId);
|
|
expect(frame).toBeTruthy();
|
|
|
|
assertType<FrameBlockComponent>(frame);
|
|
|
|
service.viewport.setCenter(900, 900);
|
|
expect(frame?.model.externalXYWH).toBeDefined();
|
|
});
|
|
|
|
test('descendant of frame should not contain itself', async () => {
|
|
const frameIds = [1, 2, 3].map(i => {
|
|
return service.doc.addBlock(
|
|
'affine:frame',
|
|
{
|
|
xywh: '[0,0,300,300]',
|
|
title: new Text(`Frame ${i}`),
|
|
},
|
|
service.surface.id
|
|
);
|
|
});
|
|
|
|
await wait();
|
|
|
|
const frames = frameIds.map(
|
|
id => service.doc.getBlock(id)?.model as FrameBlockModel
|
|
);
|
|
|
|
frames.forEach(frame => {
|
|
expect(frame.descendantElements).toHaveLength(0);
|
|
});
|
|
|
|
frames[0].addChild(frames[1]);
|
|
frames[1].addChild(frames[2]);
|
|
frames[2].addChild(frames[0]);
|
|
|
|
await wait();
|
|
expect(frames[0].descendantElements).toHaveLength(2);
|
|
expect(frames[1].descendantElements).toHaveLength(1);
|
|
expect(frames[2].descendantElements).toHaveLength(0);
|
|
});
|
|
});
|