Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions packages/superdoc/src/core/SuperDoc.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
24 changes: 24 additions & 0 deletions packages/superdoc/src/core/SuperDoc.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down