# Research chart

External component: renders a data visualisation (`bar`, `grouped-bar`, `stacked` bars, `line`, `forest-plot`, `forest`, or `pie`) from structured JSON. Optional heading and RichText footnote (`additionalCopy`). Use as a block in page content or embed inline in rich text via `rtf embed`.

**Authoritative technical reference for maintainers:** [scripts/publications/README.md](../../../scripts/publications/README.md) (publication scripts). This file is the **cms-edit / content** contract.

## When to use which `type`

| `type` | Use for |
|--------|--------|
| `bar` / `grouped-bar` | Counts, percentages, comparisons. **Grouped** (default): multiple `values` appear side-by-side per category. **`stacked: true`**: segments in one bar/column sum to a total per category. |
| `line` | Trends over ordered categories (e.g. time points). Same `categories` / `values` shape as bars; optional `errorBars` on each category. |
| `forest-plot` | Simple forest: flat `categories`, one point + optional `ci` per row, `referenceValue` or `referenceLine`. |
| `forest` | Clinical regression-style forest: grouped sections, `estimate` / `ciLow` / `ciHigh`, optional `logScale`, `effectLabel`, many rows. |
| `pie` | Part-to-whole (single value per category). |

Default **`orientation`** for bar charts is **`horizontal`** (long labels read more easily). Use **`vertical`** for classic column charts or ICPE-style figures.

## Fields & schema (Contentful)

| Field | Type | Required | Notes |
|-------|------|----------|-------|
| `externalComponentType` | Symbol | Yes | Must be `"Research chart"` |
| `heading` | Symbol | No | Chart title above the chart |
| `additionalCopy` | RichText | No | Footnote below — use `cms-edit rtf` |
| `data` | Object | Yes | Chart payload (below) |

### `data` — top-level keys

| Key | Type | Notes |
|-----|------|-------|
| `type` | See table above | Required |
| `orientation` | `"horizontal"` \| `"vertical"` | Bars only; default **`horizontal`** |
| `series` | `string[]` | Legend labels for grouped/stacked multi-value categories |
| `seriesLabels` | `string[]` | **Alias for `series`** (whitepaper JSON); renderer normalizes |
| `stacked` | `boolean` | Default `false`. When `true` and a category has multiple `values`, render **stacked** segments (not side-by-side) |
| `unit` | `string` | Shown as caption (e.g. `patients`, `%`) |
| `xAxisLabel` | `string` | Optional; line charts and bars |
| `yAxisLabel` | `string` | Optional |
| `maxValue` | `number` | Y-axis / scale max; computed if omitted |
| `referenceValue` | `number` | Reference line for `forest-plot` (often `1`) |
| `referenceLine` | `number` | **Alias for `referenceValue`** |
| `effectLabel` | `string` | X-axis description for forest plots (e.g. `"Risk Ratio (95% CI)"`) |
| `logScale` | `boolean` | For `type: "forest"` only; default `true` |
| `xAxisMin` / `xAxisMax` | `number` | For `type: "forest"`; optional axis bounds |
| `categories` | `Category[]` | Required for bar, line, pie, `forest-plot`; omit or `[]` if **`forest`** with only `groups` |
| `groups` | `ForestGroup[]` | Required for `type: "forest"` |

### `Category`

| Key | Type | Notes |
|-----|------|-------|
| `label` | `string` | Yes |
| `values` | `number[]` | One or more per category |
| `n` | `string` | e.g. `"n=1,012"` |
| `pValue` | `string` | e.g. `"P < .05"` |
| `ci` | `[number, number]` | Legacy simple forest rows |
| `colour` | `string` | Design token name (see below). When **one value** per category, sets bar colour; pie slices use per-slice colour |
| `errorBars` | `([number, number] \| null)[]` | **Line charts:** optional per-series `[low, high]` parallel to `values` |

**Allowed `colour` values** (must match design system / `src/generated/colors.ts`):

`Light`, `Primary Blue`, `Secondary Blue`, `Bright Blue`, `Light Blue`, `Dark`, `Gray 1`, `Gray 2`, `White`, `Orange`, `Light Orange`, `Purple`, `Light Purple`, `Green`, `Yellow`, `Inactive Gray`

### `forest` — `ForestGroup` / row

```json
{
  "type": "forest",
  "effectLabel": "Risk Ratio (95% CI)",
  "referenceLine": 1.0,
  "logScale": true,
  "xAxisMin": 0.25,
  "xAxisMax": 4.0,
  "groups": [
    {
      "label": "Demographics",
      "rows": [
        {
          "label": "Age",
          "estimate": 1.0,
          "ciLow": 0.99,
          "ciHigh": 1.0,
          "pValue": 0.41,
          "significant": false
        },
        {
          "label": "Reference category",
          "referenceRow": true
        }
      ]
    }
  ]
}
```

| Row field | Notes |
|-----------|--------|
| `referenceRow` | If `true`, label-only row (no marker / CI) |
| `significant` | `true` → bold / accent label styling |

## Usage (cms-edit)

```bash
cms-edit add "Research chart" --content-type externalComponent --target content
cms-edit set @ref heading "Treatment Response by Group"
cms-edit set @ref data --json '{
  "type": "bar",
  "orientation": "horizontal",
  "unit": "%",
  "categories": [
    { "label": "Group A", "values": [72], "n": "n=45", "colour": "Primary Blue" },
    { "label": "Group B", "values": [58], "n": "n=52", "colour": "Light Blue" }
  ]
}'
cms-edit rtf @ref additionalCopy --file footnote.md
cms-edit rtf embed @ref body <research-chart-entry-id>
cms-edit save
```

### Stacked bar (`series` or `seriesLabels`)

```json
{
  "type": "bar",
  "orientation": "vertical",
  "stacked": true,
  "unit": "%",
  "maxValue": 30,
  "seriesLabels": ["Low disease activity (excl. remission)", "Remission"],
  "categories": [
    { "label": "6-month follow-up", "values": [20.2, 5.4] },
    { "label": "12-month follow-up", "values": [22.8, 7.3] }
  ]
}
```

### Line chart

```json
{
  "type": "line",
  "unit": "score",
  "maxValue": 30,
  "xAxisLabel": "Time point",
  "yAxisLabel": "Mean CDAI score",
  "categories": [
    { "label": "Baseline (N=8,681)", "values": [24.3] },
    { "label": "6-month follow-up (N=8,681)", "values": [17.6], "errorBars": [[16, 19.2]] },
    { "label": "12-month follow-up (N=6,748)", "values": [16.1] }
  ]
}
```

### Simple forest (`forest-plot`)

```json
{
  "type": "forest-plot",
  "referenceValue": 1.0,
  "categories": [
    { "label": "Study 1", "values": [1.2], "ci": [0.9, 1.6] },
    { "label": "Study 2", "values": [0.8], "ci": [0.6, 1.1] }
  ]
}
```
