Skip to content

Architecture

@ringover/ringo-charts is a pure presentation library. It exports visualization components that accept display props (titles, formatted values, data tables, loading / error states) and render HTML / Canvas / SVG.

The library does not know about POST /statistics, WidgetConfig, saved views, the catalog of widget types, or the metrics + projection + periods + where contract. It wraps Chart.js, D3 and Leaflet into reusable Vue 3 components that any Ringover app (dashboard, monitoring, webapp) can consume without coupling to the stats API.

All business intelligence — API fetching, data mapping, view configuration — lives in the platform / app (Ringover Views), not in the library.

Two levels of components

The library exposes two levels: reusable visualization primitives and business widgets that compose those primitives with header / footer / objective / alert chrome.

Level 1 — Visualization primitives

Low-level components, no Widget suffix. An app can consume them directly to build its own views. See Chart primitives.

Level 2 — Business widgets

High-level components that wrap a primitive and add a header, objective footer, loading state and drill-down. The Widget suffix makes the role explicit. These are Organisms in Atomic Design terms. They are props-driven skeletons: no fetch, no store — layout is delivered through slots and props. See Business widgets.

Their props are display props — title, value, label, trend, rows, loading, errornot a WidgetConfig object.

Atomic Design layers

Every Vue file lives in exactly one of these directories under packages/ringo-charts/src/components/:

components/
├─ atoms/        # smallest UI units (badges, dots, inline bars, menus)
│  ├─ badges/    # chip / badge / pill primitives
│  └─ widgets/   # widget chrome atoms (RoWidgetMenu)
├─ molecules/
│  ├─ charts/    # chart-adjacent molecules (RoChartFrame, RoChartTooltipCard, …)
│  └─ widgets/   # widget chrome molecules (RoWidgetCard, RoWidgetHeader, …)
└─ organisms/
   ├─ charts/    # chart primitives (RoBarChart, RoLineChart, RoSankey, …)
   ├─ states/    # loading / empty / error skeletons
   └─ widgets/   # business widgets — one folder per master pattern

The …Widget suffix is reserved for business widgets in organisms/widgets/. Chart and state organisms never use it. There are no pattern-a..f folders and no legacy templates/ layer.

Allowed cross-layer imports

  • Atoms depend on nothing except vue, @ringover/ringo-ui and utils.
  • Molecules depend on atoms.
  • Chart organisms depend on atoms + chart.js / d3-* / vue-chartjs.
  • Widget chrome molecules (incl. RoWidgetCard) depend on atoms + state organisms.
  • State organisms depend on atoms only.
  • Widget organisms compose everything below them.

Data flow: from a saved view to a component

The full path between a saved view and the rendered screen shows the library / app split:

Saved View JSON  (read from S3)
   |
WidgetConfig     (type + settings + filters)        <- app
   |
StatsRequest     POST /statistics                   <- app
   |
StatsResponse    (meta + data pivoted by period)    <- backend
   |
App adapter      (maps data -> display props)       <- app (COMPONENT_BY_TYPE)
   |
<RoKPIWidget title="..." value="..." trend="..." /> <- ringo-charts renders
  1. Saved View JSON — the view is read from S3; it lists widgets with their type, settings and filters.
  2. WidgetConfig — the app parses the JSON into a WidgetConfig per widget. This type is defined in the platform, not in ringo-charts.
  3. StatsRequest — the app builds the API request (POST /statistics): timezone, periods[], a projection discriminated by type, and a where[] derived from global + per-widget filters. The library is not involved here.
  4. StatsResponse — the backend returns resolved period ranges and metrics pivoted by period (with an automatic delta on relative periods).
  5. App adapter — the platform's COMPONENT_BY_TYPE registry maps the catalog type to a ringo-charts component and transforms StatsResponse into display props (title, value, trend, rows, loading, error, …). This adapter lives in the app, not in the library.
  6. Render — the component receives its props and renders. It does not know where the data came from.

Why a private library

  • Visual consistency — every chart shares the same ringo-ui color tokens, padding, animations and fonts.
  • Pure presentation — components accept display props and render visuals; zero business logic, zero API calls.
  • Centralized maintenance — a breaking Chart.js change is fixed in one place.
  • Cross-project reuse — dashboard, monitoring and webapp all install the same package.
  • Tree-shakeable — a project that only needs KPIs does not ship the Sankey.

Chart.js, vue-chartjs, D3 and Leaflet are encapsulated inside ringo-charts and are not installed directly by consuming apps (except the documented peers in Getting started).