From ffdcabf48a03b319bd12ead007ea156b3100b4f3 Mon Sep 17 00:00:00 2001 From: Nate Kelley Date: Wed, 12 Mar 2025 10:59:40 -0600 Subject: [PATCH] created many line chart stories --- .../charts/BusterChart.BarChart.stories.tsx | 50 +++ .../charts/BusterChart.LineChart.stories.tsx | 373 ++++++++++++++++++ .../charts/BusterChart.PieChart.stories.tsx | 41 ++ .../BusterChart.ScatterChart.stories.tsx | 53 +++ .../ui/charts/BusterChart.shared.ts | 42 ++ .../ui/charts/BusterChart.stories.tsx | 191 --------- 6 files changed, 559 insertions(+), 191 deletions(-) create mode 100644 web/src/components/ui/charts/BusterChart.BarChart.stories.tsx create mode 100644 web/src/components/ui/charts/BusterChart.LineChart.stories.tsx create mode 100644 web/src/components/ui/charts/BusterChart.PieChart.stories.tsx create mode 100644 web/src/components/ui/charts/BusterChart.ScatterChart.stories.tsx create mode 100644 web/src/components/ui/charts/BusterChart.shared.ts delete mode 100644 web/src/components/ui/charts/BusterChart.stories.tsx diff --git a/web/src/components/ui/charts/BusterChart.BarChart.stories.tsx b/web/src/components/ui/charts/BusterChart.BarChart.stories.tsx new file mode 100644 index 000000000..81bffd5fa --- /dev/null +++ b/web/src/components/ui/charts/BusterChart.BarChart.stories.tsx @@ -0,0 +1,50 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { BusterChart } from './BusterChart'; +import { ChartType } from '../../../api/asset_interfaces/metric/charts/enum'; +import { IColumnLabelFormat } from '../../../api/asset_interfaces/metric/charts/columnLabelInterfaces'; +import { generateBarChartData } from '../../../mocks/chart/chartMocks'; +import { sharedMeta } from './BusterChart.shared'; + +type BarChartData = ReturnType[0]; + +const meta: Meta = { + ...sharedMeta, + title: 'UI/Charts/BusterChart/Bar' +} as Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + selectedChartType: ChartType.Bar, + data: generateBarChartData(), + barAndLineAxis: { + x: ['category'], + y: ['sales', 'units', 'returns'], + category: [] + }, + columnLabelFormats: { + category: { + columnType: 'text', + style: 'string' + } satisfies IColumnLabelFormat, + sales: { + columnType: 'number', + style: 'currency', + currency: 'USD' + } satisfies IColumnLabelFormat, + units: { + columnType: 'number', + style: 'number', + numberSeparatorStyle: ',' + } satisfies IColumnLabelFormat, + returns: { + columnType: 'number', + style: 'number', + numberSeparatorStyle: ',' + } satisfies IColumnLabelFormat + } satisfies Record, + className: 'w-[800px] h-[400px]' + } +}; diff --git a/web/src/components/ui/charts/BusterChart.LineChart.stories.tsx b/web/src/components/ui/charts/BusterChart.LineChart.stories.tsx new file mode 100644 index 000000000..300902c16 --- /dev/null +++ b/web/src/components/ui/charts/BusterChart.LineChart.stories.tsx @@ -0,0 +1,373 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { BusterChart } from './BusterChart'; +import { ChartType } from '../../../api/asset_interfaces/metric/charts/enum'; +import { IColumnLabelFormat } from '../../../api/asset_interfaces/metric/charts/columnLabelInterfaces'; +import { generateLineChartData } from '../../../mocks/chart/chartMocks'; +import { sharedMeta } from './BusterChart.shared'; + +type LineChartData = ReturnType[0]; + +const meta: Meta = { + ...sharedMeta, + title: 'UI/Charts/BusterChart/Line' +} as Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + selectedChartType: ChartType.Line, + data: generateLineChartData(), + barAndLineAxis: { + x: ['date'], + y: ['revenue', 'profit', 'customers'], + category: [] + }, + className: 'w-[800px] h-[400px]', + columnLabelFormats: { + date: { + columnType: 'date', + style: 'date', + dateFormat: 'MMM DD' + } satisfies IColumnLabelFormat, + revenue: { + columnType: 'number', + style: 'currency', + currency: 'USD' + } satisfies IColumnLabelFormat, + profit: { + columnType: 'number', + style: 'currency', + currency: 'USD' + } satisfies IColumnLabelFormat, + customers: { + columnType: 'number', + style: 'number', + numberSeparatorStyle: ',' + } satisfies IColumnLabelFormat + } satisfies Record + } +}; + +// Simple X and Y axis with numeric values +export const NumericXY: Story = { + args: { + selectedChartType: ChartType.Line, + data: [ + { score: 10, value: 100 }, + { score: 20, value: 200 }, + { score: 30, value: 150 }, + { score: 40, value: 300 }, + { score: 50, value: 250 } + ], + barAndLineAxis: { + x: ['score'], + y: ['value'], + category: [] + }, + className: 'w-[800px] h-[400px]', + columnLabelFormats: { + score: { + columnType: 'number', + style: 'number' + } satisfies IColumnLabelFormat, + value: { + columnType: 'number', + style: 'number' + } satisfies IColumnLabelFormat + } + } +}; + +// X axis with categorical data and Y axis with numeric values +export const CategoricalXNumericY: Story = { + args: { + selectedChartType: ChartType.Line, + data: [ + { category: 'A', value: 100 }, + { category: 'B', value: 200 }, + { category: 'C', value: 150 }, + { category: 'D', value: 300 }, + { category: 'E', value: 250 } + ], + barAndLineAxis: { + x: ['category'], + y: ['value'], + category: [] + }, + className: 'w-[800px] h-[400px]', + columnLabelFormats: { + category: { + columnType: 'string', + style: 'string' + } satisfies IColumnLabelFormat, + value: { + columnType: 'number', + style: 'number' + } satisfies IColumnLabelFormat + } + } +}; + +// Multi-year date range +export const MultiYearDate: Story = { + args: { + selectedChartType: ChartType.Line, + data: [ + { date: new Date('2020-01-01'), value: 100 }, + { date: new Date('2021-01-01'), value: 150 }, + { date: new Date('2022-01-01'), value: 200 }, + { date: new Date('2023-01-01'), value: 250 }, + { date: new Date('2024-01-01'), value: 300 } + ], + barAndLineAxis: { + x: ['date'], + y: ['value'], + category: [] + }, + className: 'w-[800px] h-[400px]', + columnLabelFormats: { + date: { + columnType: 'date', + style: 'date', + dateFormat: 'YYYY' // Show only year for multi-year view + } satisfies IColumnLabelFormat, + value: { + columnType: 'number', + style: 'number' + } satisfies IColumnLabelFormat + } + } +}; + +// Multiple Y axes with date X axis +export const MultipleYAxes: Story = { + args: { + selectedChartType: ChartType.Line, + data: [ + { + date: new Date('2024-01-01'), + revenue: 1000, + units: 50, + satisfaction: 4.5 + }, + { + date: new Date('2024-02-01'), + revenue: 1200, + units: 60, + satisfaction: 4.7 + }, + { + date: new Date('2024-03-01'), + revenue: 1400, + units: 70, + satisfaction: 4.8 + } + ], + barAndLineAxis: { + x: ['date'], + y: ['revenue', 'units', 'satisfaction'], + category: [] + }, + className: 'w-[800px] h-[400px]', + columnLabelFormats: { + date: { + columnType: 'date', + style: 'date', + dateFormat: 'MMM YYYY' + } satisfies IColumnLabelFormat, + revenue: { + columnType: 'number', + style: 'currency', + currency: 'USD' + } satisfies IColumnLabelFormat, + units: { + columnType: 'number', + style: 'number' + } satisfies IColumnLabelFormat, + satisfaction: { + columnType: 'number', + style: 'number', + minimumFractionDigits: 1, + maximumFractionDigits: 1 + } satisfies IColumnLabelFormat + } + } +}; + +// Unevenly spaced dates +export const UnevenlySpacedDates: Story = { + args: { + selectedChartType: ChartType.Line, + data: [ + { date: new Date('2024-01-05'), value: 120 }, + { date: new Date('2024-01-28'), value: 145 }, + { date: new Date('2024-02-15'), value: 160 }, + { date: new Date('2024-03-02'), value: 155 }, + { date: new Date('2024-04-18'), value: 180 }, + { date: new Date('2024-05-30'), value: 210 }, + { date: new Date('2024-07-12'), value: 195 }, + { date: new Date('2024-08-03'), value: 225 }, + { date: new Date('2024-09-22'), value: 240 }, + { date: new Date('2024-11-15'), value: 260 }, + { date: new Date('2024-12-28'), value: 280 }, + { date: new Date('2025-04-08'), value: 310 } + ], + barAndLineAxis: { + x: ['date'], + y: ['value'], + category: [] + }, + className: 'w-[800px] h-[400px]', + columnLabelFormats: { + date: { + columnType: 'date', + style: 'date', + dateFormat: 'll' // Full date format to show uneven spacing clearly + } satisfies IColumnLabelFormat, + value: { + columnType: 'number', + style: 'number', + minimumFractionDigits: 0, + maximumFractionDigits: 0 + } satisfies IColumnLabelFormat + } + } +}; + +// X, Y, and Category axes combined +export const WithCategory: Story = { + args: { + selectedChartType: ChartType.Line, + data: [ + { month: new Date('2024-01-01'), sales: 1200, region: 'North' }, + { month: new Date('2024-02-01'), sales: 1400, region: 'North' }, + { month: new Date('2024-03-01'), sales: 1600, region: 'North' }, + { month: new Date('2024-01-01'), sales: 800, region: 'South' }, + { month: new Date('2024-02-01'), sales: 900, region: 'South' }, + { month: new Date('2024-03-01'), sales: 1100, region: 'South' }, + { month: new Date('2024-01-01'), sales: 1500, region: 'East' }, + { month: new Date('2024-02-01'), sales: 1700, region: 'East' }, + { month: new Date('2024-03-01'), sales: 1900, region: 'East' }, + { month: new Date('2024-01-01'), sales: 1000, region: 'West' }, + { month: new Date('2024-02-01'), sales: 1300, region: 'West' }, + { month: new Date('2024-03-01'), sales: 1500, region: 'West' } + ], + barAndLineAxis: { + x: ['month'], + y: ['sales'], + category: ['region'] + }, + className: 'w-[800px] h-[400px]', + columnLabelFormats: { + month: { + columnType: 'date', + style: 'date', + dateFormat: 'MMM YYYY' + } satisfies IColumnLabelFormat, + sales: { + columnType: 'number', + style: 'currency', + currency: 'USD' + } satisfies IColumnLabelFormat, + region: { + columnType: 'string', + style: 'string' + } satisfies IColumnLabelFormat + } + } +}; + +// Multiple Y axes with Category +export const MultipleYAxesWithCategory: Story = { + args: { + selectedChartType: ChartType.Line, + data: [ + { date: new Date('2024-01-01'), revenue: 1200, satisfaction: 4.2, product: 'Hardware' }, + { date: new Date('2024-02-01'), revenue: 1400, satisfaction: 4.3, product: 'Hardware' }, + { date: new Date('2024-03-01'), revenue: 1600, satisfaction: 4.4, product: 'Hardware' }, + { date: new Date('2024-01-01'), revenue: 800, satisfaction: 4.7, product: 'Software' }, + { date: new Date('2024-02-01'), revenue: 1000, satisfaction: 4.8, product: 'Software' }, + { date: new Date('2024-03-01'), revenue: 1200, satisfaction: 4.9, product: 'Software' }, + { date: new Date('2024-01-01'), revenue: 2000, satisfaction: 4.0, product: 'Services' }, + { date: new Date('2024-02-01'), revenue: 2200, satisfaction: 4.1, product: 'Services' }, + { date: new Date('2024-03-01'), revenue: 2400, satisfaction: 4.2, product: 'Services' } + ], + barAndLineAxis: { + x: ['date'], + y: ['revenue', 'satisfaction'], + category: ['product'] + }, + className: 'w-[800px] h-[400px]', + columnLabelFormats: { + date: { + columnType: 'date', + style: 'date', + dateFormat: 'MMM YYYY' + } satisfies IColumnLabelFormat, + revenue: { + columnType: 'number', + style: 'currency', + currency: 'USD' + } satisfies IColumnLabelFormat, + satisfaction: { + columnType: 'number', + style: 'number', + minimumFractionDigits: 1, + maximumFractionDigits: 1 + } satisfies IColumnLabelFormat, + product: { + columnType: 'string', + style: 'string' + } satisfies IColumnLabelFormat + } + } +}; + +// Numeric month X axis +export const NumericMonthX: Story = { + args: { + selectedChartType: ChartType.Line, + data: [ + { month: 1, sales: 1000, customers: 150 }, + { month: 2, sales: 1200, customers: 180 }, + { month: 3, sales: 1100, customers: 165 }, + { month: 4, sales: 1400, customers: 200 }, + { month: 5, sales: 1600, customers: 220 }, + { month: 6, sales: 1800, customers: 240 }, + { month: 7, sales: 2000, customers: 260 }, + { month: 8, sales: 2200, customers: 280 }, + { month: 9, sales: 2100, customers: 270 }, + { month: 10, sales: 1900, customers: 250 }, + { month: 11, sales: 2300, customers: 290 }, + { month: 12, sales: 2500, customers: 300 } + ], + barAndLineAxis: { + x: ['month'], + y: ['sales', 'customers'], + category: [] + }, + className: 'w-[800px] h-[400px]', + + columnLabelFormats: { + month: { + columnType: 'number', + style: 'date', + dateFormat: 'MMM', + minimumFractionDigits: 0, + maximumFractionDigits: 0 + } satisfies IColumnLabelFormat, + sales: { + columnType: 'number', + style: 'currency', + currency: 'USD' + } satisfies IColumnLabelFormat, + customers: { + columnType: 'number', + style: 'number', + numberSeparatorStyle: ',' + } satisfies IColumnLabelFormat + } + } +}; diff --git a/web/src/components/ui/charts/BusterChart.PieChart.stories.tsx b/web/src/components/ui/charts/BusterChart.PieChart.stories.tsx new file mode 100644 index 000000000..799806e9d --- /dev/null +++ b/web/src/components/ui/charts/BusterChart.PieChart.stories.tsx @@ -0,0 +1,41 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { BusterChart } from './BusterChart'; +import { ChartType } from '../../../api/asset_interfaces/metric/charts/enum'; +import { IColumnLabelFormat } from '../../../api/asset_interfaces/metric/charts/columnLabelInterfaces'; +import { generatePieChartData } from '../../../mocks/chart/chartMocks'; +import { sharedMeta } from './BusterChart.shared'; + +type PieChartData = ReturnType[0]; + +const meta: Meta = { + ...sharedMeta, + title: 'UI/Charts/BusterChart/Pie' +} as Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + selectedChartType: ChartType.Pie, + data: generatePieChartData(), + pieChartAxis: { + x: ['segment'], + y: ['value'] + }, + columnLabelFormats: { + segment: { + columnType: 'text', + style: 'string' + } satisfies IColumnLabelFormat, + value: { + columnType: 'number', + style: 'number', + numberSeparatorStyle: ',' + } satisfies IColumnLabelFormat + } satisfies Record, + pieDisplayLabelAs: 'percent', + pieDonutWidth: 0, + className: 'w-[500px] h-[500px]' + } +}; diff --git a/web/src/components/ui/charts/BusterChart.ScatterChart.stories.tsx b/web/src/components/ui/charts/BusterChart.ScatterChart.stories.tsx new file mode 100644 index 000000000..b2bac82f4 --- /dev/null +++ b/web/src/components/ui/charts/BusterChart.ScatterChart.stories.tsx @@ -0,0 +1,53 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { BusterChart } from './BusterChart'; +import { ChartType } from '../../../api/asset_interfaces/metric/charts/enum'; +import { IColumnLabelFormat } from '../../../api/asset_interfaces/metric/charts/columnLabelInterfaces'; +import { generateScatterChartData } from '../../../mocks/chart/chartMocks'; +import { sharedMeta } from './BusterChart.shared'; + +type ScatterChartData = ReturnType[0]; + +const meta: Meta = { + ...sharedMeta, + title: 'UI/Charts/BusterChart/Scatter' +} as Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + selectedChartType: ChartType.Scatter, + data: generateScatterChartData(), + scatterAxis: { + x: ['x'], + y: ['y'], + size: ['size'], + category: ['category'] + }, + columnLabelFormats: { + x: { + columnType: 'number', + style: 'number', + minimumFractionDigits: 1, + maximumFractionDigits: 1 + } satisfies IColumnLabelFormat, + y: { + columnType: 'number', + style: 'number', + minimumFractionDigits: 1, + maximumFractionDigits: 1 + } satisfies IColumnLabelFormat, + size: { + columnType: 'number', + style: 'number', + numberSeparatorStyle: ',' + } satisfies IColumnLabelFormat, + category: { + columnType: 'text', + style: 'string' + } satisfies IColumnLabelFormat + } satisfies Record, + className: 'w-[800px] h-[600px]' + } +}; diff --git a/web/src/components/ui/charts/BusterChart.shared.ts b/web/src/components/ui/charts/BusterChart.shared.ts new file mode 100644 index 000000000..e7a6b51ed --- /dev/null +++ b/web/src/components/ui/charts/BusterChart.shared.ts @@ -0,0 +1,42 @@ +import type { Meta } from '@storybook/react'; +import { BusterChart } from './BusterChart'; +import { ChartType } from '../../../api/asset_interfaces/metric/charts/enum'; +import { DEFAULT_CHART_CONFIG } from '../../../api/asset_interfaces/metric/defaults'; + +export const sharedMeta: Partial> = { + component: BusterChart, + parameters: { + layout: 'centered' + }, + argTypes: { + colors: { + description: + 'Array of colors to be used for the chart series. If not provided, defaults to the theme colors.', + control: { type: 'object' }, + table: { + type: { summary: 'string[]' }, + defaultValue: { summary: 'DEFAULT_CHART_THEME' } + } + }, + selectedChartType: { + control: 'select', + description: 'The type of chart to display.', + defaultValue: ChartType.Table, + options: Object.values(ChartType) + }, + xAxisTimeInterval: { + control: 'select', + description: + 'Time interval for x-axis when displaying time series data. Only applies to combo and line charts.', + options: ['day', 'week', 'month', 'quarter', 'year', null], + table: { + type: { summary: "'day' | 'week' | 'month' | 'quarter' | 'year' | null" }, + defaultValue: { summary: 'null' } + } + } + }, + args: { + ...DEFAULT_CHART_CONFIG, + className: 'w-[800px] h-[400px]' + } +}; diff --git a/web/src/components/ui/charts/BusterChart.stories.tsx b/web/src/components/ui/charts/BusterChart.stories.tsx deleted file mode 100644 index ac0ec13d2..000000000 --- a/web/src/components/ui/charts/BusterChart.stories.tsx +++ /dev/null @@ -1,191 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; -import { BusterChart } from './BusterChart'; -import { ChartType } from '../../../api/asset_interfaces/metric/charts/enum'; -import { IColumnLabelFormat } from '../../../api/asset_interfaces/metric/charts/columnLabelInterfaces'; -import { DEFAULT_CHART_CONFIG } from '../../../api/asset_interfaces/metric/defaults'; -import { - generateBarChartData, - generateLineChartData, - generatePieChartData, - generateScatterChartData -} from '../../../mocks/chart/chartMocks'; - -// Type-safe column label formats for each chart type -type LineChartData = ReturnType[0]; -type BarChartData = ReturnType[0]; -type PieChartData = ReturnType[0]; -type ScatterChartData = ReturnType[0]; - -const meta: Meta = { - title: 'UI/Charts/BusterChart', - component: BusterChart, - parameters: { - layout: 'centered' - }, - argTypes: { - colors: { - description: - 'Array of colors to be used for the chart series. If not provided, defaults to the theme colors.', - control: 'array', - table: { - type: { summary: 'string[]' }, - defaultValue: { summary: 'DEFAULT_CHART_THEME' } - } - }, - selectedChartType: { - control: 'select', - description: 'The type of chart to display.', - defaultValue: ChartType.Table, - options: Object.values(ChartType) - }, - xAxisTimeInterval: { - control: 'select', - description: - 'Time interval for x-axis when displaying time series data. Only applies to combo and line charts.', - options: ['day', 'week', 'month', 'quarter', 'year', null], - table: { - type: { summary: "'day' | 'week' | 'month' | 'quarter' | 'year' | null" }, - defaultValue: { summary: 'null' } - } - } - }, - args: { - ...DEFAULT_CHART_CONFIG, - className: 'w-[800px] h-[400px]' - } -}; - -export default meta; -type Story = StoryObj; - -export const LineChart: Story = { - argTypes: meta.argTypes, - args: { - selectedChartType: ChartType.Line, - data: generateLineChartData(), - barAndLineAxis: { - x: ['date'], - y: ['revenue', 'profit', 'customers'], - category: [] - }, - className: 'w-[800px] h-[400px]', - columnLabelFormats: { - date: { - columnType: 'date', - style: 'date', - dateFormat: 'MMM DD' - } satisfies IColumnLabelFormat, - revenue: { - columnType: 'number', - style: 'currency', - currency: 'USD' - } satisfies IColumnLabelFormat, - profit: { - columnType: 'number', - style: 'currency', - currency: 'USD' - } satisfies IColumnLabelFormat, - customers: { - columnType: 'number', - style: 'number', - numberSeparatorStyle: ',' - } satisfies IColumnLabelFormat - } satisfies Record - } -}; - -export const BarChart: Story = { - args: { - selectedChartType: ChartType.Bar, - data: generateBarChartData(), - barAndLineAxis: { - x: ['category'], - y: ['sales', 'units', 'returns'], - category: [] - }, - columnLabelFormats: { - category: { - columnType: 'text', - style: 'string' - } satisfies IColumnLabelFormat, - sales: { - columnType: 'number', - style: 'currency', - currency: 'USD' - } satisfies IColumnLabelFormat, - units: { - columnType: 'number', - style: 'number', - numberSeparatorStyle: ',' - } satisfies IColumnLabelFormat, - returns: { - columnType: 'number', - style: 'number', - numberSeparatorStyle: ',' - } satisfies IColumnLabelFormat - } satisfies Record, - className: 'w-[800px] h-[400px]' - } -}; - -export const PieChart: Story = { - args: { - selectedChartType: ChartType.Pie, - data: generatePieChartData(), - pieChartAxis: { - x: ['segment'], - y: ['value'] - }, - columnLabelFormats: { - segment: { - columnType: 'text', - style: 'string' - } satisfies IColumnLabelFormat, - value: { - columnType: 'number', - style: 'number', - numberSeparatorStyle: ',' - } satisfies IColumnLabelFormat - } satisfies Record, - pieDisplayLabelAs: 'percent', - pieDonutWidth: 0, - className: 'w-[500px] h-[500px]' - } -}; - -export const ScatterChart: Story = { - args: { - selectedChartType: ChartType.Scatter, - data: generateScatterChartData(), - scatterAxis: { - x: ['x'], - y: ['y'], - size: ['size'], - category: ['category'] - }, - columnLabelFormats: { - x: { - columnType: 'number', - style: 'number', - minimumFractionDigits: 1, - maximumFractionDigits: 1 - } satisfies IColumnLabelFormat, - y: { - columnType: 'number', - style: 'number', - minimumFractionDigits: 1, - maximumFractionDigits: 1 - } satisfies IColumnLabelFormat, - size: { - columnType: 'number', - style: 'number', - numberSeparatorStyle: ',' - } satisfies IColumnLabelFormat, - category: { - columnType: 'text', - style: 'string' - } satisfies IColumnLabelFormat - } satisfies Record, - className: 'w-[800px] h-[600px]' - } -};