Skip to content

feat(narratives): PR 2 - print optimization, naming standardizations,…#392

Merged
nick-nlb merged 8 commits into
datacommonsorg:mainfrom
ddebasmita-lab:narratives-pr2-fixes
Jul 14, 2026
Merged

feat(narratives): PR 2 - print optimization, naming standardizations,…#392
nick-nlb merged 8 commits into
datacommonsorg:mainfrom
ddebasmita-lab:narratives-pr2-fixes

Conversation

@ddebasmita-lab

@ddebasmita-lab ddebasmita-lab commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

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 localStorage
and 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 by DataCommonsComponent. Each
component 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 placeDcids defaulted to
country/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 print
strategy 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.

@gemini-code-assist gemini-code-assist Bot 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.

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.

Comment thread narratives/src/components/drawer_session.tsx
Comment thread narratives/src/components/sidebar.tsx
Comment thread narratives/src/components/header.tsx
Comment thread narratives/src/hooks/use_sse_chat.ts Outdated
Comment thread narratives/src/hooks/use_sse_chat.ts
Comment thread narratives/src/utils/dc_component.tsx Outdated
Comment thread narratives/tsconfig.json
@ddebasmita-lab
ddebasmita-lab force-pushed the narratives-pr2-fixes branch from 1930f27 to 1333e87 Compare July 5, 2026 14:58

@juliawu juliawu left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

Comment thread narratives/src/api/dc_observations.ts Outdated
Comment thread narratives/src/index.css Outdated
Comment thread narratives/src/index.css Outdated
Comment thread narratives/src/index.css Outdated
Comment thread narratives/src/utils/print_element.ts Outdated
Comment thread narratives/src/components/tile_chart.tsx Outdated
Comment thread narratives/src/hooks/use_branding.ts Outdated
Comment thread narratives/src/hooks/chat_session_context.tsx Outdated
Comment thread narratives/src/components/drawer_session.tsx Outdated
Comment thread narratives/src/components/data_agent.tsx Outdated
- 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 nick-nlb left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thank you Debasmita! A few comments added in below.

Comment thread narratives/src/api/dc_observations.ts
Comment thread narratives/src/app.tsx
Comment thread narratives/src/components/icons.tsx
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} />

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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">

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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?

Comment thread narratives/src/components/metrics_page.tsx Outdated
/**
* `pen_spark` — New chat.
*/
export function NewChatIcon({ size = 22, className = "" }: IconProps) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Will be addressed in the next PR

Comment thread narratives/src/components/block_reasoning.tsx
@@ -226,6 +220,7 @@ function SparkleIcon() {
);
}

/** Chevron icon for the reasoning header; rotates 180° when collapsed. */
function ChevronDown({ rotated }: { rotated?: boolean }) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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"}`} 
/>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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() {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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 nick-nlb left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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).

@nick-nlb
nick-nlb merged commit 6a878fc into datacommonsorg:main Jul 14, 2026
8 checks passed
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