diff --git a/packages/pluggableWidgets/rich-text-web/CHANGELOG.md b/packages/pluggableWidgets/rich-text-web/CHANGELOG.md index 5cf684c803..863d84278e 100644 --- a/packages/pluggableWidgets/rich-text-web/CHANGELOG.md +++ b/packages/pluggableWidgets/rich-text-web/CHANGELOG.md @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +### Fixed + +- We fixed an issue where the table toolbar did not appear when the cursor was in a table and the custom toolbar was set to "Advanced" with a "Table" item. + ## [5.1.1] - 2026-10-15 ### Fixed diff --git a/packages/pluggableWidgets/rich-text-web/src/__tests__/RichText.runtime.spec.tsx b/packages/pluggableWidgets/rich-text-web/src/__tests__/RichText.runtime.spec.tsx index de431f466a..5ab36df542 100644 --- a/packages/pluggableWidgets/rich-text-web/src/__tests__/RichText.runtime.spec.tsx +++ b/packages/pluggableWidgets/rich-text-web/src/__tests__/RichText.runtime.spec.tsx @@ -385,4 +385,58 @@ describe("Rich Text runtime behaviour", () => { expect(container.querySelector(".tiptap-toolbar") !== null).toBe(hasToolbar); }); }); + + describe("table toolbar", () => { + const tableValue = "

Cell

