Skip to content

fix(appkit-ui): describe chat-mode Genie charts by their query - #736

Open
fjakobs wants to merge 1 commit into
mainfrom
fjakobs/genie-chart-alt-text
Open

fjakobs wants to merge 1 commit into
mainfrom
fjakobs/genie-chart-alt-text

Conversation

@fjakobs

@fjakobs fjakobs commented Nov 13, 2026 •

Copy link
Copy Markdown
Collaborator

Follow-up to #732.

Problem

In chat mode, the <img> for a Genie chart always gets alt="Visualization". Screen readers announce every chart the same way, with no hint of what it shows.

Root cause

GenieChatMessage takes the image alt from the visualization attachment's title. Agent-mode visualizations carry a title, but chat-mode ones don't: a chat-mode viz attachment only has query_attachment_id. The query attachment it points to has a description (for example, "You want to see the number of franchises in each country, displayed as a bar chart."), but in chat mode it usually has no title either.

Fix

When GenieChatMessage builds its visualization list, it resolves each chart's title from, in order:

  1. the viz title,
  2. the title of the query attachment the chart plots (viz.queryAttachmentId),
  3. that query's description,
  4. the existing "Visualization" fallback.

The resolved title feeds both the Chart tab image (chartImageAlt) and the standalone chart images. Empty strings fall through, so a chart never ends up with alt="" (decorative). There are no new props and no server or API changes. Agent-mode charts keep their own title.

Tests

  • New RTL tests in genie-chat-message.test.tsx:
    • A chat-mode chart with no title, whose query has only a description, gets an <img> named by that description.
    • With a query result, the query's title wins over its description for chartImageAlt.
    • Both fail without the fix.
  • The existing agent-mode test still shows that the viz title wins over the query title.
  • pnpm build, pnpm docs:build, pnpm check, pnpm -r typecheck and pnpm test pass.

End-to-end check

I ran the dev playground against a Databricks workspace with a Genie space and read each chart's alt from the DOM:

Mode Chart alt
Chat, conversation reloaded from history "You want to see the number of franchises broken down by their size, presented as a pie chart."
Chat, new chart question "You want to see the number of franchises in each country, displayed as a bar chart."
Agent "Number of franchises per country" (the viz title, unchanged)

Screenshots

Chat mode, conversation reloaded from history. The chart image's alt is the query description:

Chat mode chart from history

Chat mode, new chart question:

Chat mode chart for a new question

Agent mode, unchanged. The alt is the viz title:

Agent mode chart

This pull request and its description were written by Isaac.

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 <[email protected]>
Signed-off-by: Fabian Jakobs <[email protected]>
@fjakobs
fjakobs requested a review from a team as a code owner November 13, 2026 11:44
@fjakobs
fjakobs requested a review from atilafassina November 13, 2026 11:44
@github-actions

Copy link
Copy Markdown
Contributor

📦 Bundle size report

Compared against bundle-size-baseline.json (main).

@databricks/appkit

npm tarball (packed): 1.3 MB (+60 B) — gzipped download (dist + bin; excludes release-only docs/NOTICE).

dist raw gzip
JS (runtime) 1.3 MB (+182 B) 482 KB (+17 B)
Type declarations 471 KB 170 KB (+2 B)
Source maps 2.6 MB (+47 B) 896 KB (+29 B)
Other 11 KB 3.7 KB
Total 4.4 MB (+229 B) 1.5 MB (+48 B)
Per-entry composition (own code — deps external (as shipped))
Entry Initial (gz) Lazy (gz) Total (gz) node_modules (min) Own code (min)
. 110 KB 2.5 KB (-1 B) 112 KB (-1 B) external 355 KB
./beta 113 KB 483 B 114 KB external 339 KB
./testing 46 KB (-1 B) 33 KB (+3 B) 79 KB (+2 B) external 229 KB
./tsdown 520 B 0 B 520 B external 813 B
./type-generator 26 KB 0 B 26 KB external 74 KB

Chunks:

