Skip to content

Commit de06b91

Browse files
authored
fix(frontend): duplicate tooltip + raw i18n key on collapsed sidebar nav (1.11.0) (#13652)
fix(frontend): remove duplicate tooltip and raw i18n key on collapsed sidebar nav The floating canvas control nav (shown when the sidebar is collapsed) rendered two tooltips per icon: a native `title` attribute on ControlButton plus the styled ShadTooltip. On top of that, MemoizedComponents passed the raw i18n key (`item.tooltip`) as the tooltip text instead of running it through `t()`, so the ShadTooltip exposed strings like `sidebar.nav.bundles` while the native title showed the bare id (`bundles`). - CanvasControlButton: drop the native `title` (the duplicate tooltip); keep accessibility via `aria-label`. The ShadTooltip is the single visual tooltip. This also removes the redundant native tooltip from the zoom/fit/lock control buttons. - MemoizedComponents: translate the nav tooltip with `t(item.tooltip)`. Adds an isolated CanvasControlButton regression test asserting no native `title` and that the label is surfaced via aria-label + ShadTooltip.
1 parent 0231969 commit de06b91

3 files changed

Lines changed: 60 additions & 2 deletions

File tree

src/frontend/src/components/core/canvasControlsComponent/CanvasControlButton.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export const CanvasControlButton = ({
2828
className="group !h-8 !w-8 rounded !p-0"
2929
onClick={onClick}
3030
disabled={disabled}
31-
title={testId?.replace(/_/g, " ")}
31+
aria-label={tooltipText}
3232
>
3333
<ShadTooltip content={tooltipText} side="right">
3434
<div
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { render, screen } from "@testing-library/react";
2+
import CanvasControlButton from "../CanvasControlButton";
3+
4+
// ControlButton renders the real DOM <button> and spreads its props, so we can
5+
// assert exactly which attributes (title / aria-label) reach the element.
6+
jest.mock("@xyflow/react", () => ({
7+
ControlButton: ({ children, ...props }: any) => (
8+
<button type="button" {...props}>
9+
{children}
10+
</button>
11+
),
12+
}));
13+
14+
// ShadTooltip is the single, styled tooltip — expose its content so we can
15+
// confirm the button's label is surfaced through it (and only it).
16+
jest.mock("@/components/common/shadTooltipComponent", () => ({
17+
__esModule: true,
18+
default: ({ content, children }: any) => (
19+
<div data-testid="shad-tooltip" data-content={content}>
20+
{children}
21+
</div>
22+
),
23+
}));
24+
25+
jest.mock("@/components/common/genericIconComponent", () => ({
26+
__esModule: true,
27+
default: ({ name }: any) => <div data-testid={`icon-${name}`} />,
28+
}));
29+
30+
// Regression coverage for the duplicate sidebar tooltip bug: the control nav
31+
// buttons rendered BOTH a native `title` attribute and a ShadTooltip, so
32+
// hovering surfaced two tooltips at once (one of them exposing a raw i18n key).
33+
describe("CanvasControlButton — single tooltip (no native title)", () => {
34+
const setup = () =>
35+
render(
36+
<CanvasControlButton
37+
iconName="blocks"
38+
tooltipText="Bundles"
39+
onClick={jest.fn()}
40+
testId="bundles"
41+
/>,
42+
);
43+
44+
it("does not render a native title attribute (would be a second tooltip)", () => {
45+
setup();
46+
const button = screen.getByRole("button");
47+
expect(button).not.toHaveAttribute("title");
48+
});
49+
50+
it("exposes the label via aria-label and the ShadTooltip, not a raw key", () => {
51+
setup();
52+
expect(screen.getByRole("button")).toHaveAttribute("aria-label", "Bundles");
53+
expect(screen.getByTestId("shad-tooltip")).toHaveAttribute(
54+
"data-content",
55+
"Bundles",
56+
);
57+
});
58+
});

src/frontend/src/pages/FlowPage/components/PageComponent/MemoizedComponents.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export const MemoizedSidebarTrigger = memo(() => {
5454
iconName={item.icon}
5555
iconClasses={item.id === "mcp" ? "h-8 w-8" : ""}
5656
key={item.id}
57-
tooltipText={item.tooltip}
57+
tooltipText={t(item.tooltip)}
5858
onClick={() => {
5959
setActiveSection(item.id);
6060
if (!open) {

0 commit comments

Comments
 (0)