fix(editor): make repeated "Cancel line number" clicks work in code blocks (#14804)

Fixes #13555


https://github.com/user-attachments/assets/12e55c21-080c-4c69-9780-893ccad25b45

## Summary
- make the code block More popup reactive to `wrap` and `lineNumber`
prop updates
- read the latest toggle state at click time so same-menu double toggles
do not reuse stale closures
- add e2e coverage for wrap and line number toggling twice without
closing the More menu

## Bug Reason
- the code block More popup was rendered as a static portal, so it
stayed open without re-rendering after the first toggle
- the `Cancel line number` and `Wrap` menu actions captured render-time
state in their click handlers
- after the first click updated the model, a second click in the same
open menu reused stale state and wrote the same value again, so nothing
changed visually

## Testing
- yarn workspace @affine-test/blocksuite test e2e/code/crud.spec.ts

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Menu toggles now read and update the current wrap and line-number
states reliably.

* **Refactor**
* Replaced inline popup rendering with a dedicated more-menu component
for the code toolbar.

* **Style**
* Prevented text selection on menu action elements for smoother
interaction.

* **Tests**
  * Added e2e tests for wrap and line-number toggle flows.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Whitewater
2026-04-07 03:18:15 +08:00
committed by GitHub
parent a1ae7d11a3
commit 2ca4973167
6 changed files with 119 additions and 15 deletions
+54
View File
@@ -286,6 +286,32 @@ test('toggle code block wrap can work', async ({ page }, testInfo) => {
);
});
test('toggle code block wrap can work in the same more menu', async ({
page,
}) => {
await enterPlaygroundRoom(page);
await initEmptyCodeBlockState(page);
const codeBlockController = getCodeBlock(page);
const codeBlockContainer = page.locator(
'affine-code .affine-code-block-container'
);
await codeBlockController.codeBlock.hover();
const moreMenu = await codeBlockController.openMore();
await moreMenu.wrapButton.click();
await expect(moreMenu.menu).toBeVisible();
await expect(codeBlockContainer).toHaveClass(/wrap/);
await expect(moreMenu.cancelWrapButton).toBeVisible();
await moreMenu.cancelWrapButton.click();
await expect(moreMenu.menu).toBeVisible();
await expect(codeBlockContainer).not.toHaveClass(/wrap/);
});
test('add caption works', async ({ page }, testInfo) => {
await enterPlaygroundRoom(page);
await initEmptyCodeBlockState(page);
@@ -355,6 +381,34 @@ test('toggle code block line number can work', async ({ page }) => {
await expect(lineNumber).toBeVisible();
});
test('toggle code block line number can work in the same more menu', async ({
page,
}) => {
await enterPlaygroundRoom(page);
await initEmptyCodeBlockState(page);
await focusRichText(page);
const lineNumber = page.locator('affine-code .line-number');
await expect(lineNumber).toBeVisible();
const codeBlockController = getCodeBlock(page);
await codeBlockController.codeBlock.hover();
const moreMenu = await codeBlockController.openMore();
await moreMenu.cancelLineNumberButton.click();
await expect(moreMenu.menu).toBeVisible();
await expect(lineNumber).toBeHidden();
await expect(moreMenu.lineNumberButton).toBeVisible();
await moreMenu.lineNumberButton.click();
await expect(moreMenu.menu).toBeVisible();
await expect(lineNumber).toBeVisible();
});
test('code block toolbar widget can appear and disappear during mousemove', async ({
page,
}) => {