Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rich text editor | Fix nested list highlighting #1462

Merged
merged 11 commits into from
Sep 1, 2023
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Fix bug with rich text editor nested list button state",
"packageName": "@ni/nimble-components",
"email": "[email protected]",
"dependentChangeType": "patch"
}
20 changes: 16 additions & 4 deletions packages/nimble-components/src/rich-text-editor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@ import {
FoundationElement
} from '@microsoft/fast-foundation';
import { keyEnter, keySpace } from '@microsoft/fast-web-utilities';
import { Editor, AnyExtension, Extension } from '@tiptap/core';
import {
Editor,
findParentNode,
isList,
AnyExtension,
Extension
} from '@tiptap/core';
import {
schema,
defaultMarkdownParser,
Expand All @@ -30,6 +36,7 @@ import Text from '@tiptap/extension-text';
import { template } from './template';
import { styles } from './styles';
import type { ToggleButton } from '../toggle-button';
import { TipTapNodeName } from './types';
import type { ErrorPattern } from '../patterns/error/types';

declare global {
Expand Down Expand Up @@ -466,10 +473,15 @@ export class RichTextEditor extends FoundationElement implements ErrorPattern {
}

private updateEditorButtonsState(): void {
const { extensionManager, state } = this.tiptapEditor;
const { extensions } = extensionManager;
const { selection } = state;
const parentList = findParentNode((node: { type: { name: string } }) => isList(node.type.name, extensions))(selection);

this.boldButton.checked = this.tiptapEditor.isActive('bold');
this.italicsButton.checked = this.tiptapEditor.isActive('italic');
this.bulletListButton.checked = this.tiptapEditor.isActive('bulletList');
this.numberedListButton.checked = this.tiptapEditor.isActive('orderedList');
this.bulletListButton.checked = parentList?.node.type.name === TipTapNodeName.bulletList;
this.numberedListButton.checked = parentList?.node.type.name === TipTapNodeName.numberedList;
}

private keyActivatesButton(event: KeyboardEvent): boolean {
Expand Down Expand Up @@ -541,7 +553,7 @@ export class RichTextEditor extends FoundationElement implements ErrorPattern {
extensionName: string
): AnyExtension | undefined {
return this.tiptapEditor.extensionManager.extensions.find(
extension => extension.name === extensionName
(extension: { name: string }) => extension.name === extensionName
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ describe('RichTextEditor', () => {
]);
expect(
pageObject.getButtonCheckedState(ToolbarButton.numberedList)
).toBeTrue();
).toBeFalse();
expect(
pageObject.getButtonCheckedState(ToolbarButton.bulletList)
).toBeTrue();
Expand Down Expand Up @@ -534,7 +534,7 @@ describe('RichTextEditor', () => {
).toBeTrue();
expect(
pageObject.getButtonCheckedState(ToolbarButton.bulletList)
).toBeTrue();
).toBeFalse();
});

it('should have "ul" tag names for bullet lists when clicking "tab" to make it nested and "shift+Tab" to make it usual list', async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { ToolbarButton } from '../testing/types';
import type { TipTapNodeName } from '../types';

describe('Editor Toolbar button page object types', () => {
it('ToolbarButton fails compile if assigning arbitrary string values', () => {
Expand All @@ -8,3 +9,11 @@ describe('Editor Toolbar button page object types', () => {
expect(value).toEqual('hello');
});
});

describe('Tiptap node types', () => {
it('TipTapNodeName fails compile if assigning arbitrary string values', () => {
// @ts-expect-error This expect will fail if the enum-like type is missing "as const"
const value: TipTapNodeName = 'hello';
expect(value).toEqual('hello');
});
});
11 changes: 11 additions & 0 deletions packages/nimble-components/src/rich-text-editor/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* TipTap node types.
* @public
*/
export const TipTapNodeName = {
bulletList: 'bulletList',
numberedList: 'orderedList'
} as const;

export type TipTapNodeName =
(typeof TipTapNodeName)[keyof typeof TipTapNodeName];