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 = "
";
+
+ 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([]);
+ });
+});