Render JSON-stat 2.0 datasets as interactive, accessible SVG charts.
This package is not yet published to npm. Install it locally:
# In this project directory, build and pack:
npm run pack
# In your consuming project, install the .tgz:
npm install ../jsonstatgraphs/statisticsfinland-jsonstatgraphs-0.2.1.tgzOr link for development:
# In this project:
npm run build
npm link
# In your consuming project:
npm link @statisticsfinland/jsonstatgraphsNote: Rebuild with
npm run buildafter source changes when usingnpm link.
import { createChart } from '@statisticsfinland/jsonstatgraphs';
import type { JsonStatDataset } from '@statisticsfinland/jsonstatgraphs';
// Fetch a JSON-stat 2.0 dataset
const response = await fetch('https://example.com/api/dataset');
const dataset: JsonStatDataset = await response.json();
// Render into a container. Current selectable values are an optional fourth argument.
const container = document.getElementById('chart')!;
const chart = createChart(container, dataset, {
layout: { rows: ['region'], columns: ['year'] },
}, {
scenario: ['current'],
});
// Later: update data, config, and/or current selections
chart.update(dataset, { chartType: 'verticalBar' }, {
scenario: ['comparison'],
});
// Change chart type
chart.setChartType('line');
// Clean up
chart.destroy();const chart = createChart(container, dataset, {
chartType: 'line', // Override auto-selected chart type
locale: 'fi', // Locale for formatting (en, fi, sv)
title: 'Custom Title', // Override auto-generated title
subtitle: 'By region', // Override auto-generated selection subtitle
height: 400, // Container height in pixels
showHeader: true, // Show auto-generated header (default: true)
showUnit: false, // Also show the dataset unit in the footer (default: false)
showLegend: true, // Show legend for multi-series charts
autoTitle: true, // Auto-generate title from metadata (default: true)
layout: { // Project the JSON-stat cube into chart series and categories
rows: ['region'],
columns: ['year'],
},
defaultSelectableSelections: {
scenario: ['current'],
},
multiSelectableDimensionCode: 'region',
menuIconInheritColor: false, // Burger menu icon color inherits parent color when true
menuItemDefinitions: [ // Optional custom burger menu items (shown before built-ins)
{ text: 'Custom action', onClick: () => console.log('clicked') },
{ text: 'Documentation', url: 'https://example.com/docs', openNewTab: true },
],
footerItems: [
{ type: 'source', label: 'Source:', value: 'Statistics Finland' },
],
sourceLink: 'https://example.com/something', // Make source footer item clickable
theme: { // Override default theme
fontFamily: 'Arial, sans-serif',
colorText: '#333',
seriesColors: ['#1f77b4', '#ff7f0e', '#2ca02c'],
},
});| Option | Type | Description |
|---|---|---|
chartType |
ChartType |
Override the auto-selected chart type |
locale |
string |
BCP 47 locale used for UI strings and numeric values in axes, labels, tooltips, maps, tables, screen-reader output, and CSV exports |
title |
string |
Override the auto-generated title |
subtitle |
string |
Subtitle displayed below the title; when omitted, generated from exactly-one selected categories |
height |
number |
Container height in pixels |
showHeader |
boolean |
Show auto-generated header (default: true) |
showUnit |
boolean |
Add the dataset unit to the footer (default: false; units remain on the y-axis) |
showLegend |
boolean |
Show legend for multi-series charts (default: true) |
autoTitle |
boolean |
Auto-generate title from metadata (default: true) |
accessibilityMode |
boolean |
Enable accessibility visuals (pattern fills or marker shapes) for supported chart types |
showBurgerMenu |
boolean |
Show the chart burger menu (default: true) |
menuItemDefinitions |
(FunctionalMenuItem | LinkMenuItem)[] |
Optional custom burger menu items shown before built-in items |
menuIconInheritColor |
boolean |
When true, burger menu icon color inherits from parent text color |
footerItems |
FooterItem[] |
Array of footer items. Each item has type ('source', 'updated', 'custom'), label (prefix text), and value (main text) fields. |
sourceLink |
string |
URL to make the source footer item a clickable link. Must be an http:// or https:// URL. When set, footer items with type: 'source' render as hyperlinks opening in a new tab. The URL is validated and javascript: / data: URLs are rejected for security. |
ariaLabel |
string |
Custom aria-label for the figure element |
layout |
Layout |
Dimension projection with rows (series) and columns (categories/X axis) |
defaultSelectableSelections |
SelectableSelections |
Fallback category selections keyed by dimension code |
multiSelectableDimensionCode |
string |
Dimension whose multiple selected categories are rendered as separate series |
sorting |
string |
Supported by horizontal bar, grouped/stacked/percent horizontal bar, and pie charts. Keywords are no_sorting, reversed, sum, ascending, and descending; matching series codes are supported only by grouped/stacked/percent horizontal bars. A matching series is moved to the first series position before category sorting, which determines its stack position, legend order, and palette assignment. |
cutValueAxis |
boolean |
Allow the line chart / scatter plot value axis to omit the zero baseline (default: axis always includes 0). No effect on other chart types. |
theme |
ThemeConfig |
Theme customization options (see Theming section) |
The chart can render selected and pivoted data without mutating the caller's JSON-stat dataset. Current selections are passed separately as the fourth argument to createChart() and the third argument to chart.update():
import { createChart } from '@statisticsfinland/jsonstatgraphs';
import type { SelectableSelections } from '@statisticsfinland/jsonstatgraphs';
const selections: SelectableSelections = {
scenario: ['current'],
region: ['MK01', 'MK04'],
};
const chart = createChart(container, dataset, {
chartType: 'line',
layout: {
rows: [],
columns: ['year'],
},
defaultSelectableSelections: {
scenario: ['current'],
},
multiSelectableDimensionCode: 'region',
}, selections);
chart.update(dataset, undefined, {
scenario: ['comparison'],
region: ['MK02'],
});layout.rows forms chart series and layout.columns forms categories on the X axis. During dataset rebuilding, these dimensions are ordered first and omitted dimensions remain in their original relative order. Categorical charts use the first active category of an omitted dimension. A configured multiSelectableDimensionCode with multiple active categories is projected as series when it is not explicitly assigned to either direction.
For tables, transformation hides every dimension with one active category after selectable filtering, even when that dimension appears in layout. Such dimensions remain available in hiddenDimensions metadata, and an all-singleton table uses a single value cell without promoting a dimension into a visible row or column.
Table and CSV output use the active layout and selectable selections. Row and column dimensions therefore match the currently rendered view rather than reverting to the source dataset's automatic orientation.
When layout or selectable settings are provided, the library internally rebuilds a compact N-dimensional dataset containing only active categories and values once per render. The prepared dataset keeps every source dimension and is then passed to the chart transformer, which only projects it into the dimensionality required by that visualization. Datasets without layout or selectable settings follow the original transformation path unchanged.
JSON-stat coordinate order is defined by dataset.id: each entry corresponds to the same position in dataset.size, and together they define the flattened dataset.value order. The property order of the dataset.dimension dictionary is not significant.
Selectable settings can also be embedded in the dataset:
const datasetWithDefaults: JsonStatDataset = {
...dataset,
extension: {
...dataset.extension,
selectableConfig: {
defaultSelectableSelections: { scenario: ['current'] },
multiSelectableDimensionCode: 'region',
},
},
};Resolution precedence is:
- Current selections passed to
createChart()orupdate()overridedataset.extension.selectableConfig.selectableSelections. config.defaultSelectableSelectionsoverrides extension defaults.config.multiSelectableDimensionCodeoverrides the extension value.- A missing selection falls back to its configured default, then to the latest category for a time dimension or the first category for another dimension.
An explicitly empty selection uses a non-empty default when available; otherwise it is rejected. Unknown dimensions, unknown category codes, duplicate layout dimensions, and dimensions assigned to both rows and columns are also rejected at the data-source boundary. When a chart type is explicitly chosen, the library trusts that choice after structural dataset validation; automatic chart selection continues to choose only applicable chart types.
Selectable filtering is supported by categorical charts, tables, maps, scatter plots, pyramids, and key figures. During automatic chart selection, the scatter metric/content dimension and pyramid split dimension cannot be selectable because those dimensions define the renderer's required structure. Selected categories are reflected in automatic titles, map geometry requests, and chart-type switches.
When subtitle is omitted, the chart automatically builds a subtitle from selectable dimensions, except for key figures, which have no subtitle presentation. A dimension contributes its localized selected category name only when exactly one category is active; dimensions with no selection or multiple selections are omitted. Values are joined in selectable-dimension order with |. Category names are read from dataset metadata and fall back to the category code when a label is unavailable. An explicitly configured subtitle always takes precedence for chart types that render subtitles, and generated subtitles are rebuilt when selections change through chart.update().
JSON-stat observation statuses are keyed by flat observation index. Optional extension.missingValueDescriptions entries map status codes to display text:
const dataset: JsonStatDataset = {
// ...dimensions and metadata...
value: [42, null, null],
status: { '1': 'confidential', '2': 'notAvailable' },
extension: {
missingValueDescriptions: {
confidential: 'Confidential',
},
},
};For a missing value, tables and CSV exports display the mapped description when one exists. Otherwise they display the raw status string (notAvailable above). A missing value without a status uses an en dash in tables and an empty CSV cell. Status metadata is preserved and re-indexed when selectable filtering or layout permutation rebuilds the dataset.
Source information can be supplied at three levels. The standard JSON-stat source field remains the dataset-wide fallback. More specific source mappings are stored in the package extension namespace extension.jsonstatChart.sources.
const dataset: JsonStatDataset = {
// ...standard JSON-stat fields...
source: 'Statistics Finland',
role: {
metric: ['measure'],
},
dimension: {
measure: {
category: {
index: { population: 0, employment: 1 },
},
},
},
extension: {
jsonstatChart: {
sources: {
// Applies to every active category in this dimension.
dimension: {
measure: 'Statistics Finland, population and employment database',
},
// Overrides the dimension source for one category.
category: {
measure: {
employment: 'Statistics Finland, employment statistics',
},
},
},
},
},
};The resolution order for each active category is:
extension.jsonstatChart.sources.category[dimensionCode][categoryCode]extension.jsonstatChart.sources.dimension[dimensionCode]dataset.source
For example, the population category above uses the dimension-level source, while employment uses its category-level source. If no extension metadata is provided, a normal string-valued dataset.source works unchanged.
Sources are resolved from active metric dimensions after selectable filters are applied. This means changing selections through createChart() or chart.update() can change the displayed source. Sources are resolved in metric-dimension and category order, duplicate strings are removed while keeping their first occurrence, and the resulting list is used by chart/table footers and CSV exports. Explicit footerItems containing a source item still take precedence over automatic source generation.
Do not add source properties directly to JSON-stat dimensions or categories. Use the extension namespace so the standard JSON-stat structure remains valid.
Charts render a top-right burger menu button (☰) that opens a keyboard-accessible dropdown shell.
When the burger menu is enabled for an HTML table, the table reserves vertical space above its contents for the menu button so the button does not overlap the table header. This spacing is removed when showBurgerMenu is false.
Built-in item order:
- Custom items from
menuItemDefinitions(if any) - Download table (csv)
- Download figure (svg)
- Download figure (png)
- Show symbols in the figure
- View table / View chart
Built-in export items are shown only when export is actionable:
- Table exports (CSV) are shown only when a dataset is available.
- Figure exports (SVG, PNG) are shown only when both a dataset is available and the current chart type supports SVG export.
CSV export behavior:
- Download table (csv) exports the active table view, including the current layout and selectable selections.
- CSV includes UTF-8 BOM for spreadsheet compatibility.
- Delimiter is locale-aware:
;forfi/sv, otherwise,. - Numeric values are locale-formatted without grouping.
- Missing observations use their mapped missing-value description or raw JSON-stat status string.
- Download filename format:
<datasetLabel|export>_YYYYMMDD_HHMMSS.csv(sanitized).
SVG export behavior:
- Download figure (svg) now exports the currently rendered chart SVG.
- SVG export is available for SVG-rendered chart types and hidden for
tableandkeyFigure. - Export serializes the chart container's first
<svg>element withXMLSerializer. - Download filename format:
<datasetLabel|export>_YYYYMMDD_HHMMSS.svg(sanitized).
PNG export behavior:
- Download figure (png) now exports the currently rendered chart as a PNG image.
- PNG export is available for SVG-rendered chart types and hidden for
tableandkeyFigure. - Export serializes the chart SVG, rasterizes it to a canvas, and downloads the PNG Blob.
- Download filename format:
<datasetLabel|export>_YYYYMMDD_HHMMSS.png(sanitized).
Accessibility mode toggle behavior:
- The menu includes an accessibility toggle in chart mode for supported chart types.
- Supported chart types:
line, all bar variants,pie, andpyramid. - Unsupported types (toggle hidden):
scatterPlot,table,keyFigure,map. - Label is state-dependent:
Show symbols in the figurewhen accessibility mode is offRemove symbols from the figurewhen accessibility mode is on
- In line charts, accessibility mode uses distinct marker shapes per series.
- In bar/pie/pyramid charts, accessibility mode applies per-series or per-slice SVG pattern fills.
Table toggle behavior:
- The last menu item toggles between chart and table view.
- The label is
View tablein chart mode andView chartin table mode. - The item is shown only when a table toggle handler is provided to the menu component.
- When switching to table view with the burger menu enabled, the table reserves the menu button's vertical space before its contents.
Keyboard support:
ArrowDown/ArrowUp: move focus between items (wrap around)Enter/Space: activate focused itemEscape: close menu and return focus to menu buttonTab: close menu
Charts are styled through a three-layer cascade. Each layer overrides the one below it:
- JS config — properties passed via
config.theme(see Configuration above) - CSS custom properties —
--jsc-*variables resolved from the chart container - Built-in defaults — sensible defaults (system font, neutral palette)
Every scalar ThemeConfig property has a corresponding CSS custom property named --jsc-{kebab-case-key}. Set these on or above the chart container:
.my-chart-container {
--jsc-font-family: 'Inter', sans-serif;
--jsc-color-text: #1a1a1a;
--jsc-color-background: #f9f9f9;
--jsc-color-border: #ddd;
--jsc-font-size-title: 1.25rem;
}Series colors use --jsc-series-1 through --jsc-series-10:
.my-chart-container {
--jsc-series-1: #e63946;
--jsc-series-2: #457b9d;
--jsc-series-3: #2a9d8f;
}The default series palette, in order, is #1A56EC, #F2644C, #1B3160, #9C8D87, #26625D, #7791E8, #8C1131, #878EAF, #C73268, and #288C72. Colors repeat only after all ten slots are used.
| CSS Variable | Default | Description |
|---|---|---|
--jsc-font-family |
system-ui, -apple-system, sans-serif |
Font stack for all text |
--jsc-font-size-tick |
0.75rem |
Axis tick labels and footer text |
--jsc-font-size-label |
0.875rem |
Legend items, axis titles, subtitles |
--jsc-font-size-title |
1rem |
Chart title |
--jsc-letter-spacing |
0 |
Space between characters in all chart text |
--jsc-font-weight-normal |
400 |
Normal-weight text |
--jsc-font-weight-bold |
700 |
Bold text (title, tooltip values) |
| CSS Variable | Default | Description |
|---|---|---|
--jsc-color-background |
#ffffff |
Background color (table chart headers) |
--jsc-color-surface |
#ffffff |
Tooltip and overlay background |
--jsc-color-text |
#333333 |
Primary text (title, tooltip) |
--jsc-color-text-secondary |
#666666 |
Secondary text (subtitle, axis labels, footer) |
--jsc-color-border |
#cccccc |
Borders, grid lines, bar strokes |
--jsc-color-tick |
#767676 |
Axis tick marks and domain lines |
--jsc-color-error |
#dc3545 |
Error state color |
--jsc-color-focus-ring |
#0066cc |
Keyboard focus indicator |
--jsc-color-link |
#0563C1 |
Footer source link text color |
| CSS Variable | Default | Description |
|---|---|---|
--jsc-border-radius |
4px |
Tooltip border radius |
--jsc-grid-opacity |
0.2 |
Opacity of grid lines |
| CSS Variable | Default | Description |
|---|---|---|
--jsc-tooltip-padding |
8px 12px |
Tooltip inner padding |
--jsc-tooltip-box-shadow |
0 2px 4px rgba(0,0,0,0.15) |
Tooltip drop shadow |
| CSS Variable | Default | Description |
|---|---|---|
--jsc-burger-menu-background |
#ffffff |
Burger menu dropdown background |
--jsc-burger-menu-border-color |
#bdbdbd |
Burger menu dropdown border color |
--jsc-burger-menu-border-radius |
18px |
Burger menu and menu item corner radius |
--jsc-burger-menu-shadow |
0 4px 16px rgba(0, 0, 0, 0.12) |
Burger menu dropdown shadow |
--jsc-burger-menu-item-hover-background |
#f5f5f5 |
Burger menu item hover background |
--jsc-burger-menu-item-active-background |
#eef5ff |
Burger menu active/focused item background |
--jsc-burger-menu-item-separator-color |
#e3e3e3 |
Burger menu item separator color |
/* Dark theme example — adjust values to your design system */
.dark-theme .chart-container {
--jsc-color-background: #1a1a2e;
--jsc-color-surface: #16213e;
--jsc-color-text: #e0e0e0;
--jsc-color-text-secondary: #a0a0a0;
--jsc-color-border: #3a3a5c;
--jsc-color-tick: #8888aa;
--jsc-color-focus-ring: #64b5f6;
--jsc-tooltip-box-shadow: 0 2px 8px rgba(0,0,0,0.4);
--jsc-series-1: #64b5f6;
--jsc-series-2: #ef5350;
--jsc-series-3: #66bb6a;
--jsc-series-4: #ffa726;
}Note: CSS custom property names may change before version 1.0.
The library auto-selects the best chart type based on dataset dimensions when chartType is not specified.
| Type | Value | Description |
|---|---|---|
| Line | 'line' |
Time series or ordinal progression |
| Vertical Bar | 'verticalBar' |
Single-series vertical bars |
| Horizontal Bar | 'horizontalBar' |
Single-series horizontal bars |
| Grouped Vertical Bar | 'groupedVerticalBar' |
Multi-series side-by-side vertical |
| Grouped Horizontal Bar | 'groupedHorizontalBar' |
Multi-series side-by-side horizontal |
| Stacked Vertical Bar | 'stackedVerticalBar' |
Multi-series stacked vertical |
| Stacked Horizontal Bar | 'stackedHorizontalBar' |
Multi-series stacked horizontal |
| Percent Vertical Bar | 'percentVerticalBar' |
Normalized 100% stacked vertical |
| Percent Horizontal Bar | 'percentHorizontalBar' |
Normalized 100% stacked horizontal |
| Pie | 'pie' |
Part-to-whole proportions |
| Scatter Plot | 'scatterPlot' |
Two-variable correlation |
| Pyramid | 'pyramid' |
Mirrored horizontal bars (e.g. age-sex) |
| Key Figure | 'keyFigure' |
Single-value display (eligible when all dimensions are single-valued after selection) |
| Table | 'table' |
HTML table fallback |
The key figure chart renders the numeric value and unit inside a shared wrapper element:
.jsc-key-figure
.jsc-key-figure-title (optional — rendered when title is set)
.jsc-key-figure-display ← value + unit wrapper
.jsc-key-figure-value
.jsc-key-figure-unit (optional — rendered when unit is non-empty)
.jsc-key-figure-footer (optional — rendered when footerItems are set)
By default the wrapper stacks value and unit vertically (flex-direction: column). Override it to display them in a row:
.jsc-key-figure-display {
flex-direction: row;
align-items: baseline;
gap: 0.25em;
}Key exports from the package.
JsonStatDataset— JSON-stat 2.0 dataset inputJsonStatDatasetExtension— Known dataset extension fields, including selectable settingsSelectableConfig— Selectable settings stored indataset.extension.selectableConfigSelectableSelections— Dimension-code to selected category-code arraysLayout— Row/column projection of active dimensionsResolvedDimensionView— Selected and pivoted view of the immutable source cubeChartConfig— Configuration options (see Configuration section)ChartInstance— Returned bycreateChart()ChartType— Union of all chart type stringsThemeConfig— Theme customization options
Creates a chart instance.
container: HTMLElement— DOM element to render intodataset: JsonStatDataset— JSON-stat 2.0 dataset objectconfig?: ChartConfig— Optional configurationselectableSelections?: SelectableSelections— Current category selections keyed by dimension code- Returns
ChartInstance
update(dataset, config?, selectableSelections?)— Re-render with new data, config, and/or current selectionsdestroy()— Clean up DOM and event listenerssetChartType(type)— Switch chart typegetChartType()— Get current chart typegetApplicableChartTypes()— Get all valid chart types with rejection reasons
validateDataset(dataset)— Validate a JSON-stat dataset before renderingresolveTheme(container, themeConfig?)— Resolve partial theme to full themegetChartTypesForDataset(dataset, options?)— Get all chart types with validity and rejection reasonsselectChartTypeForDataset(dataset, options?)— Get the auto-selected chart type
CSV, SVG, and PNG downloads are also available as named exports, so a consuming project can provide its own buttons or other controls without rendering the burger menu:
import {
createChart,
exportCsv,
exportPng,
exportSvg,
} from '@statisticsfinland/jsonstat-chart';
const chart = createChart(container, dataset, {
chartType: 'line',
locale: 'en',
showBurgerMenu: false,
});
// CSV uses the dataset and optional table layout directly.
exportCsv(dataset, 'en', { layout: { rows: ['region'], columns: ['year'] } });
// SVG and PNG export the chart currently rendered in `container`.
exportSvg(container, dataset, chart.getChartType());
await exportPng(container, dataset, chart.getChartType());exportCsv returns void, exportSvg returns true when a supported chart
was downloaded (otherwise false), and exportPng returns a Promise<boolean>
with the same success semantics. SVG and PNG return false for table and
keyFigure, and require an SVG-rendered chart to be present in the container.
Pass the same active layout and prepared dataset used for the rendered chart
when the export should reflect selectable or pivoted data.
Query which chart types are valid for a dataset without creating a chart. Useful for building chart-type picker UIs:
import { getChartTypesForDataset, selectChartTypeForDataset } from '@statisticsfinland/jsonstatgraphs';
// Get all chart types with validity status and rejection reasons
const results = getChartTypesForDataset(dataset);
// [
// { type: 'line', valid: true, rejectionReasons: [] },
// { type: 'verticalBar', valid: false, rejectionReasons: ['No time or ordinal axis dimension'] },
// { type: 'map', valid: true, rejectionReasons: [] },
// ...
// ]
// Filter to valid types for a chart-type switcher UI
const validTypes = results.filter(r => r.valid).map(r => r.type);
// Or just get the best auto-selected type
const bestType = selectChartTypeForDataset(dataset);Map eligibility normally requires async geometry resolution via mapProvider. These standalone functions handle map differently based on the mapAvailable option:
mapAvailable |
Behavior |
|---|---|
true |
Map geometry confirmed — map fully eligible |
false |
Map geometry unavailable — map rejected |
| omitted | Geometry check skipped — map eligible based on structural constraints only (geo dimension, sizes) |
// Structural check only (default) — map valid if dataset has geo dimension + correct shape
const results = getChartTypesForDataset(dataset);
// After resolving geometry yourself
const results = getChartTypesForDataset(dataset, { mapAvailable: true });Note: Elimination value detection uses a
'SSS'code heuristic suited for Statistics Finland data. For other data sources needing precise control, use the lower-levelgetApplicableChartTypes()with manually constructedDimensionMeta[].
- Visual charts use a named
role="region"; the chart title or configuredariaLabelis announced without nested image semantics. - The interactive SVG inside that region uses a localized, named
role="application", prompting screen readers such as NVDA to enter focus mode when focus reaches chart data. Table and key-figure views keep native document semantics. - Focusable datapoints use a localized
aria-roledescriptionand rovingtabindex. - Right and Down arrows move forward through data points; Left and Up arrows move backward. This order follows the chart's data sequence across series.
- Arrow navigation stops at chart boundaries. Home/End move within the current point sequence, and Tab/Shift+Tab leave the chart normally.
- The library does not generate hidden data tables. Applications should expose the chart menu's explicit table view or provide a visible table when an alternate data presentation is required.
- Interactive legend with
aria-pressedtoggle - Tooltips are not
aria-liveregions to avoid duplicate screen-reader announcements - Line charts use transparent focus and hover targets for every non-null point when visible accessibility markers are disabled.
- Node.js 18+
- npm 9+
git clone <repository-url>
cd jsonstatgraphs
npm installnpm test # Run tests
npm run build # Build library to dist/
npm run storybook # Launch Storybook dev server on http://localhost:6006
npm run pack # Build + create .tgz for local installStorybook provides a visual showcase of all chart types with real JSON-stat 2.0 data from Statistics Finland.
npm run storybookThis starts a dev server at http://localhost:6006 with hot reloading. Stories are in the stories/ directory and use fixture datasets from stories/fixtures/.
To build a static Storybook site:
npm run build-storybookApache-2.0