An embeddable React viewer for eNanoMapper/AMBIT substance, composition, and
study data. It is a modern rewrite of the study/substance viewers from the legacy
jQuery jToxKit (StudyKit, SubstanceKit,
CompositionKit) that can be embedded in React apps such as
spectrasearch or run standalone.
The primary target is an embeddable React component. The same component also powers a
standalone /jtoxkit/ app for development, demos, and direct links.
The legacy jToxKit remains in use by static Eleventy/jQuery sites; this package targets React hosts and does not replace it there.
Given one substance, it renders the same things the jToxKit study page does:
- Substance card: public name, type, owner, UUID, and external identifiers.
- Composition: ingredient table(s) grouped by composition, with concentrations and optionally structure diagrams.
- Studies: tabs per top category (P-Chem, Env Fate, Eco Tox, Tox); each tab lazily loads its studies and groups them by protocol category into tables with dynamic, per-category columns, a filter box, and expand/collapse.
Consumers should depend on the npm package and import the component and bundled CSS:
import SubstanceStudyViewer from '@ideaconsult/jtoxkit-react'
import '@ideaconsult/jtoxkit-react/style.css'
<SubstanceStudyViewer
substanceUri="https://apps.ideaconsult.net/nanoreg1/substance/XXXX-..."
token={keycloakToken}
apiBase="https://apps.ideaconsult.net/nanoreg1/"
convertBase="https://api.ramanchada.ideaconsult.net/"
showDiagrams
/>Hosts own authentication and pass a bearer token with the token prop when protected
resources are needed. The viewer never starts login or redirect flow and must also render
without a token for public AMBIT data.
When embedding in another Vite app, dedupe React so hooks share one instance:
// vite.config.js
resolve: { dedupe: ['react', 'react-dom'] },
optimizeDeps: { include: ['@ideaconsult/jtoxkit-react'] },Peer dependencies are react, react-dom, and, only for the dose-response chart,
@observablehq/plot. Plot is external and optional so the host can reuse its single
instance; if the host does not provide it, the chart is simply not shown. dompurify is a
runtime dependency bundled by the library for sanitizing legacy renderer HTML.
A host search index returns studies, not only substances: in spectrasearch a hit is one
protocol application, identified by its document_uuid (the AMBIT study.uuid). Without
documentUuid such a hit can only open its parent substance, leaving the user to find the
study again by hand among the substance's other studies — for a well-characterised material
that is several hundred rows across a tab's categories. documentUuid is what makes a
study-level search result resolve to the study itself.
The tab holding it is resolved by the viewer (studysummary counts studies per topcategory
but does not say which one holds a given study), so a host that knows only the study id
does not also have to know its category. Pass initialTab alongside it when the category
is known — it saves loading the other tabs.
| Prop | Type | Notes |
|---|---|---|
substanceUri |
string | Full AMBIT substance URL. |
substanceId |
string | UUID; resolved against apiBase if substanceUri is absent. |
apiBase |
string | AMBIT base URL. Embedded hosts should pass this explicitly. |
convertBase |
string | Optional ramanchada-api base for dose-response conversion (/dataset/convert?format=effectarray). |
token |
string | Bearer token; host owns auth. Omit for public data. Standalone mode may read ?token=, sessionStorage, or postMessage. |
showDiagrams |
bool | Show structure images in the composition table. |
columnConfig |
object | Study column config; accepts the existing jToxKit config_study object. |
initialTab |
string | Top category to open first, for example TOX. |
documentUuid |
string | Open one study — its AMBIT uuid, the document_uuid of a protocol application. The tab holding it is found for you, and the view narrows to that study alone (its category group, its row), with a "Show all" link back to the whole tab. Pass initialTab as well when the category is already known; it saves loading the other tabs. |
proxyFrom |
string | Optional URL prefix to rewrite in AMBIT-returned URLs. Mostly useful for standalone/dev proxying. |
proxyTo |
string | Optional replacement prefix used with proxyFrom. |
source |
object | Inject a custom data source adapter; defaults to AMBIT REST. |
The standalone app reads URL params and Vite environment variables in src/App.jsx, then
passes props to the reusable SubstanceStudyViewer component.
Useful standalone URLs:
http://localhost:5175/jtoxkit/?substanceUri=<AMBIT substance URL>http://localhost:5175/jtoxkit/?substanceId=<uuid>&apiBase=<AMBIT base>&showDiagrams=truehttp://localhost:5175/jtoxkit/?substanceId=<uuid>&tab=TOXhttp://localhost:5175/jtoxkit/?substanceId=<uuid>&study=<document_uuid>
Standalone environment variables are documented in .env.example. Embedded hosts should
pass equivalent values as component props instead of relying on this package's Vite env.
Data is fetched directly from the AMBIT REST API, using the same calls jToxKit makes:
/substance/{uuid}, /substance/{uuid}/studysummary, per-category study URIs, and
/substance/{uuid}/composition.
All backend access is isolated behind a data-source adapter
(src/data/ambitSource.js, implementing the interface in src/data/source.js). A future
Solr/ramanchada-api adapter can map data to the same AMBIT shape and be injected through
the source prop without changing components, configs, or renderers.
The study tables are driven by the unchanged jToxKit study config. The bundled default
(src/config/studyColumns.js, composed from i5.js, bao.js, exposure.js, and
npo.js) is ported verbatim from the legacy nanodata study config. A host can pass its own
object of the same shape via columnConfig.
Both modern keys (title, visible, order, render) and legacy DataTables keys
(sTitle, bVisible, iOrder) are normalized. render() callbacks that return HTML
strings work as-is; they are sanitized and injected through src/utils/Html.jsx.
cp .env.example .env
pnpm install --frozen-lockfile
pnpm devCommon commands:
pnpm test # vitest unit tests
pnpm build # standalone app build
pnpm build:lib # library build: dist/jtoxkit-react.js + dist/style.css
pnpm preview # preview production app buildThere are currently no lint, formatter, or typecheck scripts. For code changes, run
pnpm test and the relevant build command. For packaging changes, also run
pnpm peers check, pnpm build, pnpm build:lib, and pnpm pack --dry-run.
To visually check parity, open the same substance in the existing nanodata-11ty study page (the live jQuery kit) side-by-side.
The package is published as @ideaconsult/jtoxkit-react under the ideaconsult npm
organization and is licensed under Apache-2.0. The library build emits
dist/jtoxkit-react.js and dist/style.css; consumers import the package entry and CSS,
never src/.
npm publication is handled by .github/workflows/publish.yml using npm trusted
publishing/OIDC and the GitHub npm environment. Releases do not use long-lived npm
publish tokens. Release tags must match package.json exactly as vX.Y.Z.
Local file: dependencies are acceptable only for development while iterating across
repositories. They are not a release or CI distribution strategy; hosts such as
spectrasearch should consume the semver npm package.
src/
SubstanceStudyViewer.jsx public component: providers + ViewerBody (load sequence)
App.jsx standalone shell: URL params + Vite env -> viewer props
index.js package entry (default export + useAuth/useViewerConfig/jsambit)
context/ AuthContext, ViewerConfig, DataSource providers
data/ source.js (adapter contract), ambitSource.js (AMBIT REST)
hooks/useAmbit.js load(uri) + {data,loading,error}, aborts in flight
components/ SubstanceCard, CompositionView, StudyViewer, StudyTable, DataCell
config/ config_study (+ i5/bao/exposure/npo), compositionColumns
utils/ format, ambit, tables, buildStudyColumns, Html, legacyGlobals
styles/viewer.css tokens + base, all scoped under .jtoxkit-root
- Composition uses the compound fields returned directly by AMBIT; AMBIT
feature-map decoding (jToxKitprocessEntry) is not yet ported. - The substance card does not yet render a structure image from the reference substance.
- A Solr/ramanchada-api
sourceadapter (pyambit Solr-to-AMBIT mapping) is a follow-on.