mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-27 02:42:25 +08:00
feat(editor): improve visibility of hidden content of edgeless note (#12068)
Close [BS-3066](https://linear.app/affine-design/issue/BS-3066/优化长note的展示和折叠) - Enhanced the visibility behavior of hidden content in edgeless notes by: - Showing hidden content when a note is being edited, even when it's outside the viewport - Improving hover behavior with a delay when leaving from the bottom of the note - Adding proper cleanup of hover timeouts when the component is disconnected - Optimizing the viewport element to keep editing blocks or elements visible ## Testing - Added new E2E test cases covering: - Hover behavior on selected notes - Content visibility during editing - Viewport scrolling behavior - Edge cases for content visibility ## Impact This change improves the user experience when working with collapsed notes in edgeless mode by making the content more accessible and preventing accidental content hiding during editing. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Bug Fixes** - Improved visibility of hidden content in edgeless notes when hovering near the bottom edge or editing the note, especially after resizing or clipping. - **New Features** - Enhanced hover behavior with delayed clearing based on mouse position to improve user experience. - **Tests** - Added new tests verifying hidden content visibility in edgeless notes during hover and editing, simulating diverse user interactions. - **Chores** - Added utilities to get and set viewport center for improved test control. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -10,10 +10,13 @@ import {
|
||||
dragBetweenViewCoords,
|
||||
getSelectedBound,
|
||||
getSelectedBoundCount,
|
||||
getViewportCenter,
|
||||
locatorComponentToolbar,
|
||||
locatorEdgelessZoomToolButton,
|
||||
resizeElementByHandle,
|
||||
selectNoteInEdgeless,
|
||||
setEdgelessTool,
|
||||
setViewportCenter,
|
||||
switchEditorMode,
|
||||
triggerComponentToolbarAction,
|
||||
zoomOutByKeyboard,
|
||||
@@ -34,6 +37,7 @@ import {
|
||||
pressArrowUp,
|
||||
pressBackspace,
|
||||
pressEnter,
|
||||
pressEscape,
|
||||
pressTab,
|
||||
selectAllByKeyboard,
|
||||
type,
|
||||
@@ -576,3 +580,111 @@ test('should not select doc only note', async ({ page }) => {
|
||||
);
|
||||
expect(await getSelectedBoundCount(page)).toBe(0);
|
||||
});
|
||||
|
||||
test.describe('visibility of hidden content of edgeless note', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await enterPlaygroundRoom(page);
|
||||
await initEmptyEdgelessState(page);
|
||||
await switchEditorMode(page);
|
||||
|
||||
const note = page.locator('affine-edgeless-note');
|
||||
await note.click({ clickCount: 3 });
|
||||
await type(page, 'hello');
|
||||
await pressEnter(page, 30);
|
||||
await type(page, 'world');
|
||||
await pressEscape(page, 3);
|
||||
|
||||
const vpCenter = await getViewportCenter(page);
|
||||
vpCenter[1] += 1000;
|
||||
await setViewportCenter(page, vpCenter);
|
||||
|
||||
await note.click();
|
||||
await resizeElementByHandle(page, { x: 0, y: -300 }, 'bottom-right');
|
||||
});
|
||||
|
||||
test('should hide content when note is not selected or hovered when selected', async ({
|
||||
page,
|
||||
}) => {
|
||||
const note = page.locator('affine-edgeless-note');
|
||||
const lastParagraph = page.locator('affine-paragraph').last();
|
||||
|
||||
await pressEscape(page, 3);
|
||||
await expect(lastParagraph).not.toBeInViewport();
|
||||
|
||||
const noteBound = await note.boundingBox();
|
||||
if (!noteBound) {
|
||||
test.fail();
|
||||
return;
|
||||
}
|
||||
await note.click();
|
||||
// move out to right side
|
||||
await page.mouse.move(
|
||||
noteBound.x + noteBound.width + 10,
|
||||
noteBound.y + noteBound.height - 10
|
||||
);
|
||||
await expect(lastParagraph).not.toBeInViewport();
|
||||
});
|
||||
|
||||
test('should show hidden content when hover on selected note', async ({
|
||||
page,
|
||||
}) => {
|
||||
const note = page.locator('affine-edgeless-note');
|
||||
const lastParagraph = page.locator('affine-paragraph').last();
|
||||
|
||||
const noteBound = await note.boundingBox();
|
||||
if (!noteBound) {
|
||||
test.fail();
|
||||
return;
|
||||
}
|
||||
|
||||
// move in right side
|
||||
await page.mouse.move(
|
||||
noteBound.x + noteBound.width - 10,
|
||||
noteBound.y + noteBound.height - 10
|
||||
);
|
||||
await expect(lastParagraph).toBeInViewport();
|
||||
|
||||
// move to hidden content
|
||||
await page.mouse.move(
|
||||
noteBound.x + noteBound.width - 10,
|
||||
noteBound.y + noteBound.height + 100
|
||||
);
|
||||
await expect(lastParagraph).toBeInViewport();
|
||||
});
|
||||
|
||||
test('should show hidden content when the note is being edited', async ({
|
||||
page,
|
||||
}) => {
|
||||
const note = page.locator('affine-edgeless-note');
|
||||
const lastParagraph = page.locator('affine-paragraph').last();
|
||||
|
||||
await note.click({ clickCount: 3 });
|
||||
await page.locator('affine-paragraph').nth(22).click();
|
||||
await type(page, 'test');
|
||||
|
||||
await expect(lastParagraph).toBeInViewport();
|
||||
|
||||
await note.click({ clickCount: 3 });
|
||||
await page.locator('affine-paragraph').nth(22).click();
|
||||
|
||||
const noteBound = await note.boundingBox();
|
||||
if (!noteBound) {
|
||||
test.fail();
|
||||
return;
|
||||
}
|
||||
await page.mouse.move(
|
||||
noteBound.x + noteBound.width - 10,
|
||||
noteBound.y + noteBound.height - 10
|
||||
);
|
||||
await expect(lastParagraph).toBeInViewport();
|
||||
|
||||
await note.click({ clickCount: 3 });
|
||||
await page.locator('affine-paragraph').nth(22).click();
|
||||
|
||||
await page.mouse.wheel(0, 200);
|
||||
await expect(
|
||||
lastParagraph,
|
||||
'editing note but out of viewport should also show hidden content'
|
||||
).toBeInViewport();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -970,6 +970,25 @@ export async function getZoomLevel(page: Page) {
|
||||
return Number(text.replace('%', ''));
|
||||
}
|
||||
|
||||
export async function getViewportCenter(page: Page): Promise<[number, number]> {
|
||||
return page.evaluate(() => {
|
||||
const target = document.querySelector('affine-edgeless-root');
|
||||
if (!target) {
|
||||
throw new Error('Missing edgeless page');
|
||||
}
|
||||
return [target.gfx.viewport.centerX, target.gfx.viewport.centerY];
|
||||
});
|
||||
}
|
||||
export async function setViewportCenter(page: Page, center: [number, number]) {
|
||||
await page.evaluate(center => {
|
||||
const target = document.querySelector('affine-edgeless-root');
|
||||
if (!target) {
|
||||
throw new Error('Missing edgeless page');
|
||||
}
|
||||
target.gfx.viewport.setCenter(center[0], center[1]);
|
||||
}, center);
|
||||
}
|
||||
|
||||
export async function optionMouseDrag(
|
||||
page: Page,
|
||||
start: number[],
|
||||
|
||||
Reference in New Issue
Block a user