"; + + function renderWithCaretInTable(props: Partial): RenderResult { + const result = render( + ().withValue(tableValue).build()} + /> + ); + const editor = getEditor(result.container); + let cellTextPos = -1; + editor.state.doc.descendants((node, pos) => { + if (cellTextPos === -1 && node.isText) { + cellTextPos = pos; + } + }); + act(() => { + editor.commands.setTextSelection(cellTextPos); + }); + return result; + } + + function tableConfigurationButton(container: HTMLElement): Element | null { + return container.querySelector('button[title="Table Configuration"]'); + } + + it("shows the table toolbar with the basic custom toolbar when the table group is enabled", () => { + const { container } = renderWithCaretInTable({ toolbarConfig: "basic", tableBetter: true }); + + expect(tableConfigurationButton(container)).toBeInTheDocument(); + }); + + it("shows the table toolbar with the advanced custom toolbar when it contains the table item", () => { + const { container } = renderWithCaretInTable({ + toolbarConfig: "advanced", + advancedConfig: [{ ctItemType: "bold" }, { ctItemType: "separator" }, { ctItemType: "tableBetter" }] + }); + + expect(tableConfigurationButton(container)).toBeInTheDocument(); + }); + + it("hides the table toolbar with the advanced custom toolbar when it has no table item", () => { + const { container } = renderWithCaretInTable({ + toolbarConfig: "advanced", + advancedConfig: [{ ctItemType: "bold" }] + }); + + expect(tableConfigurationButton(container)).not.toBeInTheDocument(); + }); + }); }); diff --git a/packages/pluggableWidgets/rich-text-web/src/components/toolbars/Toolbar.tsx b/packages/pluggableWidgets/rich-text-web/src/components/toolbars/Toolbar.tsx index e0785f741f..0f05c5cc3b 100644 --- a/packages/pluggableWidgets/rich-text-web/src/components/toolbars/Toolbar.tsx +++ b/packages/pluggableWidgets/rich-text-web/src/components/toolbars/Toolbar.tsx @@ -11,12 +11,12 @@ import { HelpButton } from "./components/HelpButton"; import { TableGridToolbarButton } from "./components/TableGrid"; import { ToolbarButton } from "./components/ToolbarButton"; import { - SECONDARY_TOOLBAR_GROUP, ToolbarContext, DropdownCommand, ToolbarGroupConfig, ToolbarButtonConfig, getFilteredToolbarGroups, + getSecondaryToolbarGroups, ToolbarGroupsConfig, TOOLBAR_GROUPS } from "./ToolbarConfig"; @@ -178,13 +178,7 @@ export default function Toolbar(props: ToolbarProps): ReactElement | null { // (preset "full", or custom with every group enabled). const showHelpButton = helpButton !== false && filteredGroups.length === TOOLBAR_GROUPS.length; - const filteredSecondaryGroups = useMemo( - () => - SECONDARY_TOOLBAR_GROUP.filter(group => - filteredGroups.some(filteredGroup => filteredGroup.name === group.parentName) - ), - [filteredGroups] - ); + const filteredSecondaryGroups = useMemo(() => getSecondaryToolbarGroups(filteredGroups), [filteredGroups]); useEffect(() => { if (!editor) { diff --git a/packages/pluggableWidgets/rich-text-web/src/components/toolbars/ToolbarConfig.ts b/packages/pluggableWidgets/rich-text-web/src/components/toolbars/ToolbarConfig.ts index fb0de8aa2e..e0d5eed1df 100644 --- a/packages/pluggableWidgets/rich-text-web/src/components/toolbars/ToolbarConfig.ts +++ b/packages/pluggableWidgets/rich-text-web/src/components/toolbars/ToolbarConfig.ts @@ -807,3 +807,18 @@ export function getFilteredToolbarGroups( return enhancedToolbarGroups(filteredGroups, customFonts); } + +/** + * Get the secondary toolbar groups that belong to the given toolbar groups. + * A secondary group is included when any button of its parent group is present. Buttons are + * matched by name because the advanced custom toolbar regroups them under generated group names. + * @param groups - Filtered toolbar groups + * @returns Secondary toolbar groups to render + */ +export function getSecondaryToolbarGroups(groups: ToolbarGroupConfig[]): ToolbarGroupConfig[] { + const buttonNames = new Set(groups.flatMap(group => group.buttons.map(button => button.name))); + return SECONDARY_TOOLBAR_GROUP.filter(secondaryGroup => { + const parentGroup = TOOLBAR_GROUPS.find(group => group.name === secondaryGroup.parentName); + return parentGroup?.buttons.some(button => buttonNames.has(button.name)) ?? false; + }); +} diff --git a/packages/pluggableWidgets/rich-text-web/src/components/toolbars/helpers/__tests__/ToolbarConfig.spec.ts b/packages/pluggableWidgets/rich-text-web/src/components/toolbars/helpers/__tests__/ToolbarConfig.spec.ts index d919ac7550..db117e08d9 100644 --- a/packages/pluggableWidgets/rich-text-web/src/components/toolbars/helpers/__tests__/ToolbarConfig.spec.ts +++ b/packages/pluggableWidgets/rich-text-web/src/components/toolbars/helpers/__tests__/ToolbarConfig.spec.ts @@ -1,4 +1,4 @@ -import { buildAdvancedToolbar } from "../../ToolbarConfig"; +import { buildAdvancedToolbar, getFilteredToolbarGroups, getSecondaryToolbarGroups } from "../../ToolbarConfig"; describe("buildAdvancedToolbar", () => { it("maps the header item to the text format dropdown", () => { @@ -9,3 +9,24 @@ describe("buildAdvancedToolbar", () => { expect(groups[0].buttons[0].name).toBe("textFormat"); }); }); + +describe("getSecondaryToolbarGroups", () => { + it("includes the table operations for a preset that has the table group", () => { + const groups = getSecondaryToolbarGroups(getFilteredToolbarGroups("full")); + + expect(groups.map(group => group.name)).toEqual(["table-operations"]); + }); + + it("includes the table operations for an advanced toolbar with the table item in any group", () => { + const groups = getSecondaryToolbarGroups( + buildAdvancedToolbar([{ ctItemType: "bold" }, { ctItemType: "separator" }, { ctItemType: "tableBetter" }]) + ); + + expect(groups.map(group => group.name)).toEqual(["table-operations"]); + }); + + it("excludes the table operations when no group has the table button", () => { + expect(getSecondaryToolbarGroups(getFilteredToolbarGroups("basic"))).toEqual([]); + expect(getSecondaryToolbarGroups(buildAdvancedToolbar([{ ctItemType: "bold" }]))).toEqual([]); + }); +});