diff --git a/change/@ni-nimble-components-46b1fdfb-63b3-4a04-ac71-1124855a4e45.json b/change/@ni-nimble-components-46b1fdfb-63b3-4a04-ac71-1124855a4e45.json new file mode 100644 index 0000000000..45837b8b83 --- /dev/null +++ b/change/@ni-nimble-components-46b1fdfb-63b3-4a04-ac71-1124855a4e45.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Fix for Firefox caret disappearing issue", + "packageName": "@ni/nimble-components", + "email": "123377167+aagash-ni@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/packages/nimble-components/src/rich-text-editor/index.ts b/packages/nimble-components/src/rich-text-editor/index.ts index d0ae18e4f5..cd0dbacbbc 100644 --- a/packages/nimble-components/src/rich-text-editor/index.ts +++ b/packages/nimble-components/src/rich-text-editor/index.ts @@ -220,6 +220,7 @@ export class RichTextEditor extends FoundationElement implements ErrorPattern { */ public boldButtonClick(): void { this.tiptapEditor.chain().focus().toggleBold().run(); + this.forceFocusEditor(); } /** @@ -229,6 +230,7 @@ export class RichTextEditor extends FoundationElement implements ErrorPattern { public boldButtonKeyDown(event: KeyboardEvent): boolean { if (this.keyActivatesButton(event)) { this.tiptapEditor.chain().focus().toggleBold().run(); + this.forceFocusEditor(); return false; } return true; @@ -240,6 +242,7 @@ export class RichTextEditor extends FoundationElement implements ErrorPattern { */ public italicsButtonClick(): void { this.tiptapEditor.chain().focus().toggleItalic().run(); + this.forceFocusEditor(); } /** @@ -249,6 +252,7 @@ export class RichTextEditor extends FoundationElement implements ErrorPattern { public italicsButtonKeyDown(event: KeyboardEvent): boolean { if (this.keyActivatesButton(event)) { this.tiptapEditor.chain().focus().toggleItalic().run(); + this.forceFocusEditor(); return false; } return true; @@ -260,6 +264,7 @@ export class RichTextEditor extends FoundationElement implements ErrorPattern { */ public bulletListButtonClick(): void { this.tiptapEditor.chain().focus().toggleBulletList().run(); + this.forceFocusEditor(); } /** @@ -269,6 +274,7 @@ export class RichTextEditor extends FoundationElement implements ErrorPattern { public bulletListButtonKeyDown(event: KeyboardEvent): boolean { if (this.keyActivatesButton(event)) { this.tiptapEditor.chain().focus().toggleBulletList().run(); + this.forceFocusEditor(); return false; } return true; @@ -280,6 +286,7 @@ export class RichTextEditor extends FoundationElement implements ErrorPattern { */ public numberedListButtonClick(): void { this.tiptapEditor.chain().focus().toggleOrderedList().run(); + this.forceFocusEditor(); } /** @@ -289,6 +296,7 @@ export class RichTextEditor extends FoundationElement implements ErrorPattern { public numberedListButtonKeyDown(event: KeyboardEvent): boolean { if (this.keyActivatesButton(event)) { this.tiptapEditor.chain().focus().toggleOrderedList().run(); + this.forceFocusEditor(); return false; } return true; @@ -546,6 +554,15 @@ export class RichTextEditor extends FoundationElement implements ErrorPattern { } }); } + + // In Firefox browser, once the editor gets focused, the blinking caret will be visible until we click format buttons (Bold, Italic ...) in the Firefox browser (changing focus). + // But once any of the toolbar button is clicked, editor internally has its focus but the blinking caret disappears. + // As a workaround, manually triggering blur and setting focus on editor makes the blinking caret to re-appear. + // Mozilla issue https://bugzilla.mozilla.org/show_bug.cgi?id=1496769 tracks removal of this workaround. + private forceFocusEditor(): void { + this.tiptapEditor.commands.blur(); + this.tiptapEditor.commands.focus(); + } } // eslint-disable-next-line @typescript-eslint/no-empty-interface diff --git a/packages/nimble-components/src/rich-text-editor/testing/rich-text-editor.pageobject.ts b/packages/nimble-components/src/rich-text-editor/testing/rich-text-editor.pageobject.ts index 478a087dde..3784f5e858 100644 --- a/packages/nimble-components/src/rich-text-editor/testing/rich-text-editor.pageobject.ts +++ b/packages/nimble-components/src/rich-text-editor/testing/rich-text-editor.pageobject.ts @@ -136,6 +136,14 @@ export class RichTextEditorPageObject { .map(el => el.textContent || ''); } + public isRichTextEditorActiveElement(): boolean { + return ( + document.activeElement === this.richTextEditorElement + && document.activeElement?.shadowRoot?.activeElement + === this.getTiptapEditor() + ); + } + public getEditorTabIndex(): string { return this.getTiptapEditor()?.getAttribute('tabindex') ?? ''; } diff --git a/packages/nimble-components/src/rich-text-editor/tests/rich-text-editor.spec.ts b/packages/nimble-components/src/rich-text-editor/tests/rich-text-editor.spec.ts index 6ea1bc3570..c5b8647286 100644 --- a/packages/nimble-components/src/rich-text-editor/tests/rich-text-editor.spec.ts +++ b/packages/nimble-components/src/rich-text-editor/tests/rich-text-editor.spec.ts @@ -132,6 +132,12 @@ describe('RichTextEditor', () => { expect(okButtonSpy).toHaveBeenCalledTimes(1); }); + it('Should return editor as active element after clicking formatting button', async () => { + await pageObject.setEditorTextContent('Sample Text'); + await pageObject.clickFooterButton(ToolbarButton.bulletList); + expect(pageObject.isRichTextEditorActiveElement()).toBeTrue(); + }); + const formattingButtons: { name: string, toolbarButtonIndex: ToolbarButton,