Библиотека Nivo ориентирована на создание переиспользуемых и визуально насыщенных графиков для React-приложений. При увеличении количества диаграмм, тем оформления, вариантов данных и интерактивных состояний возникает необходимость в отдельной среде документирования и тестирования компонентов. Эту задачу решает Storybook.
Storybook позволяет:
В связке с Nivo Storybook особенно полезен, поскольку графики обладают большим количеством параметров:
Для React-проекта установка выполняется через CLI:
npx storybook@latest init
После установки структура проекта дополняется каталогом:
.storybook/
И набором примеров:
src/stories/
Типичная структура для графиков Nivo:
src/
├── components/
│ ├── charts/
│ │ ├── SalesBarChart.jsx
│ │ ├── RevenueLineChart.jsx
│ │ └── PieTrafficChart.jsx
│
├── stories/
│ ├── BarChart.stories.jsx
│ ├── LineChart.stories.jsx
│ └── PieChart.stories.jsx
Более масштабируемый вариант:
src/
├── charts/
│ ├── Bar/
│ │ ├── BarChart.jsx
│ │ ├── BarChart.stories.jsx
│ │ └── mock.js
│ │
│ ├── Line/
│ └── Pie/
Подход colocated stories особенно удобен для больших UI-kit систем.
Пример компонента:
import { ResponsiveBar } from '@nivo/bar'
export const SalesBarChart = ({ data }) => {
return (
<div style={{ height: 400 }}>
<ResponsiveBar
data={data}
keys={['sales']}
indexBy="month"
margin={{ top: 40, right: 20, bottom: 50, left: 60 }}
padding={0.3}
colors={{ scheme: 'nivo' }}
axisBottom={{
legend: 'Месяц',
legendPosition: 'middle',
legendOffset: 32
}}
axisLeft={{
legend: 'Продажи',
legendPosition: 'middle',
legendOffset: -40
}}
/>
</div>
)
}
Story:
import { SalesBarChart } from './SalesBarChart'
export default {
title: 'Charts/Bar/SalesBarChart',
component: SalesBarChart
}
const sampleData = [
{ month: 'Jan', sales: 120 },
{ month: 'Feb', sales: 180 },
{ month: 'Mar', sales: 90 },
{ month: 'Apr', sales: 210 }
]
export const Default = {
args: {
data: sampleData
}
}
Для Nivo-проектов важно правильно организовать иерархию stories.
Пример удачной структуры:
Charts/
├── Bar/
├── Line/
├── Pie/
├── Radar/
├── HeatMap/
├── Stream/
└── Geo/
Для корпоративных BI-систем:
Analytics/
├── Revenue/
├── Marketing/
├── Finance/
├── KPI/
└── Monitoring/
Storybook автоматически отображает аргументы компонентов через Controls.
Пример:
export default {
title: 'Charts/Line/Revenue',
component: RevenueChart,
argTypes: {
enableGridX: {
control: 'boolean'
},
curve: {
control: 'sel ect',
options: [
'linear',
'monotoneX',
'step',
'basis'
]
},
colors: {
control: 'color'
}
}
}
Теперь параметры можно изменять прямо из UI Storybook.
Args превращают story в живую песочницу.
export const Interactive = {
args: {
enableGridX: true,
enableGridY: true,
animate: true,
curve: 'monotoneX'
}
}
Преимущества:
Для Storybook крайне важно иметь стабильные mock datasets.
Пример:
export const revenueData = [
{
id: 'revenue',
data: [
{ x: 'Jan', y: 120 },
{ x: 'Feb', y: 180 },
{ x: 'Mar', y: 240 }
]
}
]
Лучше хранить mock-данные отдельно:
charts/
├── Revenue/
│ ├── RevenueChart.jsx
│ ├── RevenueChart.stories.jsx
│ ├── revenue.mock.js
│ └── revenue.theme.js
Storybook особенно полезен для фиксации edge-cases.
export const Empty = {
args: {
data: []
}
}
export const LargeDataset = {
args: {
data: massiveDataset
}
}
export const InvalidData = {
args: {
data: malformedDataset
}
}
export const DarkTheme = {
args: {
theme: darkTheme
}
}
Storybook позволяет централизованно тестировать темы.
Пример темы:
export const darkTheme = {
background: '#1e1e1e',
textColor: '#ffffff',
axis: {
ticks: {
text: {
fill: '#ffffff'
}
}
},
grid: {
line: {
stroke: '#444'
}
}
}
Использование:
<ResponsiveLine
theme={darkTheme}
/>
В Storybook можно подключить глобальные декораторы.
export const decorators = [
(Story) => (
<div style={{
background: '#111',
padding: 20,
minHeight: '100vh'
}}>
<Story />
</div>
)
]
Это особенно полезно для проверки контрастности графиков.
Практически все компоненты Nivo используют Responsive API:
<ResponsiveBar />
<ResponsiveLine />
<ResponsivePie />
Для корректной работы Storybook необходимо задавать высоту контейнера.
Ошибка:
<ResponsiveBar data={data} />
Правильно:
<div style={{ height: 500 }}>
<ResponsiveBar data={data} />
</div>
Удобный подход — оборачивать графики в контейнер.
export const ChartContainer = ({ children }) => {
return (
<div
style={{
height: 500,
padding: 20,
background: '#fff'
}}
>
{children}
</div>
)
}
Использование:
render: (args) => (
<ChartContainer>
<RevenueChart {...args} />
</ChartContainer>
)
Современный Storybook поддерживает автоматическую генерацию документации.
Пример:
export default {
title: 'Charts/Pie/Traffic',
component: TrafficPieChart,
tags: ['autodocs']
}
Теперь Storybook автоматически создаёт:
Для TypeScript-компонентов:
interface RevenueChartProps {
data: RevenuePoint[]
animate?: boolean
enableGridX?: boolean
}
Storybook автоматически извлекает типы.
MDX позволяет объединять markdown и stories.
Пример:
import { Meta, Story, Canvas } fr om '@storybook/blocks'
import * as RevenueStories from './RevenueChart.stories'
<Meta of={RevenueStories} />
# Revenue Chart
Описание графика выручки.
<Canvas>
<Story of={RevenueStories.Default} />
</Canvas>
Nivo активно использует кастомные tooltip-компоненты.
Пример:
const CustomTooltip = ({ point }) => {
return (
<div
style={{
background: '#222',
color: '#fff',
padding: 12
}}
>
<strong>{point.data.x}</strong>
<div>{point.data.y}</div>
</div>
)
}
Storybook позволяет визуально тестировать tooltip в изоляции.
Nivo использует react-spring для motion-анимаций.
Storybook помогает тестировать:
Пример:
export const Animated = {
args: {
animate: true,
motionConfig: 'gentle'
}
}
export const SlowMotion = {
args: {
motionConfig: 'slow'
}
}
export const WobblyMotion = {
args: {
motionConfig: 'wobbly'
}
}
Это особенно важно для аналитических dashboards.
Nivo предоставляет SVG и Canvas реализации.
Пример:
import { ResponsiveLineCanvas } from '@nivo/line'
Canvas-версии полезны для:
Storybook позволяет сравнивать SVG и Canvas рядом.
Storybook часто интегрируют с:
Это помогает обнаруживать:
Графики сложнее тестировать snapshot-тестами из-за:
Для стабильности рекомендуется:
animate={false}
Storybook Viewport addon позволяет тестировать адаптивность.
Пример:
parameters: {
viewport: {
defaultViewport: 'mobile1'
}
}
Полезно для:
Nivo поддерживает accessibility API.
Пример:
isFocusable={true}
role="application"
Storybook addon-a11y помогает проверять:
Графики часто используют callbacks:
onCl ick={handleClick}
onMouseEn ter={handleEnter}
onMouseLe ave={handleLeave}
В Storybook удобно логировать события через actions.
argTypes: {
onClick: {
action: 'clicked'
}
}
Наиболее полезные addons:
| Addon | Назначение |
|---|---|
| essentials | базовый набор |
| controls | изменение props |
| viewport | responsive |
| backgrounds | темы |
| a11y | accessibility |
| interactions | интерактивные тесты |
parameters: {
backgrounds: {
default: 'dark',
values: [
{ name: 'dark', value: '#111' },
{ name: 'light', value: '#fff' }
]
}
}
Хорошая архитектура:
RevenueChart/
├── RevenueChart.jsx
├── RevenueChartView.jsx
├── useRevenueData.js
└── RevenueChart.stories.jsx
Storybook работает только с презентационным слоем.
Storybook поддерживает композицию.
export const Dashboard = {
render: () => (
<>
<RevenueChart />
<TrafficChart />
<KPIChart />
</>
)
}
Это помогает документировать целые аналитические экраны.
Nivo поддерживает custom layers.
Пример:
layers={[
'grid',
'axes',
'bars',
CustomLayer
]}
Storybook позволяет визуально проверять:
Пример настройки:
legends={[
{
anchor: 'bottom-right',
direction: 'column'
}
]}
Полезно создавать отдельные stories:
export const LegendBottom = {}
export const LegendRight = {}
export const LegendHidden = {}
Storybook помогает анализировать:
Особенно важно для:
Storybook поддерживает интерактивные сценарии.
export const HoverState = {
play: async ({ canvasElement }) => {
// interaction test
}
}
Это полезно для:
Nivo-графики часто становятся частью UI-kit.
Пример:
UI Kit/
├── Typography/
├── Colors/
├── Charts/
├── Tables/
└── Forms/
Storybook превращается в единый портал документации.
Пример composite-story:
export const AnalyticsDashboard = {
render: () => (
<DashboardGrid>
<RevenueChart />
<TrafficChart />
<ConversionChart />
<GeoChart />
</DashboardGrid>
)
}
Такой подход позволяет проверять:
При росте количества графиков важно:
const Template = (args) => (
<ChartContainer>
<RevenueChart {...args} />
</ChartContainer>
)
На основе шаблона:
export const Default = Template.bind({})
Default.args = {
animate: true
}
Для real-time charts полезно создавать stories с обновлением данных.
useEffect(() => {
const timer = setInterval(() => {
setData(generateData())
}, 1000)
return () => clearInterval(timer)
}, [])
Storybook помогает анализировать:
В крупных системах встречается структура:
Charts/
├── Financial/
├── Monitoring/
├── Operational/
├── Marketing/
├── Infrastructure/
└── Experimental/
Это облегчает навигацию между десятками visual-компонентов.
Наиболее распространённая ошибка.
<div style={{ height: 400 }}>
обязателен для Responsive API.
При SSR возможно различие между серверным и клиентским render.
Решение:
dynamic(() => import('./Chart'), {
ssr: false
})
Иногда требуется polyfill:
npm install resize-observer-polyfill
Решения:
Полезно фиксировать:
Storybook становится не просто средой разработки, а полноценной инженерной документацией визуальной аналитики.