Storybook и документирование графиков

Библиотека Nivo ориентирована на создание переиспользуемых и визуально насыщенных графиков для React-приложений. При увеличении количества диаграмм, тем оформления, вариантов данных и интерактивных состояний возникает необходимость в отдельной среде документирования и тестирования компонентов. Эту задачу решает Storybook.

Storybook позволяет:

  • изолированно разрабатывать графики;
  • документировать API компонентов;
  • демонстрировать различные сценарии визуализации;
  • тестировать темы, анимации и адаптивность;
  • хранить примеры использования в едином каталоге;
  • создавать внутренние UI-библиотеки аналитических компонентов.

В связке с Nivo Storybook особенно полезен, поскольку графики обладают большим количеством параметров:

  • оси;
  • легенды;
  • цвета;
  • tooltip;
  • motion-анимации;
  • пользовательские слои;
  • responsive-режимы;
  • SVG и Canvas-варианты.

Установка Storybook в проект с Nivo

Для React-проекта установка выполняется через CLI:

npx storybook@latest init

После установки структура проекта дополняется каталогом:

.storybook/

И набором примеров:

src/stories/

Базовая структура 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 систем.


Создание первой Story для Nivo

Пример компонента:

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
    }
}

Организация категорий Storybook

Для Nivo-проектов важно правильно организовать иерархию stories.

Пример удачной структуры:

Charts/
├── Bar/
├── Line/
├── Pie/
├── Radar/
├── HeatMap/
├── Stream/
└── Geo/

Для корпоративных BI-систем:

Analytics/
├── Revenue/
├── Marketing/
├── Finance/
├── KPI/
└── Monitoring/

Документирование props графиков

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 для интерактивной документации

Args превращают story в живую песочницу.

export const Interactive = {
    args: {
        enableGridX: true,
        enableGridY: true,
        animate: true,
        curve: 'monotoneX'
    }
}

Преимущества:

  • быстрый подбор конфигурации;
  • визуальная документация;
  • проверка поведения графика;
  • генерация UI-состояний.

Работа с mock-данными

Для 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
    }
}

Интеграция тем Nivo со Storybook

Storybook позволяет централизованно тестировать темы.

Пример темы:

export const darkTheme = {
    background: '#1e1e1e',
    textColor: '#ffffff',
    axis: {
        ticks: {
            text: {
                fill: '#ffffff'
            }
        }
    },
    grid: {
        line: {
            stroke: '#444'
        }
    }
}

Использование:

<ResponsiveLine
    theme={darkTheme}
/>

Глобальные темы через decorators

В Storybook можно подключить глобальные декораторы.

export const decorators = [
    (Story) => (
        <div style={{
            background: '#111',
            padding: 20,
            minHeight: '100vh'
        }}>
            <Story />
        </div>
    )
]

Это особенно полезно для проверки контрастности графиков.


Документация Responsive-компонентов

Практически все компоненты Nivo используют Responsive API:

<ResponsiveBar />
<ResponsiveLine />
<ResponsivePie />

Для корректной работы Storybook необходимо задавать высоту контейнера.

Ошибка:

<ResponsiveBar data={data} />

Правильно:

<div style={{ height: 500 }}>
    <ResponsiveBar data={data} />
</div>

Создание Layout-компонентов для stories

Удобный подход — оборачивать графики в контейнер.

export const ChartContainer = ({ children }) => {
    return (
        <div
            style={{
                height: 500,
                padding: 20,
                background: '#fff'
            }}
        >
            {children}
        </div>
    )
}

Использование:

render: (args) => (
    <ChartContainer>
        <RevenueChart {...args} />
    </ChartContainer>
)

Autodocs в Storybook

Современный Storybook поддерживает автоматическую генерацию документации.

Пример:

export default {
    title: 'Charts/Pie/Traffic',
    component: TrafficPieChart,
    tags: ['autodocs']
}

Теперь Storybook автоматически создаёт:

  • таблицу props;
  • примеры args;
  • описание компонента;
  • controls.

Документирование TypeScript-интерфейсов

Для TypeScript-компонентов:

interface RevenueChartProps {
    data: RevenuePoint[]
    animate?: boolean
    enableGridX?: boolean
}

Storybook автоматически извлекает типы.


Использование MDX для расширенной документации

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>

Документирование пользовательских tooltip

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 помогает тестировать:

  • плавность;
  • задержки;
  • FPS;
  • поведение при обновлении данных.

Пример:

export const Animated = {
    args: {
        animate: true,
        motionConfig: 'gentle'
    }
}

Сравнение motionConfig

export const SlowMotion = {
    args: {
        motionConfig: 'slow'
    }
}

export const WobblyMotion = {
    args: {
        motionConfig: 'wobbly'
    }
}

Это особенно важно для аналитических dashboards.


Документирование Canvas-версий

Nivo предоставляет SVG и Canvas реализации.

Пример:

import { ResponsiveLineCanvas } from '@nivo/line'

Canvas-версии полезны для:

  • больших объёмов данных;
  • высокой производительности;
  • real-time dashboards.

Storybook позволяет сравнивать SVG и Canvas рядом.


Визуальное тестирование

Storybook часто интегрируют с:

  • Chromatic;
  • Loki;
  • Percy.

Это помогает обнаруживать:

  • регрессии тем;
  • ошибки рендера;
  • изменения layout;
  • проблемы responsive-режима.

Snapshot-подход для графиков

Графики сложнее тестировать snapshot-тестами из-за:

  • SVG-id;
  • анимаций;
  • случайных значений;
  • motion transitions.

