Skip to content

Commit 812bcc1

Browse files
harbournicksuperdoc-oss-port[bot]
authored andcommitted
Merge pull request #140 from superdoc/nick/fix-behavior
fix: repair comment behavior regressions Note: this ports only the public subtree changes from a mixed source commit (8 public paths, 45 non-public paths ignored). Ported-From-Source-Repo: superdoc/orbit Ported-From-Source-Commit: f033b3cd7f1e567e4414936f5d9fdc86f2b8e0fe Ported-Public-Prefix: superdoc/public
1 parent 19243ec commit 812bcc1

8 files changed

Lines changed: 145 additions & 22 deletions

File tree

packages/super-editor/src/editors/v1/core/presentation-editor/PresentationEditor.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6352,7 +6352,17 @@ export class PresentationEditor extends EventEmitter {
63526352
}
63536353

63546354
#syncActiveStorySessionDocumentMode(session: StoryPresentationSession | null): void {
6355-
if (!session || session.kind !== 'note') {
6355+
if (!session) {
6356+
return;
6357+
}
6358+
6359+
if (session.kind === 'headerFooter') {
6360+
this.#headerFooterSession?.setDocumentMode(this.#documentMode);
6361+
this.#headerFooterSession?.syncEditorDocumentMode(session.editor);
6362+
return;
6363+
}
6364+
6365+
if (session.kind !== 'note') {
63566366
return;
63576367
}
63586368

packages/super-editor/src/editors/v1/core/presentation-editor/header-footer/HeaderFooterSessionManager.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,11 @@ export class HeaderFooterSessionManager {
772772
}
773773
}
774774

