From 2e4302c8063da8b54db1ba3a40f153df17dcbf03 Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Fri, 13 Nov 2026 11:37:56 +0000 Subject: [PATCH] fix(appkit-ui): describe chat-mode Genie charts by their query Chat-mode visualization attachments carry no title, so the chart image fell back to alt="Visualization". Use the title, else the description, of the query attachment the chart plots before the generic fallback. Agent-mode charts keep their own title. Co-authored-by: Isaac Signed-off-by: Fabian Jakobs --- .../__tests__/genie-chat-message.test.tsx | 60 ++++++++++++++++++- .../src/react/genie/genie-chat-message.tsx | 12 +++- 2 files changed, 67 insertions(+), 5 deletions(-) diff --git a/packages/appkit-ui/src/react/genie/__tests__/genie-chat-message.test.tsx b/packages/appkit-ui/src/react/genie/__tests__/genie-chat-message.test.tsx index 862d24d57..c247b24df 100644 --- a/packages/appkit-ui/src/react/genie/__tests__/genie-chat-message.test.tsx +++ b/packages/appkit-ui/src/react/genie/__tests__/genie-chat-message.test.tsx @@ -2,8 +2,15 @@ import { cleanup, render, screen } from "@testing-library/react"; import { afterEach, describe, expect, test, vi } from "vitest"; vi.mock("../genie-query-visualization", () => ({ - GenieQueryVisualization: (props: { chartImageUrl?: string }) => ( -
+ GenieQueryVisualization: (props: { + chartImageUrl?: string; + chartImageAlt?: string; + }) => ( +
), })); @@ -81,6 +88,55 @@ describe("GenieChatMessage", () => { expect(screen.queryByTestId("query-result")).toBeNull(); }); + test("describes a chat-mode chart by its query's description", () => { + render( + , + ); + + expect( + screen.getByRole("img", { + name: "You want to see the number of franchises by size.", + }), + ).toBeTruthy(); + }); + + test("names a chat-mode chart in the result by its query's title first", () => { + render( + , + ); + + expect( + screen.getByTestId("query-result").getAttribute("data-chart-alt"), + ).toBe("Franchises"); + }); + test("leaves the query result alone for a query without a visualization", () => { render( [att.attachmentId, att.query]), + ); const visualizations: Visualization[] = message.attachments.flatMap((att) => { const url = att.attachmentId && message.visualizationUrls?.[att.attachmentId]; - return att.viz && url ? [{ url, ...att.viz }] : []; + if (!att.viz || !url) return []; + const query = queryById.get(att.viz.queryAttachmentId); + // Chat-mode charts have no title; describe them by the query they plot. + const title = att.viz.title || query?.title || query?.description; + return [{ url, ...att.viz, title }]; }); - const queryIds = new Set(queryAttachments.map((att) => att.attachmentId)); // Charts whose query isn't among the attachments still get shown, after the queries. const unmatchedVisualizations = visualizations.filter( - (viz) => !queryIds.has(viz.queryAttachmentId), + (viz) => !queryById.has(viz.queryAttachmentId), ); const html = useMemo( () =>