diff --git a/src/components/chart-elements/RadarChart/RadarChart.tsx b/src/components/chart-elements/RadarChart/RadarChart.tsx new file mode 100644 index 000000000..270384fed --- /dev/null +++ b/src/components/chart-elements/RadarChart/RadarChart.tsx @@ -0,0 +1,191 @@ +"use client"; +import React, { useState } from "react"; +import { + Area, + CartesianGrid, + Legend, + Radar, + RadarChart as ReChartsRadarChart, + PolarGrid, + PolarAngleAxis, + ResponsiveContainer, + Tooltip, +} from "recharts"; + + +import { constructCategoryColors } from "../common/utils"; +import ChartLegend from "../common/ChartLegend"; +import ChartTooltip from "../common/ChartTooltip"; +import NoData from "../common/NoData"; + +import { + BaseColors, + defaultValueFormatter, + themeColorRange, + colorPalette, + getColorClassNames, + tremorTwMerge, +} from "lib"; + +export interface RadarChartProps extends BaseAnimationTimingProps { + data: any[]; + category?: string; + index?: string; + colors?: Color[]; + valueFormatter?: ValueFormatter; + outerRadius?: string | number; + dashArray?: boolean; + showAnimation?: boolean; + showTooltip?: boolean; + noDataText?: string; + showTooltip?: boolean; + showLegend?: boolean; + showGridLines?: boolean; + showGradient?: boolean; + noDataText?: string; + className?: string; + } + +const RadarChart = React.forwardRef((props, ref) => { + const { + data = [], + categories = [], + index, + colors = themeColorRange, + valueFormatter = defaultValueFormatter, + dashArray = false, + outerRadius = "80%", + showAnimation = true, + animationDuration = 900, + showTooltip = true, + showLegend = true, + showGridLines = true, + showGradient = false, + noDataText, + className, + ...other + } = props; + const [legendHeight, setLegendHeight] = useState(60); + const categoryColors = constructCategoryColors(categories, colors); + + return ( +
+ + {data?.length ? ( + + {showGridLines ? ( + + ) : null} + {showTooltip ? ( + ( + + )} + position={{ y: 0 }} + /> + ) : null} + {showLegend ? ( + ChartLegend({ payload }, categoryColors, setLegendHeight)} + /> + ) : null} + { + categories.length > 0 ? ( + categories.map((category) => { + return( + <> + + {showGradient ? ( + + + + + ) : ( + + + + )} + + + + + ) + }) + ) : ( + + ) + } + + ) : ( + + )} + +
+ ); +}); + +RadarChart.displayName = "RadarChart"; + +export default RadarChart; diff --git a/src/components/chart-elements/RadarChart/index.ts b/src/components/chart-elements/RadarChart/index.ts new file mode 100644 index 000000000..be6a7daf9 --- /dev/null +++ b/src/components/chart-elements/RadarChart/index.ts @@ -0,0 +1,2 @@ +export { default as RadarChart } from "./RadarChart"; +export type { RadarChartProps } from "./RadarChart"; diff --git a/src/components/chart-elements/index.ts b/src/components/chart-elements/index.ts index 88241fbae..31b802ecd 100644 --- a/src/components/chart-elements/index.ts +++ b/src/components/chart-elements/index.ts @@ -2,3 +2,4 @@ export * from "./AreaChart"; export * from "./BarChart"; export * from "./LineChart"; export * from "./DonutChart"; +export * from "./RadarChart" diff --git a/src/stories/chart-elements/RadarChart.stories.tsx b/src/stories/chart-elements/RadarChart.stories.tsx new file mode 100644 index 000000000..06bfc20f6 --- /dev/null +++ b/src/stories/chart-elements/RadarChart.stories.tsx @@ -0,0 +1,152 @@ +import React from "react"; + +import { ComponentMeta, ComponentStory } from "@storybook/react"; + +import { RadarChart, Card, Title } from "components"; +import { simpleBaseChartData as data } from "./helpers/testData"; +import { valueFormatter } from "./helpers/utils"; + +// More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export +export default { + title: "Tremor/ChartElements/RadarChart", + component: RadarChart, +} as ComponentMeta; +// More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args + +const ResponsiveTemplate: ComponentStory = (args) => ( + <> + Mobile +
+ + + +
+ Desktop + + + + +); + +const DefaultTemplate: ComponentStory = ({ ...args }) => ( + + + +); + +const args = { categories: ["Sales", "Successful Payments"], index: "month" }; + +export const DefaultResponsive = ResponsiveTemplate.bind({}); +// More on args: https://storybook.js.org/docs/react/writing-stories/args +DefaultResponsive.args = { + ...args, + data, +}; + +export const WithValueFormatter = ResponsiveTemplate.bind({}); +// More on args: https://storybook.js.org/docs/react/writing-stories/args +WithValueFormatter.args = { + ...args, + data, + valueFormatter: valueFormatter, +}; + +export const WithDashArray = ResponsiveTemplate.bind({}); +// More on args: https://storybook.js.org/docs/react/writing-stories/args +WithDashArray.args = { + ...args, + data, + dashArray: true +}; + +export const WithCustomOuterRadius = ResponsiveTemplate.bind({}); +// More on args: https://storybook.js.org/docs/react/writing-stories/args +WithCustomOuterRadius.args = { + ...args, + data, + outerRadius: "30%", +}; + +export const WithCustomColors = DefaultTemplate.bind({}); +// More on args: https://storybook.js.org/docs/react/writing-stories/args +WithCustomColors.args = { + ...args, + data, + colors: ["blue", "green"], +}; + +export const WithGradient = DefaultTemplate.bind({}); +// More on args: https://storybook.js.org/docs/react/writing-stories/args +WithGradient.args = { + ...args, + data, + showGradient: true, +}; + +export const WithLessColorsThanCategories = DefaultTemplate.bind({}); +WithLessColorsThanCategories.args = { + ...args, + data, + colors: ["green"], +}; + +export const WithLongValues = ResponsiveTemplate.bind({}); +WithLongValues.args = { + ...args, + data, + categories: ["This is an edge case"], + valueFormatter: valueFormatter, +}; + +export const WithMultipleCategories = ResponsiveTemplate.bind({}); +WithMultipleCategories.args = { + ...args, + data, + categories: ["Sales", "Successful Payments", "This is an edge case", "Test"], + valueFormatter: valueFormatter, +}; + +export const WithNoData = DefaultTemplate.bind({}); +// More on args: https://storybook.js.org/docs/react/writing-stories/args +WithNoData.args = { + ...args, +}; + +export const WithNoDataText = DefaultTemplate.bind({}); +WithNoDataText.args = { + ...args, + noDataText: "No data, try again later.", +}; + +export const WithNoCategories = DefaultTemplate.bind({}); +WithNoCategories.args = { + data, + index: "month", +}; + +export const WithNoIndex = DefaultTemplate.bind({}); +WithNoIndex.args = { + data, + categories: ["Sales", "Successful Payments"], +}; + +export const WithNoAnimation = DefaultTemplate.bind({}); +WithNoAnimation.args = { + ...args, + data, + showAnimation: false +}; + +export const WithLongAnimationDuration = DefaultTemplate.bind({}); +WithLongAnimationDuration.args = { + ...args, + data, + animationDuration: 5000, +}; + +export const WithShortAnimationDuration = DefaultTemplate.bind({}); +WithShortAnimationDuration.args = { + ...args, + data, + animationDuration: 100, +};