feat(narratives): PR 2 - print optimization, naming standardizations,…#392
Conversation
There was a problem hiding this comment.
Code Review
This pull request adds multi-session chat management with localStorage persistence, integrates official Data Commons web components via a new <DC> wrapper, and updates the Key Metrics dashboard to be config-driven. Key feedback includes resolving case-sensitive import paths for Icons.tsx to prevent compilation failures on Linux, guarding the SSE send function against concurrent streaming invocations, and refactoring the <DC> component to use document.createElement instead of innerHTML to preserve camelCase attributes. Finally, TypeScript's strict mode should be re-enabled in tsconfig.json to maintain type safety.
… and code review fixes
1930f27 to
1333e87
Compare
juliawu
left a comment
There was a problem hiding this comment.
Thanks for the new additions! I see a lot of refactoring, especially in making changes compatible with the updates made in the last PR -- great job! I left some comments that should be quick fixes.
Could you also fill out the PR description? It'll be really helpful down the line to have that context.
- Fix case-sensitive ./Icons imports (drawer, sidebar, header) so they compile on Linux CI. - Drop redundant empty-message check in useSseChat send (validated upstream). - Rename DC helper to DataCommonsComponent (+ TagName/Attributes types), file to datacommons_component.tsx, and getters to getDcComponentTag / getDcComponentAttributes. - Harden printElement: guard against detached targets and concurrent print runs, run cleanup in finally, and dispatch a resize on cleanup so charts redraw to the on-screen width. - Naming/readability: renderWithCitations, source/index, message, session (drop one-letter callback params); brandConfigUrl aliased at the boundary. - Add MS_PER_MINUTE/HOUR/DAY constants in formatRelativeTime. - Comment fixes: SSR wording, use_branding.ts / print_element.ts references.
Follow-up to the PR 2 review fixes: rename turn/session/question params and setState toggle args (t/s/q/v) that the earlier pass missed in chat_session_context, data_agent, and reasoning_block.
Rename the suggestion-chip map index and click handler args (i/t) to index/suggestion for consistency with the naming standard.
nick-nlb
left a comment
There was a problem hiding this comment.
Thank you Debasmita! A few comments added in below.
| className="w-12 h-12 flex items-center justify-center rounded-full hover:bg-button-hover transition-colors text-on-surface-variant" | ||
| > | ||
| <SquarePen size={20} strokeWidth={1.5} /> | ||
| <NewChatIcon size={22} /> |
There was a problem hiding this comment.
Here we are explicitly setting the size of the icon directly in the consuming component. This is a magic number, and will have the effect of making standardizing (and adjusting) the icons difficult.
There are some different options for handling this, but I would lean towards having size be of type "sm" | "md" | "lg", and having the pixel size of these defined on the theme level (with "md" being 22, "lg" 24 for example.
There was a problem hiding this comment.
will cover this and the color tokens in the next PR
|
|
||
| {/* Follow-up input — flush against the fade (no top margin) so the | ||
| gradient reads as a continuous merge rather than a separator. */} | ||
| <div className="w-full flex flex-col items-center gap-3 bg-white pb-4 sm:pb-6"> |
There was a problem hiding this comment.
I've noticed that there are a substantial number of hard-coded color references in the app (bg-white, border-gray-200). These should all be derived from the theme.
This might be a sizable job in itself, and can be set aside for a subsequent PR, but can we make sure we have the task lined up to make sure that all items that might reasonably vary from theme-to-theme be derived from theme tokens rather than being hard-coded?
| /** | ||
| * `pen_spark` — New chat. | ||
| */ | ||
| export function NewChatIcon({ size = 22, className = "" }: IconProps) { |
There was a problem hiding this comment.
Following up on that previous comment about icon sizing:
We could requisition this size prop to standardize the sizing across the application (i.e. have it take "sm", "md", "lg").
We could make this easier here by having a non-exported base Icon component that takes the size, looks up the pixel value that represents in the theme, and uses that pixel value.
The other components would then wrap this new base Icon component.
Another thing we could do, to make these components as flexible as possible, is to have them take SVG props and spread them in (something we do in base DC - although we could do it a bit differently here).
We could use a hook to handle the conversion of common props (like size) into something the icon would understand:
// Ultimately we would want these to come right out of the theme (in the branding context?)
const ICON_SIZES = {
sm: 20,
md: 22,
lg: 24,
} as const;
export type IconSizeToken = keyof typeof ICON_SIZES;
export interface IconProps extends SVGProps<SVGSVGElement> {
size?: IconSizeToken;
}
export function getSvgProps(size: IconSizeToken = "md", props: SVGProps<SVGSVGElement> = {}) {
const dimension = ICON_SIZES[size];
return {
width: dimension,
height: dimension,
fill: "currentColor",
"aria-hidden": true,
...props,
className: `shrink-0 select-none ${props.className ?? ""}`.trim(),
};
}
And then one of the actual icons:
export function SomeIcon({ size = "lg", ...props }: IconProps) {
return (
<svg
viewBox="0 -960 960 960"
{...getSvgProps(size, props)}
>
<path d="..."/>
</svg>
);
}
I played around and tested with the above and it seems to work, but haven't fully vetted it. Feel use any of this.
There was a problem hiding this comment.
Will be addressed in the next PR
| @@ -226,6 +220,7 @@ function SparkleIcon() { | |||
| ); | |||
| } | |||
|
|
|||
| /** Chevron icon for the reasoning header; rotates 180° when collapsed. */ | |||
| function ChevronDown({ rotated }: { rotated?: boolean }) { | |||
There was a problem hiding this comment.
These should be rolled out into the Icons library.
But importantly as we do that, the icons should not be responsible for their rotation - i.e., ChevronDown is not a special case of "an icon that can be rotated". Any icon should be rotatable (and that would be controlled from outside of the icon).
Once we have moved our icons to accept SVG props (see the earlier comments), we will be in a position to do that, where we could rotate the icon with something like:
<ChevronDownIcon
className={`transition-transform duration-150 ${open ? "rotate-0" : "-rotate-90"}`}
/>
There was a problem hiding this comment.
will be addressed in next PR
| * slot (layout_MOYZ4B). Verified by downloading node 3427:16783 from | ||
| * Figma at 4× and inspecting the rendered glyph. | ||
| */ | ||
| function ExportIcon() { |
There was a problem hiding this comment.
This should also be rolled into the icon library. While doing that - since this is Material icon from the same set as the other icons, it could be updated to have the same viewbox (and of course, with the paths updated to fit).
There was a problem hiding this comment.
will be covered in next PR
Per @nick-nlb's review on PR 2: - Rename to the app's lowercase category-first convention: App.tsx -> app.tsx, Icons.tsx -> icons.tsx, reasoning_block.tsx -> block_reasoning.tsx (with imports updated). icons.tsx also re-syncs casing with custom-data-commons. - Drop change-narrative comments that describe what the code used to do (phase-loader removal note, the skeleton 'previously only checked mcp' note). - metrics_page: the fileoverview described dangerouslySetInnerHTML, which is no longer used; point to the <DataCommonsComponent> wrapper instead.
nick-nlb
left a comment
There was a problem hiding this comment.
Thank you for all the updates Debasmita (and noting the deferrals to a subsequent PR for some of the changes - I think that's a good idea in the interest of keeping the PRs small where possible).
Overview
Second PR for the narratives Data Agent UI. PR 1 (#363) landed the shell —
routing, branding, and the streaming scaffold. This PR builds the actual chat
experience on top of it: multi-session conversations, real chart rendering via
Data Commons web components, a config-driven Key Metrics dashboard, and
single-answer print/PDF export. It also folds in the code-review feedback from
this PR's own earlier rounds (naming standardizations and print hardening).
Scope is the
narratives/frontend only (TypeScript + React 19 + Vite +Tailwind v4). ~42 files, no backend changes.
Background / motivation
PR 1 rendered a single ephemeral conversation with placeholder charts. To make
the Data Agent usable we needed: conversations that survive navigation and
refresh; charts that show correct per-place data with real source attribution;
a metrics landing page an instance owner can configure without code; and a way
to export an answer for sharing. Each of those is one section below.
Changes made
Multi-session chat (persisted). Conversations are stored in
localStorageand exposed through a context provider, so turns survive SPA navigation and
browser refresh. A session drawer lists past chats with new/switch/delete;
empty "New chat" drafts are collapsed so the list stays tidy. Desktop shows a
left-rail list; mobile uses a two-level slide-in panel.
Files:
hooks/chat_session_context.tsx,components/drawer_session.tsx,components/sidebar.tsx.Data Commons web-component charts. Replaced the custom Recharts layer with
official
<datacommons-*>components, wrapped byDataCommonsComponent. Eachcomponent owns its own data fetch, source caption, "About this data" / "API
code" footer, and metadata modal, wired to the actual DCIDs the agent returned.
This fixes the prior bug where charts with missing
placeDcidsdefaulted tocountry/USA.
Files:
utils/datacommons_component.tsx,components/tile_chart.tsx.Config-driven Key Metrics dashboard. The metrics page renders tiles from a
metrics config rather than hardcoded charts, so an instance can re-skin it
without touching components.
Files:
components/metrics_page.tsx,types/metrics.ts.Print / PDF export. Export one Q&A panel via a scoped
@media printstrategy that hides everything outside the selected subtree. Hardened against
detached targets and concurrent print runs, with cleanup in
finally.Files:
utils/print_element.ts,components/button_export_pdf.tsx,index.css.Streaming, follow-ups, citations. SSE streaming with a stop control and a
single-stream guard (no concurrent connections), agent follow-up questions, and
inline
[N]citation chips.Files:
hooks/use_sse_chat.ts,components/data_agent.tsx,components/questions_follow_up.tsx,components/chip_citation.tsx.