Entry Chunk Load Size (gz)
. index.js initial 105 KB
. utils.js initial 4.6 KB
. remote-tunnel-manager.js lazy 2.5 KB
./beta beta.js initial 93 KB
./beta modular.js initial 6.0 KB
./beta stream-manager.js initial 5.9 KB
./beta service-context.js initial 4.3 KB
./beta databricks.js initial 3.3 KB
./beta client.js initial 648 B
./beta index.js initial 20 B
./beta supervisor-api.js lazy 192 B
./beta databricks.js lazy 177 B
./beta index.js lazy 114 B
./testing manifest.js initial 33 KB
./testing index.js initial 10 KB
./testing wide-event-emitter.js initial 2.9 KB
./testing index.js lazy 29 KB
./testing remote-tunnel-manager.js lazy 2.5 KB
./testing utils.js lazy 1.8 KB
./tsdown index.js initial 520 B
./type-generator index.js initial 26 KB

@databricks/appkit-ui

npm tarball (packed): 409 KB (+172 B) — gzipped download (dist + bin; excludes release-only docs/NOTICE).

dist raw gzip
JS (runtime) 440 KB (+165 B) 151 KB (+52 B)
Type declarations 272 KB 100 KB (+1 B)
Source maps 870 KB (+396 B) 293 KB (+119 B)
CSS 16 KB 3.2 KB
Total 1.6 MB (+561 B) 547 KB (+172 B)
Per-entry composition (consumer bundle — deps bundled, peerDeps external)
Entry Initial (gz) Lazy (gz) Total (gz) node_modules (min) Own code (min)
./js 5.3 KB 49 KB 55 KB 208 KB 14 KB
./js/beta 2.1 KB 0 B 2.1 KB 0 B 5.0 KB
./react 441 KB (+37 B) 49 KB 489 KB (+37 B) 1.4 MB 179 KB (+105 B)
./react/beta 14 KB 0 B 14 KB 25 KB 16 KB

Chunks:

Entry Chunk Load Size (gz)
./js index.js initial 5.2 KB
./js chunk initial 120 B
./js apache-arrow lazy 49 KB
./js/beta beta.js initial 2.1 KB
./react index.js initial 439 KB
./react tslib initial 2.1 KB
./react apache-arrow lazy 49 KB
./react/beta beta.js initial 14 KB

@github-actions

Copy link
Copy Markdown
Contributor

🤖 AppKit PR bot

🔬 Run evals

Start an eval for this PR from the evals-monitor app: Go to Evals Monitor →

📦 Try this PR's app template

Scaffolds a new app from this PR's SDK build. Run it in any folder (requires the GitHub CLI — gh auth login — and the Databricks CLI):

gh run download 43419441115 -R databricks/appkit -n appkit-template-0.95.0-pr.f54a1a9-fjakobs-genie-chart-alt-text-736 -D appkit-pr-736 \
  && unzip -o "appkit-pr-736/appkit-template-0.95.0-pr.f54a1a9-fjakobs-genie-chart-alt-text-736.zip" -d "appkit-pr-736" \
  && databricks apps init --template "appkit-pr-736"

The template pins @databricks/appkit and @databricks/appkit-ui to tarballs built from this branch, so the scaffolded app runs against this PR's code.

@fjakobs
fjakobs requested a review from MarioCadenas November 13, 2026 12:08

@calvarjorge calvarjorge left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: approve. The fix is small and correct, agent mode is unchanged, and the two new tests fail without it.

One nit, not blocking:

packages/appkit-ui/src/react/genie/genie-chat-message.tsx:77: an empty description still gives alt="". a || b || c returns the last operand when all of them are falsy. So if the query's description is "", title is "", and the ?? "Visualization" fallbacks (line 53, genie-query-visualization.tsx:92) keep it. The chart is then marked decorative, which the PR description says can't happen. The SDK types these fields as optional, so it's unlikely in practice. Suggested fix:

const title = att.viz.title || query?.title || query?.description || undefined;

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants