diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index a132102d2..d0d7e07a4 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -5,12 +5,16 @@ on: branches: - main - beta - - beta-** jobs: release: name: Release runs-on: ubuntu-latest + permissions: + contents: write # to be able to publish a GitHub release + issues: write # to be able to comment on released issues + pull-requests: write # to be able to comment on released pull requests + id-token: write # to enable use of OIDC for npm provenance steps: - name: checkout uses: actions/checkout@v4 @@ -27,6 +31,6 @@ jobs: run: npm run build - name: release env: - NODE_AUTH_TOKEN: ${{secrets.NPM_AUTH_TOKEN}} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} run: npx semantic-release diff --git a/package.json b/package.json index e98d5bf8c..636a15fe8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { - "name": "@tremor/react", - "version": "0.0.0-development", + "name": "upsolve-labs-tremor", + "version": "0.1.1", + "type": "module", "description": "The React library to build dashboards faster.", "scripts": { "prebuild": "rm -rf dist", @@ -15,7 +16,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/tremorlabs/tremor.git" + "url": "git+https://github.com/Upsolve-Labs/tremor.git" }, "author": "tremor", "license": "Apache 2.0", @@ -97,6 +98,7 @@ "rollup-plugin-typescript-paths": "^1.5.0", "semantic-release": "^24.1.1", "storybook": "^8.4.7", + "storybook-source-link": "^4.0.1", "style-loader": "^3.3.4", "tailwindcss": "^3.4.16", "tslib": "^2.7.0", @@ -113,9 +115,6 @@ "dist" ], "types": "dist/index.d.ts", - "publishConfig": { - "access": "public" - }, "release": { "branches": [ "main", diff --git a/src/components/chart-elements/AreaChart/AreaChart.tsx b/src/components/chart-elements/AreaChart/AreaChart.tsx index 33c3919fa..470d96edb 100644 --- a/src/components/chart-elements/AreaChart/AreaChart.tsx +++ b/src/components/chart-elements/AreaChart/AreaChart.tsx @@ -33,13 +33,14 @@ import { themeColorRange, tremorTwMerge, } from "lib"; -import { CurveType } from "../../../lib/inputTypes"; +import { CurveType, HorizontalPosition } from "../../../lib/inputTypes"; export interface AreaChartProps extends BaseChartProps { stack?: boolean; curveType?: CurveType; connectNulls?: boolean; showGradient?: boolean; + orientations?: HorizontalPosition[]; } interface ActiveDot { @@ -58,6 +59,8 @@ const AreaChart = React.forwardRef((props, ref) startEndOnly = false, showXAxis = true, showYAxis = true, + showXAxisLine = false, + showYAxisLine = false, yAxisWidth = 56, intervalType = "equidistantPreserveStart", showAnimation = false, @@ -82,6 +85,8 @@ const AreaChart = React.forwardRef((props, ref) ? { left: 0, right: 0 } : { left: 20, right: 20 }, tickGap = 5, + dataLabelOptions, + orientations, xAxisLabel, yAxisLabel, ...other @@ -182,7 +187,8 @@ const AreaChart = React.forwardRef((props, ref) tick={{ transform: "translate(0, 6)" }} ticks={startEndOnly ? [data[0][index], data[data.length - 1][index]] : undefined} fill="" - stroke="" + stroke={showXAxisLine ? "gray" : ""} + strokeOpacity={0.2} className={tremorTwMerge( // common "text-tremor-label", @@ -193,55 +199,90 @@ const AreaChart = React.forwardRef((props, ref) )} interval={startEndOnly ? "preserveStartEnd" : intervalType} tickLine={false} - axisLine={false} + axisLine={showXAxis && showXAxisLine} minTickGap={tickGap} angle={rotateLabelX?.angle} dy={rotateLabelX?.verticalShift} height={rotateLabelX?.xAxisHeight} - > - {xAxisLabel && ( - - )} - - - {yAxisLabel && ( - - )} - + {yAxisLabel && ( + + )} + + )) + ) : ( + + {yAxisLabel && ( + + )} + + )} ((props, ref) ); })} - {categories.map((category) => { + {categories.map((category, idx) => { const gradientId = (categoryColors.get(category) ?? BaseColors.Gray).replace("#", ""); - return ( - { - const { cx, cy, stroke, strokeLinecap, strokeLinejoin, strokeWidth, dataKey } = - props; + return ( { + const { cx, cy, stroke, strokeLinecap, strokeLinejoin, strokeWidth, dataKey } = + props; + return ( + onDotClick(props, event)} + /> + ); + }} + dot={(props: any) => { + const { + stroke, + strokeLinecap, + strokeLinejoin, + strokeWidth, + cx, + cy, + dataKey, + index, + } = props; + + if ( + (hasOnlyOneValueForThisKey(data, category) && + !(activeDot || (activeLegend && activeLegend !== category))) || + (activeDot?.index === index && activeDot?.dataKey === category) + ) { return ( ((props, ref) onClick={(dotProps: any, event) => onDotClick(props, event)} /> ); - }} - dot={(props: any) => { - const { - stroke, - strokeLinecap, - strokeLinejoin, - strokeWidth, - cx, - cy, - dataKey, - index, - } = props; - - if ( - (hasOnlyOneValueForThisKey(data, category) && - !(activeDot || (activeLegend && activeLegend !== category))) || - (activeDot?.index === index && activeDot?.dataKey === category) - ) { - return ( - - ); - } - return ; - }} - key={category} - name={category} - type={curveType} - dataKey={category} - stroke="" - fill={`url(#${gradientId})`} - strokeWidth={2} - strokeLinejoin="round" - strokeLinecap="round" - isAnimationActive={showAnimation} - animationDuration={animationDuration} - stackId={stack ? "a" : undefined} - connectNulls={connectNulls} - /> - ); - })} + } + return ; + }} + key={category} + label={ + dataLabelOptions?.[category] == null + ? undefined + : { + style: { + fontSize: `${dataLabelOptions?.[category].fontSize ?? 12}px`, + fontWeight: "300", + fill: categoryColors.get(category) ?? BaseColors.Gray, + stroke: "#000", + strokeWidth: 0.3, + }, + position: dataLabelOptions[category].position ?? "top", + offset: dataLabelOptions?.[category].offset ?? 10, + angle: dataLabelOptions?.[category].angle ?? 0, + } + } + name={category} + type={curveType} + dataKey={category} + stroke="" + fill={`url(#${gradientId})`} + strokeWidth={2} + strokeLinejoin="round" + strokeLinecap="round" + isAnimationActive={showAnimation} + animationDuration={animationDuration} + stackId={stack ? "a" : undefined} + connectNulls={connectNulls} + /> + )})} {onValueChange ? categories.map((category) => ( ((props, ref) => showAnimation = false, showXAxis = true, showYAxis = true, + showXAxisLine = false, + showYAxisLine = false, yAxisWidth = 56, intervalType = "equidistantPreserveStart", showTooltip = true, @@ -95,6 +97,7 @@ const BarChart = React.forwardRef((props, ref) => rotateLabelX, barCategoryGap, tickGap = 5, + dataLabelOptions, xAxisLabel, yAxisLabel, className, @@ -193,7 +196,8 @@ const BarChart = React.forwardRef((props, ref) => tick={{ transform: "translate(0, 6)" }} ticks={startEndOnly ? [data[0][index], data[data.length - 1][index]] : undefined} fill="" - stroke="" + stroke={showXAxisLine ? "gray" : ""} + strokeOpacity={0.2} className={tremorTwMerge( // common "mt-4 text-tremor-label", @@ -203,7 +207,7 @@ const BarChart = React.forwardRef((props, ref) => "dark:fill-dark-tremor-content", )} tickLine={false} - axisLine={false} + axisLine={showXAxis && showXAxisLine} angle={rotateLabelX?.angle} dy={rotateLabelX?.verticalShift} height={rotateLabelX?.xAxisHeight} @@ -226,7 +230,8 @@ const BarChart = React.forwardRef((props, ref) => tick={{ transform: "translate(-3, 0)" }} domain={yAxisDomain as AxisDomain} fill="" - stroke="" + stroke={showXAxisLine ? "gray" : ""} + strokeOpacity={0.2} className={tremorTwMerge( // common "text-tremor-label", @@ -236,7 +241,7 @@ const BarChart = React.forwardRef((props, ref) => "dark:fill-dark-tremor-content", )} tickLine={false} - axisLine={false} + axisLine={showXAxis && showXAxisLine} tickFormatter={valueFormatter} minTickGap={tickGap} allowDecimals={allowDecimals} @@ -259,13 +264,14 @@ const BarChart = React.forwardRef((props, ref) => ((props, ref) => width={yAxisWidth} hide={!showYAxis} dataKey={index} - axisLine={false} + axisLine={showYAxis && showYAxisLine} tickLine={false} ticks={startEndOnly ? [data[0][index], data[data.length - 1][index]] : undefined} type="category" interval="preserveStartEnd" tick={{ transform: "translate(0, 6)" }} fill="" - stroke="" + stroke={showYAxisLine ? "gray" : ""} + strokeOpacity={0.2} className={tremorTwMerge( // common "text-tremor-label", @@ -390,6 +397,22 @@ const BarChart = React.forwardRef((props, ref) => stackId={stack || relative ? "a" : undefined} dataKey={category} fill="" + label={ + dataLabelOptions?.[category] == null + ? undefined + : { + style: { + fontSize: `${dataLabelOptions?.[category].fontSize ?? 12}px`, + fontWeight: "300", + fill: categoryColors.get(category) ?? BaseColors.Gray, + stroke: "#000", + strokeWidth: 0.3, + }, + position: dataLabelOptions[category].position ?? "top", + offset: dataLabelOptions?.[category].offset ?? 10, + angle: dataLabelOptions?.[category].angle ?? 0, + } + } isAnimationActive={showAnimation} animationDuration={animationDuration} shape={(props: any) => renderShape(props, activeBar, activeLegend, layout)} diff --git a/src/components/chart-elements/DonutChart/DonutChart.tsx b/src/components/chart-elements/DonutChart/DonutChart.tsx index a3e0de11b..43443be69 100644 --- a/src/components/chart-elements/DonutChart/DonutChart.tsx +++ b/src/components/chart-elements/DonutChart/DonutChart.tsx @@ -18,6 +18,7 @@ import { parseData, parseLabelInput } from "./inputParser"; import type { EventProps } from "components/chart-elements/common"; import { CustomTooltipProps } from "components/chart-elements/common/CustomTooltipProps"; import type BaseAnimationTimingProps from "../common/BaseAnimationTimingProps"; +import { DataLabelOptions } from "../common/BaseChartProps"; type DonutChartVariant = "donut" | "pie"; @@ -36,6 +37,7 @@ export interface DonutChartProps extends BaseAnimationTimingProps { className?: string; onValueChange?: (value: EventProps) => void; customTooltip?: React.ComponentType; + dataLabelOptions?: Record; } const renderInactiveShape = (props: any) => { @@ -90,6 +92,7 @@ const DonutChart = React.forwardRef((props, ref onValueChange, customTooltip, className, + dataLabelOptions, ...other } = props; const CustomTooltip = customTooltip; @@ -177,6 +180,7 @@ const DonutChart = React.forwardRef((props, ref activeIndex={activeIndex} inactiveShape={renderInactiveShape} style={{ outline: "none" }} + label={dataLabelOptions?.[category] != null} /> {/* {showTooltip ? ( ((props, ref) startEndOnly = false, showXAxis = true, showYAxis = true, + showXAxisLine = false, + showYAxisLine = false, yAxisWidth = 56, intervalType = "equidistantPreserveStart", animationDuration = 900, @@ -75,6 +78,8 @@ const LineChart = React.forwardRef((props, ref) rotateLabelX, padding = !showXAxis && !showYAxis ? { left: 0, right: 0 } : { left: 20, right: 20 }, tickGap = 5, + dataLabelOptions, + orientations, xAxisLabel, yAxisLabel, ...other @@ -177,7 +182,8 @@ const LineChart = React.forwardRef((props, ref) tick={{ transform: "translate(0, 6)" }} ticks={startEndOnly ? [data[0][index], data[data.length - 1][index]] : undefined} fill="" - stroke="" + stroke={showXAxisLine ? "gray" : ""} + strokeOpacity={0.2} className={tremorTwMerge( // common "text-tremor-label", @@ -187,55 +193,91 @@ const LineChart = React.forwardRef((props, ref) "dark:fill-dark-tremor-content", )} tickLine={false} - axisLine={false} + axisLine={showXAxis && showXAxisLine} minTickGap={tickGap} angle={rotateLabelX?.angle} dy={rotateLabelX?.verticalShift} height={rotateLabelX?.xAxisHeight} - > - {xAxisLabel && ( - - )} - - - {yAxisLabel && ( - - )} - + {yAxisLabel && ( + + )} + + )) + ) : ( + + {yAxisLabel && ( + + )} + + )} + ((props, ref) } /> ) : null} - {categories.map((category) => ( + {categories.map((category, idx) => ( ((props, ref) return ; }} key={category} + label={ + dataLabelOptions?.[category] == null + ? undefined + : { + style: { + fontSize: `${dataLabelOptions?.[category].fontSize ?? 12}px`, + fontWeight: "300", + fill: categoryColors.get(category) ?? BaseColors.Gray, + stroke: "#000", + strokeWidth: 0.3, + }, + position: dataLabelOptions[category].position ?? "top", + offset: dataLabelOptions?.[category].offset ?? 10, + angle: dataLabelOptions?.[category].angle ?? 0, + } + } name={category} type={curveType} dataKey={category} @@ -372,10 +430,11 @@ const LineChart = React.forwardRef((props, ref) isAnimationActive={showAnimation} animationDuration={animationDuration} connectNulls={connectNulls} + yAxisId={orientations ? idx : undefined} /> ))} {onValueChange - ? categories.map((category) => ( + ? categories.map((category, idx) => ( ((props, ref) const { name } = props; onCategoryClick(name); }} + yAxisId={orientations ? idx : undefined} /> )) : null} diff --git a/src/components/chart-elements/ScatterChart/ScatterChart.tsx b/src/components/chart-elements/ScatterChart/ScatterChart.tsx index 99b17afe3..7ca5548d2 100644 --- a/src/components/chart-elements/ScatterChart/ScatterChart.tsx +++ b/src/components/chart-elements/ScatterChart/ScatterChart.tsx @@ -37,6 +37,7 @@ import { tremorTwMerge, } from "lib"; import { Color, ValueFormatter, IntervalType } from "../../../lib/inputTypes"; +import { DataLabelOptions } from "../common/BaseChartProps"; export type ScatterChartValueFormatter = { x?: ValueFormatter; @@ -50,7 +51,7 @@ export interface ScatterChartProps data: any[]; x: string; y: string; - category: string; + category?: string; size?: string; valueFormatter?: ScatterChartValueFormatter; sizeRange?: number[]; @@ -59,6 +60,8 @@ export interface ScatterChartProps startEndOnly?: boolean; showXAxis?: boolean; showYAxis?: boolean; + showXAxisLine?: boolean; + showYAxisLine?: boolean; yAxisWidth?: number; intervalType?: IntervalType; showTooltip?: boolean; @@ -81,6 +84,7 @@ export interface ScatterChartProps xAxisHeight: number; }; tickGap?: number; + dataLabelOptions?: Record; xAxisLabel?: string; yAxisLabel?: string; } @@ -122,6 +126,8 @@ const ScatterChart = React.forwardRef((props, startEndOnly = false, showXAxis = true, showYAxis = true, + showXAxisLine = false, + showYAxisLine = false, yAxisWidth = 56, intervalType = "equidistantPreserveStart", animationDuration = 900, @@ -143,6 +149,7 @@ const ScatterChart = React.forwardRef((props, className, enableLegendSlider = false, tickGap = 5, + dataLabelOptions, xAxisLabel, yAxisLabel, ...other @@ -162,12 +169,21 @@ const ScatterChart = React.forwardRef((props, onValueChange?.(null); } else { setActiveNode(data.node); - setActiveLegend(data.payload[category]); - onValueChange?.({ - eventType: "bubble", - categoryClicked: data.payload[category], - ...data.payload, - }); + + // set active legends and categoryClicked state only if category is not undefined (ie there are categories to click!) + if (category) { + setActiveLegend(data.payload[category]); + onValueChange?.({ + eventType: "bubble", + categoryClicked: data.payload[category], + ...data.payload, + }); + } else { + onValueChange?.({ + eventType: "bubble", + ...data.payload, + }); + } } } @@ -189,6 +205,7 @@ const ScatterChart = React.forwardRef((props, const categories = constructCategories(data, category); const categoryColors = constructCategoryColors(categories, colors); + //maybe rename getYAxisDomain to getAxisDomain const xAxisDomain = getYAxisDomain(autoMinXValue, minXValue, maxXValue); const yAxisDomain = getYAxisDomain(autoMinYValue, minYValue, maxYValue); @@ -238,7 +255,8 @@ const ScatterChart = React.forwardRef((props, type="number" name={x} fill="" - stroke="" + stroke={showXAxisLine ? "gray" : ""} + strokeOpacity={0.2} className={tremorTwMerge( // common "text-tremor-label", @@ -249,7 +267,7 @@ const ScatterChart = React.forwardRef((props, )} tickLine={false} tickFormatter={valueFormatter.x} - axisLine={false} + axisLine={showXAxis && showXAxisLine} minTickGap={tickGap} domain={xAxisDomain as AxisDomain} allowDataOverflow={true} @@ -272,7 +290,7 @@ const ScatterChart = React.forwardRef((props, ((props, tick={{ transform: "translate(-3, 0)" }} tickFormatter={valueFormatter.y} fill="" - stroke="" + stroke={showYAxisLine ? "gray" : ""} + strokeOpacity={0.2} className={tremorTwMerge( // common "text-tremor-label", @@ -318,7 +337,10 @@ const ScatterChart = React.forwardRef((props, ({ ...payloadItem, - color: categoryColors.get(color) ?? BaseColors.Gray, + color: + categoryColors.get(color) ?? + categoryColors.get("cat_1") ?? + BaseColors.Gray, }))} active={active} label={color} @@ -341,35 +363,93 @@ const ScatterChart = React.forwardRef((props, } /> {size ? : null} - {categories.map((cat) => { - return ( - d[category] === cat) : data} - isAnimationActive={showAnimation} - animationDuration={animationDuration} - shape={(props: any) => renderShape(props, activeNode, activeLegend)} - onClick={onNodeClick} - /> - ); - })} - {showLegend ? ( + {category ? ( + categories?.map((cat) => { + return ( + d[category] === cat) : data} + isAnimationActive={showAnimation} + animationDuration={animationDuration} + shape={(props: any) => renderShape(props, activeNode, activeLegend)} + onClick={onNodeClick} + /> + ); + }) + ) : ( + renderShape(props, activeNode, activeLegend)} + onClick={onNodeClick} + /> + )} + {showLegend && category ? ( { data: any[]; categories: string[]; @@ -22,6 +30,8 @@ interface BaseChartProps extends BaseAnimationTimingProps, React.HTMLAttributes< startEndOnly?: boolean; showXAxis?: boolean; showYAxis?: boolean; + showXAxisLine?: boolean; + showYAxisLine?: boolean; yAxisWidth?: number; intervalType?: IntervalType; showTooltip?: boolean; @@ -42,6 +52,7 @@ interface BaseChartProps extends BaseAnimationTimingProps, React.HTMLAttributes< xAxisHeight?: number; }; tickGap?: number; + dataLabelOptions?: Record; xAxisLabel?: string; yAxisLabel?: string; } diff --git a/src/components/chart-elements/common/ChartTooltip.tsx b/src/components/chart-elements/common/ChartTooltip.tsx index 4596d70f9..92d425249 100644 --- a/src/components/chart-elements/common/ChartTooltip.tsx +++ b/src/components/chart-elements/common/ChartTooltip.tsx @@ -117,7 +117,7 @@ const ChartTooltip = ({ {filteredPayload.map(({ value, name }: { value: number; name: string }, idx: number) => ( diff --git a/src/components/chart-elements/common/index.ts b/src/components/chart-elements/common/index.ts index 7a73db7fe..2794e52c7 100644 --- a/src/components/chart-elements/common/index.ts +++ b/src/components/chart-elements/common/index.ts @@ -1,2 +1,2 @@ -export type { EventProps } from "./BaseChartProps"; +export type { EventProps, DataLabelOptions } from "./BaseChartProps"; export type { CustomTooltipProps } from "./CustomTooltipProps"; diff --git a/src/components/chart-elements/common/utils.ts b/src/components/chart-elements/common/utils.ts index ef4695898..dd014040b 100644 --- a/src/components/chart-elements/common/utils.ts +++ b/src/components/chart-elements/common/utils.ts @@ -5,6 +5,12 @@ export const constructCategoryColors = ( colors: (Color | string)[], ): Map => { const categoryColors = new Map(); + + // cat_1 key is a hardcoded key that allows access to a colour from a categoryless scatterchart. The colour is set to the first one passed in - default is blue if no custom colour provided. + if (categories.length === 0) { + categoryColors.set("cat_1", colors[0]); + } + categories.forEach((category, idx) => { categoryColors.set(category, colors[idx % colors.length]); }); diff --git a/src/components/chart-elements/index.ts b/src/components/chart-elements/index.ts index 2632d2800..0ff171487 100644 --- a/src/components/chart-elements/index.ts +++ b/src/components/chart-elements/index.ts @@ -1,6 +1,6 @@ export * from "./AreaChart"; export * from "./BarChart"; -export { EventProps } from "./common/BaseChartProps"; +export { EventProps, DataLabelOptions } from "./common/BaseChartProps"; export { CustomTooltipProps } from "./common/CustomTooltipProps"; export * from "./DonutChart"; export * from "./LineChart"; diff --git a/src/lib/inputTypes.ts b/src/lib/inputTypes.ts index 328e9bac6..2a45c03c8 100644 --- a/src/lib/inputTypes.ts +++ b/src/lib/inputTypes.ts @@ -1,5 +1,5 @@ export type ValueFormatter = { - (value: number): string; + (value: number, index?: number): string; }; export type CurveType = "linear" | "natural" | "monotone" | "step"; diff --git a/src/stories/chart-elements/AreaChart.stories.tsx b/src/stories/chart-elements/AreaChart.stories.tsx index 2f8893166..b410de349 100644 --- a/src/stories/chart-elements/AreaChart.stories.tsx +++ b/src/stories/chart-elements/AreaChart.stories.tsx @@ -28,6 +28,15 @@ export const Default: Story = { args: {}, }; +export const ShowAxisLines: Story = { + args: { + showXAxis: true, + showXAxisLine: true, + showYAxis: true, + showYAxisLine: true, + }, +}; + export const DefaultNegativeValues: Story = { args: { data: simpleBaseChartWithNegativeValues, @@ -40,6 +49,10 @@ export const Stacked: Story = { }, }; +export const RightOrientation: Story = { + args: { categories: ["Sales"], orientations: ["right"] }, +}; + export const ValueFormatter: Story = { args: { valueFormatter: valueFormatter, yAxisWidth: 60 }, }; @@ -105,10 +118,34 @@ export const ConnectNullsTrue: Story = { args: { data: simpleBaseChartDataWithNulls, connectNulls: true }, }; +export const ShowLabels: Story = { + args: { + dataLabelOptions: { + Sales: { position: "top", angle: -45, offset: 30, fontSize: 6 }, + "Successful Payments": { position: "bottom" }, + }, + }, +}; + export const ConnectNullsFalse: Story = { args: { data: simpleBaseChartDataWithNulls, connectNulls: false }, }; +export const MultipleYAxes: Story = { + args: { + orientations: ["left", "right"], + valueFormatter: (value, index) => { + switch (index) { + case 0: + default: + return `${value} S`; + case 1: + return `${value} P`; + } + }, + }, +}; + export const Animation: Story = { args: { showAnimation: true }, }; diff --git a/src/stories/chart-elements/BarChart.stories.tsx b/src/stories/chart-elements/BarChart.stories.tsx index 4225c2bd3..f40be9128 100644 --- a/src/stories/chart-elements/BarChart.stories.tsx +++ b/src/stories/chart-elements/BarChart.stories.tsx @@ -171,6 +171,15 @@ export const LongIndexName: Story = { args: { data: longIndexBaseChartData }, }; +export const ShowLabels: Story = { + args: { + dataLabelOptions: { + Sales: { position: "top", angle: -45, offset: 30, fontSize: 6 }, + "Successful Payments": { position: "bottom" }, + }, + }, +}; + export const LongIndexNameAndPreserveStartEnd: Story = { args: { data: longIndexBaseChartData, intervalType: "preserveStartEnd" }, }; diff --git a/src/stories/chart-elements/DonutChart.stories.tsx b/src/stories/chart-elements/DonutChart.stories.tsx index 9c32163b1..bfd5cbe31 100644 --- a/src/stories/chart-elements/DonutChart.stories.tsx +++ b/src/stories/chart-elements/DonutChart.stories.tsx @@ -79,6 +79,17 @@ export const CustomColors: Story = { }, }; +export const ShowLabels: Story = { + args: { dataLabelOptions: { sales: { position: "top", angle: -45, offset: 30, fontSize: 6 } } }, +}; + +export const ShowLabelsPie: Story = { + args: { + variant: "pie", + dataLabelOptions: { sales: { position: "top", angle: -45, offset: 30, fontSize: 6 } }, + }, +}; + export const MoreDatapointsThanColors: Story = { args: { data: [ diff --git a/src/stories/chart-elements/LineChart.stories.tsx b/src/stories/chart-elements/LineChart.stories.tsx index c10abd03c..4ccb8d252 100644 --- a/src/stories/chart-elements/LineChart.stories.tsx +++ b/src/stories/chart-elements/LineChart.stories.tsx @@ -34,6 +34,10 @@ export const DefaultNegativeValues: Story = { }, }; +export const RightOrientation: Story = { + args: { categories: ["Sales"], orientations: ["right"] }, +}; + export const ValueFormatter: Story = { args: { valueFormatter: valueFormatter, yAxisWidth: 60 }, }; @@ -98,6 +102,21 @@ export const ConnectNullsFalse: Story = { args: { data: simpleBaseChartDataWithNulls, connectNulls: false }, }; +export const MultipleYAxes: Story = { + args: { + orientations: ["left", "right"], + valueFormatter: (value, index) => { + switch (index) { + case 0: + default: + return `${value} S`; + case 1: + return `${value} P`; + } + }, + }, +}; + export const Animation: Story = { args: { showAnimation: true }, }; @@ -157,6 +176,15 @@ export const NoYAxisStartEndOnly: Story = { args: { showYAxis: false, startEndOnly: true }, }; +export const ShowLabels: Story = { + args: { + dataLabelOptions: { + Sales: { position: "top", angle: -45, offset: 30, fontSize: 6 }, + "Successful Payments": { position: "bottom" }, + }, + }, +}; + export const RotateXLabels: Story = { args: { data: longBaseChartData, diff --git a/src/stories/chart-elements/ScatterChart.stories.tsx b/src/stories/chart-elements/ScatterChart.stories.tsx index f5ff99613..317321ef3 100644 --- a/src/stories/chart-elements/ScatterChart.stories.tsx +++ b/src/stories/chart-elements/ScatterChart.stories.tsx @@ -139,6 +139,12 @@ export const MultipleZeroValues: Story = { }, }; +export const ShowLabels: Story = { + args: { + dataLabelOptions: { "Location A": { position: "top", angle: -45, offset: 30, fontSize: 6 } }, + }, +}; + export const RotateXLabel: Story = { args: { rotateLabelX: { angle: -45, verticalShift: 15, xAxisHeight: 50 }, @@ -193,3 +199,75 @@ export const AxisLabels: Story = { yAxisLabel: "Amount (USD)", }, }; + +export const NoCategory: Story = { + args: { + x: "x", + y: "y", + size: "z", + category: undefined, + data, + className: "h-72", + }, +}; + +export const NoCategoryOtherColors: Story = { + args: { + x: "x", + y: "y", + size: "z", + category: undefined, + data, + className: "h-72", + colors: ["purple"], + }, +}; + +export const CategoryCustomColors: Story = { + args: { + x: "x", + y: "y", + size: "z", + category: "location", + data, + className: "h-72", + colors: ["#32a852", "#fcba03", "orange-600", "blue-400"], + }, +}; + +export const NoCategoryOtherColorsCustomTooltip: Story = { + args: { + x: "x", + y: "y", + size: "z", + category: undefined, + data, + className: "h-72", + colors: ["#fcba03"], + customTooltip: (props: CustomTooltipProps) => { + const { payload, active, label } = props; + if (!active || !payload) return null; + + return ( +
+
+
+
+

{label}

+ {payload.map((payloadItem: any, index: number) => ( +
+

+ {payloadItem.name} +

+

+ {payloadItem.value} +

+
+ ))} +
+
+
+ ); + }, + }, +}; diff --git a/tailwind.config.js b/tailwind.config.js index e608b6095..4327ca280 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -124,7 +124,24 @@ module.exports = { }, // custom colors charts - ...["[#32a852]", "[#fcba03]"].flatMap((customColor) => [ + ...[ + "[#32a852]", + "[#fcba03]", + "[#22d3ee]", + "[#f87171]", + "[#fb923c]", + "[#4ade80]", + "[#2dd4bf]", + "[#60a5fa]", + "[#c084fc]", + "[#f472b6]", + "[#facc15]", + "[#a3e635]", + "[#818cf8]", + "[#a78bfa]", + "[#e879f9]", + "[#9ca3af]", + ].flatMap((customColor) => [ `bg-${customColor}`, `border-${customColor}`, `hover:bg-${customColor}`,