775+
syncEditorDocumentMode(editor: Editor | null): void {
776+
if (!editor) return;
777+
this.#applyChildEditorDocumentMode(editor, this.#documentMode);
778+
}
779+
775780
setTrackedChangesRenderConfig(config: HeaderFooterTrackedChangesRenderConfig): void {
776781
const nextConfig: HeaderFooterTrackedChangesRenderConfig = {
777782
mode: config.mode,

packages/super-editor/src/editors/v1/core/presentation-editor/tests/HeaderFooterSessionManager.test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,42 @@ describe('HeaderFooterSessionManager', () => {
520520
expect(activeEditor.view.dom.getAttribute('documentmode')).toBe('suggesting');
521521
});
522522

523+
it('syncs a provided header/footer editor to the current document mode', () => {
524+
manager = new HeaderFooterSessionManager({
525+
painterHost,
526+
visibleHost,
527+
selectionOverlay,
528+
editor: createMainEditorStub(),
529+
defaultPageSize: { w: 612, h: 792 },
530+
defaultMargins: {
531+
top: 72,
532+
right: 72,
533+
bottom: 72,
534+
left: 72,
535+
header: 36,
536+
footer: 36,
537+
},
538+
});
539+
const storyEditor = createHeaderFooterEditorStub(document.createElement('div')) as unknown as {
540+
commands: {
541+
disableTrackChangesShowOriginal: ReturnType<typeof vi.fn>;
542+
enableTrackChanges: ReturnType<typeof vi.fn>;
543+
};
544+
setOptions: ReturnType<typeof vi.fn>;
545+
setEditable: ReturnType<typeof vi.fn>;
546+
view: { dom: HTMLElement };
547+
};
548+
549+
manager.setDocumentMode('suggesting');
550+
manager.syncEditorDocumentMode(storyEditor as unknown as Editor);
551+
552+
expect(storyEditor.commands.disableTrackChangesShowOriginal).toHaveBeenCalledTimes(1);
553+
expect(storyEditor.commands.enableTrackChanges).toHaveBeenCalledTimes(1);
554+
expect(storyEditor.setOptions).toHaveBeenCalledWith({ documentMode: 'suggesting' });
555+
expect(storyEditor.setEditable).toHaveBeenCalledWith(true);
556+
expect(storyEditor.view.dom.getAttribute('documentmode')).toBe('suggesting');
557+
});
558+
523559
it('exits the active story session when leaving header/footer mode', async () => {
524560
const pageElement = document.createElement('div');
525561
pageElement.dataset.pageIndex = '0';

packages/superdoc/src/SuperDoc.vue

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2008,8 +2008,7 @@ const collectTouchedChangeIds = (transaction) => {
20082008
return collectTouchedTrackedChangeIds(transaction, { trackChangesPluginKey: TrackChangesBasePluginKey });
20092009
};
20102010

2011-
const onEditorTransaction = (payload = {}) => {
2012-
const { editor, transaction } = payload;
2011+
const queueTrackedChangeCommentResyncForTransaction = ({ editor, transaction } = {}) => {
20132012
const ySyncMeta = transaction?.getMeta?.(ySyncPluginKey);
20142013

20152014
// Call sync on editor transaction for undo/redo in both local history
@@ -2036,6 +2035,10 @@ const onEditorTransaction = (payload = {}) => {
20362035
changeIds: collectTouchedChangeIds(transaction),
20372036
});
20382037
}
2038+
};
2039+
2040+
const onEditorTransaction = (payload = {}) => {
2041+
queueTrackedChangeCommentResyncForTransaction(payload);
20392042

20402043
emitEditorTransaction(buildEditorTransactionPayload(payload));
20412044
};

packages/superdoc/src/components/CommentsLayer/CommentDialog.test.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1519,6 +1519,67 @@ describe('CommentDialog.vue', () => {
15191519
expect(headers[2].props('comment').commentId).toBe('child-2');
15201520
});
15211521

1522+
it('re-collapses an expanded thread when active comment leaves the parent thread', async () => {
1523+
const replyOne = reactive({
1524+
uid: 'uid-reply-1',
1525+
commentId: 'reply-1',
1526+
parentCommentId: 'comment-1',
1527+
email: 'reply1@example.com',
1528+
commentText: '<p>First reply</p>',
1529+
createdTime: 1000,
1530+
fileId: 'doc-1',
1531+
fileType: 'DOCX',
1532+
setActive: vi.fn(),
1533+
setText: vi.fn(),
1534+
setIsInternal: vi.fn(),
1535+
resolveComment: vi.fn(),
1536+
trackedChange: false,
1537+
selection: {
1538+
getValues: () => ({ selectionBounds: { top: 120, bottom: 150, left: 20, right: 40 } }),
1539+
selectionBounds: { top: 120, bottom: 150, left: 20, right: 40 },
1540+
},
1541+
});
1542+
const replyTwo = reactive({
1543+
uid: 'uid-reply-2',
1544+
commentId: 'reply-2',
1545+
parentCommentId: 'comment-1',
1546+
email: 'reply2@example.com',
1547+
commentText: '<p>Second reply</p>',
1548+
createdTime: 2000,
1549+
fileId: 'doc-1',
1550+
fileType: 'DOCX',
1551+
setActive: vi.fn(),
1552+
setText: vi.fn(),
1553+
setIsInternal: vi.fn(),
1554+
resolveComment: vi.fn(),
1555+
trackedChange: false,
1556+
selection: {
1557+
getValues: () => ({ selectionBounds: { top: 120, bottom: 150, left: 20, right: 40 } }),
1558+
selectionBounds: { top: 120, bottom: 150, left: 20, right: 40 },
1559+
},
1560+
});
1561+
1562+
const { wrapper } = await mountDialog({
1563+
extraComments: [replyOne, replyTwo],
1564+
props: { autoFocus: false },
1565+
});
1566+
1567+
commentsStoreStub.activeComment.value = 'comment-1';
1568+
await nextTick();
1569+
1570+
const collapsedPill = wrapper.find('.collapsed-replies');
1571+
expect(collapsedPill.exists()).toBe(true);
1572+
await collapsedPill.trigger('click');
1573+
await nextTick();
1574+
expect(wrapper.findAll('.conversation-item')).toHaveLength(3);
1575+
1576+
commentsStoreStub.activeComment.value = replyTwo.commentId;
1577+
await nextTick();
1578+
1579+
expect(wrapper.find('.collapsed-replies').exists()).toBe(true);
1580+
expect(wrapper.findAll('.conversation-item')).toHaveLength(2);
1581+
});
1582+
15221583
it('threads range-based comments under tracked change parent', async () => {
15231584
const rangeBasedRoot = reactive({
15241585
uid: 'uid-range-root',

packages/superdoc/src/components/CommentsLayer/CommentDialog.vue

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -366,14 +366,16 @@ const checkOverflow = () => {
366366
watch(parentBodyRef, () => {
367367
nextTick(checkOverflow);
368368
});
369-
// Reset truncation, thread collapse, and reply state when card becomes inactive
369+
const resetInactiveThreadState = () => {
370+
textExpanded.value = false;
371+
threadExpanded.value = false;
372+
isReplying.value = false;
373+
nextTick(() => emit('resize'));
374+
};
375+
376+
// Reset truncation, thread collapse, and reply state when card becomes inactive.
370377
watch(isDialogActive, (active) => {
371-
if (!active) {
372-
textExpanded.value = false;
373-
threadExpanded.value = false;
374-
isReplying.value = false;
375-
nextTick(() => emit('resize'));
376-
}
378+
if (!active) resetInactiveThreadState();
377379
});
378380

379381
/* ── Step 3: Thread collapse ──
@@ -388,7 +390,6 @@ const childComments = computed(() => comments.value.slice(1));
388390
const shouldCollapseThread = computed(() => {
389391
if (props.comment.trackedChange) return false;
390392
if (threadExpanded.value) return false;
391-
if (isDialogActive.value || activeCommentBelongsToDialog.value) return false;
392393
return childComments.value.length >= 2;
393394
});
394395

@@ -432,6 +433,11 @@ const expandThread = () => {
432433
nextTick(() => emit('resize'));
433434
};
434435

436+
watch(activeComment, (commentId) => {
437+
if (commentId === props.comment.commentId) return;
438+
resetInactiveThreadState();
439+
});
440+
435441
const isInternalDropdownDisabled = computed(() => {
436442
if (props.comment.resolvedTime) return true;
437443
return getConfig.value.readOnly;

packages/superdoc/src/core/SuperDoc.test.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1917,7 +1917,7 @@ describe('SuperDoc core', () => {
19171917
expect(setDocumentMode).toHaveBeenLastCalledWith('suggesting');
19181918
});
19191919

1920-
it('routes document mode changes through all registered runtimes for a document before falling back to legacy editors', async () => {
1920+
it('routes document mode changes through registered runtimes and their presentation editors before legacy fallback', async () => {
19211921
const { superdocStore } = createAppHarness();
19221922
const legacyDocMode = vi.fn();
19231923
const runtimeBackedEditorMode = vi.fn();
@@ -1958,13 +1958,13 @@ describe('SuperDoc core', () => {
19581958
instance.registerEditorRuntime(runtimeA);
19591959
instance.registerEditorRuntime(runtimeB);
19601960

1961-
instance.setDocumentMode('viewing');
1961+
instance.setDocumentMode('suggesting');
19621962

1963-
expect(setModeA).toHaveBeenCalledWith('viewing');
1964-
expect(setModeB).toHaveBeenCalledWith('viewing');
1963+
expect(setModeA).toHaveBeenCalledWith('suggesting');
1964+
expect(setModeB).toHaveBeenCalledWith('suggesting');
19651965
expect(runtimeBackedEditorMode).not.toHaveBeenCalled();
1966-
expect(runtimeBackedPresentationMode).not.toHaveBeenCalled();
1967-
expect(legacyDocMode).toHaveBeenCalledWith('viewing');
1966+
expect(runtimeBackedPresentationMode).toHaveBeenCalledWith('suggesting');
1967+
expect(legacyDocMode).toHaveBeenCalledWith('suggesting');
19681968
});
19691969

19701970
it('syncs a stale presentation host class after runtime document-mode propagation', async () => {

packages/superdoc/src/core/SuperDoc.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2391,23 +2391,25 @@ export class SuperDoc extends EventEmitter<SuperDocEventMap> {
23912391
#applyDocumentMode(doc: RuntimeDocument, mode: DocumentMode) {
23922392
const documentId = typeof doc.id === 'string' && doc.id.length > 0 ? doc.id : null;
23932393
const presentationEditor = typeof doc.getPresentationEditor === 'function' ? doc.getPresentationEditor() : null;
2394+
let appliedToRuntime = false;
23942395
if (documentId) {
23952396
const runtimes = this.#editorRuntimeRegistry.getAllByDocumentId(documentId);
23962397
if (runtimes.length > 0) {
23972398
for (const runtime of runtimes) {
23982399
runtime.setDocumentMode(mode);
23992400
}
2400-
if (presentationEditor && this.#isPresentationModeClassStale(presentationEditor, mode)) {
2401-
presentationEditor.setDocumentMode(mode);
2402-
}
2403-
return;
2401+
appliedToRuntime = true;
24042402
}
24052403
}
24062404

24072405
if (presentationEditor) {
2408-
presentationEditor.setDocumentMode(mode);
2406+
if (!appliedToRuntime || mode !== 'viewing' || this.#isPresentationModeClassStale(presentationEditor, mode)) {
2407+
presentationEditor.setDocumentMode(mode);
2408+
}
24092409
return;
24102410
}
2411+
if (appliedToRuntime) return;
2412+
24112413
const editor = typeof doc.getEditor === 'function' ? doc.getEditor() : null;
24122414
if (editor) {
24132415
editor.setDocumentMode(mode);

0 commit comments

Comments
 (0)