Для стабильности рекомендуется:

animate={false}

Тестирование разных размеров контейнера

Storybook Viewport addon позволяет тестировать адаптивность.

Пример:

parameters: {
    viewport: {
        defaultViewport: 'mobile1'
    }
}

Полезно для:

  • mobile dashboards;
  • embedded analytics;
  • tablet layouts.

Проверка accessibility

Nivo поддерживает accessibility API.

Пример:

isFocusable={true}
role="application"

Storybook addon-a11y помогает проверять:

  • контрастность;
  • aria-атрибуты;
  • keyboard navigation.

Документирование событий

Графики часто используют callbacks:

onCl ick={handleClick}
onMouseEn ter={handleEnter}
onMouseLe ave={handleLeave}

В Storybook удобно логировать события через actions.

argTypes: {
    onClick: {
        action: 'clicked'
    }
}

Storybook addons для Nivo

Наиболее полезные addons:

Addon Назначение
essentials базовый набор
controls изменение props
viewport responsive
backgrounds темы
a11y accessibility
interactions интерактивные тесты

Использование backgrounds для проверки тем

parameters: {
    backgrounds: {
        default: 'dark',
        values: [
            { name: 'dark', value: '#111' },
            { name: 'light', value: '#fff' }
        ]
    }
}

Изоляция бизнес-логики от визуализации

Хорошая архитектура:

RevenueChart/
├── RevenueChart.jsx
├── RevenueChartView.jsx
├── useRevenueData.js
└── RevenueChart.stories.jsx

Storybook работает только с презентационным слоем.


Композиция stories

Storybook поддерживает композицию.

export const Dashboard = {
    render: () => (
        <>
            <RevenueChart />
            <TrafficChart />
            <KPIChart />
        </>
    )
}

Это помогает документировать целые аналитические экраны.


Документирование пользовательских слоёв Nivo

Nivo поддерживает custom layers.

Пример:

layers={[
    'grid',
    'axes',
    'bars',
    CustomLayer
]}

Storybook позволяет визуально проверять:

  • порядок слоёв;
  • overlay;
  • custom SVG;
  • аннотации.

Документирование легенд

Пример настройки:

legends={[
    {
        anchor: 'bottom-right',
        direction: 'column'
    }
]}

Полезно создавать отдельные stories:

export const LegendBottom = {}
export const LegendRight = {}
export const LegendHidden = {}

Проверка производительности

Storybook помогает анализировать:

  • скорость initial render;
  • перерисовки;
  • memoization;
  • lag при hover.

Особенно важно для:

  • HeatMap;
  • ScatterPlot;
  • Stream;
  • большие LineChart.

Использование play-функций

Storybook поддерживает интерактивные сценарии.

export const HoverState = {
    play: async ({ canvasElement }) => {
        // interaction test
    }
}

Это полезно для:

  • tooltip;
  • hover-состояний;
  • drill-down поведения.

Интеграция с дизайн-системой

Nivo-графики часто становятся частью UI-kit.

Пример:

UI Kit/
├── Typography/
├── Colors/
├── Charts/
├── Tables/
└── Forms/

Storybook превращается в единый портал документации.


Документирование KPI-дашбордов

Пример composite-story:

export const AnalyticsDashboard = {
    render: () => (
        <DashboardGrid>
            <RevenueChart />
            <TrafficChart />
            <ConversionChart />
            <GeoChart />
        </DashboardGrid>
    )
}

Такой подход позволяет проверять:

  • согласованность тем;
  • spacing;
  • визуальную иерархию;
  • композицию dashboard-элементов.

Оптимизация stories для больших проектов

При росте количества графиков важно:

  • выносить mock-данные;
  • переиспользовать decorators;
  • стандартизировать layout;
  • хранить темы централизованно;
  • избегать дублирования args.

Практика создания template stories

const Template = (args) => (
    <ChartContainer>
        <RevenueChart {...args} />
    </ChartContainer>
)

На основе шаблона:

export const Default = Template.bind({})

Default.args = {
    animate: true
}

Документирование real-time графиков

Для real-time charts полезно создавать stories с обновлением данных.

useEffect(() => {
    const timer = setInterval(() => {
        setData(generateData())
    }, 1000)

    return () => clearInterval(timer)
}, [])

Storybook помогает анализировать:

  • утечки памяти;
  • FPS;
  • плавность transitions;
  • нагрузку CPU.

Организация enterprise-каталога графиков

В крупных системах встречается структура:

Charts/
├── Financial/
├── Monitoring/
├── Operational/
├── Marketing/
├── Infrastructure/
└── Experimental/

Это облегчает навигацию между десятками visual-компонентов.


Частые проблемы при использовании Storybook с Nivo

Отсутствие высоты контейнера

Наиболее распространённая ошибка.

<div style={{ height: 400 }}>

обязателен для Responsive API.


Hydration mismatch

При SSR возможно различие между серверным и клиентским render.

Решение:

dynamic(() => import('./Chart'), {
    ssr: false
})

Проблемы с ResizeObserver

Иногда требуется polyfill:

npm install resize-observer-polyfill

Медленная работа больших datasets

Решения:

  • Canvas API;
  • memoization;
  • отключение анимаций;
  • virtualized rendering.

Практика документирования корпоративных графиков

Полезно фиксировать:

  • назначение графика;
  • допустимые форматы данных;
  • ограничения;
  • performance limits;
  • особенности accessibility;
  • responsive-поведение;
  • совместимость тем.

Storybook становится не просто средой разработки, а полноценной инженерной документацией визуальной аналитики.