Skip to Content
This documentation is provided with the HEAT environment and is relevant for this HEAT instance only.
GuidesCustom ComposableChart on Legacy dashboards

Custom ComposableChart on Legacy dashboards

This guide walks through adding a custom composable chart to a Legacy session detail page. For v2 / dashboard-v2 pages, use ComposableChart (Next) instead.

Prerequisites

  • Legacy dashboard node output includes combined-custom-charts with named datasets
  • Layout uses mount id ComposableChart
  • heat-next build includes ComposableChartLegacy (run npm run build:lib in ui/dashboard when developing locally)

1. Shape runner output

Publish datasets the resolver understands:

{ "combined-custom-charts": [ { "title": "My chart", "datasets": [ { "name": "Speed", "type": "area", "unit": "km/h", "data": [ { "DateTime": "2025-02-17T15:36:37.000Z", "Data": 42 } ] }, { "name": "Comms window", "type": "rangeBar", "data": [ { "StartTime": "2025-02-17T15:37:00.000Z", "EndTime": "2025-02-17T15:38:30.000Z" } ] }, { "name": "Report events", "type": "scatter1", "data": [ { "DateTime": "2025-02-17T15:40:00.000Z", "Data": 1 } ] } ] } ] }

Dataset name values must match your manifest bindings exactly (case-insensitive match).

2. Author the legacy manifest

Start from the terrain reference:

  • TypeScript (Storybook/tests only): terrainTimelineStoryManifest in ui/dashboard/src/components/organisms/composable-chart/legacy/fixtures/terrainStory.fixture.ts
  • Docs: ComposableChart (Legacy) manifest section

Minimal example:

{ "layout": { "anchor": "time", "elements": [ { "type": "area", "order": 0, "height": 160, "series": [{ "id": "speed", "label": "Speed", "color": "#81842C" }] }, { "type": "ranges_lane", "order": 1, "label": "Comms", "height": 32 }, { "type": "events_lane", "order": 2, "label": "Reports", "height": 32, "eventSeries": [{ "id": "reports", "label": "Reports", "color": "#666" }] } ] }, "bindings": [ { "order": 0, "source": { "kind": "area_series", "series": [{ "seriesId": "speed", "datasetName": "Speed", "datasetType": "area" }] } }, { "order": 1, "source": { "kind": "dataset", "datasetName": "Comms window", "datasetType": "rangeBar" } }, { "order": 2, "source": { "kind": "multi_dataset", "datasets": { "reports": "Report events" } } } ], "dataSource": { "key": "combined-custom-charts", "chartIndex": 0 } }

3. Embed manifest in layout

Add the manifest to the layout column configuration:

{ "layoutConfiguration": [ { "cols": [ { "component": "ComposableChart", "colspan": 12, "marginBottom": 24, "configuration": { "composableChartLegacyManifest": { } } } ] } ] }

Replace { } with your manifest JSON. If omitted, the SPA wrapper uses the terrain reference manifest (demo only).

4. Verify in ui/legacy

  1. Deploy or load a session whose dashboard layout includes ComposableChart.
  2. Confirm combined-custom-charts loads (same path as TimelineChart).
  3. Check for resolver warnings (shown inline in dev when datasets are missing).
  4. Exercise local playback: scrubber should move the yellow cursor when anchor is "time".

5. Optional: unit test your manifest

In ui/dashboard, resolve against a fixture payload:

import { resolveLegacyComposableChart } from "@/components/organisms/composable-chart/legacy"; import { buildTerrainLegacyPayload } from "@/components/organisms/composable-chart/legacy/fixtures/terrainLegacyPayload.fixture"; const result = resolveLegacyComposableChart({ manifest: myManifest, payload: buildTerrainLegacyPayload(), }); expect(result.warnings).toEqual([]); expect(result.slices.length).toBeGreaterThan(0);

6. Storybook reference

Compare with TerrainTimelineMock in ui/dashboard Storybook (Organisms/ComposableChart). Legacy binding should visually match when dataset names and layout align with the terrain reference.

Limitations reminder

  • No $heat-dataservice or TimelineClock sync with map playback
  • No in-chart playback slice (placeholder only)
  • Spanning line times must be session-relative ms in layout, not v1 DateTime strings