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}"]`);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scope comment lookup to the SuperDoc container

Because document.querySelector searches the whole page, this new API will scroll to the first matching data-thread-id across all SuperDoc instances. If two editors load documents with overlapping thread ids (e.g., separate docs both have comment-1), calling scrollToComment on one instance can scroll the other instance instead. You already expose this.element as the container; scoping the lookup to that element avoids cross-instance collisions.

Useful? React with 👍 / 👎.

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