#160: Plugin-Block — Bearbeiten-Knopf nach Moduswechsel wieder da
All checks were successful
CI / Lint, typecheck, test (pull_request) Successful in 4m39s
CI / Build container images (pull_request) Successful in 1m29s
CI / Auth e2e pack (pull_request) Successful in 7m22s
CI / Import/export fidelity gate (pull_request) Successful in 55s
CD / Build and push images (push) Successful in 18s
CD / Deploy to Test (push) Successful in 13s
CD / Smoke tests against Test (push) Successful in 1m16s
CD / Promote to Int (push) Successful in 11s
Release / Build release images and notes (push) Successful in 1m9s
CI / Lint, typecheck, test (push) Successful in 4m48s
CI / Build container images (push) Has been skipped
Release / Release-candidate operations QA (push) Successful in 52s
Prod deploy / Deploy the released images to Prod (push) Successful in 18s
CI / Auth e2e pack (push) Successful in 7m2s
CI / Import/export fidelity gate (push) Successful in 54s
All checks were successful
CI / Lint, typecheck, test (pull_request) Successful in 4m39s
CI / Build container images (pull_request) Successful in 1m29s
CI / Auth e2e pack (pull_request) Successful in 7m22s
CI / Import/export fidelity gate (pull_request) Successful in 55s
CD / Build and push images (push) Successful in 18s
CD / Deploy to Test (push) Successful in 13s
CD / Smoke tests against Test (push) Successful in 1m16s
CD / Promote to Int (push) Successful in 11s
Release / Build release images and notes (push) Successful in 1m9s
CI / Lint, typecheck, test (push) Successful in 4m48s
CI / Build container images (push) Has been skipped
Release / Release-candidate operations QA (push) Successful in 52s
Prod deploy / Deploy the released images to Prod (push) Successful in 18s
CI / Auth e2e pack (push) Successful in 7m2s
CI / Import/export fidelity gate (push) Successful in 54s
Die NodeView las editor.isEditable nur beim Mount. Die Seite mountet immer im Lesemodus, und der Moduswechsel läuft über setEditable() — das emittiert in TipTap nur ein update-Event, aber keine Transaction, weshalb React-NodeViews nie neu rendern (geprüft in @tiptap/react 3.27.1: updateProps feuert nur bei Node-Änderung und Selektions- Wechsel). Folge: die Block-Leiste blieb ohne Bearbeiten-Knopf, für alle Block-Plugins (ChordPro, Mermaid, Excalidraw, draw.io). Fix: useEditorEditable abonniert das update-Event und liest isEditable reaktiv; verliert die Seite die Editierbarkeit, während die Editier-UI des Plugins offen ist, fällt der Block auf render zurück (der Lesemodus blendet die Leiste aus, es gäbe sonst keinen Weg mehr heraus). Damit stimmt auch die setData-Schreibrecht-Prüfung (editableRef) wieder. Regressionstest im plugin-blocks-Pack: Block existiert bereits, Seite lädt im Lesemodus, Wechsel in den Edit-Modus zeigt den Knopf (fiel ohne Fix reproduzierbar durch); Rückweg Lesemodus→render mitgeprüft. Die bisherigen Tests fügten Blöcke immer erst nach dem Moduswechsel ein und konnten den Fall nicht sehen. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
a879561ec7
commit
db0e563f95
@ -115,6 +115,47 @@ test('a block round-trips: insert → edit data → reload → render', async ({
|
||||
await admin.close();
|
||||
});
|
||||
|
||||
test('the edit affordance appears when edit mode starts after load (issue #160)', async ({
|
||||
browser,
|
||||
}) => {
|
||||
const admin = await contextForUser(browser, BASE_URL, 'fixture-admin');
|
||||
await installAsRequired(admin);
|
||||
const pond = await personalPond(admin);
|
||||
const created = await createPage(admin, pond.id, `E2E Block Stale Editable ${Date.now()}`);
|
||||
|
||||
// Seed a page that already carries a block (the edit round-trip persists
|
||||
// "+e" into the document before the reload below).
|
||||
const seeded = await openEditor(admin, pond.slug, created.slug);
|
||||
await insertBlock(seeded);
|
||||
await seeded.locator('.plugin-block__bar button').click();
|
||||
await expect(frameBody(seeded)).toHaveText('editing:+e');
|
||||
await seeded.locator('.plugin-block__bar button').click();
|
||||
await expect(frameBody(seeded)).toHaveText('block:+e');
|
||||
|
||||
// A fresh load mounts the node view in read mode. Entering edit mode goes
|
||||
// through `setEditable`, which dispatches no transaction — the bug (#160)
|
||||
// was a stale `editable` from mount keeping the edit button hidden.
|
||||
await seeded.reload();
|
||||
await expect(frameBody(seeded)).toHaveText('block:+e', { timeout: 15000 });
|
||||
await expect(seeded.locator('.plugin-block__surface')).toHaveAttribute('data-state', 'ready');
|
||||
|
||||
await seeded.locator('.editor-page__mode-toggle').click();
|
||||
await expect(seeded.locator('.ProseMirror')).toHaveAttribute('contenteditable', 'true');
|
||||
const editButton = seeded.locator('.plugin-block__bar button');
|
||||
await expect(editButton).toBeVisible();
|
||||
|
||||
// The button is functional, not just painted.
|
||||
await editButton.click();
|
||||
await expect(frameBody(seeded)).toHaveText('editing:+e+e');
|
||||
|
||||
// Leaving page edit mode while the plugin's edit UI is open must drop the
|
||||
// frame back to render — read mode has no bar to leave edit mode with.
|
||||
await seeded.locator('.editor-page__mode-toggle').click();
|
||||
await expect(frameBody(seeded)).toHaveText('block:+e+e');
|
||||
|
||||
await admin.close();
|
||||
});
|
||||
|
||||
test('two collaborating users see block-data changes live', async ({ browser }) => {
|
||||
const owner = await contextForUser(browser, BASE_URL, 'fixture-user');
|
||||
const admin = await contextForUser(browser, BASE_URL, 'fixture-admin');
|
||||
|
||||
@ -27,6 +27,26 @@ declare module '@tiptap/core' {
|
||||
|
||||
type BlockMode = 'render' | 'edit';
|
||||
|
||||
/**
|
||||
* `editor.isEditable`, kept live (issue #160): the page's read/edit toggle
|
||||
* flips editability through `setEditable`, which emits `update` but dispatches
|
||||
* no transaction — so React node views never re-render on their own and a
|
||||
* plain `editor.isEditable` read goes stale at mount (the page always mounts
|
||||
* in read mode, hiding the block's edit button for the whole session).
|
||||
*/
|
||||
function useEditorEditable(editor: NodeViewProps['editor']): boolean {
|
||||
const [editable, setEditable] = useState(editor.isEditable);
|
||||
useEffect(() => {
|
||||
const sync = (): void => setEditable(editor.isEditable);
|
||||
sync();
|
||||
editor.on('update', sync);
|
||||
return () => {
|
||||
editor.off('update', sync);
|
||||
};
|
||||
}, [editor]);
|
||||
return editable;
|
||||
}
|
||||
|
||||
/**
|
||||
* The live surface of one plugin block (issue #76): a sandboxed iframe (#73)
|
||||
* driven through the plugin's `render`/`edit` lifecycle. Data flows two ways —
|
||||
@ -133,6 +153,13 @@ function ActivePluginBlock({
|
||||
.catch(() => undefined);
|
||||
}
|
||||
|
||||
// Losing editability while the plugin's edit UI is open (the page toggled
|
||||
// back to read mode) must drop the frame back to render — read mode shows
|
||||
// no bar to leave edit mode with (issue #160).
|
||||
useEffect(() => {
|
||||
if (!editable && modeRef.current === 'edit') void switchMode('render');
|
||||
}, [editable]);
|
||||
|
||||
return (
|
||||
<div className="plugin-block__surface" data-state={state} data-mode={mode}>
|
||||
<div className="plugin-block__bar" contentEditable={false}>
|
||||
@ -208,6 +235,7 @@ function PluginBlockView({
|
||||
const blockType = node.attrs.blockType as string;
|
||||
const plugins = usePondPlugins(scope.pondId);
|
||||
const plugin = plugins.data?.find((entry) => entry.id === pluginId && entry.kind === 'code');
|
||||
const editable = useEditorEditable(editor);
|
||||
|
||||
return (
|
||||
<NodeViewWrapper
|
||||
@ -219,7 +247,7 @@ function PluginBlockView({
|
||||
plugin={plugin}
|
||||
blockType={blockType}
|
||||
data={node.attrs.data}
|
||||
editable={editor.isEditable}
|
||||
editable={editable}
|
||||
updateData={(data) => updateAttributes({ data })}
|
||||
/>
|
||||
) : plugins.isSuccess ? (
|
||||
|
||||
Loading…
Reference in New Issue
Block a user