diff --git a/packages/superdoc/src/core/SuperDoc.js b/packages/superdoc/src/core/SuperDoc.js index 8da548e22..26a7c21ee 100644 --- a/packages/superdoc/src/core/SuperDoc.js +++ b/packages/superdoc/src/core/SuperDoc.js @@ -667,6 +667,27 @@ export class SuperDoc extends EventEmitter { } } + /** + * Scroll the document to a given comment or tracked change by thread id. + * + * @param {string} commentId The comment or tracked change id + * @param {{ behavior?: ScrollBehavior, block?: ScrollLogicalPosition }} [options] + * @returns {boolean} Whether a matching element was found + */ + scrollToComment(commentId, options = {}) { + const commentsConfig = this.config?.modules?.comments; + if (!commentsConfig || commentsConfig === false) return false; + if (!commentId || typeof commentId !== 'string') return false; + + const element = document.querySelector(`[data-thread-id="${commentId}"]`); + if (!element) return false; + + const { behavior = 'smooth', block = 'start' } = options; + element.scrollIntoView({ behavior, block }); + this.commentsStore?.setActiveComment?.(this, commentId); + return true; + } + /** * Toggle the custom context menu globally. * Updates both flow editors and PresentationEditor instances so downstream listeners can short-circuit early. diff --git a/packages/superdoc/src/core/SuperDoc.test.js b/packages/superdoc/src/core/SuperDoc.test.js index f1d9829a3..15745739a 100644 --- a/packages/superdoc/src/core/SuperDoc.test.js +++ b/packages/superdoc/src/core/SuperDoc.test.js @@ -252,6 +252,30 @@ describe('SuperDoc core', () => { expect(instance.user).toEqual(expect.objectContaining({ name: 'Default SuperDoc user', email: null })); }); + it('scrolls to a comment and sets it active', async () => { + const { commentsStore } = createAppHarness(); + const instance = new SuperDoc({ + selector: '#host', + document: 'https://example.com/doc.docx', + documents: [], + modules: { comments: {}, toolbar: {} }, + colors: ['red'], + user: { name: 'Jane', email: 'jane@example.com' }, + onException: vi.fn(), + }); + await flushMicrotasks(); + + const target = document.createElement('div'); + target.setAttribute('data-thread-id', 'comment-1'); + target.scrollIntoView = vi.fn(); + document.body.appendChild(target); + + const result = instance.scrollToComment('comment-1'); + expect(result).toBe(true); + expect(target.scrollIntoView).toHaveBeenCalledWith({ behavior: 'smooth', block: 'start' }); + expect(commentsStore.setActiveComment).toHaveBeenCalledWith(instance, 'comment-1'); + }); + it('warns when both document object and documents list provided', async () => { const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); createAppHarness();