feat: SW-2100 Add molecule structure primitive (SMILES → 2D)#182
Open
owilliams-tetrascience wants to merge 8 commits into
Open
feat: SW-2100 Add molecule structure primitive (SMILES → 2D)#182owilliams-tetrascience wants to merge 8 commits into
owilliams-tetrascience wants to merge 8 commits into
Conversation
owilliams-tetrascience
had a problem deploying
to
artifactory-prod
July 16, 2026 21:02 — with
GitHub Actions
Failure
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
owilliams-tetrascience
force-pushed
the
claude/rdkit-component-wrapper-36a448
branch
from
July 16, 2026 21:04
23c94aa to
13013eb
Compare
owilliams-tetrascience
temporarily deployed
to
artifactory-prod
July 16, 2026 21:04 — with
GitHub Actions
Inactive
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a new composed Molecule Structure primitive (SMILES → 2D SVG) to the UI kit with lazy-loaded RDKit WASM, plus enables rich-HTML chart tooltips to support embedding molecule SVGs (and other markup) in hover content.
Changes:
- Introduces
MoleculeRenderer,StructureThumbnail,StructureTooltip, and RDKit loading helpers (useRDKit,loadRDKit,moleculeToSvg,configureRDKit) undersrc/components/composed/MoleculeStructure/. - Exports the new composed module from
src/index.tsand adds Storybook stories/play tests for the new primitives. - Updates
ChartTooltipto render tooltip lines as HTML (viadangerouslySetInnerHTML) sotooltip.contentcan return rich markup.
Reviewed changes
Copilot reviewed 10 out of 11 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| yarn.lock | Adds lockfile entry for @rdkit/rdkit to support Storybook/tests and optional peer usage. |
| package.json | Declares @rdkit/rdkit as an optional peerDependency and devDependency. |
| src/index.ts | Exports the new MoleculeStructure composed module from the library entrypoint. |
| src/components/composed/MoleculeStructure/use-rdkit.ts | React hook to load RDKit lazily and expose status/error. |
| src/components/composed/MoleculeStructure/rdkit-loader.ts | Lazy loader + CDN/self-host configuration + moleculeToSvg helper. |
| src/components/composed/MoleculeStructure/MoleculeRenderer.tsx | Core SMILES → SVG renderer with loading + invalid/failure fallback UI. |
| src/components/composed/MoleculeStructure/StructureThumbnail.tsx | Square thumbnail wrapper for table/list/card usage. |
| src/components/composed/MoleculeStructure/StructureTooltip.tsx | Tooltip wrapper rendering a structure popover with theme-aware contrast. |
| src/components/composed/MoleculeStructure/MoleculeStructure.stories.tsx | Storybook coverage (incl. play tests) exercising real RDKit via Vite ?url assets. |
| src/components/composed/MoleculeStructure/index.ts | Barrel exports for new MoleculeStructure API surface. |
| src/components/charts/ChartTooltip/ChartTooltip.tsx | Switches tooltip lines from escaped text to HTML rendering. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Add a cheminformatics primitive for rendering 2D chemical structures from SMILES, the kit's first cheminformatics component (VIZ-3 / HTS Hit Finder). - MoleculeRenderer: core SMILES → SVG, lazy-loads RDKit.js - StructureThumbnail: fixed-size or responsive square for table cells - StructureTooltip: hover popover wrapper - useRDKit / moleculeToSvg: preload + sync SVG helper for the interactive scatter's string-returning tooltip.content RDKit's ~6.6 MB WASM is isolated in the loader and lazy-loaded once per page, so non-chem consumers pay nothing. @rdkit/rdkit is an optional peerDependency; assets load from a pinned jsDelivr CDN by default, or self-hosted/preloaded via configureRDKit. The loader routes RDKit's Emscripten stdout/stderr to console.debug so invalid SMILES don't spam the host console. Stories and play tests both exercise REAL RDKit — the WASM is resolved through Vite `?url` imports so it's served identically in dev Storybook, the vitest browser runner, and the Vercel build, with no CDN dependency and no human-vs-CI behavioural split. Stories cover thumbnail, responsive, invalid-SMILES fallback, dark mode, tooltip, DataTable cell, and InteractiveScatter custom tooltip. Exported from src/index.ts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The design-system chart tooltip (useChartTooltip / ChartTooltip) rendered each content line as escaped text, so the `tooltip.content` contract — which returns HTML strings (`<br>` separators, and rich markup like `<b>` or badges) — showed literal tags instead of formatted content. Render lines via dangerouslySetInnerHTML so authored content (including a molecule SVG in the interactive scatter) renders as intended. Content originates from developer content functions and the library's own line builders, not raw untrusted input. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
owilliams-tetrascience
force-pushed
the
claude/rdkit-component-wrapper-36a448
branch
from
July 16, 2026 21:26
13013eb to
8d03364
Compare
owilliams-tetrascience
temporarily deployed
to
artifactory-prod
July 16, 2026 21:26 — with
GitHub Actions
Inactive
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
owilliams-tetrascience
had a problem deploying
to
artifactory-prod
July 17, 2026 14:08 — with
GitHub Actions
Failure
Address PR review: rendering every chart tooltip line via dangerouslySetInnerHTML turned consumer-supplied strings (point labels, trace names, metadata) into an XSS surface across all charts using useChartTooltip. Make HTML rendering explicit opt-in instead — escaped text is the default again. - ChartTooltip: render lines as escaped text unless `anchor.html` is set; key lines by index (not full content, which could be a large SVG string). - useChartTooltip / TooltipConfig: add an `html` flag threaded to the anchor. - ScatterPlotInteractive: forward `tooltip.html`; the CustomTooltips story and the molecule scatter story opt in (they return trusted markup). - ChartTooltip tests: cover both the escaped-by-default and opt-in paths. - MoleculeRenderer: correct the `onError` doc — it may fire more than once (StrictMode, or when drawing inputs change), not exactly once. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…6a448' into claude/rdkit-component-wrapper-36a448 # Conflicts: # src/components/charts/ChartTooltip/ChartTooltip.tsx # src/components/composed/MoleculeStructure/MoleculeRenderer.tsx
owilliams-tetrascience
temporarily deployed
to
artifactory-prod
July 17, 2026 14:17 — with
GitHub Actions
Inactive
…RDKit
Per review: drop the StructureThumbnail and StructureTooltip components —
MoleculeRenderer is the single primitive. Hovering a structure is now shown
by composing MoleculeRenderer with the existing HoverCard primitive (see the
InHoverCard and InDataTable stories) rather than a bespoke component.
- Remove StructureThumbnail / StructureTooltip and their exports.
- RDKit loading is air-gapped by default: the loader dynamically imports the
consumer-installed @rdkit/rdkit (optional peer) instead of injecting a
jsDelivr CDN script. No CDN, nothing fetched from the network by default.
configureRDKit({ wasmSrc }) points at a self-hosted WASM; importFactory
overrides how the factory is obtained (and makes the loader unit-testable).
- MoleculeRenderer accessibility: aria-busy while loading, labelled loading
and error states, role="img" with an aria-label for the structure.
- Stories: MoleculeRenderer is the meta component; add Sizes + InHoverCard;
DataTable renders MoleculeRenderer directly with a hover-card ID column.
- Rewrite the loader unit tests for the dynamic-import path.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
owilliams-tetrascience
temporarily deployed
to
artifactory-prod
July 17, 2026 14:41 — with
GitHub Actions
Inactive
…t/scatter tooltip changes Simplify per review: the molecule primitive no longer touches the shared chart tooltip. Revert ChartTooltip, useChartTooltip, ScatterPlotInteractive, and its TooltipConfig to their pre-molecule state (drop the opt-in HTML tooltip flag and the added ChartTooltip tests), and remove the InInteractiveScatter story that depended on it. Molecule usage is demonstrated with MoleculeRenderer directly, the HoverCard primitive, and a DataTable cell. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
owilliams-tetrascience
temporarily deployed
to
artifactory-prod
July 17, 2026 16:16 — with
GitHub Actions
Inactive
Drop the width/height props — the structure is vector and just fills its container (fixed square internal draw resolution). The Default story now shows this by rendering into a resizable box; remove the Sizes and InDataTable stories. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
owilliams-tetrascience
temporarily deployed
to
artifactory-prod
July 17, 2026 18:57 — with
GitHub Actions
Inactive
Coverage Report
File Coverage
|
||||||||||||||||||||||||||||||||||||||||||||||||||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a chemical-structure primitive to the kit (SW-2100 / VIZ-3) — the library's first cheminformatics component. Renders a 2D structure from a SMILES string, with a thumbnail and a hover-tooltip wrapper, for showing compound structures in tables, scatter tooltips, and detail cards (HTS Hit Finder).
New in
src/components/composed/MoleculeStructure/, exported fromsrc/index.ts:MoleculeRenderer— core SMILES → SVG; lazy-loads RDKit, follows dark mode, renders a fallback for invalid SMILES instead of throwing.StructureThumbnail— fixed-size (size={48}) or responsive square, for table cells / list rows / cards, with optional caption.StructureTooltip— hover popover revealing the structure; matches the kit's tooltip styling.useRDKit/moleculeToSvg/loadRDKit/configureRDKit— preload hook + synchronous SVG helper so a structure can feed the interactive scatter's string-returningtooltip.content.Dependency strategy (the important part)
RDKit's
~6.6 MBWASM is never bundled. It's isolated inrdkit-loader.tsand lazy-loaded once per page, so pages that never render a molecule pay nothing (verified: builtrdkit-loader.jsis ~3 KB, no WASM indist).@rdkit/rdkitis an optionalpeerDependency(+ devDependency for types); by default the loader pulls the script/WASM from a pinned jsDelivr CDN, and consumers can self-host or pass a preloaded instance viaconfigureRDKit. RDKit's Emscripten stdout/stderr is routed toconsole.debugso invalid input never spams the host console.Shared change (separate commit)
The interactive-scatter integration surfaced a pre-existing bug:
ChartTooltiprenderedtooltip.contentas escaped text, so the documented rich-HTML tooltips (badges, and now molecule SVGs) showed as literal tags. Fixed to render content as HTML (fix(charts):). Content originates from developer-authored content functions / the library's own line builders, not raw untrusted input.Type of Change
ChartTooltiprendered content as escaped text)Checklist
yarn lintpassesyarn buildpasses (no WASM bundled; loader ~3 KB)yarn test:allpasses (unit 818 + storybook 699; touched areas re-verified: 21 molecule + scatter play tests)Design Patterns/Molecule Structure)Testing
Stories and play tests exercise real RDKit — Vite
?urlimports resolve the loader script + WASM to asset URLs served by whichever server is active (dev Storybook, the vitest browser runner, the Vercel static build), so there's no CDN dependency in CI and no behavioural split between what a human sees and what tests run.Stories:
Thumbnail,ThumbnailWithLabel,Responsive,InvalidStructure,Tooltip,InDataTable,InInteractiveScatter,Core.Zephyr:
parameters.zephyr.testCaseIdintentionally left""on all stories — apply thezephyr_synclabel to generate/backfill IDs.Verification
Verified live in Storybook (real RDKit rendering,
navigator.webdriverfalse): correct aspirin/caffeine/paracetamol depictions; invalid-SMILES fallback; dark-mode legibility (light bonds + brightened heteroatoms — soft blue N, red O); structures inside aDataTablecell; and the structure SVG rendering inside anInteractiveScatterhover tooltip.Screenshots
UI change — verified in Storybook (thumbnail, DataTable cell, StructureTooltip popover, and the scatter hover tooltip). Reviewers can browse
Design Patterns/Molecule Structurein the preview deploy.